@agent-native/core 0.106.3 → 0.107.0

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 (196) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +13 -0
  3. package/corpus/core/docs/content/integrations.mdx +597 -0
  4. package/corpus/core/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
  5. package/corpus/core/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
  6. package/corpus/core/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
  7. package/corpus/core/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
  8. package/corpus/core/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
  9. package/corpus/core/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
  10. package/corpus/core/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
  11. package/corpus/core/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
  12. package/corpus/core/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
  13. package/corpus/core/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
  14. package/corpus/core/docs/content/mcp-clients.mdx +51 -1
  15. package/corpus/core/docs/content/organizations-teams-permissions.mdx +413 -0
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/client/AssistantChat.tsx +13 -1
  18. package/corpus/core/src/client/chat/message-components.tsx +41 -0
  19. package/corpus/core/src/client/composer/TiptapComposer.tsx +45 -8
  20. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +30 -11
  21. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +240 -0
  22. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +159 -40
  23. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +433 -4
  24. package/corpus/core/src/client/resources/mcp-integration-logos.ts +108 -0
  25. package/corpus/core/src/deploy/build.ts +32 -0
  26. package/corpus/core/src/extensions/html-shell.ts +33 -1
  27. package/corpus/core/src/ingestion/office.ts +1 -2
  28. package/corpus/core/src/ingestion/pptx.ts +2 -4
  29. package/corpus/core/src/localization/default-messages.ts +126 -1
  30. package/corpus/core/src/mcp/actions/call-mcp-tool.ts +1 -0
  31. package/corpus/core/src/server/index.ts +4 -0
  32. package/corpus/core/src/server/org-admin.ts +28 -0
  33. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
  34. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
  35. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
  36. package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +64 -0
  37. package/corpus/templates/analytics/AGENTS.md +5 -0
  38. package/corpus/templates/analytics/actions/clone-creative-context-dashboard.ts +86 -0
  39. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -0
  40. package/corpus/templates/analytics/app/i18n/zh-TW.ts +1 -0
  41. package/corpus/templates/analytics/app/i18n-data.ts +1 -0
  42. package/corpus/templates/analytics/app/pages/Ask.tsx +8 -4
  43. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +59 -0
  44. package/corpus/templates/analytics/app/routes/agent.tsx +7 -1
  45. package/corpus/templates/analytics/changelog/2026-07-17-library-now-flags-published-dashboards-with-newer-versions-a.md +6 -0
  46. package/corpus/templates/analytics/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  47. package/corpus/templates/analytics/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  48. package/corpus/templates/analytics/package.json +1 -0
  49. package/corpus/templates/analytics/server/lib/native-creative-context.ts +158 -0
  50. package/corpus/templates/analytics/server/plugins/creative-context.ts +10 -0
  51. package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +12 -3
  52. package/corpus/templates/assets/AGENTS.md +5 -0
  53. package/corpus/templates/assets/actions/clone-creative-context-asset.ts +74 -0
  54. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -1
  55. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +308 -178
  56. package/corpus/templates/assets/app/routes/library.tsx +5 -11
  57. package/corpus/templates/assets/changelog/2026-07-17-library-now-flags-published-assets-with-newer-versions-and-l.md +6 -0
  58. package/corpus/templates/assets/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  59. package/corpus/templates/assets/server/lib/native-creative-context.ts +198 -0
  60. package/corpus/templates/assets/server/plugins/creative-context.ts +4 -0
  61. package/corpus/templates/brain/actions/provider-api-request.ts +1 -0
  62. package/corpus/templates/calendar/actions/provider-api-request.ts +1 -0
  63. package/corpus/templates/clips/actions/get-recording-insights.ts +7 -4
  64. package/corpus/templates/clips/actions/list-recordings.ts +48 -0
  65. package/corpus/templates/clips/actions/list-viewers.ts +2 -1
  66. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +3 -1
  67. package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -3
  68. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +11 -6
  69. package/corpus/templates/clips/app/hooks/use-view-tracking.ts +6 -1
  70. package/corpus/templates/clips/app/lib/ffmpeg-export.ts +4 -2
  71. package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -3
  72. package/corpus/templates/clips/changelog/2026-07-17-dictation-keeps-sentence-spacing-and-no-longer-crashes-when-.md +6 -0
  73. package/corpus/templates/clips/changelog/2026-07-17-shared-clips-now-preserve-editor-access-and-show-editor-insi.md +6 -0
  74. package/corpus/templates/clips/changelog/2026-07-17-stitching-clips-no-longer-fails-when-editor-media-metadata-i.md +6 -0
  75. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +14 -8
  76. package/corpus/templates/clips/shared/view-analytics.ts +4 -0
  77. package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +12 -3
  78. package/corpus/templates/content/AGENTS.md +5 -0
  79. package/corpus/templates/content/actions/clone-creative-context-document.ts +61 -0
  80. package/corpus/templates/content/actions/provider-api-request.ts +1 -0
  81. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +21 -0
  82. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +29 -0
  83. package/corpus/templates/content/changelog/2026-07-17-library-now-flags-published-documents-with-newer-versions-an.md +6 -0
  84. package/corpus/templates/content/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  85. package/corpus/templates/content/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  86. package/corpus/templates/content/parity/matrix.md +1 -1
  87. package/corpus/templates/content/parity/matrix.ts +1 -0
  88. package/corpus/templates/content/server/lib/native-creative-context.ts +191 -0
  89. package/corpus/templates/content/server/plugins/creative-context.ts +8 -1
  90. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -3
  91. package/corpus/templates/design/AGENTS.md +5 -0
  92. package/corpus/templates/design/actions/clone-creative-context-design-native.ts +111 -0
  93. package/corpus/templates/design/actions/provider-api-request.ts +1 -0
  94. package/corpus/templates/design/app/pages/Index.tsx +44 -0
  95. package/corpus/templates/design/app/pages/design-editor/types.ts +1 -0
  96. package/corpus/templates/design/changelog/2026-07-17-library-now-flags-published-designs-with-newer-versions-and-.md +6 -0
  97. package/corpus/templates/design/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  98. package/corpus/templates/design/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  99. package/corpus/templates/design/server/lib/native-creative-context.ts +265 -0
  100. package/corpus/templates/design/server/plugins/creative-context.ts +8 -1
  101. package/corpus/templates/dispatch/actions/provider-api-request.ts +1 -0
  102. package/corpus/templates/mail/actions/provider-api-request.ts +1 -0
  103. package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +11 -3
  104. package/corpus/templates/slides/AGENTS.md +5 -0
  105. package/corpus/templates/slides/actions/clone-creative-context-deck.ts +96 -0
  106. package/corpus/templates/slides/actions/extract-pdf.ts +3 -2
  107. package/corpus/templates/slides/actions/import-file.ts +3 -2
  108. package/corpus/templates/slides/actions/provider-api-request.ts +1 -0
  109. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +27 -0
  110. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -0
  111. package/corpus/templates/slides/changelog/2026-07-17-library-now-flags-published-decks-with-newer-versions-and-le.md +6 -0
  112. package/corpus/templates/slides/changelog/2026-07-17-pdf-and-powerpoint-imports-now-work-reliably-in-hosted-decks.md +6 -0
  113. package/corpus/templates/slides/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  114. package/corpus/templates/slides/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  115. package/corpus/templates/slides/server/lib/native-creative-context.ts +288 -0
  116. package/corpus/templates/slides/server/plugins/creative-context.ts +4 -0
  117. package/dist/client/AssistantChat.d.ts.map +1 -1
  118. package/dist/client/AssistantChat.js +8 -2
  119. package/dist/client/AssistantChat.js.map +1 -1
  120. package/dist/client/chat/message-components.d.ts.map +1 -1
  121. package/dist/client/chat/message-components.js +35 -1
  122. package/dist/client/chat/message-components.js.map +1 -1
  123. package/dist/client/composer/TiptapComposer.d.ts +7 -0
  124. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  125. package/dist/client/composer/TiptapComposer.js +37 -8
  126. package/dist/client/composer/TiptapComposer.js.map +1 -1
  127. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  128. package/dist/client/integrations/IntegrationsPanel.js +3 -2
  129. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  130. package/dist/client/resources/McpConnectionSuggestion.d.ts +9 -0
  131. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -0
  132. package/dist/client/resources/McpConnectionSuggestion.js +115 -0
  133. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -0
  134. package/dist/client/resources/McpIntegrationDialog.d.ts +2 -1
  135. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  136. package/dist/client/resources/McpIntegrationDialog.js +48 -20
  137. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  138. package/dist/client/resources/mcp-integration-catalog.d.ts +9 -0
  139. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  140. package/dist/client/resources/mcp-integration-catalog.js +387 -4
  141. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  142. package/dist/client/resources/mcp-integration-logos.d.ts +2 -0
  143. package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -0
  144. package/dist/client/resources/mcp-integration-logos.js +109 -0
  145. package/dist/client/resources/mcp-integration-logos.js.map +1 -0
  146. package/dist/collab/awareness.d.ts +2 -2
  147. package/dist/collab/awareness.d.ts.map +1 -1
  148. package/dist/deploy/build.d.ts.map +1 -1
  149. package/dist/deploy/build.js +23 -0
  150. package/dist/deploy/build.js.map +1 -1
  151. package/dist/extensions/html-shell.d.ts.map +1 -1
  152. package/dist/extensions/html-shell.js +33 -1
  153. package/dist/extensions/html-shell.js.map +1 -1
  154. package/dist/ingestion/office.d.ts.map +1 -1
  155. package/dist/ingestion/office.js +1 -2
  156. package/dist/ingestion/office.js.map +1 -1
  157. package/dist/ingestion/pptx.js +2 -4
  158. package/dist/ingestion/pptx.js.map +1 -1
  159. package/dist/localization/default-messages.d.ts +98 -0
  160. package/dist/localization/default-messages.d.ts.map +1 -1
  161. package/dist/localization/default-messages.js +99 -1
  162. package/dist/localization/default-messages.js.map +1 -1
  163. package/dist/mcp/actions/call-mcp-tool.js +1 -0
  164. package/dist/mcp/actions/call-mcp-tool.js.map +1 -1
  165. package/dist/notifications/routes.d.ts +1 -1
  166. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  167. package/dist/resources/handlers.d.ts +1 -1
  168. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  169. package/dist/server/index.d.ts +1 -0
  170. package/dist/server/index.d.ts.map +1 -1
  171. package/dist/server/index.js +1 -0
  172. package/dist/server/index.js.map +1 -1
  173. package/dist/server/org-admin.d.ts +3 -0
  174. package/dist/server/org-admin.d.ts.map +1 -0
  175. package/dist/server/org-admin.js +24 -0
  176. package/dist/server/org-admin.js.map +1 -0
  177. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
  178. package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
  179. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
  180. package/docs/content/integrations.mdx +597 -0
  181. package/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
  182. package/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
  183. package/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
  184. package/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
  185. package/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
  186. package/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
  187. package/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
  188. package/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
  189. package/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
  190. package/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
  191. package/docs/content/mcp-clients.mdx +51 -1
  192. package/docs/content/organizations-teams-permissions.mdx +413 -0
  193. package/package.json +1 -1
  194. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
  195. package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
  196. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
@@ -8,6 +8,7 @@ import {
8
8
  injectSessionReplayIframeBootstrap,
9
9
  SESSION_REPLAY_IFRAME_ATTRIBUTE,
10
10
  } from "@agent-native/core/client";
11
+ import { CreativeContextShareSheet } from "@agent-native/creative-context/client";
11
12
  import {
12
13
  useSetHeaderActions,
13
14
  useSetPageTitle,
@@ -109,6 +110,7 @@ export default function Index() {
109
110
  );
110
111
  const [renameId, setRenameId] = useState<string | null>(null);
111
112
  const [renameDraft, setRenameDraft] = useState("");
113
+ const [contextDesigns, setContextDesigns] = useState<Design[]>([]);
112
114
 
113
115
  const anchorElRef = useRef<HTMLElement | null>(null);
114
116
  const anchorRef = useRef<HTMLElement | null>(null);
@@ -809,6 +811,21 @@ export default function Index() {
809
811
  </TooltipTrigger>
810
812
  <TooltipContent>{t("home.clearSelection")}</TooltipContent>
811
813
  </Tooltip>
814
+ <Button
815
+ variant="outline"
816
+ size="sm"
817
+ onClick={() =>
818
+ setContextDesigns(
819
+ designs.filter((design) =>
820
+ selectedDesignIds.has(design.id),
821
+ ),
822
+ )
823
+ }
824
+ className="cursor-pointer"
825
+ >
826
+ <IconPlus className="w-3.5 h-3.5" />
827
+ {t("creativeContext.addToContext" /* i18n-key-ignore */)}
828
+ </Button>
812
829
  <Button
813
830
  variant="destructive"
814
831
  size="sm"
@@ -936,6 +953,18 @@ export default function Index() {
936
953
  <IconCopy className="w-3.5 h-3.5 me-2" />
937
954
  {t("home.duplicate")}
938
955
  </DropdownMenuItem>
956
+ <DropdownMenuItem
957
+ onSelect={(event) => {
958
+ event.preventDefault();
959
+ setContextDesigns([design]);
960
+ }}
961
+ className="cursor-pointer"
962
+ >
963
+ <IconPlus className="w-3.5 h-3.5 me-2" />
964
+ {t(
965
+ "creativeContext.addToContext" /* i18n-key-ignore */,
966
+ )}
967
+ </DropdownMenuItem>
939
968
  <DropdownMenuItem
940
969
  onClick={() => setDeleteId(design.id)}
941
970
  className="text-red-400 focus:text-red-400 cursor-pointer"
@@ -954,6 +983,21 @@ export default function Index() {
954
983
  )}
955
984
  </main>
956
985
 
986
+ <CreativeContextShareSheet
987
+ open={contextDesigns.length > 0}
988
+ onOpenChange={(open) => {
989
+ if (!open) setContextDesigns([]);
990
+ }}
991
+ resources={contextDesigns.map((design) => ({
992
+ appId: "design",
993
+ resourceType: "design",
994
+ resourceId: design.id,
995
+ title: design.title,
996
+ updatedAt: design.updatedAt ?? design.createdAt,
997
+ preview: { kind: "document", label: "Design" },
998
+ }))}
999
+ />
1000
+
957
1001
  <PromptPopover
958
1002
  open={showNewPrompt}
959
1003
  onOpenChange={handleNewPromptOpenChange}
@@ -52,6 +52,7 @@ export type DesignAccessRole = "owner" | "admin" | "editor" | "viewer";
52
52
  export interface DesignData {
53
53
  id: string;
54
54
  title: string;
55
+ updatedAt: string;
55
56
  description?: string;
56
57
  projectType: string;
57
58
  designSystemId?: string | null;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Library now flags published designs with newer versions and lets you submit the update in place.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Resources can be added to Creative Context from their action menus.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ You can add exact, approved artifact versions to governed Creative Contexts for safe reuse.
@@ -0,0 +1,265 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { putPrivateBlob } from "@agent-native/core/private-blob";
4
+ import { accessFilter, resolveAccess } from "@agent-native/core/sharing";
5
+ import {
6
+ renderSafeNativeHtmlPreviews,
7
+ serializePrivateBlobHandle,
8
+ type NativeResourceCaptureAdapter,
9
+ } from "@agent-native/creative-context/server";
10
+ import { and, inArray } from "drizzle-orm";
11
+ import { nanoid } from "nanoid";
12
+
13
+ import { getDb, schema } from "../db/index.js";
14
+ import { buildDesignSnapshot } from "./design-snapshot.js";
15
+
16
+ function previewText(value: string, limit = 280) {
17
+ return value
18
+ .replace(/<[^>]+>/g, " ")
19
+ .replace(/\s+/g, " ")
20
+ .trim()
21
+ .slice(0, limit);
22
+ }
23
+
24
+ async function captureDesignPreviewMedia(input: {
25
+ designId: string;
26
+ ownerEmail: string;
27
+ files: Array<{
28
+ id: string;
29
+ filename: string;
30
+ fileType: string;
31
+ content: string;
32
+ }>;
33
+ }) {
34
+ const previewFiles = input.files
35
+ .filter((file) => file.fileType === "html")
36
+ .slice(0, 12);
37
+ const rendered = await renderSafeNativeHtmlPreviews(
38
+ previewFiles.map((file) => ({
39
+ id: file.id,
40
+ html: file.content,
41
+ width: 1_280,
42
+ height: 800,
43
+ })),
44
+ );
45
+ const media = new Map<
46
+ string,
47
+ {
48
+ kind: "image";
49
+ mimeType: "image/png";
50
+ accessMode: "private";
51
+ storageKey: string;
52
+ altText: string;
53
+ contentHash: string;
54
+ width: number;
55
+ height: number;
56
+ metadata: { role: "design-preview"; fileId: string };
57
+ }
58
+ >();
59
+ await Promise.all(
60
+ rendered.map(async (preview) => {
61
+ const contentHash = createHash("sha256")
62
+ .update(preview.data)
63
+ .digest("hex");
64
+ const handle = await putPrivateBlob({
65
+ data: preview.data,
66
+ filename: `${input.designId}-${preview.id}.preview.png`,
67
+ mimeType: "image/png",
68
+ ownerEmail: input.ownerEmail,
69
+ key: `creative-context/design/${input.designId}/previews/${contentHash}.png`,
70
+ metadata: {
71
+ appId: "design",
72
+ resourceType: "design-preview",
73
+ resourceId: input.designId,
74
+ contentHash,
75
+ },
76
+ }).catch(() => null);
77
+ if (!handle) return;
78
+ const file = previewFiles.find(
79
+ (candidate) => candidate.id === preview.id,
80
+ );
81
+ media.set(preview.id, {
82
+ kind: "image",
83
+ mimeType: "image/png",
84
+ accessMode: "private",
85
+ storageKey: serializePrivateBlobHandle(handle),
86
+ altText: file?.filename ?? "Design preview",
87
+ contentHash,
88
+ width: preview.width,
89
+ height: preview.height,
90
+ metadata: { role: "design-preview", fileId: preview.id },
91
+ });
92
+ }),
93
+ );
94
+ return media;
95
+ }
96
+
97
+ export const nativeDesignCreativeContextAdapter: NativeResourceCaptureAdapter =
98
+ {
99
+ appId: "design",
100
+ resourceType: "design",
101
+ async listResourceVersions(resourceIds) {
102
+ if (!resourceIds.length) return [];
103
+ return getDb()
104
+ .select({
105
+ resourceId: schema.designs.id,
106
+ sourceModifiedAt: schema.designs.updatedAt,
107
+ })
108
+ .from(schema.designs)
109
+ .where(
110
+ and(
111
+ inArray(schema.designs.id, [...resourceIds]),
112
+ accessFilter(schema.designs, schema.designShares),
113
+ ),
114
+ );
115
+ },
116
+ async capture(reference) {
117
+ const access = await resolveAccess("design", reference.resourceId);
118
+ if (!access) throw new Error("Design not found");
119
+ const design = access.resource as typeof schema.designs.$inferSelect;
120
+ if (
121
+ reference.expectedUpdatedAt &&
122
+ reference.expectedUpdatedAt !== design.updatedAt
123
+ )
124
+ throw new Error(
125
+ "Design changed before it could be submitted to Context.",
126
+ );
127
+ const snapshot = await buildDesignSnapshot(design.id, design.data);
128
+ const payload = JSON.stringify({
129
+ designId: design.id,
130
+ designData: design.data,
131
+ files: snapshot.files,
132
+ tweaks: snapshot.tweaks,
133
+ appliedTweaks: snapshot.appliedTweaks,
134
+ resolvedCssVars: snapshot.resolvedCssVars,
135
+ });
136
+ const contentHash = createHash("sha256").update(payload).digest("hex");
137
+ const versionId = nanoid();
138
+ await getDb().insert(schema.designVersions).values({
139
+ id: versionId,
140
+ designId: design.id,
141
+ label: "Creative Context submission",
142
+ snapshot: payload,
143
+ createdAt: new Date().toISOString(),
144
+ });
145
+ const handle = await putPrivateBlob({
146
+ data: Buffer.from(payload),
147
+ filename: `${design.id}.design.json`,
148
+ mimeType: "application/json",
149
+ ownerEmail: design.ownerEmail,
150
+ key: `creative-context/design/${design.id}/${contentHash}.json`,
151
+ metadata: {
152
+ appId: "design",
153
+ resourceType: "design",
154
+ resourceId: design.id,
155
+ contentHash,
156
+ },
157
+ });
158
+ if (!handle)
159
+ throw new Error(
160
+ "Private blob storage is required to submit a design to Context.",
161
+ );
162
+ const previewMedia = await captureDesignPreviewMedia({
163
+ designId: design.id,
164
+ ownerEmail: design.ownerEmail,
165
+ files: snapshot.files,
166
+ });
167
+ const sourceModifiedAt = design.updatedAt ?? undefined;
168
+ return {
169
+ artifactKey: `design:design:${design.id}`,
170
+ source: {
171
+ name: "Design",
172
+ kind: "native-app",
173
+ externalRef: design.id,
174
+ access: {
175
+ visibility: design.visibility ?? "private",
176
+ canManage: access.role === "owner" || access.role === "admin",
177
+ },
178
+ },
179
+ items: [
180
+ {
181
+ externalId: `native:design:design:${design.id}`,
182
+ kind: "design-project",
183
+ title: design.title,
184
+ canonicalUrl: `/design/${design.id}`,
185
+ mimeType: "application/json",
186
+ content: snapshot.files
187
+ .map(
188
+ (file) =>
189
+ `${file.filename}\n${file.content.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ")}`,
190
+ )
191
+ .join("\n")
192
+ .slice(0, 40_000),
193
+ summary: `${snapshot.files.length} saved design files captured as an immutable version.`,
194
+ contentHash,
195
+ sourceModifiedAt,
196
+ sourceVersion: versionId,
197
+ metadata: {
198
+ preview: {
199
+ type: "design",
200
+ fileCount: snapshot.files.length,
201
+ frames: snapshot.files.slice(0, 24).map((file) => ({
202
+ title: file.filename.slice(0, 160),
203
+ fileType: file.fileType.slice(0, 80),
204
+ excerpt: previewText(file.content),
205
+ })),
206
+ },
207
+ },
208
+ media: [...previewMedia.values()],
209
+ edges: snapshot.files.map((file) => ({
210
+ relation: "contains",
211
+ toExternalId: `native:design:design:${design.id}:frame:${file.id}`,
212
+ })),
213
+ },
214
+ ...snapshot.files.map((file) => {
215
+ const content = file.content
216
+ .replace(/<[^>]+>/g, " ")
217
+ .replace(/\s+/g, " ")
218
+ .trim();
219
+ return {
220
+ externalId: `native:design:design:${design.id}:frame:${file.id}`,
221
+ kind: "design-frame",
222
+ title: file.filename,
223
+ canonicalUrl: `/design/${design.id}`,
224
+ mimeType: "text/html",
225
+ content,
226
+ summary: content.slice(0, 500),
227
+ contentHash: createHash("sha256")
228
+ .update(file.content)
229
+ .digest("hex"),
230
+ sourceModifiedAt,
231
+ sourceVersion: versionId,
232
+ metadata: {
233
+ preview: {
234
+ type: "design-frame",
235
+ title: file.filename.slice(0, 160),
236
+ fileType: file.fileType.slice(0, 80),
237
+ excerpt: previewText(file.content),
238
+ },
239
+ },
240
+ media: previewMedia.has(file.id)
241
+ ? [previewMedia.get(file.id)!]
242
+ : undefined,
243
+ };
244
+ }),
245
+ ],
246
+ privateMetadata: {
247
+ nativeResource: {
248
+ appId: "design",
249
+ resourceType: "design",
250
+ resourceId: design.id,
251
+ expectedUpdatedAt: reference.expectedUpdatedAt,
252
+ },
253
+ clone: {
254
+ handle,
255
+ appId: "design",
256
+ resourceType: "design",
257
+ resourceId: design.id,
258
+ contentHash,
259
+ sourceVersion: versionId,
260
+ updatedAt: design.updatedAt,
261
+ },
262
+ },
263
+ };
264
+ },
265
+ };
@@ -1,7 +1,12 @@
1
1
  import { registerOnboardingStep } from "@agent-native/core/onboarding";
2
- import { setupCreativeContext } from "@agent-native/creative-context/server";
2
+ import {
3
+ registerNativeResourceCaptureAdapter,
4
+ setupCreativeContext,
5
+ } from "@agent-native/creative-context/server";
3
6
  import { listContextSources } from "@agent-native/creative-context/store";
4
7
 
8
+ import { nativeDesignCreativeContextAdapter } from "../lib/native-creative-context.js";
9
+
5
10
  registerOnboardingStep({
6
11
  id: "creative-context-library",
7
12
  order: 18,
@@ -28,4 +33,6 @@ registerOnboardingStep({
28
33
  },
29
34
  });
30
35
 
36
+ registerNativeResourceCaptureAdapter(nativeDesignCreativeContextAdapter);
37
+
31
38
  export default setupCreativeContext({ appId: "design" });
@@ -196,6 +196,7 @@ export default defineAction({
196
196
  ),
197
197
  }),
198
198
  http: false,
199
+ toolCallable: false,
199
200
  run: async (args) => {
200
201
  if (args.stageAs) {
201
202
  const ctx = getCredentialContext();
@@ -205,6 +205,7 @@ export default defineAction({
205
205
  ),
206
206
  }),
207
207
  http: false,
208
+ toolCallable: false,
208
209
  run: async (args) => {
209
210
  if (args.stageAs) {
210
211
  const ctx = getCredentialContext();
@@ -35,9 +35,11 @@ role cannot silently stand in for another:
35
35
 
36
36
  1. Follow the user's explicit instructions and the object currently selected in
37
37
  the app.
38
- 2. Read the `creative-context` application-state record. If
39
- `pinnedPackId` is set, prefer that immutable pack. Otherwise use
40
- `currentPackId` when it records context already selected for this session.
38
+ 2. Read the `creative-context` application-state record. If `pinnedPackId` is
39
+ set, replay that immutable pack. Otherwise honor `selectedContextId`; when it
40
+ is unset, let retrieval use Default plus at most one app-bound or
41
+ semantically matching specialty context. `currentPackId` is the receipt from
42
+ the latest materialized generation, not a replacement for context selection.
41
43
  3. Call `search-creative-context` with a narrow query for exact facts, visual
42
44
  language, audience guidance, prior decisions, or reusable references.
43
45
  4. Call `get-context-item` only for the specific search result versions
@@ -60,6 +62,7 @@ The single state key is `creative-context`:
60
62
  ```json
61
63
  {
62
64
  "contextMode": "auto",
65
+ "selectedContextId": null,
63
66
  "currentPackId": null,
64
67
  "pinnedPackId": null
65
68
  }
@@ -120,3 +123,8 @@ If `nativeCode.content` is `null` and `oversized` is true, use the named
120
123
  `get-context-item`, but the inline content is only the validated manifest shell.
121
124
  Never concatenate a truncated fragment or use delimited `version.content` as
122
125
  HTML.
126
+
127
+ For an app-created deck snapshot, use `clone-creative-context-deck` instead.
128
+ That typed action resolves the exact approved private deck payload and runs it
129
+ through Slides' normal deck creation and sanitization path. Generic context
130
+ actions must never receive or return that payload.
@@ -44,6 +44,11 @@ Detailed deck, slide-editing, image, design-system, and export workflows live in
44
44
  `.agents/skills/creative-context/SKILL.md`: explicit request and current deck
45
45
  first, then a pinned/current pack, then narrow library search. Respect
46
46
  `creative-context.contextMode: "off"` without silently restoring a pack.
47
+ - To submit a deck to a governed Creative Context, use the Context tab or
48
+ `manage-context-membership`; it captures one immutable deck version. Reuse
49
+ only a returned opaque native clone reference through the Slides clone action.
50
+ Use `operation="submit-latest"` with a Library membership id when its native
51
+ update status reports `update-available`.
47
52
 
48
53
  ## Persistence Model
49
54
 
@@ -0,0 +1,96 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { defineAction } from "@agent-native/core";
4
+ import {
5
+ readPrivateBlob,
6
+ type PrivateBlobHandle,
7
+ } from "@agent-native/core/private-blob";
8
+ import {
9
+ getRequestOrgId,
10
+ getRequestUserEmail,
11
+ } from "@agent-native/core/server/request-context";
12
+ import { resolveNativeContextCloneReference } from "@agent-native/creative-context/server";
13
+ import { eq } from "drizzle-orm";
14
+ import { nanoid } from "nanoid";
15
+ import { z } from "zod";
16
+
17
+ import { getDb, schema } from "../server/db/index.js";
18
+ import { getDeckUrl } from "./_app-url.js";
19
+
20
+ export default defineAction({
21
+ description:
22
+ "Clone one exact governed Slides deck version without exposing its native payload.",
23
+ schema: z.object({
24
+ contextId: z.string(),
25
+ artifactKey: z.string(),
26
+ resourceId: z.string(),
27
+ expectedUpdatedAt: z.string().optional(),
28
+ title: z.string().optional(),
29
+ }),
30
+ publicAgent: { expose: true, readOnly: false, requiresAuth: true },
31
+ run: async (args) => {
32
+ const reference = await resolveNativeContextCloneReference({
33
+ appId: "slides",
34
+ resourceType: "deck",
35
+ resourceId: args.resourceId,
36
+ expectedUpdatedAt: args.expectedUpdatedAt,
37
+ contextId: args.contextId,
38
+ artifactKey: args.artifactKey,
39
+ });
40
+ const raw = await readPrivateBlob(
41
+ reference.cloneHandle as PrivateBlobHandle,
42
+ );
43
+ const data = Buffer.from(raw.data).toString("utf8");
44
+ const contentHash = createHash("sha256").update(data).digest("hex");
45
+ if (
46
+ raw.metadata?.appId !== "slides" ||
47
+ raw.metadata?.resourceType !== "deck" ||
48
+ raw.metadata?.resourceId !== args.resourceId ||
49
+ raw.metadata?.contentHash !== contentHash
50
+ )
51
+ throw new Error(
52
+ "Governed deck clone payload failed integrity verification.",
53
+ );
54
+ const deck = JSON.parse(data) as Record<string, unknown> & {
55
+ slides?: Array<Record<string, unknown>>;
56
+ title?: string;
57
+ designSystemId?: string;
58
+ };
59
+ const email = getRequestUserEmail();
60
+ if (!email) throw new Error("no authenticated user");
61
+ const id = `deck-${nanoid()}`;
62
+ const now = new Date().toISOString();
63
+ const title = args.title?.trim() || `Copy of ${deck.title ?? "deck"}`;
64
+ deck.title = title;
65
+ deck.createdAt = now;
66
+ deck.updatedAt = now;
67
+ deck.slides = (deck.slides ?? []).map((slide) => ({
68
+ ...slide,
69
+ id: `slide-${nanoid(8)}`,
70
+ }));
71
+ await getDb()
72
+ .insert(schema.decks)
73
+ .values({
74
+ id,
75
+ title,
76
+ data: JSON.stringify(deck),
77
+ designSystemId: deck.designSystemId ?? null,
78
+ ownerEmail: email,
79
+ orgId: getRequestOrgId() ?? null,
80
+ createdAt: now,
81
+ updatedAt: now,
82
+ });
83
+ const [saved] = await getDb()
84
+ .select({ id: schema.decks.id, title: schema.decks.title })
85
+ .from(schema.decks)
86
+ .where(eq(schema.decks.id, id));
87
+ if (!saved) throw new Error("Deck clone did not persist.");
88
+ return {
89
+ id: saved.id,
90
+ title: saved.title,
91
+ slideCount: deck.slides.length,
92
+ url: getDeckUrl(id),
93
+ clonedExactVersion: reference.publishedItemVersionId,
94
+ };
95
+ },
96
+ });
@@ -10,13 +10,14 @@ export default async function (args: string[]) {
10
10
  }
11
11
 
12
12
  const buf = fs.readFileSync(pdfPath);
13
- const { CanvasFactory } = await import("pdf-parse/worker");
13
+ const { CanvasFactory, getData } = await import("pdf-parse/worker");
14
14
  const { PDFParse } = await import("pdf-parse");
15
+ PDFParse.setWorker(getData());
15
16
  const pdf = new PDFParse({
16
17
  data: new Uint8Array(buf),
17
18
  CanvasFactory,
18
19
  });
19
- const result = await pdf.getText();
20
+ const result = await pdf.getText().finally(() => pdf.destroy());
20
21
  const pages = result.pages || [];
21
22
  console.log("Total pages:", pages.length);
22
23
  pages.forEach((page: { num: number; text: string }) => {
@@ -211,15 +211,16 @@ export default defineAction({
211
211
  }
212
212
 
213
213
  if (detectedFormat === "pdf") {
214
- const { CanvasFactory } = await import("pdf-parse/worker");
214
+ const { CanvasFactory, getData } = await import("pdf-parse/worker");
215
215
  const { PDFParse } = await import("pdf-parse");
216
+ PDFParse.setWorker(getData());
216
217
  const { convertSectionsToSlides } =
217
218
  await import("../server/handlers/import/html-converter.js");
218
219
  const pdf = new PDFParse({
219
220
  data: new Uint8Array(fileBuffer),
220
221
  CanvasFactory,
221
222
  });
222
- const result = await pdf.getText();
223
+ const result = await pdf.getText().finally(() => pdf.destroy());
223
224
  const pages = normalizePdfPages(result);
224
225
  const textPages = pages.filter((p) => p.text.trim());
225
226
  const title = titleFromPath(filename);
@@ -204,6 +204,7 @@ export default defineAction({
204
204
  ),
205
205
  }),
206
206
  http: false,
207
+ toolCallable: false,
207
208
  run: async (args) => {
208
209
  if (args.stageAs) {
209
210
  const ctx = getCredentialContext();
@@ -1,10 +1,12 @@
1
1
  import { VisibilityBadge, useT } from "@agent-native/core/client";
2
+ import { CreativeContextShareSheet } from "@agent-native/creative-context/client";
2
3
  import {
3
4
  IconDots,
4
5
  IconTrash,
5
6
  IconCopy,
6
7
  IconPencil,
7
8
  IconPalette,
9
+ IconPlus,
8
10
  } from "@tabler/icons-react";
9
11
  import { useState, useRef, useEffect } from "react";
10
12
  import { Link } from "react-router";
@@ -42,6 +44,7 @@ export default function DeckCard({
42
44
  const [isRenaming, setIsRenaming] = useState(false);
43
45
  const [menuOpen, setMenuOpen] = useState(false);
44
46
  const [renameValue, setRenameValue] = useState(deck.title);
47
+ const [contextOpen, setContextOpen] = useState(false);
45
48
  const inputRef = useRef<HTMLInputElement>(null);
46
49
  const pendingRenameRef = useRef(false);
47
50
 
@@ -188,6 +191,16 @@ export default function DeckCard({
188
191
  <IconCopy className="w-3.5 h-3.5 me-2" />
189
192
  {isDuplicating ? "Duplicating..." : "Duplicate"}
190
193
  </DropdownMenuItem>
194
+ <DropdownMenuItem
195
+ onSelect={(event) => {
196
+ event.preventDefault();
197
+ setMenuOpen(false);
198
+ setContextOpen(true);
199
+ }}
200
+ >
201
+ <IconPlus className="w-3.5 h-3.5 me-2" />
202
+ {t("creativeContext.addToContext" /* i18n-key-ignore */)}
203
+ </DropdownMenuItem>
191
204
  <DropdownMenuSeparator />
192
205
  <DropdownMenuItem
193
206
  onClick={(e) => {
@@ -203,6 +216,20 @@ export default function DeckCard({
203
216
  </DropdownMenuContent>
204
217
  </DropdownMenu>
205
218
  </div>
219
+ <CreativeContextShareSheet
220
+ open={contextOpen}
221
+ onOpenChange={setContextOpen}
222
+ resource={{
223
+ appId: "slides",
224
+ resourceType: "deck",
225
+ resourceId: deck.id,
226
+ title: deck.title,
227
+ updatedAt: deck.updatedAt,
228
+ visibility: deck.visibility,
229
+ preview: { kind: "document", label: "Deck" },
230
+ }}
231
+ canManage={deck.createdByMe}
232
+ />
206
233
  </div>
207
234
  );
208
235
  }