@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 } from "@agent-native/core";
2
+ import type { ActionRunContext } from "@agent-native/core/action";
2
3
  import {
3
4
  deleteAppState,
4
5
  readAppState,
@@ -7,28 +8,29 @@ import {
7
8
  import { z } from "zod";
8
9
 
9
10
  import { markAssetSaved } from "../server/handlers/assets.js";
10
- import type { AssetVariantState } from "../shared/api.js";
11
11
  import { getAssetOrThrow, serializeAsset } from "./_helpers.js";
12
+ import {
13
+ deleteVariantState,
14
+ readVariantState,
15
+ writeVariantState,
16
+ } from "./variant-slots.js";
12
17
 
13
18
  export default defineAction({
14
19
  description:
15
- "Save a generated asset candidate to the library. Accepts an assetId directly or a variant slot ID from application_state.asset-variants.",
20
+ "Save a generated asset candidate to the library. Accepts an assetId directly or a variant slot ID from the current thread's application_state.asset-variants.",
16
21
  schema: z.object({
17
22
  assetId: z.string().optional(),
18
23
  slotId: z.string().optional(),
19
24
  folderId: z.string().min(1).nullable().optional(),
25
+ threadId: z.string().nullable().optional(),
20
26
  }),
21
- run: async ({ assetId, slotId, folderId }) => {
27
+ run: async (
28
+ { assetId, slotId, folderId, threadId },
29
+ context?: ActionRunContext,
30
+ ) => {
31
+ const effectiveThreadId = threadId ?? context?.threadId ?? null;
22
32
  let resolvedAssetId = assetId;
23
- const raw = (await readAppState("asset-variants")) as unknown | null;
24
- const legacyRaw =
25
- raw ??
26
- ((await readAppState("image-variants").catch(() => null)) as
27
- | unknown
28
- | null);
29
- const variants = (raw ?? null) as AssetVariantState | null;
30
- const legacyVariants = (legacyRaw ?? null) as AssetVariantState | null;
31
- const activeVariants = variants ?? legacyVariants;
33
+ const activeVariants = await readVariantState(effectiveThreadId);
32
34
  if (!resolvedAssetId && slotId && activeVariants) {
33
35
  resolvedAssetId = activeVariants.slots.find(
34
36
  (slot) => slot.slotId === slotId,
@@ -39,18 +41,18 @@ export default defineAction({
39
41
  await markAssetSaved(resolvedAssetId, folderId);
40
42
  const asset = await getAssetOrThrow(resolvedAssetId);
41
43
  if (activeVariants) {
44
+ const stateThreadId =
45
+ effectiveThreadId ??
46
+ activeVariants.variantScopeId ??
47
+ activeVariants.threadId ??
48
+ null;
42
49
  activeVariants.slots = activeVariants.slots.filter(
43
50
  (slot) => slot.assetId !== resolvedAssetId,
44
51
  );
45
52
  if (activeVariants.slots.length) {
46
- await writeAppState(
47
- "asset-variants",
48
- activeVariants as unknown as Record<string, unknown>,
49
- );
50
- await deleteAppState("image-variants").catch(() => {});
53
+ await writeVariantState(activeVariants, stateThreadId);
51
54
  } else {
52
- await deleteAppState("asset-variants");
53
- await deleteAppState("image-variants").catch(() => {});
55
+ await deleteVariantState(stateThreadId);
54
56
  }
55
57
  }
56
58
  return serializeAsset({ ...asset, status: "saved" });
@@ -7,13 +7,21 @@ import {
7
7
  import { nowIso } from "../server/lib/json.js";
8
8
  import type { AssetVariantState } from "../shared/api.js";
9
9
 
10
- type VariantSlotInput = {
10
+ const GLOBAL_VARIANT_STATE_KEY = "asset-variants";
11
+ const LEGACY_VARIANT_STATE_KEY = "image-variants";
12
+
13
+ type VariantScopeInput = {
11
14
  runId: string;
12
15
  batchId?: string | null;
13
16
  libraryId: string;
14
17
  collectionId?: string | null;
15
18
  presetId?: string | null;
16
19
  sessionId?: string | null;
20
+ threadId?: string | null;
21
+ variantScopeId?: string | null;
22
+ };
23
+
24
+ type VariantSlotInput = VariantScopeInput & {
17
25
  prompt: string;
18
26
  slotId: string;
19
27
  status: "pending" | "ready" | "failed";
@@ -25,6 +33,29 @@ type VariantSlotInput = {
25
33
 
26
34
  let variantStateLock: Promise<void> = Promise.resolve();
27
35
 
36
+ export function variantStateKey(scopeId?: string | null) {
37
+ const scopedId = normalizeVariantScopeId(scopeId);
38
+ return scopedId
39
+ ? `${GLOBAL_VARIANT_STATE_KEY}:${scopedId}`
40
+ : GLOBAL_VARIANT_STATE_KEY;
41
+ }
42
+
43
+ function normalizeVariantScopeId(value: unknown): string | null {
44
+ if (typeof value !== "string") return null;
45
+ const trimmed = value.trim();
46
+ return /^[A-Za-z0-9:_-]{1,160}$/.test(trimmed) ? trimmed : null;
47
+ }
48
+
49
+ function variantScopeIdFor(input: {
50
+ threadId?: string | null;
51
+ variantScopeId?: string | null;
52
+ }): string | null {
53
+ return (
54
+ normalizeVariantScopeId(input.variantScopeId) ??
55
+ normalizeVariantScopeId(input.threadId)
56
+ );
57
+ }
58
+
28
59
  export async function withVariantStateLock<T>(
29
60
  operation: () => Promise<T>,
30
61
  ): Promise<T> {
@@ -44,9 +75,17 @@ export async function withVariantStateLock<T>(
44
75
  export async function wasVariantSlotDismissed(
45
76
  libraryId: string,
46
77
  slotId: string,
78
+ scope?:
79
+ | { threadId?: string | null; variantScopeId?: string | null }
80
+ | string
81
+ | null,
47
82
  ): Promise<boolean> {
48
83
  return withVariantStateLock(async () => {
49
- const state = await readVariantStateUnlocked();
84
+ const scopeId =
85
+ typeof scope === "object" && scope
86
+ ? variantScopeIdFor(scope)
87
+ : normalizeVariantScopeId(scope);
88
+ const state = await readVariantStateUnlocked(scopeId);
50
89
  if (!state) return true;
51
90
  if (state.libraryId !== libraryId) return false;
52
91
  return !state.slots.some((slot) => slot.slotId === slotId);
@@ -55,26 +94,32 @@ export async function wasVariantSlotDismissed(
55
94
 
56
95
  export async function upsertVariantSlot(input: VariantSlotInput) {
57
96
  await withVariantStateLock(async () => {
58
- const previous = await readVariantStateUnlocked();
59
- const state = isSameVariantScope(previous, input)
60
- ? previous
61
- : {
62
- runId: input.runId,
63
- batchId: input.batchId ?? null,
64
- libraryId: input.libraryId,
65
- collectionId: input.collectionId,
66
- presetId: input.presetId ?? null,
67
- sessionId: input.sessionId ?? null,
68
- prompt: input.prompt,
69
- slots: [],
70
- updatedAt: nowIso(),
71
- };
97
+ const scopeId = variantScopeIdFor(input);
98
+ const previous = await readVariantStateUnlocked(scopeId);
99
+ const state =
100
+ previous && isSameVariantScope(previous, input)
101
+ ? previous
102
+ : {
103
+ runId: input.runId,
104
+ batchId: input.batchId ?? null,
105
+ libraryId: input.libraryId,
106
+ collectionId: input.collectionId,
107
+ presetId: input.presetId ?? null,
108
+ sessionId: input.sessionId ?? null,
109
+ threadId: input.threadId ?? null,
110
+ variantScopeId: scopeId,
111
+ prompt: input.prompt,
112
+ slots: [],
113
+ updatedAt: nowIso(),
114
+ };
72
115
 
73
116
  state.runId = input.runId;
74
117
  state.batchId = input.batchId ?? null;
75
118
  state.collectionId = input.collectionId ?? null;
76
119
  state.presetId = input.presetId ?? null;
77
120
  state.sessionId = input.sessionId ?? null;
121
+ state.threadId = input.threadId ?? null;
122
+ state.variantScopeId = scopeId;
78
123
  state.prompt = input.prompt;
79
124
 
80
125
  const now = nowIso();
@@ -97,14 +142,14 @@ export async function upsertVariantSlot(input: VariantSlotInput) {
97
142
  else state.slots.push(nextSlot);
98
143
 
99
144
  state.updatedAt = now;
100
- await writeVariantStateUnlocked(state);
145
+ await writeVariantStateUnlocked(state, scopeId);
101
146
  });
102
147
  }
103
148
 
104
149
  function isSameVariantScope(
105
150
  previous: AssetVariantState | null,
106
- input: VariantSlotInput,
107
- ): previous is AssetVariantState {
151
+ input: VariantScopeInput,
152
+ ): boolean {
108
153
  if (!previous) return false;
109
154
 
110
155
  // The batch/run id is the generation boundary: batch slots may have distinct
@@ -122,20 +167,78 @@ function variantScopeId(input: { batchId?: string | null; runId: string }) {
122
167
  return input.batchId ?? input.runId;
123
168
  }
124
169
 
125
- async function readVariantStateUnlocked(): Promise<AssetVariantState | null> {
126
- const current = (await readAppState("asset-variants")) as unknown | null;
170
+ export async function readVariantState(
171
+ scopeId?: string | null,
172
+ ): Promise<AssetVariantState | null> {
173
+ return withVariantStateLock(() => readVariantStateUnlocked(scopeId));
174
+ }
175
+
176
+ export async function writeVariantState(
177
+ state: AssetVariantState,
178
+ scopeId?: string | null,
179
+ ) {
180
+ await withVariantStateLock(() => writeVariantStateUnlocked(state, scopeId));
181
+ }
182
+
183
+ export async function deleteVariantState(scopeId?: string | null) {
184
+ await withVariantStateLock(() => deleteVariantStateUnlocked(scopeId));
185
+ }
186
+
187
+ async function readVariantStateUnlocked(
188
+ scopeId?: string | null,
189
+ ): Promise<AssetVariantState | null> {
190
+ const key = variantStateKey(scopeId);
191
+ const current = (await readAppState(key)) as unknown | null;
192
+ if (current) return current as AssetVariantState;
193
+
194
+ if (scopeId) {
195
+ const globalCurrent = (await readAppState(GLOBAL_VARIANT_STATE_KEY)) as
196
+ | unknown
197
+ | null;
198
+ const globalState = (globalCurrent ?? null) as AssetVariantState | null;
199
+ return globalState?.threadId === scopeId ||
200
+ globalState?.variantScopeId === scopeId
201
+ ? globalState
202
+ : null;
203
+ }
204
+
127
205
  const legacyCurrent =
128
206
  current ??
129
- ((await readAppState("image-variants").catch(() => null)) as
207
+ ((await readAppState(LEGACY_VARIANT_STATE_KEY).catch(() => null)) as
130
208
  | unknown
131
209
  | null);
132
210
  return (legacyCurrent ?? null) as AssetVariantState | null;
133
211
  }
134
212
 
135
- async function writeVariantStateUnlocked(state: AssetVariantState) {
136
- await writeAppState(
137
- "asset-variants",
138
- state as unknown as Record<string, unknown>,
139
- );
140
- await deleteAppState("image-variants").catch(() => {});
213
+ async function writeVariantStateUnlocked(
214
+ state: AssetVariantState,
215
+ scopeId?: string | null,
216
+ ) {
217
+ const key = variantStateKey(scopeId);
218
+ await writeAppState(key, state as unknown as Record<string, unknown>);
219
+ if (key !== GLOBAL_VARIANT_STATE_KEY) {
220
+ await writeAppState(
221
+ GLOBAL_VARIANT_STATE_KEY,
222
+ state as unknown as Record<string, unknown>,
223
+ );
224
+ }
225
+ await deleteAppState(LEGACY_VARIANT_STATE_KEY).catch(() => {});
226
+ }
227
+
228
+ async function deleteVariantStateUnlocked(scopeId?: string | null) {
229
+ const key = variantStateKey(scopeId);
230
+ await deleteAppState(key);
231
+ if (key !== GLOBAL_VARIANT_STATE_KEY) {
232
+ const globalCurrent = (await readAppState(GLOBAL_VARIANT_STATE_KEY)) as
233
+ | unknown
234
+ | null;
235
+ const globalState = (globalCurrent ?? null) as AssetVariantState | null;
236
+ if (
237
+ globalState?.threadId === scopeId ||
238
+ globalState?.variantScopeId === scopeId
239
+ ) {
240
+ await deleteAppState(GLOBAL_VARIANT_STATE_KEY);
241
+ }
242
+ }
243
+ await deleteAppState(LEGACY_VARIANT_STATE_KEY).catch(() => {});
141
244
  }
@@ -71,6 +71,15 @@ export default defineAction({
71
71
  });
72
72
  }
73
73
  }
74
+ if (nav?.view === "library" && nav?.selection === "all") {
75
+ screen.libraries = await listLibraries.run({ compact: false });
76
+ screen.assets = await listAssets.run({
77
+ query:
78
+ typeof nav.search === "string" && nav.search.trim()
79
+ ? nav.search
80
+ : undefined,
81
+ });
82
+ }
74
83
  if (nav?.view === "audit") {
75
84
  screen.audit = await listAuditRuns.run({ limit: 20 });
76
85
  }