@agent-native/core 0.92.5 → 0.92.6
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/template-assets.mdx +139 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +96 -6
- package/corpus/core/src/client/AssistantChat.tsx +127 -15
- package/corpus/core/src/client/chat/tool-call-display.tsx +9 -3
- package/corpus/core/src/client/org/TeamPage.tsx +274 -177
- package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -6
- package/corpus/core/src/client/settings/SettingsPanel.tsx +1 -1
- package/corpus/core/src/client/settings/SettingsTabsPage.tsx +49 -1
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
- package/corpus/core/src/demo/config.ts +3 -3
- package/corpus/core/src/demo/redact.ts +720 -0
- package/corpus/core/src/extensions/url-safety.ts +13 -1
- package/corpus/core/src/localization/default-messages.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +7 -0
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +7 -0
- package/corpus/templates/analytics/AGENTS.md +6 -3
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-monitors-use-scheduled-workers-in-production-serverless.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +5 -0
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +14 -2
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +35 -0
- package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +19 -0
- package/corpus/templates/assets/AGENTS.md +13 -0
- package/corpus/templates/assets/actions/_generation-preset-settings.ts +73 -0
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +63 -0
- package/corpus/templates/assets/actions/create-generation-preset.ts +22 -8
- package/corpus/templates/assets/actions/duplicate-library.ts +49 -1
- package/corpus/templates/assets/actions/generate-image-batch.ts +14 -1
- package/corpus/templates/assets/actions/generate-image.ts +195 -26
- package/corpus/templates/assets/actions/import-asset-from-url.ts +323 -0
- package/corpus/templates/assets/actions/list-generation-presets.ts +1 -1
- package/corpus/templates/assets/actions/rerun-generation-run.ts +37 -0
- package/corpus/templates/assets/actions/update-generation-preset.ts +19 -1
- package/corpus/templates/assets/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/assets/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/assets/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/assets/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/assets/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/assets/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/assets/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/assets/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/assets/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +41 -0
- package/corpus/templates/assets/app/i18n-data.ts +93 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +66 -11
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +959 -24
- package/corpus/templates/assets/changelog/2026-07-07-preset-skeleton-controls-fit-correctly-on-narrower-edit-page.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-09-import-reference-images-from-url.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-09-preset-reference-board.md +6 -0
- package/corpus/templates/assets/server/handlers/assets.ts +7 -27
- package/corpus/templates/assets/server/lib/generation.ts +83 -2
- package/corpus/templates/assets/server/lib/preset-chat-context.ts +17 -4
- package/corpus/templates/assets/server/lib/preset-references.ts +182 -0
- package/corpus/templates/assets/server/lib/upload-validation.ts +38 -0
- package/corpus/templates/assets/shared/api.ts +20 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +6 -0
- package/corpus/templates/calendar/actions/create-event.ts +8 -2
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +22 -7
- package/corpus/templates/calendar/changelog/2026-07-09-calendar-events-with-guests-now-automatically-get-a-google-m.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-09-new-events-now-mark-the-creator-rsvp-as-yes.md +6 -0
- package/corpus/templates/calendar/server/handlers/events.ts +9 -2
- package/corpus/templates/calendar/server/lib/event-video-conferencing.ts +58 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +1 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-reminder-popovers-can-now-be-dismissed-with-a-top-le.md +6 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +3 -1
- package/corpus/templates/clips/desktop/src/styles.css +13 -9
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +5 -2
- package/dist/agent/production-agent.d.ts +34 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +58 -5
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +3 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +96 -15
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +5 -1
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +32 -13
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts +3 -3
- package/dist/client/settings/DemoModeSection.js +4 -4
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
- package/dist/client/settings/SettingsTabsPage.js +38 -2
- package/dist/client/settings/SettingsTabsPage.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/demo/actions/toggle-demo-mode.js +2 -2
- package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
- package/dist/demo/config.js +3 -3
- package/dist/demo/config.js.map +1 -1
- package/dist/demo/redact.d.ts +1 -1
- package/dist/demo/redact.d.ts.map +1 -1
- package/dist/demo/redact.js +0 -0
- package/dist/demo/redact.js.map +1 -1
- package/dist/extensions/url-safety.d.ts +5 -0
- package/dist/extensions/url-safety.d.ts.map +1 -1
- package/dist/extensions/url-safety.js +10 -0
- package/dist/extensions/url-safety.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +7 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +7 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/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/templates/default/app/i18n/ar-SA.ts +7 -0
- package/dist/templates/default/app/i18n/de-DE.ts +7 -0
- package/dist/templates/default/app/i18n/en-US.ts +7 -0
- package/dist/templates/default/app/i18n/es-ES.ts +7 -0
- package/dist/templates/default/app/i18n/fr-FR.ts +7 -0
- package/dist/templates/default/app/i18n/hi-IN.ts +7 -0
- package/dist/templates/default/app/i18n/ja-JP.ts +7 -0
- package/dist/templates/default/app/i18n/ko-KR.ts +7 -0
- package/dist/templates/default/app/i18n/pt-BR.ts +7 -0
- package/dist/templates/default/app/i18n/zh-CN.ts +7 -0
- package/dist/templates/default/app/i18n/zh-TW.ts +7 -0
- package/docs/content/template-assets.mdx +139 -0
- package/package.json +1 -1
- package/src/templates/default/app/i18n/ar-SA.ts +7 -0
- package/src/templates/default/app/i18n/de-DE.ts +7 -0
- package/src/templates/default/app/i18n/en-US.ts +7 -0
- package/src/templates/default/app/i18n/es-ES.ts +7 -0
- package/src/templates/default/app/i18n/fr-FR.ts +7 -0
- package/src/templates/default/app/i18n/hi-IN.ts +7 -0
- package/src/templates/default/app/i18n/ja-JP.ts +7 -0
- package/src/templates/default/app/i18n/ko-KR.ts +7 -0
- package/src/templates/default/app/i18n/pt-BR.ts +7 -0
- package/src/templates/default/app/i18n/zh-CN.ts +7 -0
- package/src/templates/default/app/i18n/zh-TW.ts +7 -0
|
@@ -5,12 +5,14 @@ import {
|
|
|
5
5
|
getRequestUserEmail,
|
|
6
6
|
} from "@agent-native/core/server/request-context";
|
|
7
7
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
8
|
-
import { and, eq, inArray, ne } from "drizzle-orm";
|
|
8
|
+
import { and, eq, inArray, ne, notInArray } from "drizzle-orm";
|
|
9
9
|
import { nanoid } from "nanoid";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
12
|
import { getDb, schema } from "../server/db/index.js";
|
|
13
13
|
import { nowIso, parseJson, stringifyJson } from "../server/lib/json.js";
|
|
14
|
+
import { normalizePresetReferences } from "../server/lib/preset-references.js";
|
|
15
|
+
import { normalizePresetSkeletonSpec } from "../server/lib/preset-skeleton.js";
|
|
14
16
|
import {
|
|
15
17
|
serializeAsset,
|
|
16
18
|
serializeGenerationPreset,
|
|
@@ -57,6 +59,35 @@ function remapJsonText(
|
|
|
57
59
|
return stringifyJson(remapJsonValue(parseJson(text, {}), ids));
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
// Assets pinned by preset settings (reference board entries, skeleton plate/
|
|
63
|
+
// mask/foreground) must be copied even when the general asset filter would
|
|
64
|
+
// skip them — e.g. board subject photos upload as role "subject_reference".
|
|
65
|
+
// Otherwise the duplicated preset keeps source-library asset ids and
|
|
66
|
+
// generation in the copy fails its library-membership check.
|
|
67
|
+
function pinnedPresetAssetIds(
|
|
68
|
+
presets: Array<{ settings: string | null }>,
|
|
69
|
+
): Set<string> {
|
|
70
|
+
const ids = new Set<string>();
|
|
71
|
+
for (const preset of presets) {
|
|
72
|
+
const settings = parseJson<{
|
|
73
|
+
skeletonSpec?: unknown;
|
|
74
|
+
presetReferences?: unknown;
|
|
75
|
+
}>(preset.settings, {});
|
|
76
|
+
for (const entry of normalizePresetReferences(settings.presetReferences)) {
|
|
77
|
+
for (const assetId of entry.assetIds) ids.add(assetId);
|
|
78
|
+
}
|
|
79
|
+
const skeleton = normalizePresetSkeletonSpec(settings.skeletonSpec);
|
|
80
|
+
if (skeleton) {
|
|
81
|
+
ids.add(skeleton.background.assetId);
|
|
82
|
+
if (skeleton.mask) ids.add(skeleton.mask.assetId);
|
|
83
|
+
for (const layer of skeleton.foreground ?? []) {
|
|
84
|
+
if (typeof layer.source === "object") ids.add(layer.source.assetId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return ids;
|
|
89
|
+
}
|
|
90
|
+
|
|
60
91
|
export default defineAction({
|
|
61
92
|
description:
|
|
62
93
|
"Duplicate a Brand Kit into a new private copy owned by the current user. Copies the kit metadata, collections, folders, generation presets, and curated reference/saved assets, but not generation history, handoff sessions, shares, or visibility.",
|
|
@@ -127,6 +158,23 @@ export default defineAction({
|
|
|
127
158
|
),
|
|
128
159
|
]);
|
|
129
160
|
|
|
161
|
+
const missingPinnedAssetIds = [...pinnedPresetAssetIds(presets)].filter(
|
|
162
|
+
(assetId) => !assets.some((asset) => asset.id === assetId),
|
|
163
|
+
);
|
|
164
|
+
if (missingPinnedAssetIds.length) {
|
|
165
|
+
const pinnedAssets = await db
|
|
166
|
+
.select()
|
|
167
|
+
.from(schema.assets)
|
|
168
|
+
.where(
|
|
169
|
+
and(
|
|
170
|
+
eq(schema.assets.libraryId, id),
|
|
171
|
+
inArray(schema.assets.id, missingPinnedAssetIds),
|
|
172
|
+
notInArray(schema.assets.status, ["archived", "failed"]),
|
|
173
|
+
),
|
|
174
|
+
);
|
|
175
|
+
assets.push(...pinnedAssets);
|
|
176
|
+
}
|
|
177
|
+
|
|
130
178
|
const collectionIds = new Map(
|
|
131
179
|
collections.map((collection) => [collection.id, nanoid()]),
|
|
132
180
|
);
|
|
@@ -23,9 +23,14 @@ import { upsertVariantSlot } from "./variant-slots.js";
|
|
|
23
23
|
|
|
24
24
|
const IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12 * 60_000;
|
|
25
25
|
|
|
26
|
+
const presetReferenceFillSchema = z.object({
|
|
27
|
+
referenceId: z.string().min(1),
|
|
28
|
+
assetIds: z.array(z.string().min(1)).min(1).max(4),
|
|
29
|
+
});
|
|
30
|
+
|
|
26
31
|
export default defineAction({
|
|
27
32
|
description:
|
|
28
|
-
"Generate several brand-consistent images in parallel from one brand kit/library. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. This is synchronous for images: one call waits for every slot and returns final image artifacts. Use this for slide decks, landing pages, and multi-slot design work. Do not call get-generation-run or refresh-generation-run after a normal image batch result.",
|
|
33
|
+
"Generate several brand-consistent images in parallel from one brand kit/library. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. If no preset is tagged, call list-generation-presets first and use a matching preset's presetId; the user may not know presets exist. Generate presetless only when no preset matches the request. This is synchronous for images: one call waits for every slot and returns final image artifacts. Use this for slide decks, landing pages, and multi-slot design work. Do not call get-generation-run or refresh-generation-run after a normal image batch result.",
|
|
29
34
|
schema: z.object({
|
|
30
35
|
libraryId: z
|
|
31
36
|
.string()
|
|
@@ -40,6 +45,13 @@ export default defineAction({
|
|
|
40
45
|
.describe(
|
|
41
46
|
"Generation preset ID (from a @preset mention or list-generation-presets). The preset already defines aspectRatio, imageSize, model, tier, and category. When you set presetId, OMIT each slot's aspectRatio/imageSize and the top-level model/tier so the preset's values are used; only pass one when the user explicitly asks for a value that differs from the preset.",
|
|
42
47
|
),
|
|
48
|
+
presetReferenceFills: z
|
|
49
|
+
.array(presetReferenceFillSchema)
|
|
50
|
+
.max(6)
|
|
51
|
+
.optional()
|
|
52
|
+
.describe(
|
|
53
|
+
'Per-run images for the preset\'s reference board (see the tagged preset brief or list-generation-presets settings.presetReferences). Each fill REPLACES the images of one variable entry by id, e.g. [{ referenceId: "guest-speaker", assetIds: ["..."] }]. Required entries without pinned images MUST be filled or the call fails. Only valid when presetId is set. Applies to every slot in the batch.',
|
|
54
|
+
),
|
|
43
55
|
sessionId: z.string().optional(),
|
|
44
56
|
slots: z
|
|
45
57
|
.array(
|
|
@@ -150,6 +162,7 @@ export default defineAction({
|
|
|
150
162
|
libraryId: base.libraryId,
|
|
151
163
|
collectionId: base.collectionId,
|
|
152
164
|
presetId: base.presetId,
|
|
165
|
+
presetReferenceFills: base.presetReferenceFills,
|
|
153
166
|
sessionId: base.sessionId,
|
|
154
167
|
prompt: slot.prompt,
|
|
155
168
|
embeddedText: slot.embeddedText,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
getRequestOrgId,
|
|
10
10
|
} from "@agent-native/core/server/request-context";
|
|
11
11
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
12
|
-
import { eq } from "drizzle-orm";
|
|
12
|
+
import { eq, inArray } from "drizzle-orm";
|
|
13
13
|
import { nanoid } from "nanoid";
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
DEFAULT_GENERATION_REFERENCE_LIMIT,
|
|
22
22
|
generateWithManagedImageProvider,
|
|
23
23
|
isImageGenerationSetupError,
|
|
24
|
+
loadReferenceData,
|
|
24
25
|
resolveImageModelForRequest,
|
|
25
26
|
selectReferences,
|
|
26
27
|
type ReferenceForGeneration,
|
|
@@ -33,6 +34,11 @@ import {
|
|
|
33
34
|
prepareGptImage2SkeletonInpaintImages,
|
|
34
35
|
} from "../server/lib/image-processing.js";
|
|
35
36
|
import { nowIso, parseJson, stringifyJson } from "../server/lib/json.js";
|
|
37
|
+
import {
|
|
38
|
+
normalizePresetReferences,
|
|
39
|
+
PRESET_REFERENCE_ROLE_MAP,
|
|
40
|
+
resolvePresetReferenceFills,
|
|
41
|
+
} from "../server/lib/preset-references.js";
|
|
36
42
|
import {
|
|
37
43
|
aspectRatioValue,
|
|
38
44
|
clampCutoutAspectRatio,
|
|
@@ -65,9 +71,14 @@ import { upsertVariantSlot, wasVariantSlotDismissed } from "./variant-slots.js";
|
|
|
65
71
|
|
|
66
72
|
const IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12 * 60_000;
|
|
67
73
|
|
|
74
|
+
const presetReferenceFillSchema = z.object({
|
|
75
|
+
referenceId: z.string().min(1),
|
|
76
|
+
assetIds: z.array(z.string().min(1)).min(1).max(4),
|
|
77
|
+
});
|
|
78
|
+
|
|
68
79
|
export default defineAction({
|
|
69
80
|
description:
|
|
70
|
-
"Generate one brand-consistent image from a brand kit/library. This is synchronous for images and returns the final asset with preview/download/embed URLs. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. Use generate-image-batch for multiple independent slots; do not poll image runs after this action returns.",
|
|
81
|
+
"Generate one brand-consistent image from a brand kit/library. This is synchronous for images and returns the final asset with preview/download/embed URLs. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. If no preset is tagged, call list-generation-presets first and use a matching preset's presetId; the user may not know presets exist. Generate presetless only when no preset matches the request. Use generate-image-batch for multiple independent slots; do not poll image runs after this action returns.",
|
|
71
82
|
schema: z.object({
|
|
72
83
|
libraryId: z
|
|
73
84
|
.string()
|
|
@@ -124,6 +135,13 @@ export default defineAction({
|
|
|
124
135
|
.describe(
|
|
125
136
|
"Exact reference assets to use. When omitted, the server deterministically chooses a small relevant subset from the latest library references.",
|
|
126
137
|
),
|
|
138
|
+
presetReferenceFills: z
|
|
139
|
+
.array(presetReferenceFillSchema)
|
|
140
|
+
.max(6)
|
|
141
|
+
.optional()
|
|
142
|
+
.describe(
|
|
143
|
+
'Per-run images for the preset\'s reference board (see the tagged preset brief or list-generation-presets settings.presetReferences). Each fill REPLACES the images of one variable entry by id, e.g. [{ referenceId: "guest-speaker", assetIds: ["..."] }]. Required entries without pinned images MUST be filled or the call fails. Only valid when presetId is set.',
|
|
144
|
+
),
|
|
127
145
|
includeLogo: z.coerce
|
|
128
146
|
.boolean()
|
|
129
147
|
.optional()
|
|
@@ -281,10 +299,27 @@ export default defineAction({
|
|
|
281
299
|
tier?: ImageQualityTier;
|
|
282
300
|
includeLogo?: boolean;
|
|
283
301
|
skeletonSpec?: unknown;
|
|
302
|
+
presetReferences?: unknown;
|
|
284
303
|
}>(preset?.settings, {});
|
|
285
304
|
const skeletonSpec = normalizePresetSkeletonSpec(
|
|
286
305
|
presetSettings.skeletonSpec,
|
|
287
306
|
);
|
|
307
|
+
const presetReferences = normalizePresetReferences(
|
|
308
|
+
presetSettings.presetReferences,
|
|
309
|
+
);
|
|
310
|
+
if (args.presetReferenceFills?.length && !preset) {
|
|
311
|
+
throw new Error(
|
|
312
|
+
"presetReferenceFills requires a presetId whose preset declares a reference board.",
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
const resolvedPresetReferences = resolvePresetReferenceFills({
|
|
316
|
+
entries: presetReferences,
|
|
317
|
+
fills: args.presetReferenceFills,
|
|
318
|
+
presetTitle: preset?.title ?? "",
|
|
319
|
+
});
|
|
320
|
+
const boardAssignments = Object.fromEntries(
|
|
321
|
+
resolvedPresetReferences.map((item) => [item.entry.id, item.assetIds]),
|
|
322
|
+
);
|
|
288
323
|
const isSkeletonCutout = skeletonSpec?.contentMode === "cutout";
|
|
289
324
|
const outputAspectRatio = (args.aspectRatio ??
|
|
290
325
|
preset?.aspectRatio ??
|
|
@@ -333,11 +368,32 @@ export default defineAction({
|
|
|
333
368
|
model: resolvedModel,
|
|
334
369
|
});
|
|
335
370
|
}
|
|
371
|
+
// Mirror the create/update-generation-preset guard for per-run model
|
|
372
|
+
// overrides: subject board photos on a model without character
|
|
373
|
+
// consistency would silently ignore the people they exist to preserve.
|
|
374
|
+
if (
|
|
375
|
+
resolvedModel === "gemini-2.5-flash-image" &&
|
|
376
|
+
resolvedPresetReferences.some((item) => item.entry.role === "subject")
|
|
377
|
+
) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"Subject reference entries need a model with character consistency. Use gemini-3.1-flash-image, gemini-3-pro-image, gpt-image-1, or gpt-image-2.",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
336
382
|
const resolvedCategories =
|
|
337
383
|
args.categories ??
|
|
338
384
|
(preset?.category ? ([preset.category] as any) : undefined);
|
|
339
385
|
const skeletonReferenceExclusionIds =
|
|
340
386
|
skeletonCompositeAssetIds(skeletonSpec);
|
|
387
|
+
const boardRefs = await loadPresetReferenceBoardRefs({
|
|
388
|
+
db,
|
|
389
|
+
libraryId: args.libraryId,
|
|
390
|
+
resolved: resolvedPresetReferences,
|
|
391
|
+
});
|
|
392
|
+
const boardAssetIdSet = new Set(boardRefs.map((ref) => ref.id));
|
|
393
|
+
const referenceExclusionIds = [
|
|
394
|
+
...(skeletonReferenceExclusionIds ?? []),
|
|
395
|
+
...boardAssetIdSet,
|
|
396
|
+
];
|
|
341
397
|
const skeletonAssets = skeletonSpec
|
|
342
398
|
? await loadSkeletonAssets({
|
|
343
399
|
spec: skeletonSpec,
|
|
@@ -395,35 +451,67 @@ export default defineAction({
|
|
|
395
451
|
.join("\n")
|
|
396
452
|
: "";
|
|
397
453
|
let references: ReferenceForGeneration[];
|
|
454
|
+
const baseReferenceLimit =
|
|
455
|
+
args.intent !== "restyle" &&
|
|
456
|
+
preset?.referencePolicy === "explicit" &&
|
|
457
|
+
!args.referenceAssetIds?.length
|
|
458
|
+
? 0
|
|
459
|
+
: DEFAULT_GENERATION_REFERENCE_LIMIT;
|
|
398
460
|
if (useSkeletonInpaint) {
|
|
399
|
-
|
|
461
|
+
const guidanceReferenceLimit = Math.max(
|
|
462
|
+
0,
|
|
463
|
+
baseReferenceLimit - boardRefs.length,
|
|
464
|
+
);
|
|
465
|
+
const guidanceReferences =
|
|
466
|
+
baseReferenceLimit > 0 || args.referenceAssetIds?.length
|
|
467
|
+
? await selectReferences({
|
|
468
|
+
libraryId: args.libraryId,
|
|
469
|
+
collectionId: resolvedCollectionId,
|
|
470
|
+
categories: resolvedCategories,
|
|
471
|
+
referenceAssetIds: args.referenceAssetIds,
|
|
472
|
+
excludeAssetIds: referenceExclusionIds,
|
|
473
|
+
sourceAssetId: args.sourceAssetId,
|
|
474
|
+
subjectAssetId: args.subjectAssetId,
|
|
475
|
+
intent: args.intent,
|
|
476
|
+
limit: guidanceReferenceLimit,
|
|
477
|
+
})
|
|
478
|
+
: [];
|
|
479
|
+
const inpaintReferences = skeletonInpaint?.references ?? [];
|
|
480
|
+
const [plateReference, ...maskReferences] = inpaintReferences;
|
|
481
|
+
references = [
|
|
482
|
+
...(plateReference ? [plateReference] : []),
|
|
483
|
+
...boardRefs.map(referenceForSkeletonInpaintGuidance),
|
|
484
|
+
...guidanceReferences.map(referenceForSkeletonInpaintGuidance),
|
|
485
|
+
...maskReferences,
|
|
486
|
+
];
|
|
400
487
|
} else {
|
|
401
488
|
const backgroundPlateReference =
|
|
402
489
|
backgroundPlateReferenceForSkeleton(skeletonAssets);
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
!args.referenceAssetIds?.length
|
|
407
|
-
? 0
|
|
408
|
-
: DEFAULT_GENERATION_REFERENCE_LIMIT;
|
|
409
|
-
const referenceLimit =
|
|
410
|
-
backgroundPlateReference && baseReferenceLimit > 0
|
|
490
|
+
const referenceLimit = Math.max(
|
|
491
|
+
0,
|
|
492
|
+
(backgroundPlateReference && baseReferenceLimit > 0
|
|
411
493
|
? baseReferenceLimit + 1
|
|
412
|
-
: baseReferenceLimit
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
494
|
+
: baseReferenceLimit) - boardRefs.length,
|
|
495
|
+
);
|
|
496
|
+
const autoReferences =
|
|
497
|
+
referenceLimit > 0 || args.referenceAssetIds?.length
|
|
498
|
+
? await selectReferences({
|
|
499
|
+
libraryId: args.libraryId,
|
|
500
|
+
collectionId: resolvedCollectionId,
|
|
501
|
+
categories: resolvedCategories,
|
|
502
|
+
referenceAssetIds: args.referenceAssetIds,
|
|
503
|
+
excludeAssetIds: referenceExclusionIds,
|
|
504
|
+
sourceAssetId: args.sourceAssetId,
|
|
505
|
+
subjectAssetId: args.subjectAssetId,
|
|
506
|
+
intent: args.intent,
|
|
507
|
+
limit: referenceLimit,
|
|
508
|
+
})
|
|
509
|
+
: [];
|
|
510
|
+
references = [
|
|
511
|
+
...(backgroundPlateReference ? [backgroundPlateReference] : []),
|
|
512
|
+
...boardRefs,
|
|
513
|
+
...autoReferences,
|
|
514
|
+
];
|
|
427
515
|
}
|
|
428
516
|
const compiledPrompt = compilePrompt({
|
|
429
517
|
libraryTitle: library.title,
|
|
@@ -448,6 +536,12 @@ export default defineAction({
|
|
|
448
536
|
category,
|
|
449
537
|
intent: args.intent,
|
|
450
538
|
styleStrength: args.styleStrength,
|
|
539
|
+
referenceBoard: resolvedPresetReferences.map((item) => ({
|
|
540
|
+
label: item.entry.label,
|
|
541
|
+
role: item.entry.role,
|
|
542
|
+
count: item.assetIds.length,
|
|
543
|
+
description: item.entry.description,
|
|
544
|
+
})),
|
|
451
545
|
});
|
|
452
546
|
const runId = nanoid();
|
|
453
547
|
const now = nowIso();
|
|
@@ -476,6 +570,7 @@ export default defineAction({
|
|
|
476
570
|
selectionReasons: Object.fromEntries(
|
|
477
571
|
references.map((ref) => [ref.id, ref.selectionReason ?? "scored"]),
|
|
478
572
|
),
|
|
573
|
+
boardAssignments,
|
|
479
574
|
};
|
|
480
575
|
const settingsUsed = {
|
|
481
576
|
model: resolvedModel,
|
|
@@ -497,6 +592,7 @@ export default defineAction({
|
|
|
497
592
|
embeddedText: args.embeddedText ?? null,
|
|
498
593
|
textPlacement: args.textPlacement ?? null,
|
|
499
594
|
customInstructions: library.customInstructions ?? "",
|
|
595
|
+
boardAssignments,
|
|
500
596
|
};
|
|
501
597
|
const dismissibleSlot = args.dismissible !== false && Boolean(slotId);
|
|
502
598
|
const baseMetadata = {
|
|
@@ -593,6 +689,7 @@ export default defineAction({
|
|
|
593
689
|
collectionId: resolvedCollectionId ?? null,
|
|
594
690
|
source: args.source,
|
|
595
691
|
callerAppId: args.callerAppId,
|
|
692
|
+
hasBoardReferences: boardRefs.length > 0,
|
|
596
693
|
}),
|
|
597
694
|
);
|
|
598
695
|
await deleteAppState("image-generation-setup").catch(() => {});
|
|
@@ -847,6 +944,62 @@ function backgroundPlateReferenceForSkeleton(
|
|
|
847
944
|
};
|
|
848
945
|
}
|
|
849
946
|
|
|
947
|
+
async function loadPresetReferenceBoardRefs(input: {
|
|
948
|
+
db: any;
|
|
949
|
+
libraryId: string;
|
|
950
|
+
resolved: Array<{
|
|
951
|
+
entry: {
|
|
952
|
+
id: string;
|
|
953
|
+
role: keyof typeof PRESET_REFERENCE_ROLE_MAP;
|
|
954
|
+
};
|
|
955
|
+
assetIds: string[];
|
|
956
|
+
}>;
|
|
957
|
+
}): Promise<ReferenceForGeneration[]> {
|
|
958
|
+
const assetIds = [
|
|
959
|
+
...new Set(input.resolved.flatMap((item) => item.assetIds)),
|
|
960
|
+
];
|
|
961
|
+
if (!assetIds.length) return [];
|
|
962
|
+
|
|
963
|
+
const rows = await input.db
|
|
964
|
+
.select()
|
|
965
|
+
.from(schema.assets)
|
|
966
|
+
.where(inArray(schema.assets.id, assetIds));
|
|
967
|
+
const byId = new Map<string, any>(
|
|
968
|
+
(rows as any[]).map((asset) => [asset.id, asset]),
|
|
969
|
+
);
|
|
970
|
+
const valid = assetIds.every((assetId) => {
|
|
971
|
+
const asset = byId.get(assetId);
|
|
972
|
+
return (
|
|
973
|
+
asset &&
|
|
974
|
+
asset.libraryId === input.libraryId &&
|
|
975
|
+
typeof asset.mimeType === "string" &&
|
|
976
|
+
asset.mimeType.startsWith("image/") &&
|
|
977
|
+
asset.status !== "archived" &&
|
|
978
|
+
asset.status !== "failed"
|
|
979
|
+
);
|
|
980
|
+
});
|
|
981
|
+
if (!valid) {
|
|
982
|
+
throw new Error(
|
|
983
|
+
"Reference board images must be images in this asset library.",
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const refs: ReferenceForGeneration[] = [];
|
|
988
|
+
for (const item of input.resolved) {
|
|
989
|
+
const assets = item.assetIds
|
|
990
|
+
.map((assetId) => byId.get(assetId))
|
|
991
|
+
.filter((asset): asset is any => asset != null);
|
|
992
|
+
refs.push(
|
|
993
|
+
...(await loadReferenceData(
|
|
994
|
+
assets,
|
|
995
|
+
() => PRESET_REFERENCE_ROLE_MAP[item.entry.role],
|
|
996
|
+
() => `preset-ref:${item.entry.id}`,
|
|
997
|
+
)),
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
return refs;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
850
1003
|
async function inpaintReferencesForSkeleton(
|
|
851
1004
|
skeletonAssets: LoadedSkeletonAssets | null,
|
|
852
1005
|
): Promise<{
|
|
@@ -897,6 +1050,22 @@ async function inpaintReferencesForSkeleton(
|
|
|
897
1050
|
};
|
|
898
1051
|
}
|
|
899
1052
|
|
|
1053
|
+
function referenceForSkeletonInpaintGuidance(
|
|
1054
|
+
ref: ReferenceForGeneration,
|
|
1055
|
+
): ReferenceForGeneration {
|
|
1056
|
+
if (
|
|
1057
|
+
ref.role !== "subject_reference" &&
|
|
1058
|
+
ref.role !== "generated" &&
|
|
1059
|
+
ref.role !== "edit_target"
|
|
1060
|
+
) {
|
|
1061
|
+
return ref;
|
|
1062
|
+
}
|
|
1063
|
+
return {
|
|
1064
|
+
...ref,
|
|
1065
|
+
role: "product_reference",
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
|
|
900
1069
|
function closestSupportedAspectRatioForSize(
|
|
901
1070
|
size: { width: number; height: number },
|
|
902
1071
|
supported: readonly AspectRatio[],
|