veryfront 0.0.73 → 0.0.75

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,56 +79,158 @@ 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
+ if (boundContext) {
142
+ this.boundContext = boundContext;
143
+ }
95
144
  }
145
+ boundContext = {};
96
146
  setLevel(level) {
97
147
  this.level = level;
98
148
  }
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
  config;
196
322
  constructor(config) {
@@ -231,7 +357,17 @@ var BaseProvider = class {
231
357
  }));
232
358
  }
233
359
  const data = await response.json();
234
- return this.transformResponse(data);
360
+ const parseResult = OpenAICompletionResponseSchema.safeParse(data);
361
+ if (!parseResult.success) {
362
+ agentLogger.warn(`${this.name}: Invalid response structure`, {
363
+ errors: parseResult.error.flatten()
364
+ });
365
+ throw toError(createError({
366
+ type: "agent",
367
+ message: `${this.name}: Invalid response structure from provider`
368
+ }));
369
+ }
370
+ return this.transformResponse(parseResult.data);
235
371
  }
236
372
  /**
237
373
  * Stream a completion
@@ -1931,6 +2067,9 @@ var veryfrontConfigSchema = z5.object({
1931
2067
  }).partial().optional(),
1932
2068
  router: z5.enum(["app", "pages"]).optional(),
1933
2069
  defaultLayout: z5.string().optional(),
2070
+ layout: z5.string().optional(),
2071
+ provider: z5.string().optional(),
2072
+ app: z5.string().optional(),
1934
2073
  theme: z5.object({ colors: z5.record(z5.string()).optional() }).partial().optional(),
1935
2074
  build: z5.object({
1936
2075
  outDir: z5.string().optional(),
@@ -2026,6 +2165,8 @@ var veryfrontConfigSchema = z5.object({
2026
2165
  apiBaseUrl: z5.string().url(),
2027
2166
  apiToken: z5.string(),
2028
2167
  projectSlug: z5.string(),
2168
+ proxyMode: z5.boolean().optional(),
2169
+ productionMode: z5.boolean().optional(),
2029
2170
  cache: z5.object({
2030
2171
  enabled: z5.boolean().optional(),
2031
2172
  ttl: z5.number().int().positive().optional(),
@@ -2087,6 +2228,9 @@ function findUnknownTopLevelKeys(input) {
2087
2228
  "experimental",
2088
2229
  "router",
2089
2230
  "defaultLayout",
2231
+ "layout",
2232
+ "provider",
2233
+ "app",
2090
2234
  "theme",
2091
2235
  "build",
2092
2236
  "cache",
@@ -2104,12 +2248,12 @@ function findUnknownTopLevelKeys(input) {
2104
2248
  }
2105
2249
 
2106
2250
  // src/core/config/loader.ts
2107
- import { join } from "node:path";
2251
+ import { dirname, join } from "node:path";
2108
2252
 
2109
2253
  // deno.json
2110
2254
  var deno_default = {
2111
2255
  name: "veryfront",
2112
- version: "0.0.73",
2256
+ version: "0.0.75",
2113
2257
  nodeModulesDir: "auto",
2114
2258
  exclude: [
2115
2259
  "npm/",
@@ -2141,7 +2285,11 @@ var deno_default = {
2141
2285
  "./oauth": "./src/core/oauth/index.ts",
2142
2286
  "./oauth/providers": "./src/core/oauth/providers/index.ts",
2143
2287
  "./oauth/handlers": "./src/core/oauth/handlers/index.ts",
2144
- "./oauth/token-store": "./src/core/oauth/token-store/index.ts"
2288
+ "./oauth/token-store": "./src/core/oauth/token-store/index.ts",
2289
+ "./head": "./src/exports/head.ts",
2290
+ "./router": "./src/exports/router.ts",
2291
+ "./context": "./src/exports/context.ts",
2292
+ "./fonts": "./src/exports/fonts.ts"
2145
2293
  },
2146
2294
  imports: {
2147
2295
  "@veryfront": "./src/index.ts",
@@ -2194,12 +2342,12 @@ var deno_default = {
2194
2342
  csstype: "https://esm.sh/csstype@3.2.3",
2195
2343
  "@types/react": "https://esm.sh/@types/react@18.3.27?deps=csstype@3.2.3",
2196
2344
  "@types/react-dom": "https://esm.sh/@types/react-dom@18.3.7?deps=csstype@3.2.3",
2197
- react: "https://esm.sh/react@18.3.1",
2198
- "react-dom": "https://esm.sh/react-dom@18.3.1",
2199
- "react-dom/server": "https://esm.sh/react-dom@18.3.1/server",
2200
- "react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
2201
- "react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
2202
- "react/jsx-dev-runtime": "https://esm.sh/react@18.3.1/jsx-dev-runtime",
2345
+ react: "npm:react@18.3.1",
2346
+ "react-dom": "npm:react-dom@18.3.1",
2347
+ "react-dom/server": "npm:react-dom@18.3.1/server",
2348
+ "react-dom/client": "npm:react-dom@18.3.1/client",
2349
+ "react/jsx-runtime": "npm:react@18.3.1/jsx-runtime",
2350
+ "react/jsx-dev-runtime": "npm:react@18.3.1/jsx-dev-runtime",
2203
2351
  "@mdx-js/mdx": "npm:@mdx-js/mdx@3.0.0",
2204
2352
  "@mdx-js/react": "npm:@mdx-js/react@3.0.0",
2205
2353
  "unist-util-visit": "npm:unist-util-visit@5.0.0",
@@ -2209,27 +2357,36 @@ var deno_default = {
2209
2357
  "remark-frontmatter": "npm:remark-frontmatter@5.0.0",
2210
2358
  "rehype-highlight": "npm:rehype-highlight@7.0.2",
2211
2359
  "rehype-slug": "npm:rehype-slug@6.0.0",
2212
- esbuild: "https://deno.land/x/esbuild@v0.20.1/wasm.js",
2213
- "esbuild/mod.js": "https://deno.land/x/esbuild@v0.20.1/mod.js",
2360
+ esbuild: "npm:esbuild@0.20.2",
2361
+ "esbuild/mod.js": "npm:esbuild@0.20.2",
2214
2362
  "es-module-lexer": "npm:es-module-lexer@1.5.0",
2215
- zod: "npm:zod@3.23.8",
2363
+ zod: "npm:zod@3.25.76",
2216
2364
  "mime-types": "npm:mime-types@2.1.35",
2217
2365
  mdast: "npm:@types/mdast@4.0.3",
2218
2366
  hast: "npm:@types/hast@3.0.3",
2219
2367
  unist: "npm:@types/unist@3.0.2",
2220
2368
  unified: "npm:unified@11.0.5",
2221
- ai: "https://esm.sh/ai@5.0.76?deps=react@18.3.1,react-dom@18.3.1",
2222
- "ai/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
2223
- "@ai-sdk/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
2369
+ ai: "npm:ai@5.0.76",
2370
+ "ai/react": "npm:@ai-sdk/react@2.0.1",
2371
+ "@ai-sdk/react": "npm:@ai-sdk/react@2.0.1",
2224
2372
  "@ai-sdk/openai": "https://esm.sh/@ai-sdk/openai@2.0.1",
2225
2373
  "@ai-sdk/anthropic": "https://esm.sh/@ai-sdk/anthropic@2.0.1",
2226
2374
  unocss: "https://esm.sh/unocss@0.59.0",
2227
2375
  "@unocss/core": "https://esm.sh/@unocss/core@0.59.0",
2228
2376
  "@unocss/preset-wind": "https://esm.sh/@unocss/preset-wind@0.59.0",
2377
+ "next-themes": "npm:next-themes@0.4",
2229
2378
  redis: "npm:redis",
2230
2379
  pg: "npm:pg",
2231
2380
  "@opentelemetry/api": "npm:@opentelemetry/api@1",
2232
- "@opentelemetry/core": "npm:@opentelemetry/core@1"
2381
+ "@opentelemetry/core": "npm:@opentelemetry/core@1",
2382
+ "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@1",
2383
+ "@opentelemetry/exporter-trace-otlp-http": "npm:@opentelemetry/exporter-trace-otlp-http@0.57",
2384
+ "@opentelemetry/resources": "npm:@opentelemetry/resources@1",
2385
+ "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@1",
2386
+ "@babel/parser": "npm:@babel/parser@7.26.3",
2387
+ "@babel/traverse": "npm:@babel/traverse@7.26.3",
2388
+ "@babel/generator": "npm:@babel/generator@7.26.3",
2389
+ "@babel/types": "npm:@babel/types@7.26.3"
2233
2390
  },
2234
2391
  compilerOptions: {
2235
2392
  jsx: "react-jsx",
@@ -2237,7 +2394,7 @@ var deno_default = {
2237
2394
  strict: true,
2238
2395
  noImplicitAny: true,
2239
2396
  noUncheckedIndexedAccess: true,
2240
- types: [],
2397
+ types: ["npm:@types/react@18"],
2241
2398
  lib: [
2242
2399
  "deno.window",
2243
2400
  "dom",
@@ -2252,9 +2409,9 @@ var deno_default = {
2252
2409
  build: "deno compile --allow-all --output ../../bin/veryfront src/cli/main.ts",
2253
2410
  "build:npm": "deno run -A scripts/build-npm.ts",
2254
2411
  release: "deno run -A scripts/release.ts",
2255
- test: "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
2256
- "test:unit": "DENO_JOBS=1 deno test --parallel --allow-all --v8-flags=--max-old-space-size=8192 --ignore=tests --unstable-worker-options --unstable-net",
2257
- "test:integration": "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
2412
+ test: "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
2413
+ "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",
2414
+ "test:integration": "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
2258
2415
  "test:coverage": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage --unstable-worker-options --unstable-net || exit 1",
2259
2416
  "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",
2260
2417
  "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",
@@ -2316,6 +2473,7 @@ var deno_default = {
2316
2473
 
2317
2474
  // src/core/utils/version.ts
2318
2475
  var VERSION = getEnv("VERYFRONT_VERSION") || (typeof deno_default.version === "string" ? deno_default.version : "0.0.0");
2476
+ var SERVER_START_TIME = Date.now();
2319
2477
 
2320
2478
  // src/core/utils/constants/cdn.ts
2321
2479
  var ESM_CDN_BASE = "https://esm.sh";
@@ -2431,6 +2589,164 @@ var DEV_SERVER_ENDPOINTS = {
2431
2589
  ERROR_OVERLAY: INTERNAL_ENDPOINTS.ERROR_OVERLAY
2432
2590
  };
2433
2591
 
2592
+ // src/platform/compat/fs.ts
2593
+ var NodeFileSystem = class {
2594
+ fs = null;
2595
+ os = null;
2596
+ path = null;
2597
+ initialized = false;
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
+
2434
2750
  // src/core/config/loader.ts
2435
2751
  function getDefaultImportMapForConfig() {
2436
2752
  return { imports: getReactImportMap(REACT_DEFAULT_VERSION) };
@@ -2567,7 +2883,62 @@ var ConfigValidationError = class extends Error {
2567
2883
  this.name = "ConfigValidationError";
2568
2884
  }
2569
2885
  };
2570
- 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" || adapterName === "MultiProjectFSAdapter";
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
+ }
2571
2942
  try {
2572
2943
  const configUrl = `file://${configPath}?t=${Date.now()}-${crypto.randomUUID()}`;
2573
2944
  const configModule = await import(configUrl);
@@ -2603,7 +2974,7 @@ async function getConfig(projectDir, adapter) {
2603
2974
  if (!exists)
2604
2975
  continue;
2605
2976
  try {
2606
- const merged = await loadAndMergeConfig(configPath, projectDir);
2977
+ const merged = await loadAndMergeConfig(configPath, projectDir, adapter);
2607
2978
  if (merged)
2608
2979
  return merged;
2609
2980
  } catch (error) {
@@ -2776,164 +3147,6 @@ function createMockAdapter() {
2776
3147
  };
2777
3148
  }
2778
3149
 
2779
- // src/platform/compat/fs.ts
2780
- var NodeFileSystem = class {
2781
- fs = null;
2782
- os = null;
2783
- path = null;
2784
- initialized = false;
2785
- async ensureInitialized() {
2786
- if (this.initialized)
2787
- return;
2788
- if (!isNode) {
2789
- throw toError(createError({
2790
- type: "not_supported",
2791
- message: "Node.js fs modules not available",
2792
- feature: "Node.js"
2793
- }));
2794
- }
2795
- const [fsModule, osModule, pathModule] = await Promise.all([
2796
- import("node:fs/promises"),
2797
- import("node:os"),
2798
- import("node:path")
2799
- ]);
2800
- this.fs = fsModule;
2801
- this.os = osModule;
2802
- this.path = pathModule;
2803
- this.initialized = true;
2804
- }
2805
- async readTextFile(path) {
2806
- await this.ensureInitialized();
2807
- return await this.fs.readFile(path, { encoding: "utf8" });
2808
- }
2809
- async readFile(path) {
2810
- await this.ensureInitialized();
2811
- return await this.fs.readFile(path);
2812
- }
2813
- async writeTextFile(path, data) {
2814
- await this.ensureInitialized();
2815
- await this.fs.writeFile(path, data, { encoding: "utf8" });
2816
- }
2817
- async writeFile(path, data) {
2818
- await this.ensureInitialized();
2819
- await this.fs.writeFile(path, data);
2820
- }
2821
- async exists(path) {
2822
- await this.ensureInitialized();
2823
- try {
2824
- await this.fs.access(path);
2825
- return true;
2826
- } catch (error) {
2827
- if (error.code === "ENOENT") {
2828
- return false;
2829
- }
2830
- throw error;
2831
- }
2832
- }
2833
- async stat(path) {
2834
- await this.ensureInitialized();
2835
- const stat = await this.fs.stat(path);
2836
- return {
2837
- isFile: stat.isFile(),
2838
- isDirectory: stat.isDirectory(),
2839
- isSymlink: stat.isSymbolicLink(),
2840
- size: stat.size,
2841
- mtime: stat.mtime
2842
- };
2843
- }
2844
- async mkdir(path, options) {
2845
- await this.ensureInitialized();
2846
- await this.fs.mkdir(path, { recursive: options?.recursive ?? false });
2847
- }
2848
- async *readDir(path) {
2849
- await this.ensureInitialized();
2850
- const entries = await this.fs.readdir(path, { withFileTypes: true });
2851
- for (const entry of entries) {
2852
- yield {
2853
- name: entry.name,
2854
- isFile: entry.isFile(),
2855
- isDirectory: entry.isDirectory()
2856
- };
2857
- }
2858
- }
2859
- async remove(path, options) {
2860
- await this.ensureInitialized();
2861
- await this.fs.rm(path, {
2862
- recursive: options?.recursive ?? false,
2863
- force: options?.recursive ?? false
2864
- });
2865
- }
2866
- async makeTempDir(options) {
2867
- await this.ensureInitialized();
2868
- const tempDir = this.path.join(
2869
- this.os.tmpdir(),
2870
- `${options?.prefix ?? "tmp-"}${Math.random().toString(36).substring(2, 8)}`
2871
- );
2872
- await this.fs.mkdir(tempDir, { recursive: true });
2873
- return tempDir;
2874
- }
2875
- };
2876
- var DenoFileSystem = class {
2877
- async readTextFile(path) {
2878
- return await Deno.readTextFile(path);
2879
- }
2880
- async readFile(path) {
2881
- return await Deno.readFile(path);
2882
- }
2883
- async writeTextFile(path, data) {
2884
- await Deno.writeTextFile(path, data);
2885
- }
2886
- async writeFile(path, data) {
2887
- await Deno.writeFile(path, data);
2888
- }
2889
- async exists(path) {
2890
- try {
2891
- await Deno.stat(path);
2892
- return true;
2893
- } catch (error) {
2894
- if (error instanceof Deno.errors.NotFound) {
2895
- return false;
2896
- }
2897
- throw error;
2898
- }
2899
- }
2900
- async stat(path) {
2901
- const stat = await Deno.stat(path);
2902
- return {
2903
- isFile: stat.isFile,
2904
- isDirectory: stat.isDirectory,
2905
- isSymlink: stat.isSymlink,
2906
- size: stat.size,
2907
- mtime: stat.mtime
2908
- };
2909
- }
2910
- async mkdir(path, options) {
2911
- await Deno.mkdir(path, { recursive: options?.recursive ?? false });
2912
- }
2913
- async *readDir(path) {
2914
- for await (const entry of Deno.readDir(path)) {
2915
- yield {
2916
- name: entry.name,
2917
- isFile: entry.isFile,
2918
- isDirectory: entry.isDirectory
2919
- };
2920
- }
2921
- }
2922
- async remove(path, options) {
2923
- await Deno.remove(path, { recursive: options?.recursive ?? false });
2924
- }
2925
- async makeTempDir(options) {
2926
- return await Deno.makeTempDir({ prefix: options?.prefix });
2927
- }
2928
- };
2929
- function createFileSystem() {
2930
- if (isDeno) {
2931
- return new DenoFileSystem();
2932
- } else {
2933
- return new NodeFileSystem();
2934
- }
2935
- }
2936
-
2937
3150
  // src/platform/compat/path-helper.ts
2938
3151
  import nodePath from "node:path";
2939
3152
  var pathMod = null;
@@ -2949,7 +3162,7 @@ function getPathMod() {
2949
3162
  return pathMod;
2950
3163
  return nodePath;
2951
3164
  }
2952
- var dirname = (path) => getPathMod().dirname(path);
3165
+ var dirname2 = (path) => getPathMod().dirname(path);
2953
3166
  var join2 = (...paths) => getPathMod().join(...paths);
2954
3167
  var resolve = (...paths) => getPathMod().resolve(...paths);
2955
3168
  var extname = (path) => getPathMod().extname(path);
@@ -2996,7 +3209,7 @@ function createFsAdapterPlugin(fsAdapter) {
2996
3209
  build.onResolve(
2997
3210
  { filter: /^\.\.?\// },
2998
3211
  async (args) => {
2999
- const importerDir = args.importer ? dirname(args.importer) : args.resolveDir;
3212
+ const importerDir = args.importer ? dirname2(args.importer) : args.resolveDir;
3000
3213
  const basePath = resolve(importerDir, args.path);
3001
3214
  const resolvedPath = await resolveWithExtensions(basePath);
3002
3215
  if (resolvedPath) {
@@ -3023,7 +3236,7 @@ function createFsAdapterPlugin(fsAdapter) {
3023
3236
  contents: content,
3024
3237
  loader,
3025
3238
  // Set resolveDir for nested imports from this file
3026
- resolveDir: dirname(args.path)
3239
+ resolveDir: dirname2(args.path)
3027
3240
  };
3028
3241
  } catch (error) {
3029
3242
  return {
@@ -3058,7 +3271,7 @@ async function importModule(file, context) {
3058
3271
  const isJsx = filePath.endsWith(".jsx");
3059
3272
  const loader = isTsx ? "tsx" : isJsx ? "jsx" : filePath.endsWith(".ts") ? "ts" : "js";
3060
3273
  const { build } = await import("esbuild");
3061
- const fileDir = dirname(filePath);
3274
+ const fileDir = dirname2(filePath);
3062
3275
  const relativeImports = [];
3063
3276
  if (isDeno) {
3064
3277
  const relativeImportPattern = /from\s+["'](\.\.[^"']+)["']/g;
@@ -3812,6 +4025,9 @@ var InMemoryBundleManifestStore = class {
3812
4025
  };
3813
4026
  var manifestStore = new InMemoryBundleManifestStore();
3814
4027
 
4028
+ // src/core/utils/perf-timer.ts
4029
+ var enabled = typeof process !== "undefined" ? process.env?.VERYFRONT_PERF === "1" : typeof Deno !== "undefined" ? Deno.env.get("VERYFRONT_PERF") === "1" : false;
4030
+
3815
4031
  // src/observability/tracing/config.ts
3816
4032
  var DEFAULT_CONFIG2 = {
3817
4033
  enabled: false,
@@ -4106,7 +4322,27 @@ async function withSpan(name, fn, options = {}) {
4106
4322
  );
4107
4323
  }
4108
4324
 
4109
- // src/ai/agent/runtime.ts
4325
+ // src/ai/config/defaults.ts
4326
+ var AGENT_DEFAULTS = {
4327
+ /** Maximum tokens for completion */
4328
+ maxTokens: 4096,
4329
+ /** Default temperature for generation */
4330
+ temperature: 0.7,
4331
+ /** Maximum agent loop steps */
4332
+ maxSteps: 20,
4333
+ /** Default memory type */
4334
+ memoryType: "conversation",
4335
+ /** Default memory max tokens */
4336
+ memoryMaxTokens: 4e3
4337
+ };
4338
+ var STREAMING_DEFAULTS = {
4339
+ /** Maximum buffer size for streaming (1MB) */
4340
+ maxBufferSize: 1024 * 1024,
4341
+ /** Chunk size for stream processing */
4342
+ chunkSize: 16384
4343
+ };
4344
+
4345
+ // src/ai/agent/streaming/stream-events.ts
4110
4346
  import { z as z6 } from "zod";
4111
4347
  var AgentStreamEventSchema = z6.discriminatedUnion("type", [
4112
4348
  z6.object({
@@ -4146,8 +4382,8 @@ var AgentStreamEventSchema = z6.discriminatedUnion("type", [
4146
4382
  })
4147
4383
  })
4148
4384
  ]);
4149
- var DEFAULT_MAX_TOKENS = 4096;
4150
- var DEFAULT_TEMPERATURE = 0.7;
4385
+
4386
+ // src/ai/agent/message-converter.ts
4151
4387
  function convertMessageToProvider(msg) {
4152
4388
  const content = getTextFromParts(msg.parts);
4153
4389
  const providerMsg = {
@@ -4177,6 +4413,11 @@ function convertMessageToProvider(msg) {
4177
4413
  }
4178
4414
  return providerMsg;
4179
4415
  }
4416
+
4417
+ // src/ai/agent/runtime.ts
4418
+ var DEFAULT_MAX_TOKENS = AGENT_DEFAULTS.maxTokens;
4419
+ var DEFAULT_TEMPERATURE = AGENT_DEFAULTS.temperature;
4420
+ var MAX_STREAM_BUFFER_SIZE = STREAMING_DEFAULTS.maxBufferSize;
4180
4421
  var AgentRuntime = class {
4181
4422
  id;
4182
4423
  config;
@@ -4610,6 +4851,10 @@ var AgentRuntime = class {
4610
4851
  if (done)
4611
4852
  break;
4612
4853
  partial += decoder.decode(value, { stream: true });
4854
+ if (partial.length > MAX_STREAM_BUFFER_SIZE) {
4855
+ serverLogger.warn("[AGENT] Stream buffer exceeded max size, truncating");
4856
+ partial = partial.slice(-MAX_STREAM_BUFFER_SIZE / 2);
4857
+ }
4613
4858
  const segments = partial.split("\n");
4614
4859
  partial = segments.pop() ?? "";
4615
4860
  const lines = segments.filter((line) => line.trim());
@@ -6944,9 +7189,16 @@ var DAGExecutor = class {
6944
7189
  context,
6945
7190
  nodeStates
6946
7191
  );
7192
+ case "loop":
7193
+ return await this.executeLoopNode(
7194
+ node,
7195
+ config,
7196
+ context,
7197
+ nodeStates
7198
+ );
6947
7199
  default:
6948
7200
  throw new Error(
6949
- `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow`
7201
+ `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow, loop`
6950
7202
  );
6951
7203
  }
6952
7204
  }
@@ -7104,6 +7356,155 @@ var DAGExecutor = class {
7104
7356
  waiting: result.waiting
7105
7357
  };
7106
7358
  }
7359
+ /**
7360
+ * Execute a loop node
7361
+ */
7362
+ async executeLoopNode(node, config, context, nodeStates) {
7363
+ const startTime = Date.now();
7364
+ const previousResults = [];
7365
+ let iteration = 0;
7366
+ let exitReason = "condition";
7367
+ let lastError;
7368
+ const existingLoopState = context[`${node.id}_loop_state`];
7369
+ if (existingLoopState) {
7370
+ iteration = existingLoopState.iteration;
7371
+ previousResults.push(...existingLoopState.previousResults);
7372
+ }
7373
+ while (iteration < config.maxIterations) {
7374
+ const loopContext = {
7375
+ iteration,
7376
+ totalIterations: iteration,
7377
+ previousResults: [...previousResults],
7378
+ isFirstIteration: iteration === 0,
7379
+ isLastAllowedIteration: iteration === config.maxIterations - 1
7380
+ };
7381
+ const shouldContinue = await config.while(context, loopContext);
7382
+ if (!shouldContinue) {
7383
+ exitReason = "condition";
7384
+ break;
7385
+ }
7386
+ const steps = typeof config.steps === "function" ? config.steps(context, loopContext) : config.steps;
7387
+ const result = await this.execute(steps, {
7388
+ id: `${node.id}_iter_${iteration}`,
7389
+ workflowId: "",
7390
+ status: "running",
7391
+ input: context.input,
7392
+ nodeStates: {},
7393
+ currentNodes: [],
7394
+ context: { ...context, _loop: loopContext },
7395
+ checkpoints: [],
7396
+ pendingApprovals: [],
7397
+ createdAt: /* @__PURE__ */ new Date()
7398
+ });
7399
+ if (result.waiting) {
7400
+ Object.assign(nodeStates, result.nodeStates);
7401
+ const state2 = {
7402
+ nodeId: node.id,
7403
+ status: "running",
7404
+ output: {
7405
+ iteration,
7406
+ waiting: true,
7407
+ previousResults
7408
+ },
7409
+ attempt: 1,
7410
+ startedAt: new Date(startTime)
7411
+ };
7412
+ return {
7413
+ state: state2,
7414
+ contextUpdates: {
7415
+ ...result.context,
7416
+ [`${node.id}_loop_state`]: { iteration, previousResults }
7417
+ },
7418
+ waiting: true
7419
+ };
7420
+ }
7421
+ if (result.error) {
7422
+ lastError = result.error;
7423
+ exitReason = "error";
7424
+ break;
7425
+ }
7426
+ previousResults.push(result.context);
7427
+ Object.assign(context, result.context);
7428
+ Object.assign(nodeStates, result.nodeStates);
7429
+ if (config.delay && iteration < config.maxIterations - 1) {
7430
+ const delayMs = typeof config.delay === "number" ? config.delay : this.parseDuration(config.delay);
7431
+ await this.sleep(delayMs);
7432
+ }
7433
+ iteration++;
7434
+ }
7435
+ if (iteration >= config.maxIterations && exitReason !== "condition") {
7436
+ exitReason = "maxIterations";
7437
+ }
7438
+ const finalLoopContext = {
7439
+ iteration,
7440
+ totalIterations: iteration,
7441
+ previousResults,
7442
+ isFirstIteration: false,
7443
+ isLastAllowedIteration: true
7444
+ };
7445
+ let completionUpdates = {};
7446
+ if (exitReason === "maxIterations" && config.onMaxIterations) {
7447
+ completionUpdates = await config.onMaxIterations(context, finalLoopContext);
7448
+ } else if (exitReason === "condition" && config.onComplete) {
7449
+ completionUpdates = await config.onComplete(context, finalLoopContext);
7450
+ }
7451
+ const output = {
7452
+ exitReason,
7453
+ iterations: iteration,
7454
+ previousResults,
7455
+ ...completionUpdates
7456
+ };
7457
+ const state = {
7458
+ nodeId: node.id,
7459
+ status: exitReason === "error" ? "failed" : "completed",
7460
+ output,
7461
+ error: lastError,
7462
+ attempt: 1,
7463
+ startedAt: new Date(startTime),
7464
+ completedAt: /* @__PURE__ */ new Date()
7465
+ };
7466
+ this.config.onNodeComplete?.(node.id, state);
7467
+ return {
7468
+ state,
7469
+ contextUpdates: {
7470
+ [node.id]: output,
7471
+ ...completionUpdates
7472
+ },
7473
+ waiting: false
7474
+ };
7475
+ }
7476
+ /**
7477
+ * Parse duration string to milliseconds
7478
+ */
7479
+ parseDuration(duration) {
7480
+ if (typeof duration === "number")
7481
+ return duration;
7482
+ const match = duration.match(/^(\d+)(ms|s|m|h|d)$/);
7483
+ if (!match)
7484
+ return 0;
7485
+ const value = parseInt(match[1], 10);
7486
+ const unit = match[2];
7487
+ switch (unit) {
7488
+ case "ms":
7489
+ return value;
7490
+ case "s":
7491
+ return value * 1e3;
7492
+ case "m":
7493
+ return value * 60 * 1e3;
7494
+ case "h":
7495
+ return value * 60 * 60 * 1e3;
7496
+ case "d":
7497
+ return value * 24 * 60 * 60 * 1e3;
7498
+ default:
7499
+ return 0;
7500
+ }
7501
+ }
7502
+ /**
7503
+ * Sleep for specified milliseconds
7504
+ */
7505
+ sleep(ms) {
7506
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7507
+ }
7107
7508
  /**
7108
7509
  * Execute a step node
7109
7510
  */
@@ -7499,6 +7900,12 @@ var CheckpointManager = class {
7499
7900
  };
7500
7901
 
7501
7902
  // src/ai/workflow/executor/step-executor.ts
7903
+ var DEFAULT_RETRY = {
7904
+ maxAttempts: 1,
7905
+ backoff: "exponential",
7906
+ initialDelay: 1e3,
7907
+ maxDelay: 3e4
7908
+ };
7502
7909
  var DEFAULT_STEP_TIMEOUT_MS = 5 * 60 * 1e3;
7503
7910
  var StepExecutor = class {
7504
7911
  config;
@@ -7509,7 +7916,7 @@ var StepExecutor = class {
7509
7916
  };
7510
7917
  }
7511
7918
  /**
7512
- * Execute a step node
7919
+ * Execute a step node with retry support
7513
7920
  */
7514
7921
  async execute(node, context) {
7515
7922
  const startTime = Date.now();
@@ -7519,30 +7926,96 @@ var StepExecutor = class {
7519
7926
  `StepExecutor can only execute 'step' nodes, but node "${node.id}" has type '${config.type}'. This is likely a bug in the DAG executor routing.`
7520
7927
  );
7521
7928
  }
7522
- try {
7523
- const resolvedInput = await this.resolveInput(config.input, context);
7524
- this.config.onStepStart?.(node.id, resolvedInput);
7525
- const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
7526
- const output = await this.executeWithTimeout(
7527
- () => this.executeStep(config, resolvedInput, context),
7528
- timeout,
7529
- node.id
7530
- );
7531
- this.config.onStepComplete?.(node.id, output);
7532
- return {
7533
- success: true,
7534
- output,
7535
- executionTime: Date.now() - startTime
7536
- };
7537
- } catch (error) {
7538
- const errorMessage = error instanceof Error ? error.message : String(error);
7539
- this.config.onStepError?.(node.id, error);
7540
- return {
7541
- success: false,
7542
- error: errorMessage,
7543
- executionTime: Date.now() - startTime
7544
- };
7929
+ const retryConfig = { ...DEFAULT_RETRY, ...config.retry };
7930
+ const maxAttempts = retryConfig.maxAttempts ?? 1;
7931
+ let lastError;
7932
+ let attempt = 0;
7933
+ while (attempt < maxAttempts) {
7934
+ attempt++;
7935
+ try {
7936
+ const resolvedInput = await this.resolveInput(config.input, context);
7937
+ this.config.onStepStart?.(node.id, resolvedInput);
7938
+ const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
7939
+ const output = await this.executeWithTimeout(
7940
+ () => this.executeStep(config, resolvedInput, context),
7941
+ timeout,
7942
+ node.id
7943
+ );
7944
+ this.config.onStepComplete?.(node.id, output);
7945
+ return {
7946
+ success: true,
7947
+ output,
7948
+ executionTime: Date.now() - startTime
7949
+ };
7950
+ } catch (error) {
7951
+ lastError = error instanceof Error ? error : new Error(String(error));
7952
+ const shouldRetry = attempt < maxAttempts && this.isRetryableError(lastError, retryConfig);
7953
+ if (shouldRetry) {
7954
+ const delay2 = this.calculateRetryDelay(attempt, retryConfig);
7955
+ await this.sleep(delay2);
7956
+ continue;
7957
+ }
7958
+ this.config.onStepError?.(node.id, lastError);
7959
+ return {
7960
+ success: false,
7961
+ error: lastError.message,
7962
+ executionTime: Date.now() - startTime
7963
+ };
7964
+ }
7545
7965
  }
7966
+ return {
7967
+ success: false,
7968
+ error: lastError?.message ?? "Unknown error",
7969
+ executionTime: Date.now() - startTime
7970
+ };
7971
+ }
7972
+ /**
7973
+ * Check if error is retryable
7974
+ */
7975
+ isRetryableError(error, config) {
7976
+ if (config.retryIf) {
7977
+ return config.retryIf(error);
7978
+ }
7979
+ const retryablePatterns = [
7980
+ /timeout/i,
7981
+ /ECONNRESET/i,
7982
+ /ECONNREFUSED/i,
7983
+ /ETIMEDOUT/i,
7984
+ /rate limit/i,
7985
+ /429/,
7986
+ /503/,
7987
+ /502/
7988
+ ];
7989
+ return retryablePatterns.some((pattern) => pattern.test(error.message));
7990
+ }
7991
+ /**
7992
+ * Calculate retry delay based on backoff strategy
7993
+ */
7994
+ calculateRetryDelay(attempt, config) {
7995
+ const initialDelay = config.initialDelay ?? 1e3;
7996
+ const maxDelay = config.maxDelay ?? 3e4;
7997
+ let delay2;
7998
+ switch (config.backoff) {
7999
+ case "exponential":
8000
+ delay2 = initialDelay * Math.pow(2, attempt - 1);
8001
+ break;
8002
+ case "linear":
8003
+ delay2 = initialDelay * attempt;
8004
+ break;
8005
+ case "fixed":
8006
+ default:
8007
+ delay2 = initialDelay;
8008
+ break;
8009
+ }
8010
+ const jitter = delay2 * 0.1 * (Math.random() * 2 - 1);
8011
+ delay2 = Math.min(delay2 + jitter, maxDelay);
8012
+ return Math.floor(delay2);
8013
+ }
8014
+ /**
8015
+ * Sleep for specified milliseconds
8016
+ */
8017
+ sleep(ms) {
8018
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7546
8019
  }
7547
8020
  /**
7548
8021
  * Resolve step input from context
@@ -8361,7 +8834,35 @@ var WorkflowClient = class {
8361
8834
  this.executor = new WorkflowExecutor({
8362
8835
  backend: this.backend,
8363
8836
  debug: this.debug,
8364
- ...config.executor
8837
+ ...config.executor,
8838
+ onWaiting: async (run, nodeId) => {
8839
+ const nodeState = run.nodeStates[nodeId];
8840
+ if (!nodeState?.input) {
8841
+ if (this.debug) {
8842
+ console.log(`[WorkflowClient] No wait config found for node: ${nodeId}`);
8843
+ }
8844
+ return;
8845
+ }
8846
+ const input = nodeState.input;
8847
+ if (input.type !== "approval") {
8848
+ return;
8849
+ }
8850
+ const waitConfig = {
8851
+ type: "wait",
8852
+ waitType: "approval",
8853
+ message: input.message,
8854
+ payload: input.payload
8855
+ };
8856
+ try {
8857
+ await this.approvalManager.createApproval(run, nodeId, waitConfig, run.context);
8858
+ if (this.debug) {
8859
+ console.log(`[WorkflowClient] Created approval for node: ${nodeId}`);
8860
+ }
8861
+ } catch (error) {
8862
+ console.error(`[WorkflowClient] Failed to create approval:`, error);
8863
+ }
8864
+ config.executor?.onWaiting?.(run, nodeId);
8865
+ }
8365
8866
  });
8366
8867
  this.approvalManager = new ApprovalManager({
8367
8868
  backend: this.backend,