@agent-native/core 0.125.0 → 0.127.1
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 +79 -0
- package/corpus/core/docs/content/external-agents-catalog.mdx +14 -11
- package/corpus/core/docs/content/mcp-protocol.mdx +9 -4
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/index.ts +2 -0
- package/corpus/core/src/agent/production-agent.ts +6 -1
- package/corpus/core/src/agent/run-manager.ts +37 -0
- package/corpus/core/src/agent/run-store.ts +18 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +75 -43
- package/corpus/core/src/client/agent-chat.ts +19 -1
- package/corpus/core/src/client/chat/tool-call-display.tsx +25 -0
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +23 -0
- package/corpus/core/src/client/settings/AgentsSection.tsx +565 -71
- package/corpus/core/src/client/settings/SecretsSection.tsx +71 -36
- package/corpus/core/src/client/use-chat-models.ts +54 -37
- package/corpus/core/src/localization/default-messages.ts +11 -0
- package/corpus/core/src/mcp/ask-app-task-handle.ts +183 -0
- package/corpus/core/src/mcp/builtin-tools.ts +146 -34
- package/corpus/core/src/server/agent-capabilities.ts +8 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +17 -0
- package/corpus/core/src/server/agent-discovery.ts +1 -1
- package/corpus/core/src/server/agent-peer-probe.ts +133 -0
- package/corpus/core/src/server/core-routes-plugin.ts +67 -0
- package/corpus/core/src/server/email-template.ts +13 -0
- package/corpus/core/src/server/email.ts +40 -4
- package/corpus/core/src/shared/mcp-embed-headers.ts +4 -0
- package/corpus/core/src/sharing/actions/share-resource.ts +55 -5
- package/corpus/core/src/sharing/registry.ts +41 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
- package/corpus/templates/assets/actions/edit-image.ts +31 -18
- package/corpus/templates/assets/actions/refine-image.ts +43 -29
- package/corpus/templates/assets/actions/restyle-image.ts +30 -19
- package/corpus/templates/assets/actions/variant-slots.ts +26 -0
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +85 -17
- package/corpus/templates/clips/changelog/2026-07-27-share-notification-emails-now-include-a-clickable-recording-.md +6 -0
- package/corpus/templates/clips/server/db/index.ts +28 -6
- package/corpus/templates/clips/server/lib/share-email-hero.ts +105 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +100 -12
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +17 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +169 -22
- package/corpus/templates/design/app/hooks/use-question-flow.ts +21 -1
- package/corpus/templates/design/changelog/2026-07-28-the-model-you-pick-when-starting-a-new-design-is-now-used-fo.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-28-pending-apps-are-hidden-by-default-and-app-ownership-details.md +6 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +5 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +5 -0
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +31 -8
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +15 -0
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +78 -8
- package/corpus/templates/slides/app/components/presentation/PresenterView.tsx +179 -0
- package/corpus/templates/slides/app/components/presentation/present-channel.ts +15 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +10 -1
- package/corpus/templates/slides/app/lib/export-google-slides-client.ts +67 -0
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +16 -3
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -0
- package/corpus/templates/slides/app/pages/Presentation.tsx +11 -3
- package/corpus/templates/slides/changelog/2026-07-28-export-to-google-slides-now-creates-the-deck-directly-in-you.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-28-opening-a-presentation-link-directly-no-longer-bounces-back-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-28-presenter-view-open-a-second-window-while-presenting-to-see-.md +6 -0
- package/corpus/templates/slides/server/routes/api/exports/google-slides.post.ts +90 -0
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/composer/TiptapComposer.tsx +143 -111
- package/corpus/toolkit/src/composer/runtime-adapters.tsx +2 -0
- package/dist/a2a/index.d.ts +2 -0
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +1 -0
- package/dist/a2a/index.js.map +1 -1
- package/dist/agent/production-agent.d.ts +2 -2
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +6 -3
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +11 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +34 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +8 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +16 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +60 -31
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +6 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +13 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +14 -0
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +16 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/settings/AgentsSection.d.ts.map +1 -1
- package/dist/client/settings/AgentsSection.js +250 -30
- package/dist/client/settings/AgentsSection.js.map +1 -1
- package/dist/client/settings/SecretsSection.d.ts.map +1 -1
- package/dist/client/settings/SecretsSection.js +16 -2
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +46 -27
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +9 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +9 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/ask-app-task-handle.d.ts +25 -0
- package/dist/mcp/ask-app-task-handle.d.ts.map +1 -0
- package/dist/mcp/ask-app-task-handle.js +136 -0
- package/dist/mcp/ask-app-task-handle.js.map +1 -0
- package/dist/mcp/builtin-tools.d.ts +3 -3
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +115 -38
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +6 -6
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-capabilities.d.ts +7 -1
- package/dist/server/agent-capabilities.d.ts.map +1 -1
- package/dist/server/agent-capabilities.js +1 -0
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +7 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +16 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts +1 -0
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +1 -1
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/agent-peer-probe.d.ts +37 -0
- package/dist/server/agent-peer-probe.d.ts.map +1 -0
- package/dist/server/agent-peer-probe.js +90 -0
- package/dist/server/agent-peer-probe.js.map +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +53 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/email-template.d.ts +6 -0
- package/dist/server/email-template.d.ts.map +1 -1
- package/dist/server/email-template.js +6 -0
- package/dist/server/email-template.js.map +1 -1
- package/dist/server/email.d.ts +7 -0
- package/dist/server/email.d.ts.map +1 -1
- package/dist/server/email.js +31 -6
- package/dist/server/email.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -0
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +4 -0
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/sharing/actions/share-resource.d.ts.map +1 -1
- package/dist/sharing/actions/share-resource.js +49 -5
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/sharing/registry.d.ts +40 -1
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
- package/docs/content/external-agents-catalog.mdx +14 -11
- package/docs/content/mcp-protocol.mdx +9 -4
- package/package.json +2 -2
- package/src/a2a/index.ts +2 -0
- package/src/agent/production-agent.ts +6 -1
- package/src/agent/run-manager.ts +37 -0
- package/src/agent/run-store.ts +18 -0
- package/src/client/MultiTabAssistantChat.tsx +75 -43
- package/src/client/agent-chat.ts +19 -1
- package/src/client/chat/tool-call-display.tsx +25 -0
- package/src/client/resources/mcp-integration-catalog.ts +23 -0
- package/src/client/settings/AgentsSection.tsx +565 -71
- package/src/client/settings/SecretsSection.tsx +71 -36
- package/src/client/use-chat-models.ts +54 -37
- package/src/localization/default-messages.ts +11 -0
- package/src/mcp/ask-app-task-handle.ts +183 -0
- package/src/mcp/builtin-tools.ts +146 -34
- package/src/server/agent-capabilities.ts +8 -1
- package/src/server/agent-chat-plugin.ts +17 -0
- package/src/server/agent-discovery.ts +1 -1
- package/src/server/agent-peer-probe.ts +133 -0
- package/src/server/core-routes-plugin.ts +67 -0
- package/src/server/email-template.ts +13 -0
- package/src/server/email.ts +40 -4
- package/src/shared/mcp-embed-headers.ts +4 -0
- package/src/sharing/actions/share-resource.ts +55 -5
- package/src/sharing/registry.ts +41 -1
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import type { ActionRunContext } from "@agent-native/core/action";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
|
|
4
5
|
import { IMAGE_MODELS, IMAGE_QUALITY_TIERS } from "../shared/api.js";
|
|
5
6
|
import { getAssetOrThrow } from "./_helpers.js";
|
|
6
7
|
import generateImage from "./generate-image.js";
|
|
8
|
+
import { resolveLiveBatchContinuation } from "./variant-slots.js";
|
|
7
9
|
|
|
8
10
|
export default defineAction({
|
|
9
11
|
description:
|
|
@@ -24,26 +26,37 @@ export default defineAction({
|
|
|
24
26
|
contextModeOverride: z.literal("off").optional(),
|
|
25
27
|
}),
|
|
26
28
|
parallelSafe: true,
|
|
27
|
-
run: async (args) => {
|
|
29
|
+
run: async (args, context?: ActionRunContext) => {
|
|
28
30
|
const asset = await getAssetOrThrow(args.assetId);
|
|
29
|
-
|
|
31
|
+
const continuation = await resolveLiveBatchContinuation({
|
|
32
|
+
threadId: context?.threadId,
|
|
30
33
|
libraryId: asset.libraryId,
|
|
31
|
-
collectionId: asset.collectionId ?? undefined,
|
|
32
|
-
prompt: args.instruction,
|
|
33
|
-
aspectRatio: (asset.aspectRatio ?? "16:9") as any,
|
|
34
|
-
imageSize: (asset.imageSize ?? "2K") as any,
|
|
35
|
-
model: args.model,
|
|
36
|
-
tier: args.tier,
|
|
37
|
-
intent: "edit",
|
|
38
|
-
styleStrength: "balanced",
|
|
39
|
-
referenceAssetIds: [],
|
|
40
|
-
includeLogo: false,
|
|
41
|
-
groundingMode: "off",
|
|
42
|
-
subjectAssetId: asset.id,
|
|
43
|
-
slotId: args.slotId,
|
|
44
|
-
source: args.source,
|
|
45
|
-
callerAppId: args.callerAppId,
|
|
46
|
-
contextModeOverride: args.contextModeOverride,
|
|
47
34
|
});
|
|
35
|
+
return generateImage.run(
|
|
36
|
+
{
|
|
37
|
+
libraryId: asset.libraryId,
|
|
38
|
+
collectionId:
|
|
39
|
+
asset.collectionId ?? continuation?.collectionId ?? undefined,
|
|
40
|
+
presetId: continuation?.presetId ?? undefined,
|
|
41
|
+
sessionId: continuation?.sessionId ?? undefined,
|
|
42
|
+
prompt: args.instruction,
|
|
43
|
+
aspectRatio: (asset.aspectRatio ?? "16:9") as any,
|
|
44
|
+
imageSize: (asset.imageSize ?? "2K") as any,
|
|
45
|
+
model: args.model,
|
|
46
|
+
tier: args.tier,
|
|
47
|
+
intent: "edit",
|
|
48
|
+
styleStrength: "balanced",
|
|
49
|
+
referenceAssetIds: [],
|
|
50
|
+
includeLogo: false,
|
|
51
|
+
groundingMode: "off",
|
|
52
|
+
subjectAssetId: asset.id,
|
|
53
|
+
slotId: args.slotId,
|
|
54
|
+
variantBatchId: continuation?.variantBatchId,
|
|
55
|
+
source: args.source,
|
|
56
|
+
callerAppId: args.callerAppId,
|
|
57
|
+
contextModeOverride: args.contextModeOverride,
|
|
58
|
+
},
|
|
59
|
+
context,
|
|
60
|
+
);
|
|
48
61
|
},
|
|
49
62
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import type { ActionRunContext } from "@agent-native/core/action";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
|
|
4
5
|
import { parseJson } from "../server/lib/json.js";
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
requireGenerationSessionInLibrary,
|
|
9
10
|
} from "./_helpers.js";
|
|
10
11
|
import generateImage from "./generate-image.js";
|
|
12
|
+
import { resolveLiveBatchContinuation } from "./variant-slots.js";
|
|
11
13
|
|
|
12
14
|
export default defineAction({
|
|
13
15
|
description:
|
|
@@ -31,19 +33,22 @@ export default defineAction({
|
|
|
31
33
|
contextModeOverride: z.literal("off").optional(),
|
|
32
34
|
}),
|
|
33
35
|
parallelSafe: true,
|
|
34
|
-
run: async (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
run: async (
|
|
37
|
+
{
|
|
38
|
+
assetId,
|
|
39
|
+
feedback,
|
|
40
|
+
presetId,
|
|
41
|
+
sessionId,
|
|
42
|
+
model,
|
|
43
|
+
aspectRatio,
|
|
44
|
+
imageSize,
|
|
45
|
+
slotId,
|
|
46
|
+
source,
|
|
47
|
+
callerAppId,
|
|
48
|
+
contextModeOverride,
|
|
49
|
+
},
|
|
50
|
+
context?: ActionRunContext,
|
|
51
|
+
) => {
|
|
47
52
|
const asset = await getAssetOrThrow(assetId);
|
|
48
53
|
if (sessionId) {
|
|
49
54
|
await requireGenerationSessionInLibrary(sessionId, asset.libraryId);
|
|
@@ -57,23 +62,32 @@ export default defineAction({
|
|
|
57
62
|
"",
|
|
58
63
|
"Preserve the strongest successful parts of the prior candidate unless the feedback contradicts them.",
|
|
59
64
|
].join("\n");
|
|
60
|
-
|
|
65
|
+
const continuation = await resolveLiveBatchContinuation({
|
|
66
|
+
threadId: context?.threadId,
|
|
61
67
|
libraryId: asset.libraryId,
|
|
62
|
-
collectionId: asset.collectionId ?? undefined,
|
|
63
|
-
presetId,
|
|
64
|
-
sessionId,
|
|
65
|
-
prompt,
|
|
66
|
-
aspectRatio: (aspectRatio ?? asset.aspectRatio ?? "16:9") as any,
|
|
67
|
-
imageSize: (imageSize ?? asset.imageSize ?? "2K") as any,
|
|
68
|
-
model: (model ?? asset.model ?? "gemini-3.1-flash-image") as any,
|
|
69
|
-
categories: metadata.category ? [metadata.category] : undefined,
|
|
70
|
-
includeLogo: false,
|
|
71
|
-
groundingMode: "auto",
|
|
72
|
-
sourceAssetId: asset.id,
|
|
73
|
-
slotId,
|
|
74
|
-
source,
|
|
75
|
-
callerAppId,
|
|
76
|
-
contextModeOverride,
|
|
77
68
|
});
|
|
69
|
+
return generateImage.run(
|
|
70
|
+
{
|
|
71
|
+
libraryId: asset.libraryId,
|
|
72
|
+
collectionId:
|
|
73
|
+
asset.collectionId ?? continuation?.collectionId ?? undefined,
|
|
74
|
+
presetId: presetId ?? continuation?.presetId ?? undefined,
|
|
75
|
+
sessionId: sessionId ?? continuation?.sessionId ?? undefined,
|
|
76
|
+
prompt,
|
|
77
|
+
aspectRatio: (aspectRatio ?? asset.aspectRatio ?? "16:9") as any,
|
|
78
|
+
imageSize: (imageSize ?? asset.imageSize ?? "2K") as any,
|
|
79
|
+
model: (model ?? asset.model ?? "gemini-3.1-flash-image") as any,
|
|
80
|
+
categories: metadata.category ? [metadata.category] : undefined,
|
|
81
|
+
includeLogo: false,
|
|
82
|
+
groundingMode: "auto",
|
|
83
|
+
sourceAssetId: asset.id,
|
|
84
|
+
slotId,
|
|
85
|
+
variantBatchId: continuation?.variantBatchId,
|
|
86
|
+
source,
|
|
87
|
+
callerAppId,
|
|
88
|
+
contextModeOverride,
|
|
89
|
+
},
|
|
90
|
+
context,
|
|
91
|
+
);
|
|
78
92
|
},
|
|
79
93
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import type { ActionRunContext } from "@agent-native/core/action";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
|
|
4
5
|
import {
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
} from "../shared/api.js";
|
|
11
12
|
import { getAssetOrThrow } from "./_helpers.js";
|
|
12
13
|
import generateImage from "./generate-image.js";
|
|
14
|
+
import { resolveLiveBatchContinuation } from "./variant-slots.js";
|
|
13
15
|
|
|
14
16
|
export default defineAction({
|
|
15
17
|
description:
|
|
@@ -38,30 +40,39 @@ export default defineAction({
|
|
|
38
40
|
contextModeOverride: z.literal("off").optional(),
|
|
39
41
|
}),
|
|
40
42
|
parallelSafe: true,
|
|
41
|
-
run: async (args) => {
|
|
43
|
+
run: async (args, context?: ActionRunContext) => {
|
|
42
44
|
const subject = await getAssetOrThrow(args.subjectAssetId);
|
|
43
45
|
const prompt =
|
|
44
46
|
args.prompt?.trim() ||
|
|
45
47
|
"Apply this library's brand style to the subject image while preserving the subject, pose, composition, and framing.";
|
|
46
|
-
|
|
48
|
+
const continuation = await resolveLiveBatchContinuation({
|
|
49
|
+
threadId: context?.threadId,
|
|
47
50
|
libraryId: subject.libraryId,
|
|
48
|
-
collectionId: subject.collectionId ?? undefined,
|
|
49
|
-
presetId: args.presetId,
|
|
50
|
-
sessionId: args.sessionId,
|
|
51
|
-
prompt,
|
|
52
|
-
aspectRatio: (args.aspectRatio ?? subject.aspectRatio ?? "16:9") as any,
|
|
53
|
-
imageSize: (args.imageSize ?? subject.imageSize ?? "2K") as any,
|
|
54
|
-
model: args.model,
|
|
55
|
-
tier: args.tier,
|
|
56
|
-
intent: "restyle",
|
|
57
|
-
styleStrength: args.styleStrength,
|
|
58
|
-
includeLogo: false,
|
|
59
|
-
groundingMode: "auto",
|
|
60
|
-
subjectAssetId: subject.id,
|
|
61
|
-
slotId: args.slotId,
|
|
62
|
-
source: args.source,
|
|
63
|
-
callerAppId: args.callerAppId,
|
|
64
|
-
contextModeOverride: args.contextModeOverride,
|
|
65
51
|
});
|
|
52
|
+
return generateImage.run(
|
|
53
|
+
{
|
|
54
|
+
libraryId: subject.libraryId,
|
|
55
|
+
collectionId:
|
|
56
|
+
subject.collectionId ?? continuation?.collectionId ?? undefined,
|
|
57
|
+
presetId: args.presetId ?? continuation?.presetId ?? undefined,
|
|
58
|
+
sessionId: args.sessionId ?? continuation?.sessionId ?? undefined,
|
|
59
|
+
prompt,
|
|
60
|
+
aspectRatio: (args.aspectRatio ?? subject.aspectRatio ?? "16:9") as any,
|
|
61
|
+
imageSize: (args.imageSize ?? subject.imageSize ?? "2K") as any,
|
|
62
|
+
model: args.model,
|
|
63
|
+
tier: args.tier,
|
|
64
|
+
intent: "restyle",
|
|
65
|
+
styleStrength: args.styleStrength,
|
|
66
|
+
includeLogo: false,
|
|
67
|
+
groundingMode: "auto",
|
|
68
|
+
subjectAssetId: subject.id,
|
|
69
|
+
slotId: args.slotId,
|
|
70
|
+
variantBatchId: continuation?.variantBatchId,
|
|
71
|
+
source: args.source,
|
|
72
|
+
callerAppId: args.callerAppId,
|
|
73
|
+
contextModeOverride: args.contextModeOverride,
|
|
74
|
+
},
|
|
75
|
+
context,
|
|
76
|
+
);
|
|
66
77
|
},
|
|
67
78
|
});
|
|
@@ -177,6 +177,32 @@ export async function readVariantState(
|
|
|
177
177
|
return withVariantStateLock(() => readVariantStateUnlocked(scopeId));
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
// Iterations (refine/edit/restyle) are a continuation of what is already on
|
|
181
|
+
// screen for this thread, not a new generation topic: they should append into
|
|
182
|
+
// the live tray instead of tripping the "new batch/run" reset in
|
|
183
|
+
// isSameVariantScope. Passing the current tray's batch/run id plus its
|
|
184
|
+
// collection/preset/session back through keeps every isSameVariantScope check
|
|
185
|
+
// satisfied.
|
|
186
|
+
export async function resolveLiveBatchContinuation(input: {
|
|
187
|
+
threadId?: string | null;
|
|
188
|
+
libraryId: string;
|
|
189
|
+
}): Promise<{
|
|
190
|
+
variantBatchId: string;
|
|
191
|
+
collectionId: string | null;
|
|
192
|
+
presetId: string | null;
|
|
193
|
+
sessionId: string | null;
|
|
194
|
+
} | null> {
|
|
195
|
+
if (!input.threadId) return null;
|
|
196
|
+
const state = await readVariantState(input.threadId);
|
|
197
|
+
if (!state || state.libraryId !== input.libraryId) return null;
|
|
198
|
+
return {
|
|
199
|
+
variantBatchId: state.batchId ?? state.runId,
|
|
200
|
+
collectionId: state.collectionId ?? null,
|
|
201
|
+
presetId: state.presetId ?? null,
|
|
202
|
+
sessionId: state.sessionId ?? null,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
180
206
|
export async function writeVariantState(
|
|
181
207
|
state: AssetVariantState,
|
|
182
208
|
scopeId?: string | null,
|
|
@@ -89,6 +89,9 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
|
|
|
89
89
|
const queryClient = useQueryClient();
|
|
90
90
|
const [clearAllOpen, setClearAllOpen] = useState(false);
|
|
91
91
|
const [previewSlotId, setPreviewSlotId] = useState<string | null>(null);
|
|
92
|
+
const trayScrollRef = useRef<HTMLDivElement | null>(null);
|
|
93
|
+
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
|
94
|
+
const [canScrollRight, setCanScrollRight] = useState(false);
|
|
92
95
|
const stateKey = variantStateKey(threadId);
|
|
93
96
|
const stateQueryKey = useMemo(() => ["app-state", stateKey], [stateKey]);
|
|
94
97
|
const { data: variants } = useQuery({
|
|
@@ -129,6 +132,21 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
|
|
|
129
132
|
),
|
|
130
133
|
[variants?.slots],
|
|
131
134
|
);
|
|
135
|
+
// Variant numbers reflect generation order (oldest = 1), not display order:
|
|
136
|
+
// refined candidates render first but keep the next number in sequence
|
|
137
|
+
// instead of stealing "Variant 1" from the newest-first display sort above.
|
|
138
|
+
const variantNumberBySlotId = useMemo(() => {
|
|
139
|
+
const map = new Map<string, number>();
|
|
140
|
+
(variants?.slots ?? [])
|
|
141
|
+
.slice()
|
|
142
|
+
.sort(
|
|
143
|
+
(left, right) =>
|
|
144
|
+
slotTime(left) - slotTime(right) ||
|
|
145
|
+
left.slotId.localeCompare(right.slotId),
|
|
146
|
+
)
|
|
147
|
+
.forEach((slot, index) => map.set(slot.slotId, index + 1));
|
|
148
|
+
return map;
|
|
149
|
+
}, [variants?.slots]);
|
|
132
150
|
const belongsToThread = Boolean(
|
|
133
151
|
variants &&
|
|
134
152
|
(threadId ? variants.threadId === threadId : !variants.threadId),
|
|
@@ -165,6 +183,25 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
|
|
|
165
183
|
[previewSlotId, slots],
|
|
166
184
|
);
|
|
167
185
|
|
|
186
|
+
const updateScrollEdges = () => {
|
|
187
|
+
const el = trayScrollRef.current;
|
|
188
|
+
if (!el) return;
|
|
189
|
+
setCanScrollLeft(el.scrollLeft > 1);
|
|
190
|
+
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
// Re-measure after the slot list changes size (new/removed candidates)
|
|
195
|
+
// since that can flip whether either arrow should be enabled.
|
|
196
|
+
updateScrollEdges();
|
|
197
|
+
}, [slots.length]);
|
|
198
|
+
|
|
199
|
+
const scrollTrayBy = (direction: 1 | -1) => {
|
|
200
|
+
const el = trayScrollRef.current;
|
|
201
|
+
if (!el) return;
|
|
202
|
+
el.scrollBy({ left: direction * el.clientWidth * 0.9, behavior: "smooth" });
|
|
203
|
+
};
|
|
204
|
+
|
|
168
205
|
if (!belongsToThread || !variants) return null;
|
|
169
206
|
if (slots.length === 0) return null;
|
|
170
207
|
|
|
@@ -296,6 +333,7 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
|
|
|
296
333
|
<GenerationPreviewDialog
|
|
297
334
|
slot={previewSlot}
|
|
298
335
|
slots={slots}
|
|
336
|
+
variantNumberBySlotId={variantNumberBySlotId}
|
|
299
337
|
prompt={variants.prompt}
|
|
300
338
|
libraryTitle={libraryTitle}
|
|
301
339
|
isSaving={saveGenerated.isPending}
|
|
@@ -351,22 +389,50 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
|
|
|
351
389
|
</Button>
|
|
352
390
|
</div>
|
|
353
391
|
|
|
354
|
-
<div className="
|
|
355
|
-
<div
|
|
356
|
-
{
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
392
|
+
<div className="relative">
|
|
393
|
+
<div
|
|
394
|
+
ref={trayScrollRef}
|
|
395
|
+
onScroll={updateScrollEdges}
|
|
396
|
+
className="assets-library-tray flex gap-3 overflow-x-auto scroll-smooth p-3"
|
|
397
|
+
>
|
|
398
|
+
{slots.map((slot) => {
|
|
399
|
+
const variantNumber =
|
|
400
|
+
variantNumberBySlotId.get(slot.slotId) ?? 1;
|
|
401
|
+
return (
|
|
402
|
+
<GenerationDraftCard
|
|
403
|
+
key={slot.slotId}
|
|
404
|
+
slot={slot}
|
|
405
|
+
variantNumber={variantNumber}
|
|
406
|
+
isSaving={saveGenerated.isPending}
|
|
407
|
+
isDismissing={dismissSlot.isPending}
|
|
408
|
+
onPreview={() => setPreviewSlotId(slot.slotId)}
|
|
409
|
+
onSave={() => saveSlot(slot)}
|
|
410
|
+
onRefine={() => refineSlot(slot, variantNumber)}
|
|
411
|
+
onDismiss={() => dismissSlotById(slot)}
|
|
412
|
+
/>
|
|
413
|
+
);
|
|
414
|
+
})}
|
|
369
415
|
</div>
|
|
416
|
+
{canScrollLeft ? (
|
|
417
|
+
<button
|
|
418
|
+
type="button"
|
|
419
|
+
aria-label={t("library.previousImage")}
|
|
420
|
+
onClick={() => scrollTrayBy(-1)}
|
|
421
|
+
className="absolute left-1 top-1/2 z-10 inline-flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full border border-border/80 bg-background/90 text-foreground shadow-sm transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
422
|
+
>
|
|
423
|
+
<IconChevronLeft className="h-4 w-4" />
|
|
424
|
+
</button>
|
|
425
|
+
) : null}
|
|
426
|
+
{canScrollRight ? (
|
|
427
|
+
<button
|
|
428
|
+
type="button"
|
|
429
|
+
aria-label={t("library.nextImage")}
|
|
430
|
+
onClick={() => scrollTrayBy(1)}
|
|
431
|
+
className="absolute right-1 top-1/2 z-10 inline-flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full border border-border/80 bg-background/90 text-foreground shadow-sm transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
432
|
+
>
|
|
433
|
+
<IconChevronRight className="h-4 w-4" />
|
|
434
|
+
</button>
|
|
435
|
+
) : null}
|
|
370
436
|
</div>
|
|
371
437
|
</div>
|
|
372
438
|
</section>
|
|
@@ -399,7 +465,7 @@ function GenerationDraftCard({
|
|
|
399
465
|
number: variantNumber,
|
|
400
466
|
});
|
|
401
467
|
return (
|
|
402
|
-
<div className="overflow-hidden rounded-lg border border-border/80 bg-background transition hover:border-foreground/25 hover:bg-muted/10">
|
|
468
|
+
<div className="w-36 shrink-0 overflow-hidden rounded-lg border border-border/80 bg-background transition hover:border-foreground/25 hover:bg-muted/10 sm:w-44">
|
|
403
469
|
<div className="group relative">
|
|
404
470
|
<button
|
|
405
471
|
type="button"
|
|
@@ -510,6 +576,7 @@ function GenerationDraftCard({
|
|
|
510
576
|
function GenerationPreviewDialog({
|
|
511
577
|
slot,
|
|
512
578
|
slots,
|
|
579
|
+
variantNumberBySlotId,
|
|
513
580
|
prompt,
|
|
514
581
|
libraryTitle,
|
|
515
582
|
isSaving,
|
|
@@ -522,6 +589,7 @@ function GenerationPreviewDialog({
|
|
|
522
589
|
}: {
|
|
523
590
|
slot: VariantSlot | null;
|
|
524
591
|
slots: VariantSlot[];
|
|
592
|
+
variantNumberBySlotId: Map<string, number>;
|
|
525
593
|
prompt: string;
|
|
526
594
|
libraryTitle: string | null;
|
|
527
595
|
isSaving: boolean;
|
|
@@ -553,7 +621,7 @@ function GenerationPreviewDialog({
|
|
|
553
621
|
const sources = slot ? assetPreviewSources(slot, "preview") : [];
|
|
554
622
|
const src = sources[0];
|
|
555
623
|
const variantNumber = slot
|
|
556
|
-
?
|
|
624
|
+
? (variantNumberBySlotId.get(slot.slotId) ?? 0)
|
|
557
625
|
: 0;
|
|
558
626
|
const variantLabel = t("library.variantWithNumber", {
|
|
559
627
|
number: variantNumber,
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { createGetDb, getDbExec } from "@agent-native/core/db";
|
|
2
|
-
import {
|
|
2
|
+
import { organizations } from "@agent-native/core/org";
|
|
3
3
|
import { registerShareableResource } from "@agent-native/core/sharing";
|
|
4
4
|
import { eq } from "drizzle-orm";
|
|
5
5
|
|
|
6
|
+
import {
|
|
7
|
+
absoluteUrl,
|
|
8
|
+
recordingShareHeroHtml,
|
|
9
|
+
} from "../lib/share-email-hero.js";
|
|
6
10
|
import * as schema from "./schema.js";
|
|
7
11
|
|
|
8
12
|
export const getDb = createGetDb(schema);
|
|
@@ -22,9 +26,20 @@ async function orgBrandLogoUrl(
|
|
|
22
26
|
.from(schema.organizationSettings)
|
|
23
27
|
.where(eq(schema.organizationSettings.organizationId, organizationId))
|
|
24
28
|
.limit(1);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
return absoluteUrl(row?.brandLogoUrl);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Show the sharing org's name beside the logo instead of the app name. */
|
|
33
|
+
async function orgBrandName(
|
|
34
|
+
organizationId: string | undefined,
|
|
35
|
+
): Promise<string | undefined> {
|
|
36
|
+
if (!organizationId) return undefined;
|
|
37
|
+
const [row] = await getDb()
|
|
38
|
+
.select({ name: organizations.name })
|
|
39
|
+
.from(organizations)
|
|
40
|
+
.where(eq(organizations.id, organizationId))
|
|
41
|
+
.limit(1);
|
|
42
|
+
return row?.name?.trim() || undefined;
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
registerShareableResource({
|
|
@@ -34,8 +49,15 @@ registerShareableResource({
|
|
|
34
49
|
displayName: "Recording",
|
|
35
50
|
titleColumn: "title",
|
|
36
51
|
getResourcePath: (recording) => `/r/${recording.id}`,
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
getLogoUrl: (recording) => orgBrandLogoUrl(recording.organizationId),
|
|
53
|
+
getBrandName: (recording) => orgBrandName(recording.organizationId),
|
|
54
|
+
// Replies reach the person who shared the clip; the sending address stays
|
|
55
|
+
// the verified one so SPF/DKIM still pass.
|
|
56
|
+
getSender: (_recording, ctx) => ({
|
|
57
|
+
fromName: `${ctx.sender.name} via Clips`,
|
|
58
|
+
replyTo: ctx.sender.email,
|
|
59
|
+
}),
|
|
60
|
+
getHeroHtml: (recording, ctx) => recordingShareHeroHtml(recording, ctx),
|
|
39
61
|
getDb,
|
|
40
62
|
ownerAccessIgnoresOrg: true,
|
|
41
63
|
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAppProductionUrl,
|
|
3
|
+
withConfiguredAppBasePath,
|
|
4
|
+
} from "@agent-native/core/server";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Origin plus the configured mount path. Deployments served under
|
|
8
|
+
* APP_BASE_PATH (e.g. `/clips`) would otherwise link email assets at the
|
|
9
|
+
* gateway root, where nothing is served.
|
|
10
|
+
*/
|
|
11
|
+
function appBaseUrl(): string {
|
|
12
|
+
return withConfiguredAppBasePath(getAppProductionUrl());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Make a stored URL absolute for use in emails (relative paths won't load). */
|
|
16
|
+
export function absoluteUrl(
|
|
17
|
+
url: string | null | undefined,
|
|
18
|
+
): string | undefined {
|
|
19
|
+
const trimmed = url?.trim();
|
|
20
|
+
if (!trimmed) return undefined;
|
|
21
|
+
return trimmed.startsWith("/") ? `${appBaseUrl()}${trimmed}` : trimmed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function escapeAttr(s: string): string {
|
|
25
|
+
return s
|
|
26
|
+
.replace(/&/g, "&")
|
|
27
|
+
.replace(/</g, "<")
|
|
28
|
+
.replace(/>/g, ">")
|
|
29
|
+
.replace(/"/g, """)
|
|
30
|
+
.replace(/'/g, "'");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Percent-encode characters that could terminate a CSS `url('…')`. A style
|
|
35
|
+
* attribute is HTML-decoded before it is parsed as CSS, so HTML escaping alone
|
|
36
|
+
* does not protect the CSS context — an escaped quote decodes back to a real
|
|
37
|
+
* one and lets the value break out into further declarations.
|
|
38
|
+
*/
|
|
39
|
+
function escapeCssUrl(url: string): string {
|
|
40
|
+
return url.replace(
|
|
41
|
+
/['"()\\\s]/g,
|
|
42
|
+
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isHttpUrl(value: string): boolean {
|
|
47
|
+
try {
|
|
48
|
+
const { protocol } = new URL(value);
|
|
49
|
+
return protocol === "https:" || protocol === "http:";
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type ShareHeroRecording = {
|
|
56
|
+
thumbnailUrl?: string | null;
|
|
57
|
+
animatedThumbnailUrl?: string | null;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build the share-email preview for a recording: a 16:9 thumbnail with a
|
|
62
|
+
* centered play badge, linking to the clip. Uses the background-image + VML
|
|
63
|
+
* technique so the badge stays centered across clients, including Outlook. The
|
|
64
|
+
* play badge is served from Clips' own public assets.
|
|
65
|
+
*/
|
|
66
|
+
export function recordingShareHeroHtml(
|
|
67
|
+
recording: ShareHeroRecording,
|
|
68
|
+
ctx: { href: string; alt?: string },
|
|
69
|
+
): string | undefined {
|
|
70
|
+
// GIF-only recordings leave `thumbnailUrl` empty, so fall back to the
|
|
71
|
+
// animated thumbnail rather than dropping the preview entirely.
|
|
72
|
+
const thumb = absoluteUrl(
|
|
73
|
+
recording.thumbnailUrl || recording.animatedThumbnailUrl,
|
|
74
|
+
);
|
|
75
|
+
if (!thumb || !isHttpUrl(thumb)) return undefined;
|
|
76
|
+
const url = escapeAttr(thumb);
|
|
77
|
+
const cssUrl = escapeAttr(escapeCssUrl(thumb));
|
|
78
|
+
const href = escapeAttr(ctx.href);
|
|
79
|
+
const title = escapeAttr(ctx.alt ?? "Play video");
|
|
80
|
+
const badge = escapeAttr(`${appBaseUrl()}/play-badge.png`);
|
|
81
|
+
const W = 488;
|
|
82
|
+
const H = 275;
|
|
83
|
+
return `
|
|
84
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="margin:24px 0 0 0;">
|
|
85
|
+
<tr>
|
|
86
|
+
<td>
|
|
87
|
+
<a href="${href}" style="display:block; text-decoration:none;" title="${title}">
|
|
88
|
+
<!--[if mso]>
|
|
89
|
+
<v:image xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:${W}px;height:${H}px;" src="${url}" />
|
|
90
|
+
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="false" stroke="false" style="position:absolute;width:${W}px;height:${H}px;">
|
|
91
|
+
<v:textbox inset="0,0,0,0"><center>
|
|
92
|
+
<![endif]-->
|
|
93
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${W}" height="${H}" background="${url}" style="width:100%; max-width:${W}px; height:${H}px; background-image:url('${cssUrl}'); background-size:cover; background-position:center; background-color:#141417; border-radius:12px;">
|
|
94
|
+
<tr>
|
|
95
|
+
<td align="center" valign="middle" height="${H}" style="height:${H}px;">
|
|
96
|
+
<img src="${badge}" alt="Play video" width="64" height="64" style="width:64px; height:64px; border:0; display:inline-block;" />
|
|
97
|
+
</td>
|
|
98
|
+
</tr>
|
|
99
|
+
</table>
|
|
100
|
+
<!--[if mso]></center></v:textbox></v:rect><![endif]-->
|
|
101
|
+
</a>
|
|
102
|
+
</td>
|
|
103
|
+
</tr>
|
|
104
|
+
</table>`;
|
|
105
|
+
}
|