@agent-native/core 0.146.8 → 0.148.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 (317) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +115 -68
  3. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +18 -6
  4. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  5. package/corpus/templates/analytics/DEVELOPING.md +50 -3
  6. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
  7. package/corpus/templates/analytics/app/i18n/index.ts +14 -29
  8. package/corpus/templates/analytics/app/i18n-data.ts +10 -10
  9. package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
  10. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
  11. package/corpus/templates/analytics/app/routes/status.$slug.tsx +4 -2
  12. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +253 -24
  13. package/corpus/templates/analytics/server/db/schema.ts +23 -0
  14. package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
  15. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
  16. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
  17. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
  18. package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
  19. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
  20. package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
  21. package/corpus/templates/analytics/server/plugins/db.ts +42 -0
  22. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
  23. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +18 -6
  24. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  25. package/corpus/templates/assets/app/i18n/index.ts +14 -29
  26. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +9 -0
  27. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
  28. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +18 -6
  29. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  30. package/corpus/templates/brain/app/i18n/index.ts +14 -29
  31. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
  32. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +18 -6
  33. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  34. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
  35. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  36. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
  37. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  38. package/corpus/templates/chat/app/i18n/index.ts +14 -29
  39. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
  40. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +18 -6
  41. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  42. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
  43. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
  44. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
  45. package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
  46. package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
  47. package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
  48. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +16 -4
  49. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +14 -5
  50. package/corpus/templates/clips/desktop/src/app.tsx +1 -1
  51. package/corpus/templates/clips/package.json +1 -0
  52. package/corpus/templates/clips/react-router.config.ts +7 -0
  53. package/corpus/templates/clips/server/plugins/auth.ts +6 -1
  54. package/corpus/templates/clips/shared/prerendered-public-paths.ts +21 -0
  55. package/corpus/templates/clips/shared/share-meta.ts +8 -3
  56. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
  57. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +18 -6
  58. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  59. package/corpus/templates/content/app/i18n/index.ts +14 -29
  60. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +18 -6
  61. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  62. package/corpus/templates/crm/app/i18n/index.ts +14 -29
  63. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
  64. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +18 -6
  65. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  66. package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
  67. package/corpus/templates/design/actions/import-code.ts +2 -43
  68. package/corpus/templates/design/app/i18n/index.ts +14 -29
  69. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
  70. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +18 -6
  71. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  72. package/corpus/templates/dispatch/app/chat-first-pages.ts +747 -0
  73. package/corpus/templates/dispatch/app/i18n-data.ts +182 -1
  74. package/corpus/templates/dispatch/app/routes/settings.tsx +62 -1
  75. package/corpus/templates/dispatch/changelog/2026-08-08-chat-first-navigation-keeps-conversations-central-while-open.md +6 -0
  76. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +9 -0
  77. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
  78. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +18 -6
  79. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  80. package/corpus/templates/factory/AGENTS.md +40 -49
  81. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
  82. package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
  83. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
  84. package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
  85. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
  86. package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
  87. package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
  88. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  89. package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
  90. package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
  91. package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
  92. package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
  93. package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
  94. package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
  95. package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
  96. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
  97. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
  98. package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
  99. package/corpus/templates/factory/app/routes/factory.tsx +203 -118
  100. package/corpus/templates/factory/changelog/2026-08-08-scheduler-health-moves-to-settings.md +6 -0
  101. package/corpus/templates/factory/server/db/schema.ts +37 -0
  102. package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
  103. package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
  104. package/corpus/templates/factory/server/triage/audit.ts +79 -0
  105. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
  106. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +18 -6
  107. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  108. package/corpus/templates/forms/app/i18n/index.ts +14 -29
  109. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
  110. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +18 -6
  111. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  112. package/corpus/templates/macros/app/i18n/index.ts +14 -29
  113. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
  114. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +18 -6
  115. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  116. package/corpus/templates/mail/app/i18n/index.ts +14 -29
  117. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +9 -0
  118. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
  119. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +18 -6
  120. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  121. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
  122. package/corpus/templates/plan/app/i18n/index.ts +14 -29
  123. package/corpus/templates/plan/app/lib/plan-document-title.ts +3 -1
  124. package/corpus/templates/plan/app/root.tsx +1 -0
  125. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
  126. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +18 -6
  127. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  128. package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
  129. package/corpus/templates/slides/actions/import-code.ts +2 -43
  130. package/corpus/templates/slides/app/i18n/index.ts +14 -29
  131. package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
  132. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +9 -0
  133. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
  134. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +18 -6
  135. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  136. package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
  137. package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
  138. package/corpus/templates/tasks/app/i18n/index.ts +14 -29
  139. package/dist/agent/engine/ai-sdk-engine.js +2 -1
  140. package/dist/agent/engine/builder-engine.d.ts +11 -1
  141. package/dist/agent/engine/builder-engine.js +11 -4
  142. package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
  143. package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
  144. package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
  145. package/dist/agent/engine/provider-endpoint-validation.js +26 -0
  146. package/dist/agent/engine/provider-env-vars.d.ts +1 -1
  147. package/dist/agent/engine/provider-env-vars.js +1 -1
  148. package/dist/agent/engine/registry.d.ts +8 -6
  149. package/dist/agent/engine/registry.js +37 -24
  150. package/dist/agent/tool-search.d.ts +1 -0
  151. package/dist/agent/tool-search.js +17 -1
  152. package/dist/automations/service.js +4 -10
  153. package/dist/brand-kit/brand-signals.d.ts +13 -0
  154. package/dist/brand-kit/brand-signals.js +15 -0
  155. package/dist/brand-kit/index.d.ts +2 -2
  156. package/dist/brand-kit/index.js +1 -1
  157. package/dist/chat-threads/store.d.ts +2 -0
  158. package/dist/chat-threads/store.js +1 -1
  159. package/dist/cli/agent-plugin.d.ts +5 -2
  160. package/dist/cli/agent-plugin.js +17 -9
  161. package/dist/cli/code-agent-executor.js +25 -5
  162. package/dist/cli/code-agent-mcp-config.d.ts +21 -0
  163. package/dist/cli/code-agent-mcp-config.js +103 -0
  164. package/dist/cli/plan-blocks.d.ts +3 -18
  165. package/dist/cli/plan-blocks.js +2 -95
  166. package/dist/cli/plan-publish-store.d.ts +2 -61
  167. package/dist/cli/plan-publish-store.js +2 -127
  168. package/dist/cli/skills-content/connection.d.ts +1 -1
  169. package/dist/cli/skills-content/connection.js +1 -52
  170. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  171. package/dist/cli/skills-content/visual-recap-skill.js +1 -553
  172. package/dist/client/NewWorkspaceAppFlow.js +4 -1
  173. package/dist/client/agent-chat/index.d.ts +4 -1
  174. package/dist/client/agent-chat/index.js +4 -1
  175. package/dist/client/agent-engine-key.d.ts +14 -4
  176. package/dist/client/agent-engine-key.js +51 -5
  177. package/dist/client/agent-page/AgentTabsPage.d.ts +1 -0
  178. package/dist/client/agent-page/AgentTabsPage.js +4 -3
  179. package/dist/client/agent-provider-catalog.d.ts +20 -0
  180. package/dist/client/agent-provider-catalog.js +100 -0
  181. package/dist/client/app-providers.d.ts +3 -1
  182. package/dist/client/app-providers.js +50 -5
  183. package/dist/client/blocks/index.d.ts +1 -0
  184. package/dist/client/blocks/index.js +1 -0
  185. package/dist/client/chat/run-recovery.js +4 -48
  186. package/dist/client/chat/runtime.js +63 -0
  187. package/dist/client/chat-first/agents-pane.d.ts +2 -0
  188. package/dist/client/chat-first/agents-pane.js +6 -0
  189. package/dist/client/chat-first/app-pane.d.ts +2 -0
  190. package/dist/client/chat-first/app-pane.js +6 -0
  191. package/dist/client/chat-first/apps-rail.d.ts +2 -0
  192. package/dist/client/chat-first/apps-rail.js +103 -0
  193. package/dist/client/chat-first/browser-pane.d.ts +2 -0
  194. package/dist/client/chat-first/browser-pane.js +64 -0
  195. package/dist/client/chat-first/chat-history.d.ts +12 -0
  196. package/dist/client/chat-first/chat-history.js +10 -0
  197. package/dist/client/chat-first/copy.d.ts +2 -0
  198. package/dist/client/chat-first/copy.js +117 -0
  199. package/dist/client/chat-first/index.d.ts +13 -0
  200. package/dist/client/chat-first/index.js +12 -0
  201. package/dist/client/chat-first/primary-nav.d.ts +8 -0
  202. package/dist/client/chat-first/primary-nav.js +6 -0
  203. package/dist/client/chat-first/session-watch-pane.d.ts +2 -0
  204. package/dist/client/chat-first/session-watch-pane.js +9 -0
  205. package/dist/client/chat-first/surface-panel.d.ts +9 -0
  206. package/dist/client/chat-first/surface-panel.js +9 -0
  207. package/dist/client/chat-first/surface-tabs.d.ts +9 -0
  208. package/dist/client/chat-first/surface-tabs.js +74 -0
  209. package/dist/client/chat-first/types.d.ts +74 -0
  210. package/dist/client/chat-first/types.js +1 -0
  211. package/dist/client/chat-first-agent-activity.d.ts +11 -0
  212. package/dist/client/chat-first-agent-activity.js +51 -0
  213. package/dist/client/chat-first-surface-panel-toggle.d.ts +5 -0
  214. package/dist/client/chat-first-surface-panel-toggle.js +6 -0
  215. package/dist/client/chat-first.d.ts +208 -0
  216. package/dist/client/chat-first.js +817 -0
  217. package/dist/client/extensions/ExtensionViewer.js +8 -1
  218. package/dist/client/i18n.d.ts +14 -0
  219. package/dist/client/i18n.js +18 -0
  220. package/dist/client/resources/McpIntegrationDialog.js +3 -2
  221. package/dist/client/resources/McpServerDetail.js +6 -11
  222. package/dist/client/resources/index.d.ts +1 -0
  223. package/dist/client/resources/index.js +1 -0
  224. package/dist/client/resources/use-mcp-servers.d.ts +20 -0
  225. package/dist/client/resources/use-mcp-servers.js +82 -46
  226. package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
  227. package/dist/client/settings/ProviderSetupForm.js +145 -0
  228. package/dist/client/settings/SettingsPanel.js +52 -49
  229. package/dist/client/settings/index.d.ts +3 -0
  230. package/dist/client/settings/index.js +3 -0
  231. package/dist/coding-tools/run-code.d.ts +5 -0
  232. package/dist/coding-tools/run-code.js +196 -36
  233. package/dist/coding-tools/tool-orchestration.d.ts +10 -0
  234. package/dist/coding-tools/tool-orchestration.js +107 -0
  235. package/dist/collab/routes.d.ts +1 -1
  236. package/dist/collab/struct-routes.d.ts +1 -1
  237. package/dist/jobs/background-automation-runner.js +19 -2
  238. package/dist/jobs/frontmatter.d.ts +9 -0
  239. package/dist/jobs/frontmatter.js +16 -0
  240. package/dist/jobs/scheduler-health.d.ts +20 -0
  241. package/dist/jobs/scheduler-health.js +86 -1
  242. package/dist/jobs/scheduler.js +46 -9
  243. package/dist/jobs/tools.d.ts +1 -1
  244. package/dist/jobs/tools.js +2 -7
  245. package/dist/localization/default-messages.d.ts +20 -0
  246. package/dist/localization/default-messages.js +25 -5
  247. package/dist/mcp-client/routes.d.ts +3 -0
  248. package/dist/mcp-client/routes.js +14 -0
  249. package/dist/notifications/routes.d.ts +1 -1
  250. package/dist/observability/routes.d.ts +3 -3
  251. package/dist/onboarding/app-profile.js +1 -1
  252. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  253. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  254. package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
  255. package/dist/secrets/routes.d.ts +6 -6
  256. package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
  257. package/dist/server/agent-chat/run-code-tools.js +3 -1
  258. package/dist/server/agent-chat-plugin.js +6 -6
  259. package/dist/server/agent-engine-api-key-route.js +43 -12
  260. package/dist/server/agents-bundle.d.ts +2 -0
  261. package/dist/server/agents-bundle.js +32 -2
  262. package/dist/server/auth.js +21 -5
  263. package/dist/server/credential-provider.d.ts +8 -2
  264. package/dist/server/credential-provider.js +14 -9
  265. package/dist/server/design-token-utils.d.ts +18 -0
  266. package/dist/server/design-token-utils.js +31 -0
  267. package/dist/server/onboarding-html.js +22 -2
  268. package/dist/server/transcribe-voice.d.ts +1 -1
  269. package/dist/shared/chat-first-app-creation.d.ts +19 -0
  270. package/dist/shared/chat-first-app-creation.js +75 -0
  271. package/dist/shared/document-title.d.ts +7 -0
  272. package/dist/shared/document-title.js +29 -0
  273. package/dist/shared/index.d.ts +2 -0
  274. package/dist/shared/index.js +2 -0
  275. package/dist/styles/chat-history-list.css +16 -0
  276. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
  277. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  278. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
  279. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  280. package/dist/templates/chat/app/i18n/index.ts +14 -29
  281. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +9 -0
  282. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  283. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -6
  284. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  285. package/dist/templates/default/app/i18n/index.ts +14 -29
  286. package/dist/templates/default/react-router.config.ts +18 -0
  287. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -6
  288. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  289. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +9 -0
  290. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  291. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  292. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -6
  293. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  294. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -0
  295. package/dist/triggers/dispatcher.js +1 -8
  296. package/docs/content/environment-variables.mdx +39 -38
  297. package/package.json +5 -4
  298. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
  299. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  300. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
  301. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  302. package/src/templates/chat/app/i18n/index.ts +14 -29
  303. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +9 -0
  304. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  305. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -6
  306. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  307. package/src/templates/default/app/i18n/index.ts +14 -29
  308. package/src/templates/default/react-router.config.ts +18 -0
  309. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -6
  310. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  311. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +9 -0
  312. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  313. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  314. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -6
  315. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  316. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -0
  317. package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
@@ -7,6 +7,7 @@ import { buildExtensionHtml } from "../../extensions/html-shell.js";
7
7
  import { extensionPath, isExtensionPathname } from "../../extensions/path.js";
8
8
  import { getThemeVars } from "../../extensions/theme.js";
9
9
  import { SESSION_REPLAY_IFRAME_ATTRIBUTE } from "../../session-replay-iframe-protocol.js";
10
+ import { normalizeDocumentTitle } from "../../shared/document-title.js";
10
11
  import { sendToAgentChat } from "../agent-chat.js";
11
12
  import { AgentToggleButton } from "../AgentPanel.js";
12
13
  import { agentNativePath, appPath } from "../api-path.js";
@@ -68,7 +69,13 @@ function readExtensionTitleSuffix() {
68
69
  return match?.[1]?.trim() || null;
69
70
  }
70
71
  function extensionDocumentTitle(name, suffix) {
71
- return suffix ? `${name} \u2014 ${suffix}` : `${name} \u2014 Extensions`;
72
+ const safeName = normalizeDocumentTitle(name, "Untitled extension");
73
+ const safeSuffix = suffix
74
+ ? normalizeDocumentTitle(suffix, "Extensions")
75
+ : null;
76
+ return safeSuffix
77
+ ? `${safeName} \u2014 ${safeSuffix}`
78
+ : `${safeName} \u2014 Extensions`;
72
79
  }
73
80
  function extensionRole(value) {
74
81
  return value === "owner" ||
@@ -3,6 +3,9 @@ import { type LocaleCode, type LocaleMetadata, type LocalePreference, type Local
3
3
  export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, localeDirection, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, type LocaleCode, type LocaleMetadata, type LocalePreference, type LocalizationPreference, } from "../localization/shared.js";
4
4
  export { getLocaleInitScript } from "../localization/server.js";
5
5
  export type LocaleMessages = Record<string, unknown>;
6
+ export type AgentNativeI18nLocaleLoader = () => Promise<LocaleMessages | {
7
+ default: LocaleMessages;
8
+ }>;
6
9
  export interface LocaleHydrationPayload {
7
10
  locale?: LocaleCode;
8
11
  preference?: LocalizationPreference;
@@ -21,6 +24,17 @@ export interface AgentNativeI18nCatalog {
21
24
  */
22
25
  supportedLocales?: readonly LocaleCode[];
23
26
  }
27
+ export declare function createAgentNativeI18nCatalog(args: {
28
+ messages: LocaleMessages;
29
+ localeLoaders: Partial<Record<LocaleCode, AgentNativeI18nLocaleLoader>>;
30
+ namespace?: string;
31
+ sourceLocale?: LocaleCode;
32
+ supportedLocales?: readonly LocaleCode[];
33
+ }): AgentNativeI18nCatalog & {
34
+ messages: LocaleMessages;
35
+ sourceLocale: LocaleCode;
36
+ loadMessages: (locale: LocaleCode) => Promise<LocaleMessages | null>;
37
+ };
24
38
  export interface AgentNativeI18nProviderProps {
25
39
  children: React.ReactNode;
26
40
  catalog?: AgentNativeI18nCatalog;
@@ -12,6 +12,24 @@ import { callAction } from "./use-action.js";
12
12
  import { cn } from "./utils.js";
13
13
  export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, localeDirection, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
14
14
  export { getLocaleInitScript } from "../localization/server.js";
15
+ function isAgentNativeI18nModuleNamespace(value) {
16
+ return Object.prototype.toString.call(value) === "[object Module]";
17
+ }
18
+ export function createAgentNativeI18nCatalog(args) {
19
+ return {
20
+ namespace: args.namespace,
21
+ sourceLocale: args.sourceLocale ?? DEFAULT_LOCALE,
22
+ messages: args.messages,
23
+ supportedLocales: args.supportedLocales,
24
+ loadMessages: async (locale) => {
25
+ const loader = args.localeLoaders[locale];
26
+ if (!loader)
27
+ return null;
28
+ const loaded = await loader();
29
+ return isAgentNativeI18nModuleNamespace(loaded) ? loaded.default : loaded;
30
+ },
31
+ };
32
+ }
15
33
  const LocaleContext = globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ ??
16
34
  (globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ =
17
35
  createContext(null));
@@ -9,7 +9,7 @@ import { useT } from "../i18n.js";
9
9
  import { cn } from "../utils.js";
10
10
  import { buildMcpOAuthStartUrl, createMcpIntegrationFormDefaults, filterMcpIntegrations, getMcpIntegrationApiFallback, getDefaultMcpIntegrations, isCustomMcpIntegrationEnabled, navigateToMcpOAuthStart, resolveMcpIntegrationScope, shouldOfferMcpIntegrationOrganizationScope, shouldOfferMcpOrganizationScope, } from "./mcp-integration-catalog.js";
11
11
  import { McpIntegrationLogo } from "./McpIntegrationLogo.js";
12
- import { formatMcpServerError, getMcpUrlValidationError, testMcpServerUrl, useMcpServers, } from "./use-mcp-servers.js";
12
+ import { formatMcpServerError, getMcpUrlValidationError, useMcpServersApi, useMcpServers, } from "./use-mcp-servers.js";
13
13
  function parseHeaderLines(text) {
14
14
  const out = {};
15
15
  for (const line of text.split(/\r?\n/)) {
@@ -64,6 +64,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
64
64
  const [error, setError] = useState(null);
65
65
  const [testResult, setTestResult] = useState(null);
66
66
  const inputRef = useRef(null);
67
+ const mcpApi = useMcpServersApi();
67
68
  const mcpServersQuery = useMcpServers();
68
69
  const defaultIntegrations = useMemo(() => integrations ?? getDefaultMcpIntegrations(), [integrations]);
69
70
  const customIntegrationEnabled = useMemo(() => isCustomMcpIntegrationEnabled(), []);
@@ -250,7 +251,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
250
251
  setError(null);
251
252
  setTestResult(null);
252
253
  try {
253
- const res = await testMcpServerUrl(trimmedUrl, parseHeaderLines(headersText));
254
+ const res = await mcpApi.test(trimmedUrl, parseHeaderLines(headersText));
254
255
  setTestResult(res.ok
255
256
  ? {
256
257
  ok: true,
@@ -9,9 +9,10 @@ import { IconPlugConnected, IconAlertTriangle, IconLoader2, IconCheck, IconTestP
9
9
  * delete and recreate if they need to change a URL or headers.
10
10
  */
11
11
  import { useState } from "react";
12
- import { agentNativePath } from "../api-path.js";
13
12
  import { cn } from "../utils.js";
13
+ import { useMcpServersApi, } from "./use-mcp-servers.js";
14
14
  export function McpServerDetail({ server }) {
15
+ const mcpApi = useMcpServersApi();
15
16
  const [testing, setTesting] = useState(false);
16
17
  const [testResult, setTestResult] = useState(null);
17
18
  const headers = server.headers ? Object.keys(server.headers) : [];
@@ -19,17 +20,11 @@ export function McpServerDetail({ server }) {
19
20
  setTesting(true);
20
21
  setTestResult(null);
21
22
  try {
22
- // We don't have the real header values client-side (redacted). The
23
- // test-existing endpoint uses the stored headers, but there's no
24
- // convenient way to hit it from here without the server id + scope,
25
- // which we do have — so wire that up.
26
- const res = await fetch(agentNativePath(`/_agent-native/mcp/servers/${encodeURIComponent(server.id)}/test?scope=${server.scope}`), {
27
- method: "POST",
28
- credentials: "include",
29
- headers: { "Content-Type": "application/json" },
23
+ const result = await mcpApi.testExisting({
24
+ id: server.id,
25
+ scope: server.scope,
30
26
  });
31
- const body = (await res.json().catch(() => ({})));
32
- setTestResult(body.ok ? body : { ok: false, error: body.error });
27
+ setTestResult(result.ok ? result : { ok: false, error: result.error });
33
28
  }
34
29
  catch (err) {
35
30
  setTestResult({ ok: false, error: err?.message ?? String(err) });
@@ -7,3 +7,4 @@ export * from "./use-builtin-capabilities.js";
7
7
  export { DEFAULT_MCP_INTEGRATIONS, filterMcpIntegrations, findMcpIntegrationForText, getDefaultMcpIntegrations, isCustomMcpIntegrationEnabled, isMcpIntegrationCatalogAvailable, mergeDefaultMcpIntegrations, type DefaultMcpIntegration, } from "./mcp-integration-catalog.js";
8
8
  export { McpIntegrationDialog, type McpIntegrationDialogProps, } from "./McpIntegrationDialog.js";
9
9
  export { McpConnectionSuggestion, type McpConnectionSuggestionProps, type McpConnectionSuggestionVariant, } from "./McpConnectionSuggestion.js";
10
+ export { McpServersApiProvider, useMcpServersApi, useMcpServers, useCreateMcpServer, useDeleteMcpServer, useReconnectMcpServer, testMcpServerUrl, type CreateMcpServerArgs, type McpServer, type McpServerScope, type McpServersApi, type McpServersList, type ReconnectMcpServerArgs, type TestMcpUrlResult, } from "./use-mcp-servers.js";
@@ -7,3 +7,4 @@ export * from "./use-builtin-capabilities.js";
7
7
  export { DEFAULT_MCP_INTEGRATIONS, filterMcpIntegrations, findMcpIntegrationForText, getDefaultMcpIntegrations, isCustomMcpIntegrationEnabled, isMcpIntegrationCatalogAvailable, mergeDefaultMcpIntegrations, } from "./mcp-integration-catalog.js";
8
8
  export { McpIntegrationDialog, } from "./McpIntegrationDialog.js";
9
9
  export { McpConnectionSuggestion, } from "./McpConnectionSuggestion.js";
10
+ export { McpServersApiProvider, useMcpServersApi, useMcpServers, useCreateMcpServer, useDeleteMcpServer, useReconnectMcpServer, testMcpServerUrl, } from "./use-mcp-servers.js";
@@ -7,6 +7,7 @@
7
7
  * endpoints so the Workspace UI can list, create, and delete them with the
8
8
  * same keys/invalidations the old Settings panel used.
9
9
  */
10
+ import { type ReactNode } from "react";
10
11
  export type McpServerScope = "user" | "org";
11
12
  export interface McpServer {
12
13
  id: string;
@@ -37,6 +38,25 @@ export interface McpServersList {
37
38
  orgId: string | null;
38
39
  role: string | null;
39
40
  }
41
+ export interface McpServersApi {
42
+ list: () => Promise<McpServersList>;
43
+ create: (args: CreateMcpServerArgs) => Promise<McpServer>;
44
+ delete: (args: {
45
+ id: string;
46
+ scope: McpServerScope;
47
+ }) => Promise<void>;
48
+ reconnect: (args: ReconnectMcpServerArgs) => Promise<void>;
49
+ test: (url: string, headers?: Record<string, string>) => Promise<TestMcpUrlResult>;
50
+ testExisting: (args: {
51
+ id: string;
52
+ scope: McpServerScope;
53
+ }) => Promise<TestMcpUrlResult>;
54
+ }
55
+ export declare function McpServersApiProvider({ api, children, }: {
56
+ api: McpServersApi;
57
+ children: ReactNode;
58
+ }): import("react").FunctionComponentElement<import("react").ProviderProps<McpServersApi | null>>;
59
+ export declare function useMcpServersApi(): McpServersApi;
40
60
  export declare function useMcpServers(): import("@tanstack/react-query").UseQueryResult<NoInfer<McpServersList>, Error>;
41
61
  export interface CreateMcpServerArgs {
42
62
  scope: McpServerScope;
@@ -8,54 +8,104 @@
8
8
  * same keys/invalidations the old Settings panel used.
9
9
  */
10
10
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
11
+ import { createContext, createElement, useContext, } from "react";
11
12
  import { agentNativePath } from "../api-path.js";
12
13
  const ENDPOINT = agentNativePath("/_agent-native/mcp/servers");
13
14
  const LIST_KEY = ["mcp-servers"];
15
+ const McpServersApiContext = createContext(null);
16
+ export function McpServersApiProvider({ api, children, }) {
17
+ return createElement(McpServersApiContext.Provider, { value: api }, children);
18
+ }
19
+ export function useMcpServersApi() {
20
+ return useContext(McpServersApiContext) ?? defaultMcpServersApi;
21
+ }
22
+ async function listMcpServers() {
23
+ const res = await fetch(ENDPOINT, { credentials: "include" });
24
+ if (!res.ok)
25
+ throw new Error(`Failed to load (${res.status})`);
26
+ return (await res.json());
27
+ }
28
+ async function createMcpServer(args) {
29
+ const res = await fetch(ENDPOINT, {
30
+ method: "POST",
31
+ credentials: "include",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify(args),
34
+ });
35
+ const body = (await res.json().catch((error) => {
36
+ throw new Error("MCP create returned invalid JSON.", { cause: error });
37
+ }));
38
+ if (!res.ok || !body.ok) {
39
+ throw new Error(body.error || `Create failed (${res.status})`);
40
+ }
41
+ return body.server;
42
+ }
43
+ async function deleteMcpServer(args) {
44
+ const res = await fetch(`${ENDPOINT}/${encodeURIComponent(args.id)}?scope=${args.scope}`, {
45
+ method: "DELETE",
46
+ credentials: "include",
47
+ headers: { "Content-Type": "application/json" },
48
+ });
49
+ const body = (await res.json().catch((error) => {
50
+ throw new Error("MCP delete returned invalid JSON.", { cause: error });
51
+ }));
52
+ if (!res.ok || !body.ok) {
53
+ throw new Error(body.error || `Delete failed (${res.status})`);
54
+ }
55
+ }
56
+ async function reconnectMcpServer(args) {
57
+ const res = await fetch(reconnectMcpServerUrl(args), {
58
+ method: "POST",
59
+ credentials: "include",
60
+ });
61
+ const body = await readMcpMutationBody(res);
62
+ const error = typeof body?.error === "string" && body.error.trim()
63
+ ? body.error.trim()
64
+ : undefined;
65
+ if (!res.ok || body?.ok !== true) {
66
+ throw new Error(error || `Reconnect failed (${res.status})`);
67
+ }
68
+ }
69
+ async function testExistingMcpServer(args) {
70
+ const res = await fetch(agentNativePath(`/_agent-native/mcp/servers/${encodeURIComponent(args.id)}/test?scope=${args.scope}`), {
71
+ method: "POST",
72
+ credentials: "include",
73
+ headers: { "Content-Type": "application/json" },
74
+ });
75
+ const body = (await res.json().catch((error) => {
76
+ throw new Error("MCP test returned invalid JSON.", { cause: error });
77
+ }));
78
+ return res.ok ? body : { ok: false, error: body.error };
79
+ }
80
+ const defaultMcpServersApi = {
81
+ list: listMcpServers,
82
+ create: createMcpServer,
83
+ delete: deleteMcpServer,
84
+ reconnect: reconnectMcpServer,
85
+ test: testMcpServerUrl,
86
+ testExisting: testExistingMcpServer,
87
+ };
14
88
  export function useMcpServers() {
89
+ const api = useMcpServersApi();
15
90
  return useQuery({
16
91
  queryKey: LIST_KEY,
17
- queryFn: async () => {
18
- const res = await fetch(ENDPOINT, { credentials: "include" });
19
- if (!res.ok)
20
- throw new Error(`Failed to load (${res.status})`);
21
- return (await res.json());
22
- },
92
+ queryFn: api.list,
23
93
  staleTime: 10_000,
24
94
  });
25
95
  }
26
96
  export function useCreateMcpServer() {
27
97
  const qc = useQueryClient();
98
+ const api = useMcpServersApi();
28
99
  return useMutation({
29
- mutationFn: async (args) => {
30
- const res = await fetch(ENDPOINT, {
31
- method: "POST",
32
- credentials: "include",
33
- headers: { "Content-Type": "application/json" },
34
- body: JSON.stringify(args),
35
- });
36
- const body = (await res.json().catch(() => ({})));
37
- if (!res.ok || !body.ok) {
38
- throw new Error(body.error || `Create failed (${res.status})`);
39
- }
40
- return body.server;
41
- },
100
+ mutationFn: api.create,
42
101
  onSuccess: () => qc.invalidateQueries({ queryKey: LIST_KEY }),
43
102
  });
44
103
  }
45
104
  export function useDeleteMcpServer() {
46
105
  const qc = useQueryClient();
106
+ const api = useMcpServersApi();
47
107
  return useMutation({
48
- mutationFn: async (args) => {
49
- const res = await fetch(`${ENDPOINT}/${encodeURIComponent(args.id)}?scope=${args.scope}`, {
50
- method: "DELETE",
51
- credentials: "include",
52
- headers: { "Content-Type": "application/json" },
53
- });
54
- const body = (await res.json().catch(() => ({})));
55
- if (!res.ok || !body.ok) {
56
- throw new Error(body.error || `Delete failed (${res.status})`);
57
- }
58
- },
108
+ mutationFn: api.delete,
59
109
  onSuccess: () => qc.invalidateQueries({ queryKey: LIST_KEY }),
60
110
  });
61
111
  }
@@ -75,23 +125,9 @@ function reconnectMcpServerUrl(args) {
75
125
  }
76
126
  export function useReconnectMcpServer() {
77
127
  const qc = useQueryClient();
128
+ const api = useMcpServersApi();
78
129
  return useMutation({
79
- mutationFn: async (args) => {
80
- const res = await fetch(reconnectMcpServerUrl(args), {
81
- method: "POST",
82
- credentials: "include",
83
- });
84
- const body = await readMcpMutationBody(res);
85
- const error = typeof body?.error === "string" && body.error.trim()
86
- ? body.error.trim()
87
- : undefined;
88
- if (!res.ok) {
89
- throw new Error(error || `Reconnect failed (${res.status})`);
90
- }
91
- if (body?.ok !== true) {
92
- throw new Error(error || "Reconnect failed");
93
- }
94
- },
130
+ mutationFn: api.reconnect,
95
131
  onSuccess: () => qc.invalidateQueries({ queryKey: LIST_KEY }),
96
132
  });
97
133
  }
@@ -0,0 +1,21 @@
1
+ import { type AgentProviderId, type AgentProviderOption } from "../agent-provider-catalog.js";
2
+ export interface AgentProviderPickerProps {
3
+ value: AgentProviderId;
4
+ onChange: (provider: AgentProviderId) => void;
5
+ options?: readonly AgentProviderOption[];
6
+ configuredProviders?: ReadonlySet<AgentProviderId>;
7
+ disabled?: boolean;
8
+ className?: string;
9
+ layout?: "compact" | "page";
10
+ }
11
+ export declare function AgentProviderPicker({ value, onChange, options, configuredProviders, disabled, className, layout, }: AgentProviderPickerProps): import("react").JSX.Element;
12
+ export interface AgentProviderSetupFormProps {
13
+ initialProvider?: AgentProviderId;
14
+ configuredProviders?: ReadonlySet<AgentProviderId>;
15
+ onConnected?: (provider: AgentProviderId) => void;
16
+ scope?: "user" | "org";
17
+ layout?: "compact" | "page";
18
+ showTitle?: boolean;
19
+ className?: string;
20
+ }
21
+ export declare function AgentProviderSetupForm({ initialProvider, configuredProviders, onConnected, scope, layout, showTitle, className, }: AgentProviderSetupFormProps): import("react").JSX.Element;
@@ -0,0 +1,145 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@agent-native/toolkit/ui/command";
3
+ import { IconBolt, IconCheck, IconChevronDown, IconCloud, IconDeviceDesktop, IconExternalLink, IconKey, IconLoader2, IconRoute, IconServer2, } from "@tabler/icons-react";
4
+ import { useEffect, useState } from "react";
5
+ import { saveAgentEngineProviderSettings, setAgentEngineProvider, } from "../agent-engine-key.js";
6
+ import { AGENT_PROVIDER_CATALOG, getAgentProviderOption, } from "../agent-provider-catalog.js";
7
+ import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
8
+ import { useT } from "../i18n.js";
9
+ import { cn } from "../utils.js";
10
+ function ProviderIcon({ option, size = 15, }) {
11
+ if (option.kind === "local")
12
+ return _jsx(IconDeviceDesktop, { size: size });
13
+ if (option.kind === "gateway")
14
+ return _jsx(IconRoute, { size: size });
15
+ return _jsx(IconCloud, { size: size });
16
+ }
17
+ export function AgentProviderPicker({ value, onChange, options = AGENT_PROVIDER_CATALOG, configuredProviders, disabled = false, className, layout = "compact", }) {
18
+ const t = useT();
19
+ const [open, setOpen] = useState(false);
20
+ const active = getAgentProviderOption(value);
21
+ const isPage = layout === "page";
22
+ return (_jsxs("div", { className: cn("space-y-1.5", className), children: [_jsx("p", { className: cn("font-medium text-foreground", isPage ? "text-sm" : "text-[11px]"), children: t("agentPanel.chooseProvider", {
23
+ defaultValue: "Choose a provider",
24
+ }) }), _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: disabled, "aria-label": t("agentPanel.chooseProvider", {
25
+ defaultValue: "Choose a provider",
26
+ }), className: cn("flex w-full items-center gap-2 rounded-md border border-border bg-background text-start text-foreground transition-colors hover:bg-accent/40 disabled:cursor-not-allowed disabled:opacity-60", isPage ? "min-h-10 px-3 text-sm" : "min-h-9 px-2.5 text-[12px]"), children: [_jsx("span", { className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-accent/50 text-muted-foreground", children: _jsx(ProviderIcon, { option: active, size: isPage ? 16 : 14 }) }), _jsx("span", { className: "min-w-0 flex-1", children: _jsx("span", { className: "block truncate font-medium", children: active.label }) }), _jsx(IconChevronDown, { size: isPage ? 16 : 14, className: "shrink-0 text-muted-foreground" })] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 6, className: "w-[min(390px,calc(100vw-2rem))] p-0", children: _jsxs(Command, { filter: (candidate, search) => candidate.toLowerCase().includes(search.toLowerCase()) ? 1 : 0, children: [_jsx(CommandInput, { placeholder: t("agentPanel.searchProviders", {
27
+ defaultValue: "Search providers...",
28
+ }) }), _jsxs(CommandList, { className: "max-h-[min(440px,calc(100vh-8rem))]", children: [_jsx(CommandEmpty, { children: t("agentPanel.noProvidersFound", {
29
+ defaultValue: "No providers found.",
30
+ }) }), _jsx(CommandGroup, { heading: t("agentPanel.availableProviders", {
31
+ defaultValue: "Available providers",
32
+ }), children: options.map((option) => {
33
+ const configured = configuredProviders?.has(option.id);
34
+ return (_jsxs(CommandItem, { value: `${option.label} ${option.description} ${option.key ?? "local"}`, onSelect: () => {
35
+ onChange(option.id);
36
+ setOpen(false);
37
+ }, className: "items-center gap-2.5 py-2.5", children: [_jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-accent/50 text-muted-foreground", children: _jsx(ProviderIcon, { option: option }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate font-medium", children: option.label }), configured ? (_jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 text-[10px] font-medium text-primary", children: [_jsx(IconCheck, { size: 11 }), t("agentPanel.configured", {
38
+ defaultValue: "Configured",
39
+ })] })) : null] }), _jsx("span", { className: "block truncate text-[11px] text-muted-foreground", children: option.description })] }), _jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: option.key
40
+ ? t("agentPanel.apiKey", {
41
+ defaultValue: "API key",
42
+ })
43
+ : t("agentPanel.localRuntime", {
44
+ defaultValue: "Local",
45
+ }) }), _jsx(IconCheck, { size: 15, className: cn("shrink-0", option.id === value ? "opacity-100" : "opacity-0") })] }, option.id));
46
+ }) })] })] }) })] })] }));
47
+ }
48
+ export function AgentProviderSetupForm({ initialProvider = "anthropic", configuredProviders, onConnected, scope = "user", layout = "compact", showTitle = true, className, }) {
49
+ const t = useT();
50
+ const isPage = layout === "page";
51
+ const [provider, setProvider] = useState(initialProvider);
52
+ const [apiKey, setApiKey] = useState("");
53
+ const [model, setModel] = useState("");
54
+ const [endpoint, setEndpoint] = useState("");
55
+ const [endpointOpen, setEndpointOpen] = useState(initialProvider === "ollama");
56
+ const [saving, setSaving] = useState(false);
57
+ const [saved, setSaved] = useState(false);
58
+ const [error, setError] = useState(null);
59
+ const active = getAgentProviderOption(provider);
60
+ useEffect(() => {
61
+ setModel(active.defaultModel);
62
+ setApiKey("");
63
+ setEndpoint("");
64
+ setEndpointOpen(provider === "ollama");
65
+ setError(null);
66
+ setSaved(false);
67
+ }, [active.defaultModel, provider]);
68
+ const handleProviderChange = (nextProvider) => {
69
+ setProvider(nextProvider);
70
+ };
71
+ const handleSave = async () => {
72
+ if (saving)
73
+ return;
74
+ if (active.key && !apiKey.trim()) {
75
+ setError(t("agentPanel.enterApiKey", {
76
+ defaultValue: `Enter your ${active.label} API key.`,
77
+ }));
78
+ return;
79
+ }
80
+ setSaving(true);
81
+ setError(null);
82
+ try {
83
+ if (active.key || endpoint.trim()) {
84
+ await saveAgentEngineProviderSettings({
85
+ provider,
86
+ ...(active.key ? { key: active.key } : {}),
87
+ ...(apiKey.trim() ? { apiKey } : {}),
88
+ ...(endpoint.trim() ? { baseUrl: endpoint } : {}),
89
+ scope,
90
+ });
91
+ }
92
+ await setAgentEngineProvider({
93
+ provider,
94
+ model: model.trim() || active.defaultModel,
95
+ });
96
+ setApiKey("");
97
+ setSaved(true);
98
+ onConnected?.(provider);
99
+ window.setTimeout(() => setSaved(false), 2200);
100
+ }
101
+ catch (err) {
102
+ setError(err instanceof Error
103
+ ? err.message
104
+ : t("agentPanel.providerSetupFailed", {
105
+ defaultValue: "Could not configure this provider.",
106
+ }));
107
+ }
108
+ finally {
109
+ setSaving(false);
110
+ }
111
+ };
112
+ const isConfigured = configuredProviders?.has(provider) || saved;
113
+ const modelInputVisible = active.supportsCustomModel;
114
+ const endpointVisible = active.supportsEndpoint;
115
+ return (_jsxs("form", { className: cn("space-y-3", className), onSubmit: (event) => {
116
+ event.preventDefault();
117
+ void handleSave();
118
+ }, children: [showTitle ? (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(IconKey, { size: isPage ? 16 : 13, className: "text-muted-foreground" }), _jsx("h4", { className: cn("font-medium text-foreground", isPage ? "text-sm" : "text-[11px]"), children: t("agentPanel.addOwnKeys", {
119
+ defaultValue: "Custom keys",
120
+ }) })] }), _jsx("p", { className: cn("mt-1 leading-relaxed text-muted-foreground", isPage ? "text-xs" : "text-[11px]"), children: t("agentPanel.configureProviderKeys", {
121
+ defaultValue: "Choose a provider.",
122
+ }) })] })) : null, _jsx(AgentProviderPicker, { value: provider, onChange: handleProviderChange, configuredProviders: configuredProviders, disabled: saving, layout: layout }), _jsxs("div", { className: cn("rounded-md border border-border bg-accent/20", isPage ? "space-y-3 p-3.5" : "space-y-2.5 p-2.5"), children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-md bg-background text-muted-foreground", children: active.kind === "local" ? (_jsx(IconDeviceDesktop, { size: isPage ? 15 : 13 })) : active.kind === "gateway" ? (_jsx(IconRoute, { size: isPage ? 15 : 13 })) : (_jsx(IconBolt, { size: isPage ? 15 : 13 })) }), _jsx("div", { className: "min-w-0 flex-1", title: active.description, children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: cn("font-medium text-foreground", isPage ? "text-sm" : "text-[12px]"), children: active.label }), isConfigured ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] font-medium text-primary", children: [_jsx(IconCheck, { size: 11 }), t("agentPanel.configured", {
123
+ defaultValue: "Configured",
124
+ })] })) : null] }) })] }), active.key ? (_jsxs("label", { className: "block space-y-1.5", children: [_jsx("span", { className: cn("font-medium text-foreground", isPage ? "text-xs" : "text-[11px]"), children: t("agentPanel.apiKey", { defaultValue: "API key" }) }), _jsx("input", { type: "password", value: apiKey, autoComplete: "off", spellCheck: false, placeholder: active.placeholder, disabled: saving, onChange: (event) => {
125
+ setApiKey(event.target.value);
126
+ if (error)
127
+ setError(null);
128
+ }, className: cn("w-full rounded-md border border-input bg-background text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background placeholder:text-muted-foreground/50", isPage ? "h-10 px-3 text-sm" : "h-8 px-2.5 text-[12px]") })] })) : (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-border/70 bg-background/60 px-2.5 py-2", children: [_jsx(IconServer2, { size: isPage ? 15 : 13, className: "mt-0.5 shrink-0 text-muted-foreground" }), _jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: t("agentPanel.noApiKeyNeeded", {
129
+ defaultValue: "No API key required.",
130
+ }) })] })), modelInputVisible ? (_jsxs("label", { className: "block space-y-1.5", children: [_jsx("span", { className: cn("font-medium text-foreground", isPage ? "text-xs" : "text-[11px]"), children: t("agentPanel.modelId", { defaultValue: "Model ID" }) }), _jsx("input", { type: "text", value: model, list: `agent-provider-models-${provider}`, disabled: saving, spellCheck: false, autoComplete: "off", placeholder: active.defaultModel, onChange: (event) => setModel(event.target.value), className: cn("w-full rounded-md border border-input bg-background text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background placeholder:text-muted-foreground/50", isPage ? "h-10 px-3 text-sm" : "h-8 px-2.5 text-[12px]") }), _jsx("datalist", { id: `agent-provider-models-${provider}`, children: active.supportedModels.map((modelOption) => (_jsx("option", { value: modelOption }, modelOption))) })] })) : null, endpointVisible ? (_jsxs("div", { className: "border-t border-border/70 pt-2", children: [_jsxs("button", { type: "button", onClick: () => setEndpointOpen((open) => !open), className: "flex w-full items-center justify-between gap-2 text-start text-[11px] font-medium text-foreground", "aria-expanded": endpointOpen, title: provider === "ollama"
131
+ ? "Defaults to Ollama at http://localhost:11434."
132
+ : "Use for LiteLLM or another OpenAI-compatible gateway.", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx(IconChevronDown, { size: 13, className: cn("text-muted-foreground transition-transform", !endpointOpen && "-rotate-90") }), t("agentPanel.endpointUrl", {
133
+ defaultValue: "Endpoint URL",
134
+ })] }), _jsx("span", { className: "text-[10px] font-normal text-muted-foreground", children: t("agentPanel.optional", { defaultValue: "Optional" }) })] }), endpointOpen ? (_jsx("div", { className: "mt-2 space-y-1.5", children: _jsx("input", { type: "url", value: endpoint, disabled: saving, spellCheck: false, autoComplete: "off", placeholder: active.endpointPlaceholder, onChange: (event) => setEndpoint(event.target.value), className: cn("w-full rounded-md border border-input bg-background text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background placeholder:text-muted-foreground/50", isPage ? "h-10 px-3 text-sm" : "h-8 px-2.5 text-[12px]") }) })) : null] })) : null, _jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-0.5", children: [_jsx("button", { type: "submit", disabled: saving || Boolean(active.key && !apiKey.trim()), className: cn("inline-flex items-center justify-center gap-1.5 rounded-md bg-foreground font-medium text-background transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50", isPage ? "h-9 px-3 text-xs" : "h-8 px-3 text-[11px]"), children: saving ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: isPage ? 14 : 11, className: "animate-spin" }), t("agentPanel.savingProvider", {
135
+ defaultValue: "Saving...",
136
+ })] })) : saved ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { size: isPage ? 14 : 11 }), t("agentPanel.providerSaved", {
137
+ defaultValue: "Connected",
138
+ })] })) : provider === "ollama" ? (t("agentPanel.useProvider", {
139
+ provider: active.label,
140
+ defaultValue: `Use ${active.label}`,
141
+ })) : (t("agentPanel.saveAndUseProvider", {
142
+ provider: active.label,
143
+ defaultValue: `Save and use ${active.label}`,
144
+ })) }), active.docsUrl ? (_jsxs("a", { href: active.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[10px] font-medium text-muted-foreground no-underline transition-colors hover:bg-accent/40 hover:text-foreground", children: [t("agentPanel.getApiKey", { defaultValue: "Get an API key" }), _jsx(IconExternalLink, { size: 11 })] })) : null] }), error ? (_jsx("p", { className: "text-[11px] leading-relaxed text-destructive", children: error })) : null] })] }));
145
+ }