veryfront 0.1.61 → 0.1.63

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 (74) hide show
  1. package/esm/cli/templates/manifest.js +37 -37
  2. package/esm/deno.d.ts +3 -0
  3. package/esm/deno.js +6 -3
  4. package/esm/src/agent/composition/composition.d.ts.map +1 -1
  5. package/esm/src/agent/composition/composition.js +13 -3
  6. package/esm/src/agent/factory.d.ts.map +1 -1
  7. package/esm/src/agent/factory.js +3 -3
  8. package/esm/src/agent/middleware/security/validator.d.ts +92 -0
  9. package/esm/src/agent/middleware/security/validator.d.ts.map +1 -0
  10. package/esm/src/agent/middleware/security/validator.js +187 -0
  11. package/esm/src/agent/runtime/index.d.ts +3 -2
  12. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  13. package/esm/src/agent/runtime/index.js +16 -8
  14. package/esm/src/agent/types.d.ts +4 -0
  15. package/esm/src/agent/types.d.ts.map +1 -1
  16. package/esm/src/channels/invoke.d.ts +427 -0
  17. package/esm/src/channels/invoke.d.ts.map +1 -0
  18. package/esm/src/channels/invoke.js +401 -0
  19. package/esm/src/embedding/embedding.d.ts.map +1 -1
  20. package/esm/src/embedding/embedding.js +5 -1
  21. package/esm/src/embedding/rag-store.js +2 -0
  22. package/esm/src/embedding/vector-store.d.ts.map +1 -1
  23. package/esm/src/embedding/vector-store.js +2 -0
  24. package/esm/src/embedding/veryfront-cloud/rag-store.d.ts.map +1 -1
  25. package/esm/src/embedding/veryfront-cloud/rag-store.js +2 -0
  26. package/esm/src/integrations/schema.d.ts +2 -2
  27. package/esm/src/oauth/handlers/init-handler.d.ts +6 -2
  28. package/esm/src/oauth/handlers/init-handler.d.ts.map +1 -1
  29. package/esm/src/oauth/handlers/init-handler.js +8 -2
  30. package/esm/src/platform/compat/opaque-deps.d.ts.map +1 -1
  31. package/esm/src/platform/compat/opaque-deps.js +10 -1
  32. package/esm/src/prompt/factory.d.ts.map +1 -1
  33. package/esm/src/prompt/factory.js +9 -1
  34. package/esm/src/react/components/ai/markdown.d.ts.map +1 -1
  35. package/esm/src/react/components/ai/markdown.js +4 -4
  36. package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
  37. package/esm/src/server/handlers/dev/framework-candidates.generated.js +5 -4
  38. package/esm/src/server/handlers/preview/markdown-html-generator.js +1 -1
  39. package/esm/src/server/handlers/request/api/api-handler-wrapper.d.ts.map +1 -1
  40. package/esm/src/server/handlers/request/api/api-handler-wrapper.js +1 -74
  41. package/esm/src/server/handlers/request/api/project-discovery.d.ts +9 -0
  42. package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -0
  43. package/esm/src/server/handlers/request/api/project-discovery.js +74 -0
  44. package/esm/src/server/handlers/request/channel-invoke.handler.d.ts +11 -0
  45. package/esm/src/server/handlers/request/channel-invoke.handler.d.ts.map +1 -0
  46. package/esm/src/server/handlers/request/channel-invoke.handler.js +72 -0
  47. package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
  48. package/esm/src/server/runtime-handler/index.js +2 -0
  49. package/esm/src/transforms/md/compiler/md-compiler.d.ts.map +1 -1
  50. package/esm/src/transforms/md/compiler/md-compiler.js +25 -1
  51. package/package.json +3 -1
  52. package/src/cli/templates/manifest.js +37 -37
  53. package/src/deno.js +6 -3
  54. package/src/src/agent/composition/composition.ts +15 -3
  55. package/src/src/agent/factory.ts +19 -6
  56. package/src/src/agent/middleware/security/validator.ts +288 -0
  57. package/src/src/agent/runtime/index.ts +26 -3
  58. package/src/src/agent/types.ts +4 -0
  59. package/src/src/channels/invoke.ts +521 -0
  60. package/src/src/embedding/embedding.ts +5 -1
  61. package/src/src/embedding/rag-store.ts +1 -0
  62. package/src/src/embedding/vector-store.ts +1 -0
  63. package/src/src/embedding/veryfront-cloud/rag-store.ts +1 -0
  64. package/src/src/oauth/handlers/init-handler.ts +20 -5
  65. package/src/src/platform/compat/opaque-deps.ts +19 -4
  66. package/src/src/prompt/factory.ts +10 -1
  67. package/src/src/react/components/ai/markdown.tsx +5 -4
  68. package/src/src/server/handlers/dev/framework-candidates.generated.ts +5 -4
  69. package/src/src/server/handlers/preview/markdown-html-generator.ts +1 -1
  70. package/src/src/server/handlers/request/api/api-handler-wrapper.ts +1 -85
  71. package/src/src/server/handlers/request/api/project-discovery.ts +86 -0
  72. package/src/src/server/handlers/request/channel-invoke.handler.ts +95 -0
  73. package/src/src/server/runtime-handler/index.ts +2 -0
  74. package/src/src/transforms/md/compiler/md-compiler.ts +27 -1
@@ -3059,10 +3059,10 @@ export const FRAMEWORK_CANDIDATES: readonly string[] = [
3059
3059
  "html:",
3060
3060
  "html>",
3061
3061
  "https://ai-sdk.dev/elements)",
3062
- "https://esm.sh/mermaid@11",
3063
- "https://esm.sh/react-markdown@9?external=react",
3064
- "https://esm.sh/rehype-highlight@7?target=es2022",
3065
- "https://esm.sh/remark-gfm@4?target=es2022",
3062
+ "https://esm.sh/mermaid@11.4.1?pin=v135",
3063
+ "https://esm.sh/react-markdown@9.0.3?external=react",
3064
+ "https://esm.sh/rehype-highlight@7.0.2?target=es2022",
3065
+ "https://esm.sh/remark-gfm@4.0.1?target=es2022",
3066
3066
  "hydration",
3067
3067
  "i)",
3068
3068
  "icon",
@@ -4007,6 +4007,7 @@ export const FRAMEWORK_CANDIDATES: readonly string[] = [
4007
4007
  "persistThreads],",
4008
4008
  "pickers,",
4009
4009
  "pill",
4010
+ "pin=v135",
4010
4011
  "pl-2",
4011
4012
  "pl-2.5",
4012
4013
  "pl-3",
@@ -118,7 +118,7 @@ export function generateMarkdownHtml(options: MarkdownHtmlOptions): string {
118
118
  ${studioScript}
119
119
 
120
120
  <script type="module">
121
- import mermaid from 'https://esm.sh/mermaid@11';
121
+ import mermaid from 'https://esm.sh/mermaid@11.4.1?pin=v135';
122
122
 
123
123
  function getMermaidTheme() {
124
124
  return document.documentElement.dataset.theme === 'dark' ? 'dark' : 'default';
@@ -9,9 +9,7 @@ import type {
9
9
  import { getApiHandler } from "./pages-api-handler.js";
10
10
  import { PRIORITY_MEDIUM_API } from "../../../../utils/constants/index.js";
11
11
  import { withSpan } from "../../../../observability/tracing/otlp-setup.js";
12
- import { serverLogger } from "../../../../utils/index.js";
13
-
14
- const logger = serverLogger.component("api-wrapper");
12
+ import { ensureProjectDiscovery } from "./project-discovery.js";
15
13
 
16
14
  type FsWrapper = {
17
15
  isMultiProjectMode?: () => boolean;
@@ -24,88 +22,6 @@ type FsWrapper = {
24
22
  ) => Promise<T>;
25
23
  };
26
24
 
27
- /**
28
- * Tracks in-flight and completed AI discovery per project+release.
29
- *
30
- * Key: `{projectSlug}:{releaseId}` for production, `{projectSlug}:preview` for preview.
31
- * This ensures a new deployment triggers re-discovery of agents/tools.
32
- *
33
- * Using a Map<string, Promise> deduplicates concurrent requests and
34
- * allows retry on failure (the key is deleted if discovery rejects).
35
- */
36
- const discoveredProjects = new Map<string, Promise<void>>();
37
-
38
- /** Build a discovery cache key that incorporates the release/version. */
39
- function discoveryKey(ctx: HandlerContext): string {
40
- const slug = ctx.projectSlug ?? ctx.projectDir;
41
- const version = ctx.releaseId ?? "preview";
42
- return `${slug}:${version}`;
43
- }
44
-
45
- /**
46
- * Run AI discovery (agents, tools) for a project if not already done.
47
- * Must be called within a runWithContext scope so the VFS can resolve
48
- * the correct remote project files and the agent registry uses the
49
- * correct project scope.
50
- */
51
- async function ensureProjectDiscovery(ctx: HandlerContext): Promise<void> {
52
- const key = discoveryKey(ctx);
53
-
54
- const existing = discoveredProjects.get(key);
55
- if (existing) return existing;
56
-
57
- const promise = (async () => {
58
- const { discoverAll } = await import("../../../../discovery/index.js");
59
- const { agentRegistry } = await import(
60
- "../../../../agent/composition/composition.js"
61
- );
62
- const { toolRegistry } = await import("../../../../tool/registry.js");
63
-
64
- // Clear stale entries for this project scope before re-discovery.
65
- // This prevents agents/tools removed in a new release from lingering.
66
- agentRegistry.clear();
67
- toolRegistry.clear();
68
-
69
- const result = await discoverAll({
70
- baseDir: ctx.projectDir,
71
- fsAdapter: ctx.adapter.fs,
72
- verbose: false,
73
- });
74
-
75
- const logData = {
76
- projectSlug: ctx.projectSlug,
77
- releaseId: ctx.releaseId,
78
- agents: result.agents.size,
79
- tools: result.tools.size,
80
- errors: result.errors.length,
81
- };
82
-
83
- if (result.agents.size === 0 && result.tools.size === 0) {
84
- logger.warn("AI discovery found 0 agents and 0 tools", {
85
- ...logData,
86
- errorMessages: result.errors.map((e) => e.error.message).slice(0, 5),
87
- baseDir: ctx.projectDir,
88
- });
89
- } else {
90
- logger.info("AI discovery completed", logData);
91
- }
92
- })();
93
-
94
- discoveredProjects.set(key, promise);
95
-
96
- try {
97
- await promise;
98
- } catch (error) {
99
- // Allow retry on next request
100
- discoveredProjects.delete(key);
101
- logger.warn("AI discovery failed (will retry)", {
102
- projectSlug: ctx.projectSlug,
103
- error: error instanceof Error ? error.message : String(error),
104
- stack: error instanceof Error ? error.stack : undefined,
105
- });
106
- }
107
- }
108
-
109
25
  export class ApiHandlerWrapper extends BaseHandler {
110
26
  private projectDir: string;
111
27
  private adapter: import("../../../../platform/adapters/base.js").RuntimeAdapter;
@@ -0,0 +1,86 @@
1
+ import { serverLogger } from "../../../../utils/index.js";
2
+ import type { HandlerContext } from "../../types.js";
3
+
4
+ const logger = serverLogger.component("api-wrapper");
5
+
6
+ /**
7
+ * Tracks in-flight and completed AI discovery per project+release.
8
+ *
9
+ * Key: `{projectSlug}:{releaseId}` for production, `{projectSlug}:preview` for preview.
10
+ * This ensures a new deployment triggers re-discovery of agents/tools.
11
+ *
12
+ * Using a Map<string, Promise> deduplicates concurrent requests and
13
+ * allows retry on failure (the key is deleted if discovery rejects).
14
+ */
15
+ const discoveredProjects = new Map<string, Promise<void>>();
16
+
17
+ /** Build a discovery cache key that incorporates the release/version. */
18
+ function discoveryKey(ctx: HandlerContext): string {
19
+ const slug = ctx.projectSlug ?? ctx.projectDir;
20
+ const version = ctx.releaseId ?? "preview";
21
+ return `${slug}:${version}`;
22
+ }
23
+
24
+ /**
25
+ * Run AI discovery (agents, tools) for a project if not already done.
26
+ * Must be called within a runWithContext scope so the VFS can resolve
27
+ * the correct remote project files and the agent registry uses the
28
+ * correct project scope.
29
+ */
30
+ export async function ensureProjectDiscovery(ctx: HandlerContext): Promise<void> {
31
+ const key = discoveryKey(ctx);
32
+
33
+ const existing = discoveredProjects.get(key);
34
+ if (existing) return existing;
35
+
36
+ const promise = (async () => {
37
+ const { discoverAll } = await import("../../../../discovery/index.js");
38
+ const { agentRegistry } = await import(
39
+ "../../../../agent/composition/composition.js"
40
+ );
41
+ const { toolRegistry } = await import("../../../../tool/registry.js");
42
+
43
+ // Clear stale entries for this project scope before re-discovery.
44
+ // This prevents agents/tools removed in a new release from lingering.
45
+ agentRegistry.clear();
46
+ toolRegistry.clear();
47
+
48
+ const result = await discoverAll({
49
+ baseDir: ctx.projectDir,
50
+ fsAdapter: ctx.adapter.fs,
51
+ verbose: false,
52
+ });
53
+
54
+ const logData = {
55
+ projectSlug: ctx.projectSlug,
56
+ releaseId: ctx.releaseId,
57
+ agents: result.agents.size,
58
+ tools: result.tools.size,
59
+ errors: result.errors.length,
60
+ };
61
+
62
+ if (result.agents.size === 0 && result.tools.size === 0) {
63
+ logger.warn("AI discovery found 0 agents and 0 tools", {
64
+ ...logData,
65
+ errorMessages: result.errors.map((e) => e.error.message).slice(0, 5),
66
+ baseDir: ctx.projectDir,
67
+ });
68
+ } else {
69
+ logger.info("AI discovery completed", logData);
70
+ }
71
+ })();
72
+
73
+ discoveredProjects.set(key, promise);
74
+
75
+ try {
76
+ await promise;
77
+ } catch (error) {
78
+ // Allow retry on next request
79
+ discoveredProjects.delete(key);
80
+ logger.warn("AI discovery failed (will retry)", {
81
+ projectSlug: ctx.projectSlug,
82
+ error: error instanceof Error ? error.message : String(error),
83
+ stack: error instanceof Error ? error.stack : undefined,
84
+ });
85
+ }
86
+ }
@@ -0,0 +1,95 @@
1
+ import * as dntShim from "../../../../_dnt.shims.js";
2
+ import { BaseHandler } from "../response/base.js";
3
+ import type { HandlerContext, HandlerMetadata, HandlerPriority, HandlerResult } from "../types.js";
4
+ import {
5
+ type ChannelInvokeDeps,
6
+ ChannelInvokeRequestSchema,
7
+ defaultChannelInvokeDeps,
8
+ executeChannelInvoke,
9
+ verifyDispatchJws,
10
+ } from "../../../channels/invoke.js";
11
+ import {
12
+ HTTP_INTERNAL_SERVER_ERROR,
13
+ PRIORITY_MEDIUM_API,
14
+ } from "../../../utils/constants/index.js";
15
+
16
+ const DISPATCH_JWS_HEADER = "x-veryfront-dispatch-jws";
17
+ const MAX_DISPATCH_SIGNATURE_AGE_SECONDS = 60;
18
+
19
+ export class ChannelInvokeHandler extends BaseHandler {
20
+ metadata: HandlerMetadata = {
21
+ name: "ChannelInvokeHandler",
22
+ priority: PRIORITY_MEDIUM_API as HandlerPriority,
23
+ patterns: [{ pattern: "/channels/invoke", exact: true, method: "POST" }],
24
+ };
25
+
26
+ constructor(private readonly deps: ChannelInvokeDeps = defaultChannelInvokeDeps) {
27
+ super();
28
+ }
29
+
30
+ async handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult> {
31
+ if (!this.shouldHandle(req, ctx)) {
32
+ return this.continue();
33
+ }
34
+
35
+ return this.withProxyContext(ctx, async () => {
36
+ const builder = this.createResponseBuilder(ctx)
37
+ .withCORS(req, ctx.securityConfig?.cors)
38
+ .withSecurity(ctx.securityConfig ?? undefined, req);
39
+
40
+ const publicKeyPem = ctx.adapter.env.get("CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY");
41
+ if (!publicKeyPem) {
42
+ this.logWarn("Missing CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY for channel invoke endpoint");
43
+ return this.respond(
44
+ builder.json(
45
+ { error: "Channel dispatch verification is not configured" },
46
+ HTTP_INTERNAL_SERVER_ERROR,
47
+ ),
48
+ );
49
+ }
50
+
51
+ const projectSlug = ctx.projectSlug;
52
+ if (!projectSlug) {
53
+ this.logWarn("Channel invoke request arrived without resolved project slug");
54
+ return this.respond(builder.json({ error: "Project context is unavailable" }, 400));
55
+ }
56
+
57
+ const dispatchJws = req.headers.get(DISPATCH_JWS_HEADER);
58
+ if (!dispatchJws) {
59
+ return this.respond(builder.json({ error: "Missing dispatch signature" }, 401));
60
+ }
61
+
62
+ const rawBody = await req.text();
63
+ try {
64
+ await verifyDispatchJws(dispatchJws, rawBody, {
65
+ audience: projectSlug,
66
+ expectedProjectId: ctx.projectId,
67
+ publicKeyPem,
68
+ maxAgeSeconds: MAX_DISPATCH_SIGNATURE_AGE_SECONDS,
69
+ });
70
+ } catch (error) {
71
+ this.logWarn("Channel invoke signature verification failed", {
72
+ error: error instanceof Error ? error.message : String(error),
73
+ projectSlug,
74
+ projectId: ctx.projectId,
75
+ });
76
+ return this.respond(builder.json({ error: "Invalid dispatch signature" }, 401));
77
+ }
78
+
79
+ let payload;
80
+ try {
81
+ payload = ChannelInvokeRequestSchema.parse(JSON.parse(rawBody));
82
+ } catch (error) {
83
+ this.logWarn("Channel invoke request validation failed", {
84
+ error: error instanceof Error ? error.message : String(error),
85
+ projectSlug,
86
+ projectId: ctx.projectId,
87
+ });
88
+ return this.respond(builder.json({ error: "Invalid channel invoke request" }, 400));
89
+ }
90
+
91
+ const response = await executeChannelInvoke(payload, ctx, this.deps);
92
+ return this.respond(builder.json(response, 200));
93
+ });
94
+ }
95
+ }
@@ -53,6 +53,7 @@ import { HMRHandler } from "../handlers/preview/hmr.handler.js";
53
53
  import { MarkdownPreviewHandler } from "../handlers/preview/markdown-preview.handler.js";
54
54
  import { OpenAPIHandler } from "../handlers/request/openapi.handler.js";
55
55
  import { OpenAPIDocsHandler } from "../handlers/request/openapi-docs.handler.js";
56
+ import { ChannelInvokeHandler } from "../handlers/request/channel-invoke.handler.js";
56
57
  import { DevDashboardHandler } from "../handlers/dev/dashboard/index.js";
57
58
  import { ProjectsHandler } from "../handlers/dev/projects/index.js";
58
59
 
@@ -204,6 +205,7 @@ export function createVeryfrontHandler(
204
205
  new DebugContextHandler(),
205
206
  new OpenAPIHandler(),
206
207
  new OpenAPIDocsHandler(),
208
+ new ChannelInvokeHandler(),
207
209
  new DevDashboardHandler(),
208
210
  new ProjectsHandler(),
209
211
  new StudioBridgeModulesHandler(),
@@ -5,6 +5,8 @@ import remarkFrontmatter from "remark-frontmatter";
5
5
  import remarkRehype from "remark-rehype";
6
6
  import rehypeStarryNight from "rehype-starry-night";
7
7
  import rehypeSlug from "rehype-slug";
8
+ import rehypeRaw from "rehype-raw";
9
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
8
10
  import rehypeStringify from "rehype-stringify";
9
11
  import { visit } from "unist-util-visit";
10
12
  import { toString } from "mdast-util-to-string";
@@ -98,12 +100,36 @@ export function compileMarkdownRuntime(
98
100
  .use(rehypeStarryNight)
99
101
  .use(rehypeSlug);
100
102
 
103
+ // Parse raw HTML nodes into proper elements before sanitizing.
104
+ pipeline.use(rehypeRaw);
105
+
106
+ // Add node positions after rehype-raw so attributes survive re-parsing.
101
107
  if (studioEmbed && filePath) {
102
108
  pipeline.use(rehypeNodePositions, { filePath });
103
109
  }
104
110
 
111
+ // Extend the sanitize schema in studio embed mode to preserve
112
+ // data-node-* attributes used for element-to-source mapping.
113
+ const sanitizeSchema = studioEmbed
114
+ ? {
115
+ ...defaultSchema,
116
+ attributes: {
117
+ ...defaultSchema.attributes,
118
+ "*": [
119
+ ...(defaultSchema.attributes?.["*"] ?? []),
120
+ "data-node-file",
121
+ "data-node-name",
122
+ "data-node-line",
123
+ "data-node-column",
124
+ "data-node-source",
125
+ ],
126
+ },
127
+ }
128
+ : defaultSchema;
129
+
105
130
  const result = await pipeline
106
- .use(rehypeStringify, { allowDangerousHtml: true })
131
+ .use(rehypeSanitize, sanitizeSchema)
132
+ .use(rehypeStringify)
107
133
  .process(body);
108
134
  const html = String(result);
109
135