@agent-native/core 0.106.2 → 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 (240) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +19 -0
  3. package/corpus/core/docs/content/external-agents.mdx +8 -0
  4. package/corpus/core/docs/content/integrations.mdx +597 -0
  5. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +7 -0
  6. package/corpus/core/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
  7. package/corpus/core/docs/content/locales/ar-SA/mcp-protocol.mdx +7 -0
  8. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +9 -0
  9. package/corpus/core/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
  10. package/corpus/core/docs/content/locales/de-DE/mcp-protocol.mdx +9 -0
  11. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +9 -0
  12. package/corpus/core/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
  13. package/corpus/core/docs/content/locales/es-ES/mcp-protocol.mdx +9 -0
  14. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +9 -0
  15. package/corpus/core/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
  16. package/corpus/core/docs/content/locales/fr-FR/mcp-protocol.mdx +9 -0
  17. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +8 -0
  18. package/corpus/core/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
  19. package/corpus/core/docs/content/locales/hi-IN/mcp-protocol.mdx +8 -0
  20. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +8 -0
  21. package/corpus/core/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
  22. package/corpus/core/docs/content/locales/ja-JP/mcp-protocol.mdx +8 -0
  23. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +8 -0
  24. package/corpus/core/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
  25. package/corpus/core/docs/content/locales/ko-KR/mcp-protocol.mdx +8 -0
  26. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +9 -0
  27. package/corpus/core/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
  28. package/corpus/core/docs/content/locales/pt-BR/mcp-protocol.mdx +9 -0
  29. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +6 -0
  30. package/corpus/core/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
  31. package/corpus/core/docs/content/locales/zh-CN/mcp-protocol.mdx +6 -0
  32. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +6 -0
  33. package/corpus/core/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
  34. package/corpus/core/docs/content/locales/zh-TW/mcp-protocol.mdx +6 -0
  35. package/corpus/core/docs/content/mcp-clients.mdx +51 -1
  36. package/corpus/core/docs/content/mcp-protocol.mdx +8 -0
  37. package/corpus/core/docs/content/organizations-teams-permissions.mdx +413 -0
  38. package/corpus/core/package.json +1 -1
  39. package/corpus/core/src/client/AssistantChat.tsx +13 -1
  40. package/corpus/core/src/client/chat/message-components.tsx +41 -0
  41. package/corpus/core/src/client/composer/TiptapComposer.tsx +45 -8
  42. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +30 -11
  43. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +240 -0
  44. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +159 -40
  45. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +433 -4
  46. package/corpus/core/src/client/resources/mcp-integration-logos.ts +108 -0
  47. package/corpus/core/src/deploy/build.ts +32 -0
  48. package/corpus/core/src/extensions/html-shell.ts +33 -1
  49. package/corpus/core/src/ingestion/office.ts +1 -2
  50. package/corpus/core/src/ingestion/pptx.ts +2 -4
  51. package/corpus/core/src/localization/default-messages.ts +126 -1
  52. package/corpus/core/src/mcp/actions/call-mcp-tool.ts +1 -0
  53. package/corpus/core/src/mcp/builtin-tools.ts +115 -10
  54. package/corpus/core/src/server/index.ts +4 -0
  55. package/corpus/core/src/server/org-admin.ts +28 -0
  56. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
  57. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
  58. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
  59. package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +64 -0
  60. package/corpus/templates/analytics/AGENTS.md +5 -0
  61. package/corpus/templates/analytics/actions/clone-creative-context-dashboard.ts +86 -0
  62. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -0
  63. package/corpus/templates/analytics/app/i18n/zh-TW.ts +1 -0
  64. package/corpus/templates/analytics/app/i18n-data.ts +1 -0
  65. package/corpus/templates/analytics/app/pages/Ask.tsx +8 -4
  66. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +59 -0
  67. package/corpus/templates/analytics/app/routes/agent.tsx +7 -1
  68. package/corpus/templates/analytics/changelog/2026-07-17-library-now-flags-published-dashboards-with-newer-versions-a.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  71. package/corpus/templates/analytics/package.json +1 -0
  72. package/corpus/templates/analytics/server/lib/native-creative-context.ts +158 -0
  73. package/corpus/templates/analytics/server/plugins/creative-context.ts +10 -0
  74. package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +12 -3
  75. package/corpus/templates/assets/AGENTS.md +5 -0
  76. package/corpus/templates/assets/actions/clone-creative-context-asset.ts +74 -0
  77. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -1
  78. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +308 -178
  79. package/corpus/templates/assets/app/routes/library.tsx +5 -11
  80. package/corpus/templates/assets/changelog/2026-07-17-library-now-flags-published-assets-with-newer-versions-and-l.md +6 -0
  81. package/corpus/templates/assets/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  82. package/corpus/templates/assets/server/lib/native-creative-context.ts +198 -0
  83. package/corpus/templates/assets/server/plugins/creative-context.ts +4 -0
  84. package/corpus/templates/brain/actions/provider-api-request.ts +1 -0
  85. package/corpus/templates/calendar/actions/provider-api-request.ts +1 -0
  86. package/corpus/templates/clips/actions/get-recording-insights.ts +7 -4
  87. package/corpus/templates/clips/actions/list-recordings.ts +48 -0
  88. package/corpus/templates/clips/actions/list-viewers.ts +2 -1
  89. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +3 -1
  90. package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -3
  91. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +11 -6
  92. package/corpus/templates/clips/app/hooks/use-view-tracking.ts +6 -1
  93. package/corpus/templates/clips/app/lib/ffmpeg-export.ts +4 -2
  94. package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -3
  95. package/corpus/templates/clips/changelog/2026-07-17-dictation-keeps-sentence-spacing-and-no-longer-crashes-when-.md +6 -0
  96. package/corpus/templates/clips/changelog/2026-07-17-shared-clips-now-preserve-editor-access-and-show-editor-insi.md +6 -0
  97. package/corpus/templates/clips/changelog/2026-07-17-stitching-clips-no-longer-fails-when-editor-media-metadata-i.md +6 -0
  98. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +14 -8
  99. package/corpus/templates/clips/shared/view-analytics.ts +4 -0
  100. package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +12 -3
  101. package/corpus/templates/content/AGENTS.md +5 -0
  102. package/corpus/templates/content/actions/clone-creative-context-document.ts +61 -0
  103. package/corpus/templates/content/actions/provider-api-request.ts +1 -0
  104. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +21 -0
  105. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +29 -0
  106. package/corpus/templates/content/changelog/2026-07-17-library-now-flags-published-documents-with-newer-versions-an.md +6 -0
  107. package/corpus/templates/content/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  108. package/corpus/templates/content/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  109. package/corpus/templates/content/parity/matrix.md +1 -1
  110. package/corpus/templates/content/parity/matrix.ts +1 -0
  111. package/corpus/templates/content/server/lib/native-creative-context.ts +191 -0
  112. package/corpus/templates/content/server/plugins/creative-context.ts +8 -1
  113. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -3
  114. package/corpus/templates/design/AGENTS.md +5 -0
  115. package/corpus/templates/design/actions/clone-creative-context-design-native.ts +111 -0
  116. package/corpus/templates/design/actions/provider-api-request.ts +1 -0
  117. package/corpus/templates/design/app/pages/Index.tsx +44 -0
  118. package/corpus/templates/design/app/pages/design-editor/types.ts +1 -0
  119. package/corpus/templates/design/changelog/2026-07-17-library-now-flags-published-designs-with-newer-versions-and-.md +6 -0
  120. package/corpus/templates/design/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  121. package/corpus/templates/design/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  122. package/corpus/templates/design/server/lib/native-creative-context.ts +265 -0
  123. package/corpus/templates/design/server/plugins/creative-context.ts +8 -1
  124. package/corpus/templates/dispatch/actions/provider-api-request.ts +1 -0
  125. package/corpus/templates/mail/actions/provider-api-request.ts +1 -0
  126. package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +11 -3
  127. package/corpus/templates/slides/AGENTS.md +5 -0
  128. package/corpus/templates/slides/actions/clone-creative-context-deck.ts +96 -0
  129. package/corpus/templates/slides/actions/extract-pdf.ts +3 -2
  130. package/corpus/templates/slides/actions/import-file.ts +3 -2
  131. package/corpus/templates/slides/actions/provider-api-request.ts +1 -0
  132. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +27 -0
  133. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -0
  134. package/corpus/templates/slides/changelog/2026-07-17-library-now-flags-published-decks-with-newer-versions-and-le.md +6 -0
  135. package/corpus/templates/slides/changelog/2026-07-17-pdf-and-powerpoint-imports-now-work-reliably-in-hosted-decks.md +6 -0
  136. package/corpus/templates/slides/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
  137. package/corpus/templates/slides/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
  138. package/corpus/templates/slides/server/lib/native-creative-context.ts +288 -0
  139. package/corpus/templates/slides/server/plugins/creative-context.ts +4 -0
  140. package/dist/client/AssistantChat.d.ts.map +1 -1
  141. package/dist/client/AssistantChat.js +8 -2
  142. package/dist/client/AssistantChat.js.map +1 -1
  143. package/dist/client/chat/message-components.d.ts.map +1 -1
  144. package/dist/client/chat/message-components.js +35 -1
  145. package/dist/client/chat/message-components.js.map +1 -1
  146. package/dist/client/composer/TiptapComposer.d.ts +7 -0
  147. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  148. package/dist/client/composer/TiptapComposer.js +37 -8
  149. package/dist/client/composer/TiptapComposer.js.map +1 -1
  150. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  151. package/dist/client/integrations/IntegrationsPanel.js +3 -2
  152. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  153. package/dist/client/resources/McpConnectionSuggestion.d.ts +9 -0
  154. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -0
  155. package/dist/client/resources/McpConnectionSuggestion.js +115 -0
  156. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -0
  157. package/dist/client/resources/McpIntegrationDialog.d.ts +2 -1
  158. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  159. package/dist/client/resources/McpIntegrationDialog.js +48 -20
  160. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  161. package/dist/client/resources/mcp-integration-catalog.d.ts +9 -0
  162. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  163. package/dist/client/resources/mcp-integration-catalog.js +387 -4
  164. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  165. package/dist/client/resources/mcp-integration-logos.d.ts +2 -0
  166. package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -0
  167. package/dist/client/resources/mcp-integration-logos.js +109 -0
  168. package/dist/client/resources/mcp-integration-logos.js.map +1 -0
  169. package/dist/deploy/build.d.ts.map +1 -1
  170. package/dist/deploy/build.js +23 -0
  171. package/dist/deploy/build.js.map +1 -1
  172. package/dist/extensions/html-shell.d.ts.map +1 -1
  173. package/dist/extensions/html-shell.js +33 -1
  174. package/dist/extensions/html-shell.js.map +1 -1
  175. package/dist/ingestion/office.d.ts.map +1 -1
  176. package/dist/ingestion/office.js +1 -2
  177. package/dist/ingestion/office.js.map +1 -1
  178. package/dist/ingestion/pptx.js +2 -4
  179. package/dist/ingestion/pptx.js.map +1 -1
  180. package/dist/localization/default-messages.d.ts +98 -0
  181. package/dist/localization/default-messages.d.ts.map +1 -1
  182. package/dist/localization/default-messages.js +99 -1
  183. package/dist/localization/default-messages.js.map +1 -1
  184. package/dist/mcp/actions/call-mcp-tool.js +1 -0
  185. package/dist/mcp/actions/call-mcp-tool.js.map +1 -1
  186. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  187. package/dist/mcp/builtin-tools.js +87 -6
  188. package/dist/mcp/builtin-tools.js.map +1 -1
  189. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  190. package/dist/server/index.d.ts +1 -0
  191. package/dist/server/index.d.ts.map +1 -1
  192. package/dist/server/index.js +1 -0
  193. package/dist/server/index.js.map +1 -1
  194. package/dist/server/org-admin.d.ts +3 -0
  195. package/dist/server/org-admin.d.ts.map +1 -0
  196. package/dist/server/org-admin.js +24 -0
  197. package/dist/server/org-admin.js.map +1 -0
  198. package/dist/server/transcribe-voice.d.ts +1 -1
  199. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
  200. package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
  201. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
  202. package/docs/content/external-agents.mdx +8 -0
  203. package/docs/content/integrations.mdx +597 -0
  204. package/docs/content/locales/ar-SA/external-agents.mdx +7 -0
  205. package/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
  206. package/docs/content/locales/ar-SA/mcp-protocol.mdx +7 -0
  207. package/docs/content/locales/de-DE/external-agents.mdx +9 -0
  208. package/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
  209. package/docs/content/locales/de-DE/mcp-protocol.mdx +9 -0
  210. package/docs/content/locales/es-ES/external-agents.mdx +9 -0
  211. package/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
  212. package/docs/content/locales/es-ES/mcp-protocol.mdx +9 -0
  213. package/docs/content/locales/fr-FR/external-agents.mdx +9 -0
  214. package/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
  215. package/docs/content/locales/fr-FR/mcp-protocol.mdx +9 -0
  216. package/docs/content/locales/hi-IN/external-agents.mdx +8 -0
  217. package/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
  218. package/docs/content/locales/hi-IN/mcp-protocol.mdx +8 -0
  219. package/docs/content/locales/ja-JP/external-agents.mdx +8 -0
  220. package/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
  221. package/docs/content/locales/ja-JP/mcp-protocol.mdx +8 -0
  222. package/docs/content/locales/ko-KR/external-agents.mdx +8 -0
  223. package/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
  224. package/docs/content/locales/ko-KR/mcp-protocol.mdx +8 -0
  225. package/docs/content/locales/pt-BR/external-agents.mdx +9 -0
  226. package/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
  227. package/docs/content/locales/pt-BR/mcp-protocol.mdx +9 -0
  228. package/docs/content/locales/zh-CN/external-agents.mdx +6 -0
  229. package/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
  230. package/docs/content/locales/zh-CN/mcp-protocol.mdx +6 -0
  231. package/docs/content/locales/zh-TW/external-agents.mdx +6 -0
  232. package/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
  233. package/docs/content/locales/zh-TW/mcp-protocol.mdx +6 -0
  234. package/docs/content/mcp-clients.mdx +51 -1
  235. package/docs/content/mcp-protocol.mdx +8 -0
  236. package/docs/content/organizations-teams-permissions.mdx +413 -0
  237. package/package.json +1 -1
  238. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
  239. package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
  240. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
@@ -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,198 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { putPrivateBlob } from "@agent-native/core/private-blob";
4
+ import { accessFilter } from "@agent-native/core/sharing";
5
+ import {
6
+ serializePrivateBlobHandle,
7
+ type NativeResourceCaptureAdapter,
8
+ } from "@agent-native/creative-context/server";
9
+ import { and, eq, inArray } from "drizzle-orm";
10
+
11
+ import {
12
+ getAssetOrThrow,
13
+ requireLibraryAccess,
14
+ } from "../../actions/_helpers.js";
15
+ import { getDb, schema } from "../db/index.js";
16
+ import { getObject } from "./storage.js";
17
+
18
+ export const nativeAssetCreativeContextAdapter: NativeResourceCaptureAdapter = {
19
+ appId: "assets",
20
+ resourceType: "asset",
21
+ async listResourceVersions(resourceIds) {
22
+ if (!resourceIds.length) return [];
23
+ return getDb()
24
+ .select({
25
+ resourceId: schema.assets.id,
26
+ sourceModifiedAt: schema.assets.updatedAt,
27
+ })
28
+ .from(schema.assets)
29
+ .innerJoin(
30
+ schema.assetLibraries,
31
+ eq(schema.assetLibraries.id, schema.assets.libraryId),
32
+ )
33
+ .where(
34
+ and(
35
+ inArray(schema.assets.id, [...resourceIds]),
36
+ accessFilter(schema.assetLibraries, schema.assetLibraryShares),
37
+ ),
38
+ );
39
+ },
40
+ async capture(reference) {
41
+ const asset = await getAssetOrThrow(reference.resourceId);
42
+ const libraryAccess = await requireLibraryAccess(asset.libraryId);
43
+ const library = libraryAccess.resource;
44
+ if (
45
+ reference.expectedUpdatedAt &&
46
+ reference.expectedUpdatedAt !== asset.updatedAt
47
+ )
48
+ throw new Error("Asset changed before it could be submitted to Context.");
49
+ const bytes = await getObject(asset.objectKey);
50
+ const contentHash = createHash("sha256").update(bytes).digest("hex");
51
+ const extension = asset.mimeType.split("/").pop() ?? "asset";
52
+ const handle = await putPrivateBlob({
53
+ data: bytes,
54
+ filename: asset.title ?? `${asset.id}.${extension}`,
55
+ mimeType: asset.mimeType,
56
+ key: `creative-context/assets/${asset.id}/${contentHash}`,
57
+ metadata: {
58
+ appId: "assets",
59
+ resourceType: "asset",
60
+ resourceId: asset.id,
61
+ contentHash,
62
+ },
63
+ });
64
+ if (!handle)
65
+ throw new Error(
66
+ "Private blob storage is required to submit an asset to Context.",
67
+ );
68
+ const previewIsThumbnail = Boolean(
69
+ asset.mediaType === "video" && asset.thumbnailObjectKey,
70
+ );
71
+ const previewBytes = previewIsThumbnail
72
+ ? await getObject(asset.thumbnailObjectKey!)
73
+ : bytes;
74
+ const previewMimeType = previewIsThumbnail ? "image/webp" : asset.mimeType;
75
+ const previewHandle = await putPrivateBlob({
76
+ data: previewBytes,
77
+ filename: previewIsThumbnail
78
+ ? `${asset.id}.preview.webp`
79
+ : `${asset.id}.${extension}`,
80
+ mimeType: previewMimeType,
81
+ key: `creative-context/assets/${asset.id}/${contentHash}.preview`,
82
+ metadata: {
83
+ appId: "assets",
84
+ resourceType: "asset-preview",
85
+ resourceId: asset.id,
86
+ contentHash,
87
+ },
88
+ });
89
+ if (!previewHandle) {
90
+ throw new Error(
91
+ "Private blob storage is required to preview an asset in Context.",
92
+ );
93
+ }
94
+ const safeDescription = [
95
+ asset.title,
96
+ asset.description,
97
+ asset.altText,
98
+ asset.prompt,
99
+ ]
100
+ .filter(Boolean)
101
+ .join("\n");
102
+ return {
103
+ artifactKey: `assets:asset:${asset.id}`,
104
+ source: {
105
+ name: "Assets",
106
+ kind: "native-app",
107
+ externalRef: asset.id,
108
+ access: {
109
+ visibility: library.visibility ?? "private",
110
+ canManage:
111
+ libraryAccess.role === "owner" || libraryAccess.role === "admin",
112
+ },
113
+ },
114
+ items: [
115
+ {
116
+ externalId: `native:assets:asset:${asset.id}`,
117
+ kind: asset.mediaType,
118
+ title: asset.title ?? "Untitled asset",
119
+ canonicalUrl: `/asset/${asset.id}`,
120
+ mimeType: asset.mimeType,
121
+ content: safeDescription.slice(0, 12_000),
122
+ summary:
123
+ safeDescription.slice(0, 500) ||
124
+ `Immutable ${asset.mediaType} asset.`,
125
+ contentHash,
126
+ sourceModifiedAt: asset.updatedAt,
127
+ sourceVersion: contentHash,
128
+ metadata: {
129
+ preview: {
130
+ type: "asset",
131
+ mediaType: asset.mediaType,
132
+ width: asset.width,
133
+ height: asset.height,
134
+ durationSeconds: asset.durationSeconds,
135
+ },
136
+ },
137
+ media: [
138
+ {
139
+ kind:
140
+ previewIsThumbnail || asset.mediaType !== "video"
141
+ ? "image"
142
+ : "video",
143
+ mimeType: previewMimeType,
144
+ accessMode: "private",
145
+ storageKey: serializePrivateBlobHandle(previewHandle),
146
+ altText: asset.altText ?? asset.title ?? undefined,
147
+ contentHash: createHash("sha256")
148
+ .update(previewBytes)
149
+ .digest("hex"),
150
+ width: asset.width ?? undefined,
151
+ height: asset.height ?? undefined,
152
+ durationMs:
153
+ asset.durationSeconds != null
154
+ ? Math.round(asset.durationSeconds * 1_000)
155
+ : undefined,
156
+ metadata: { role: previewIsThumbnail ? "thumbnail" : "preview" },
157
+ },
158
+ ...(previewIsThumbnail
159
+ ? [
160
+ {
161
+ kind: "video" as const,
162
+ mimeType: asset.mimeType,
163
+ accessMode: "private" as const,
164
+ storageKey: serializePrivateBlobHandle(handle),
165
+ altText: asset.altText ?? asset.title ?? undefined,
166
+ contentHash,
167
+ durationMs:
168
+ asset.durationSeconds != null
169
+ ? Math.round(asset.durationSeconds * 1_000)
170
+ : undefined,
171
+ metadata: { role: "playback" },
172
+ },
173
+ ]
174
+ : []),
175
+ ],
176
+ },
177
+ ],
178
+ privateMetadata: {
179
+ nativeResource: {
180
+ appId: "assets",
181
+ resourceType: "asset",
182
+ resourceId: asset.id,
183
+ expectedUpdatedAt: reference.expectedUpdatedAt,
184
+ },
185
+ clone: {
186
+ handle,
187
+ appId: "assets",
188
+ resourceType: "asset",
189
+ resourceId: asset.id,
190
+ contentHash,
191
+ sourceVersion: contentHash,
192
+ updatedAt: asset.updatedAt,
193
+ libraryId: asset.libraryId,
194
+ },
195
+ },
196
+ };
197
+ },
198
+ };
@@ -6,6 +6,7 @@ import {
6
6
  getRequestUserEmail,
7
7
  } from "@agent-native/core/server/request-context";
8
8
  import {
9
+ registerNativeResourceCaptureAdapter,
9
10
  readCreativeContextMedia,
10
11
  setupCreativeContext,
11
12
  type CreativeContextProjectionAdapters,
@@ -20,6 +21,7 @@ import { getDb, schema } from "../db/index.js";
20
21
  import { createAssetFromBuffer } from "../lib/assets.js";
21
22
  import { seedDefaultGenerationPresets } from "../lib/generation-presets.js";
22
23
  import { nowIso, parseJson, stringifyJson } from "../lib/json.js";
24
+ import { nativeAssetCreativeContextAdapter } from "../lib/native-creative-context.js";
23
25
 
24
26
  const IMPORT_LIBRARY_TITLE = "Creative context imports";
25
27
 
@@ -202,4 +204,6 @@ registerOnboardingStep({
202
204
  },
203
205
  });
204
206
 
207
+ registerNativeResourceCaptureAdapter(nativeAssetCreativeContextAdapter);
208
+
205
209
  export default setupCreativeContext({ appId: "assets", projections });
@@ -197,6 +197,7 @@ export default defineAction({
197
197
  ),
198
198
  }),
199
199
  http: false,
200
+ toolCallable: false,
200
201
  run: async (args) => {
201
202
  if (args.stageAs) {
202
203
  const ctx = getCredentialContext();
@@ -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();
@@ -16,6 +16,7 @@ import { eq } from "drizzle-orm";
16
16
  import { z } from "zod";
17
17
 
18
18
  import { getDb, schema } from "../server/db/index.js";
19
+ import { clampCompletionPct } from "../shared/view-analytics.js";
19
20
 
20
21
  export default defineAction({
21
22
  description:
@@ -46,8 +47,10 @@ export default defineAction({
46
47
  const completionRate =
47
48
  viewerRows.length === 0
48
49
  ? 0
49
- : viewerRows.reduce((acc, v) => acc + (v.completedPct ?? 0), 0) /
50
- viewerRows.length;
50
+ : viewerRows.reduce(
51
+ (acc, v) => acc + clampCompletionPct(v.completedPct),
52
+ 0,
53
+ ) / viewerRows.length;
51
54
 
52
55
  // Drop-off: 100 buckets across the video's duration.
53
56
  // Use the recording's duration as the denominator.
@@ -64,7 +67,7 @@ export default defineAction({
64
67
  }));
65
68
 
66
69
  for (const v of viewerRows) {
67
- const pct = Math.min(100, Math.max(0, v.completedPct ?? 0));
70
+ const pct = clampCompletionPct(v.completedPct);
68
71
  // Each viewer contributes to all buckets up to their max reached.
69
72
  for (let i = 0; i < pct; i++) {
70
73
  buckets[i].watching += 1;
@@ -84,7 +87,7 @@ export default defineAction({
84
87
  viewerEmail: v.viewerEmail,
85
88
  viewerName: v.viewerName,
86
89
  totalWatchMs: v.totalWatchMs ?? 0,
87
- completedPct: v.completedPct ?? 0,
90
+ completedPct: clampCompletionPct(v.completedPct),
88
91
  }));
89
92
 
90
93
  return {
@@ -16,6 +16,7 @@ import {
16
16
  import { z } from "zod";
17
17
 
18
18
  import { getDb, schema } from "../server/db/index.js";
19
+ import { resolvePlayerVideoUrl } from "../server/lib/player-video-url.js";
19
20
  import {
20
21
  getActiveOrganizationId,
21
22
  ownerEmailMatches,
@@ -26,6 +27,39 @@ function escapeLike(s: string): string {
26
27
  return s.replace(/([\\%_])/g, "\\$1");
27
28
  }
28
29
 
30
+ type RecordingMediaFields = {
31
+ id: string;
32
+ sourceAppName?: string | null;
33
+ sourceWindowTitle?: string | null;
34
+ videoUrl?: string | null;
35
+ videoFormat?: string | null;
36
+ };
37
+
38
+ export function resolveListRecordingMedia(
39
+ recording: RecordingMediaFields,
40
+ includeMedia: boolean,
41
+ ): { videoUrl: string | null; videoFormat: "webm" | "mp4" | null } {
42
+ if (!includeMedia) {
43
+ return { videoUrl: null, videoFormat: null };
44
+ }
45
+
46
+ return {
47
+ videoUrl: resolvePlayerVideoUrl(
48
+ {
49
+ id: recording.id,
50
+ sourceAppName: recording.sourceAppName,
51
+ sourceWindowTitle: recording.sourceWindowTitle,
52
+ videoUrl: recording.videoUrl,
53
+ },
54
+ { proxyRemoteMedia: true },
55
+ ),
56
+ videoFormat:
57
+ recording.videoFormat === "webm" || recording.videoFormat === "mp4"
58
+ ? recording.videoFormat
59
+ : null,
60
+ };
61
+ }
62
+
29
63
  export default defineAction({
30
64
  description:
31
65
  "List recordings visible to the current user. Supports filtering by view (library/shared/space/archive/trash/all), folder, space, tag, free-text, and sort. The shared view returns accessible recordings owned by someone else.",
@@ -67,6 +101,13 @@ export default defineAction({
67
101
  )
68
102
  .default(false)
69
103
  .describe("Return only the total count, skipping the row payload"),
104
+ includeMedia: z
105
+ .preprocess(
106
+ (v) => (typeof v === "string" ? v === "true" : v),
107
+ z.boolean(),
108
+ )
109
+ .default(false)
110
+ .describe("Include playable media fields for editor workflows"),
70
111
  }),
71
112
  http: { method: "GET" },
72
113
  run: async (args) => {
@@ -225,6 +266,12 @@ export default defineAction({
225
266
  hasCamera: schema.recordings.hasCamera,
226
267
  width: schema.recordings.width,
227
268
  height: schema.recordings.height,
269
+ videoUrl: args.includeMedia
270
+ ? schema.recordings.videoUrl
271
+ : sql<string | null>`NULL`,
272
+ videoFormat: args.includeMedia
273
+ ? schema.recordings.videoFormat
274
+ : sql<string | null>`NULL`,
228
275
  },
229
276
  transcriptStatus: schema.recordingTranscripts.status,
230
277
  // Compute the has-text signal in SQL instead of shipping the full
@@ -313,6 +360,7 @@ export default defineAction({
313
360
  hasCamera: Boolean(r.hasCamera),
314
361
  width: r.width,
315
362
  height: r.height,
363
+ ...resolveListRecordingMedia(r, args.includeMedia),
316
364
  transcriptStatus: row.transcriptStatus ?? null,
317
365
  transcriptHasText: Number(row.transcriptHasText ?? 0) > 0,
318
366
  };
@@ -11,6 +11,7 @@ import { eq } from "drizzle-orm";
11
11
  import { z } from "zod";
12
12
 
13
13
  import { getDb, schema } from "../server/db/index.js";
14
+ import { clampCompletionPct } from "../shared/view-analytics.js";
14
15
 
15
16
  export default defineAction({
16
17
  description:
@@ -38,7 +39,7 @@ export default defineAction({
38
39
  viewerEmail: v.viewerEmail,
39
40
  viewerName: v.viewerName,
40
41
  totalWatchMs: v.totalWatchMs ?? 0,
41
- completedPct: v.completedPct ?? 0,
42
+ completedPct: clampCompletionPct(v.completedPct),
42
43
  countedView: Boolean(v.countedView),
43
44
  ctaClicked: Boolean(v.ctaClicked),
44
45
  firstViewedAt: v.firstViewedAt,
@@ -70,7 +70,9 @@ export function StitchManager({
70
70
  const [title, setTitle] = useState(t("stitchManager.defaultTitle"));
71
71
  const [dragIndex, setDragIndex] = useState<number | null>(null);
72
72
 
73
- const listQuery = useActionQuery("list-recordings", {});
73
+ const listQuery = useActionQuery("list-recordings", {
74
+ includeMedia: true,
75
+ });
74
76
  const stitch = useActionMutation("stitch-recordings");
75
77
 
76
78
  useEffect(() => {
@@ -386,9 +386,7 @@ function LinkTab({
386
386
  : t("shareDialog.shareWithHumans")
387
387
  }
388
388
  value={shareUrl}
389
- disabled={
390
- visibilityPending || !sharesLoaded || (!isPublic && canManage)
391
- }
389
+ disabled={visibilityPending || !sharesLoaded}
392
390
  />
393
391
 
394
392
  {!isPublic ? (
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useState } from "react";
2
2
 
3
3
  import { useIsMobile } from "@/hooks/use-mobile";
4
4
  import {
5
- hasDismissedDesktopPromo,
6
- markDesktopPromoDismissed,
5
+ hasDownloadedDesktopApp,
6
+ markDesktopAppDownloaded,
7
7
  } from "@/lib/capture-install-options";
8
8
 
9
9
  function detectDesktopApp(): boolean {
@@ -23,23 +23,28 @@ function detectDesktopApp(): boolean {
23
23
  export function useDesktopPromo() {
24
24
  const isMobile = useIsMobile();
25
25
  const [isDesktopApp, setIsDesktopApp] = useState(false);
26
+ const [runtimeDetected, setRuntimeDetected] = useState(false);
26
27
  const [dismissed, setDismissed] = useState(false);
27
28
 
28
29
  useEffect(() => {
29
30
  setIsDesktopApp(detectDesktopApp());
30
- setDismissed(hasDismissedDesktopPromo());
31
+ setDismissed(hasDownloadedDesktopApp());
32
+ // Keep desktop prompts hidden until the client runtime is known. This
33
+ // prevents the web CTA from flashing in the desktop shell's first render.
34
+ setRuntimeDetected(true);
31
35
  }, []);
32
36
 
33
37
  const dismiss = useCallback(() => {
34
38
  setDismissed(true);
35
- markDesktopPromoDismissed();
39
+ markDesktopAppDownloaded();
36
40
  }, []);
37
41
 
38
42
  return {
39
43
  isDesktopApp,
40
44
  isMobile,
41
- shouldShowPromo: !isMobile && !isDesktopApp && !dismissed,
42
- shouldShowSidebarLink: !isMobile && !isDesktopApp,
45
+ shouldShowPromo:
46
+ runtimeDetected && !isMobile && !isDesktopApp && !dismissed,
47
+ shouldShowSidebarLink: runtimeDetected && !isMobile && !isDesktopApp,
43
48
  dismiss,
44
49
  };
45
50
  }
@@ -1,6 +1,8 @@
1
1
  import { appBasePath } from "@agent-native/core/client";
2
2
  import { useEffect, useRef } from "react";
3
3
 
4
+ import { clampCompletionPct } from "../../shared/view-analytics";
5
+
4
6
  const SESSION_KEY = "clips-view-session-id";
5
7
 
6
8
  function getSessionId(): string {
@@ -107,7 +109,10 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
107
109
  if (!v) return;
108
110
  const completedPct =
109
111
  durationMs > 0 ? (watchMsRef.current / durationMs) * 100 : 0;
110
- maxPctRef.current = Math.max(maxPctRef.current, completedPct);
112
+ maxPctRef.current = Math.max(
113
+ maxPctRef.current,
114
+ clampCompletionPct(completedPct),
115
+ );
111
116
  fetch(`${appBasePath()}/api/view-event`, {
112
117
  method: "POST",
113
118
  keepalive: kind === "watch-progress" || kind === "pause",
@@ -164,15 +164,17 @@ export async function exportMp4(
164
164
  onProgress?.({ progress: 0, stage: "loading-ffmpeg" });
165
165
  // Stable reference so we can remove it in `finally` — without this the
166
166
  // listener piles up across exports and ffmpeg log lines fan out N×.
167
+ let lastProgress = 0;
167
168
  const onLog = (msg: string) => {
168
- onProgress?.({ progress: -1, stage: "encoding", message: msg });
169
+ onProgress?.({ progress: lastProgress, stage: "encoding", message: msg });
169
170
  };
170
171
  const ffmpeg = await loadFfmpeg(onLog);
171
172
 
172
173
  // Hook ffmpeg progress events — they fire per frame written.
173
174
  const handleProgress = ({ progress }: { progress: number }) => {
175
+ lastProgress = Math.max(0, Math.min(1, progress));
174
176
  onProgress?.({
175
- progress: Math.max(0, Math.min(1, progress)),
177
+ progress: lastProgress,
176
178
  stage: "encoding",
177
179
  });
178
180
  };
@@ -40,6 +40,7 @@ import { CaptureInstallButton } from "@/components/capture-install-options";
40
40
  import { AccessPasswordPrompt } from "@/components/player/access-password-prompt";
41
41
  import { CommentsPanel } from "@/components/player/comments-panel";
42
42
  import { RecordingOptionsMenu } from "@/components/player/delete-recording-menu";
43
+ import { InsightsPanel } from "@/components/player/insights-panel";
43
44
  import { ReactionsTray } from "@/components/player/reactions-tray";
44
45
  import { ShareRecordingPopover } from "@/components/player/share-dialog";
45
46
  import { SignInPromptDialog } from "@/components/player/sign-in-prompt-dialog";
@@ -484,7 +485,17 @@ export default function ShareRoute() {
484
485
  dataQ.data?.data?.transcript?.failureReason ?? null;
485
486
  const ctas = dataQ.data?.data?.ctas ?? [];
486
487
  const firstCta = ctas[0] ?? null;
487
- const viewerCanEdit = Boolean(dataQ.data?.data?.viewer?.canEdit);
488
+ const viewerRole = dataQ.data?.data?.viewer?.role as
489
+ | "owner"
490
+ | "admin"
491
+ | "editor"
492
+ | "viewer"
493
+ | undefined;
494
+ const viewerCanEdit =
495
+ Boolean(dataQ.data?.data?.viewer?.canEdit) ||
496
+ viewerRole === "owner" ||
497
+ viewerRole === "admin" ||
498
+ viewerRole === "editor";
488
499
  const viewerIsOwner = Boolean(dataQ.data?.data?.viewer?.isOwner);
489
500
  const showTitleSkeleton = recording
490
501
  ? shouldShowGeneratedTitleSkeleton(recording, transcriptStatus)
@@ -945,7 +956,7 @@ export default function ShareRoute() {
945
956
  durationMs={recording.durationMs}
946
957
  editsJson={recording.editsJson}
947
958
  thumbnailUrl={recording.thumbnailUrl}
948
- role={viewerCanEdit ? "owner" : "viewer"}
959
+ role={viewerRole ?? (viewerCanEdit ? "owner" : "viewer")}
949
960
  defaultSpeed={parsePlaybackSpeed(recording.defaultSpeed) ?? 1.2}
950
961
  comments={comments}
951
962
  chapters={chapters}
@@ -1123,7 +1134,14 @@ export default function ShareRoute() {
1123
1134
  value="insights"
1124
1135
  className="mt-3 min-h-0 flex-1 data-[state=inactive]:hidden"
1125
1136
  >
1126
- <PublicInsightsState />
1137
+ {viewerCanEdit ? (
1138
+ <InsightsPanel
1139
+ recordingId={recording.id}
1140
+ durationMs={recording.durationMs}
1141
+ />
1142
+ ) : (
1143
+ <PublicInsightsState />
1144
+ )}
1127
1145
  </TabsContent>
1128
1146
  </Tabs>
1129
1147
  </aside>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Dictation keeps sentence spacing and no longer crashes when submitted mid-capture
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Shared Clips now preserve Editor access and show editor insights
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-17
4
+ ---
5
+
6
+ Stitching clips no longer fails when editor media metadata is loaded
@@ -6,11 +6,11 @@
6
6
  * authenticated `/_agent-native/actions/get-recording-player-data` route.
7
7
  *
8
8
  * Only returns data when:
9
- * - recording.visibility === 'public' (or the signed-in viewer is owner), AND
9
+ * - recording.visibility === 'public', or the signed-in viewer has org/share access, AND
10
10
  * - either no password is set, the viewer is owner, or the provided password matches
11
11
  *
12
- * For `org` or `private` visibility, returns 401 (viewer must sign in and use
13
- * the authenticated player route).
12
+ * For `org` or `private` visibility, signed-in org members and explicit shares
13
+ * may load the same player payload as the authenticated route.
14
14
  */
15
15
 
16
16
  import {
@@ -467,11 +467,17 @@ export default defineEventHandler(async (event) => {
467
467
  placement: c.placement,
468
468
  })),
469
469
  viewer: session?.email
470
- ? {
471
- canEdit: viewerIsOwner,
472
- isOwner: viewerIsOwner,
473
- role: viewerIsOwner ? "owner" : "viewer",
474
- }
470
+ ? (() => {
471
+ const role =
472
+ viewerAccess?.role ?? (viewerIsOrgMember ? "viewer" : null);
473
+ const canEdit =
474
+ role === "owner" || role === "admin" || role === "editor";
475
+ return {
476
+ canEdit,
477
+ isOwner: role === "owner",
478
+ role: role ?? "viewer",
479
+ };
480
+ })()
475
481
  : null,
476
482
  };
477
483
  });
@@ -0,0 +1,4 @@
1
+ export function clampCompletionPct(value: number | null | undefined): number {
2
+ if (typeof value !== "number" || !Number.isFinite(value)) return 0;
3
+ return Math.min(100, Math.max(0, value));
4
+ }
@@ -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
  }
@@ -83,3 +86,9 @@ versions may support factual claims. Preserve the returned `contextPackId`
83
86
  and short reuse labels with the document generation provenance. A published
84
87
  brand profile can guide tone automatically; it never overrides the user's
85
88
  current brief, and it must not turn an unsupported prior claim into a fact.
89
+
90
+ When an approved app-created document already fits, call
91
+ `clone-creative-context-document` with its exact item and version ids before
92
+ rewriting from excerpts. The typed action resolves the approved private
93
+ Markdown/document payload through Content's normal create path; generic context
94
+ actions never expose that payload.
@@ -43,6 +43,11 @@ Detailed document editing, Notion, storage, and UI rules live in
43
43
  document first, then a pinned/current pack, then narrow library search.
44
44
  Respect `creative-context.contextMode: "off"` without silently restoring a
45
45
  pack.
46
+ - To submit a document to a governed Creative Context, use the Context tab or
47
+ `manage-context-membership`; the app flushes live collaboration and captures
48
+ one immutable Markdown version. Reuse only its opaque native clone reference.
49
+ Use `operation="submit-latest"` with a Library membership id when its native
50
+ update status reports `update-available`.
46
51
 
47
52
  - `navigation` exposes document, selected block, comment, media, and Notion view
48
53
  context.