@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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +13 -0
- package/corpus/core/docs/content/integrations.mdx +597 -0
- package/corpus/core/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
- package/corpus/core/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
- package/corpus/core/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
- package/corpus/core/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
- package/corpus/core/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
- package/corpus/core/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
- package/corpus/core/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
- package/corpus/core/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
- package/corpus/core/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
- package/corpus/core/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
- package/corpus/core/docs/content/mcp-clients.mdx +51 -1
- package/corpus/core/docs/content/organizations-teams-permissions.mdx +413 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +13 -1
- package/corpus/core/src/client/chat/message-components.tsx +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +45 -8
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +30 -11
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +240 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +159 -40
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +433 -4
- package/corpus/core/src/client/resources/mcp-integration-logos.ts +108 -0
- package/corpus/core/src/deploy/build.ts +32 -0
- package/corpus/core/src/extensions/html-shell.ts +33 -1
- package/corpus/core/src/ingestion/office.ts +1 -2
- package/corpus/core/src/ingestion/pptx.ts +2 -4
- package/corpus/core/src/localization/default-messages.ts +126 -1
- package/corpus/core/src/mcp/actions/call-mcp-tool.ts +1 -0
- package/corpus/core/src/server/index.ts +4 -0
- package/corpus/core/src/server/org-admin.ts +28 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
- package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +64 -0
- package/corpus/templates/analytics/AGENTS.md +5 -0
- package/corpus/templates/analytics/actions/clone-creative-context-dashboard.ts +86 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/analytics/app/i18n-data.ts +1 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +8 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +59 -0
- package/corpus/templates/analytics/app/routes/agent.tsx +7 -1
- package/corpus/templates/analytics/changelog/2026-07-17-library-now-flags-published-dashboards-with-newer-versions-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/analytics/package.json +1 -0
- package/corpus/templates/analytics/server/lib/native-creative-context.ts +158 -0
- package/corpus/templates/analytics/server/plugins/creative-context.ts +10 -0
- package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +12 -3
- package/corpus/templates/assets/AGENTS.md +5 -0
- package/corpus/templates/assets/actions/clone-creative-context-asset.ts +74 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +308 -178
- package/corpus/templates/assets/app/routes/library.tsx +5 -11
- package/corpus/templates/assets/changelog/2026-07-17-library-now-flags-published-assets-with-newer-versions-and-l.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/assets/server/lib/native-creative-context.ts +198 -0
- package/corpus/templates/assets/server/plugins/creative-context.ts +4 -0
- package/corpus/templates/brain/actions/provider-api-request.ts +1 -0
- package/corpus/templates/calendar/actions/provider-api-request.ts +1 -0
- package/corpus/templates/clips/actions/get-recording-insights.ts +7 -4
- package/corpus/templates/clips/actions/list-recordings.ts +48 -0
- package/corpus/templates/clips/actions/list-viewers.ts +2 -1
- package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +3 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -3
- package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +11 -6
- package/corpus/templates/clips/app/hooks/use-view-tracking.ts +6 -1
- package/corpus/templates/clips/app/lib/ffmpeg-export.ts +4 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -3
- package/corpus/templates/clips/changelog/2026-07-17-dictation-keeps-sentence-spacing-and-no-longer-crashes-when-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-17-shared-clips-now-preserve-editor-access-and-show-editor-insi.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-17-stitching-clips-no-longer-fails-when-editor-media-metadata-i.md +6 -0
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +14 -8
- package/corpus/templates/clips/shared/view-analytics.ts +4 -0
- package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +12 -3
- package/corpus/templates/content/AGENTS.md +5 -0
- package/corpus/templates/content/actions/clone-creative-context-document.ts +61 -0
- package/corpus/templates/content/actions/provider-api-request.ts +1 -0
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +21 -0
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +29 -0
- package/corpus/templates/content/changelog/2026-07-17-library-now-flags-published-documents-with-newer-versions-an.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/lib/native-creative-context.ts +191 -0
- package/corpus/templates/content/server/plugins/creative-context.ts +8 -1
- package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -3
- package/corpus/templates/design/AGENTS.md +5 -0
- package/corpus/templates/design/actions/clone-creative-context-design-native.ts +111 -0
- package/corpus/templates/design/actions/provider-api-request.ts +1 -0
- package/corpus/templates/design/app/pages/Index.tsx +44 -0
- package/corpus/templates/design/app/pages/design-editor/types.ts +1 -0
- package/corpus/templates/design/changelog/2026-07-17-library-now-flags-published-designs-with-newer-versions-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/design/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/design/server/lib/native-creative-context.ts +265 -0
- package/corpus/templates/design/server/plugins/creative-context.ts +8 -1
- package/corpus/templates/dispatch/actions/provider-api-request.ts +1 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +1 -0
- package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +11 -3
- package/corpus/templates/slides/AGENTS.md +5 -0
- package/corpus/templates/slides/actions/clone-creative-context-deck.ts +96 -0
- package/corpus/templates/slides/actions/extract-pdf.ts +3 -2
- package/corpus/templates/slides/actions/import-file.ts +3 -2
- package/corpus/templates/slides/actions/provider-api-request.ts +1 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +27 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -0
- package/corpus/templates/slides/changelog/2026-07-17-library-now-flags-published-decks-with-newer-versions-and-le.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-pdf-and-powerpoint-imports-now-work-reliably-in-hosted-decks.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/slides/server/lib/native-creative-context.ts +288 -0
- package/corpus/templates/slides/server/plugins/creative-context.ts +4 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +8 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +35 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +7 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +37 -8
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +3 -2
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +9 -0
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -0
- package/dist/client/resources/McpConnectionSuggestion.js +115 -0
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -0
- package/dist/client/resources/McpIntegrationDialog.d.ts +2 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +48 -20
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +9 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +387 -4
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/mcp-integration-logos.d.ts +2 -0
- package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -0
- package/dist/client/resources/mcp-integration-logos.js +109 -0
- package/dist/client/resources/mcp-integration-logos.js.map +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +23 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +33 -1
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/ingestion/office.d.ts.map +1 -1
- package/dist/ingestion/office.js +1 -2
- package/dist/ingestion/office.js.map +1 -1
- package/dist/ingestion/pptx.js +2 -4
- package/dist/ingestion/pptx.js.map +1 -1
- package/dist/localization/default-messages.d.ts +98 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +99 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/actions/call-mcp-tool.js +1 -0
- package/dist/mcp/actions/call-mcp-tool.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/org-admin.d.ts +3 -0
- package/dist/server/org-admin.d.ts.map +1 -0
- package/dist/server/org-admin.js +24 -0
- package/dist/server/org-admin.js.map +1 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
- package/docs/content/integrations.mdx +597 -0
- package/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
- package/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
- package/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
- package/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
- package/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
- package/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
- package/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
- package/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
- package/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
- package/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
- package/docs/content/mcp-clients.mdx +51 -1
- package/docs/content/organizations-teams-permissions.mdx +413 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
|
@@ -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 });
|
|
@@ -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(
|
|
50
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
6
|
-
|
|
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(
|
|
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
|
-
|
|
39
|
+
markDesktopAppDownloaded();
|
|
36
40
|
}, []);
|
|
37
41
|
|
|
38
42
|
return {
|
|
39
43
|
isDesktopApp,
|
|
40
44
|
isMobile,
|
|
41
|
-
shouldShowPromo:
|
|
42
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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>
|
|
@@ -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'
|
|
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,
|
|
13
|
-
* the
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
});
|
|
@@ -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
|
-
|
|
40
|
-
|
|
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.
|