@agent-native/core 0.58.0 → 0.58.2

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 (42) hide show
  1. package/dist/agent/production-agent.d.ts.map +1 -1
  2. package/dist/agent/production-agent.js +26 -12
  3. package/dist/agent/production-agent.js.map +1 -1
  4. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  5. package/dist/agent/thread-data-builder.js +10 -2
  6. package/dist/agent/thread-data-builder.js.map +1 -1
  7. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  8. package/dist/client/agent-chat-adapter.js +29 -5
  9. package/dist/client/agent-chat-adapter.js.map +1 -1
  10. package/dist/client/chat/attachment-adapters.d.ts.map +1 -1
  11. package/dist/client/chat/attachment-adapters.js +29 -4
  12. package/dist/client/chat/attachment-adapters.js.map +1 -1
  13. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  14. package/dist/client/composer/PromptComposer.js +5 -1
  15. package/dist/client/composer/PromptComposer.js.map +1 -1
  16. package/dist/client/composer/attachment-accept.d.ts +2 -0
  17. package/dist/client/composer/attachment-accept.d.ts.map +1 -1
  18. package/dist/client/composer/attachment-accept.js +30 -1
  19. package/dist/client/composer/attachment-accept.js.map +1 -1
  20. package/dist/data-widgets/index.d.ts +5 -5
  21. package/dist/file-upload/pre-upload-attachments.d.ts +2 -0
  22. package/dist/file-upload/pre-upload-attachments.d.ts.map +1 -1
  23. package/dist/file-upload/pre-upload-attachments.js +66 -9
  24. package/dist/file-upload/pre-upload-attachments.js.map +1 -1
  25. package/dist/server/agent-chat-plugin.d.ts +16 -0
  26. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  27. package/dist/server/agent-chat-plugin.js +67 -5
  28. package/dist/server/agent-chat-plugin.js.map +1 -1
  29. package/dist/server/auth.d.ts.map +1 -1
  30. package/dist/server/auth.js +20 -0
  31. package/dist/server/auth.js.map +1 -1
  32. package/dist/server/prompts/framework-core-compact.d.ts.map +1 -1
  33. package/dist/server/prompts/framework-core-compact.js +2 -1
  34. package/dist/server/prompts/framework-core-compact.js.map +1 -1
  35. package/dist/server/prompts/framework-core.d.ts.map +1 -1
  36. package/dist/server/prompts/framework-core.js +1 -0
  37. package/dist/server/prompts/framework-core.js.map +1 -1
  38. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  39. package/docs/content/authentication.md +14 -12
  40. package/docs/content/native-chat-ui.md +12 -0
  41. package/package.json +1 -1
  42. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
@@ -1,6 +1,29 @@
1
1
  import { getActiveFileUploadProvider, uploadFile } from "./registry.js";
2
- const IMAGE_DATA_URL_RE = /^data:(image\/[^;]+);base64,(.+)$/;
3
2
  const FILE_DATA_URL_RE = /^data:([^;]+);base64,(.+)$/;
3
+ const SVG_REFERENCE_SECURITY_NOTE = "SVG content may contain active markup; use this URL as a file reference unless the target app sanitizes it.";
4
+ function normalizeContentType(value) {
5
+ return value?.split(";")[0]?.trim().toLowerCase() || undefined;
6
+ }
7
+ function hasSvgFilename(name) {
8
+ return /\.svg$/i.test(name ?? "");
9
+ }
10
+ function isSvgAttachment(args) {
11
+ return (normalizeContentType(args.contentType) === "image/svg+xml" ||
12
+ hasSvgFilename(args.name));
13
+ }
14
+ function isSvgPayload(args) {
15
+ const contentType = normalizeContentType(args.contentType);
16
+ return (contentType === "image/svg+xml" ||
17
+ ((contentType === undefined ||
18
+ contentType === "application/octet-stream") &&
19
+ hasSvgFilename(args.name)));
20
+ }
21
+ function markReferenceOnlySvgAttachment(att, contentType) {
22
+ att.type = "file";
23
+ att.contentType = normalizeContentType(contentType) ?? "image/svg+xml";
24
+ att.referenceOnly = true;
25
+ att.securityNote = SVG_REFERENCE_SECURITY_NOTE;
26
+ }
4
27
  function escapeXmlAttr(value) {
5
28
  return value
6
29
  .replace(/&/g, "&")
@@ -63,13 +86,23 @@ export async function preUploadAttachments(opts) {
63
86
  continue;
64
87
  if (att.url) {
65
88
  // Already pre-uploaded earlier in the pipeline — reuse it.
89
+ const isReferenceOnlySvg = isSvgAttachment(att);
90
+ if (isReferenceOnlySvg) {
91
+ markReferenceOnlySvgAttachment(att, att.contentType);
92
+ }
66
93
  const entry = {
67
94
  name: att.name,
68
95
  url: att.url,
69
96
  provider: att.uploadProvider || "unknown",
70
97
  contentType: att.contentType,
98
+ ...(isReferenceOnlySvg
99
+ ? {
100
+ referenceOnly: true,
101
+ securityNote: SVG_REFERENCE_SECURITY_NOTE,
102
+ }
103
+ : {}),
71
104
  };
72
- if (isImage) {
105
+ if (isImage && !isReferenceOnlySvg) {
73
106
  uploaded.push(entry);
74
107
  }
75
108
  else {
@@ -77,11 +110,15 @@ export async function preUploadAttachments(opts) {
77
110
  }
78
111
  continue;
79
112
  }
80
- const re = isImage ? IMAGE_DATA_URL_RE : FILE_DATA_URL_RE;
81
- const match = att.data.match(re);
113
+ const match = att.data.match(FILE_DATA_URL_RE);
82
114
  if (!match)
83
115
  continue;
84
- const mimeType = att.contentType || match[1];
116
+ const dataUrlMimeType = normalizeContentType(match[1]);
117
+ const mimeType = dataUrlMimeType || normalizeContentType(att.contentType) || match[1];
118
+ const uploadAsImage = isImage && !isSvgPayload({ name: att.name, contentType: mimeType });
119
+ const uploadAsFile = !uploadAsImage && (isImage || (includeFiles && isFile));
120
+ if (!uploadAsImage && !uploadAsFile)
121
+ continue;
85
122
  let bytes;
86
123
  try {
87
124
  bytes = new Uint8Array(Buffer.from(match[2], "base64"));
@@ -97,20 +134,33 @@ export async function preUploadAttachments(opts) {
97
134
  ownerEmail: opts.ownerEmail || undefined,
98
135
  });
99
136
  if (!result) {
100
- if (isImage)
137
+ if (uploadAsImage)
101
138
  providerMissing = true;
102
139
  continue;
103
140
  }
104
141
  att.url = result.url;
105
142
  att.uploadProvider = result.provider;
143
+ const isReferenceOnlySvg = isSvgPayload({
144
+ name: att.name,
145
+ contentType: mimeType,
146
+ });
147
+ if (isReferenceOnlySvg) {
148
+ markReferenceOnlySvgAttachment(att, mimeType);
149
+ }
106
150
  const entry = {
107
151
  name: att.name,
108
152
  url: result.url,
109
153
  provider: result.provider,
110
- contentType: att.contentType,
154
+ contentType: isReferenceOnlySvg ? att.contentType : mimeType,
111
155
  sizeBytes: bytes.byteLength,
156
+ ...(isReferenceOnlySvg
157
+ ? {
158
+ referenceOnly: true,
159
+ securityNote: SVG_REFERENCE_SECURITY_NOTE,
160
+ }
161
+ : {}),
112
162
  };
113
- if (isImage) {
163
+ if (uploadAsImage) {
114
164
  uploaded.push(entry);
115
165
  }
116
166
  else {
@@ -141,11 +191,18 @@ export async function preUploadAttachments(opts) {
141
191
  `url="${escapeXmlAttr(f.url)}"`,
142
192
  f.contentType ? `contentType="${escapeXmlAttr(f.contentType)}"` : null,
143
193
  `provider="${escapeXmlAttr(f.provider)}"`,
194
+ f.referenceOnly ? `referenceOnly="true"` : null,
195
+ f.securityNote
196
+ ? `securityNote="${escapeXmlAttr(f.securityNote)}"`
197
+ : null,
144
198
  ].filter(Boolean);
145
199
  lines.push(`<chat-file-attachment ${attrs.join(" ")} />`);
146
200
  }
201
+ const hasReferenceOnlySvg = uploadedFiles.some((file) => file.referenceOnly && isSvgAttachment(file));
147
202
  injectedText = [
148
- '<chat-attachments note="The user attached these files. They have been uploaded — use the url attribute when embedding in HTML, slide content, or any outbound message.">',
203
+ hasReferenceOnlySvg
204
+ ? '<chat-attachments note="The user attached these files. Image attachment URLs may be used for embedding. File attachment URLs are references; SVG files are unsanitized vector source and must not be inlined as HTML or embedded in outbound content unless the target app sanitizes or stores them safely.">'
205
+ : '<chat-attachments note="The user attached these files. Image attachment URLs may be used for embedding in HTML, slide content, or outbound messages. File attachment URLs are references for reading or attaching in target apps.">',
149
206
  ...lines,
150
207
  "</chat-attachments>",
151
208
  ].join("\n");
@@ -1 +1 @@
1
- {"version":3,"file":"pre-upload-attachments.js","sourceRoot":"","sources":["../../src/file-upload/pre-upload-attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentChatAttachment } from \"../agent/types.js\";\nimport { getActiveFileUploadProvider, uploadFile } from \"./registry.js\";\n\nexport interface PreUploadedImageAttachment {\n name?: string;\n url: string;\n provider: string;\n contentType?: string;\n}\n\n/**\n * A file/non-image attachment that was successfully uploaded to a hosted URL.\n * Consumers can use the URL in place of the base64 data to avoid persisting\n * large blobs in the thread repo and SQL.\n */\nexport interface PreUploadedFileAttachment {\n name?: string;\n url: string;\n provider: string;\n contentType?: string;\n sizeBytes?: number;\n}\n\nexport interface PreUploadAttachmentsResult {\n /** Same array reference. Each image attachment that was uploaded also gets a\n * `url` property attached (non-breaking; consumers that don't read it are\n * unaffected). */\n attachments: AgentChatAttachment[];\n /** Set when at least one image was uploaded. List of hosted URLs the agent\n * can embed in HTML, slide content, documents, etc. */\n uploaded: PreUploadedImageAttachment[];\n /** Uploaded non-image files (PDF, generic binary). Parallel to `uploaded`\n * but for the file/document attachment type. */\n uploadedFiles: PreUploadedFileAttachment[];\n /** True if at least one image attachment failed to upload because no\n * file-upload provider is configured. Templates use this to render a\n * \"Connect Builder.io\" suggestion. */\n providerMissing: boolean;\n /** A pre-formatted block to inject into the user message text so the agent\n * has each hosted URL inline. Null when nothing was uploaded or no provider\n * is configured. */\n injectedText: string | null;\n}\n\nconst IMAGE_DATA_URL_RE = /^data:(image\\/[^;]+);base64,(.+)$/;\nconst FILE_DATA_URL_RE = /^data:([^;]+);base64,(.+)$/;\n\nfunction escapeXmlAttr(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Returns true when a file-upload provider is currently configured.\n * Used to decide whether to attempt upload-first or fall back to base64.\n */\nexport function isFileUploadProviderConfigured(): boolean {\n return getActiveFileUploadProvider() !== null;\n}\n\n/**\n * Pre-upload chat image attachments through the active file-upload provider\n * (Builder.io by default) so the agent can embed hosted URLs in HTML, slide\n * content, and outbound messages. Keeps the original base64 data URL on the\n * attachment so multimodal vision still works — only adds a hosted `url`.\n *\n * Safe to call when no provider is configured: it returns the attachments\n * untouched with `providerMissing: true` so callers can surface a connect-\n * Builder.io hint to the agent.\n */\nexport async function preUploadImageAttachments(opts: {\n attachments: AgentChatAttachment[] | undefined;\n ownerEmail: string | null | undefined;\n}): Promise<PreUploadAttachmentsResult> {\n return preUploadAttachments({ ...opts, includeFiles: false });\n}\n\n/**\n * Pre-upload ALL chat attachments (images AND files/PDFs) through the active\n * file-upload provider. When a provider is configured, each attachment gets a\n * `url` property injected so downstream code can store/send URLs instead of\n * base64. The base64 data is kept in-memory for the current turn so vision and\n * file-reading still work; callers that persist the attachment can drop the\n * data when a URL exists.\n *\n * Falls back gracefully when no provider is configured: returns untouched\n * attachments with `providerMissing: true` for image-type failures.\n */\nexport async function preUploadAttachments(opts: {\n attachments: AgentChatAttachment[] | undefined;\n ownerEmail: string | null | undefined;\n /** When false, only images are uploaded (legacy behaviour). Default: true */\n includeFiles?: boolean;\n}): Promise<PreUploadAttachmentsResult> {\n const list = Array.isArray(opts.attachments) ? opts.attachments : [];\n const includeFiles = opts.includeFiles !== false;\n const uploaded: PreUploadedImageAttachment[] = [];\n const uploadedFiles: PreUploadedFileAttachment[] = [];\n let providerMissing = false;\n\n if (list.length === 0) {\n return {\n attachments: list,\n uploaded,\n uploadedFiles,\n providerMissing: false,\n injectedText: null,\n };\n }\n\n for (const att of list) {\n const isImage = att.type === \"image\";\n const isFile = att.type === \"file\" || att.type === \"document\";\n if (!isImage && !(includeFiles && isFile)) continue;\n if (typeof att.data !== \"string\") continue;\n\n if ((att as any).url) {\n // Already pre-uploaded earlier in the pipeline — reuse it.\n const entry = {\n name: att.name,\n url: (att as any).url as string,\n provider: ((att as any).uploadProvider as string) || \"unknown\",\n contentType: att.contentType,\n };\n if (isImage) {\n uploaded.push(entry);\n } else {\n uploadedFiles.push(entry);\n }\n continue;\n }\n\n const re = isImage ? IMAGE_DATA_URL_RE : FILE_DATA_URL_RE;\n const match = att.data.match(re);\n if (!match) continue;\n const mimeType = att.contentType || match[1];\n let bytes: Uint8Array;\n try {\n bytes = new Uint8Array(Buffer.from(match[2], \"base64\"));\n } catch {\n continue;\n }\n\n try {\n const result = await uploadFile({\n data: bytes,\n filename: att.name,\n mimeType,\n ownerEmail: opts.ownerEmail || undefined,\n });\n if (!result) {\n if (isImage) providerMissing = true;\n continue;\n }\n (att as any).url = result.url;\n (att as any).uploadProvider = result.provider;\n const entry = {\n name: att.name,\n url: result.url,\n provider: result.provider,\n contentType: att.contentType,\n sizeBytes: bytes.byteLength,\n };\n if (isImage) {\n uploaded.push(entry);\n } else {\n uploadedFiles.push(entry);\n }\n } catch (err) {\n // Real upload failure (network, API). Keep the base64 so the model\n // can still see the image/file, but don't crash the turn.\n console.warn(\n \"[agent-native] pre-upload of chat attachment failed:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n let injectedText: string | null = null;\n if (uploaded.length > 0 || uploadedFiles.length > 0) {\n const lines: string[] = [];\n for (const u of uploaded) {\n const attrs = [\n u.name ? `name=\"${escapeXmlAttr(u.name)}\"` : null,\n `url=\"${escapeXmlAttr(u.url)}\"`,\n u.contentType ? `contentType=\"${escapeXmlAttr(u.contentType)}\"` : null,\n `provider=\"${escapeXmlAttr(u.provider)}\"`,\n ].filter(Boolean);\n lines.push(`<chat-image-attachment ${attrs.join(\" \")} />`);\n }\n for (const f of uploadedFiles) {\n const attrs = [\n f.name ? `name=\"${escapeXmlAttr(f.name)}\"` : null,\n `url=\"${escapeXmlAttr(f.url)}\"`,\n f.contentType ? `contentType=\"${escapeXmlAttr(f.contentType)}\"` : null,\n `provider=\"${escapeXmlAttr(f.provider)}\"`,\n ].filter(Boolean);\n lines.push(`<chat-file-attachment ${attrs.join(\" \")} />`);\n }\n injectedText = [\n '<chat-attachments note=\"The user attached these files. They have been uploaded — use the url attribute when embedding in HTML, slide content, or any outbound message.\">',\n ...lines,\n \"</chat-attachments>\",\n ].join(\"\\n\");\n } else if (providerMissing) {\n injectedText = [\n \"<chat-image-attachment-upload-error>\",\n \"The user attached one or more images, but no file-upload provider is configured for this app.\",\n \"Tell the user to connect or reconnect Builder.io from Settings → File uploads. If `connect-builder` is available, use it to render the inline connection card. Workspaces with a custom storage provider can also use one registered via registerFileUploadProvider().\",\n \"Until that's done, you can still SEE the image, but you do NOT have a URL to embed it in HTML or share with other apps.\",\n \"</chat-image-attachment-upload-error>\",\n ].join(\"\\n\");\n }\n\n return {\n attachments: list,\n uploaded,\n uploadedFiles,\n providerMissing,\n injectedText,\n };\n}\n"]}
1
+ {"version":3,"file":"pre-upload-attachments.js","sourceRoot":"","sources":["../../src/file-upload/pre-upload-attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentChatAttachment } from \"../agent/types.js\";\nimport { getActiveFileUploadProvider, uploadFile } from \"./registry.js\";\n\nexport interface PreUploadedImageAttachment {\n name?: string;\n url: string;\n provider: string;\n contentType?: string;\n}\n\n/**\n * A file/non-image attachment that was successfully uploaded to a hosted URL.\n * Consumers can use the URL in place of the base64 data to avoid persisting\n * large blobs in the thread repo and SQL.\n */\nexport interface PreUploadedFileAttachment {\n name?: string;\n url: string;\n provider: string;\n contentType?: string;\n sizeBytes?: number;\n referenceOnly?: boolean;\n securityNote?: string;\n}\n\nexport interface PreUploadAttachmentsResult {\n /** Same array reference. Each image attachment that was uploaded also gets a\n * `url` property attached (non-breaking; consumers that don't read it are\n * unaffected). */\n attachments: AgentChatAttachment[];\n /** Set when at least one image was uploaded. List of hosted URLs the agent\n * can embed in HTML, slide content, documents, etc. */\n uploaded: PreUploadedImageAttachment[];\n /** Uploaded non-image files (PDF, generic binary). Parallel to `uploaded`\n * but for the file/document attachment type. */\n uploadedFiles: PreUploadedFileAttachment[];\n /** True if at least one image attachment failed to upload because no\n * file-upload provider is configured. Templates use this to render a\n * \"Connect Builder.io\" suggestion. */\n providerMissing: boolean;\n /** A pre-formatted block to inject into the user message text so the agent\n * has each hosted URL inline. Null when nothing was uploaded or no provider\n * is configured. */\n injectedText: string | null;\n}\n\nconst FILE_DATA_URL_RE = /^data:([^;]+);base64,(.+)$/;\nconst SVG_REFERENCE_SECURITY_NOTE =\n \"SVG content may contain active markup; use this URL as a file reference unless the target app sanitizes it.\";\n\nfunction normalizeContentType(value: string | undefined): string | undefined {\n return value?.split(\";\")[0]?.trim().toLowerCase() || undefined;\n}\n\nfunction hasSvgFilename(name: string | undefined): boolean {\n return /\\.svg$/i.test(name ?? \"\");\n}\n\nfunction isSvgAttachment(args: {\n name?: string;\n contentType?: string;\n}): boolean {\n return (\n normalizeContentType(args.contentType) === \"image/svg+xml\" ||\n hasSvgFilename(args.name)\n );\n}\n\nfunction isSvgPayload(args: { name?: string; contentType?: string }): boolean {\n const contentType = normalizeContentType(args.contentType);\n return (\n contentType === \"image/svg+xml\" ||\n ((contentType === undefined ||\n contentType === \"application/octet-stream\") &&\n hasSvgFilename(args.name))\n );\n}\n\nfunction markReferenceOnlySvgAttachment(\n att: AgentChatAttachment,\n contentType: string | undefined,\n) {\n att.type = \"file\";\n att.contentType = normalizeContentType(contentType) ?? \"image/svg+xml\";\n (att as any).referenceOnly = true;\n (att as any).securityNote = SVG_REFERENCE_SECURITY_NOTE;\n}\n\nfunction escapeXmlAttr(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Returns true when a file-upload provider is currently configured.\n * Used to decide whether to attempt upload-first or fall back to base64.\n */\nexport function isFileUploadProviderConfigured(): boolean {\n return getActiveFileUploadProvider() !== null;\n}\n\n/**\n * Pre-upload chat image attachments through the active file-upload provider\n * (Builder.io by default) so the agent can embed hosted URLs in HTML, slide\n * content, and outbound messages. Keeps the original base64 data URL on the\n * attachment so multimodal vision still works — only adds a hosted `url`.\n *\n * Safe to call when no provider is configured: it returns the attachments\n * untouched with `providerMissing: true` so callers can surface a connect-\n * Builder.io hint to the agent.\n */\nexport async function preUploadImageAttachments(opts: {\n attachments: AgentChatAttachment[] | undefined;\n ownerEmail: string | null | undefined;\n}): Promise<PreUploadAttachmentsResult> {\n return preUploadAttachments({ ...opts, includeFiles: false });\n}\n\n/**\n * Pre-upload ALL chat attachments (images AND files/PDFs) through the active\n * file-upload provider. When a provider is configured, each attachment gets a\n * `url` property injected so downstream code can store/send URLs instead of\n * base64. The base64 data is kept in-memory for the current turn so vision and\n * file-reading still work; callers that persist the attachment can drop the\n * data when a URL exists.\n *\n * Falls back gracefully when no provider is configured: returns untouched\n * attachments with `providerMissing: true` for image-type failures.\n */\nexport async function preUploadAttachments(opts: {\n attachments: AgentChatAttachment[] | undefined;\n ownerEmail: string | null | undefined;\n /** When false, only images are uploaded (legacy behaviour). Default: true */\n includeFiles?: boolean;\n}): Promise<PreUploadAttachmentsResult> {\n const list = Array.isArray(opts.attachments) ? opts.attachments : [];\n const includeFiles = opts.includeFiles !== false;\n const uploaded: PreUploadedImageAttachment[] = [];\n const uploadedFiles: PreUploadedFileAttachment[] = [];\n let providerMissing = false;\n\n if (list.length === 0) {\n return {\n attachments: list,\n uploaded,\n uploadedFiles,\n providerMissing: false,\n injectedText: null,\n };\n }\n\n for (const att of list) {\n const isImage = att.type === \"image\";\n const isFile = att.type === \"file\" || att.type === \"document\";\n if (!isImage && !(includeFiles && isFile)) continue;\n if (typeof att.data !== \"string\") continue;\n\n if ((att as any).url) {\n // Already pre-uploaded earlier in the pipeline — reuse it.\n const isReferenceOnlySvg = isSvgAttachment(att);\n if (isReferenceOnlySvg) {\n markReferenceOnlySvgAttachment(att, att.contentType);\n }\n const entry = {\n name: att.name,\n url: (att as any).url as string,\n provider: ((att as any).uploadProvider as string) || \"unknown\",\n contentType: att.contentType,\n ...(isReferenceOnlySvg\n ? {\n referenceOnly: true,\n securityNote: SVG_REFERENCE_SECURITY_NOTE,\n }\n : {}),\n };\n if (isImage && !isReferenceOnlySvg) {\n uploaded.push(entry);\n } else {\n uploadedFiles.push(entry);\n }\n continue;\n }\n\n const match = att.data.match(FILE_DATA_URL_RE);\n if (!match) continue;\n const dataUrlMimeType = normalizeContentType(match[1]);\n const mimeType =\n dataUrlMimeType || normalizeContentType(att.contentType) || match[1];\n const uploadAsImage =\n isImage && !isSvgPayload({ name: att.name, contentType: mimeType });\n const uploadAsFile =\n !uploadAsImage && (isImage || (includeFiles && isFile));\n if (!uploadAsImage && !uploadAsFile) continue;\n\n let bytes: Uint8Array;\n try {\n bytes = new Uint8Array(Buffer.from(match[2], \"base64\"));\n } catch {\n continue;\n }\n\n try {\n const result = await uploadFile({\n data: bytes,\n filename: att.name,\n mimeType,\n ownerEmail: opts.ownerEmail || undefined,\n });\n if (!result) {\n if (uploadAsImage) providerMissing = true;\n continue;\n }\n (att as any).url = result.url;\n (att as any).uploadProvider = result.provider;\n const isReferenceOnlySvg = isSvgPayload({\n name: att.name,\n contentType: mimeType,\n });\n if (isReferenceOnlySvg) {\n markReferenceOnlySvgAttachment(att, mimeType);\n }\n const entry = {\n name: att.name,\n url: result.url,\n provider: result.provider,\n contentType: isReferenceOnlySvg ? att.contentType : mimeType,\n sizeBytes: bytes.byteLength,\n ...(isReferenceOnlySvg\n ? {\n referenceOnly: true,\n securityNote: SVG_REFERENCE_SECURITY_NOTE,\n }\n : {}),\n };\n if (uploadAsImage) {\n uploaded.push(entry);\n } else {\n uploadedFiles.push(entry);\n }\n } catch (err) {\n // Real upload failure (network, API). Keep the base64 so the model\n // can still see the image/file, but don't crash the turn.\n console.warn(\n \"[agent-native] pre-upload of chat attachment failed:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n let injectedText: string | null = null;\n if (uploaded.length > 0 || uploadedFiles.length > 0) {\n const lines: string[] = [];\n for (const u of uploaded) {\n const attrs = [\n u.name ? `name=\"${escapeXmlAttr(u.name)}\"` : null,\n `url=\"${escapeXmlAttr(u.url)}\"`,\n u.contentType ? `contentType=\"${escapeXmlAttr(u.contentType)}\"` : null,\n `provider=\"${escapeXmlAttr(u.provider)}\"`,\n ].filter(Boolean);\n lines.push(`<chat-image-attachment ${attrs.join(\" \")} />`);\n }\n for (const f of uploadedFiles) {\n const attrs = [\n f.name ? `name=\"${escapeXmlAttr(f.name)}\"` : null,\n `url=\"${escapeXmlAttr(f.url)}\"`,\n f.contentType ? `contentType=\"${escapeXmlAttr(f.contentType)}\"` : null,\n `provider=\"${escapeXmlAttr(f.provider)}\"`,\n f.referenceOnly ? `referenceOnly=\"true\"` : null,\n f.securityNote\n ? `securityNote=\"${escapeXmlAttr(f.securityNote)}\"`\n : null,\n ].filter(Boolean);\n lines.push(`<chat-file-attachment ${attrs.join(\" \")} />`);\n }\n const hasReferenceOnlySvg = uploadedFiles.some(\n (file) => file.referenceOnly && isSvgAttachment(file),\n );\n injectedText = [\n hasReferenceOnlySvg\n ? '<chat-attachments note=\"The user attached these files. Image attachment URLs may be used for embedding. File attachment URLs are references; SVG files are unsanitized vector source and must not be inlined as HTML or embedded in outbound content unless the target app sanitizes or stores them safely.\">'\n : '<chat-attachments note=\"The user attached these files. Image attachment URLs may be used for embedding in HTML, slide content, or outbound messages. File attachment URLs are references for reading or attaching in target apps.\">',\n ...lines,\n \"</chat-attachments>\",\n ].join(\"\\n\");\n } else if (providerMissing) {\n injectedText = [\n \"<chat-image-attachment-upload-error>\",\n \"The user attached one or more images, but no file-upload provider is configured for this app.\",\n \"Tell the user to connect or reconnect Builder.io from Settings → File uploads. If `connect-builder` is available, use it to render the inline connection card. Workspaces with a custom storage provider can also use one registered via registerFileUploadProvider().\",\n \"Until that's done, you can still SEE the image, but you do NOT have a URL to embed it in HTML or share with other apps.\",\n \"</chat-image-attachment-upload-error>\",\n ].join(\"\\n\");\n }\n\n return {\n attachments: list,\n uploaded,\n uploadedFiles,\n providerMissing,\n injectedText,\n };\n}\n"]}
@@ -15,6 +15,7 @@ export declare function assembleA2AFinalResponse(events: readonly AgentChatEvent
15
15
  responseText: string;
16
16
  finalText: string;
17
17
  };
18
+ declare function createDataWidgetActionEntries(): Record<string, ActionEntry>;
18
19
  type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
19
20
  export interface AgentChatPluginOptions {
20
21
  /** Template-specific actions (email ops, booking ops, etc.) */
@@ -292,6 +293,8 @@ export declare const _agentChatPromptSectionsForTests: {
292
293
  frameworkCore: string;
293
294
  frameworkCoreCompact: string;
294
295
  frameworkContextSections: Record<string, string>;
296
+ generateActionsPrompt: typeof generateActionsPrompt;
297
+ createDataWidgetActionEntries: typeof createDataWidgetActionEntries;
295
298
  };
296
299
  /**
297
300
  * Pre-load the agent's context: AGENTS.md (workspace/template/runtime
@@ -317,6 +320,19 @@ export declare const _agentChatPromptSectionsForTests: {
317
320
  * the bundle in dev so changes land instantly.
318
321
  */
319
322
  export declare function loadResourcesForPrompt(owner: string, compact?: boolean, selfAppId?: string): Promise<string>;
323
+ /**
324
+ * Generates a system prompt section describing registered template actions.
325
+ * This helps the agent prefer template-specific actions over raw db-query/db-exec.
326
+ *
327
+ * Two output modes:
328
+ *
329
+ * - `"tool"` — used in production, where template actions are registered
330
+ * as native Anthropic tools. Output reads `name(arg*: type; ...) — desc`.
331
+ * - `"cli"` — used in dev, where template actions are NOT registered as
332
+ * native tools and must be invoked via `bash(command="pnpm action ...")`.
333
+ * Output reads `pnpm action name --arg <type> [--opt <type>] — desc`.
334
+ */
335
+ declare function generateActionsPrompt(registry: Record<string, ActionEntry>, mode?: "cli" | "tool"): string;
320
336
  export declare function shouldBlockInProductCodeEditingSurface(input: {
321
337
  surface?: string | null;
322
338
  userAgent?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-chat-plugin.d.ts","sourceRoot":"","sources":["../../src/server/agent-chat-plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-chat-plugin.d.ts","sourceRoot":"","sources":["../../src/server/agent-chat-plugin.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,8 @@ import { updateTaskStatusMessage } from "../a2a/task-store.js";
33
33
  import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js";
34
34
  import { buildRuntimeContextPrompt } from "../agent/runtime-context.js";
35
35
  import { buildFrameworkCore, buildFrameworkCoreCompact, FIRST_SESSION_PERSONALIZATION, getModelFamilyOverlay, } from "./prompts/index.js";
36
+ import { ACTION_CHAT_UI_DATA_WIDGET_RENDERER } from "../action-ui.js";
37
+ import { dataWidgetResultSchema } from "../data-widgets/index.js";
36
38
  // Lazy fs — loaded via dynamic import() on first use.
37
39
  // This avoids require() which bundlers convert to createRequire(import.meta.url)
38
40
  // that crashes on CF Workers where import.meta.url is undefined.
@@ -643,6 +645,58 @@ function createUrlTools() {
643
645
  },
644
646
  };
645
647
  }
648
+ function createDataWidgetActionEntries() {
649
+ return {
650
+ "render-data-widget": {
651
+ readOnly: true,
652
+ parallelSafe: true,
653
+ chatUI: {
654
+ renderer: ACTION_CHAT_UI_DATA_WIDGET_RENDERER,
655
+ title: "Data widget",
656
+ description: "Render a validated native data table or chart in chat.",
657
+ },
658
+ tool: {
659
+ description: "Render a native Agent-Native chat data widget from compact, real data you already retrieved or the user provided. Use this for in-chat tables, charts, graphs, trends, and compact reports when no domain-specific action already returns a native widget. Never fabricate rows or metrics just to make a chart.",
660
+ parameters: {
661
+ type: "object",
662
+ properties: {
663
+ widget: {
664
+ type: "string",
665
+ enum: ["data-table", "data-chart", "data-insights"],
666
+ description: "Widget kind. Use data-chart for a chart, data-table for a table, or data-insights for a combined summary/chart/table card.",
667
+ },
668
+ widgetId: {
669
+ type: "string",
670
+ description: "Optional stable widget identifier.",
671
+ },
672
+ title: {
673
+ type: "string",
674
+ description: "Optional widget title.",
675
+ },
676
+ summary: {
677
+ type: "object",
678
+ description: "Optional scalar summary values for data-insights cards.",
679
+ },
680
+ display: {
681
+ type: "object",
682
+ description: "Optional display metadata: title, description, primaryAction.",
683
+ },
684
+ table: {
685
+ type: "object",
686
+ description: "For data-table/data-insights: { title?, columns: [{ key, label, align? }], rows, totalRows?, sampledRows?, truncated? }.",
687
+ },
688
+ chartSeries: {
689
+ type: "object",
690
+ description: "For data-chart/data-insights: { type: 'bar'|'line'|'area', title?, xKey, series: [{ key, label, color? }], data, sampled? }.",
691
+ },
692
+ },
693
+ required: ["widget"],
694
+ },
695
+ },
696
+ run: async (args) => dataWidgetResultSchema.parse(args),
697
+ },
698
+ };
699
+ }
646
700
  /**
647
701
  * Creates db-* tools (db-query, db-exec, db-patch, db-schema) as native tools.
648
702
  * These let the agent read and write the app's own SQL database. Scoping to
@@ -1951,6 +2005,8 @@ export const _agentChatPromptSectionsForTests = (() => {
1951
2005
  frameworkCore,
1952
2006
  frameworkCoreCompact,
1953
2007
  frameworkContextSections: FRAMEWORK_CONTEXT_SECTIONS,
2008
+ generateActionsPrompt,
2009
+ createDataWidgetActionEntries,
1954
2010
  };
1955
2011
  })();
1956
2012
  /**
@@ -2137,10 +2193,13 @@ function generateActionsPrompt(registry, mode = "tool") {
2137
2193
  if (!registry || Object.keys(registry).length === 0)
2138
2194
  return "";
2139
2195
  const actionEntries = Object.entries(registry);
2196
+ const nativeWidgetNote = (entry) => entry.chatUI && typeof entry.chatUI.renderer === "string"
2197
+ ? ` Native chat widget: \`${entry.chatUI.renderer}\`.`
2198
+ : "";
2140
2199
  if (mode === "tool") {
2141
2200
  const summaryLines = actionEntries.map(([name, entry]) => {
2142
2201
  const desc = compactPromptLine(entry.tool.description, MAX_ACTION_SUMMARY_DESCRIPTION_CHARS);
2143
- return `- \`${name}\` — ${desc}`;
2202
+ return `- \`${name}\` — ${desc}${nativeWidgetNote(entry)}`;
2144
2203
  });
2145
2204
  return `\n\n## Available Actions
2146
2205
 
@@ -2154,7 +2213,7 @@ ${summaryLines.join("\n")}`;
2154
2213
  const requiredFields = new Set(entry.tool.parameters?.required ?? []);
2155
2214
  // CLI mode: emit `pnpm action <name> --required <type> [--optional <type>]`
2156
2215
  if (!params || Object.keys(params).length === 0) {
2157
- return `- \`pnpm action ${name}\` — ${desc}`;
2216
+ return `- \`pnpm action ${name}\` — ${desc}${nativeWidgetNote(entry)}`;
2158
2217
  }
2159
2218
  const entries = Object.entries(params);
2160
2219
  // Required first (alphabetical), then optional (alphabetical)
@@ -2181,7 +2240,7 @@ ${summaryLines.join("\n")}`;
2181
2240
  }
2182
2241
  const cmd = ["pnpm action " + name, ...required, ...optional].join(" ");
2183
2242
  const requiredNote = requiredNames.length > 0 ? ` Required: ${requiredNames.join(", ")}.` : "";
2184
- return `- \`${cmd}\` — ${desc}.${requiredNote}`;
2243
+ return `- \`${cmd}\` — ${desc}.${requiredNote}${nativeWidgetNote(entry)}`;
2185
2244
  });
2186
2245
  return `\n\n## Available Actions
2187
2246
 
@@ -2749,10 +2808,13 @@ export function createAgentChatPlugin(options) {
2749
2808
  workspaceFilesTool = createWorkspaceFilesTool();
2750
2809
  }
2751
2810
  catch { }
2752
- let toolActions = {};
2811
+ let toolActions = createDataWidgetActionEntries();
2753
2812
  try {
2754
2813
  const { createExtensionActionEntries } = await import("../extensions/actions.js");
2755
- toolActions = createExtensionActionEntries();
2814
+ toolActions = {
2815
+ ...toolActions,
2816
+ ...createExtensionActionEntries(),
2817
+ };
2756
2818
  }
2757
2819
  catch { }
2758
2820
  let browserSessionTools = {};