@agent-native/core 0.72.3 → 0.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +38 -0
  3. package/corpus/core/docs/content/skills-guide.md +14 -0
  4. package/corpus/core/docs/content/template-clips.md +7 -1
  5. package/corpus/core/package.json +6 -6
  6. package/corpus/core/src/a2a/client.ts +7 -3
  7. package/corpus/core/src/agent/durable-background.ts +25 -12
  8. package/corpus/core/src/cli/create.ts +1 -1
  9. package/corpus/core/src/cli/skills.ts +450 -19
  10. package/corpus/core/src/client/api-path.ts +28 -0
  11. package/corpus/core/src/client/embed-auth.ts +26 -0
  12. package/corpus/core/src/client/mcp-app-host.ts +6 -0
  13. package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +227 -33
  14. package/corpus/core/src/client/resources/use-mcp-servers.ts +1 -0
  15. package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -1
  16. package/corpus/core/src/demo/fetch-interceptor.ts +1 -1
  17. package/corpus/core/src/embedding/agent.ts +6 -1
  18. package/corpus/core/src/embedding/bridge.ts +34 -1
  19. package/corpus/core/src/mcp/build-server.ts +38 -10
  20. package/corpus/core/src/mcp/embed-app.ts +120 -45
  21. package/corpus/core/src/mcp/index.ts +5 -0
  22. package/corpus/core/src/mcp/org-directory.ts +70 -11
  23. package/corpus/core/src/mcp/server.ts +14 -0
  24. package/corpus/core/src/mcp-client/config.ts +12 -0
  25. package/corpus/core/src/mcp-client/index.ts +2 -0
  26. package/corpus/core/src/mcp-client/manager.ts +268 -9
  27. package/corpus/core/src/mcp-client/remote-store.ts +154 -0
  28. package/corpus/core/src/mcp-client/routes.ts +2 -0
  29. package/corpus/core/src/server/agent-chat-plugin.ts +8 -0
  30. package/corpus/core/src/server/auth.ts +7 -0
  31. package/corpus/core/src/server/index.ts +1 -0
  32. package/corpus/core/src/server/onboarding-html.ts +95 -9
  33. package/corpus/core/src/shared/mcp-embed-headers.ts +44 -3
  34. package/corpus/core/src/templates/default/AGENTS.md +4 -0
  35. package/corpus/core/src/templates/default/package.json +2 -1
  36. package/corpus/core/src/templates/headless/AGENTS.md +4 -0
  37. package/corpus/core/src/templates/headless/package.json +1 -0
  38. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
  39. package/corpus/core/src/templates/workspace-root/README.md +4 -0
  40. package/corpus/core/src/templates/workspace-root/package.json +1 -0
  41. package/corpus/core/src/vite/client.ts +53 -0
  42. package/corpus/templates/analytics/actions/update-dashboard.ts +3 -4
  43. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +144 -40
  44. package/corpus/templates/analytics/app/global.css +15 -13
  45. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +9 -32
  46. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +0 -26
  47. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +0 -23
  48. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +65 -2
  49. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +3 -4
  50. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-charts-now-fill-sparse-template-time-series-suppor.md +6 -0
  51. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-rows-now-auto-fit-panels-so-one-two-or-three-chart.md +6 -0
  52. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-traffic-tables-now-show-top-urls-and-shared-clips-.md +6 -0
  53. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +5 -0
  54. package/corpus/templates/analytics/changelog/2026-06-24-first-party-template-dashboards-now-include-repeat-users-ret.md +6 -0
  55. package/corpus/templates/analytics/changelog/2026-06-24-retention-charts-now-break-out-1-day-and-7-day-return-rates-.md +6 -0
  56. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +256 -44
  57. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +7 -2
  58. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +35 -8
  59. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +287 -42
  60. package/corpus/templates/assets/.agents/skills/a2a-assets/SKILL.md +17 -10
  61. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +15 -6
  62. package/corpus/templates/assets/AGENTS.md +7 -5
  63. package/corpus/templates/assets/actions/generate-asset.ts +161 -0
  64. package/corpus/templates/assets/actions/list-assets.ts +4 -2
  65. package/corpus/templates/assets/actions/open-asset-picker.ts +84 -3
  66. package/corpus/templates/assets/app/routes/library.tsx +243 -40
  67. package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +1 -1
  68. package/corpus/templates/calendar/server/plugins/auth.ts +12 -0
  69. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -7
  70. package/corpus/templates/clips/AGENTS.md +6 -2
  71. package/corpus/templates/clips/app/components/capture-install-options.tsx +20 -4
  72. package/corpus/templates/clips/app/components/player/player-controls.tsx +62 -32
  73. package/corpus/templates/clips/app/lib/capture-install-options.ts +39 -5
  74. package/corpus/templates/clips/app/routes/download.tsx +2 -2
  75. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
  76. package/corpus/templates/clips/changelog/2026-06-24-inline-slack-playback-controls-no-longer-shift-when-adjustin.md +6 -0
  77. package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-console-log-stream-all-le.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-fetch-xhr-network-request.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-06-24-the-chrome-extension-option-now-only-appears-on-supported-hosts.md +5 -0
  80. package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +13 -8
  81. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  82. package/corpus/templates/clips/chrome-extension/scripts/package.ts +15 -2
  83. package/corpus/templates/clips/chrome-extension/src/overlay.css +4 -25
  84. package/corpus/templates/clips/chrome-extension/src/overlay.ts +1 -3
  85. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +9 -0
  86. package/corpus/templates/clips/server/lib/public-agent-context.ts +67 -24
  87. package/corpus/templates/design/AGENTS.md +10 -5
  88. package/corpus/templates/design/actions/connect-assets-mcp.ts +131 -0
  89. package/corpus/templates/design/actions/insert-asset.ts +219 -0
  90. package/corpus/templates/design/actions/view-screen.ts +3 -1
  91. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +1 -1
  92. package/corpus/templates/design/app/pages/DesignEditor.tsx +53 -0
  93. package/corpus/templates/design/server/plugins/agent-chat.ts +5 -1
  94. package/corpus/templates/design/server/plugins/assets-mcp.ts +60 -0
  95. package/corpus/templates/mail/server/plugins/auth.ts +4 -1
  96. package/corpus/templates/plan/app/pages/PlansPage.tsx +271 -197
  97. package/corpus/templates/plan/server/plan-content.ts +45 -2
  98. package/dist/a2a/client.d.ts +1 -0
  99. package/dist/a2a/client.d.ts.map +1 -1
  100. package/dist/a2a/client.js +5 -3
  101. package/dist/a2a/client.js.map +1 -1
  102. package/dist/agent/durable-background.d.ts +9 -4
  103. package/dist/agent/durable-background.d.ts.map +1 -1
  104. package/dist/agent/durable-background.js +24 -11
  105. package/dist/agent/durable-background.js.map +1 -1
  106. package/dist/cli/create.js +1 -1
  107. package/dist/cli/create.js.map +1 -1
  108. package/dist/cli/skills.d.ts.map +1 -1
  109. package/dist/cli/skills.js +361 -19
  110. package/dist/cli/skills.js.map +1 -1
  111. package/dist/client/api-path.d.ts.map +1 -1
  112. package/dist/client/api-path.js +27 -0
  113. package/dist/client/api-path.js.map +1 -1
  114. package/dist/client/embed-auth.d.ts.map +1 -1
  115. package/dist/client/embed-auth.js +27 -0
  116. package/dist/client/embed-auth.js.map +1 -1
  117. package/dist/client/mcp-app-host.d.ts.map +1 -1
  118. package/dist/client/mcp-app-host.js +5 -0
  119. package/dist/client/mcp-app-host.js.map +1 -1
  120. package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
  121. package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  122. package/dist/client/mcp-apps/McpAppRenderer.js +179 -33
  123. package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
  124. package/dist/client/resources/use-mcp-servers.d.ts +1 -0
  125. package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
  126. package/dist/client/resources/use-mcp-servers.js.map +1 -1
  127. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  128. package/dist/client/settings/DemoModeSection.js +3 -1
  129. package/dist/client/settings/DemoModeSection.js.map +1 -1
  130. package/dist/demo/fetch-interceptor.js +1 -1
  131. package/dist/demo/fetch-interceptor.js.map +1 -1
  132. package/dist/embedding/agent.d.ts +1 -1
  133. package/dist/embedding/agent.d.ts.map +1 -1
  134. package/dist/embedding/agent.js +1 -1
  135. package/dist/embedding/agent.js.map +1 -1
  136. package/dist/embedding/bridge.d.ts.map +1 -1
  137. package/dist/embedding/bridge.js +27 -1
  138. package/dist/embedding/bridge.js.map +1 -1
  139. package/dist/extensions/schema.d.ts +2 -2
  140. package/dist/mcp/build-server.d.ts +2 -0
  141. package/dist/mcp/build-server.d.ts.map +1 -1
  142. package/dist/mcp/build-server.js +32 -9
  143. package/dist/mcp/build-server.js.map +1 -1
  144. package/dist/mcp/embed-app.d.ts.map +1 -1
  145. package/dist/mcp/embed-app.js +120 -45
  146. package/dist/mcp/embed-app.js.map +1 -1
  147. package/dist/mcp/index.d.ts +1 -0
  148. package/dist/mcp/index.d.ts.map +1 -1
  149. package/dist/mcp/index.js +1 -0
  150. package/dist/mcp/index.js.map +1 -1
  151. package/dist/mcp/org-directory.d.ts +1 -0
  152. package/dist/mcp/org-directory.d.ts.map +1 -1
  153. package/dist/mcp/org-directory.js +59 -11
  154. package/dist/mcp/org-directory.js.map +1 -1
  155. package/dist/mcp/server.d.ts.map +1 -1
  156. package/dist/mcp/server.js +9 -0
  157. package/dist/mcp/server.js.map +1 -1
  158. package/dist/mcp-client/config.d.ts +12 -0
  159. package/dist/mcp-client/config.d.ts.map +1 -1
  160. package/dist/mcp-client/config.js.map +1 -1
  161. package/dist/mcp-client/index.d.ts +1 -1
  162. package/dist/mcp-client/index.d.ts.map +1 -1
  163. package/dist/mcp-client/index.js +1 -1
  164. package/dist/mcp-client/index.js.map +1 -1
  165. package/dist/mcp-client/manager.d.ts +2 -0
  166. package/dist/mcp-client/manager.d.ts.map +1 -1
  167. package/dist/mcp-client/manager.js +201 -9
  168. package/dist/mcp-client/manager.js.map +1 -1
  169. package/dist/mcp-client/remote-store.d.ts +27 -0
  170. package/dist/mcp-client/remote-store.d.ts.map +1 -1
  171. package/dist/mcp-client/remote-store.js +105 -0
  172. package/dist/mcp-client/remote-store.js.map +1 -1
  173. package/dist/mcp-client/routes.d.ts +1 -0
  174. package/dist/mcp-client/routes.d.ts.map +1 -1
  175. package/dist/mcp-client/routes.js +1 -0
  176. package/dist/mcp-client/routes.js.map +1 -1
  177. package/dist/provider-api/actions/github-repo-files.d.ts +2 -2
  178. package/dist/provider-api/actions/query-staged-dataset.d.ts +3 -3
  179. package/dist/provider-api/corpus-jobs.d.ts +8 -8
  180. package/dist/server/agent-chat-plugin.d.ts +2 -0
  181. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  182. package/dist/server/agent-chat-plugin.js +8 -0
  183. package/dist/server/agent-chat-plugin.js.map +1 -1
  184. package/dist/server/auth.d.ts +7 -0
  185. package/dist/server/auth.d.ts.map +1 -1
  186. package/dist/server/auth.js +1 -0
  187. package/dist/server/auth.js.map +1 -1
  188. package/dist/server/index.d.ts +1 -1
  189. package/dist/server/index.d.ts.map +1 -1
  190. package/dist/server/index.js +1 -1
  191. package/dist/server/index.js.map +1 -1
  192. package/dist/server/onboarding-html.d.ts +15 -0
  193. package/dist/server/onboarding-html.d.ts.map +1 -1
  194. package/dist/server/onboarding-html.js +73 -9
  195. package/dist/server/onboarding-html.js.map +1 -1
  196. package/dist/shared/mcp-embed-headers.d.ts +3 -1
  197. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  198. package/dist/shared/mcp-embed-headers.js +40 -3
  199. package/dist/shared/mcp-embed-headers.js.map +1 -1
  200. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  201. package/dist/sharing/actions/set-resource-visibility.d.ts +2 -2
  202. package/dist/sharing/actions/share-resource.d.ts +1 -1
  203. package/dist/sharing/schema.d.ts +1 -1
  204. package/dist/templates/default/AGENTS.md +4 -0
  205. package/dist/templates/default/package.json +2 -1
  206. package/dist/templates/headless/AGENTS.md +4 -0
  207. package/dist/templates/headless/package.json +1 -0
  208. package/dist/templates/workspace-root/AGENTS.md +4 -0
  209. package/dist/templates/workspace-root/README.md +4 -0
  210. package/dist/templates/workspace-root/package.json +1 -0
  211. package/dist/vite/client.d.ts.map +1 -1
  212. package/dist/vite/client.js +44 -0
  213. package/dist/vite/client.js.map +1 -1
  214. package/docs/content/skills-guide.md +14 -0
  215. package/docs/content/template-clips.md +7 -1
  216. package/package.json +6 -6
  217. package/src/templates/default/AGENTS.md +4 -0
  218. package/src/templates/default/package.json +2 -1
  219. package/src/templates/headless/AGENTS.md +4 -0
  220. package/src/templates/headless/package.json +1 -0
  221. package/src/templates/workspace-root/AGENTS.md +4 -0
  222. package/src/templates/workspace-root/README.md +4 -0
  223. package/src/templates/workspace-root/package.json +1 -0
@@ -9,6 +9,7 @@ import {
9
9
  appPath,
10
10
  getBrowserTabId,
11
11
  getEmbedAuthToken,
12
+ isEmbedMcpChatBridgeActive,
12
13
  isEmbedAuthActive,
13
14
  sendMcpAppHostMessage,
14
15
  updateMcpAppModelContext,
@@ -53,6 +54,7 @@ import {
53
54
  DialogTitle,
54
55
  } from "@/components/ui/dialog";
55
56
  import { DEFAULT_LIBRARY_PRESETS } from "../../shared/library-presets";
57
+ import type { ImageQualityTier, StyleStrength } from "../../shared/api";
56
58
 
57
59
  type AssetTab = "all" | "generated" | "references";
58
60
 
@@ -127,12 +129,44 @@ type HostConfig = {
127
129
  prompt?: string;
128
130
  query?: string;
129
131
  libraryId?: string;
132
+ libraryHint?: string;
130
133
  aspectRatio?: string;
131
134
  presetId?: string;
132
135
  count?: number;
136
+ tier?: ImageQualityTier;
137
+ styleStrength?: StyleStrength;
138
+ includeLogo?: boolean;
139
+ callerAppId?: string;
133
140
  autoGenerate?: boolean;
141
+ candidateRunIds?: string[];
134
142
  };
135
143
 
144
+ // Preselect the library whose title/description best matches a free-text brand
145
+ // or use-case hint. Falls back to no match (caller uses the first library).
146
+ function matchLibraryByHint(
147
+ libraries: Library[],
148
+ hint: string | undefined,
149
+ ): string | undefined {
150
+ const needle = hint?.trim().toLowerCase();
151
+ if (!needle) return undefined;
152
+ const terms = needle.split(/\s+/).filter(Boolean);
153
+ let best: { id: string; score: number } | null = null;
154
+ for (const library of libraries) {
155
+ const haystack =
156
+ `${library.title ?? ""} ${library.description ?? ""}`.toLowerCase();
157
+ if (!haystack.trim()) continue;
158
+ let score = 0;
159
+ if (haystack.includes(needle)) score += 10;
160
+ for (const term of terms) {
161
+ if (haystack.includes(term)) score += 1;
162
+ }
163
+ if (score > 0 && (!best || score > best.score)) {
164
+ best = { id: library.id, score };
165
+ }
166
+ }
167
+ return best?.id;
168
+ }
169
+
136
170
  function isEmbeddedWindow() {
137
171
  if (typeof window === "undefined") return false;
138
172
  try {
@@ -154,6 +188,40 @@ function normalizeMediaType(value: unknown): PickerMediaType {
154
188
  return value === "video" ? "video" : "image";
155
189
  }
156
190
 
191
+ function normalizeBoolean(value: unknown): boolean | undefined {
192
+ if (typeof value === "boolean") return value;
193
+ if (typeof value !== "string") return undefined;
194
+ const normalized = value.trim().toLowerCase();
195
+ if (["1", "true", "yes", "on"].includes(normalized)) return true;
196
+ if (["", "0", "false", "no", "off"].includes(normalized)) return false;
197
+ return undefined;
198
+ }
199
+
200
+ function normalizeTier(value: unknown): ImageQualityTier | undefined {
201
+ return value === "auto" || value === "fast" || value === "best"
202
+ ? value
203
+ : undefined;
204
+ }
205
+
206
+ function normalizeStyleStrength(value: unknown): StyleStrength | undefined {
207
+ return value === "subtle" || value === "balanced" || value === "strong"
208
+ ? value
209
+ : undefined;
210
+ }
211
+
212
+ function normalizeCandidateRunIds(value: unknown): string[] | undefined {
213
+ if (value === null || value === undefined) return undefined;
214
+ const raw = Array.isArray(value)
215
+ ? value
216
+ : typeof value === "string"
217
+ ? value.split(",")
218
+ : [];
219
+ const ids = raw
220
+ .map((item) => (typeof item === "string" ? item.trim() : ""))
221
+ .filter(Boolean);
222
+ return ids;
223
+ }
224
+
157
225
  function normalizeHostConfig(value: unknown): HostConfig {
158
226
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
159
227
  const record = value as Record<string, unknown>;
@@ -166,17 +234,22 @@ function normalizeHostConfig(value: unknown): HostConfig {
166
234
  query: typeof record.query === "string" ? record.query : undefined,
167
235
  libraryId:
168
236
  typeof record.libraryId === "string" ? record.libraryId : undefined,
237
+ libraryHint:
238
+ typeof record.libraryHint === "string" ? record.libraryHint : undefined,
169
239
  aspectRatio:
170
240
  typeof record.aspectRatio === "string" ? record.aspectRatio : undefined,
171
241
  presetId: typeof record.presetId === "string" ? record.presetId : undefined,
172
242
  count:
173
243
  record.count === undefined ? undefined : normalizeCount(record.count),
244
+ tier: normalizeTier(record.tier),
245
+ styleStrength: normalizeStyleStrength(record.styleStrength),
246
+ includeLogo: normalizeBoolean(record.includeLogo),
247
+ callerAppId:
248
+ typeof record.callerAppId === "string" ? record.callerAppId : undefined,
249
+ candidateRunIds: normalizeCandidateRunIds(record.candidateRunIds),
174
250
  };
175
251
  if (Object.prototype.hasOwnProperty.call(record, "autoGenerate")) {
176
- config.autoGenerate =
177
- record.autoGenerate === true ||
178
- record.autoGenerate === "true" ||
179
- record.autoGenerate === "1";
252
+ config.autoGenerate = normalizeBoolean(record.autoGenerate) ?? false;
180
253
  }
181
254
  return config;
182
255
  }
@@ -440,32 +513,53 @@ function blobToBase64(blob: Blob): Promise<string> {
440
513
  });
441
514
  }
442
515
 
516
+ const MCP_IMAGE_CONTENT_MAX_BYTES = 4 * 1024 * 1024;
517
+
518
+ function base64ByteLength(data: string) {
519
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
520
+ return Math.floor((data.length * 3) / 4) - padding;
521
+ }
522
+
523
+ async function fetchImageContent(url: string, fallbackMimeType: string) {
524
+ const credentials =
525
+ typeof window !== "undefined" &&
526
+ new URL(url, window.location.href).origin === window.location.origin
527
+ ? "same-origin"
528
+ : "omit";
529
+ const response = await fetch(url, { credentials });
530
+ if (!response.ok) return null;
531
+ const blob = await response.blob();
532
+ const detectedMimeType = blob.type.startsWith("image/")
533
+ ? blob.type
534
+ : fallbackMimeType;
535
+ return {
536
+ type: "image",
537
+ data: await blobToBase64(blob),
538
+ mimeType: detectedMimeType,
539
+ };
540
+ }
541
+
443
542
  async function imageContentForAsset(payload: ReturnType<typeof assetPayload>) {
444
543
  const url = payload.url ?? payload.downloadUrl ?? payload.previewUrl;
445
544
  const mimeType = payload.mimeType?.startsWith("image/")
446
545
  ? payload.mimeType
447
546
  : "image/png";
448
547
  if (!url || payload.mediaType !== "image") return null;
449
- try {
450
- const credentials =
451
- typeof window !== "undefined" &&
452
- new URL(url, window.location.href).origin === window.location.origin
453
- ? "same-origin"
454
- : "omit";
455
- const response = await fetch(url, { credentials });
456
- if (!response.ok) return null;
457
- const blob = await response.blob();
458
- const detectedMimeType = blob.type.startsWith("image/")
459
- ? blob.type
460
- : mimeType;
461
- return {
462
- type: "image",
463
- data: await blobToBase64(blob),
464
- mimeType: detectedMimeType,
465
- };
466
- } catch {
467
- return null;
548
+ const sources = uniqueSources([url, payload.thumbnailUrl]);
549
+ for (const source of sources) {
550
+ try {
551
+ const content = await fetchImageContent(source, mimeType);
552
+ if (
553
+ content &&
554
+ base64ByteLength(content.data) <= MCP_IMAGE_CONTENT_MAX_BYTES
555
+ ) {
556
+ return content;
557
+ }
558
+ } catch {
559
+ // Try the next smaller source.
560
+ }
468
561
  }
562
+ return null;
469
563
  }
470
564
 
471
565
  function notifyMcpHost(payload: ReturnType<typeof assetPayload>) {
@@ -490,16 +584,18 @@ function notifyMcpHost(payload: ReturnType<typeof assetPayload>) {
490
584
  }) || false,
491
585
  )
492
586
  .catch(() => false)
493
- .then(() =>
494
- Promise.resolve(
587
+ .then((contextOk) => {
588
+ return Promise.resolve(
495
589
  sendMcpAppHostMessage({
496
590
  message,
497
591
  context: JSON.stringify(context, null, 2),
498
592
  content: chatContent,
499
593
  structuredContent: context,
500
594
  }) || false,
501
- ).catch(() => false),
502
- );
595
+ )
596
+ .catch(() => false)
597
+ .then((chatOk) => contextOk || chatOk);
598
+ });
503
599
  });
504
600
  }
505
601
 
@@ -619,6 +715,9 @@ function AssetOverlayImage({ asset }: { asset: Asset }) {
619
715
  export default function AssetPicker() {
620
716
  const [searchParams] = useSearchParams();
621
717
  const searchParamsKey = searchParams.toString();
718
+ const mcpChatBridgeActive =
719
+ searchParams.get("__an_mcp_chat_bridge") === "1" ||
720
+ isEmbedMcpChatBridgeActive();
622
721
  const urlHostConfig = useMemo(() => {
623
722
  const params = new URLSearchParams(searchParamsKey);
624
723
  return {
@@ -626,12 +725,20 @@ export default function AssetPicker() {
626
725
  prompt: params.get("prompt") ?? undefined,
627
726
  query: params.get("q") ?? undefined,
628
727
  libraryId: params.get("libraryId") ?? undefined,
728
+ libraryHint: params.get("libraryHint") ?? undefined,
629
729
  aspectRatio: params.get("aspectRatio") ?? undefined,
630
730
  presetId: params.get("presetId") ?? undefined,
631
731
  count: normalizeCount(params.get("count")),
632
- autoGenerate:
633
- params.get("autoGenerate") === "1" ||
634
- params.get("autoGenerate") === "true",
732
+ tier: normalizeTier(params.get("tier")),
733
+ styleStrength: normalizeStyleStrength(params.get("styleStrength")),
734
+ includeLogo: normalizeBoolean(params.get("includeLogo")),
735
+ callerAppId: params.get("callerAppId") ?? undefined,
736
+ candidateRunIds: normalizeCandidateRunIds(
737
+ params.getAll("candidateRunIds").length > 0
738
+ ? params.getAll("candidateRunIds")
739
+ : params.get("candidateRunIds"),
740
+ ),
741
+ autoGenerate: normalizeBoolean(params.get("autoGenerate")) ?? false,
635
742
  } satisfies HostConfig;
636
743
  }, [searchParamsKey]);
637
744
  const bridgeRef = useRef<EmbeddedAppBridge | null>(null);
@@ -655,6 +762,9 @@ export default function AssetPicker() {
655
762
  useState<Library | null>(null);
656
763
  const autoGenerateKeyRef = useRef<string | null>(null);
657
764
  const autoCreateLibraryRef = useRef(false);
765
+ const [visibleCandidateRunIds, setVisibleCandidateRunIds] = useState<
766
+ string[]
767
+ >(() => hostConfig.candidateRunIds ?? []);
658
768
 
659
769
  useEffect(() => {
660
770
  setHostConfig((current) => ({ ...current, ...urlHostConfig }));
@@ -665,6 +775,7 @@ export default function AssetPicker() {
665
775
  setAspectRatio(urlHostConfig.aspectRatio ?? "16:9");
666
776
  setPresetId(urlHostConfig.presetId ?? "none");
667
777
  setCount(urlHostConfig.count ?? 3);
778
+ setVisibleCandidateRunIds(urlHostConfig.candidateRunIds ?? []);
668
779
  }, [urlHostConfig]);
669
780
 
670
781
  const librariesQuery = useActionQuery("list-libraries", {
@@ -693,7 +804,10 @@ export default function AssetPicker() {
693
804
  const firstLibraryId = displayLibraries[0]?.id;
694
805
  if (!firstLibraryId) return;
695
806
  if (!selectedLibraryId) {
696
- setSelectedLibraryId(firstLibraryId);
807
+ setSelectedLibraryId(
808
+ matchLibraryByHint(displayLibraries, hostConfig.libraryHint) ??
809
+ firstLibraryId,
810
+ );
697
811
  return;
698
812
  }
699
813
  if (!libraryListReady) return;
@@ -703,7 +817,12 @@ export default function AssetPicker() {
703
817
  if (!selectedLibraryExists) {
704
818
  setSelectedLibraryId(firstLibraryId);
705
819
  }
706
- }, [displayLibraries, libraryListReady, selectedLibraryId]);
820
+ }, [
821
+ displayLibraries,
822
+ hostConfig.libraryHint,
823
+ libraryListReady,
824
+ selectedLibraryId,
825
+ ]);
707
826
 
708
827
  const { data: config } = useActionQuery(
709
828
  "get-image-generation-config",
@@ -742,9 +861,6 @@ export default function AssetPicker() {
742
861
  Boolean(selectedLibraryId) &&
743
862
  (presetsLoading || presetsFetching || presetsPending || !selectedPreset);
744
863
  const mediaLabel = mediaType === "video" ? "video" : "image";
745
- const [visibleCandidateRunIds, setVisibleCandidateRunIds] = useState<
746
- string[]
747
- >([]);
748
864
  const generateBatch = useActionMutation(
749
865
  "generate-image-batch" as any,
750
866
  {
@@ -876,12 +992,28 @@ export default function AssetPicker() {
876
992
  [],
877
993
  );
878
994
 
995
+ const postEmbeddedSelectionMessage = useCallback(
996
+ (
997
+ name: "chooseAsset" | "chooseImage",
998
+ payload: ReturnType<typeof assetPayload>,
999
+ ) => {
1000
+ try {
1001
+ return bridgeRef.current?.postMessage(name, payload) ?? false;
1002
+ } catch {
1003
+ return false;
1004
+ }
1005
+ },
1006
+ [],
1007
+ );
1008
+
879
1009
  const chooseAsset = (asset: Asset) => {
880
1010
  const payload = assetPayload(asset, mediaType);
881
1011
  if (embedded) {
882
- bridgeRef.current?.postMessage("chooseAsset", payload);
883
- if (payload.mediaType === "image") {
884
- bridgeRef.current?.postMessage("chooseImage", payload);
1012
+ if (!mcpChatBridgeActive) {
1013
+ postEmbeddedSelectionMessage("chooseAsset", payload);
1014
+ if (payload.mediaType === "image") {
1015
+ postEmbeddedSelectionMessage("chooseImage", payload);
1016
+ }
885
1017
  }
886
1018
  void notifyMcpHost(payload).then((ok) => {
887
1019
  if (ok) {
@@ -912,6 +1044,9 @@ export default function AssetPicker() {
912
1044
  setQuery("");
913
1045
  },
914
1046
  onError: (error: Error) => {
1047
+ // Allow the auto-create effect to retry after a transient failure;
1048
+ // otherwise the picker stays stuck on "Preparing..." until reload.
1049
+ autoCreateLibraryRef.current = false;
915
1050
  toast.error(error.message || "Could not prepare an image library");
916
1051
  },
917
1052
  } as any,
@@ -931,12 +1066,20 @@ export default function AssetPicker() {
931
1066
  imageSize: selectedPreset?.imageSize || "2K",
932
1067
  dismissible: false,
933
1068
  })),
1069
+ tier: hostConfig.tier,
1070
+ styleStrength: hostConfig.styleStrength ?? "balanced",
1071
+ includeLogo: hostConfig.includeLogo ?? false,
934
1072
  source: "ui",
1073
+ callerAppId: hostConfig.callerAppId,
935
1074
  } as any);
936
1075
  }, [
937
1076
  count,
938
1077
  effectiveAspectRatio,
939
1078
  generateBatch,
1079
+ hostConfig.callerAppId,
1080
+ hostConfig.includeLogo,
1081
+ hostConfig.styleStrength,
1082
+ hostConfig.tier,
940
1083
  prompt,
941
1084
  setVisibleCandidateRunIds,
942
1085
  selectedLibraryId,
@@ -945,8 +1088,35 @@ export default function AssetPicker() {
945
1088
  ]);
946
1089
 
947
1090
  useEffect(() => {
1091
+ const hostCandidateFilterMatches =
1092
+ hostConfig.candidateRunIds?.length &&
1093
+ normalizeMediaType(hostConfig.mediaType) === mediaType &&
1094
+ (hostConfig.libraryId ?? "") === (selectedLibraryId ?? "") &&
1095
+ (hostConfig.prompt ?? "") === prompt &&
1096
+ (hostConfig.aspectRatio ?? "16:9") === aspectRatio &&
1097
+ (hostConfig.presetId ?? "none") === presetId &&
1098
+ (hostConfig.count ?? 3) === count;
1099
+ if (hostCandidateFilterMatches) return;
948
1100
  setVisibleCandidateRunIds([]);
949
- }, [aspectRatio, count, mediaType, presetId, prompt, selectedLibraryId]);
1101
+ }, [
1102
+ aspectRatio,
1103
+ count,
1104
+ hostConfig.aspectRatio,
1105
+ hostConfig.candidateRunIds,
1106
+ hostConfig.count,
1107
+ hostConfig.callerAppId,
1108
+ hostConfig.includeLogo,
1109
+ hostConfig.libraryId,
1110
+ hostConfig.mediaType,
1111
+ hostConfig.presetId,
1112
+ hostConfig.prompt,
1113
+ hostConfig.styleStrength,
1114
+ hostConfig.tier,
1115
+ mediaType,
1116
+ presetId,
1117
+ prompt,
1118
+ selectedLibraryId,
1119
+ ]);
950
1120
 
951
1121
  useEffect(() => {
952
1122
  const bridge = createEmbeddedAppBridge({
@@ -961,6 +1131,9 @@ export default function AssetPicker() {
961
1131
  if (next.aspectRatio !== undefined) setAspectRatio(next.aspectRatio);
962
1132
  if (next.presetId !== undefined) setPresetId(next.presetId || "none");
963
1133
  if (next.count !== undefined) setCount(next.count);
1134
+ if (next.candidateRunIds !== undefined) {
1135
+ setVisibleCandidateRunIds(next.candidateRunIds);
1136
+ }
964
1137
  },
965
1138
  });
966
1139
  bridgeRef.current = bridge;
@@ -1016,6 +1189,31 @@ export default function AssetPicker() {
1016
1189
  usingStarterLibrary;
1017
1190
  const preparingGenerationLibrary =
1018
1191
  needsGenerationLibrary && createPickerLibrary.isPending;
1192
+ const waitingForLibraries = mediaType === "image" && !libraryListReady;
1193
+ const preparingAutoLibrary =
1194
+ mediaType === "image" &&
1195
+ Boolean(prompt.trim()) &&
1196
+ (waitingForLibraries ||
1197
+ needsGenerationLibrary ||
1198
+ preparingGenerationLibrary);
1199
+ const generationButtonLabel = generateBatch.isPending
1200
+ ? "Generating..."
1201
+ : waitingForRequestedPreset
1202
+ ? "Loading preset..."
1203
+ : setupNeeded
1204
+ ? "Setup needed"
1205
+ : preparingAutoLibrary
1206
+ ? "Preparing..."
1207
+ : "Generate";
1208
+ const generationStatus = generateBatch.isPending
1209
+ ? "Generating candidates..."
1210
+ : preparingAutoLibrary
1211
+ ? waitingForLibraries
1212
+ ? "Checking image libraries..."
1213
+ : "Preparing an image library..."
1214
+ : waitingForRequestedPreset
1215
+ ? "Loading the requested preset..."
1216
+ : null;
1019
1217
 
1020
1218
  useEffect(() => {
1021
1219
  if (!selectedPreset?.aspectRatio) return;
@@ -1213,9 +1411,14 @@ export default function AssetPicker() {
1213
1411
  disabled={!canGenerate}
1214
1412
  onClick={runGenerate}
1215
1413
  >
1216
- {generateBatch.isPending ? "Generating..." : "Generate"}
1414
+ {generationButtonLabel}
1217
1415
  </Button>
1218
1416
  </div>
1417
+ {generationStatus && (
1418
+ <div className="-mt-1 px-3 pb-2 text-[11px] leading-4 text-muted-foreground">
1419
+ {generationStatus}
1420
+ </div>
1421
+ )}
1219
1422
  </div>
1220
1423
  ) : null}
1221
1424
 
@@ -3,4 +3,4 @@ type: added
3
3
  date: 2026-06-23
4
4
  ---
5
5
 
6
- Added a heads-up explaining Google's 'app not verified' screen when connecting your calendar, so you know it's safe to continue
6
+ Added a heads-up explaining Google's 'app not verified' screen when signing in or connecting your calendar, so you know it's safe to continue
@@ -16,6 +16,18 @@ export default createAuthPlugin({
16
16
  "Manages availability and booking links automatically",
17
17
  "Answers schedule questions and resolves conflicts instantly",
18
18
  ],
19
+ runLocalCommand:
20
+ "npx @agent-native/core@latest create my-calendar-app --template calendar",
21
+ },
22
+ googleSignInNotice: {
23
+ host: "calendar.agent-native.com",
24
+ title: "Google may show a warning",
25
+ body: [
26
+ "You'll see this screen because this demo uses Agent-Native's Google app, not a Google-reviewed public app.",
27
+ "It's safe to continue: click Advanced, then “Go to … (unsafe)” to finish signing in.",
28
+ ],
29
+ continueLabel: "Continue to Google",
30
+ cancelLabel: "Run locally",
19
31
  },
20
32
  publicPaths: [
21
33
  "/book",
@@ -57,13 +57,13 @@ the recorder page itself. The Clips Chrome extension is the active-tab path for
57
57
  browser logs: it launches `/record` with an extension capture session and passes
58
58
  `developerLogs=1/0`, then saves diagnostics with source `extension`.
59
59
 
60
- Keep the public Chrome extension option hidden until the Web Store listing is
61
- live. UI prompts that otherwise say "Download desktop app" should use the shared
62
- install-choice component so `VITE_CLIPS_CHROME_EXTENSION_ENABLED=1` can reveal
63
- two options: Chrome extension for browser logs, and desktop app for the most
64
- seamless native capture. Also set `VITE_CLIPS_CHROME_EXTENSION_URL` when the
65
- extension URL exists; leave the boolean unset while preparing/submitting the
66
- listing.
60
+ The Web Store listing is live, so the public Chrome extension option shows by
61
+ default. UI prompts that otherwise say "Download desktop app" use the shared
62
+ install-choice component (`CaptureInstallButton` / `CaptureInstallInlineLink`)
63
+ to offer two options: Chrome extension for browser logs, and desktop app for the
64
+ most seamless native capture. Set `VITE_CLIPS_CHROME_EXTENSION_ENABLED=0` to hide
65
+ the Chrome option again, or `VITE_CLIPS_CHROME_EXTENSION_URL` to point at a
66
+ different listing.
67
67
 
68
68
  ## Pause / resume
69
69
 
@@ -65,8 +65,12 @@ Detailed media, meeting, dictation, editing, and sharing rules live in
65
65
  the recording session. `save-browser-diagnostics` is UI/internal and stores
66
66
  bounded console logs plus fetch/XHR method, URL path/query keys, status, and
67
67
  duration; it never captures headers, bodies, cookies, or query values. Use
68
- `get-recording-player-data` for full diagnostics when you have editor access;
69
- public agent context only exposes a compact issue summary.
68
+ `get-recording-player-data` for full diagnostics when you have editor access.
69
+ Public agent context exposes the redacted console stream (all levels) as
70
+ `browserDiagnostics.consoleLogs` and the fetch/XHR stream as
71
+ `browserDiagnostics.networkRequests` (method, sanitized URL with query values
72
+ redacted, status, duration), plus `consoleIssues` and `failedNetworkRequests`
73
+ highlights. All bounded; page URL, headers, bodies, and cookies stay omitted.
70
74
  - The Chrome extension lives in `chrome-extension/`. It launches `/record` with
71
75
  `clipsExtensionId` and `clipsCaptureSessionId`, then the recorder sends
72
76
  `CLIPS_CAPTURE_START/STOP/CANCEL` back to the extension. The extension uses
@@ -1,7 +1,9 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { Link } from "react-router";
3
3
  import {
4
- IconBrowser,
4
+ IconBrandApple,
5
+ IconBrandChrome,
6
+ IconBrandWindows,
5
7
  IconChevronDown,
6
8
  IconDeviceDesktop,
7
9
  IconExternalLink,
@@ -35,8 +37,22 @@ type CaptureInstallInlineLinkProps = PopoverPlacement & {
35
37
  desktopHref?: string;
36
38
  };
37
39
 
40
+ /**
41
+ * The desktop-app tile shows the icon for the visitor's current OS — Apple on
42
+ * macOS, Windows on Windows — and falls back to a neutral desktop glyph on other
43
+ * platforms or during SSR. The Chrome tile always uses the Chrome brand icon.
44
+ */
45
+ function desktopOsIcon(): typeof IconDeviceDesktop {
46
+ if (typeof navigator === "undefined") return IconDeviceDesktop;
47
+ const ua = navigator.userAgent;
48
+ if (/Windows/i.test(ua)) return IconBrandWindows;
49
+ if (/Mac|iPhone|iPad/i.test(ua)) return IconBrandApple;
50
+ return IconDeviceDesktop;
51
+ }
52
+
38
53
  function InstallOptionsContent({ desktopHref = "/download" }) {
39
54
  const chromeAvailable = Boolean(clipsChromeExtensionUrl);
55
+ const DesktopIcon = desktopOsIcon();
40
56
 
41
57
  return (
42
58
  <div className="grid gap-2">
@@ -57,7 +73,7 @@ function InstallOptionsContent({ desktopHref = "/download" }) {
57
73
  rel="noreferrer"
58
74
  className="flex items-start gap-3 rounded-md border border-border p-3 text-left transition hover:bg-accent"
59
75
  >
60
- <IconBrowser className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
76
+ <IconBrandChrome className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
61
77
  <span className="min-w-0 flex-1">
62
78
  <span className="block text-sm font-medium">Chrome extension</span>
63
79
  <span className="mt-0.5 block text-xs leading-snug text-muted-foreground">
@@ -69,7 +85,7 @@ function InstallOptionsContent({ desktopHref = "/download" }) {
69
85
  </a>
70
86
  ) : (
71
87
  <div className="flex items-start gap-3 rounded-md border border-dashed border-border p-3 text-left opacity-70">
72
- <IconBrowser className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
88
+ <IconBrandChrome className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
73
89
  <span className="min-w-0 flex-1">
74
90
  <span className="block text-sm font-medium">Chrome extension</span>
75
91
  <span className="mt-0.5 block text-xs leading-snug text-muted-foreground">
@@ -83,7 +99,7 @@ function InstallOptionsContent({ desktopHref = "/download" }) {
83
99
  to={desktopHref}
84
100
  className="flex items-start gap-3 rounded-md border border-border p-3 text-left transition hover:bg-accent"
85
101
  >
86
- <IconDeviceDesktop className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
102
+ <DesktopIcon className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
87
103
  <span className="min-w-0 flex-1">
88
104
  <span className="block text-sm font-medium">Desktop app</span>
89
105
  <span className="mt-0.5 block text-xs leading-snug text-muted-foreground">