@agent-native/core 0.119.0 → 0.119.2

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 (274) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +23 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +4 -0
  5. package/corpus/core/src/agent/production-agent.ts +1 -0
  6. package/corpus/core/src/client/AssistantChat.tsx +11 -50
  7. package/corpus/core/src/client/ErrorBoundary.tsx +25 -19
  8. package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +1 -1
  9. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +23 -0
  10. package/corpus/core/src/client/components/ui/message-scroller.tsx +1 -9
  11. package/corpus/core/src/client/resources/ResourceTree.tsx +3 -3
  12. package/corpus/core/src/client/resources/ResourcesPanel.tsx +64 -60
  13. package/corpus/core/src/client/settings/AccountSettingsCard.tsx +228 -0
  14. package/corpus/core/src/client/settings/SecretsSection.tsx +7 -5
  15. package/corpus/core/src/client/settings/SettingsPanel.tsx +67 -244
  16. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +88 -21
  17. package/corpus/core/src/client/settings/agent-settings-search.ts +0 -2
  18. package/corpus/core/src/client/settings/index.ts +7 -0
  19. package/corpus/core/src/client/vite-dev-recovery-script.ts +18 -1
  20. package/corpus/core/src/db/client.ts +65 -15
  21. package/corpus/core/src/db/index.ts +2 -0
  22. package/corpus/core/src/ingestion/figma-node-to-html.ts +20 -99
  23. package/corpus/core/src/ingestion/figma-paint-math.ts +371 -0
  24. package/corpus/core/src/ingestion/index.ts +20 -0
  25. package/corpus/core/src/localization/default-messages.ts +1 -0
  26. package/corpus/core/src/server/email.ts +33 -1
  27. package/corpus/core/src/styles/agent-native.css +23 -0
  28. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  29. package/corpus/core/src/templates/default/app/routes/settings.tsx +2 -0
  30. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  31. package/corpus/core/src/vite/client.ts +76 -8
  32. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +28 -0
  33. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +3 -1
  34. package/corpus/templates/analytics/app/components/layout/Layout.tsx +9 -13
  35. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +2 -2
  36. package/corpus/templates/analytics/app/global.css +0 -5
  37. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -13
  38. package/corpus/templates/analytics/app/lib/sql-query.ts +15 -2
  39. package/corpus/templates/analytics/app/pages/Settings.tsx +2 -24
  40. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +6 -0
  41. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  42. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +1 -1
  43. package/corpus/templates/analytics/changelog/2026-07-23-ask-analytics-is-better-centered-with-a-quieter-chat-rail-an.md +6 -0
  44. package/corpus/templates/analytics/changelog/2026-07-23-daily-dashboard-emails-load-read-heavy-panels-reliably.md +6 -0
  45. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +2 -2
  46. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +39 -5
  47. package/corpus/templates/analytics/server/lib/dashboard-report.ts +186 -62
  48. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +3 -0
  49. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +18 -2
  50. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +3 -0
  51. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +28 -0
  52. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -4
  53. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +1 -1
  54. package/corpus/templates/assets/app/global.css +0 -5
  55. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +0 -22
  56. package/corpus/templates/assets/app/routes/library.tsx +17 -9
  57. package/corpus/templates/assets/app/routes/settings.tsx +2 -0
  58. package/corpus/templates/assets/changelog/2026-07-23-create-and-chat-surfaces-are-better-centered-with-quieter-ch.md +6 -0
  59. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +28 -0
  60. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  61. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  62. package/corpus/templates/brain/app/global.css +0 -9
  63. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +0 -12
  64. package/corpus/templates/brain/app/i18n-data.ts +1 -0
  65. package/corpus/templates/brain/app/lib/settings-navigation.ts +1 -1
  66. package/corpus/templates/brain/app/routes/settings.tsx +21 -22
  67. package/corpus/templates/brain/changelog/2026-07-23-ask-brain-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  68. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +28 -0
  69. package/corpus/templates/calendar/app/pages/Settings.tsx +2 -0
  70. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  71. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  72. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  73. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  74. package/corpus/templates/chat/app/routes/settings.tsx +2 -0
  75. package/corpus/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  76. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +28 -0
  77. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +2 -0
  78. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +28 -0
  79. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -0
  80. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +28 -0
  81. package/corpus/templates/design/AGENTS.md +18 -5
  82. package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +25 -12
  83. package/corpus/templates/design/actions/hydrate-figma-paste-images.ts +115 -0
  84. package/corpus/templates/design/actions/import-figma-clipboard.ts +83 -18
  85. package/corpus/templates/design/actions/import-figma-frame.ts +34 -21
  86. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +28 -1
  87. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +106 -11
  88. package/corpus/templates/design/app/components/design/FigmaHydrationDialog.tsx +273 -0
  89. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -7
  90. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +57 -9
  91. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +10 -0
  92. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +8 -1
  93. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  94. package/corpus/templates/design/app/i18n-data.ts +150 -2
  95. package/corpus/templates/design/app/lib/design-file-upload.ts +85 -0
  96. package/corpus/templates/design/app/lib/design-import.ts +19 -1
  97. package/corpus/templates/design/app/lib/figma-clipboard.ts +110 -10
  98. package/corpus/templates/design/app/routes/settings.tsx +2 -0
  99. package/corpus/templates/design/changelog/2026-07-20-paste-figma-frames-directly-onto-the-canvas-no-token-needed.md +6 -0
  100. package/corpus/templates/design/changelog/2026-07-21-fig-upload-frame-url-and-rate-limit-errors.md +5 -0
  101. package/corpus/templates/design/changelog/2026-07-22-connecting-a-figma-token-now-actually-fills-in-a-pasted-desi.md +6 -0
  102. package/corpus/templates/design/changelog/2026-07-22-figma-line-arrow-vectors-and-stroked-icons-now-render-instea.md +6 -0
  103. package/corpus/templates/design/changelog/2026-07-22-fill-a-no-token-figma-paste-s-images-by-dropping-the-origina.md +6 -0
  104. package/corpus/templates/design/changelog/2026-07-22-imported-and-pasted-figma-frames-now-render-faithfully-fixed.md +6 -0
  105. package/corpus/templates/design/server/handlers/import-design-file.ts +50 -0
  106. package/corpus/templates/design/server/lib/fig-file-decoder.ts +90 -49
  107. package/corpus/templates/design/server/lib/fig-file-import.ts +19 -5
  108. package/corpus/templates/design/server/lib/fig-file-to-html.ts +583 -78
  109. package/corpus/templates/design/server/lib/figma-clipboard-local-decode.ts +238 -0
  110. package/corpus/templates/design/server/lib/figma-image-hydration.ts +389 -0
  111. package/corpus/templates/design/server/lib/figma-node-import.ts +136 -36
  112. package/corpus/templates/design/server/register-secrets.ts +14 -2
  113. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +28 -0
  114. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -0
  115. package/corpus/templates/dispatch/changelog/2026-07-23-quieter-progressive-disclosure.md +6 -0
  116. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +28 -0
  117. package/corpus/templates/forms/app/global.css +0 -5
  118. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +0 -7
  119. package/corpus/templates/forms/app/root.tsx +1 -7
  120. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -0
  121. package/corpus/templates/forms/changelog/2026-07-23-ask-forms-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  122. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +28 -0
  123. package/corpus/templates/macros/app/routes/settings.tsx +2 -0
  124. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +28 -0
  125. package/corpus/templates/mail/app/pages/SettingsPage.tsx +3 -1
  126. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +28 -0
  127. package/corpus/templates/plan/app/components/layout/Layout.tsx +2 -2
  128. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +1 -13
  129. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +0 -4
  130. package/corpus/templates/plan/app/routes/settings.tsx +2 -0
  131. package/corpus/templates/plan/changelog/2026-07-23-ask-plan-is-now-a-clean-full-page-chat-surface-with-better-c.md +6 -0
  132. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +28 -0
  133. package/corpus/templates/slides/app/routes/settings.tsx +2 -0
  134. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +28 -0
  135. package/corpus/toolkit/CHANGELOG.md +6 -0
  136. package/corpus/toolkit/package.json +1 -1
  137. package/corpus/toolkit/src/chat-history.css +2 -2
  138. package/corpus/toolkit/src/composer/PromptComposer.tsx +0 -31
  139. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +7 -2
  140. package/corpus/toolkit/src/styles.css +183 -36
  141. package/dist/a2a/client.d.ts +2 -0
  142. package/dist/a2a/client.d.ts.map +1 -1
  143. package/dist/a2a/client.js +2 -0
  144. package/dist/a2a/client.js.map +1 -1
  145. package/dist/agent/production-agent.d.ts.map +1 -1
  146. package/dist/agent/production-agent.js +1 -0
  147. package/dist/agent/production-agent.js.map +1 -1
  148. package/dist/client/AssistantChat.d.ts.map +1 -1
  149. package/dist/client/AssistantChat.js +11 -42
  150. package/dist/client/AssistantChat.js.map +1 -1
  151. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  152. package/dist/client/ErrorBoundary.js +3 -2
  153. package/dist/client/ErrorBoundary.js.map +1 -1
  154. package/dist/client/agent-page/AgentTabFrame.js +1 -1
  155. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  156. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  157. package/dist/client/agent-page/AgentTabsPage.js +13 -1
  158. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  159. package/dist/client/components/ui/message-scroller.d.ts.map +1 -1
  160. package/dist/client/components/ui/message-scroller.js +2 -2
  161. package/dist/client/components/ui/message-scroller.js.map +1 -1
  162. package/dist/client/resources/ResourceTree.js +2 -2
  163. package/dist/client/resources/ResourceTree.js.map +1 -1
  164. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  165. package/dist/client/resources/ResourcesPanel.js +11 -11
  166. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  167. package/dist/client/settings/AccountSettingsCard.d.ts +9 -0
  168. package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -0
  169. package/dist/client/settings/AccountSettingsCard.js +78 -0
  170. package/dist/client/settings/AccountSettingsCard.js.map +1 -0
  171. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  172. package/dist/client/settings/SecretsSection.js +2 -2
  173. package/dist/client/settings/SecretsSection.js.map +1 -1
  174. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  175. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  176. package/dist/client/settings/SettingsPanel.js +25 -77
  177. package/dist/client/settings/SettingsPanel.js.map +1 -1
  178. package/dist/client/settings/SettingsTabsPage.d.ts +5 -1
  179. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  180. package/dist/client/settings/SettingsTabsPage.js +35 -9
  181. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  182. package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
  183. package/dist/client/settings/agent-settings-search.js +0 -2
  184. package/dist/client/settings/agent-settings-search.js.map +1 -1
  185. package/dist/client/settings/index.d.ts +2 -1
  186. package/dist/client/settings/index.d.ts.map +1 -1
  187. package/dist/client/settings/index.js +2 -1
  188. package/dist/client/settings/index.js.map +1 -1
  189. package/dist/client/vite-dev-recovery-script.d.ts.map +1 -1
  190. package/dist/client/vite-dev-recovery-script.js +18 -1
  191. package/dist/client/vite-dev-recovery-script.js.map +1 -1
  192. package/dist/collab/routes.d.ts +1 -1
  193. package/dist/db/client.d.ts +18 -8
  194. package/dist/db/client.d.ts.map +1 -1
  195. package/dist/db/client.js +34 -11
  196. package/dist/db/client.js.map +1 -1
  197. package/dist/db/index.d.ts +1 -1
  198. package/dist/db/index.d.ts.map +1 -1
  199. package/dist/db/index.js.map +1 -1
  200. package/dist/ingestion/figma-node-to-html.d.ts.map +1 -1
  201. package/dist/ingestion/figma-node-to-html.js +14 -88
  202. package/dist/ingestion/figma-node-to-html.js.map +1 -1
  203. package/dist/ingestion/figma-paint-math.d.ts +157 -0
  204. package/dist/ingestion/figma-paint-math.d.ts.map +1 -0
  205. package/dist/ingestion/figma-paint-math.js +274 -0
  206. package/dist/ingestion/figma-paint-math.js.map +1 -0
  207. package/dist/ingestion/index.d.ts +1 -0
  208. package/dist/ingestion/index.d.ts.map +1 -1
  209. package/dist/ingestion/index.js +1 -0
  210. package/dist/ingestion/index.js.map +1 -1
  211. package/dist/localization/default-messages.d.ts +1 -0
  212. package/dist/localization/default-messages.d.ts.map +1 -1
  213. package/dist/localization/default-messages.js +1 -0
  214. package/dist/localization/default-messages.js.map +1 -1
  215. package/dist/notifications/routes.d.ts +2 -2
  216. package/dist/observability/routes.d.ts +3 -3
  217. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  218. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  219. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  220. package/dist/secrets/routes.d.ts +6 -6
  221. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  222. package/dist/server/email.d.ts +1 -0
  223. package/dist/server/email.d.ts.map +1 -1
  224. package/dist/server/email.js +29 -1
  225. package/dist/server/email.js.map +1 -1
  226. package/dist/server/transcribe-voice.d.ts +1 -1
  227. package/dist/styles/agent-native.css +23 -0
  228. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  229. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  230. package/dist/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  231. package/dist/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  232. package/dist/templates/chat/app/routes/settings.tsx +2 -0
  233. package/dist/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  234. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  235. package/dist/templates/default/app/routes/settings.tsx +2 -0
  236. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  237. package/dist/vite/client.d.ts.map +1 -1
  238. package/dist/vite/client.js +68 -6
  239. package/dist/vite/client.js.map +1 -1
  240. package/package.json +2 -2
  241. package/src/a2a/client.ts +4 -0
  242. package/src/agent/production-agent.ts +1 -0
  243. package/src/client/AssistantChat.tsx +11 -50
  244. package/src/client/ErrorBoundary.tsx +25 -19
  245. package/src/client/agent-page/AgentTabFrame.tsx +1 -1
  246. package/src/client/agent-page/AgentTabsPage.tsx +23 -0
  247. package/src/client/components/ui/message-scroller.tsx +1 -9
  248. package/src/client/resources/ResourceTree.tsx +3 -3
  249. package/src/client/resources/ResourcesPanel.tsx +64 -60
  250. package/src/client/settings/AccountSettingsCard.tsx +228 -0
  251. package/src/client/settings/SecretsSection.tsx +7 -5
  252. package/src/client/settings/SettingsPanel.tsx +67 -244
  253. package/src/client/settings/SettingsTabsPage.tsx +88 -21
  254. package/src/client/settings/agent-settings-search.ts +0 -2
  255. package/src/client/settings/index.ts +7 -0
  256. package/src/client/vite-dev-recovery-script.ts +18 -1
  257. package/src/db/client.ts +65 -15
  258. package/src/db/index.ts +2 -0
  259. package/src/ingestion/figma-node-to-html.ts +20 -99
  260. package/src/ingestion/figma-paint-math.ts +371 -0
  261. package/src/ingestion/index.ts +20 -0
  262. package/src/localization/default-messages.ts +1 -0
  263. package/src/server/email.ts +33 -1
  264. package/src/styles/agent-native.css +23 -0
  265. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  266. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  267. package/src/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  268. package/src/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  269. package/src/templates/chat/app/routes/settings.tsx +2 -0
  270. package/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  271. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  272. package/src/templates/default/app/routes/settings.tsx +2 -0
  273. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  274. package/src/vite/client.ts +76 -8
@@ -36,6 +36,8 @@ const MAX_FIGMA_IMAGE_BYTES = 15 * 1024 * 1024;
36
36
  const MAX_TOTAL_FIGMA_IMAGE_BYTES = 64 * 1024 * 1024;
37
37
  const MAX_FIGMA_IMAGE_REFERENCES = 256;
38
38
  const MAX_FIGMA_IMAGE_IDS_PER_REQUEST = 50;
39
+ // Figma's `/images` endpoint takes ids in the query string; cap the batch by
40
+ // both count and URL length so a complex frame's ids are fetched in full.
39
41
  const MAX_FIGMA_IMAGE_IDS_QUERY_CHARS = 1_800;
40
42
  const MAX_CONCURRENT_FIGMA_IMAGE_UPLOADS = 4;
41
43
  const FIGMA_IMAGE_FETCH_TIMEOUT_MS = 20_000;
@@ -415,6 +417,7 @@ type FigmaProviderEnvelope = {
415
417
  ok?: boolean;
416
418
  status?: number;
417
419
  statusText?: string;
420
+ headers?: Record<string, string>;
418
421
  json?: unknown;
419
422
  text?: string;
420
423
  truncated?: boolean;
@@ -422,6 +425,46 @@ type FigmaProviderEnvelope = {
422
425
  };
423
426
  };
424
427
 
428
+ export type FigmaRateLimitError = Error & {
429
+ statusCode: 429;
430
+ retryAfterSeconds?: number;
431
+ figmaPlanTier?: string;
432
+ figmaRateLimitType?: "low" | "high";
433
+ figmaUpgradeUrl?: string;
434
+ };
435
+
436
+ const FIGMA_PLAN_TIERS = new Set([
437
+ "enterprise",
438
+ "org",
439
+ "pro",
440
+ "starter",
441
+ "student",
442
+ ]);
443
+
444
+ export function isFigmaRateLimitError(
445
+ err: unknown,
446
+ ): err is FigmaRateLimitError {
447
+ return (
448
+ err instanceof Error && (err as { statusCode?: unknown }).statusCode === 429
449
+ );
450
+ }
451
+
452
+ function figmaUpgradeUrl(value: string | undefined): string | undefined {
453
+ if (!value) return undefined;
454
+ try {
455
+ const url = new URL(value);
456
+ if (
457
+ url.protocol === "https:" &&
458
+ (url.hostname === "figma.com" || url.hostname.endsWith(".figma.com"))
459
+ ) {
460
+ return value;
461
+ }
462
+ } catch {
463
+ return undefined;
464
+ }
465
+ return undefined;
466
+ }
467
+
425
468
  export function providerJson(envelope: unknown, label: string): unknown {
426
469
  const response = (envelope as FigmaProviderEnvelope | null)?.response;
427
470
  if (!response) throw new Error(`Figma ${label} response was empty.`);
@@ -431,11 +474,41 @@ export function providerJson(envelope: unknown, label: string): unknown {
431
474
  );
432
475
  }
433
476
  if (response.ok === false) {
477
+ const jsonBody = response.json as {
478
+ message?: string;
479
+ error?: string;
480
+ } | null;
434
481
  const detail =
435
482
  (typeof response.text === "string" && response.text.trim()) ||
483
+ (typeof jsonBody?.message === "string" && jsonBody.message) ||
436
484
  response.statusText ||
437
485
  `HTTP ${response.status ?? "error"}`;
438
- throw new Error(`Figma ${label} request failed: ${detail}`);
486
+ const err = new Error(`Figma ${label} request failed: ${detail}`);
487
+ if (response.status === 429) {
488
+ const rateLimitError = err as FigmaRateLimitError;
489
+ rateLimitError.statusCode = 429;
490
+
491
+ const retryAfterHeader = response.headers?.["retry-after"];
492
+ if (retryAfterHeader) {
493
+ rateLimitError.retryAfterSeconds = parseInt(retryAfterHeader, 10);
494
+ }
495
+
496
+ const planTier = response.headers?.["x-figma-plan-tier"];
497
+ if (planTier && FIGMA_PLAN_TIERS.has(planTier)) {
498
+ rateLimitError.figmaPlanTier = planTier;
499
+ }
500
+
501
+ const rateLimitType = response.headers?.["x-figma-rate-limit-type"];
502
+ if (rateLimitType === "low" || rateLimitType === "high") {
503
+ rateLimitError.figmaRateLimitType = rateLimitType;
504
+ }
505
+
506
+ const upgradeUrl = figmaUpgradeUrl(
507
+ response.headers?.["x-figma-upgrade-link"],
508
+ );
509
+ if (upgradeUrl) rateLimitError.figmaUpgradeUrl = upgradeUrl;
510
+ }
511
+ throw err;
439
512
  }
440
513
  return response.json;
441
514
  }
@@ -567,47 +640,39 @@ async function fetchFallbackImageUrls(
567
640
  nodeIds: string[],
568
641
  ): Promise<Record<string, string>> {
569
642
  if (nodeIds.length === 0) return {};
570
- const batches: string[][] = [];
643
+ const result: Record<string, string> = {};
644
+ const fetchBatch = async (ids: string[]) => {
645
+ if (ids.length === 0) return;
646
+ const envelope = await figmaGet(`/images/${fileKey}`, {
647
+ ids: ids.join(","),
648
+ format: "png",
649
+ scale: 2,
650
+ });
651
+ const json = providerJson(envelope, "images") as {
652
+ images?: Record<string, string | null | undefined>;
653
+ };
654
+ for (const [id, url] of Object.entries(json.images ?? {})) {
655
+ if (typeof url === "string" && url) result[id] = url;
656
+ }
657
+ };
658
+ // Batch over the full ID list by count and query length so complex frames
659
+ // don't silently drop layers beyond the first request.
571
660
  let batch: string[] = [];
572
661
  let queryChars = 0;
573
662
  for (const nodeId of nodeIds) {
574
- if (nodeId.length > MAX_FIGMA_IMAGE_IDS_QUERY_CHARS) {
575
- throw new Error("A Figma image fallback node id was unexpectedly long.");
576
- }
577
- const addedChars = nodeId.length + (batch.length > 0 ? 1 : 0);
663
+ const addedChars = nodeId.length + 1;
578
664
  if (
579
665
  batch.length >= MAX_FIGMA_IMAGE_IDS_PER_REQUEST ||
580
666
  queryChars + addedChars > MAX_FIGMA_IMAGE_IDS_QUERY_CHARS
581
667
  ) {
582
- batches.push(batch);
668
+ await fetchBatch(batch);
583
669
  batch = [];
584
670
  queryChars = 0;
585
671
  }
586
672
  batch.push(nodeId);
587
- queryChars += nodeId.length + (batch.length > 1 ? 1 : 0);
588
- }
589
- if (batch.length > 0) batches.push(batch);
590
-
591
- const responses = await mapWithConcurrency(
592
- batches,
593
- MAX_CONCURRENT_FIGMA_IMAGE_UPLOADS,
594
- async (ids) => {
595
- const envelope = await figmaGet(`/images/${fileKey}`, {
596
- ids: ids.join(","),
597
- format: "png",
598
- scale: 2,
599
- });
600
- return providerJson(envelope, "images") as {
601
- images?: Record<string, string | null | undefined>;
602
- };
603
- },
604
- );
605
- const result: Record<string, string> = {};
606
- for (const response of responses) {
607
- for (const [id, url] of Object.entries(response.images ?? {})) {
608
- if (typeof url === "string" && url) result[id] = url;
609
- }
673
+ queryChars += addedChars;
610
674
  }
675
+ await fetchBatch(batch);
611
676
  return result;
612
677
  }
613
678
 
@@ -628,6 +693,34 @@ async function fetchImageFillUrls(
628
693
  return result;
629
694
  }
630
695
 
696
+ /**
697
+ * Fetch CDN URLs for the given image-fill hex hashes via Figma's
698
+ * `/files/:key/images` endpoint, then mirror them to durable storage.
699
+ * Returns a Map from hex hash to durable URL. Hashes that Figma cannot
700
+ * resolve (deleted images, permission gaps) are omitted from the result.
701
+ *
702
+ * Used by `hydrate-figma-paste-images` to fill in `about:blank` placeholders
703
+ * that the local-kiwi clipboard decode path leaves behind.
704
+ */
705
+ export async function resolveImageFillRefs(
706
+ fileKey: string,
707
+ hexHashes: string[],
708
+ ): Promise<Map<string, string>> {
709
+ if (hexHashes.length === 0) return new Map();
710
+ const cdnUrls = await fetchImageFillUrls(fileKey, hexHashes);
711
+ const cdnUrlList = Object.values(cdnUrls).filter(
712
+ (u): u is string => typeof u === "string" && u.length > 0,
713
+ );
714
+ if (cdnUrlList.length === 0) return new Map();
715
+ const durableMap = await mirrorFigmaImageUrls(cdnUrlList);
716
+ const result = new Map<string, string>();
717
+ for (const [hash, cdnUrl] of Object.entries(cdnUrls)) {
718
+ const durableUrl = durableMap.get(cdnUrl);
719
+ if (durableUrl) result.set(hash, durableUrl);
720
+ }
721
+ return result;
722
+ }
723
+
631
724
  export function sanitizeTitle(
632
725
  name: string | undefined,
633
726
  fallback: string,
@@ -673,7 +766,11 @@ export async function buildScreenFilesFromFigmaNodes(
673
766
  source?: (nodeId: string, node: FigmaNode) => Record<string, unknown>;
674
767
  sourceLabel?: (nodeId: string, node: FigmaNode) => string;
675
768
  } = {},
676
- ): Promise<{ files: ImportedDesignFile[]; fidelityEntries: FidelityEntry[] }> {
769
+ ): Promise<{
770
+ files: ImportedDesignFile[];
771
+ fidelityEntries: FidelityEntry[];
772
+ missingImageFillCount: number;
773
+ }> {
677
774
  const entries = Object.entries(nodesById);
678
775
  const fallbackNodeIds = new Set<string>();
679
776
  const imageFillRefs = new Set<string>();
@@ -696,16 +793,16 @@ export async function buildScreenFilesFromFigmaNodes(
696
793
  (nodeId) => !fallbackImageUrls[nodeId],
697
794
  );
698
795
  if (missingFallbackNodeIds.length > 0) {
699
- throw new Error(
700
- `Figma could not render ${missingFallbackNodeIds.length} required fallback layer${missingFallbackNodeIds.length === 1 ? "" : "s"} (${missingFallbackNodeIds.slice(0, 5).join(", ")}${missingFallbackNodeIds.length > 5 ? ", …" : ""}). Nothing was imported so the design would not silently lose visible content. Try a smaller selection or retry the import.`,
796
+ console.warn(
797
+ `[figma-import] ${missingFallbackNodeIds.length} fallback layer(s) could not be rendered and will be omitted (${missingFallbackNodeIds.slice(0, 5).join(", ")}${missingFallbackNodeIds.length > 5 ? ", …" : ""}).`,
701
798
  );
702
799
  }
703
800
  const missingImageFillRefs = Array.from(imageFillRefs).filter(
704
801
  (imageRef) => !imageFillUrls[imageRef],
705
802
  );
706
803
  if (missingImageFillRefs.length > 0) {
707
- throw new Error(
708
- `Figma did not return ${missingImageFillRefs.length} required image fill${missingImageFillRefs.length === 1 ? "" : "s"}. Nothing was imported so the design would not silently omit imagery. Retry the import or export the affected frame from Figma.`,
804
+ console.warn(
805
+ `[figma-import] ${missingImageFillRefs.length} image fill ref(s) could not be resolved (likely from a component library file); those fills will be omitted.`,
709
806
  );
710
807
  }
711
808
  const durableUrls = await mirrorFigmaImageUrls([
@@ -763,5 +860,8 @@ export async function buildScreenFilesFromFigmaNodes(
763
860
  });
764
861
  }
765
862
 
766
- return { files, fidelityEntries };
863
+ const finalMissingCount = missingImageFillRefs.filter(
864
+ (r) => !imageFillUrls[r],
865
+ ).length;
866
+ return { files, fidelityEntries, missingImageFillCount: finalMissingCount };
767
867
  }
@@ -65,7 +65,18 @@ registerRequiredSecret({
65
65
  "User-Agent": "AgentNative/1.0",
66
66
  },
67
67
  });
68
- if (res.ok) return true;
68
+ console.log("[figma-validator] GET /v1/me status:", res.status);
69
+ if (res.ok) {
70
+ console.log("[figma-validator] token accepted");
71
+ return true;
72
+ }
73
+ let body = "";
74
+ try {
75
+ body = await res.text();
76
+ } catch {
77
+ /* ignore */
78
+ }
79
+ console.log("[figma-validator] Figma error body:", body);
69
80
  if (res.status === 401 || res.status === 403) {
70
81
  return {
71
82
  ok: false,
@@ -73,7 +84,8 @@ registerRequiredSecret({
73
84
  };
74
85
  }
75
86
  return { ok: false, error: `Figma returned ${res.status}.` };
76
- } catch {
87
+ } catch (err) {
88
+ console.log("[figma-validator] fetch threw:", err);
77
89
  return {
78
90
  ok: false,
79
91
  error: "Could not reach Figma. Check your network and try again.",
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -2,6 +2,7 @@ import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
2
2
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
3
3
  import { TeamPage } from "@agent-native/core/client/org";
4
4
  import {
5
+ AccountSettingsCard,
5
6
  SettingsTabsPage,
6
7
  useAgentSettingsTabs,
7
8
  type SettingsSearchEntry,
@@ -50,6 +51,7 @@ export default function SettingsRoute() {
50
51
 
51
52
  return (
52
53
  <SettingsTabsPage
54
+ account={<AccountSettingsCard />}
53
55
  extraTabs={agentSettingsTabs}
54
56
  generalSearchEntries={generalSearchEntries}
55
57
  general={
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-23
4
+ ---
5
+
6
+ Dispatch keeps messaging setup and destination details behind focused expandable panels.
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -179,11 +179,6 @@
179
179
  width: 100%;
180
180
  }
181
181
 
182
- .forms-ask-chat-panel [data-agent-empty-state="centered"] {
183
- justify-content: center;
184
- padding-bottom: clamp(2rem, 6vh, 4rem);
185
- }
186
-
187
182
  .forms-ask-chat-panel [data-agent-empty-state="centered"] .agent-chat-scroll {
188
183
  flex: 0 0 auto;
189
184
  min-height: 0;
@@ -1,7 +1,5 @@
1
- import { markAgentChatHomeHandoff } from "@agent-native/core/client/agent-chat";
2
1
  import { appBasePath } from "@agent-native/core/client/api-path";
3
2
  import { useAgentRouteState } from "@agent-native/core/client/navigation";
4
- import { useLocation } from "react-router";
5
3
 
6
4
  import {
7
5
  formsRoutePath,
@@ -74,8 +72,6 @@ function routerPath(path: string): string {
74
72
  }
75
73
 
76
74
  export function useNavigationState() {
77
- const location = useLocation();
78
-
79
75
  useAgentRouteState<NavigationState, NavigateCommand>({
80
76
  browserTabId: TAB_ID,
81
77
  requestSource: TAB_ID,
@@ -129,9 +125,6 @@ export function useNavigationState() {
129
125
  navigateOptions: { flushSync: true, replace: true },
130
126
  onNavigate: (_command, path) => {
131
127
  void prewarmFormsRoutePath(path);
132
- if (location.pathname === "/ask" && path !== "/ask") {
133
- markAgentChatHomeHandoff("forms");
134
- }
135
128
  },
136
129
  });
137
130
  }
@@ -1,7 +1,4 @@
1
- import {
2
- markAgentChatHomeHandoff,
3
- navigateWithAgentChatViewTransition,
4
- } from "@agent-native/core/client/agent-chat";
1
+ import { navigateWithAgentChatViewTransition } from "@agent-native/core/client/agent-chat";
5
2
  import { configureTracking } from "@agent-native/core/client/analytics";
6
3
  import { appPath } from "@agent-native/core/client/api-path";
7
4
  import {
@@ -202,9 +199,6 @@ function OpenLinkInterceptor() {
202
199
  if (!path) return;
203
200
 
204
201
  event.preventDefault();
205
- if (location.pathname === "/ask" && path !== "/ask") {
206
- markAgentChatHomeHandoff("forms");
207
- }
208
202
  navigateWithAgentChatViewTransition(navigate, path);
209
203
  }
210
204
 
@@ -2,6 +2,7 @@ import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
2
2
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
3
3
  import { TeamPage } from "@agent-native/core/client/org";
4
4
  import {
5
+ AccountSettingsCard,
5
6
  SettingsTabsPage,
6
7
  useAgentSettingsTabs,
7
8
  type SettingsSearchEntry,
@@ -44,6 +45,7 @@ export default function SettingsRoute() {
44
45
 
45
46
  return (
46
47
  <SettingsTabsPage
48
+ account={<AccountSettingsCard />}
47
49
  teamLabel={t("navigation.team")}
48
50
  extraTabs={agentSettingsTabs}
49
51
  generalSearchEntries={generalSearchEntries}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-23
4
+ ---
5
+
6
+ Ask Forms is better centered, with quieter chat history and a left-aligned New chat action.
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -2,6 +2,7 @@ import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
2
2
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
3
3
  import { TeamPage } from "@agent-native/core/client/org";
4
4
  import {
5
+ AccountSettingsCard,
5
6
  SettingsTabsPage,
6
7
  useAgentSettingsTabs,
7
8
  type SettingsSearchEntry,
@@ -44,6 +45,7 @@ export default function SettingsRoute() {
44
45
 
45
46
  return (
46
47
  <SettingsTabsPage
48
+ account={<AccountSettingsCard />}
47
49
  teamLabel={t("team.title")}
48
50
  extraTabs={agentSettingsTabs}
49
51
  generalSearchEntries={generalSearchEntries}
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -9,6 +9,7 @@ import {
9
9
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
10
10
  import { TeamPage } from "@agent-native/core/client/org";
11
11
  import {
12
+ AccountSettingsCard,
12
13
  SettingsTabsPage,
13
14
  useAgentSettingsTabs,
14
15
  type SettingsSearchEntry,
@@ -1549,7 +1550,7 @@ export function SettingsPage() {
1549
1550
  );
1550
1551
 
1551
1552
  const validSectionIds = useMemo(() => {
1552
- const ids = new Set<string>(["general", "team", "whats-new"]);
1553
+ const ids = new Set<string>(["general", "account", "team", "whats-new"]);
1553
1554
  for (const tab of extraTabs) ids.add(tab.id);
1554
1555
  return ids;
1555
1556
  }, [extraTabs]);
@@ -1578,6 +1579,7 @@ export function SettingsPage() {
1578
1579
 
1579
1580
  return (
1580
1581
  <SettingsTabsPage
1582
+ account={<AccountSettingsCard />}
1581
1583
  className="flex-1"
1582
1584
  generalLabel={t("settings.general")}
1583
1585
  teamLabel={t("settings.team")}
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -95,7 +95,7 @@ export function Layout({ children }: LayoutProps) {
95
95
  useAgentChatHomeHandoffLinks({
96
96
  storageKey: "plans",
97
97
  chatPath: "/",
98
- requireActiveHandoff: false,
98
+ requireActiveHandoff: true,
99
99
  });
100
100
  const hideAppNavigation = planDetailRoute && planReaderImmersive;
101
101
  const effectiveSidebarCollapsed = chatRoute
@@ -153,7 +153,7 @@ export function Layout({ children }: LayoutProps) {
153
153
 
154
154
  const pageContent = (
155
155
  <div className="flex h-full flex-1 flex-col overflow-hidden">
156
- {ownsToolbar ? (
156
+ {chatRoute ? null : ownsToolbar ? (
157
157
  hideAppNavigation ? null : (
158
158
  <div className="flex h-12 items-center border-b border-border px-4 md:hidden shrink-0">
159
159
  <button