@agent-native/core 0.77.24 → 0.78.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.
Files changed (212) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +20 -0
  3. package/corpus/core/docs/content/template-analytics.md +5 -0
  4. package/corpus/core/docs/content/tracking.md +45 -0
  5. package/corpus/core/package.json +3 -1
  6. package/corpus/core/src/agent/types.ts +22 -0
  7. package/corpus/core/src/client/AgentPanel.tsx +34 -18
  8. package/corpus/core/src/client/AssistantChat.tsx +40 -0
  9. package/corpus/core/src/client/agent-chat.ts +173 -0
  10. package/corpus/core/src/client/analytics.ts +141 -5
  11. package/corpus/core/src/client/composer/TiptapComposer.tsx +316 -60
  12. package/corpus/core/src/client/composer/extensions/MentionReference.tsx +3 -0
  13. package/corpus/core/src/client/composer/types.ts +22 -0
  14. package/corpus/core/src/client/index.ts +14 -0
  15. package/corpus/core/src/client/session-replay.ts +800 -0
  16. package/corpus/core/src/db/schema.ts +12 -0
  17. package/corpus/core/src/deploy/build.ts +75 -0
  18. package/corpus/core/src/private-blob/index.ts +18 -0
  19. package/corpus/core/src/private-blob/registry.ts +241 -0
  20. package/corpus/core/src/private-blob/types.ts +55 -0
  21. package/corpus/core/src/scripts/utils.ts +34 -6
  22. package/corpus/core/src/server/agent-chat-plugin.ts +10 -0
  23. package/corpus/core/src/server/email.ts +5 -0
  24. package/corpus/core/src/server/index.ts +1 -0
  25. package/corpus/core/src/styles/agent-native.css +24 -0
  26. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +1 -1
  27. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +43 -0
  28. package/corpus/templates/analytics/AGENTS.md +32 -1
  29. package/corpus/templates/analytics/DEVELOPING.md +9 -1
  30. package/corpus/templates/analytics/actions/delete-dashboard-report-subscription.ts +24 -0
  31. package/corpus/templates/analytics/actions/get-session-replay-events.ts +50 -0
  32. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +27 -0
  33. package/corpus/templates/analytics/actions/list-dashboard-report-subscriptions.ts +24 -0
  34. package/corpus/templates/analytics/actions/list-session-recordings.ts +59 -0
  35. package/corpus/templates/analytics/actions/navigate.ts +15 -5
  36. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
  37. package/corpus/templates/analytics/actions/save-dashboard-report-subscription.ts +36 -0
  38. package/corpus/templates/analytics/actions/send-dashboard-report-now.ts +60 -0
  39. package/corpus/templates/analytics/actions/view-screen.ts +50 -2
  40. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +51 -2
  41. package/corpus/templates/analytics/app/components/layout/Header.tsx +4 -1
  42. package/corpus/templates/analytics/app/components/layout/Layout.tsx +14 -2
  43. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +6 -6
  44. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +35 -6
  45. package/corpus/templates/analytics/app/i18n-data.ts +1119 -0
  46. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +437 -0
  47. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -12
  48. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +43 -0
  49. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +136 -43
  50. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-filters.ts +26 -0
  51. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +450 -0
  52. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +419 -0
  53. package/corpus/templates/analytics/app/routes/overview.tsx +13 -6
  54. package/corpus/templates/analytics/app/routes/sessions.$recordingId.tsx +10 -0
  55. package/corpus/templates/analytics/app/routes/sessions._index.tsx +10 -0
  56. package/corpus/templates/analytics/changelog/2026-06-25-analytics-now-opens-directly-to-ask-instead-of-the-old-overv.md +6 -0
  57. package/corpus/templates/analytics/changelog/2026-06-25-dashboard-charts-now-show-reliable-blue-placement-lines-whil.md +6 -0
  58. package/corpus/templates/analytics/changelog/2026-06-26-dashboards-can-now-send-scheduled-daily-email-reports-from-t.md +6 -0
  59. package/corpus/templates/analytics/changelog/2026-06-26-session-replay-browser-sessions-can-now-be-reviewed-from.md +6 -0
  60. package/corpus/templates/analytics/package.json +5 -2
  61. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +131 -0
  62. package/corpus/templates/analytics/scripts/seed-session-replay.ts +198 -0
  63. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +140 -27
  64. package/corpus/templates/analytics/server/db/index.ts +12 -0
  65. package/corpus/templates/analytics/server/db/schema.ts +131 -0
  66. package/corpus/templates/analytics/server/handlers/session-replay.ts +281 -0
  67. package/corpus/templates/analytics/server/handlers/sql-query.ts +8 -348
  68. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +66 -0
  69. package/corpus/templates/analytics/server/jobs/session-replay-retention.ts +29 -0
  70. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +365 -0
  71. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +473 -0
  72. package/corpus/templates/analytics/server/lib/dashboard-report.ts +405 -0
  73. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +63 -18
  74. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +98 -26
  75. package/corpus/templates/analytics/server/lib/session-replay.ts +1645 -0
  76. package/corpus/templates/analytics/server/plugins/auth.ts +1 -1
  77. package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +47 -0
  78. package/corpus/templates/analytics/server/plugins/db.ts +200 -0
  79. package/corpus/templates/analytics/server/plugins/session-replay-retention-jobs.ts +41 -0
  80. package/corpus/templates/analytics/server/routes/api/analytics/replay.options.ts +1 -0
  81. package/corpus/templates/analytics/server/routes/api/analytics/replay.post.ts +1 -0
  82. package/corpus/templates/analytics/server/routes/api/dashboard-reports/run.post.ts +61 -0
  83. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks/[seq].get.ts +1 -0
  84. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/events.get.ts +1 -0
  85. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/manifest.get.ts +1 -0
  86. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId].get.ts +1 -0
  87. package/corpus/templates/analytics/server/routes/api/session-replay/recordings.get.ts +1 -0
  88. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +10 -2
  89. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +14 -7
  90. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +12 -0
  91. package/corpus/templates/assets/AGENTS.md +18 -3
  92. package/corpus/templates/assets/actions/_image-model-default.ts +24 -0
  93. package/corpus/templates/assets/actions/dismiss-variant-slots.ts +15 -22
  94. package/corpus/templates/assets/actions/generate-asset.ts +30 -23
  95. package/corpus/templates/assets/actions/generate-image-batch.ts +76 -29
  96. package/corpus/templates/assets/actions/generate-image.ts +46 -30
  97. package/corpus/templates/assets/actions/generate-video.ts +18 -3
  98. package/corpus/templates/assets/actions/list-assets.ts +39 -8
  99. package/corpus/templates/assets/actions/list-libraries.ts +24 -2
  100. package/corpus/templates/assets/actions/navigate.ts +1 -1
  101. package/corpus/templates/assets/actions/open-asset-picker.ts +38 -1
  102. package/corpus/templates/assets/actions/refresh-generation-run.ts +10 -0
  103. package/corpus/templates/assets/actions/save-generated-image.ts +21 -19
  104. package/corpus/templates/assets/actions/variant-slots.ts +131 -28
  105. package/corpus/templates/assets/actions/view-screen.ts +9 -0
  106. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +425 -0
  107. package/corpus/templates/assets/app/components/layout/Header.tsx +3 -1
  108. package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
  109. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -6
  110. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +47 -21
  111. package/corpus/templates/assets/app/i18n-data.ts +1815 -0
  112. package/corpus/templates/assets/app/lib/libraries.ts +6 -0
  113. package/corpus/templates/assets/app/lib/picker-chat-handoff.ts +71 -0
  114. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  115. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
  116. package/corpus/templates/assets/app/routes/audit.tsx +1 -1
  117. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1557 -2164
  118. package/corpus/templates/assets/app/routes/brand-kits._index.tsx +7 -191
  119. package/corpus/templates/assets/app/routes/brand-kits.tsx +1 -5
  120. package/corpus/templates/assets/app/routes/libraries.tsx +4 -4
  121. package/corpus/templates/assets/app/routes/library.$id.tsx +9 -7
  122. package/corpus/templates/assets/app/routes/library.tsx +1454 -175
  123. package/corpus/templates/assets/server/db/index.ts +1 -1
  124. package/corpus/templates/assets/server/lib/generation.ts +86 -15
  125. package/corpus/templates/assets/server/plugins/agent-chat.ts +196 -0
  126. package/corpus/templates/assets/server/plugins/db.ts +5 -0
  127. package/corpus/templates/assets/shared/api.ts +6 -3
  128. package/dist/agent/types.d.ts +21 -0
  129. package/dist/agent/types.d.ts.map +1 -1
  130. package/dist/agent/types.js.map +1 -1
  131. package/dist/client/AgentPanel.d.ts +6 -2
  132. package/dist/client/AgentPanel.d.ts.map +1 -1
  133. package/dist/client/AgentPanel.js +10 -4
  134. package/dist/client/AgentPanel.js.map +1 -1
  135. package/dist/client/AssistantChat.d.ts +6 -0
  136. package/dist/client/AssistantChat.d.ts.map +1 -1
  137. package/dist/client/AssistantChat.js +15 -3
  138. package/dist/client/AssistantChat.js.map +1 -1
  139. package/dist/client/agent-chat.d.ts +32 -0
  140. package/dist/client/agent-chat.d.ts.map +1 -1
  141. package/dist/client/agent-chat.js +113 -0
  142. package/dist/client/agent-chat.js.map +1 -1
  143. package/dist/client/analytics.d.ts +22 -3
  144. package/dist/client/analytics.d.ts.map +1 -1
  145. package/dist/client/analytics.js +96 -4
  146. package/dist/client/analytics.js.map +1 -1
  147. package/dist/client/composer/TiptapComposer.d.ts +2 -1
  148. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  149. package/dist/client/composer/TiptapComposer.js +228 -56
  150. package/dist/client/composer/TiptapComposer.js.map +1 -1
  151. package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -1
  152. package/dist/client/composer/extensions/MentionReference.js +3 -0
  153. package/dist/client/composer/extensions/MentionReference.js.map +1 -1
  154. package/dist/client/composer/types.d.ts +21 -0
  155. package/dist/client/composer/types.d.ts.map +1 -1
  156. package/dist/client/composer/types.js.map +1 -1
  157. package/dist/client/index.d.ts +2 -2
  158. package/dist/client/index.d.ts.map +1 -1
  159. package/dist/client/index.js +2 -2
  160. package/dist/client/index.js.map +1 -1
  161. package/dist/client/session-replay.d.ts +41 -0
  162. package/dist/client/session-replay.d.ts.map +1 -0
  163. package/dist/client/session-replay.js +595 -0
  164. package/dist/client/session-replay.js.map +1 -0
  165. package/dist/collab/awareness.d.ts +2 -2
  166. package/dist/collab/awareness.d.ts.map +1 -1
  167. package/dist/collab/routes.d.ts +2 -2
  168. package/dist/db/schema.d.ts +3 -1
  169. package/dist/db/schema.d.ts.map +1 -1
  170. package/dist/db/schema.js +6 -2
  171. package/dist/db/schema.js.map +1 -1
  172. package/dist/deploy/build.d.ts +8 -0
  173. package/dist/deploy/build.d.ts.map +1 -1
  174. package/dist/deploy/build.js +66 -0
  175. package/dist/deploy/build.js.map +1 -1
  176. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  177. package/dist/notifications/routes.d.ts +3 -3
  178. package/dist/observability/routes.d.ts +8 -8
  179. package/dist/private-blob/index.d.ts +3 -0
  180. package/dist/private-blob/index.d.ts.map +1 -0
  181. package/dist/private-blob/index.js +2 -0
  182. package/dist/private-blob/index.js.map +1 -0
  183. package/dist/private-blob/registry.d.ts +10 -0
  184. package/dist/private-blob/registry.d.ts.map +1 -0
  185. package/dist/private-blob/registry.js +152 -0
  186. package/dist/private-blob/registry.js.map +1 -0
  187. package/dist/private-blob/types.d.ts +51 -0
  188. package/dist/private-blob/types.d.ts.map +1 -0
  189. package/dist/private-blob/types.js +2 -0
  190. package/dist/private-blob/types.js.map +1 -0
  191. package/dist/resources/handlers.d.ts +3 -3
  192. package/dist/scripts/utils.d.ts +4 -4
  193. package/dist/scripts/utils.d.ts.map +1 -1
  194. package/dist/scripts/utils.js +30 -6
  195. package/dist/scripts/utils.js.map +1 -1
  196. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  197. package/dist/server/agent-chat-plugin.js +5 -0
  198. package/dist/server/agent-chat-plugin.js.map +1 -1
  199. package/dist/server/agent-engine-api-key-route.d.ts +2 -2
  200. package/dist/server/email.d.ts +2 -0
  201. package/dist/server/email.d.ts.map +1 -1
  202. package/dist/server/email.js +3 -0
  203. package/dist/server/email.js.map +1 -1
  204. package/dist/server/index.d.ts +1 -1
  205. package/dist/server/index.d.ts.map +1 -1
  206. package/dist/server/index.js.map +1 -1
  207. package/dist/server/transcribe-voice.d.ts +1 -1
  208. package/dist/styles/agent-native.css +24 -0
  209. package/docs/content/template-analytics.md +5 -0
  210. package/docs/content/tracking.md +45 -0
  211. package/package.json +3 -1
  212. package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +0 -209
@@ -1,4 +1,5 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
+ import type { ActionRunContext } from "@agent-native/core/action";
2
3
  import { z } from "zod";
3
4
 
4
5
  import {
@@ -116,16 +117,19 @@ const action = defineAction({
116
117
  view: "picker",
117
118
  };
118
119
  },
119
- run: async (args) => {
120
+ run: async (args, context?: ActionRunContext) => {
120
121
  if (args.mediaType === "video") {
121
- const picker = (await openAssetPicker.run({
122
- mediaType: "video",
123
- prompt: args.prompt,
124
- libraryId: args.libraryId,
125
- libraryHint: args.libraryHint,
126
- count: args.count,
127
- callerAppId: args.callerAppId,
128
- })) as Record<string, unknown>;
122
+ const picker = (await openAssetPicker.run(
123
+ {
124
+ mediaType: "video",
125
+ prompt: args.prompt,
126
+ libraryId: args.libraryId,
127
+ libraryHint: args.libraryHint,
128
+ count: args.count,
129
+ callerAppId: args.callerAppId,
130
+ },
131
+ context,
132
+ )) as Record<string, unknown>;
129
133
  return {
130
134
  ...picker,
131
135
  generated: false,
@@ -134,20 +138,23 @@ const action = defineAction({
134
138
  };
135
139
  }
136
140
 
137
- const picker = (await openAssetPicker.run({
138
- mediaType: "image",
139
- prompt: args.prompt,
140
- libraryId: args.libraryId,
141
- libraryHint: args.libraryHint,
142
- aspectRatio: args.aspectRatio,
143
- presetId: args.presetId,
144
- count: args.count,
145
- autoGenerate: true,
146
- tier: args.tier,
147
- styleStrength: args.styleStrength,
148
- includeLogo: args.includeLogo,
149
- callerAppId: args.callerAppId,
150
- })) as Record<string, unknown>;
141
+ const picker = (await openAssetPicker.run(
142
+ {
143
+ mediaType: "image",
144
+ prompt: args.prompt,
145
+ libraryId: args.libraryId,
146
+ libraryHint: args.libraryHint,
147
+ aspectRatio: args.aspectRatio,
148
+ presetId: args.presetId,
149
+ count: args.count,
150
+ autoGenerate: true,
151
+ tier: args.tier,
152
+ styleStrength: args.styleStrength,
153
+ includeLogo: args.includeLogo,
154
+ callerAppId: args.callerAppId,
155
+ },
156
+ context,
157
+ )) as Record<string, unknown>;
151
158
  return {
152
159
  ...picker,
153
160
  generated: false,
@@ -1,4 +1,5 @@
1
1
  import { defineAction } from "@agent-native/core";
2
+ import type { ActionRunContext } from "@agent-native/core/action";
2
3
  import { assertAccess } from "@agent-native/core/sharing";
3
4
  import { eq } from "drizzle-orm";
4
5
  import { nanoid } from "nanoid";
@@ -17,13 +18,20 @@ import {
17
18
  STYLE_STRENGTHS,
18
19
  } from "../shared/api.js";
19
20
  import { requireGenerationSessionInLibrary } from "./_helpers.js";
21
+ import { readImageModelDefault } from "./_image-model-default.js";
20
22
  import generateImage from "./generate-image.js";
23
+ import { upsertVariantSlot } from "./variant-slots.js";
21
24
 
22
25
  export default defineAction({
23
26
  description:
24
- "Generate several brand-consistent images in parallel from one library. 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.",
27
+ "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.",
25
28
  schema: z.object({
26
- libraryId: z.string(),
29
+ libraryId: z
30
+ .string()
31
+ .optional()
32
+ .describe(
33
+ "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
34
+ ),
27
35
  collectionId: z.string().optional(),
28
36
  presetId: z.string().optional(),
29
37
  sessionId: z.string().optional(),
@@ -45,6 +53,12 @@ export default defineAction({
45
53
  )
46
54
  .min(1)
47
55
  .max(12),
56
+ variantScopeId: z
57
+ .string()
58
+ .optional()
59
+ .describe(
60
+ "Internal UI state scope for live candidate slots. Usually omitted; embedded picker UIs pass a browser-tab scope.",
61
+ ),
48
62
  model: z.enum(IMAGE_MODELS).optional(),
49
63
  tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
50
64
  intent: z.enum(GENERATION_INTENTS).default("generate"),
@@ -60,41 +74,74 @@ export default defineAction({
60
74
  ),
61
75
  }),
62
76
  parallelSafe: true,
63
- run: async ({ slots, ...base }) => {
77
+ run: async ({ slots, ...inputBase }, context?: ActionRunContext) => {
78
+ const imageModelDefault = await readImageModelDefault();
79
+ const libraryId = inputBase.libraryId;
80
+ if (!libraryId) {
81
+ throw new Error(
82
+ "No brand kit selected. Tag a brand kit with @ or pass libraryId.",
83
+ );
84
+ }
85
+ const base = {
86
+ ...inputBase,
87
+ libraryId,
88
+ model: inputBase.model ?? imageModelDefault,
89
+ };
64
90
  await assertAccess("asset-library", base.libraryId, "editor");
65
91
  if (base.sessionId) {
66
92
  await requireGenerationSessionInLibrary(base.sessionId, base.libraryId);
67
93
  }
68
- const limit = pLimit(4);
69
94
  const variantBatchId = nanoid();
95
+ await Promise.all(
96
+ slots.map((slot, index) =>
97
+ upsertVariantSlot({
98
+ runId: `pending-${variantBatchId}-${index + 1}`,
99
+ batchId: variantBatchId,
100
+ libraryId: base.libraryId,
101
+ collectionId: base.collectionId ?? null,
102
+ presetId: base.presetId ?? null,
103
+ sessionId: base.sessionId ?? null,
104
+ threadId: context?.threadId ?? null,
105
+ variantScopeId: base.variantScopeId ?? null,
106
+ prompt: slot.prompt,
107
+ slotId: slot.slotId,
108
+ status: "pending",
109
+ }),
110
+ ),
111
+ );
112
+ const limit = pLimit(4);
70
113
  const results = await Promise.allSettled(
71
114
  slots.map((slot) =>
72
115
  limit(() =>
73
- generateImage.run({
74
- libraryId: base.libraryId,
75
- collectionId: base.collectionId,
76
- presetId: base.presetId,
77
- sessionId: base.sessionId,
78
- prompt: slot.prompt,
79
- aspectRatio: slot.aspectRatio,
80
- imageSize: slot.imageSize,
81
- model: base.model,
82
- tier: base.tier,
83
- intent: slot.intent ?? base.intent,
84
- styleStrength: slot.styleStrength ?? base.styleStrength,
85
- categories: slot.categories,
86
- referenceAssetIds: slot.referenceAssetIds,
87
- includeLogo: base.includeLogo,
88
- groundingMode: base.groundingMode,
89
- slotId: slot.slotId,
90
- variantBatchId,
91
- dismissible: slot.dismissible,
92
- sourceAssetId: slot.sourceAssetId,
93
- subjectAssetId: slot.subjectAssetId,
94
- source: base.source,
95
- callerAppId: base.callerAppId,
96
- activateSessionAsset: false,
97
- }),
116
+ generateImage.run(
117
+ {
118
+ libraryId: base.libraryId,
119
+ collectionId: base.collectionId,
120
+ presetId: base.presetId,
121
+ sessionId: base.sessionId,
122
+ prompt: slot.prompt,
123
+ aspectRatio: slot.aspectRatio,
124
+ imageSize: slot.imageSize,
125
+ model: base.model,
126
+ tier: base.tier,
127
+ intent: slot.intent ?? base.intent,
128
+ styleStrength: slot.styleStrength ?? base.styleStrength,
129
+ categories: slot.categories,
130
+ referenceAssetIds: slot.referenceAssetIds,
131
+ includeLogo: base.includeLogo,
132
+ groundingMode: base.groundingMode,
133
+ slotId: slot.slotId,
134
+ variantBatchId,
135
+ variantScopeId: base.variantScopeId,
136
+ dismissible: slot.dismissible,
137
+ sourceAssetId: slot.sourceAssetId,
138
+ subjectAssetId: slot.subjectAssetId,
139
+ source: base.source,
140
+ callerAppId: base.callerAppId,
141
+ activateSessionAsset: false,
142
+ },
143
+ context,
144
+ ),
98
145
  ),
99
146
  ),
100
147
  );
@@ -1,6 +1,6 @@
1
1
  import { defineAction } from "@agent-native/core";
2
+ import type { ActionRunContext } from "@agent-native/core/action";
2
3
  import {
3
- readAppState,
4
4
  writeAppState,
5
5
  deleteAppState,
6
6
  } from "@agent-native/core/application-state";
@@ -43,6 +43,7 @@ import {
43
43
  requireGenerationSessionInLibrary,
44
44
  serializeAsset,
45
45
  } from "./_helpers.js";
46
+ import { readImageModelDefault } from "./_image-model-default.js";
46
47
  import { upsertVariantSlot, wasVariantSlotDismissed } from "./variant-slots.js";
47
48
 
48
49
  function resolveModelForTier(
@@ -57,33 +58,16 @@ function resolveModelForTier(
57
58
  : "gemini-3.1-flash-image";
58
59
  }
59
60
 
60
- /**
61
- * The user's default image model, chosen from the composer's model picker and
62
- * persisted in per-user application state. Used as a fallback when no explicit
63
- * model, tier, or preset model is supplied. Returns undefined when unset or
64
- * invalid so the hardcoded default still applies.
65
- */
66
- async function readUserDefaultImageModel(): Promise<ImageModel | undefined> {
67
- try {
68
- const stored = await readAppState("imageGenerationModel");
69
- const model = stored?.model;
70
- if (
71
- typeof model === "string" &&
72
- (IMAGE_MODELS as readonly string[]).includes(model)
73
- ) {
74
- return model as ImageModel;
75
- }
76
- } catch {
77
- // No request context or read failure — fall back to defaults below.
78
- }
79
- return undefined;
80
- }
81
-
82
61
  export default defineAction({
83
62
  description:
84
- "Generate one brand-consistent image from a library. This is synchronous for images and returns the final asset with preview/download/embed URLs. Use generate-image-batch for multiple independent slots; do not poll image runs after this action returns.",
63
+ "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.",
85
64
  schema: z.object({
86
- libraryId: z.string(),
65
+ libraryId: z
66
+ .string()
67
+ .optional()
68
+ .describe(
69
+ "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
70
+ ),
87
71
  collectionId: z.string().optional(),
88
72
  presetId: z.string().optional(),
89
73
  sessionId: z.string().optional(),
@@ -104,6 +88,12 @@ export default defineAction({
104
88
  includeLogo: z.coerce.boolean().default(false),
105
89
  slotId: z.string().optional(),
106
90
  variantBatchId: z.string().optional(),
91
+ variantScopeId: z
92
+ .string()
93
+ .optional()
94
+ .describe(
95
+ "Internal UI state scope for live candidate slots. Usually omitted; embedded picker UIs pass a browser-tab scope.",
96
+ ),
107
97
  dismissible: z.coerce
108
98
  .boolean()
109
99
  .default(true)
@@ -135,7 +125,18 @@ export default defineAction({
135
125
  ),
136
126
  }),
137
127
  parallelSafe: true,
138
- run: async (args) => {
128
+ run: async (input, context?: ActionRunContext) => {
129
+ const imageModelDefault = await readImageModelDefault();
130
+ const libraryId = input.libraryId;
131
+ if (!libraryId) {
132
+ throw new Error(
133
+ "No brand kit selected. Tag a brand kit with @ or pass libraryId.",
134
+ );
135
+ }
136
+ const args = {
137
+ ...input,
138
+ libraryId,
139
+ };
139
140
  await assertAccess("asset-library", args.libraryId, "editor");
140
141
  const db = getDb();
141
142
  const [library] = await db
@@ -248,9 +249,9 @@ export default defineAction({
248
249
  preset?.category ??
249
250
  collection?.category) as ImageCategory | undefined;
250
251
  const resolvedModel = (args.model ??
252
+ imageModelDefault ??
251
253
  resolveModelForTier(resolvedTier, category) ??
252
254
  preset?.model ??
253
- (await readUserDefaultImageModel()) ??
254
255
  "gemini-3.1-flash-image") as (typeof IMAGE_MODELS)[number];
255
256
  const resolvedCategories =
256
257
  args.categories ??
@@ -303,6 +304,8 @@ export default defineAction({
303
304
  });
304
305
  const runId = nanoid();
305
306
  const now = nowIso();
307
+ const slotId = args.slotId ?? runId;
308
+ const variantScopeId = args.variantScopeId ?? context?.threadId ?? null;
306
309
  // Capture identity at insert time so the org-admin audit log can filter
307
310
  // by owner / org without re-resolving who triggered the run later.
308
311
  const ownerEmail = getRequestUserEmail() ?? null;
@@ -342,11 +345,12 @@ export default defineAction({
342
345
  sessionId: session?.id ?? null,
343
346
  customInstructions: library.customInstructions ?? "",
344
347
  };
345
- const slotId = args.slotId ?? runId;
346
348
  const dismissibleSlot = args.dismissible !== false && Boolean(slotId);
347
349
  const baseMetadata = {
348
350
  slotId,
349
351
  variantBatchId: args.variantBatchId ?? null,
352
+ threadId: context?.threadId ?? null,
353
+ variantScopeId,
350
354
  dismissible: dismissibleSlot,
351
355
  sourceAssetId: args.sourceAssetId,
352
356
  subjectAssetId: args.subjectAssetId,
@@ -389,6 +393,8 @@ export default defineAction({
389
393
  collectionId: resolvedCollectionId ?? null,
390
394
  presetId: preset?.id ?? null,
391
395
  sessionId: session?.id ?? null,
396
+ threadId: context?.threadId ?? null,
397
+ variantScopeId,
392
398
  prompt: args.prompt,
393
399
  slotId,
394
400
  status: "pending",
@@ -430,7 +436,10 @@ export default defineAction({
430
436
  }
431
437
  if (
432
438
  dismissibleSlot &&
433
- (await wasVariantSlotDismissed(args.libraryId, slotId))
439
+ (await wasVariantSlotDismissed(args.libraryId, slotId, {
440
+ threadId: context?.threadId ?? null,
441
+ variantScopeId,
442
+ }))
434
443
  ) {
435
444
  await db
436
445
  .update(schema.assetGenerationRuns)
@@ -539,6 +548,8 @@ export default defineAction({
539
548
  collectionId: resolvedCollectionId ?? null,
540
549
  presetId: preset?.id ?? null,
541
550
  sessionId: session?.id ?? null,
551
+ threadId: context?.threadId ?? null,
552
+ variantScopeId,
542
553
  prompt: args.prompt,
543
554
  slotId,
544
555
  status: "ready",
@@ -570,7 +581,10 @@ export default defineAction({
570
581
  .where(eq(schema.assetGenerationRuns.id, runId));
571
582
  if (
572
583
  dismissibleSlot &&
573
- (await wasVariantSlotDismissed(args.libraryId, slotId))
584
+ (await wasVariantSlotDismissed(args.libraryId, slotId, {
585
+ threadId: context?.threadId ?? null,
586
+ variantScopeId,
587
+ }))
574
588
  ) {
575
589
  throw err;
576
590
  }
@@ -581,6 +595,8 @@ export default defineAction({
581
595
  collectionId: resolvedCollectionId ?? null,
582
596
  presetId: preset?.id ?? null,
583
597
  sessionId: session?.id ?? null,
598
+ threadId: context?.threadId ?? null,
599
+ variantScopeId,
584
600
  prompt: args.prompt,
585
601
  slotId,
586
602
  status: "failed",
@@ -32,9 +32,14 @@ import { serializeAsset, serializeGenerationRun } from "./_helpers.js";
32
32
 
33
33
  export default defineAction({
34
34
  description:
35
- "Start an async Veo video generation run from an asset library. Poll the returned run with refresh-generation-run until it completes and creates a video asset.",
35
+ "Start an async Veo video generation run from a brand kit/library. Use a media-type @mention with refId video to choose this instead of image generation, and use a brand-kit @mention as libraryId. Poll the returned run with refresh-generation-run until it completes and creates a video asset.",
36
36
  schema: z.object({
37
- libraryId: z.string(),
37
+ libraryId: z
38
+ .string()
39
+ .optional()
40
+ .describe(
41
+ "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
42
+ ),
38
43
  folderId: z.string().min(1).nullable().optional(),
39
44
  collectionId: z.string().optional(),
40
45
  prompt: z.string().min(1),
@@ -66,7 +71,17 @@ export default defineAction({
66
71
  callerAppId: z.string().optional(),
67
72
  waitForCompletion: z.coerce.boolean().default(false),
68
73
  }),
69
- run: async (args) => {
74
+ run: async (input) => {
75
+ const libraryId = input.libraryId;
76
+ if (!libraryId) {
77
+ throw new Error(
78
+ "No brand kit selected. Tag a brand kit with @ or pass libraryId.",
79
+ );
80
+ }
81
+ const args = {
82
+ ...input,
83
+ libraryId,
84
+ };
70
85
  await assertAccess("asset-library", args.libraryId, "editor");
71
86
  const db = getDb();
72
87
  const [library] = await db
@@ -1,5 +1,6 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { and, desc, eq, isNull } from "drizzle-orm";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { and, desc, eq, inArray, isNull } from "drizzle-orm";
3
4
  import { z } from "zod";
4
5
 
5
6
  import { getDb, schema } from "../server/db/index.js";
@@ -17,9 +18,9 @@ import {
17
18
 
18
19
  export default defineAction({
19
20
  description:
20
- "List DAM assets in a library, optionally filtered by folder, collection, media type, status, role, category, or text query.",
21
+ "List DAM assets in one library, or across all accessible libraries when libraryId is omitted. Optionally filter by folder, collection, media type, status, role, category, or text query.",
21
22
  schema: z.object({
22
- libraryId: z.string(),
23
+ libraryId: z.string().optional(),
23
24
  collectionId: z.string().optional(),
24
25
  folderId: z.string().nullable().optional(),
25
26
  mediaType: z.enum(ASSET_MEDIA_TYPES).optional(),
@@ -51,8 +52,32 @@ export default defineAction({
51
52
  includeCandidates,
52
53
  candidateRunIds,
53
54
  }) => {
54
- await requireLibrary(libraryId);
55
- const filters = [eq(schema.assets.libraryId, libraryId)];
55
+ const db = getDb();
56
+ const libraryRows = libraryId
57
+ ? [await requireLibrary(libraryId)]
58
+ : await db
59
+ .select({
60
+ id: schema.assetLibraries.id,
61
+ title: schema.assetLibraries.title,
62
+ })
63
+ .from(schema.assetLibraries)
64
+ .where(
65
+ and(
66
+ accessFilter(schema.assetLibraries, schema.assetLibraryShares),
67
+ isNull(schema.assetLibraries.archivedAt),
68
+ ),
69
+ );
70
+ const libraryIds = libraryRows.map((library) => library.id);
71
+ if (!libraryIds.length) return { count: 0, assets: [] };
72
+
73
+ const libraryTitleById = new Map(
74
+ libraryRows.map((library) => [library.id, library.title]),
75
+ );
76
+ const filters = [
77
+ libraryId
78
+ ? eq(schema.assets.libraryId, libraryId)
79
+ : inArray(schema.assets.libraryId, libraryIds),
80
+ ];
56
81
  if (collectionId)
57
82
  filters.push(eq(schema.assets.collectionId, collectionId));
58
83
  if (folderId !== undefined) {
@@ -67,7 +92,6 @@ export default defineAction({
67
92
  if (role) filters.push(eq(schema.assets.role, role));
68
93
  const normalizedQuery = query?.trim().toLowerCase();
69
94
  const candidateRunIdSet = new Set(candidateRunIds ?? []);
70
- const db = getDb();
71
95
  const [rows, lineageRows] = await Promise.all([
72
96
  db
73
97
  .select()
@@ -89,7 +113,11 @@ export default defineAction({
89
113
  createdAt: schema.assets.createdAt,
90
114
  })
91
115
  .from(schema.assets)
92
- .where(eq(schema.assets.libraryId, libraryId)),
116
+ .where(
117
+ libraryId
118
+ ? eq(schema.assets.libraryId, libraryId)
119
+ : inArray(schema.assets.libraryId, libraryIds),
120
+ ),
93
121
  ]);
94
122
  const lineageById = buildAssetLineage(lineageRows);
95
123
  const assets = rows
@@ -111,7 +139,10 @@ export default defineAction({
111
139
  );
112
140
  })
113
141
  .filter((asset) => assetMatchesSearch(asset, normalizedQuery, category))
114
- .map((asset) => serializeAsset(asset, lineageById.get(asset.id) ?? null));
142
+ .map((asset) => ({
143
+ ...serializeAsset(asset, lineageById.get(asset.id) ?? null),
144
+ libraryTitle: libraryTitleById.get(asset.libraryId) ?? null,
145
+ }));
115
146
  return { count: assets.length, assets };
116
147
  },
117
148
  });
@@ -67,10 +67,11 @@ export default defineAction({
67
67
  "List asset libraries accessible to the current user, including counts and preview thumbnails.",
68
68
  schema: z.object({
69
69
  compact: z.coerce.boolean().optional(),
70
+ includeFolders: z.coerce.boolean().optional(),
70
71
  }),
71
72
  http: { method: "GET" },
72
73
  readOnly: true,
73
- run: async ({ compact }) => {
74
+ run: async ({ compact, includeFolders }) => {
74
75
  const db = getDb();
75
76
  const rows = await db
76
77
  .select()
@@ -93,8 +94,23 @@ export default defineAction({
93
94
  ),
94
95
  )
95
96
  : [];
97
+ const folders =
98
+ includeFolders && rows.length
99
+ ? await db
100
+ .select()
101
+ .from(schema.assetFolders)
102
+ .where(
103
+ inArray(
104
+ schema.assetFolders.libraryId,
105
+ rows.map((row) => row.id),
106
+ ),
107
+ )
108
+ : [];
96
109
  const libraries = rows.map((row) => {
97
110
  const libAssets = assets.filter((asset) => asset.libraryId === row.id);
111
+ const libFolders = includeFolders
112
+ ? folders.filter((folder) => folder.libraryId === row.id)
113
+ : undefined;
98
114
  const visibleAssets = libAssets.filter((asset) =>
99
115
  shouldIncludeAssetInLibraryResults(asset),
100
116
  );
@@ -106,7 +122,12 @@ export default defineAction({
106
122
  const previewAssets = uniqueAssets([cover, ...imageAssets]).slice(0, 4);
107
123
  const base = serializeLibrary(row);
108
124
  return compact
109
- ? { id: base.id, title: base.title, description: base.description }
125
+ ? {
126
+ id: base.id,
127
+ title: base.title,
128
+ description: base.description,
129
+ ...(includeFolders ? { folders: libFolders } : {}),
130
+ }
110
131
  : {
111
132
  ...base,
112
133
  referenceCount: visibleAssets.filter((asset) =>
@@ -122,6 +143,7 @@ export default defineAction({
122
143
  ).length,
123
144
  coverAsset: cover ? serializeAsset(cover) : null,
124
145
  previewAssets: previewAssets.map(serializeAsset),
146
+ ...(includeFolders ? { folders: libFolders } : {}),
125
147
  };
126
148
  });
127
149
  return { count: libraries.length, libraries };
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
 
5
5
  export default defineAction({
6
6
  description:
7
- "Navigate the Assets UI. Views (internal keys, with the surface they open): create, picker (the image Library browser), libraries (the Brand Kits list), library (a single Brand Kit), asset, generation-session, generation-run, extensions, audit, settings. Use threadId to open a specific create/chat thread; use libraryId, assetId, sessionId, runId, or extensionId where appropriate.",
7
+ "Navigate the Assets UI. Views (internal keys, with the surface they open): create, picker (the embedded image Library picker), libraries (the unified Library workspace), library (a single Brand Kit inside Library), asset, generation-session, generation-run, extensions, audit, settings. Use threadId to open a specific create/chat thread; use libraryId, assetId, sessionId, runId, or extensionId where appropriate.",
8
8
  schema: z.object({
9
9
  view: z
10
10
  .enum([
@@ -1,4 +1,7 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
+ import type { ActionRunContext } from "@agent-native/core/action";
3
+ import { writeAppState } from "@agent-native/core/application-state";
4
+ import { getRequestRunContext } from "@agent-native/core/server/request-context";
2
5
  import { z } from "zod";
3
6
 
4
7
  import { IMAGE_QUALITY_TIERS, STYLE_STRENGTHS } from "../shared/api.js";
@@ -77,6 +80,7 @@ const schema = z.object({
77
80
  });
78
81
 
79
82
  type OpenAssetPickerArgs = z.infer<typeof schema>;
83
+ const SAFE_BROWSER_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;
80
84
 
81
85
  type ActionWithToolParameters = {
82
86
  tool: { parameters?: { properties?: Record<string, any> } };
@@ -109,6 +113,7 @@ const FALLBACK_INSTRUCTIONS =
109
113
 
110
114
  function pickerPath(args: Partial<OpenAssetPickerArgs>): string {
111
115
  const params = new URLSearchParams();
116
+ params.set("__an_picker", "1");
112
117
  params.set("mediaType", args.mediaType ?? "image");
113
118
  if (args.prompt?.trim()) params.set("prompt", args.prompt.trim());
114
119
  if (args.query?.trim()) params.set("q", args.query.trim());
@@ -134,6 +139,21 @@ function pickerPath(args: Partial<OpenAssetPickerArgs>): string {
134
139
  return `/library?${params.toString()}`;
135
140
  }
136
141
 
142
+ function navigateCommandKey(): string | null {
143
+ const browserTabId = getRequestRunContext()?.browserTabId?.trim();
144
+ if (browserTabId && SAFE_BROWSER_TAB_ID_RE.test(browserTabId)) {
145
+ return `navigate:${browserTabId}`;
146
+ }
147
+ return null;
148
+ }
149
+
150
+ function shouldWriteNavigateCommand(
151
+ context: ActionRunContext | undefined,
152
+ commandKey: string | null,
153
+ ): commandKey is string {
154
+ return Boolean(commandKey) && context?.caller !== "mcp";
155
+ }
156
+
137
157
  const action = defineAction({
138
158
  description:
139
159
  'Open the image Library picker inline so a person can browse, search, generate, and select an image or video asset. When the user asks to create a specific image and choose the best one, pass prompt, autoGenerate: true, and count: 3 so the Library opens with generated candidates. If the host can only open a browser link (e.g. a CLI or code editor), surface that link: after the user picks, the page auto-copies a short paste-back summary — or the user can simply tell you which candidate they want (e.g. "use image A"). Use search-assets, generate-image, generate-video, and export-asset for unattended flows.',
@@ -173,8 +193,25 @@ const action = defineAction({
173
193
  view: "picker",
174
194
  };
175
195
  },
176
- run: async (args) => {
196
+ run: async (args, context) => {
177
197
  const path = pickerPath(args);
198
+ const commandKey = navigateCommandKey();
199
+ if (shouldWriteNavigateCommand(context, commandKey)) {
200
+ const command = {
201
+ view: "picker" as const,
202
+ mediaType: args.mediaType,
203
+ path,
204
+ libraryId: args.libraryId ?? null,
205
+ query: args.query ?? null,
206
+ prompt: args.prompt ?? null,
207
+ aspectRatio: args.aspectRatio ?? null,
208
+ presetId: args.presetId ?? null,
209
+ _writeId: `open-asset-picker-${Date.now()}-${Math.random()
210
+ .toString(36)
211
+ .slice(2, 8)}`,
212
+ };
213
+ await writeAppState(commandKey, command);
214
+ }
178
215
  return {
179
216
  app: "assets",
180
217
  view: "picker",
@@ -36,6 +36,14 @@ async function syncImageVariantSlot(
36
36
  typeof metadata.variantBatchId === "string" && metadata.variantBatchId
37
37
  ? metadata.variantBatchId
38
38
  : null;
39
+ const threadId =
40
+ typeof metadata.threadId === "string" && metadata.threadId
41
+ ? metadata.threadId
42
+ : null;
43
+ const variantScopeId =
44
+ typeof metadata.variantScopeId === "string" && metadata.variantScopeId
45
+ ? metadata.variantScopeId
46
+ : null;
39
47
  const serialized = options.asset ? serializeAsset(options.asset) : null;
40
48
 
41
49
  await upsertVariantSlot({
@@ -45,6 +53,8 @@ async function syncImageVariantSlot(
45
53
  collectionId: run.collectionId ?? null,
46
54
  presetId: run.presetId ?? null,
47
55
  sessionId: run.sessionId ?? null,
56
+ threadId,
57
+ variantScopeId,
48
58
  prompt: run.prompt,
49
59
  slotId,
50
60
  status,