veryfront 0.1.246 → 0.1.248

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 (160) hide show
  1. package/esm/cli/mcp/tools/project-tools.d.ts +1 -1
  2. package/esm/cli/mcp/tools/scaffold-tools.d.ts +2 -2
  3. package/esm/cli/shared/server-startup.d.ts.map +1 -1
  4. package/esm/cli/shared/server-startup.js +8 -0
  5. package/esm/deno.d.ts +0 -9
  6. package/esm/deno.js +5 -12
  7. package/esm/src/agent/durable.d.ts +67 -0
  8. package/esm/src/agent/durable.d.ts.map +1 -1
  9. package/esm/src/agent/durable.js +134 -0
  10. package/esm/src/agent/index.d.ts +1 -1
  11. package/esm/src/agent/index.d.ts.map +1 -1
  12. package/esm/src/agent/index.js +1 -1
  13. package/esm/src/embedding/upload-loader.d.ts +2 -2
  14. package/esm/src/embedding/upload-loader.d.ts.map +1 -1
  15. package/esm/src/embedding/upload-loader.js +11 -48
  16. package/esm/src/errors/middleware/cli-error-boundary.js +1 -1
  17. package/esm/src/errors/middleware/http-error-boundary.js +1 -1
  18. package/esm/src/errors/tracing.d.ts +3 -3
  19. package/esm/src/errors/tracing.d.ts.map +1 -1
  20. package/esm/src/errors/tracing.js +3 -3
  21. package/esm/src/extensions/interfaces/index.d.ts +2 -2
  22. package/esm/src/extensions/interfaces/index.d.ts.map +1 -1
  23. package/esm/src/extensions/interfaces/node-compat.d.ts +68 -10
  24. package/esm/src/extensions/interfaces/node-compat.d.ts.map +1 -1
  25. package/esm/src/extensions/interfaces/tracing-exporter.d.ts +35 -1
  26. package/esm/src/extensions/interfaces/tracing-exporter.d.ts.map +1 -1
  27. package/esm/src/integrations/schema.d.ts +10 -10
  28. package/esm/src/mcp/index.d.ts +7 -2
  29. package/esm/src/mcp/index.d.ts.map +1 -1
  30. package/esm/src/mcp/index.js +7 -2
  31. package/esm/src/mcp/schemas/index.d.ts +1 -1
  32. package/esm/src/mcp/schemas/index.d.ts.map +1 -1
  33. package/esm/src/mcp/schemas/index.js +1 -1
  34. package/esm/src/mcp/schemas/mcp.schema.d.ts +13 -7
  35. package/esm/src/mcp/schemas/mcp.schema.d.ts.map +1 -1
  36. package/esm/src/mcp/schemas/mcp.schema.js +17 -7
  37. package/esm/src/mcp/server.d.ts +13 -0
  38. package/esm/src/mcp/server.d.ts.map +1 -1
  39. package/esm/src/mcp/server.js +43 -6
  40. package/esm/src/observability/auto-instrument/http-instrumentation.js +1 -1
  41. package/esm/src/observability/auto-instrument/react-instrumentation.js +1 -1
  42. package/esm/src/observability/instruments/build-instruments.d.ts +1 -1
  43. package/esm/src/observability/instruments/build-instruments.d.ts.map +1 -1
  44. package/esm/src/observability/instruments/cache-instruments.d.ts +1 -1
  45. package/esm/src/observability/instruments/cache-instruments.d.ts.map +1 -1
  46. package/esm/src/observability/instruments/data-instruments.d.ts +1 -1
  47. package/esm/src/observability/instruments/data-instruments.d.ts.map +1 -1
  48. package/esm/src/observability/instruments/error-instruments.d.ts +1 -1
  49. package/esm/src/observability/instruments/error-instruments.d.ts.map +1 -1
  50. package/esm/src/observability/instruments/http-instruments.d.ts +1 -1
  51. package/esm/src/observability/instruments/http-instruments.d.ts.map +1 -1
  52. package/esm/src/observability/instruments/instruments-factory.d.ts +1 -1
  53. package/esm/src/observability/instruments/instruments-factory.d.ts.map +1 -1
  54. package/esm/src/observability/instruments/memory-instruments.d.ts +1 -1
  55. package/esm/src/observability/instruments/memory-instruments.d.ts.map +1 -1
  56. package/esm/src/observability/instruments/render-instruments.d.ts +1 -1
  57. package/esm/src/observability/instruments/render-instruments.d.ts.map +1 -1
  58. package/esm/src/observability/instruments/rsc-instruments.d.ts +1 -1
  59. package/esm/src/observability/instruments/rsc-instruments.d.ts.map +1 -1
  60. package/esm/src/observability/metrics/manager.d.ts.map +1 -1
  61. package/esm/src/observability/metrics/manager.js +10 -2
  62. package/esm/src/observability/metrics/types.d.ts +1 -1
  63. package/esm/src/observability/metrics/types.d.ts.map +1 -1
  64. package/esm/src/observability/simple-metrics/otel-instruments.d.ts.map +1 -1
  65. package/esm/src/observability/simple-metrics/otel-instruments.js +7 -2
  66. package/esm/src/observability/simple-metrics/types.d.ts +1 -1
  67. package/esm/src/observability/simple-metrics/types.d.ts.map +1 -1
  68. package/esm/src/observability/tracing/api-shim.d.ts +177 -0
  69. package/esm/src/observability/tracing/api-shim.d.ts.map +1 -0
  70. package/esm/src/observability/tracing/api-shim.js +234 -0
  71. package/esm/src/observability/tracing/manager.d.ts.map +1 -1
  72. package/esm/src/observability/tracing/manager.js +29 -4
  73. package/esm/src/observability/tracing/otlp-setup.d.ts +12 -8
  74. package/esm/src/observability/tracing/otlp-setup.d.ts.map +1 -1
  75. package/esm/src/observability/tracing/otlp-setup.js +59 -136
  76. package/esm/src/observability/tracing/types.d.ts +1 -1
  77. package/esm/src/observability/tracing/types.d.ts.map +1 -1
  78. package/esm/src/platform/compat/kv/factory.d.ts.map +1 -1
  79. package/esm/src/platform/compat/kv/factory.js +15 -6
  80. package/esm/src/platform/compat/opaque-deps.d.ts +6 -5
  81. package/esm/src/platform/compat/opaque-deps.d.ts.map +1 -1
  82. package/esm/src/platform/compat/opaque-deps.js +13 -37
  83. package/esm/src/proxy/tracing.d.ts +7 -2
  84. package/esm/src/proxy/tracing.d.ts.map +1 -1
  85. package/esm/src/proxy/tracing.js +31 -96
  86. package/esm/src/rendering/rsc/client-module-strategy.d.ts.map +1 -1
  87. package/esm/src/rendering/rsc/client-module-strategy.js +9 -1
  88. package/esm/src/server/bootstrap.d.ts.map +1 -1
  89. package/esm/src/server/bootstrap.js +27 -1
  90. package/esm/src/server/context/request-context.d.ts.map +1 -1
  91. package/esm/src/server/context/request-context.js +7 -4
  92. package/esm/src/server/dev-server/middleware.js +1 -1
  93. package/esm/src/server/handlers/dev/files/dev-file.handler.d.ts.map +1 -1
  94. package/esm/src/server/handlers/dev/files/dev-file.handler.js +4 -14
  95. package/esm/src/server/services/rendering/ssr.service.d.ts.map +1 -1
  96. package/esm/src/server/services/rendering/ssr.service.js +4 -1
  97. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +1 -1
  98. package/esm/src/utils/version-constant.d.ts +1 -1
  99. package/esm/src/utils/version-constant.js +1 -1
  100. package/esm/src/workflow/claude-code/workspace-sync.d.ts +17 -2
  101. package/esm/src/workflow/claude-code/workspace-sync.d.ts.map +1 -1
  102. package/esm/src/workflow/claude-code/workspace-sync.js +106 -19
  103. package/esm/src/workflow/schemas/workflow.schema.d.ts +3 -3
  104. package/package.json +1 -9
  105. package/src/cli/shared/server-startup.ts +8 -0
  106. package/src/deno.js +5 -12
  107. package/src/src/agent/durable.ts +237 -0
  108. package/src/src/agent/index.ts +4 -0
  109. package/src/src/cache/backends/factory.ts +1 -1
  110. package/src/src/cache/backends/redis.ts +1 -1
  111. package/src/src/cache/keys/utils.ts +1 -1
  112. package/src/src/cache/registry.ts +1 -1
  113. package/src/src/data/static-paths-fetcher.ts +1 -1
  114. package/src/src/embedding/upload-loader.ts +14 -63
  115. package/src/src/errors/middleware/cli-error-boundary.ts +1 -1
  116. package/src/src/errors/middleware/http-error-boundary.ts +1 -1
  117. package/src/src/errors/tracing.ts +4 -4
  118. package/src/src/extensions/interfaces/index.ts +7 -2
  119. package/src/src/extensions/interfaces/node-compat.ts +74 -10
  120. package/src/src/extensions/interfaces/tracing-exporter.ts +36 -1
  121. package/src/src/mcp/index.ts +7 -2
  122. package/src/src/mcp/schemas/index.ts +1 -0
  123. package/src/src/mcp/schemas/mcp.schema.ts +20 -7
  124. package/src/src/mcp/server.ts +60 -6
  125. package/src/src/observability/auto-instrument/http-instrumentation.ts +1 -1
  126. package/src/src/observability/auto-instrument/react-instrumentation.ts +1 -1
  127. package/src/src/observability/auto-instrument/wrappers.ts +1 -1
  128. package/src/src/observability/instruments/build-instruments.ts +1 -1
  129. package/src/src/observability/instruments/cache-instruments.ts +6 -1
  130. package/src/src/observability/instruments/data-instruments.ts +1 -1
  131. package/src/src/observability/instruments/error-instruments.ts +1 -1
  132. package/src/src/observability/instruments/http-instruments.ts +6 -1
  133. package/src/src/observability/instruments/instruments-factory.ts +1 -1
  134. package/src/src/observability/instruments/memory-instruments.ts +5 -1
  135. package/src/src/observability/instruments/render-instruments.ts +1 -1
  136. package/src/src/observability/instruments/rsc-instruments.ts +1 -1
  137. package/src/src/observability/metrics/manager.ts +11 -3
  138. package/src/src/observability/metrics/types.ts +7 -1
  139. package/src/src/observability/simple-metrics/otel-instruments.ts +7 -2
  140. package/src/src/observability/simple-metrics/types.ts +1 -1
  141. package/src/src/observability/tracing/api-shim.ts +409 -0
  142. package/src/src/observability/tracing/manager.ts +29 -4
  143. package/src/src/observability/tracing/otlp-setup.ts +76 -168
  144. package/src/src/observability/tracing/types.ts +1 -1
  145. package/src/src/platform/compat/kv/factory.ts +18 -7
  146. package/src/src/platform/compat/opaque-deps.ts +16 -44
  147. package/src/src/proxy/tracing.ts +43 -117
  148. package/src/src/rendering/rsc/client-module-strategy.ts +9 -1
  149. package/src/src/server/bootstrap.ts +38 -1
  150. package/src/src/server/context/request-context.ts +10 -7
  151. package/src/src/server/dev-server/middleware.ts +1 -1
  152. package/src/src/server/handlers/dev/files/dev-file.handler.ts +4 -22
  153. package/src/src/server/services/rendering/ssr.service.ts +4 -1
  154. package/src/src/server/services/rsc/endpoints/rsc-bundles.generated.ts +1 -1
  155. package/src/src/utils/version-constant.ts +1 -1
  156. package/src/src/workflow/claude-code/workspace-sync.ts +109 -20
  157. package/esm/src/utils/logger/trace-bridge.d.ts +0 -11
  158. package/esm/src/utils/logger/trace-bridge.d.ts.map +0 -1
  159. package/esm/src/utils/logger/trace-bridge.js +0 -12
  160. package/src/src/utils/logger/trace-bridge.ts +0 -14
@@ -77,7 +77,11 @@ export declare class WorkspaceSync {
77
77
  */
78
78
  detectChanges(): Promise<FileChange[]>;
79
79
  /**
80
- * Recursively walk directory and detect changes
80
+ * Recursively walk directory and detect changes.
81
+ *
82
+ * SECURITY: Uses lstat (not stat) and skips any symlink it finds, so a
83
+ * symlink planted inside the workspace cannot cause us to descend into —
84
+ * or read the contents of — files outside the workspace (VULN-FS-4).
81
85
  */
82
86
  private walkAndDetect;
83
87
  /**
@@ -91,7 +95,18 @@ export declare class WorkspaceSync {
91
95
  onUpload?: (path: string, content: string, type: FileChange["type"]) => Promise<void>;
92
96
  }): Promise<UploadResult>;
93
97
  /**
94
- * Safely resolve a path within the workspace, preventing path traversal attacks
98
+ * Safely resolve a path within the workspace, preventing path traversal
99
+ * and symlink-based escapes (VULN-FS-4).
100
+ *
101
+ * - Rejects NUL bytes outright.
102
+ * - Rejects any intermediate path segment that is a symlink.
103
+ * - Re-checks containment by realpath-ing the parent directory after the
104
+ * segment walk, so a symlink that resolves through a non-symlink directory
105
+ * chain still cannot escape the workspace.
106
+ *
107
+ * Note: this deliberately rejects ALL symlinks inside the workspace — even
108
+ * those whose targets remain within it — because the race window between
109
+ * resolution and use is not worth the complexity for our use-case.
95
110
  */
96
111
  private resolveSafePath;
97
112
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-sync.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/claude-code/workspace-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-sync.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/claude-code/workspace-sync.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAUzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IAEd,oCAAoC;IACpC,MAAM,EAAE,qBAAqB,CAAC;IAE9B,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IAErB,iCAAiC;IACjC,eAAe,EAAE,MAAM,CAAC;IAExB,6BAA6B;IAC7B,eAAe,EAAE,MAAM,CAAC;IAExB,oDAAoD;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;IAEvB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,QAAQ,EAAE,UAAU,EAAE,CAAC;IAEvB,kCAAkC;IAClC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE/C,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA8CD;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAGZ;IACF,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,eAAe;IAgBnC;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAiGhD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAwC5C;;;;;;OAMG;YACW,aAAa;IAiD3B;;;;;OAKG;IACG,aAAa,CACjB,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,GAAE;QACP,8CAA8C;QAC9C,QAAQ,CAAC,EAAE,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,KACrB,OAAO,CAAC,IAAI,CAAC,CAAC;KACf,GACL,OAAO,CAAC,YAAY,CAAC;IA6CxB;;;;;;;;;;;;;OAaG;YACW,eAAe;IAiF7B;;OAEG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK7C;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7D;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB/B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,aAAa,CAE1E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,MAAM,EAAE,eAAe,EACvB,EAAE,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3C,OAAO,CAAC;IACT,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,mBAAmB,CAAC;CACjC,CAAC,CAkBD"}
@@ -12,8 +12,9 @@
12
12
  import * as dntShim from "../../../_dnt.shims.js";
13
13
  import { logger as baseLogger } from "../../utils/index.js";
14
14
  import { api } from "../api.js";
15
- import { join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
15
+ import { dirname, join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
16
16
  import { INITIALIZATION_ERROR, INVALID_ARGUMENT, SECURITY_VIOLATION } from "../../errors/index.js";
17
+ import { isWithinDirectory } from "../../utils/path-utils.js";
17
18
  const logger = baseLogger.component("workspace-sync");
18
19
  /** Maximum file size for workspace sync (10 MB) */
19
20
  const MAX_WORKSPACE_FILE_SIZE = 10 * 1024 * 1024;
@@ -136,8 +137,8 @@ export class WorkspaceSync {
136
137
  const hash = await checksum(content);
137
138
  this.fileChecksums.set(path, hash);
138
139
  // Write to local filesystem (use safe path resolution)
139
- const localPath = this.resolveSafePath(path);
140
- const dir = localPath.substring(0, localPath.lastIndexOf("/"));
140
+ const localPath = await this.resolveSafePath(path);
141
+ const dir = dirname(localPath);
141
142
  await dntShim.Deno.mkdir(dir, { recursive: true });
142
143
  await dntShim.Deno.writeTextFile(localPath, content);
143
144
  filesDownloaded++;
@@ -188,7 +189,7 @@ export class WorkspaceSync {
188
189
  // Check for deleted files
189
190
  for (const [path, originalHash] of this.fileChecksums) {
190
191
  try {
191
- const localPath = this.resolveSafePath(path);
192
+ const localPath = await this.resolveSafePath(path);
192
193
  await dntShim.Deno.stat(localPath);
193
194
  }
194
195
  catch (_) {
@@ -206,17 +207,28 @@ export class WorkspaceSync {
206
207
  return changes;
207
208
  }
208
209
  /**
209
- * Recursively walk directory and detect changes
210
+ * Recursively walk directory and detect changes.
211
+ *
212
+ * SECURITY: Uses lstat (not stat) and skips any symlink it finds, so a
213
+ * symlink planted inside the workspace cannot cause us to descend into —
214
+ * or read the contents of — files outside the workspace (VULN-FS-4).
210
215
  */
211
216
  async walkAndDetect(localPath, relativePath, changes) {
212
- const stat = await dntShim.Deno.stat(localPath);
217
+ const stat = await dntShim.Deno.lstat(localPath);
218
+ // Ignore symlinks outright — we never treat them as real files here.
219
+ if (stat.isSymlink) {
220
+ if (this.config.debug) {
221
+ logger.info("Skipping symlink during change detection", { localPath });
222
+ }
223
+ return;
224
+ }
213
225
  if (stat.isDirectory) {
214
226
  for await (const entry of dntShim.Deno.readDir(localPath)) {
215
227
  await this.walkAndDetect(`${localPath}/${entry.name}`, `${relativePath}/${entry.name}`, changes);
216
228
  }
217
229
  return;
218
230
  }
219
- // It's a file - check for changes
231
+ // It's a regular file - check for changes
220
232
  const content = await dntShim.Deno.readTextFile(localPath);
221
233
  const newHash = await checksum(content);
222
234
  const originalHash = this.fileChecksums.get(relativePath);
@@ -258,7 +270,7 @@ export class WorkspaceSync {
258
270
  continue;
259
271
  }
260
272
  try {
261
- const localPath = this.resolveSafePath(change.path);
273
+ const localPath = await this.resolveSafePath(change.path);
262
274
  const content = await dntShim.Deno.readTextFile(localPath);
263
275
  if (options.onUpload) {
264
276
  await options.onUpload(change.path, content, change.type);
@@ -287,34 +299,109 @@ export class WorkspaceSync {
287
299
  };
288
300
  }
289
301
  /**
290
- * Safely resolve a path within the workspace, preventing path traversal attacks
302
+ * Safely resolve a path within the workspace, preventing path traversal
303
+ * and symlink-based escapes (VULN-FS-4).
304
+ *
305
+ * - Rejects NUL bytes outright.
306
+ * - Rejects any intermediate path segment that is a symlink.
307
+ * - Re-checks containment by realpath-ing the parent directory after the
308
+ * segment walk, so a symlink that resolves through a non-symlink directory
309
+ * chain still cannot escape the workspace.
310
+ *
311
+ * Note: this deliberately rejects ALL symlinks inside the workspace — even
312
+ * those whose targets remain within it — because the race window between
313
+ * resolution and use is not worth the complexity for our use-case.
291
314
  */
292
- resolveSafePath(path) {
293
- // Normalize the input path
315
+ async resolveSafePath(path) {
316
+ // Reject NUL bytes — they confuse filesystem APIs and are never legitimate.
317
+ if (path.includes("\0")) {
318
+ throw SECURITY_VIOLATION.create({ detail: `NUL byte in path` });
319
+ }
320
+ // Workspace-relative paths only. A single leading "/" is the canonical
321
+ // API form for "the project root" (e.g. "/src/foo.ts") and is accepted;
322
+ // anything that syntactically looks like a system-absolute path beyond
323
+ // that one-slash convention is rejected.
324
+ //
325
+ // - Windows drive letters (C:\...) — rejected.
326
+ // - UNC paths (//host/share) — rejected.
327
+ // - Unix absolute paths with a leading slash are treated as
328
+ // workspace-relative, but any component that tries to escape the
329
+ // workspace is still caught by the traversal / realpath checks below.
330
+ if (/^[A-Za-z]:[\\/]/.test(path)) {
331
+ throw SECURITY_VIOLATION.create({ detail: `Absolute path not allowed: ${path}` });
332
+ }
333
+ if (path.startsWith("//")) {
334
+ throw SECURITY_VIOLATION.create({ detail: `Absolute path not allowed: ${path}` });
335
+ }
336
+ // Normalize the input path (treat leading "/" as workspace-relative).
294
337
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
295
- // Resolve the full path
338
+ // Empty path resolves to the workspace dir itself — writing to it would
339
+ // clobber the workspace as a regular file. Reject explicitly.
340
+ if (normalizedPath === "") {
341
+ throw SECURITY_VIOLATION.create({ detail: `Empty path not allowed` });
342
+ }
343
+ // Resolve the full path lexically first (catches literal "..").
296
344
  const fullPath = resolve(join(this.workspaceDir, normalizedPath));
297
- // Verify the resolved path is within the workspace
298
345
  const relativePath = relative(this.workspaceDir, fullPath);
299
- if (relativePath.startsWith("..") || !relativePath || relativePath === "..") {
346
+ if (!relativePath || relativePath.startsWith("..") || relativePath === "..") {
300
347
  throw SECURITY_VIOLATION.create({ detail: `Path traversal detected: ${path}` });
301
348
  }
349
+ // Walk each segment and reject any existing symlink along the way.
350
+ // A segment that does not yet exist is fine — it will be created later.
351
+ const relSegments = relativePath === "" ? [] : relativePath.split(/[\\/]/).filter(Boolean);
352
+ let cursor = this.workspaceDir;
353
+ for (const seg of relSegments) {
354
+ cursor = join(cursor, seg);
355
+ try {
356
+ const info = await dntShim.Deno.lstat(cursor);
357
+ if (info.isSymlink) {
358
+ throw SECURITY_VIOLATION.create({
359
+ detail: `Refusing to traverse symlink: ${cursor}`,
360
+ });
361
+ }
362
+ }
363
+ catch (e) {
364
+ if (e instanceof dntShim.Deno.errors.NotFound) {
365
+ // Segment doesn't exist yet — the rest of the chain will be
366
+ // created under a verified-non-symlink parent, so stop walking.
367
+ break;
368
+ }
369
+ throw e;
370
+ }
371
+ }
372
+ // Final containment check against the realpath of the parent directory,
373
+ // to defeat any symlink-in-parent we might have missed (e.g. one that
374
+ // appeared mid-walk). If the parent doesn't exist yet, that's fine —
375
+ // the segment walk above already proved every existing ancestor is real.
376
+ try {
377
+ const parentReal = await dntShim.Deno.realPath(dirname(fullPath));
378
+ const workspaceReal = await dntShim.Deno.realPath(this.workspaceDir);
379
+ if (!isWithinDirectory(workspaceReal, parentReal)) {
380
+ throw SECURITY_VIOLATION.create({
381
+ detail: `Resolved parent outside workspace: ${parentReal}`,
382
+ });
383
+ }
384
+ }
385
+ catch (e) {
386
+ if (!(e instanceof dntShim.Deno.errors.NotFound))
387
+ throw e;
388
+ }
302
389
  return fullPath;
303
390
  }
304
391
  /**
305
392
  * Read a file from the workspace
306
393
  */
307
394
  async readFile(path) {
308
- const localPath = this.resolveSafePath(path);
395
+ const localPath = await this.resolveSafePath(path);
309
396
  return await dntShim.Deno.readTextFile(localPath);
310
397
  }
311
398
  /**
312
399
  * Write a file to the workspace
313
400
  */
314
401
  async writeFile(path, content) {
315
- const localPath = this.resolveSafePath(path);
402
+ const localPath = await this.resolveSafePath(path);
316
403
  // Ensure directory exists
317
- const dir = localPath.substring(0, localPath.lastIndexOf("/"));
404
+ const dir = dirname(localPath);
318
405
  await dntShim.Deno.mkdir(dir, { recursive: true });
319
406
  await dntShim.Deno.writeTextFile(localPath, content);
320
407
  }
@@ -322,7 +409,7 @@ export class WorkspaceSync {
322
409
  * Delete a file from the workspace
323
410
  */
324
411
  async deleteFile(path) {
325
- const localPath = this.resolveSafePath(path);
412
+ const localPath = await this.resolveSafePath(path);
326
413
  await dntShim.Deno.remove(localPath);
327
414
  }
328
415
  /**
@@ -330,7 +417,7 @@ export class WorkspaceSync {
330
417
  */
331
418
  async fileExists(path) {
332
419
  try {
333
- const localPath = this.resolveSafePath(path);
420
+ const localPath = await this.resolveSafePath(path);
334
421
  await dntShim.Deno.stat(localPath);
335
422
  return true;
336
423
  }
@@ -19,11 +19,11 @@ export declare const WorkflowStatusSchema: z.ZodEnum<{
19
19
  * Node status schema
20
20
  */
21
21
  export declare const NodeStatusSchema: z.ZodEnum<{
22
- skipped: "skipped";
23
22
  failed: "failed";
24
23
  pending: "pending";
25
24
  completed: "completed";
26
25
  running: "running";
26
+ skipped: "skipped";
27
27
  }>;
28
28
  /**
29
29
  * Workflow node type schema
@@ -74,11 +74,11 @@ export declare const LoopExecutionContextSchema: z.ZodObject<{
74
74
  export declare const NodeStateSchema: z.ZodObject<{
75
75
  nodeId: z.ZodString;
76
76
  status: z.ZodEnum<{
77
- skipped: "skipped";
78
77
  failed: "failed";
79
78
  pending: "pending";
80
79
  completed: "completed";
81
80
  running: "running";
81
+ skipped: "skipped";
82
82
  }>;
83
83
  input: z.ZodOptional<z.ZodUnknown>;
84
84
  output: z.ZodOptional<z.ZodUnknown>;
@@ -106,11 +106,11 @@ export declare const CheckpointSchema: z.ZodObject<{
106
106
  nodeStates: z.ZodRecord<z.ZodString, z.ZodObject<{
107
107
  nodeId: z.ZodString;
108
108
  status: z.ZodEnum<{
109
- skipped: "skipped";
110
109
  failed: "failed";
111
110
  pending: "pending";
112
111
  completed: "completed";
113
112
  running: "running";
113
+ skipped: "skipped";
114
114
  }>;
115
115
  input: z.ZodOptional<z.ZodUnknown>;
116
116
  output: z.ZodOptional<z.ZodUnknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.246",
3
+ "version": "0.1.248",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -192,15 +192,7 @@
192
192
  "@babel/parser": "7.29.2",
193
193
  "@babel/traverse": "7.29.0",
194
194
  "@babel/types": "7.29.0",
195
- "@kreuzberg/wasm": "4.5.2",
196
195
  "@mdx-js/mdx": "3.1.1",
197
- "@opentelemetry/api": "1.9.0",
198
- "@opentelemetry/context-async-hooks": "2.6.0",
199
- "@opentelemetry/core": "2.6.0",
200
- "@opentelemetry/exporter-trace-otlp-http": "0.213.0",
201
- "@opentelemetry/resources": "2.6.0",
202
- "@opentelemetry/sdk-trace-base": "2.6.0",
203
- "@opentelemetry/semantic-conventions": "1.40.0",
204
196
  "@types/hast": "3.0.3",
205
197
  "@types/mdast": "4.0.3",
206
198
  "@types/unist": "3.0.2",
@@ -99,6 +99,14 @@ export async function startCliProductionServer(
99
99
  signal: options.signal,
100
100
  defaultProjectSlug: options.defaultProjectSlug,
101
101
  defaultProjectId: options.defaultProjectId,
102
+ // Do NOT register a `localProjects` mapping here. `vf serve` and the
103
+ // compiled binary are production deployments, and `isLocalProject: true`
104
+ // flips `isDev` on in security headers (suppressing CSP) and in the SSR
105
+ // error overlay (exposing absolute paths and stack traces) — the exact
106
+ // dev-surface leak VULN-SRV-1 / VULN-SRV-2 was closing. The strategy
107
+ // narrowing in `client-module-strategy.ts` already routes hydration
108
+ // through `/_veryfront/rsc/module?` for non-local deployments, so no
109
+ // `localProjects` entry is required for the compiled binary to work.
102
110
  };
103
111
  return await startProductionServer(serverOptions);
104
112
  }
package/src/deno.js CHANGED
@@ -1,10 +1,12 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.246",
3
+ "version": "0.1.248",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
7
- "./extensions/ext-redis"
7
+ "./extensions/ext-redis",
8
+ "./extensions/ext-opentelemetry",
9
+ "./extensions/ext-node-compat"
8
10
  ],
9
11
  "exclude": [
10
12
  "npm/",
@@ -267,21 +269,12 @@ export default {
267
269
  "tailwindcss/plugin": "https://esm.sh/tailwindcss@4.2.2/plugin",
268
270
  "tailwindcss/defaultTheme": "https://esm.sh/tailwindcss@4.2.2/defaultTheme",
269
271
  "tailwindcss/colors": "https://esm.sh/tailwindcss@4.2.2/colors",
270
- "@opentelemetry/api": "npm:@opentelemetry/api@1.9.0",
271
- "@opentelemetry/core": "npm:@opentelemetry/core@2.6.0",
272
- "@opentelemetry/context-async-hooks": "npm:@opentelemetry/context-async-hooks@2.6.0",
273
- "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@2.6.0",
274
- "@opentelemetry/exporter-trace-otlp-http": "npm:@opentelemetry/exporter-trace-otlp-http@0.213.0",
275
- "@opentelemetry/resources": "npm:@opentelemetry/resources@2.6.0",
276
- "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@1.40.0",
277
272
  "@babel/parser": "npm:@babel/parser@7.29.2",
278
273
  "@babel/traverse": "npm:@babel/traverse@7.29.0",
279
274
  "@babel/generator": "npm:@babel/generator@7.29.1",
280
275
  "@babel/types": "npm:@babel/types@7.29.0",
281
276
  "class-variance-authority": "npm:class-variance-authority@0.7.1",
282
- "tailwind-merge": "npm:tailwind-merge@3.5.0",
283
- "@kreuzberg/wasm": "npm:@kreuzberg/wasm@4.5.2",
284
- "#kreuzberg-wasm-glue": "npm:@kreuzberg/wasm@4.5.2/dist/pkg/kreuzberg_wasm.js"
277
+ "tailwind-merge": "npm:tailwind-merge@3.5.0"
285
278
  },
286
279
  "compilerOptions": {
287
280
  "jsx": "react-jsx",
@@ -142,6 +142,15 @@ export type ConversationRunAppendFailureOutcome =
142
142
  | "resumed"
143
143
  | "stopped"
144
144
  | "retry_scheduled";
145
+ export type ConversationRunAppendExecutionOutcome =
146
+ | "resumed"
147
+ | "stopped"
148
+ | "retry_scheduled";
149
+ export type ConversationRunBatchFlushOutcome =
150
+ | "flushed"
151
+ | "resumed"
152
+ | "stopped"
153
+ | "retry_scheduled";
145
154
 
146
155
  export const CreateConversationRunAcceptedSchema = z
147
156
  .object({
@@ -223,6 +232,8 @@ export type AppendConversationRunEventsResponse = z.infer<
223
232
  typeof AppendConversationRunEventsResponseSchema
224
233
  >;
225
234
 
235
+ const DEFAULT_MAX_CONVERSATION_RUN_BATCH_BYTES = 512 * 1024;
236
+
226
237
  const ConversationRunErrorSchema = z.object({
227
238
  detail: z.string().min(1).optional(),
228
239
  error: z.string().min(1).optional(),
@@ -531,6 +542,232 @@ export async function recoverConversationRunAppendFailure(input: {
531
542
  };
532
543
  }
533
544
 
545
+ export async function recoverConversationRunAppendExecution(input: {
546
+ error: unknown;
547
+ authToken: string;
548
+ apiUrl: string;
549
+ conversationId: string;
550
+ runId: string;
551
+ latestEventId: number;
552
+ latestExternalEventSequence: number;
553
+ remainingEvents: unknown[];
554
+ pendingEvents: unknown[];
555
+ cursorResyncsThisFlush: number;
556
+ consecutiveFailures: number;
557
+ maxCursorResyncsPerFlush: number;
558
+ abortSignal?: AbortSignal;
559
+ }): Promise<
560
+ | {
561
+ outcome: "resumed";
562
+ latestEventId: number;
563
+ latestExternalEventSequence: number;
564
+ pendingEvents: unknown[];
565
+ consecutiveFailures: number;
566
+ }
567
+ | {
568
+ outcome: "stopped";
569
+ latestEventId: number;
570
+ latestExternalEventSequence: number;
571
+ disableReason?: "cursor_resyncs_exhausted" | "non_appendable" | "ignorable_append_rejection";
572
+ }
573
+ | {
574
+ outcome: "retry_scheduled";
575
+ latestEventId: number;
576
+ latestExternalEventSequence: number;
577
+ pendingEvents: unknown[];
578
+ consecutiveFailures: number;
579
+ errorMessage: string;
580
+ }
581
+ > {
582
+ const recovered = await recoverConversationRunAppendFailure({
583
+ error: input.error,
584
+ authToken: input.authToken,
585
+ apiUrl: input.apiUrl,
586
+ conversationId: input.conversationId,
587
+ runId: input.runId,
588
+ latestEventId: input.latestEventId,
589
+ latestExternalEventSequence: input.latestExternalEventSequence,
590
+ cursorResyncsThisFlush: input.cursorResyncsThisFlush,
591
+ maxCursorResyncsPerFlush: input.maxCursorResyncsPerFlush,
592
+ abortSignal: input.abortSignal,
593
+ });
594
+
595
+ if (recovered.outcome === "resumed") {
596
+ return {
597
+ outcome: "resumed",
598
+ latestEventId: recovered.latestEventId,
599
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
600
+ pendingEvents: [...input.remainingEvents, ...input.pendingEvents],
601
+ consecutiveFailures: 0,
602
+ };
603
+ }
604
+
605
+ if (recovered.outcome === "stopped") {
606
+ return {
607
+ outcome: "stopped",
608
+ latestEventId: recovered.latestEventId,
609
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
610
+ ...(recovered.disableReason ? { disableReason: recovered.disableReason } : {}),
611
+ };
612
+ }
613
+
614
+ return {
615
+ outcome: "retry_scheduled",
616
+ latestEventId: recovered.latestEventId,
617
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
618
+ pendingEvents: [...input.remainingEvents, ...input.pendingEvents],
619
+ consecutiveFailures: input.consecutiveFailures + 1,
620
+ errorMessage: recovered.errorMessage ?? "Conversation run append failed",
621
+ };
622
+ }
623
+
624
+ function getConversationRunEventJsonByteLength(event: unknown): number {
625
+ return new TextEncoder().encode(JSON.stringify(event)).byteLength;
626
+ }
627
+
628
+ function buildConversationRunEventBatches(input: {
629
+ events: unknown[];
630
+ maxEventsPerBatch: number;
631
+ maxBatchPayloadBytes?: number;
632
+ }): unknown[][] {
633
+ const maxBatchPayloadBytes = input.maxBatchPayloadBytes ??
634
+ DEFAULT_MAX_CONVERSATION_RUN_BATCH_BYTES;
635
+ const batches: unknown[][] = [];
636
+ let currentBatch: unknown[] = [];
637
+ let currentBatchBytes = 0;
638
+
639
+ for (const event of input.events) {
640
+ const eventBytes = getConversationRunEventJsonByteLength(event);
641
+
642
+ if (
643
+ currentBatch.length > 0 &&
644
+ (currentBatch.length >= input.maxEventsPerBatch ||
645
+ currentBatchBytes + eventBytes > maxBatchPayloadBytes)
646
+ ) {
647
+ batches.push(currentBatch);
648
+ currentBatch = [];
649
+ currentBatchBytes = 0;
650
+ }
651
+
652
+ currentBatch.push(event);
653
+ currentBatchBytes += eventBytes;
654
+ }
655
+
656
+ if (currentBatch.length > 0) {
657
+ batches.push(currentBatch);
658
+ }
659
+
660
+ return batches;
661
+ }
662
+
663
+ export async function flushConversationRunEventBatches(input: {
664
+ authToken: string;
665
+ apiUrl: string;
666
+ conversationId: string;
667
+ runId: string;
668
+ latestEventId: number;
669
+ latestExternalEventSequence: number;
670
+ events: unknown[];
671
+ pendingEvents?: unknown[];
672
+ maxEventsPerBatch: number;
673
+ maxBatchPayloadBytes?: number;
674
+ cursorResyncsThisFlush?: number;
675
+ consecutiveFailures?: number;
676
+ maxCursorResyncsPerFlush: number;
677
+ abortSignal?: AbortSignal;
678
+ }): Promise<
679
+ | {
680
+ outcome: "flushed";
681
+ latestEventId: number;
682
+ latestExternalEventSequence: number;
683
+ }
684
+ | {
685
+ outcome: "resumed" | "retry_scheduled";
686
+ latestEventId: number;
687
+ latestExternalEventSequence: number;
688
+ pendingEvents: unknown[];
689
+ consecutiveFailures: number;
690
+ errorMessage?: string;
691
+ }
692
+ | {
693
+ outcome: "stopped";
694
+ latestEventId: number;
695
+ latestExternalEventSequence: number;
696
+ disableReason?: "cursor_resyncs_exhausted" | "non_appendable" | "ignorable_append_rejection";
697
+ }
698
+ > {
699
+ const batches = buildConversationRunEventBatches({
700
+ events: input.events,
701
+ maxEventsPerBatch: input.maxEventsPerBatch,
702
+ maxBatchPayloadBytes: input.maxBatchPayloadBytes,
703
+ });
704
+
705
+ let latestEventId = input.latestEventId;
706
+ let latestExternalEventSequence = input.latestExternalEventSequence;
707
+
708
+ for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
709
+ const batch = batches[batchIndex];
710
+ if (!batch) {
711
+ continue;
712
+ }
713
+ try {
714
+ const response = await appendConversationRunEvents({
715
+ authToken: input.authToken,
716
+ apiUrl: input.apiUrl,
717
+ conversationId: input.conversationId,
718
+ runId: input.runId,
719
+ expectedPreviousExternalEventSequence: latestExternalEventSequence,
720
+ events: batch,
721
+ abortSignal: input.abortSignal,
722
+ });
723
+ latestEventId = response.latestEventId;
724
+ latestExternalEventSequence = response.latestExternalEventSequence;
725
+ } catch (error) {
726
+ const recovered = await recoverConversationRunAppendExecution({
727
+ error,
728
+ authToken: input.authToken,
729
+ apiUrl: input.apiUrl,
730
+ conversationId: input.conversationId,
731
+ runId: input.runId,
732
+ latestEventId,
733
+ latestExternalEventSequence,
734
+ remainingEvents: batches.slice(batchIndex).flat(),
735
+ pendingEvents: input.pendingEvents ?? [],
736
+ cursorResyncsThisFlush: input.cursorResyncsThisFlush ?? 0,
737
+ consecutiveFailures: input.consecutiveFailures ?? 0,
738
+ maxCursorResyncsPerFlush: input.maxCursorResyncsPerFlush,
739
+ abortSignal: input.abortSignal,
740
+ });
741
+
742
+ if (recovered.outcome === "stopped") {
743
+ return {
744
+ outcome: "stopped",
745
+ latestEventId: recovered.latestEventId,
746
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
747
+ ...(recovered.disableReason ? { disableReason: recovered.disableReason } : {}),
748
+ };
749
+ }
750
+
751
+ return {
752
+ outcome: recovered.outcome,
753
+ latestEventId: recovered.latestEventId,
754
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
755
+ pendingEvents: recovered.pendingEvents,
756
+ consecutiveFailures: recovered.consecutiveFailures,
757
+ ...(recovered.outcome === "retry_scheduled"
758
+ ? { errorMessage: recovered.errorMessage }
759
+ : {}),
760
+ };
761
+ }
762
+ }
763
+
764
+ return {
765
+ outcome: "flushed",
766
+ latestEventId,
767
+ latestExternalEventSequence,
768
+ };
769
+ }
770
+
534
771
  async function waitForConversationRunPoll(
535
772
  ms: number,
536
773
  abortSignal?: AbortSignal,
@@ -220,8 +220,10 @@ export {
220
220
  CompleteConversationRunResponseSchema,
221
221
  type ConversationAgentRunUsage,
222
222
  type ConversationRunAppendCursorResyncResult,
223
+ type ConversationRunAppendExecutionOutcome,
223
224
  type ConversationRunAppendFailureOutcome,
224
225
  type ConversationRunAppendRecoveryOutcome,
226
+ type ConversationRunBatchFlushOutcome,
225
227
  type ConversationRunProjection,
226
228
  ConversationRunProjectionSchema,
227
229
  ConversationRunStatusSchema,
@@ -230,6 +232,7 @@ export {
230
232
  ConversationRunTerminalStateError,
231
233
  createConversationAgentRun,
232
234
  finalizeConversationAgentRun,
235
+ flushConversationRunEventBatches,
233
236
  getConversationRun,
234
237
  isActiveConversationRunStatus,
235
238
  isAppendableConversationRunProjection,
@@ -237,6 +240,7 @@ export {
237
240
  isIgnorableConversationRunAppendError,
238
241
  monitorConversationRunStatus,
239
242
  parseAppendConversationRunEventsErrorBody,
243
+ recoverConversationRunAppendExecution,
240
244
  recoverConversationRunAppendFailure,
241
245
  recoverConversationRunCursorMismatch,
242
246
  resolveConversationRunTargets,
@@ -1,7 +1,7 @@
1
1
  import { logger as baseLogger } from "../../utils/index.js";
2
2
  import { withSpan } from "../../observability/tracing/otlp-setup.js";
3
3
  import { SpanNames } from "../../observability/tracing/span-names.js";
4
- import type { Span } from "@opentelemetry/api";
4
+ import type { Span } from "../../observability/tracing/api-shim.js";
5
5
  import { getEnv } from "../../platform/compat/process.js";
6
6
  import type { CacheBackend } from "../types.js";
7
7
  import {
@@ -1,7 +1,7 @@
1
1
  import { logger as baseLogger } from "../../utils/index.js";
2
2
  import { withSpan } from "../../observability/tracing/otlp-setup.js";
3
3
  import { SpanNames } from "../../observability/tracing/span-names.js";
4
- import type { Span } from "@opentelemetry/api";
4
+ import type { Span } from "../../observability/tracing/api-shim.js";
5
5
  import {
6
6
  getRedisClient,
7
7
  isRedisConfigured,