@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
@@ -75,14 +75,16 @@ export declare function resolveDelegatedRunModel(engine: ModelResolvableEngine,
75
75
  * verbatim instead of normalized against the built-in catalog.
76
76
  *
77
77
  * `normalizeModelForEngine` honors a live engine's `preserveCustomModels`, but
78
- * that flag is only set on an AI SDK engine INSTANCE when the OpenAI provider
79
- * is pointed at an OpenAI-compatible gateway (a custom base URL — e.g. Ollama
80
- * Cloud or LiteLLM), whose model IDs are not in the built-in OpenAI catalog.
78
+ * that flag is only set on an AI SDK engine INSTANCE when the provider is
79
+ * Ollama, or when OpenAI is pointed at an OpenAI-compatible gateway (a custom
80
+ * base URL — e.g. Ollama Cloud or LiteLLM), whose model IDs are not in the
81
+ * built-in catalogs.
81
82
  * The static registry entry the settings actions pass to
82
83
  * `normalizeModelForEngine` cannot carry that runtime flag, so this async
83
- * helper reproduces the same decision — `ai-sdk:openai` AND a resolved base URL
84
- * — from the request's stored/deploy config. First-party OpenAI (no gateway)
85
- * returns false so an unknown/invalid model still normalizes to a supported one.
84
+ * helper reproduces the same decision from the request's stored/deploy config.
85
+ * Ollama always returns true because its local model inventory is user-defined;
86
+ * first-party OpenAI (no gateway) returns false so an unknown/invalid model
87
+ * still normalizes to a supported one.
86
88
  */
87
89
  export declare function resolveEnginePreservesCustomModels(entry: Pick<AgentEngineEntry, "name">): Promise<boolean>;
88
90
  /**
@@ -11,7 +11,8 @@ import { createRequire } from "node:module";
11
11
  import { assertCredentialStoreReadable, canUseDeployCredentialFallbackForRequest, getBuilderCredentialAuthFailure, getProviderCredentialAuthFailure, readDeployCredentialEnv, resolveBuilderCredentialsDetailed, resolveSecret, } from "../../server/credential-provider.js";
12
12
  import { getSetting } from "../../settings/store.js";
13
13
  import { getAgentAppModelDefaultForCurrentRequest } from "../app-model-defaults.js";
14
- import { normalizeOpenAiBaseUrl, OPENAI_BASE_URL_ENV_VAR, } from "./openai-compatible-endpoint.js";
14
+ import { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, } from "./openai-compatible-endpoint.js";
15
+ import { validateProviderBaseUrl } from "./provider-endpoint-validation.js";
15
16
  const require = createRequire(import.meta.url);
16
17
  const _registry = new Map();
17
18
  const _packageAvailabilityCache = new Map();
@@ -234,20 +235,24 @@ export function resolveDelegatedRunModel(engine, options) {
234
235
  * verbatim instead of normalized against the built-in catalog.
235
236
  *
236
237
  * `normalizeModelForEngine` honors a live engine's `preserveCustomModels`, but
237
- * that flag is only set on an AI SDK engine INSTANCE when the OpenAI provider
238
- * is pointed at an OpenAI-compatible gateway (a custom base URL — e.g. Ollama
239
- * Cloud or LiteLLM), whose model IDs are not in the built-in OpenAI catalog.
238
+ * that flag is only set on an AI SDK engine INSTANCE when the provider is
239
+ * Ollama, or when OpenAI is pointed at an OpenAI-compatible gateway (a custom
240
+ * base URL — e.g. Ollama Cloud or LiteLLM), whose model IDs are not in the
241
+ * built-in catalogs.
240
242
  * The static registry entry the settings actions pass to
241
243
  * `normalizeModelForEngine` cannot carry that runtime flag, so this async
242
- * helper reproduces the same decision — `ai-sdk:openai` AND a resolved base URL
243
- * — from the request's stored/deploy config. First-party OpenAI (no gateway)
244
- * returns false so an unknown/invalid model still normalizes to a supported one.
244
+ * helper reproduces the same decision from the request's stored/deploy config.
245
+ * Ollama always returns true because its local model inventory is user-defined;
246
+ * first-party OpenAI (no gateway) returns false so an unknown/invalid model
247
+ * still normalizes to a supported one.
245
248
  */
246
249
  export async function resolveEnginePreservesCustomModels(entry) {
250
+ if (entry.name === "ai-sdk:ollama")
251
+ return true;
247
252
  if (entry.name !== "ai-sdk:openai")
248
253
  return false;
249
254
  try {
250
- return Boolean(await resolveOpenAiBaseUrl());
255
+ return Boolean(await resolveProviderBaseUrl(OPENAI_BASE_URL_ENV_VAR));
251
256
  }
252
257
  catch {
253
258
  return false;
@@ -493,19 +498,22 @@ function engineCreateConfig(entry, apiKey, extra) {
493
498
  ...(extra ?? {}),
494
499
  };
495
500
  }
496
- async function resolveOpenAiBaseUrl() {
497
- let raw = null;
498
- try {
499
- raw = await resolveSecret(OPENAI_BASE_URL_ENV_VAR);
500
- }
501
- catch {
502
- raw = null;
503
- }
504
- if (!raw &&
505
- canUseDeployCredentialFallbackForRequest(OPENAI_BASE_URL_ENV_VAR)) {
506
- raw = readDeployCredentialEnv(OPENAI_BASE_URL_ENV_VAR);
507
- }
508
- return raw ? normalizeOpenAiBaseUrl(raw) : undefined;
501
+ async function resolveProviderBaseUrl(envVar) {
502
+ const raw = await resolveSecret(envVar);
503
+ if (!raw && canUseDeployCredentialFallbackForRequest(envVar)) {
504
+ const deployValue = readDeployCredentialEnv(envVar);
505
+ if (!deployValue)
506
+ return undefined;
507
+ return validateProviderBaseUrl(deployValue, {
508
+ allowPrivate: true,
509
+ });
510
+ }
511
+ return raw
512
+ ? validateProviderBaseUrl(raw, {
513
+ allowLocalOllama: envVar === OLLAMA_BASE_URL_ENV_VAR &&
514
+ process.env.NODE_ENV === "development",
515
+ })
516
+ : undefined;
509
517
  }
510
518
  /**
511
519
  * A Builder connection we could not read is not a missing connection. Throwing
@@ -609,12 +617,17 @@ async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCr
609
617
  : undefined;
610
618
  }
611
619
  }
612
- if (entry.name === "ai-sdk:openai") {
620
+ if (entry.name === "ai-sdk:openai" || entry.name === "ai-sdk:ollama") {
613
621
  if (typeof safeExtra.baseURL === "string" && safeExtra.baseUrl == null) {
614
- safeExtra.baseUrl = normalizeOpenAiBaseUrl(safeExtra.baseURL);
622
+ safeExtra.baseUrl = await validateProviderBaseUrl(safeExtra.baseURL, {
623
+ allowLocalOllama: entry.name === "ai-sdk:ollama" &&
624
+ process.env.NODE_ENV === "development",
625
+ });
615
626
  }
616
627
  if (safeExtra.baseUrl == null) {
617
- const baseUrl = await resolveOpenAiBaseUrl();
628
+ const baseUrl = await resolveProviderBaseUrl(entry.name === "ai-sdk:ollama"
629
+ ? OLLAMA_BASE_URL_ENV_VAR
630
+ : OPENAI_BASE_URL_ENV_VAR);
618
631
  if (baseUrl)
619
632
  safeExtra.baseUrl = baseUrl;
620
633
  }
@@ -4,6 +4,7 @@ type ToolSearchArgs = {
4
4
  query?: unknown;
5
5
  limit?: unknown;
6
6
  includeSchemas?: unknown;
7
+ readOnlyOnly?: unknown;
7
8
  };
8
9
  type ToolParameterSummary = {
9
10
  name: string;
@@ -23,6 +23,10 @@ export function createToolSearchEntry(getRegistry, options = {}) {
23
23
  type: "boolean",
24
24
  description: "When true, include each matching tool's full input schema. Default false.",
25
25
  },
26
+ readOnlyOnly: {
27
+ type: "boolean",
28
+ description: "When true, return tools whose current policy is read-only or can classify the supplied arguments as read-only. The bounded orchestration host rechecks conditional policies before every call.",
29
+ },
26
30
  },
27
31
  },
28
32
  },
@@ -44,11 +48,13 @@ export function searchToolRegistry(registry, args = {}, options = {}) {
44
48
  // actually needs. A query switches to ranked search with parameter details.
45
49
  const listAll = query.length === 0;
46
50
  const includeSchemas = !listAll && parseBoolean(args.includeSchemas);
51
+ const readOnlyOnly = parseBoolean(args.readOnlyOnly);
47
52
  const limit = parseLimit(args.limit, options.defaultLimit ?? DEFAULT_LIMIT, options.maxLimit ?? MAX_LIMIT);
48
53
  const cacheKey = normalizeToolSearchCacheKey({
49
54
  query,
50
55
  limit,
51
56
  includeSchemas,
57
+ readOnlyOnly,
52
58
  });
53
59
  const runCtx = getRequestRunContext();
54
60
  const priorSearch = includeSchemas
@@ -83,13 +89,22 @@ export function searchToolRegistry(registry, args = {}, options = {}) {
83
89
  if (name.startsWith("mcp__") && !isMcpToolAllowedForRequest(name)) {
84
90
  continue;
85
91
  }
86
- totalTools++;
87
92
  const description = normalizeWhitespace(entry.tool.description ?? "");
88
93
  const parsedMcp = parseMcpToolName(name);
89
94
  const kind = parsedMcp ? "mcp" : "action";
90
95
  const source = parsedMcp?.serverId;
91
96
  const callable = entry.allowInPlanMode !== false;
92
97
  const planAvailability = getPlanAvailability(name, entry);
98
+ // Conditional policies still need discovery: MCP tools and provider/web
99
+ // actions classify the arguments at call time. Bash is marked conditional
100
+ // for Plan mode, but the orchestration bridge intentionally does not expose
101
+ // shell execution, so do not advertise it as a child-call candidate.
102
+ if (readOnlyOnly &&
103
+ planAvailability !== "read" &&
104
+ (planAvailability !== "conditional" || name === "bash")) {
105
+ continue;
106
+ }
107
+ totalTools++;
93
108
  if (listAll) {
94
109
  candidates.push({
95
110
  name,
@@ -181,6 +196,7 @@ function normalizeToolSearchCacheKey(options) {
181
196
  query: options.query.trim().toLowerCase(),
182
197
  limit: options.limit,
183
198
  includeSchemas: options.includeSchemas,
199
+ readOnlyOnly: options.readOnlyOnly,
184
200
  });
185
201
  }
186
202
  function rememberToolSearchResult(cacheKey, result) {
@@ -1,6 +1,6 @@
1
1
  import { getDbExec } from "../db/client.js";
2
2
  import { isValidCron, isValidTimezone, nextOccurrence } from "../jobs/cron.js";
3
- import { buildJobResourceContent, normalizeJobMcpTools, parseJobResource, } from "../jobs/frontmatter.js";
3
+ import { buildJobResourceContent, jobBelongsToApp, normalizeJobMcpTools, parseJobResource, } from "../jobs/frontmatter.js";
4
4
  import { deleteAutomationRuns } from "../jobs/run-history.js";
5
5
  import { resolveUserSchedulingTimezone } from "../localization/user-timezone.js";
6
6
  import { organizationIdFromResourceOwner, organizationResourceOwner, resourceDelete, resourceGetByPath, resourceList, resourcePut, } from "../resources/store.js";
@@ -60,12 +60,6 @@ async function requireOrganizationMembership(actor) {
60
60
  function isOrganizationAdmin(membership) {
61
61
  return membership.role === "owner" || membership.role === "admin";
62
62
  }
63
- function automationBelongsToApp(meta, appId) {
64
- const ownerAppId = meta.appId?.trim();
65
- if (!ownerAppId || !appId?.trim())
66
- return true;
67
- return ownerAppId === appId.trim();
68
- }
69
63
  async function mutationAccess(actorInput, resource, meta) {
70
64
  const actor = normalizeActor(actorInput);
71
65
  const resourceOrgId = organizationIdFromResourceOwner(resource.owner);
@@ -94,7 +88,7 @@ async function mutationAccess(actorInput, resource, meta) {
94
88
  export async function canUpdateAutomationResource(actorInput, resource) {
95
89
  const { meta } = parseJobResource(resource.content);
96
90
  const actor = normalizeActor(actorInput);
97
- if (!automationBelongsToApp(meta, actor.appId))
91
+ if (!jobBelongsToApp(meta, actor.appId))
98
92
  return false;
99
93
  return (await mutationAccess(actor, resource, meta)).canUpdate;
100
94
  }
@@ -124,7 +118,7 @@ async function readDefinition(actorInput, scope, name) {
124
118
  throw httpError(`Automation "${automationName(path)}" not found.`, 404);
125
119
  }
126
120
  const definition = assertExplicitAutomation(resource);
127
- if (!automationBelongsToApp(definition.meta, actor.appId)) {
121
+ if (!jobBelongsToApp(definition.meta, actor.appId)) {
128
122
  throw httpError(`Automation "${automationName(path)}" not found.`, 404);
129
123
  }
130
124
  const access = await mutationAccess(actor, resource, definition.meta);
@@ -154,7 +148,7 @@ export async function listAutomationDefinitions(actorInput, scope) {
154
148
  const parsed = parseJobResource(resource.content);
155
149
  if (parsed.classification.kind !== "automation")
156
150
  continue;
157
- if (!automationBelongsToApp(parsed.meta, actor.appId))
151
+ if (!jobBelongsToApp(parsed.meta, actor.appId))
158
152
  continue;
159
153
  const isCreator = parsed.meta.createdBy?.trim().toLowerCase() === actor.userEmail;
160
154
  automations.push({
@@ -7,6 +7,19 @@
7
7
  * thin action wrapper.
8
8
  */
9
9
  import type { BrandWebsiteSignals } from "./types.js";
10
+ export interface BrandAnalysisResult {
11
+ companyName?: string;
12
+ brandNotes?: string;
13
+ existingDesignSystem?: unknown;
14
+ websiteAnalysis?: unknown;
15
+ }
16
+ /** Assemble brand-analysis data without performing template or network work. */
17
+ export declare function buildBrandAnalysisResult(input: {
18
+ companyName?: string;
19
+ brandNotes?: string;
20
+ existingDesignSystem?: unknown;
21
+ websiteAnalysis?: unknown;
22
+ }): BrandAnalysisResult;
10
23
  /**
11
24
  * Normalize a user-supplied brand website URL: add an `https://` scheme when
12
25
  * missing and reject anything that isn't http(s). Throws on empty input or an
@@ -7,6 +7,21 @@
7
7
  * thin action wrapper.
8
8
  */
9
9
  import { ssrfSafeFetch } from "../extensions/url-safety.js";
10
+ /** Assemble brand-analysis data without performing template or network work. */
11
+ export function buildBrandAnalysisResult(input) {
12
+ const result = {};
13
+ if (input.companyName)
14
+ result.companyName = input.companyName;
15
+ if (input.brandNotes)
16
+ result.brandNotes = input.brandNotes;
17
+ if (input.existingDesignSystem !== undefined) {
18
+ result.existingDesignSystem = input.existingDesignSystem;
19
+ }
20
+ if (input.websiteAnalysis !== undefined) {
21
+ result.websiteAnalysis = input.websiteAnalysis;
22
+ }
23
+ return result;
24
+ }
10
25
  /**
11
26
  * Normalize a user-supplied brand website URL: add an `https://` scheme when
12
27
  * missing and reject anything that isn't http(s). Throws on empty input or an
@@ -17,5 +17,5 @@
17
17
  export * from "./types.js";
18
18
  export * from "./brand-signals.js";
19
19
  export * from "./tokens.js";
20
- export { extractDesignTokensFromUrl, validateUrl, parseOwnerRepo, fetchGitHubJson, fetchGitHubJsonResult, fetchGitHubRaw, parseTailwindConfig, parseCss, detectStylingFramework, createCodeAnalysisState, analyzeCodeFile, analyzeCssFile, analyzeTailwindConfig, analyzeJsonTheme, analyzePackageJson, analyzeThemeSourceFile, addFont, extractCssVars, extractCodeColors, extractCodeFonts, extractDocumentColors, extractDocumentFonts, classifyFile, suggestionsForType, unique, MAX_FILES, MAX_FILE_SIZE, FETCH_TIMEOUT, ROOT_PATTERNS, SECONDARY_PATHS, CODE_MAX_FILES, CODE_MAX_TOTAL_BYTES, } from "../server/design-token-utils.js";
21
- export type { ContentType, ParsedCss, ParsedTailwindConfig, CodeAnalysisState, UrlExtractionResult, GitHubFetchOptions, GitHubJsonResult, } from "../server/design-token-utils.js";
20
+ export { extractDesignTokensFromUrl, validateUrl, parseOwnerRepo, fetchGitHubJson, fetchGitHubJsonResult, fetchGitHubRaw, parseTailwindConfig, parseCss, detectStylingFramework, createCodeAnalysisState, analyzeCodeFile, analyzeCodeFiles, analyzeCssFile, analyzeTailwindConfig, analyzeJsonTheme, analyzePackageJson, analyzeThemeSourceFile, addFont, extractCssVars, extractCodeColors, extractCodeFonts, extractDocumentColors, extractDocumentFonts, classifyFile, suggestionsForType, unique, MAX_FILES, MAX_FILE_SIZE, FETCH_TIMEOUT, ROOT_PATTERNS, SECONDARY_PATHS, CODE_MAX_FILES, CODE_MAX_TOTAL_BYTES, } from "../server/design-token-utils.js";
21
+ export type { ContentType, ParsedCss, ParsedTailwindConfig, CodeAnalysisState, CodeAnalysisFile, CodeAnalysisResult, UrlExtractionResult, GitHubFetchOptions, GitHubJsonResult, } from "../server/design-token-utils.js";
@@ -27,7 +27,7 @@ parseOwnerRepo, fetchGitHubJson, fetchGitHubJsonResult, fetchGitHubRaw,
27
27
  // Tailwind / CSS parsing
28
28
  parseTailwindConfig, parseCss, detectStylingFramework,
29
29
  // Code-file analysis
30
- createCodeAnalysisState, analyzeCodeFile, analyzeCssFile, analyzeTailwindConfig, analyzeJsonTheme, analyzePackageJson, analyzeThemeSourceFile, addFont, extractCssVars, extractCodeColors, extractCodeFonts,
30
+ createCodeAnalysisState, analyzeCodeFile, analyzeCodeFiles, analyzeCssFile, analyzeTailwindConfig, analyzeJsonTheme, analyzePackageJson, analyzeThemeSourceFile, addFont, extractCssVars, extractCodeColors, extractCodeFonts,
31
31
  // Document analysis
32
32
  extractDocumentColors, extractDocumentFonts, classifyFile, suggestionsForType, unique,
33
33
  // Constants
@@ -73,6 +73,8 @@ export declare function createThread(ownerEmail: string, opts?: {
73
73
  title?: string;
74
74
  scope?: ChatThreadScope | null;
75
75
  source?: ChatThreadSource | null;
76
+ /** Explicit owner organization for durable/background callers. */
77
+ orgId?: string | null;
76
78
  }): Promise<ChatThread>;
77
79
  export declare function registerChatThreadsShareable(): void;
78
80
  export declare function ensureChatThreadTables(): Promise<void>;
@@ -367,7 +367,7 @@ export async function createThread(ownerEmail, opts) {
367
367
  const title = opts?.title ?? "";
368
368
  const scope = opts?.scope ?? null;
369
369
  const source = opts?.source ?? null;
370
- const orgId = getRequestOrgId() ?? null;
370
+ const orgId = opts?.orgId ?? getRequestOrgId() ?? null;
371
371
  await client.execute({
372
372
  sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, source_platform, source_app_id, source_url, org_id, visibility) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'private')`,
373
373
  args: [
@@ -2,8 +2,9 @@
2
2
  * Import and validate the portable Agent Plugin format.
3
3
  *
4
4
  * The importer intentionally handles only local plugin directories. Skills are
5
- * copied as inert files, and remote Streamable HTTP MCP entries are translated
6
- * to the Agent-Native `mcp.config.json` shape without copying package headers.
5
+ * copied as inert files into the workspace's agent-visible `.agents/skills`
6
+ * tree, and remote Streamable HTTP MCP entries are translated to the
7
+ * Agent-Native `mcp.config.json` shape without copying package headers.
7
8
  * A plugin package is not a credential store and this command never executes
8
9
  * a plugin's scripts or stdio servers.
9
10
  */
@@ -62,6 +63,8 @@ export interface LoadedAgentPlugin {
62
63
  }
63
64
  export interface AgentPluginImportOptions {
64
65
  targetDir?: string;
66
+ /** Optional skills root used by hosts whose agent cwd is a child workspace. */
67
+ skillsTargetDir?: string;
65
68
  force?: boolean;
66
69
  dryRun?: boolean;
67
70
  }
@@ -2,8 +2,9 @@
2
2
  * Import and validate the portable Agent Plugin format.
3
3
  *
4
4
  * The importer intentionally handles only local plugin directories. Skills are
5
- * copied as inert files, and remote Streamable HTTP MCP entries are translated
6
- * to the Agent-Native `mcp.config.json` shape without copying package headers.
5
+ * copied as inert files into the workspace's agent-visible `.agents/skills`
6
+ * tree, and remote Streamable HTTP MCP entries are translated to the
7
+ * Agent-Native `mcp.config.json` shape without copying package headers.
7
8
  * A plugin package is not a credential store and this command never executes
8
9
  * a plugin's scripts or stdio servers.
9
10
  */
@@ -42,7 +43,7 @@ Usage:
42
43
  Commands:
43
44
  import Import a standard Agent Plugin's Skills and remote MCP servers into an Agent-Native workspace.
44
45
 
45
- The importer copies Skills into a namespaced skills/<plugin>/<skill> path and
46
+ The importer copies Skills into a namespaced .agents/skills/<plugin>/<skill> path and
46
47
  maps Streamable HTTP MCP servers into mcp.config.json. Package headers are
47
48
  never imported as credentials; configure authentication through the app's
48
49
  normal Connections flow. Stdio and SSE servers are reported and skipped.`;
@@ -691,10 +692,10 @@ function planMcpServers(plugin, existing, force) {
691
692
  }
692
693
  return planned;
693
694
  }
694
- function planSkills(plugin, targetDir, targetPluginSlug, force) {
695
+ function planSkills(plugin, skillsRoot, force) {
695
696
  return plugin.skills.map((skill) => {
696
- const destination = path.join(targetDir, "skills", targetPluginSlug, skill.name);
697
- assertPathInside(targetDir, destination, "Imported skill destination");
697
+ const destination = path.join(skillsRoot, skill.name);
698
+ assertPathInside(skillsRoot, destination, "Imported skill destination");
698
699
  assertPathOutside(plugin.rootDir, destination, "Imported skill destination");
699
700
  assertNoSymlinkInExistingPath(path.dirname(destination), "Imported skill destination");
700
701
  if (!pathExists(destination)) {
@@ -878,12 +879,19 @@ export function importAgentPlugin(input, options = {}) {
878
879
  }
879
880
  assertNoSymlinkInExistingPath(targetDir, "Agent-Native import target");
880
881
  const slug = pluginSlug(plugin.manifest.name);
881
- const skillsRoot = path.join(targetDir, "skills", slug);
882
+ const skillsTargetDir = canonicalPathForComparison(options.skillsTargetDir ?? path.join(targetDir, ".agents", "skills"));
883
+ if (pathExists(skillsTargetDir) &&
884
+ !fs.statSync(skillsTargetDir).isDirectory()) {
885
+ throw new Error(`Agent-Native skills target must be a directory: ${skillsTargetDir}`);
886
+ }
887
+ assertPathOutside(plugin.rootDir, skillsTargetDir, "Imported skills target");
888
+ assertNoSymlinkInExistingPath(skillsTargetDir, "Imported skills target");
889
+ const skillsRoot = path.join(skillsTargetDir, slug);
882
890
  const metadataFile = path.join(targetDir, ".agent-native", "plugins", `${slug}.json`);
883
891
  const mcpFile = path.join(targetDir, "mcp.config.json");
884
- assertPathInside(targetDir, skillsRoot, "Imported skills root");
885
892
  assertPathInside(targetDir, metadataFile, "Imported metadata path");
886
893
  assertPathInside(targetDir, mcpFile, "Imported MCP config path");
894
+ assertPathInside(skillsTargetDir, skillsRoot, "Imported skills root");
887
895
  assertPathOutside(plugin.rootDir, skillsRoot, "Imported skills root");
888
896
  assertPathOutside(plugin.rootDir, metadataFile, "Imported metadata path");
889
897
  assertPathOutside(plugin.rootDir, mcpFile, "Imported MCP config path");
@@ -892,7 +900,7 @@ export function importAgentPlugin(input, options = {}) {
892
900
  const force = options.force ?? false;
893
901
  const dryRun = options.dryRun ?? false;
894
902
  const existingMcp = readExistingMcpConfig(mcpFile);
895
- const skillPlans = planSkills(plugin, targetDir, slug, force);
903
+ const skillPlans = planSkills(plugin, skillsRoot, force);
896
904
  const mcpPlans = planMcpServers(plugin, existingMcp, force);
897
905
  const existingMetadata = readExistingMetadata(metadataFile);
898
906
  const metadataIsSame = metadataMatches(existingMetadata, plugin);
@@ -14,6 +14,7 @@ import { readAgentsBundleFromFs, generateDevelopmentSkillsPromptBlock, } from ".
14
14
  import { getAmbientOrgId, getAmbientUserEmail, runWithRequestContext, } from "../server/request-context.js";
15
15
  import { isReasoningEffort, } from "../shared/reasoning-effort.js";
16
16
  import { createAgentNativeConfigContext, loadResolvedAgentNativeConfig, } from "../vite/agent-native-config-loader.js";
17
+ import { codexMcpConfigArgs, mergeCodeAgentMcpConfig, restrictCodeAgentMcpConfig, } from "./code-agent-mcp-config.js";
17
18
  import { createCodeAgentOutputSmoother } from "./code-agent-output-smoother.js";
18
19
  import { addCodeAgentCommandToAllowlist, appendCodeAgentTranscriptEvent, dequeueCodeAgentFollowUp, getCodeAgentRunRecord, isCodeAgentCommandAllowed, listCodeAgentTranscriptEvents, updateCodeAgentRunRecord, } from "./code-agent-runs.js";
19
20
  const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
@@ -443,20 +444,26 @@ async function executeCodexCliRun(options) {
443
444
  const model = normalizeCodexCliModel(options.model);
444
445
  const streamToolOutputToStdout = options.streamToolOutputToStdout ??
445
446
  process.env.AGENT_NATIVE_CODE_AGENT_STRUCTURED_STDOUT !== "1";
447
+ const additionalSkillsRoot = process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT?.trim();
446
448
  const args = [
449
+ ...codexMcpConfigArgs(),
447
450
  "--ask-for-approval",
448
451
  "never",
449
- "exec",
452
+ "--sandbox",
453
+ codexSandboxForPermissionMode(options.permissionMode),
450
454
  "--cd",
451
455
  cwd,
456
+ "exec",
452
457
  "--color",
453
458
  "never",
454
459
  "--skip-git-repo-check",
455
- "--sandbox",
456
- codexSandboxForPermissionMode(options.permissionMode),
460
+ "--ignore-user-config",
457
461
  "--output-last-message",
458
462
  outputPath,
459
463
  ];
464
+ if (additionalSkillsRoot && fs.existsSync(additionalSkillsRoot)) {
465
+ args.splice(args.indexOf("--output-last-message"), 0, "--add-dir", additionalSkillsRoot);
466
+ }
460
467
  if (model)
461
468
  args.push("--model", model);
462
469
  args.push("-");
@@ -663,9 +670,15 @@ function buildCodexCliPrompt(run, prompt) {
663
670
  const modeInstruction = mode === "Plan"
664
671
  ? "Inspect and explain only. Do not edit files or run mutating commands."
665
672
  : "Edit and verify as needed. Do not create, switch, reset, rebase, or stash git branches.";
673
+ const additionalSkillsRoot = process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT?.trim();
666
674
  return [
667
675
  `You are running from Agent-Native Code in ${run.cwd || process.cwd()}.`,
668
676
  "Follow the repository AGENTS.md and any relevant skill instructions.",
677
+ ...(additionalSkillsRoot
678
+ ? [
679
+ `Additional installed Agent Plugin skills are available under ${additionalSkillsRoot}. Read any relevant SKILL.md files before acting.`,
680
+ ]
681
+ : []),
669
682
  `Run mode: ${mode} (${permissionMode}). ${modeInstruction}`,
670
683
  "",
671
684
  "# User request",
@@ -933,9 +946,12 @@ async function startCodeAgentMcpManager(runId) {
933
946
  });
934
947
  return null;
935
948
  });
936
- if (!config || Object.keys(config.servers ?? {}).length === 0)
949
+ const effectiveConfig = restrictCodeAgentMcpConfig(mergeCodeAgentMcpConfig(config));
950
+ if (!effectiveConfig ||
951
+ Object.keys(effectiveConfig.servers ?? {}).length === 0) {
937
952
  return null;
938
- const manager = new McpClientManager(config);
953
+ }
954
+ const manager = new McpClientManager(effectiveConfig);
939
955
  await manager.start().catch((err) => {
940
956
  const message = err instanceof Error ? err.message : String(err);
941
957
  appendCodeAgentTranscriptEvent({
@@ -1299,6 +1315,10 @@ export async function buildCodeAgentSystemPrompt(cwd, permissionMode) {
1299
1315
  const appConfig = await loadResolvedAgentNativeConfig(cwd, createAgentNativeConfigContext("serve", "development"));
1300
1316
  const bundle = readAgentsBundleFromFs(cwd, null, {
1301
1317
  instructions: appConfig.instructions,
1318
+ additionalSkillDirs: (process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT ?? "")
1319
+ .split(path.delimiter)
1320
+ .map((value) => value.trim())
1321
+ .filter(Boolean),
1302
1322
  });
1303
1323
  // If the bundle has no AGENTS.md, try CLAUDE.md as a fallback — many repos
1304
1324
  // use that name for agent instructions (e.g. Claude Code projects).
@@ -0,0 +1,21 @@
1
+ import type { McpConfig } from "../mcp-client/config.js";
2
+ /**
3
+ * Keep a desktop coding run on the app registry's MCP servers. The normal
4
+ * server-side manager merges persisted MCP settings for web requests, but a
5
+ * local shell must not inherit arbitrary user MCP servers when the desktop
6
+ * host has supplied an explicit workspace allowlist.
7
+ */
8
+ export declare function restrictCodeAgentMcpConfig(config: McpConfig | null, environment?: NodeJS.ProcessEnv): McpConfig | null;
9
+ /**
10
+ * Add host-provided app/plugin servers to the normal merged config. The
11
+ * filesystem and environment layers have different precedence rules, so the
12
+ * child process combines them explicitly instead of silently dropping one.
13
+ */
14
+ export declare function mergeCodeAgentMcpConfig(config: McpConfig | null, environment?: NodeJS.ProcessEnv): McpConfig | null;
15
+ /**
16
+ * Convert the host-scoped HTTP MCP config into Codex CLI's `-c` overrides.
17
+ * The caller adds `--ignore-user-config` in the `exec` segment so a local
18
+ * coding session receives the workspace apps, not the user's global MCP
19
+ * catalog.
20
+ */
21
+ export declare function codexMcpConfigArgs(environment?: NodeJS.ProcessEnv): string[];
@@ -0,0 +1,103 @@
1
+ const DESKTOP_MCP_ALLOWLIST_ENV = "AGENT_NATIVE_CODE_AGENT_MCP_SERVER_ALLOWLIST";
2
+ function parseServerAllowlist(raw) {
3
+ if (raw === undefined)
4
+ return null;
5
+ return new Set(raw
6
+ .split(",")
7
+ .map((value) => value.trim())
8
+ .filter(Boolean));
9
+ }
10
+ /**
11
+ * Keep a desktop coding run on the app registry's MCP servers. The normal
12
+ * server-side manager merges persisted MCP settings for web requests, but a
13
+ * local shell must not inherit arbitrary user MCP servers when the desktop
14
+ * host has supplied an explicit workspace allowlist.
15
+ */
16
+ export function restrictCodeAgentMcpConfig(config, environment = process.env) {
17
+ const allowlist = parseServerAllowlist(environment[DESKTOP_MCP_ALLOWLIST_ENV]);
18
+ if (!allowlist || !config)
19
+ return config;
20
+ const servers = Object.fromEntries(Object.entries(config.servers).filter(([id]) => allowlist.has(id)));
21
+ return { ...config, servers };
22
+ }
23
+ function codexConfigKey(serverId) {
24
+ const normalized = serverId.replace(/[^A-Za-z0-9_-]/g, "_");
25
+ return normalized || "workspace";
26
+ }
27
+ function tomlString(value) {
28
+ return JSON.stringify(value);
29
+ }
30
+ function tomlInlineTable(headers) {
31
+ return `{${Object.entries(headers)
32
+ .map(([key, value]) => `${tomlString(key)}=${tomlString(value)}`)
33
+ .join(",")}}`;
34
+ }
35
+ function parseMcpServers(environment) {
36
+ const raw = environment.MCP_SERVERS?.trim();
37
+ if (!raw)
38
+ return {};
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(raw);
42
+ }
43
+ catch {
44
+ throw new Error("MCP_SERVERS is not valid JSON.");
45
+ }
46
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
47
+ throw new Error("MCP_SERVERS must contain a servers object.");
48
+ }
49
+ const serverMap = "servers" in parsed &&
50
+ parsed.servers &&
51
+ typeof parsed.servers === "object" &&
52
+ !Array.isArray(parsed.servers)
53
+ ? parsed.servers
54
+ : parsed;
55
+ return Object.fromEntries(Object.entries(serverMap).map(([id, value]) => {
56
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
57
+ throw new Error(`MCP_SERVERS entry ${id} is invalid.`);
58
+ }
59
+ return [id, value];
60
+ }));
61
+ }
62
+ /**
63
+ * Add host-provided app/plugin servers to the normal merged config. The
64
+ * filesystem and environment layers have different precedence rules, so the
65
+ * child process combines them explicitly instead of silently dropping one.
66
+ */
67
+ export function mergeCodeAgentMcpConfig(config, environment = process.env) {
68
+ const environmentServers = parseMcpServers(environment);
69
+ if (Object.keys(environmentServers).length === 0)
70
+ return config;
71
+ return {
72
+ ...(config ?? { servers: {} }),
73
+ servers: {
74
+ ...(config?.servers ?? {}),
75
+ ...environmentServers,
76
+ },
77
+ source: config?.source
78
+ ? `${config.source}+desktop-environment`
79
+ : "env:MCP_SERVERS",
80
+ };
81
+ }
82
+ /**
83
+ * Convert the host-scoped HTTP MCP config into Codex CLI's `-c` overrides.
84
+ * The caller adds `--ignore-user-config` in the `exec` segment so a local
85
+ * coding session receives the workspace apps, not the user's global MCP
86
+ * catalog.
87
+ */
88
+ export function codexMcpConfigArgs(environment = process.env) {
89
+ const servers = parseMcpServers(environment);
90
+ if (Object.keys(servers).length === 0)
91
+ return [];
92
+ const args = [];
93
+ for (const [serverId, server] of Object.entries(servers)) {
94
+ if (server.type !== "http" || !server.url)
95
+ continue;
96
+ const key = codexConfigKey(serverId);
97
+ args.push("-c", `mcp_servers.${tomlString(key)}.url=${tomlString(server.url)}`);
98
+ if (server.headers && Object.keys(server.headers).length > 0) {
99
+ args.push("-c", `mcp_servers.${tomlString(key)}.http_headers=${tomlInlineTable(server.headers)}`);
100
+ }
101
+ }
102
+ return args;
103
+ }
@@ -1,18 +1,3 @@
1
- export declare const DEFAULT_PLAN_APP_URL = "https://plan.agent-native.com";
2
- export type PlanBlockFormat = "reference" | "schema";
3
- export type FetchPlanBlockCatalogInput = {
4
- appUrl?: string;
5
- out?: string;
6
- format?: PlanBlockFormat;
7
- fetchFn?: typeof fetch;
8
- };
9
- export type FetchPlanBlockCatalogResult = {
10
- ok: true;
11
- out: string;
12
- count?: number;
13
- format: PlanBlockFormat;
14
- };
15
- export declare function planActionEndpoint(appUrl: string, action: string): string;
16
- export declare function normalizePlanBlockFormat(value: string | undefined): PlanBlockFormat;
17
- export declare function defaultPlanBlocksOut(format: PlanBlockFormat): string;
18
- export declare function fetchPlanBlockCatalog(input: FetchPlanBlockCatalogInput): Promise<FetchPlanBlockCatalogResult>;
1
+ /** Compatibility re-export for the canonical recap CLI implementation. */
2
+ export { DEFAULT_PLAN_APP_URL, defaultPlanBlocksOut, fetchPlanBlockCatalog, normalizePlanBlockFormat, planActionEndpoint, } from "@agent-native/recap-cli";
3
+ export type { FetchPlanBlockCatalogInput, FetchPlanBlockCatalogResult, PlanBlockFormat, } from "@agent-native/recap-cli";