veryfront 0.1.157 → 0.1.161

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.
Files changed (39) hide show
  1. package/esm/cli/mcp/remote-file-tools.d.ts.map +1 -1
  2. package/esm/cli/mcp/remote-file-tools.js +16 -9
  3. package/esm/deno.js +1 -1
  4. package/esm/src/agent/human-input.d.ts +298 -0
  5. package/esm/src/agent/human-input.d.ts.map +1 -0
  6. package/esm/src/agent/human-input.js +112 -0
  7. package/esm/src/agent/index.d.ts +2 -1
  8. package/esm/src/agent/index.d.ts.map +1 -1
  9. package/esm/src/agent/index.js +1 -0
  10. package/esm/src/agent/runtime/index.d.ts +2 -12
  11. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  12. package/esm/src/agent/runtime/index.js +62 -25
  13. package/esm/src/agent/types.d.ts +37 -0
  14. package/esm/src/agent/types.d.ts.map +1 -1
  15. package/esm/src/mcp/http-transport.d.ts +33 -0
  16. package/esm/src/mcp/http-transport.d.ts.map +1 -0
  17. package/esm/src/mcp/http-transport.js +97 -0
  18. package/esm/src/mcp/server.d.ts +2 -1
  19. package/esm/src/mcp/server.d.ts.map +1 -1
  20. package/esm/src/mcp/server.js +20 -106
  21. package/esm/src/platform/adapters/fs/veryfront/stat-operations.d.ts.map +1 -1
  22. package/esm/src/platform/adapters/fs/veryfront/stat-operations.js +13 -45
  23. package/esm/src/routing/api/module-loader/loader.d.ts +7 -0
  24. package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
  25. package/esm/src/routing/api/module-loader/loader.js +104 -94
  26. package/esm/src/utils/version-constant.d.ts +1 -1
  27. package/esm/src/utils/version-constant.js +1 -1
  28. package/package.json +1 -1
  29. package/src/cli/mcp/remote-file-tools.ts +24 -13
  30. package/src/deno.js +1 -1
  31. package/src/src/agent/human-input.ts +152 -0
  32. package/src/src/agent/index.ts +24 -0
  33. package/src/src/agent/runtime/index.ts +118 -11
  34. package/src/src/agent/types.ts +47 -0
  35. package/src/src/mcp/http-transport.ts +163 -0
  36. package/src/src/mcp/server.ts +21 -120
  37. package/src/src/platform/adapters/fs/veryfront/stat-operations.ts +19 -49
  38. package/src/src/routing/api/module-loader/loader.ts +127 -100
  39. package/src/src/utils/version-constant.ts +1 -1
@@ -255,7 +255,7 @@ export class StatOperations extends VeryfrontOperationsBase {
255
255
  this.cache.set(cacheKey, files);
256
256
  return files;
257
257
  }
258
- buildResolveSearchPatterns(normalizedPath, options) {
258
+ buildResolveSearchPatterns(normalizedPath, options, knownExtensionFallback = "exact") {
259
259
  const patterns = new Set();
260
260
  const pathWithoutExt = stripKnownExtension(normalizedPath, EXTENSION_PRIORITY);
261
261
  const allowPagesPrefix = options?.allowPagesPrefix !== false;
@@ -264,7 +264,7 @@ export class StatOperations extends VeryfrontOperationsBase {
264
264
  patterns.add(pattern);
265
265
  };
266
266
  if (EXTENSION_PRIORITY.some((ext) => normalizedPath.endsWith(ext))) {
267
- addPattern(normalizedPath);
267
+ addPattern(knownExtensionFallback === "wildcard" ? `${pathWithoutExt}.*` : normalizedPath);
268
268
  return [...patterns];
269
269
  }
270
270
  addPattern(`${pathWithoutExt}.*`);
@@ -282,7 +282,7 @@ export class StatOperations extends VeryfrontOperationsBase {
282
282
  path: normalizeIndexedFilePath(match).normalizedPath,
283
283
  }));
284
284
  }
285
- async tryResolveViaApiSearch(normalizedPath, options) {
285
+ async tryResolveViaApiSearch(normalizedPath, options, knownExtensionFallback = "exact") {
286
286
  if (isFrameworkSourcePath(normalizedPath)) {
287
287
  logger.debug("Skipping API search for framework path", { normalizedPath });
288
288
  return null;
@@ -291,7 +291,7 @@ export class StatOperations extends VeryfrontOperationsBase {
291
291
  logger.warn("API search circuit breaker open, skipping", { normalizedPath });
292
292
  return undefined;
293
293
  }
294
- const patterns = this.buildResolveSearchPatterns(normalizedPath, options);
294
+ const patterns = this.buildResolveSearchPatterns(normalizedPath, options, knownExtensionFallback);
295
295
  let sawSuccessfulSearch = false;
296
296
  for (const pattern of patterns) {
297
297
  try {
@@ -459,49 +459,17 @@ export class StatOperations extends VeryfrontOperationsBase {
459
459
  logger.debug("Skipping API search for framework path", { normalizedPath });
460
460
  return null;
461
461
  }
462
- // NOTE: Removed optimization that skipped API search for paths with extensions.
463
- // This was causing layout files and other project files to not be found when
464
- // they were missing from the file index (due to cache issues, incomplete fetch, etc.).
465
- // The API pattern search is the fallback to ensure files can still be found.
466
- if (!this.apiSearchCircuitBreaker.canSearch()) {
467
- logger.warn("API search circuit breaker open, skipping", { normalizedPath });
468
- return null;
469
- }
470
- const searchPattern = `${pathWithoutExt}.*`;
471
- logger.debug("Searching for file via API", {
472
- pattern: searchPattern,
473
- normalizedPath,
474
- });
475
- try {
476
- const matches = await this.client.searchFiles(searchPattern);
477
- this.apiSearchCircuitBreaker.recordSuccess();
478
- logger.debug("API search result", {
479
- pattern: searchPattern,
480
- matchCount: matches.length,
481
- matches: matches.map((m) => m.path).slice(0, 5),
482
- });
483
- const sortedMatches = sortPathsByExtensionPriority(matches, EXTENSION_PRIORITY);
484
- const first = sortedMatches[0];
485
- if (first) {
486
- logger.debug("resolveFile found via API search", { path: first.path });
487
- this.cache.set(cacheKey, first.path);
488
- return first.path;
489
- }
462
+ // NOTE: Keep the post-index API fallback aligned with the pre-index helper for extensionless
463
+ // paths, while preserving the older wildcard sibling-extension lookup for known-extension
464
+ // paths. Incomplete file-list snapshots otherwise hide valid files until the cache refreshes.
465
+ const apiResolved = await this.tryResolveViaApiSearch(normalizedPath, options, "wildcard");
466
+ if (typeof apiResolved === "string") {
467
+ this.cache.set(cacheKey, apiResolved);
468
+ return apiResolved;
490
469
  }
491
- catch (error) {
492
- const result = this.apiSearchCircuitBreaker.recordFailure();
493
- if (result.tripped) {
494
- logger.warn("API search circuit breaker tripped", {
495
- failures: result.failures,
496
- });
497
- }
498
- logger.error("API pattern search failed", { pattern: searchPattern, error });
470
+ if (apiResolved === null) {
471
+ this.cache.set(cacheKey, NOT_FOUND_SENTINEL);
499
472
  }
500
- logger.debug("resolveFile not found after API search", {
501
- normalizedPath,
502
- pathWithoutExt,
503
- });
504
- this.cache.set(cacheKey, NOT_FOUND_SENTINEL);
505
473
  return null;
506
474
  }
507
475
  }
@@ -1,4 +1,11 @@
1
1
  import type { APIRoute, LoadModuleOptions } from "./types.js";
2
+ import type { FileSystem } from "../../../platform/compat/fs.js";
2
3
  export declare function toCjsDestructureBindings(bindings: string): string;
3
4
  export declare function loadHandlerModule(options: LoadModuleOptions): Promise<APIRoute | null>;
5
+ export declare function getNodeExternalPackagesToResolve(userDeps: Map<string, string>): string[];
6
+ export declare function resolveNodePackageToFileUrl(projectDir: string, packageName: string, fs: FileSystem, pathToFileURL: typeof import("node:url").pathToFileURL): Promise<string | null>;
7
+ export declare function loadVeryfrontExportsMap(projectDir: string, fs: FileSystem): Promise<Record<string, {
8
+ import?: string;
9
+ }>>;
10
+ export declare function rewriteNodeExternalImports(code: string, projectDir: string, fs: FileSystem, userDeps: Map<string, string>): Promise<string>;
4
11
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgL9D,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAejE;AAgBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyBtF"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAI9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AA4KjE,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAejE;AAgBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyBtF;AAuaD,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAUxF;AAED,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,cAAc,UAAU,EAAE,aAAa,GACrD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBxB;AAED,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAW9C;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CA6EjB"}
@@ -512,105 +512,115 @@ async function loadModuleFromCode(code, projectDir, fs, userDeps = new Map()) {
512
512
  await fs.remove(tempDir, { recursive: true });
513
513
  }
514
514
  }
515
- async function rewriteExternalImports(code, projectDir, fs, userDeps = new Map()) {
515
+ export function getNodeExternalPackagesToResolve(userDeps) {
516
+ const externalPackagesToResolve = ["zod"];
517
+ for (const name of userDeps.keys()) {
518
+ if (!externalPackagesToResolve.includes(name)) {
519
+ externalPackagesToResolve.push(name);
520
+ }
521
+ }
522
+ return externalPackagesToResolve;
523
+ }
524
+ export async function resolveNodePackageToFileUrl(projectDir, packageName, fs, pathToFileURL) {
525
+ const packagePath = pathHelper.join(projectDir, "node_modules", packageName);
526
+ const packageJsonPath = pathHelper.join(packagePath, "package.json");
527
+ try {
528
+ const pkgJson = JSON.parse(await fs.readTextFile(packageJsonPath));
529
+ let entryPoint;
530
+ if (pkgJson.exports) {
531
+ entryPoint = resolveExportEntry(pkgJson.exports["."]);
532
+ }
533
+ entryPoint ||= pkgJson.module || pkgJson.main || "index.js";
534
+ if (!entryPoint)
535
+ return null;
536
+ return pathToFileURL(pathHelper.join(packagePath, entryPoint)).href;
537
+ }
538
+ catch (_) {
539
+ /* expected: package.json may not exist or be invalid */
540
+ return null;
541
+ }
542
+ }
543
+ export async function loadVeryfrontExportsMap(projectDir, fs) {
544
+ const vfPackagePath = pathHelper.join(projectDir, "node_modules", "veryfront");
545
+ const vfPackageJsonPath = pathHelper.join(vfPackagePath, "package.json");
546
+ try {
547
+ const pkgJson = JSON.parse(await fs.readTextFile(vfPackageJsonPath));
548
+ return pkgJson.exports || {};
549
+ }
550
+ catch (_error) {
551
+ logger.debug("Could not read veryfront package.json");
552
+ return {};
553
+ }
554
+ }
555
+ export async function rewriteNodeExternalImports(code, projectDir, fs, userDeps) {
556
+ const { pathToFileURL } = await import("node:url");
516
557
  let transformed = code;
517
- if (isNode) {
518
- try {
519
- const { pathToFileURL } = await import("node:url");
520
- logger.debug(`Rewriting external imports for Node.js, projectDir: ${projectDir}`);
521
- const resolvePackageToFileUrl = async (packageName) => {
522
- const packagePath = pathHelper.join(projectDir, "node_modules", packageName);
523
- const packageJsonPath = pathHelper.join(packagePath, "package.json");
524
- try {
525
- const pkgJson = JSON.parse(await fs.readTextFile(packageJsonPath));
526
- let entryPoint;
527
- if (pkgJson.exports) {
528
- entryPoint = resolveExportEntry(pkgJson.exports["."]);
529
- }
530
- entryPoint ||= pkgJson.module || pkgJson.main || "index.js";
531
- if (!entryPoint)
532
- return null;
533
- return pathToFileURL(pathHelper.join(packagePath, entryPoint)).href;
534
- }
535
- catch (_) {
536
- /* expected: package.json may not exist or be invalid */
537
- return null;
538
- }
539
- };
540
- const externalPackagesToResolve = ["zod"];
541
- for (const name of userDeps.keys()) {
542
- if (!externalPackagesToResolve.includes(name)) {
543
- externalPackagesToResolve.push(name);
544
- }
545
- }
546
- for (const pkg of externalPackagesToResolve) {
547
- const escapedPkg = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
548
- // Match both exact imports (from "pkg") and subpath imports (from "pkg/sub")
549
- const staticImportRegex = new RegExp(`from\\s*["']${escapedPkg}(/[^"']*)?["']`, "g");
550
- const dynamicImportRegex = new RegExp(`import\\s*\\(\\s*["']${escapedPkg}(/[^"']*)?["']\\s*\\)`, "g");
551
- const needsStatic = staticImportRegex.test(transformed);
552
- staticImportRegex.lastIndex = 0;
553
- const needsDynamic = dynamicImportRegex.test(transformed);
554
- dynamicImportRegex.lastIndex = 0;
555
- if (!needsStatic && !needsDynamic)
556
- continue;
557
- const packageDir = pathToFileURL(pathHelper.join(projectDir, "node_modules", pkg)).href;
558
- const resolvedUrl = await resolvePackageToFileUrl(pkg);
559
- if (needsStatic) {
560
- transformed = transformed.replace(staticImportRegex, (_, subpath) => {
561
- if (subpath) {
562
- const subUrl = `${packageDir}${subpath}`;
563
- logger.debug(`Resolved ${pkg}${subpath} -> ${subUrl}`);
564
- return `from "${subUrl}"`;
565
- }
566
- if (!resolvedUrl)
567
- return `from "${pkg}"`;
568
- logger.debug(`Resolved ${pkg} -> ${resolvedUrl}`);
569
- return `from "${resolvedUrl}"`;
570
- });
571
- }
572
- if (needsDynamic) {
573
- transformed = transformed.replace(dynamicImportRegex, (_, subpath) => {
574
- if (subpath) {
575
- const subUrl = `${packageDir}${subpath}`;
576
- return `import("${subUrl}")`;
577
- }
578
- if (!resolvedUrl)
579
- return `import("${pkg}")`;
580
- return `import("${resolvedUrl}")`;
581
- });
582
- }
583
- }
584
- const vfPackagePath = pathHelper.join(projectDir, "node_modules", "veryfront");
585
- const vfPackageJsonPath = pathHelper.join(vfPackagePath, "package.json");
586
- let exportsMap = {};
587
- try {
588
- const pkgJson = JSON.parse(await fs.readTextFile(vfPackageJsonPath));
589
- exportsMap = pkgJson.exports || {};
590
- }
591
- catch (_error) {
592
- logger.debug(`Could not read veryfront package.json: `);
593
- }
594
- transformed = transformed.replace(/from\s+["'](veryfront\/[^"']+)["']/g, (match, fullSpecifier) => {
595
- const subpath = "./" + fullSpecifier.replace("veryfront/", "");
596
- const exportEntry = exportsMap[subpath];
597
- if (!exportEntry?.import) {
598
- logger.warn(`No export found for ${subpath}`);
599
- return match;
558
+ logger.debug(`Rewriting external imports for Node.js, projectDir: ${projectDir}`);
559
+ for (const pkg of getNodeExternalPackagesToResolve(userDeps)) {
560
+ const escapedPkg = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
561
+ const staticImportRegex = new RegExp(`from\\s*["']${escapedPkg}(/[^"']*)?["']`, "g");
562
+ const dynamicImportRegex = new RegExp(`import\\s*\\(\\s*["']${escapedPkg}(/[^"']*)?["']\\s*\\)`, "g");
563
+ const needsStatic = staticImportRegex.test(transformed);
564
+ staticImportRegex.lastIndex = 0;
565
+ const needsDynamic = dynamicImportRegex.test(transformed);
566
+ dynamicImportRegex.lastIndex = 0;
567
+ if (!needsStatic && !needsDynamic)
568
+ continue;
569
+ const packageDir = pathToFileURL(pathHelper.join(projectDir, "node_modules", pkg)).href;
570
+ const resolvedUrl = await resolveNodePackageToFileUrl(projectDir, pkg, fs, pathToFileURL);
571
+ if (needsStatic) {
572
+ transformed = transformed.replace(staticImportRegex, (_, subpath) => {
573
+ if (subpath) {
574
+ const subUrl = `${packageDir}${subpath}`;
575
+ logger.debug(`Resolved ${pkg}${subpath} -> ${subUrl}`);
576
+ return `from "${subUrl}"`;
600
577
  }
601
- const resolvedPath = pathHelper.join(vfPackagePath, exportEntry.import);
602
- logger.debug(`Resolved ${fullSpecifier} -> ${resolvedPath}`);
603
- return `from "${pathToFileURL(resolvedPath).href}"`;
578
+ if (!resolvedUrl)
579
+ return `from "${pkg}"`;
580
+ logger.debug(`Resolved ${pkg} -> ${resolvedUrl}`);
581
+ return `from "${resolvedUrl}"`;
604
582
  });
605
- transformed = transformed.replace(/from\s+["']veryfront["']/g, () => {
606
- const exportEntry = exportsMap["."];
607
- if (!exportEntry?.import)
608
- return 'from "veryfront"';
609
- const resolvedPath = pathHelper.join(vfPackagePath, exportEntry.import);
610
- logger.debug(`Resolved veryfront -> ${resolvedPath}`);
611
- return `from "${pathToFileURL(resolvedPath).href}"`;
583
+ }
584
+ if (needsDynamic) {
585
+ transformed = transformed.replace(dynamicImportRegex, (_, subpath) => {
586
+ if (subpath) {
587
+ return `import("${packageDir}${subpath}")`;
588
+ }
589
+ if (!resolvedUrl)
590
+ return `import("${pkg}")`;
591
+ return `import("${resolvedUrl}")`;
612
592
  });
613
593
  }
594
+ }
595
+ const vfPackagePath = pathHelper.join(projectDir, "node_modules", "veryfront");
596
+ const exportsMap = await loadVeryfrontExportsMap(projectDir, fs);
597
+ transformed = transformed.replace(/from\s+["'](veryfront\/[^"']+)["']/g, (match, fullSpecifier) => {
598
+ const subpath = "./" + fullSpecifier.replace("veryfront/", "");
599
+ const exportEntry = exportsMap[subpath];
600
+ if (!exportEntry?.import) {
601
+ logger.warn(`No export found for ${subpath}`);
602
+ return match;
603
+ }
604
+ const resolvedPath = pathHelper.join(vfPackagePath, exportEntry.import);
605
+ logger.debug(`Resolved ${fullSpecifier} -> ${resolvedPath}`);
606
+ return `from "${pathToFileURL(resolvedPath).href}"`;
607
+ });
608
+ transformed = transformed.replace(/from\s+["']veryfront["']/g, () => {
609
+ const exportEntry = exportsMap["."];
610
+ if (!exportEntry?.import)
611
+ return 'from "veryfront"';
612
+ const resolvedPath = pathHelper.join(vfPackagePath, exportEntry.import);
613
+ logger.debug(`Resolved veryfront -> ${resolvedPath}`);
614
+ return `from "${pathToFileURL(resolvedPath).href}"`;
615
+ });
616
+ return transformed;
617
+ }
618
+ async function rewriteExternalImports(code, projectDir, fs, userDeps = new Map()) {
619
+ let transformed = code;
620
+ if (isNode) {
621
+ try {
622
+ transformed = await rewriteNodeExternalImports(transformed, projectDir, fs, userDeps);
623
+ }
614
624
  catch (e) {
615
625
  logger.warn(`Failed to import node:module: ${e}`);
616
626
  }
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.157";
1
+ export declare const VERSION = "0.1.161";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.157";
3
+ export const VERSION = "0.1.161";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.157",
3
+ "version": "0.1.161",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -87,6 +87,15 @@ function getBranchParam(branch?: string): string {
87
87
  return branch ? `?branch_id=${branch}` : "";
88
88
  }
89
89
 
90
+ function buildProjectApiPath(project: string, resource: string, branch?: string): string {
91
+ const normalizedResource = resource.startsWith("/") ? resource.slice(1) : resource;
92
+ return `/${project}${getBranchPath(branch)}/${normalizedResource}`;
93
+ }
94
+
95
+ function buildProjectFilePath(project: string, filePath: string, branch?: string): string {
96
+ return buildProjectApiPath(project, `files/${encodeFilePath(filePath)}`, branch);
97
+ }
98
+
90
99
  interface RemoteFile {
91
100
  id?: string;
92
101
  path: string;
@@ -214,9 +223,9 @@ export const vfRemoteListFiles: MCPTool<RemoteListFilesInput, RemoteListFilesOut
214
223
  params.set("limit", String(input.limit));
215
224
  params.set("fields", "(path,type,size)");
216
225
 
217
- const apiPath = `/${input.project}${
218
- getBranchPath(input.branch)
219
- }/files?${params.toString()}`;
226
+ const apiPath = `${
227
+ buildProjectApiPath(input.project, "files", input.branch)
228
+ }?${params.toString()}`;
220
229
  const result = await apiRequest<FileListResponse>("GET", apiPath);
221
230
  if (!result.ok) return { success: false, error: result.error };
222
231
 
@@ -260,9 +269,7 @@ export const vfRemoteGetFile: MCPTool<RemoteGetFileInput, RemoteGetFileOutput> =
260
269
  withSpan(
261
270
  "cli.mcp.tool.vf_remote_get_file",
262
271
  async () => {
263
- const apiPath = `/${input.project}${getBranchPath(input.branch)}/files/${
264
- encodeFilePath(input.path)
265
- }`;
272
+ const apiPath = buildProjectFilePath(input.project, input.path, input.branch);
266
273
  const result = await apiRequest<RemoteFile>("GET", apiPath);
267
274
  if (!result.ok) return { success: false, error: result.error };
268
275
 
@@ -314,7 +321,7 @@ export const vfRemoteUpdateFile: MCPTool<RemoteUpdateFileInput, RemoteUpdateFile
314
321
  withSpan(
315
322
  "cli.mcp.tool.vf_remote_update_file",
316
323
  async () => {
317
- const apiPath = `/${input.project}/files/${encodeFilePath(input.path)}${
324
+ const apiPath = `${buildProjectFilePath(input.project, input.path)}${
318
325
  getBranchParam(input.branch)
319
326
  }`;
320
327
  const result = await apiRequest<{ id: string; path: string }>("PUT", apiPath, {
@@ -362,7 +369,7 @@ export const vfRemoteDeleteFile: MCPTool<RemoteDeleteFileInput, RemoteDeleteFile
362
369
  description: "Delete a file from a remote Veryfront project.",
363
370
  inputSchema: remoteDeleteFileInput,
364
371
  execute: async (input) => {
365
- const apiPath = `/${input.project}/files/${encodeFilePath(input.path)}${
372
+ const apiPath = `${buildProjectFilePath(input.project, input.path)}${
366
373
  getBranchParam(input.branch)
367
374
  }`;
368
375
  const result = await apiRequest<void>("DELETE", apiPath);
@@ -405,7 +412,7 @@ export const vfRemoteSearchFiles: MCPTool<RemoteSearchFilesInput, RemoteSearchFi
405
412
  withSpan(
406
413
  "cli.mcp.tool.vf_remote_search_files",
407
414
  async () => {
408
- const apiPath = `/${input.project}${getBranchPath(input.branch)}/files/search`;
415
+ const apiPath = buildProjectApiPath(input.project, "files/search", input.branch);
409
416
  const result = await apiRequest<SearchResponse>("POST", apiPath, {
410
417
  body: {
411
418
  query: input.query,
@@ -455,7 +462,11 @@ export const vfRemoteMoveFile: MCPTool<RemoteMoveFileInput, RemoteMoveFileOutput
455
462
  description: "Move or rename a file in a remote Veryfront project.",
456
463
  inputSchema: remoteMoveFileInput,
457
464
  execute: async (input) => {
458
- const apiPath = `/${input.project}/files/move${getBranchParam(input.branch)}`;
465
+ const apiPath = `${buildProjectApiPath(input.project, "files/move")}${
466
+ getBranchParam(
467
+ input.branch,
468
+ )
469
+ }`;
459
470
  const result = await apiRequest<{ source_path: string; destination_path: string }>(
460
471
  "POST",
461
472
  apiPath,
@@ -732,7 +743,7 @@ export const vfRemoteCloneProject: MCPTool<RemoteCloneProjectInput, RemoteCloneP
732
743
 
733
744
  const listResult = await apiRequest<FileListResponse>(
734
745
  "GET",
735
- `/${input.source_project}/files?${params.toString()}`,
746
+ `${buildProjectApiPath(input.source_project, "files")}?${params.toString()}`,
736
747
  );
737
748
 
738
749
  if (!listResult.ok) {
@@ -750,7 +761,7 @@ export const vfRemoteCloneProject: MCPTool<RemoteCloneProjectInput, RemoteCloneP
750
761
  for (const file of sourceFiles) {
751
762
  const getResult = await apiRequest<RemoteFile>(
752
763
  "GET",
753
- `/${input.source_project}/files/${encodeFilePath(file.path)}`,
764
+ buildProjectFilePath(input.source_project, file.path),
754
765
  );
755
766
 
756
767
  if (!getResult.ok || !getResult.data) {
@@ -760,7 +771,7 @@ export const vfRemoteCloneProject: MCPTool<RemoteCloneProjectInput, RemoteCloneP
760
771
 
761
772
  const createFileResult = await apiRequest<{ id: string; path: string }>(
762
773
  "PUT",
763
- `/${createResult.slug}/files/${encodeFilePath(file.path)}`,
774
+ buildProjectFilePath(createResult.slug, file.path),
764
775
  { body: { content: getResult.data.content } },
765
776
  );
766
777
 
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.157",
3
+ "version": "0.1.161",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -0,0 +1,152 @@
1
+ import { z } from "zod";
2
+ import { AgUiRuntimeRunIdSchema } from "./runtime-ag-ui-contract.js";
3
+ import { RunResumeSessionManager } from "./runtime/index.js";
4
+
5
+ const TOOL_CALL_ID_SCHEMA = z.string().min(1).max(128);
6
+
7
+ export const HumanInputOptionSchema = z.object({
8
+ value: z.string(),
9
+ label: z.string(),
10
+ description: z.string().optional(),
11
+ recommended: z.boolean().optional(),
12
+ });
13
+
14
+ const BaseHumanInputFieldSchema = z.object({
15
+ name: z.string().min(1).max(128),
16
+ label: z.string().min(1).max(256),
17
+ description: z.string().max(1024).optional(),
18
+ required: z.boolean().optional().default(false),
19
+ secret: z.boolean().optional().default(false),
20
+ });
21
+
22
+ export const HumanInputFieldSchema = z.discriminatedUnion("type", [
23
+ BaseHumanInputFieldSchema.extend({
24
+ type: z.enum(["text", "email", "url", "password", "number"]),
25
+ placeholder: z.string().max(512).optional(),
26
+ defaultValue: z.string().optional(),
27
+ pattern: z.string().max(512).optional(),
28
+ minLength: z.number().int().nonnegative().optional(),
29
+ maxLength: z.number().int().positive().optional(),
30
+ min: z.number().optional(),
31
+ max: z.number().optional(),
32
+ }),
33
+ BaseHumanInputFieldSchema.extend({
34
+ type: z.literal("textarea"),
35
+ placeholder: z.string().max(512).optional(),
36
+ defaultValue: z.string().optional(),
37
+ minLength: z.number().int().nonnegative().optional(),
38
+ maxLength: z.number().int().positive().optional(),
39
+ rows: z.number().int().positive().optional().default(3),
40
+ }),
41
+ BaseHumanInputFieldSchema.extend({
42
+ type: z.literal("select"),
43
+ options: z.array(HumanInputOptionSchema).min(1),
44
+ defaultValue: z.string().optional(),
45
+ placeholder: z.string().max(512).optional(),
46
+ }),
47
+ BaseHumanInputFieldSchema.extend({
48
+ type: z.literal("checkbox"),
49
+ defaultValue: z.boolean().optional().default(false),
50
+ }),
51
+ BaseHumanInputFieldSchema.extend({
52
+ type: z.literal("radio"),
53
+ options: z.array(HumanInputOptionSchema).min(1),
54
+ defaultValue: z.string().optional(),
55
+ }),
56
+ BaseHumanInputFieldSchema.extend({
57
+ type: z.literal("confirm"),
58
+ confirmLabel: z.string().max(64).optional().default("Yes"),
59
+ denyLabel: z.string().max(64).optional().default("No"),
60
+ }),
61
+ ]);
62
+
63
+ export const HumanInputRequestSchema = z.object({
64
+ title: z.string().min(1).max(256),
65
+ description: z.string().max(2048).optional(),
66
+ fields: z.array(HumanInputFieldSchema).min(1),
67
+ submitLabel: z.string().max(64).optional().default("Submit"),
68
+ metadata: z.record(z.string(), z.unknown()).optional(),
69
+ });
70
+
71
+ export const HumanInputResponseValuesSchema = z.record(
72
+ z.string(),
73
+ z.union([z.string(), z.boolean(), z.number(), z.null()]),
74
+ );
75
+
76
+ export const HumanInputResultSchema = z.discriminatedUnion("submitted", [
77
+ z.object({
78
+ submitted: z.literal(true),
79
+ values: HumanInputResponseValuesSchema,
80
+ }),
81
+ z.object({
82
+ submitted: z.literal(false),
83
+ values: HumanInputResponseValuesSchema.default({}),
84
+ }),
85
+ ]);
86
+
87
+ export const HumanInputPendingRequestSchema = z.object({
88
+ runId: AgUiRuntimeRunIdSchema,
89
+ toolCallId: TOOL_CALL_ID_SCHEMA,
90
+ request: HumanInputRequestSchema,
91
+ });
92
+
93
+ export type HumanInputOption = z.infer<typeof HumanInputOptionSchema>;
94
+ export type HumanInputField = z.infer<typeof HumanInputFieldSchema>;
95
+ export type HumanInputFieldInput = z.input<typeof HumanInputFieldSchema>;
96
+ export type HumanInputRequest = z.infer<typeof HumanInputRequestSchema>;
97
+ export type HumanInputRequestInput = z.input<typeof HumanInputRequestSchema>;
98
+ export type HumanInputResult = z.infer<typeof HumanInputResultSchema>;
99
+ export type HumanInputPendingRequest = z.infer<typeof HumanInputPendingRequestSchema>;
100
+
101
+ type HumanInputResumeValue = {
102
+ result: unknown;
103
+ isError: boolean;
104
+ };
105
+
106
+ export interface WaitForHumanInputOptions {
107
+ sessionManager: RunResumeSessionManager<HumanInputResumeValue>;
108
+ runId: string;
109
+ toolCallId: string;
110
+ request: HumanInputRequestInput;
111
+ onRequest?: ((request: HumanInputPendingRequest) => void | Promise<void>) | undefined;
112
+ }
113
+
114
+ export class HumanInputResumeError extends Error {
115
+ constructor(readonly detail: unknown) {
116
+ super(
117
+ typeof detail === "string" ? detail : "Human input resume failed",
118
+ );
119
+ this.name = "HumanInputResumeError";
120
+ }
121
+ }
122
+
123
+ export class InvalidHumanInputResultError extends Error {
124
+ constructor(readonly detail: z.ZodIssue[]) {
125
+ super("Invalid human input resume payload");
126
+ this.name = "InvalidHumanInputResultError";
127
+ }
128
+ }
129
+
130
+ export async function waitForHumanInput(
131
+ options: WaitForHumanInputOptions,
132
+ ): Promise<HumanInputResult> {
133
+ const pendingRequest = HumanInputPendingRequestSchema.parse({
134
+ runId: options.runId,
135
+ toolCallId: options.toolCallId,
136
+ request: options.request,
137
+ });
138
+
139
+ await options.onRequest?.(pendingRequest);
140
+
141
+ const resumed = await options.sessionManager.waitForSignal(options.runId, options.toolCallId);
142
+ if (resumed.isError) {
143
+ throw new HumanInputResumeError(resumed.result);
144
+ }
145
+
146
+ const parsed = HumanInputResultSchema.safeParse(resumed.result);
147
+ if (!parsed.success) {
148
+ throw new InvalidHumanInputResultError(parsed.error.issues);
149
+ }
150
+
151
+ return parsed.data;
152
+ }
@@ -97,7 +97,13 @@ export type {
97
97
  MessagePart,
98
98
  ModelProvider,
99
99
  ModelString,
100
+ ModelTransportRequest,
101
+ ModelTransportResolver,
100
102
  ResolvedAgentConfig,
103
+ ResolvedModelTransport,
104
+ ResolvedRuntimeState,
105
+ RuntimeStateRequest,
106
+ RuntimeStateResolver,
101
107
  StreamToolCall,
102
108
  ToolCall,
103
109
  ToolCallPart,
@@ -162,6 +168,24 @@ export {
162
168
  AgUiRequestSchema,
163
169
  createAgUiHandler,
164
170
  } from "./ag-ui-handler.js";
171
+ export {
172
+ type HumanInputField,
173
+ type HumanInputFieldInput,
174
+ HumanInputFieldSchema,
175
+ type HumanInputOption,
176
+ HumanInputOptionSchema,
177
+ type HumanInputPendingRequest,
178
+ HumanInputPendingRequestSchema,
179
+ type HumanInputRequest,
180
+ type HumanInputRequestInput,
181
+ HumanInputRequestSchema,
182
+ type HumanInputResult,
183
+ HumanInputResultSchema,
184
+ HumanInputResumeError,
185
+ InvalidHumanInputResultError,
186
+ waitForHumanInput,
187
+ type WaitForHumanInputOptions,
188
+ } from "./human-input.js";
165
189
  export {
166
190
  type ChatHandlerBeforeStream,
167
191
  type ChatHandlerBeforeStreamContext,