@agent-native/core 0.114.16 → 0.115.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/agent-native.eject.json +2 -0
  2. package/corpus/README.md +3 -3
  3. package/corpus/core/CHANGELOG.md +29 -0
  4. package/corpus/core/agent-native.eject.json +2 -0
  5. package/corpus/core/docs/content/agent-native-toolkit.mdx +16 -0
  6. package/corpus/core/docs/content/recurring-jobs.mdx +35 -6
  7. package/corpus/core/export-snapshot.json +4 -0
  8. package/corpus/core/package.json +5 -1
  9. package/corpus/core/src/action.ts +22 -1
  10. package/corpus/core/src/agent/production-agent.ts +105 -3
  11. package/corpus/core/src/cli/templates-meta.ts +12 -0
  12. package/corpus/core/src/client/AssistantChat.tsx +53 -26
  13. package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +19 -0
  14. package/corpus/core/src/client/agent-page/use-jobs.ts +1 -0
  15. package/corpus/core/src/client/chat/message-components.tsx +35 -1
  16. package/corpus/core/src/client/chat/tool-call-display.tsx +38 -3
  17. package/corpus/core/src/connections/catalog.ts +20 -1
  18. package/corpus/core/src/connections/reader.ts +65 -0
  19. package/corpus/core/src/dashboard-storage/index.ts +27 -0
  20. package/corpus/core/src/dashboard-storage/panel-source.ts +168 -0
  21. package/corpus/core/src/dashboard-storage/schema.ts +62 -0
  22. package/corpus/core/src/dashboard-storage/store.ts +333 -0
  23. package/corpus/core/src/deploy/build.ts +95 -16
  24. package/corpus/core/src/eject/provider-api-definitions.ts +1 -0
  25. package/corpus/core/src/eject/workspace-connections.ts +1 -0
  26. package/corpus/core/src/extensions/actions.ts +199 -73
  27. package/corpus/core/src/jobs/actions/list-recurring-jobs.ts +2 -0
  28. package/corpus/core/src/jobs/scheduler.ts +71 -4
  29. package/corpus/core/src/jobs/tools.ts +31 -1
  30. package/corpus/core/src/localization/default-messages.ts +1 -0
  31. package/corpus/core/src/mcp-client/index.ts +6 -0
  32. package/corpus/core/src/provider-api/actions/provider-api.ts +1 -1
  33. package/corpus/core/src/provider-api/index.ts +190 -5
  34. package/corpus/core/src/secrets/register-framework-secrets.ts +8 -0
  35. package/corpus/core/src/server/agent-chat/context-tools.ts +9 -0
  36. package/corpus/core/src/server/agent-chat/framework-prompts.ts +2 -2
  37. package/corpus/core/src/server/agent-chat-plugin.ts +22 -2
  38. package/corpus/core/src/server/core-routes-plugin.ts +1 -0
  39. package/corpus/core/src/server/credential-provider.ts +9 -3
  40. package/corpus/core/src/server/workspace-provider-oauth.ts +181 -4
  41. package/corpus/core/src/styles/agent-native.css +45 -2
  42. package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +27 -0
  43. package/corpus/core/src/templates/default/.agents/skills/app-branding/SKILL.md +159 -0
  44. package/corpus/core/src/templates/default/.agents/skills/app-permissions/SKILL.md +155 -0
  45. package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +27 -0
  46. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +27 -0
  47. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +31 -18
  48. package/corpus/core/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +15 -0
  49. package/corpus/core/src/triggers/actions.ts +11 -2
  50. package/corpus/core/src/triggers/dispatcher.ts +20 -1
  51. package/corpus/core/src/triggers/types.ts +5 -0
  52. package/corpus/core/src/vite/action-types-plugin.ts +9 -1
  53. package/corpus/templates/analytics/.agents/skills/agent-native-docs/SKILL.md +115 -0
  54. package/corpus/templates/analytics/AGENTS.md +11 -11
  55. package/corpus/templates/analytics/_gitignore +39 -0
  56. package/corpus/templates/analytics/actions/query-dashboard-panel.ts +30 -0
  57. package/corpus/templates/analytics/actions/update-dashboard.ts +2 -1
  58. package/corpus/templates/analytics/app/lib/sql-query.ts +11 -25
  59. package/corpus/templates/analytics/changelog/2026-07-21-fixed-extension-updates-failing-before-changes-could-be-appl.md +6 -0
  60. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +1 -0
  61. package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +51 -0
  62. package/corpus/templates/analytics/server/lib/prometheus.ts +1 -1
  63. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
  64. package/corpus/templates/assets/.agents/skills/agent-native-docs/SKILL.md +115 -0
  65. package/corpus/templates/brain/.agents/skills/agent-native-docs/SKILL.md +115 -0
  66. package/corpus/templates/calendar/.agents/skills/agent-native-docs/SKILL.md +115 -0
  67. package/corpus/templates/calendar/_gitignore +39 -0
  68. package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +27 -0
  69. package/corpus/templates/chat/AGENTS.md +1 -1
  70. package/corpus/templates/clips/.agents/skills/agent-native-docs/SKILL.md +115 -0
  71. package/corpus/templates/clips/AGENTS.md +7 -0
  72. package/corpus/templates/clips/actions/prepare-crm-call-evidence.ts +58 -0
  73. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +88 -11
  74. package/corpus/templates/clips/changelog/2026-07-21-crm-call-evidence-handoff.md +6 -0
  75. package/corpus/templates/clips/changelog/2026-07-21-recording-retries-and-restarts-keep-their-upload-session-so-.md +6 -0
  76. package/corpus/templates/clips/chrome-extension/src/background.ts +20 -5
  77. package/corpus/templates/clips/chrome-extension/src/native-recording-state.ts +19 -0
  78. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -0
  79. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +9 -0
  80. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +54 -28
  81. package/corpus/templates/clips/server/plugins/agent-chat.ts +1 -0
  82. package/corpus/templates/content/.agents/skills/agent-native-docs/SKILL.md +115 -0
  83. package/corpus/templates/content/_gitignore +39 -0
  84. package/corpus/templates/content/learnings.defaults.md +5 -0
  85. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +115 -0
  86. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  87. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +128 -0
  88. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +145 -0
  89. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +169 -0
  90. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +100 -0
  91. package/corpus/templates/crm/.env.example +5 -0
  92. package/corpus/templates/crm/AGENTS.md +120 -0
  93. package/corpus/templates/crm/CHANGELOG.md +1 -0
  94. package/corpus/templates/crm/DEVELOPING.md +66 -0
  95. package/corpus/templates/crm/README.md +106 -0
  96. package/corpus/templates/crm/_gitignore +12 -0
  97. package/corpus/templates/crm/actions/_crm-action-utils.ts +87 -0
  98. package/corpus/templates/crm/actions/_crm-dashboard.ts +69 -0
  99. package/corpus/templates/crm/actions/_crm-signal-utils.ts +79 -0
  100. package/corpus/templates/crm/actions/apply-crm-proposals.ts +203 -0
  101. package/corpus/templates/crm/actions/attach-call-evidence.ts +170 -0
  102. package/corpus/templates/crm/actions/configure-crm-connection.ts +126 -0
  103. package/corpus/templates/crm/actions/configure-native-crm.ts +44 -0
  104. package/corpus/templates/crm/actions/create-crm-record.ts +148 -0
  105. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +58 -0
  106. package/corpus/templates/crm/actions/delete-staged-dataset.ts +14 -0
  107. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +36 -0
  108. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +33 -0
  109. package/corpus/templates/crm/actions/get-crm-dashboard.ts +14 -0
  110. package/corpus/templates/crm/actions/get-crm-overview.ts +13 -0
  111. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +61 -0
  112. package/corpus/templates/crm/actions/get-crm-record.ts +75 -0
  113. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +81 -0
  114. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +15 -0
  115. package/corpus/templates/crm/actions/list-crm-dashboards.ts +15 -0
  116. package/corpus/templates/crm/actions/list-crm-proposals.ts +31 -0
  117. package/corpus/templates/crm/actions/list-crm-records.ts +79 -0
  118. package/corpus/templates/crm/actions/list-crm-saved-views.ts +15 -0
  119. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +43 -0
  120. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +32 -0
  121. package/corpus/templates/crm/actions/list-crm-tasks.ts +22 -0
  122. package/corpus/templates/crm/actions/list-staged-datasets.ts +14 -0
  123. package/corpus/templates/crm/actions/list-workspace-connections.ts +47 -0
  124. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +87 -0
  125. package/corpus/templates/crm/actions/manage-crm-task.ts +132 -0
  126. package/corpus/templates/crm/actions/navigate.ts +63 -0
  127. package/corpus/templates/crm/actions/provider-api-catalog.ts +22 -0
  128. package/corpus/templates/crm/actions/provider-api-docs.ts +54 -0
  129. package/corpus/templates/crm/actions/provider-api-request.ts +92 -0
  130. package/corpus/templates/crm/actions/query-staged-dataset.ts +44 -0
  131. package/corpus/templates/crm/actions/record-crm-call-insight.ts +160 -0
  132. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +150 -0
  133. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +23 -0
  134. package/corpus/templates/crm/actions/review-crm-signal.ts +31 -0
  135. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +103 -0
  136. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +275 -0
  137. package/corpus/templates/crm/actions/run.ts +11 -0
  138. package/corpus/templates/crm/actions/save-crm-dashboard.ts +51 -0
  139. package/corpus/templates/crm/actions/save-crm-saved-view.ts +119 -0
  140. package/corpus/templates/crm/actions/sync-crm.ts +146 -0
  141. package/corpus/templates/crm/actions/update-crm-record.ts +508 -0
  142. package/corpus/templates/crm/actions/view-screen.ts +218 -0
  143. package/corpus/templates/crm/agent-native.app-skill.json +58 -0
  144. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +158 -0
  145. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +83 -0
  146. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +225 -0
  147. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +413 -0
  148. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +510 -0
  149. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +134 -0
  150. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +367 -0
  151. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +92 -0
  152. package/corpus/templates/crm/app/components/crm/Surface.tsx +96 -0
  153. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +180 -0
  154. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +106 -0
  155. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +84 -0
  156. package/corpus/templates/crm/app/entry.client.tsx +16 -0
  157. package/corpus/templates/crm/app/entry.server.tsx +10 -0
  158. package/corpus/templates/crm/app/global.css +167 -0
  159. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +60 -0
  160. package/corpus/templates/crm/app/i18n/en-US.ts +89 -0
  161. package/corpus/templates/crm/app/i18n/index.ts +34 -0
  162. package/corpus/templates/crm/app/lib/dashboard.ts +13 -0
  163. package/corpus/templates/crm/app/lib/navigation.ts +54 -0
  164. package/corpus/templates/crm/app/lib/tab-id.ts +1 -0
  165. package/corpus/templates/crm/app/lib/types.ts +172 -0
  166. package/corpus/templates/crm/app/lib/utils.ts +1 -0
  167. package/corpus/templates/crm/app/root.tsx +88 -0
  168. package/corpus/templates/crm/app/routes/_index.tsx +26 -0
  169. package/corpus/templates/crm/app/routes/accounts.tsx +41 -0
  170. package/corpus/templates/crm/app/routes/agent.tsx +5 -0
  171. package/corpus/templates/crm/app/routes/ask.tsx +53 -0
  172. package/corpus/templates/crm/app/routes/dashboard.tsx +118 -0
  173. package/corpus/templates/crm/app/routes/opportunities.tsx +41 -0
  174. package/corpus/templates/crm/app/routes/people.tsx +41 -0
  175. package/corpus/templates/crm/app/routes/proposals.tsx +266 -0
  176. package/corpus/templates/crm/app/routes/records.$recordId.tsx +35 -0
  177. package/corpus/templates/crm/app/routes/settings.tsx +63 -0
  178. package/corpus/templates/crm/app/routes/setup.tsx +308 -0
  179. package/corpus/templates/crm/app/routes/tasks.tsx +252 -0
  180. package/corpus/templates/crm/app/routes/views.tsx +333 -0
  181. package/corpus/templates/crm/app/routes.ts +17 -0
  182. package/corpus/templates/crm/app/vite-env.d.ts +6 -0
  183. package/corpus/templates/crm/changelog/2026-07-21-agent-native-crm.md +6 -0
  184. package/corpus/templates/crm/changelog/2026-07-21-clips-call-evidence-review-recipe.md +6 -0
  185. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-find-and-review-evidence-grounded-call-signals-w.md +6 -0
  186. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-manage-keyword-and-smart-call-signal-trackers-fr.md +6 -0
  187. package/corpus/templates/crm/changelog/2026-07-21-delegated-local-crm-automation.md +6 -0
  188. package/corpus/templates/crm/changelog/2026-07-21-native-sql.md +6 -0
  189. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboard.md +6 -0
  190. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboards-now-install-correctly-from-the-crm-actio.md +6 -0
  191. package/corpus/templates/crm/changelog/2026-07-21-salesforce-connection.md +6 -0
  192. package/corpus/templates/crm/components.json +20 -0
  193. package/corpus/templates/crm/docs/architecture/crm-contract.md +180 -0
  194. package/corpus/templates/crm/learnings.defaults.md +5 -0
  195. package/corpus/templates/crm/package.json +59 -0
  196. package/corpus/templates/crm/public/favicon.svg +1 -0
  197. package/corpus/templates/crm/public/icon-180.svg +1 -0
  198. package/corpus/templates/crm/public/manifest.json +11 -0
  199. package/corpus/templates/crm/react-router.config.ts +7 -0
  200. package/corpus/templates/crm/server/crm/adapter.ts +32 -0
  201. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +64 -0
  202. package/corpus/templates/crm/server/crm/crm-mirror.ts +711 -0
  203. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +1143 -0
  204. package/corpus/templates/crm/server/crm/native-adapter.ts +1715 -0
  205. package/corpus/templates/crm/server/crm/read-through.ts +207 -0
  206. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +1255 -0
  207. package/corpus/templates/crm/server/db/crm-store.ts +1170 -0
  208. package/corpus/templates/crm/server/db/index.ts +169 -0
  209. package/corpus/templates/crm/server/db/schema.ts +441 -0
  210. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +65 -0
  211. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +105 -0
  212. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +100 -0
  213. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +144 -0
  214. package/corpus/templates/crm/server/lib/intelligence/summary.ts +141 -0
  215. package/corpus/templates/crm/server/lib/provider-api.ts +64 -0
  216. package/corpus/templates/crm/server/middleware/auth.ts +4 -0
  217. package/corpus/templates/crm/server/plugins/agent-chat.ts +68 -0
  218. package/corpus/templates/crm/server/plugins/auth.ts +14 -0
  219. package/corpus/templates/crm/server/plugins/core-routes.ts +17 -0
  220. package/corpus/templates/crm/server/plugins/db.ts +454 -0
  221. package/corpus/templates/crm/server/routes/[...page].get.ts +5 -0
  222. package/corpus/templates/crm/shared/crm-automation-recipes.ts +132 -0
  223. package/corpus/templates/crm/shared/crm-contract.ts +254 -0
  224. package/corpus/templates/crm/shared/crm-sales-config.ts +63 -0
  225. package/corpus/templates/crm/ssr-entry.ts +11 -0
  226. package/corpus/templates/crm/tsconfig.json +24 -0
  227. package/corpus/templates/crm/vite.config.ts +15 -0
  228. package/corpus/templates/crm/vitest.config.ts +5 -0
  229. package/corpus/templates/design/.agents/skills/agent-native-docs/SKILL.md +115 -0
  230. package/corpus/templates/dispatch/.agents/skills/agent-native-docs/SKILL.md +115 -0
  231. package/corpus/templates/forms/.agents/skills/agent-native-docs/SKILL.md +115 -0
  232. package/corpus/templates/forms/_gitignore +39 -0
  233. package/corpus/templates/forms/learnings.defaults.md +5 -0
  234. package/corpus/templates/macros/.agents/skills/agent-native-docs/SKILL.md +115 -0
  235. package/corpus/templates/macros/_gitignore +39 -0
  236. package/corpus/templates/macros/learnings.defaults.md +5 -0
  237. package/corpus/templates/mail/.agents/skills/agent-native-docs/SKILL.md +115 -0
  238. package/corpus/templates/mail/_gitignore +39 -0
  239. package/corpus/templates/plan/.agents/skills/agent-native-docs/SKILL.md +115 -0
  240. package/corpus/templates/slides/.agents/skills/agent-native-docs/SKILL.md +115 -0
  241. package/corpus/templates/slides/_gitignore +39 -0
  242. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +27 -0
  243. package/corpus/templates/tasks/_gitignore +39 -0
  244. package/corpus/toolkit/CHANGELOG.md +12 -0
  245. package/corpus/toolkit/README.md +30 -0
  246. package/corpus/toolkit/agent-native.eject.json +33 -0
  247. package/corpus/toolkit/package.json +15 -1
  248. package/corpus/toolkit/src/dashboard/DataTable.tsx +273 -0
  249. package/corpus/toolkit/src/dashboard/DateRangePicker.tsx +57 -0
  250. package/corpus/toolkit/src/dashboard/GenericChartPanel.tsx +461 -0
  251. package/corpus/toolkit/src/dashboard/MetricCard.tsx +54 -0
  252. package/corpus/toolkit/src/dashboard/StatsCard.tsx +49 -0
  253. package/corpus/toolkit/src/dashboard/dashboard-layout.ts +329 -0
  254. package/corpus/toolkit/src/dashboard/index.ts +6 -0
  255. package/corpus/toolkit/src/index.ts +1 -0
  256. package/corpus/toolkit/src/ui/calendar.tsx +8 -8
  257. package/corpus/toolkit/src/ui/date-picker.tsx +4 -1
  258. package/dist/action.d.ts +14 -1
  259. package/dist/action.d.ts.map +1 -1
  260. package/dist/action.js.map +1 -1
  261. package/dist/agent/production-agent.d.ts +3 -1
  262. package/dist/agent/production-agent.d.ts.map +1 -1
  263. package/dist/agent/production-agent.js +83 -3
  264. package/dist/agent/production-agent.js.map +1 -1
  265. package/dist/cli/templates-meta.d.ts.map +1 -1
  266. package/dist/cli/templates-meta.js +12 -0
  267. package/dist/cli/templates-meta.js.map +1 -1
  268. package/dist/client/AssistantChat.d.ts +4 -0
  269. package/dist/client/AssistantChat.d.ts.map +1 -1
  270. package/dist/client/AssistantChat.js +30 -17
  271. package/dist/client/AssistantChat.js.map +1 -1
  272. package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
  273. package/dist/client/agent-page/AgentJobsTab.js +3 -1
  274. package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
  275. package/dist/client/agent-page/use-jobs.d.ts +1 -0
  276. package/dist/client/agent-page/use-jobs.d.ts.map +1 -1
  277. package/dist/client/agent-page/use-jobs.js.map +1 -1
  278. package/dist/client/chat/message-components.d.ts +5 -0
  279. package/dist/client/chat/message-components.d.ts.map +1 -1
  280. package/dist/client/chat/message-components.js +17 -1
  281. package/dist/client/chat/message-components.js.map +1 -1
  282. package/dist/client/chat/tool-call-display.d.ts +3 -1
  283. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  284. package/dist/client/chat/tool-call-display.js +17 -7
  285. package/dist/client/chat/tool-call-display.js.map +1 -1
  286. package/dist/collab/awareness.d.ts +2 -2
  287. package/dist/collab/awareness.d.ts.map +1 -1
  288. package/dist/collab/routes.d.ts +1 -1
  289. package/dist/collab/struct-routes.d.ts +1 -1
  290. package/dist/connections/catalog.d.ts +17 -3
  291. package/dist/connections/catalog.d.ts.map +1 -1
  292. package/dist/connections/catalog.js +16 -1
  293. package/dist/connections/catalog.js.map +1 -1
  294. package/dist/connections/reader.d.ts +18 -0
  295. package/dist/connections/reader.d.ts.map +1 -1
  296. package/dist/connections/reader.js +41 -0
  297. package/dist/connections/reader.js.map +1 -1
  298. package/dist/dashboard-storage/index.d.ts +4 -0
  299. package/dist/dashboard-storage/index.d.ts.map +1 -0
  300. package/dist/dashboard-storage/index.js +4 -0
  301. package/dist/dashboard-storage/index.js.map +1 -0
  302. package/dist/dashboard-storage/panel-source.d.ts +68 -0
  303. package/dist/dashboard-storage/panel-source.d.ts.map +1 -0
  304. package/dist/dashboard-storage/panel-source.js +76 -0
  305. package/dist/dashboard-storage/panel-source.js.map +1 -0
  306. package/dist/dashboard-storage/schema.d.ts +562 -0
  307. package/dist/dashboard-storage/schema.d.ts.map +1 -0
  308. package/dist/dashboard-storage/schema.js +32 -0
  309. package/dist/dashboard-storage/schema.js.map +1 -0
  310. package/dist/dashboard-storage/store.d.ts +62 -0
  311. package/dist/dashboard-storage/store.d.ts.map +1 -0
  312. package/dist/dashboard-storage/store.js +227 -0
  313. package/dist/dashboard-storage/store.js.map +1 -0
  314. package/dist/deploy/build.d.ts +7 -0
  315. package/dist/deploy/build.d.ts.map +1 -1
  316. package/dist/deploy/build.js +78 -14
  317. package/dist/deploy/build.js.map +1 -1
  318. package/dist/eject/provider-api-definitions.d.ts +2 -1
  319. package/dist/eject/provider-api-definitions.d.ts.map +1 -1
  320. package/dist/eject/provider-api-definitions.js +1 -0
  321. package/dist/eject/provider-api-definitions.js.map +1 -1
  322. package/dist/eject/workspace-connections.d.ts.map +1 -1
  323. package/dist/eject/workspace-connections.js +1 -0
  324. package/dist/eject/workspace-connections.js.map +1 -1
  325. package/dist/extensions/actions.d.ts.map +1 -1
  326. package/dist/extensions/actions.js +165 -67
  327. package/dist/extensions/actions.js.map +1 -1
  328. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  329. package/dist/jobs/actions/list-recurring-jobs.d.ts +1 -0
  330. package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -1
  331. package/dist/jobs/actions/list-recurring-jobs.js +1 -0
  332. package/dist/jobs/actions/list-recurring-jobs.js.map +1 -1
  333. package/dist/jobs/scheduler.d.ts +17 -2
  334. package/dist/jobs/scheduler.d.ts.map +1 -1
  335. package/dist/jobs/scheduler.js +51 -2
  336. package/dist/jobs/scheduler.js.map +1 -1
  337. package/dist/jobs/tools.d.ts.map +1 -1
  338. package/dist/jobs/tools.js +32 -2
  339. package/dist/jobs/tools.js.map +1 -1
  340. package/dist/localization/default-messages.d.ts +1 -0
  341. package/dist/localization/default-messages.d.ts.map +1 -1
  342. package/dist/localization/default-messages.js +1 -0
  343. package/dist/localization/default-messages.js.map +1 -1
  344. package/dist/mcp-client/index.d.ts +2 -0
  345. package/dist/mcp-client/index.d.ts.map +1 -1
  346. package/dist/mcp-client/index.js +5 -0
  347. package/dist/mcp-client/index.js.map +1 -1
  348. package/dist/notifications/routes.d.ts +3 -3
  349. package/dist/observability/routes.d.ts +5 -5
  350. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  351. package/dist/provider-api/actions/provider-api.d.ts +8 -6
  352. package/dist/provider-api/actions/provider-api.d.ts.map +1 -1
  353. package/dist/provider-api/actions/provider-api.js +1 -1
  354. package/dist/provider-api/actions/provider-api.js.map +1 -1
  355. package/dist/provider-api/index.d.ts +11 -7
  356. package/dist/provider-api/index.d.ts.map +1 -1
  357. package/dist/provider-api/index.js +143 -5
  358. package/dist/provider-api/index.js.map +1 -1
  359. package/dist/resources/handlers.d.ts +1 -1
  360. package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
  361. package/dist/secrets/register-framework-secrets.js +7 -0
  362. package/dist/secrets/register-framework-secrets.js.map +1 -1
  363. package/dist/secrets/routes.d.ts +9 -9
  364. package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
  365. package/dist/server/agent-chat/context-tools.js +9 -0
  366. package/dist/server/agent-chat/context-tools.js.map +1 -1
  367. package/dist/server/agent-chat/framework-prompts.js +2 -2
  368. package/dist/server/agent-chat/framework-prompts.js.map +1 -1
  369. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  370. package/dist/server/agent-chat-plugin.js +18 -2
  371. package/dist/server/agent-chat-plugin.js.map +1 -1
  372. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  373. package/dist/server/core-routes-plugin.js +1 -0
  374. package/dist/server/core-routes-plugin.js.map +1 -1
  375. package/dist/server/credential-provider.d.ts +4 -3
  376. package/dist/server/credential-provider.d.ts.map +1 -1
  377. package/dist/server/credential-provider.js +9 -3
  378. package/dist/server/credential-provider.js.map +1 -1
  379. package/dist/server/transcribe-voice.d.ts +1 -1
  380. package/dist/server/workspace-provider-oauth.d.ts +6 -1
  381. package/dist/server/workspace-provider-oauth.d.ts.map +1 -1
  382. package/dist/server/workspace-provider-oauth.js +141 -4
  383. package/dist/server/workspace-provider-oauth.js.map +1 -1
  384. package/dist/styles/agent-native.css +45 -2
  385. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +27 -0
  386. package/dist/templates/chat/AGENTS.md +1 -1
  387. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +27 -0
  388. package/dist/templates/default/.agents/skills/app-branding/SKILL.md +159 -0
  389. package/dist/templates/default/.agents/skills/app-permissions/SKILL.md +155 -0
  390. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +27 -0
  391. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +27 -0
  392. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +31 -18
  393. package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +15 -0
  394. package/dist/triggers/actions.d.ts.map +1 -1
  395. package/dist/triggers/actions.js +10 -2
  396. package/dist/triggers/actions.js.map +1 -1
  397. package/dist/triggers/dispatcher.d.ts.map +1 -1
  398. package/dist/triggers/dispatcher.js +15 -0
  399. package/dist/triggers/dispatcher.js.map +1 -1
  400. package/dist/triggers/types.d.ts +5 -0
  401. package/dist/triggers/types.d.ts.map +1 -1
  402. package/dist/triggers/types.js.map +1 -1
  403. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  404. package/dist/vite/action-types-plugin.js +4 -1
  405. package/dist/vite/action-types-plugin.js.map +1 -1
  406. package/docs/content/agent-native-toolkit.mdx +16 -0
  407. package/docs/content/recurring-jobs.mdx +35 -6
  408. package/package.json +6 -2
  409. package/src/action.ts +22 -1
  410. package/src/agent/production-agent.ts +105 -3
  411. package/src/cli/templates-meta.ts +12 -0
  412. package/src/client/AssistantChat.tsx +53 -26
  413. package/src/client/agent-page/AgentJobsTab.tsx +19 -0
  414. package/src/client/agent-page/use-jobs.ts +1 -0
  415. package/src/client/chat/message-components.tsx +35 -1
  416. package/src/client/chat/tool-call-display.tsx +38 -3
  417. package/src/connections/catalog.ts +20 -1
  418. package/src/connections/reader.ts +65 -0
  419. package/src/dashboard-storage/index.ts +27 -0
  420. package/src/dashboard-storage/panel-source.ts +168 -0
  421. package/src/dashboard-storage/schema.ts +62 -0
  422. package/src/dashboard-storage/store.ts +333 -0
  423. package/src/deploy/build.ts +95 -16
  424. package/src/eject/provider-api-definitions.ts +1 -0
  425. package/src/eject/workspace-connections.ts +1 -0
  426. package/src/extensions/actions.ts +199 -73
  427. package/src/jobs/actions/list-recurring-jobs.ts +2 -0
  428. package/src/jobs/scheduler.ts +71 -4
  429. package/src/jobs/tools.ts +31 -1
  430. package/src/localization/default-messages.ts +1 -0
  431. package/src/mcp-client/index.ts +6 -0
  432. package/src/provider-api/actions/provider-api.ts +1 -1
  433. package/src/provider-api/index.ts +190 -5
  434. package/src/secrets/register-framework-secrets.ts +8 -0
  435. package/src/server/agent-chat/context-tools.ts +9 -0
  436. package/src/server/agent-chat/framework-prompts.ts +2 -2
  437. package/src/server/agent-chat-plugin.ts +22 -2
  438. package/src/server/core-routes-plugin.ts +1 -0
  439. package/src/server/credential-provider.ts +9 -3
  440. package/src/server/workspace-provider-oauth.ts +181 -4
  441. package/src/styles/agent-native.css +45 -2
  442. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +27 -0
  443. package/src/templates/chat/AGENTS.md +1 -1
  444. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +27 -0
  445. package/src/templates/default/.agents/skills/app-branding/SKILL.md +159 -0
  446. package/src/templates/default/.agents/skills/app-permissions/SKILL.md +155 -0
  447. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +27 -0
  448. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +27 -0
  449. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +31 -18
  450. package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +15 -0
  451. package/src/triggers/actions.ts +11 -2
  452. package/src/triggers/dispatcher.ts +20 -1
  453. package/src/triggers/types.ts +5 -0
  454. package/src/vite/action-types-plugin.ts +9 -1
  455. package/corpus/templates/analytics/server/handlers/sql-query.ts +0 -36
  456. package/corpus/templates/analytics/server/routes/api/sql-query.post.ts +0 -1
@@ -2345,6 +2345,83 @@ export function isDurableBackgroundDeployEnabled(): boolean {
2345
2345
  return v === "1" || v === "true" || v === "yes" || v === "on";
2346
2346
  }
2347
2347
 
2348
+ const NETLIFY_KEEP_WARM_FUNCTION_NAME = "agent-native-keep-warm";
2349
+
2350
+ /**
2351
+ * Emit a site-local Netlify Scheduled Function that wakes the public server
2352
+ * function and its database every minute. GitHub Actions schedules can be
2353
+ * delayed by tens of minutes, which is longer than a scale-to-zero database's
2354
+ * autosuspend window and leaves the next visitor to pay the cold-start cost.
2355
+ */
2356
+ export function emitSingleTemplateNetlifyKeepWarmFunction(
2357
+ projectCwd: string,
2358
+ ): void {
2359
+ const internalDir = path.join(projectCwd, ".netlify", "functions-internal");
2360
+ const serverBundle = path.join(internalDir, "server", "main.mjs");
2361
+ if (!fs.existsSync(serverBundle)) {
2362
+ console.warn(
2363
+ "[build] Keep-warm emit skipped: expected Nitro Netlify function at " +
2364
+ ".netlify/functions-internal/server/main.mjs was not found.",
2365
+ );
2366
+ return;
2367
+ }
2368
+
2369
+ const dest = path.join(internalDir, NETLIFY_KEEP_WARM_FUNCTION_NAME);
2370
+ fs.rmSync(dest, { recursive: true, force: true });
2371
+ fs.mkdirSync(dest, { recursive: true });
2372
+
2373
+ const entry = `const HEALTH_PATH = "/_agent-native/health";
2374
+ const REQUEST_TIMEOUT_MS = 25_000;
2375
+
2376
+ function siteOrigin(request) {
2377
+ const configured = process.env.URL || process.env.DEPLOY_URL;
2378
+ if (configured) return configured;
2379
+ return new URL(request.url).origin;
2380
+ }
2381
+
2382
+ export default async function handler(request) {
2383
+ const url = new URL(HEALTH_PATH, siteOrigin(request));
2384
+ let response;
2385
+ try {
2386
+ response = await fetch(url, {
2387
+ headers: { "user-agent": "agent-native-netlify-keep-warm" },
2388
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
2389
+ });
2390
+ } catch (error) {
2391
+ console.error("[agent-native-keep-warm] Health request failed:", url.toString(), error);
2392
+ throw error;
2393
+ }
2394
+
2395
+ if (!response.ok) {
2396
+ const body = await response.text().catch(() => "");
2397
+ throw new Error(
2398
+ "[agent-native-keep-warm] Health request failed with " +
2399
+ response.status +
2400
+ ": " +
2401
+ body.slice(0, 500),
2402
+ );
2403
+ }
2404
+
2405
+ console.log("[agent-native-keep-warm] Warmed", url.toString());
2406
+ return new Response(null, { status: 204 });
2407
+ }
2408
+
2409
+ export const config = {
2410
+ name: "agent-native server keep warm",
2411
+ generator: "agent-native build",
2412
+ schedule: "* * * * *",
2413
+ };
2414
+ `;
2415
+
2416
+ fs.writeFileSync(
2417
+ path.join(dest, `${NETLIFY_KEEP_WARM_FUNCTION_NAME}.mjs`),
2418
+ entry,
2419
+ );
2420
+ console.log(
2421
+ `[build] Emitted Netlify scheduled keep-warm function "${NETLIFY_KEEP_WARM_FUNCTION_NAME}".`,
2422
+ );
2423
+ }
2424
+
2348
2425
  /**
2349
2426
  * Single-template Netlify build: emit an async (background) function INSIDE the
2350
2427
  * scanned functions dir so the chat `_process-run` worker runs on Netlify's
@@ -3528,24 +3605,26 @@ export default bundle;
3528
3605
  bundleYjsRuntimeForServerlessOutput(nitro.options.output.serverDir, cwd);
3529
3606
  }
3530
3607
 
3531
- // Durable background agent runs (default-OFF / opt-in; enable with a truthy
3532
- // AGENT_CHAT_DURABLE_BACKGROUND). Additive ONLY: emits a SECOND Netlify
3533
- // function whose name ends in `-background` re-exporting the same handler
3534
- // bundle, so the chat `_process-run` POST lands on Netlify's async (15-min)
3535
- // function. When not opted in this is a no-op and the single-function
3536
- // deploy is byte-for-byte unchanged.
3537
- if (preset === "netlify" && isDurableBackgroundDeployEnabled()) {
3538
- try {
3539
- emitSingleTemplateNetlifyBackgroundFunction(cwd);
3540
- } catch (err) {
3541
- console.warn(
3542
- "[build] Failed to emit durable-background Netlify function (non-fatal):",
3543
- err instanceof Error ? err.message : err,
3544
- );
3608
+ if (preset === "netlify") {
3609
+ emitSingleTemplateNetlifyKeepWarmFunction(cwd);
3610
+
3611
+ // Durable background agent runs (default-OFF / opt-in; enable with a truthy
3612
+ // AGENT_CHAT_DURABLE_BACKGROUND). Additive ONLY: emits a SECOND Netlify
3613
+ // function whose name ends in `-background` re-exporting the same handler
3614
+ // bundle, so the chat `_process-run` POST lands on Netlify's async (15-min)
3615
+ // function. When not opted in this is a no-op and the single-function
3616
+ // deploy is byte-for-byte unchanged.
3617
+ if (isDurableBackgroundDeployEnabled()) {
3618
+ try {
3619
+ emitSingleTemplateNetlifyBackgroundFunction(cwd);
3620
+ } catch (err) {
3621
+ console.warn(
3622
+ "[build] Failed to emit durable-background Netlify function (non-fatal):",
3623
+ err instanceof Error ? err.message : err,
3624
+ );
3625
+ }
3545
3626
  }
3546
- }
3547
3627
 
3548
- if (preset === "netlify") {
3549
3628
  writeSingleTemplateNetlifyRedirects(cwd);
3550
3629
  assertSingleTemplateNetlifyBuildOutput(cwd);
3551
3630
  }
@@ -44,6 +44,7 @@ export const providerApiOverrides: ProviderApiConfig[] = [
44
44
  provider("granola"),
45
45
  provider("grafana"),
46
46
  provider("hubspot"),
47
+ provider("salesforce"),
47
48
  provider("jira"),
48
49
  provider("mixpanel"),
49
50
  provider("notion"),
@@ -19,6 +19,7 @@ export const WORKSPACE_CONNECTION_PROVIDERS: WorkspaceConnectionProvider[] = [
19
19
  "gmail",
20
20
  "google_drive",
21
21
  "hubspot",
22
+ "salesforce",
22
23
  "jira",
23
24
  "sentry",
24
25
  "granola",
@@ -623,7 +623,7 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
623
623
  "update-extension": {
624
624
  tool: {
625
625
  description:
626
- 'Update an existing sandboxed Alpine.js mini-app extension. If the user is viewing the extension, use the extensionId from <current-screen> or <current-url> directly; do not list extensions first just to find the current id. Prefer granular edits for surgical changes; full-body replacement is blocked by default to preserve existing layout, CSS, copy, and interactions during data-only fixes. Use `patches` or `edits` for targeted changes; set `allowFullReplacement=true` only when the user explicitly asks for a visual rewrite or supplies a complete replacement body. Supported edits include literal replace, insert-before/after marker, replace-between markers, replace-section/wrap-section/remove-section for <!-- agent-native:section name --> blocks, and regex-replace. Pass format=true to run Prettier on the final HTML. To replace the whole body with a large pasted file, pass contentFromAttachment (the attachment name, or "latest") instead of copying the file into `content` that avoids re-emitting thousands of tokens.',
626
+ 'Update an existing sandboxed Alpine.js mini-app extension. Pass exactly three fields: `id`, `operation`, and `payloadJson`. `payloadJson` is a JSON object encoded as a string so model gateways cannot fill its optional members with invalid empty placeholders. For operation="edit", pass {"edits":[...]} or {"patches":[...]} and optional format=true. For operation="replace", pass exactly one of content, contentFromAttachment, or contentFromWorkspaceFile; use this operation only for a user-requested broad visual rewrite or complete replacement body. For operation="metadata", pass name, description, or icon. Change sharing through set-resource-visibility instead. If the user is viewing the extension, use the extensionId from <current-screen> or <current-url> directly.',
627
627
  parameters: {
628
628
  type: "object",
629
629
  properties: {
@@ -632,72 +632,35 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
632
632
  description:
633
633
  "Extension id to update. Prefer the extensionId from <current-screen> or <current-url> for the current extension.",
634
634
  },
635
- name: {
636
- type: "string",
637
- description: "Optional new display name.",
638
- },
639
- description: {
640
- type: "string",
641
- description: "Optional new description.",
642
- },
643
- content: {
644
- type: "string",
645
- description:
646
- "Optional full replacement Alpine.js HTML body snippet.",
647
- },
648
- contentFromAttachment: {
635
+ operation: {
649
636
  type: "string",
637
+ enum: ["edit", "replace", "metadata"],
650
638
  description:
651
- 'Optional full replacement sourced from a pasted/attached file on the current turn, by attachment name (or the literal "latest" for the most recent pasted block). Use instead of `content` when replacing the whole body with a large pasted file so you do not have to re-type it. Ignored when `content` is provided.',
639
+ 'The single update mode: "edit" for focused patches/edits, "replace" for an explicitly authorized complete body replacement, or "metadata" for name/description/icon.',
652
640
  },
653
- contentFromWorkspaceFile: {
641
+ payloadJson: {
654
642
  type: "string",
643
+ minLength: 2,
655
644
  description:
656
- 'Optional full replacement sourced from a workspace/shared resource file, by resource path (e.g. "intuit-analytics-extension.html"). The server reads the full file and uses it as the replacement body. Use instead of `content` when replacing the whole body with a large file that exists as a workspace resource. Ignored when `content` is provided.',
657
- },
658
- allowFullReplacement: {
659
- type: "boolean",
660
- description:
661
- "Explicitly allow replacing the entire existing body. Omit or set false for data-only repairs so the existing visual design is protected. Use true only when the user explicitly requested a broad visual rewrite or supplied a complete replacement body.",
662
- },
663
- patches: {
664
- anyOf: [
665
- { type: "string" },
666
- { type: "array", items: { type: "object" } },
667
- ],
668
- description:
669
- 'Optional patches as a JSON-encoded string or native array of { "find": "...", "replace": "...", "all"?: true, "expectedMatches"?: 1, "required"?: true } objects. Missing required targets fail instead of silently no-oping.',
670
- },
671
- edits: {
672
- anyOf: [
673
- { type: "string" },
674
- { type: "array", items: { type: "object" } },
675
- ],
676
- description:
677
- 'Preferred optional granular edit operations as a JSON-encoded string or native array. Examples: { "op": "insert-after", "marker": "<!-- section:metrics -->", "content": "..." }, { "op": "replace-section", "section": "npm-chart", "content": "..." }, { "op": "wrap-section", "section": "charts", "before": "<div>", "after": "</div>" }, { "op": "regex-replace", "pattern": "...", "replace": "...", "expectedMatches": 1 }.',
678
- },
679
- format: {
680
- type: "boolean",
681
- description:
682
- "When true, format the final extension HTML with Prettier after applying content, patches, and edits.",
683
- },
684
- icon: {
685
- type: "string",
686
- description: "Optional icon name or short label.",
687
- },
688
- visibility: {
689
- type: "string",
690
- description:
691
- "Optional sharing visibility. Public extension sharing is not supported; use private or org.",
692
- enum: ["private", "org"],
645
+ 'A non-empty JSON object encoded as a string. edit example: {"edits":[{"op":"replace","find":"old","replace":"new"}],"format":true}. replace example: {"contentFromAttachment":"latest"}. metadata example: {"name":"New name"}.',
693
646
  },
694
647
  },
695
- required: ["id"],
648
+ required: ["id", "operation", "payloadJson"],
649
+ additionalProperties: false,
696
650
  },
697
651
  },
698
652
  run: async (args, ctx) => {
653
+ const normalizedUpdate = normalizeUpdateExtensionArgs(args);
654
+ if ("error" in normalizedUpdate) return normalizedUpdate.error;
655
+ args = normalizedUpdate.args;
699
656
  const id = String(args?.id ?? "").trim();
700
657
  if (!id) return "Error: id is required.";
658
+ if (
659
+ args?.name !== undefined &&
660
+ (typeof args.name !== "string" || args.name.trim().length === 0)
661
+ ) {
662
+ return "Error: extension name must be a non-empty string. No changes were applied.";
663
+ }
701
664
  const localMessage = await localExtensionEditMessage(id);
702
665
  if (localMessage) return localMessage;
703
666
 
@@ -722,11 +685,19 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
722
685
  });
723
686
  }
724
687
 
688
+ const attachmentRef =
689
+ typeof args?.contentFromAttachment === "string"
690
+ ? args.contentFromAttachment.trim()
691
+ : "";
692
+ const workspaceFileRef =
693
+ typeof args?.contentFromWorkspaceFile === "string"
694
+ ? args.contentFromWorkspaceFile.trim()
695
+ : "";
725
696
  const fullReplacementRequested =
726
697
  (typeof args?.content === "string" &&
727
698
  args.content.trim().length > 0) ||
728
- args?.contentFromAttachment !== undefined ||
729
- args?.contentFromWorkspaceFile !== undefined;
699
+ attachmentRef.length > 0 ||
700
+ workspaceFileRef.length > 0;
730
701
  if (
731
702
  fullReplacementRequested &&
732
703
  !coerceBoolean(args?.allowFullReplacement)
@@ -748,8 +719,7 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
748
719
  : undefined;
749
720
  if (
750
721
  replacementContent === undefined &&
751
- (args?.contentFromAttachment !== undefined ||
752
- args?.contentFromWorkspaceFile !== undefined)
722
+ (attachmentRef.length > 0 || workspaceFileRef.length > 0)
753
723
  ) {
754
724
  const resolved = await resolveExtensionContentAsync(args, ctx);
755
725
  if ("error" in resolved) return resolved.error;
@@ -757,20 +727,20 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
757
727
  }
758
728
 
759
729
  let result = null;
730
+ const patches = parsePatches((args as any).patches);
731
+ if (args?.patches !== undefined && !patches) {
732
+ return "Error: patches must be a JSON array of { find, replace } objects.";
733
+ }
734
+ const edits = parseEdits((args as any).edits);
735
+ if (args?.edits !== undefined && !edits) {
736
+ return "Error: edits must be a JSON array of supported extension edit operations.";
737
+ }
760
738
  const hasContentUpdate =
761
739
  replacementContent !== undefined ||
762
- args?.patches !== undefined ||
763
- args?.edits !== undefined ||
764
- args?.format !== undefined;
740
+ (patches?.length ?? 0) > 0 ||
741
+ (edits?.length ?? 0) > 0 ||
742
+ coerceBoolean(args?.format);
765
743
  if (hasContentUpdate) {
766
- const patches = parsePatches((args as any).patches);
767
- if (args?.patches !== undefined && !patches) {
768
- return "Error: patches must be a JSON array of { find, replace } objects.";
769
- }
770
- const edits = parseEdits((args as any).edits);
771
- if (args?.edits !== undefined && !edits) {
772
- return "Error: edits must be a JSON array of supported extension edit operations.";
773
- }
774
744
  try {
775
745
  result = await updateExtensionContent(id, {
776
746
  content: replacementContent,
@@ -806,13 +776,15 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
806
776
  meta.description = String(args.description).trim();
807
777
  }
808
778
  if (args?.icon !== undefined) meta.icon = String(args.icon);
809
- if (args?.visibility !== undefined) {
779
+ if (args?.visibility !== undefined)
810
780
  meta.visibility = String(args.visibility);
811
- }
812
781
  if (Object.keys(meta).length > 0) {
813
782
  result = await updateExtension(id, meta as any);
814
783
  }
815
784
 
785
+ if (!result && !hasContentUpdate && Object.keys(meta).length === 0) {
786
+ return "Error: update-extension received no actual changes. Choose one operation and provide a non-empty payloadJson object; empty placeholder fields are not an update.";
787
+ }
816
788
  if (!result) result = await getExtension(id);
817
789
  if (!result) return `Error: extension not found: ${id}`;
818
790
  const hiddenIds = await getHiddenExtensionIdsForCurrentUser();
@@ -1444,7 +1416,7 @@ async function summarizeExtensionForAgentRead(
1444
1416
  reason: "unchanged-content-already-returned-this-run",
1445
1417
  contentHash: fingerprint,
1446
1418
  contentLength: row.content.length,
1447
- next: "Use the content already returned earlier in this run and call update-extension with focused edits/patches. Set forceContent=true only if you truly need the full body again.",
1419
+ next: 'Use the content already returned earlier in this run and call update-extension with operation="edit" plus focused edits/patches inside payloadJson. Set forceContent=true only if you truly need the full body again.',
1448
1420
  },
1449
1421
  };
1450
1422
  }
@@ -1746,6 +1718,160 @@ function coerceBoolean(value: unknown): boolean {
1746
1718
  return value === true || value === "true";
1747
1719
  }
1748
1720
 
1721
+ function normalizeUpdateExtensionArgs(
1722
+ args: any,
1723
+ ): { args: Record<string, unknown> } | { error: string } {
1724
+ const hasCompactContract =
1725
+ args?.operation !== undefined || args?.payloadJson !== undefined;
1726
+ if (!hasCompactContract) {
1727
+ const legacyArgs = { ...((args ?? {}) as Record<string, unknown>) };
1728
+ const hasInvalidPlaceholderSignal =
1729
+ (typeof legacyArgs.visibility === "string" &&
1730
+ legacyArgs.visibility.trim().length === 0) ||
1731
+ [legacyArgs.patches, legacyArgs.edits].some(
1732
+ (value) =>
1733
+ value !== null &&
1734
+ typeof value === "object" &&
1735
+ (Array.isArray(value)
1736
+ ? value.length > 0 &&
1737
+ value.every(
1738
+ (item) =>
1739
+ item !== null &&
1740
+ typeof item === "object" &&
1741
+ !Array.isArray(item) &&
1742
+ Object.keys(item).length === 0,
1743
+ )
1744
+ : Object.keys(value).length === 0),
1745
+ );
1746
+ if (hasInvalidPlaceholderSignal) {
1747
+ for (const [key, value] of Object.entries(legacyArgs)) {
1748
+ if (key === "id") continue;
1749
+ const empty =
1750
+ value === false ||
1751
+ value === null ||
1752
+ (typeof value === "string" && value.trim().length === 0) ||
1753
+ (Array.isArray(value) &&
1754
+ (value.length === 0 ||
1755
+ value.every(
1756
+ (item) =>
1757
+ item !== null &&
1758
+ typeof item === "object" &&
1759
+ !Array.isArray(item) &&
1760
+ Object.keys(item).length === 0,
1761
+ ))) ||
1762
+ (typeof value === "object" &&
1763
+ value !== null &&
1764
+ !Array.isArray(value) &&
1765
+ Object.keys(value).length === 0);
1766
+ if (empty) delete legacyArgs[key];
1767
+ }
1768
+ delete legacyArgs.visibility;
1769
+ }
1770
+ return { args: legacyArgs };
1771
+ }
1772
+
1773
+ const operation = String(args?.operation ?? "").trim();
1774
+ if (!(["edit", "replace", "metadata"] as const).includes(operation as any)) {
1775
+ return {
1776
+ error:
1777
+ 'Error: operation must be "edit", "replace", or "metadata". No changes were applied.',
1778
+ };
1779
+ }
1780
+
1781
+ let payload: unknown;
1782
+ try {
1783
+ payload = JSON.parse(String(args?.payloadJson ?? ""));
1784
+ } catch {
1785
+ return {
1786
+ error:
1787
+ "Error: payloadJson must be a valid JSON object encoded as a string. No changes were applied.",
1788
+ };
1789
+ }
1790
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
1791
+ return {
1792
+ error:
1793
+ "Error: payloadJson must decode to a non-empty JSON object. No changes were applied.",
1794
+ };
1795
+ }
1796
+
1797
+ const payloadRecord = payload as Record<string, unknown>;
1798
+ const allowedByOperation: Record<string, Set<string>> = {
1799
+ edit: new Set(["patches", "edits", "format"]),
1800
+ replace: new Set([
1801
+ "content",
1802
+ "contentFromAttachment",
1803
+ "contentFromWorkspaceFile",
1804
+ "format",
1805
+ ]),
1806
+ metadata: new Set(["name", "description", "icon"]),
1807
+ };
1808
+ const unexpected = Object.keys(payloadRecord).filter(
1809
+ (key) => !allowedByOperation[operation].has(key),
1810
+ );
1811
+ if (unexpected.length > 0) {
1812
+ return {
1813
+ error: `Error: operation=${operation} does not accept ${unexpected.join(", ")}. No changes were applied.`,
1814
+ };
1815
+ }
1816
+
1817
+ if (operation === "edit") {
1818
+ const patches = parsePatches(payloadRecord.patches);
1819
+ const edits = parseEdits(payloadRecord.edits);
1820
+ const hasEdit =
1821
+ (patches?.length ?? 0) > 0 ||
1822
+ (edits?.length ?? 0) > 0 ||
1823
+ coerceBoolean(payloadRecord.format);
1824
+ if (!hasEdit) {
1825
+ return {
1826
+ error:
1827
+ "Error: operation=edit requires at least one valid patch/edit or format=true. No changes were applied.",
1828
+ };
1829
+ }
1830
+ }
1831
+
1832
+ if (operation === "replace") {
1833
+ const replacementSources = [
1834
+ payloadRecord.content,
1835
+ payloadRecord.contentFromAttachment,
1836
+ payloadRecord.contentFromWorkspaceFile,
1837
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
1838
+ if (replacementSources.length !== 1) {
1839
+ return {
1840
+ error:
1841
+ "Error: operation=replace requires exactly one non-empty content, contentFromAttachment, or contentFromWorkspaceFile value. No changes were applied.",
1842
+ };
1843
+ }
1844
+ }
1845
+
1846
+ if (operation === "metadata") {
1847
+ const keys = Object.keys(payloadRecord);
1848
+ if (keys.length === 0) {
1849
+ return {
1850
+ error:
1851
+ "Error: operation=metadata requires name, description, or icon. No changes were applied.",
1852
+ };
1853
+ }
1854
+ if (
1855
+ payloadRecord.name !== undefined &&
1856
+ (typeof payloadRecord.name !== "string" ||
1857
+ payloadRecord.name.trim().length === 0)
1858
+ ) {
1859
+ return {
1860
+ error:
1861
+ "Error: extension name must be a non-empty string. No changes were applied.",
1862
+ };
1863
+ }
1864
+ }
1865
+
1866
+ return {
1867
+ args: {
1868
+ id: String(args?.id ?? "").trim(),
1869
+ ...payloadRecord,
1870
+ ...(operation === "replace" ? { allowFullReplacement: true } : {}),
1871
+ },
1872
+ };
1873
+ }
1874
+
1749
1875
  function parseInlineContext(
1750
1876
  value: unknown,
1751
1877
  ): Record<string, unknown> | undefined {
@@ -46,6 +46,7 @@ export interface RecurringJobActionItem {
46
46
  lastError: string | null;
47
47
  nextRun: string | null;
48
48
  createdBy: string | null;
49
+ mcpTools: string[];
49
50
  canUpdate: boolean;
50
51
  }
51
52
 
@@ -96,6 +97,7 @@ export default defineAction({
96
97
  lastError: meta.lastError ?? null,
97
98
  nextRun: nextRun(meta),
98
99
  createdBy: meta.createdBy ?? null,
100
+ mcpTools: meta.mcpTools ?? [],
99
101
  canUpdate,
100
102
  });
101
103
  }
@@ -42,6 +42,50 @@ export interface JobFrontmatter {
42
42
  deliveryThreadRef?: string;
43
43
  deliveryTenantId?: string;
44
44
  model?: string;
45
+ /** Explicit MCP tool capabilities available to this background run. */
46
+ mcpTools?: string[];
47
+ }
48
+
49
+ const MAX_JOB_MCP_TOOLS = 64;
50
+ const JOB_MCP_TOOL_NAME_RE = /^mcp__[^\s]+__[^\s]+$/;
51
+
52
+ /**
53
+ * Normalize the non-secret MCP capability references persisted with a job.
54
+ * Tool names are opaque framework identifiers; URLs and credentials never
55
+ * belong in job frontmatter.
56
+ */
57
+ export function normalizeJobMcpTools(value: unknown): string[] | undefined {
58
+ if (value === undefined || value === null) return undefined;
59
+ const parsed =
60
+ typeof value === "string"
61
+ ? (() => {
62
+ try {
63
+ return JSON.parse(value);
64
+ } catch {
65
+ throw new Error("mcpTools must be a JSON array of tool names.");
66
+ }
67
+ })()
68
+ : value;
69
+ if (!Array.isArray(parsed)) {
70
+ throw new Error("mcpTools must be an array of MCP tool names.");
71
+ }
72
+ if (parsed.length > MAX_JOB_MCP_TOOLS) {
73
+ throw new Error(
74
+ `mcpTools may contain at most ${MAX_JOB_MCP_TOOLS} tool names.`,
75
+ );
76
+ }
77
+ const normalized = [...new Set(parsed)];
78
+ if (
79
+ normalized.some(
80
+ (toolName) =>
81
+ typeof toolName !== "string" || !JOB_MCP_TOOL_NAME_RE.test(toolName),
82
+ )
83
+ ) {
84
+ throw new Error(
85
+ "mcpTools must contain only framework MCP tool names such as mcp__server__tool.",
86
+ );
87
+ }
88
+ return normalized;
45
89
  }
46
90
 
47
91
  const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
@@ -129,6 +173,14 @@ export function parseJobFrontmatter(content: string): {
129
173
  case "model":
130
174
  meta.model = value;
131
175
  break;
176
+ case "mcpTools":
177
+ try {
178
+ meta.mcpTools = normalizeJobMcpTools(value);
179
+ } catch {
180
+ // Ignore malformed optional capability metadata and keep the job
181
+ // readable; newly-created jobs are validated before persistence.
182
+ }
183
+ break;
132
184
  }
133
185
  }
134
186
 
@@ -166,6 +218,8 @@ export function buildJobContent(meta: JobFrontmatter, body: string): string {
166
218
  if (meta.deliveryTenantId)
167
219
  lines.push(`deliveryTenantId: ${meta.deliveryTenantId}`);
168
220
  if (meta.model) lines.push(`model: ${meta.model}`);
221
+ if (meta.mcpTools?.length)
222
+ lines.push(`mcpTools: ${JSON.stringify(meta.mcpTools)}`);
169
223
  lines.push(`---`);
170
224
  lines.push("");
171
225
  lines.push(body);
@@ -174,8 +228,15 @@ export function buildJobContent(meta: JobFrontmatter, body: string): string {
174
228
 
175
229
  // ─── Job execution ──────────────────────────────────────────────────────────
176
230
 
231
+ export interface RecurringJobContext {
232
+ name: string;
233
+ meta: JobFrontmatter;
234
+ body: string;
235
+ resource: Resource;
236
+ }
237
+
177
238
  export interface SchedulerDeps {
178
- getActions: () => Record<string, ActionEntry>;
239
+ getActions: (job?: RecurringJobContext) => Record<string, ActionEntry>;
179
240
  getSystemPrompt: (owner: string) => Promise<string>;
180
241
  /**
181
242
  * Tool names to expose on the FIRST engine request for a job run. When
@@ -188,7 +249,7 @@ export interface SchedulerDeps {
188
249
  * merged actions are the app's own vs. framework additions, so this must
189
250
  * be supplied explicitly rather than inferred here.
190
251
  */
191
- getInitialToolNames?: () => string[] | undefined;
252
+ getInitialToolNames?: (job?: RecurringJobContext) => string[] | undefined;
192
253
  /** Optional engine override. Defaults to the resolved request engine. */
193
254
  engine?: AgentEngine;
194
255
  apiKey?: string;
@@ -477,9 +538,15 @@ async function executeJob(
477
538
  },
478
539
  async () => {
479
540
  try {
480
- const baseActions = deps.getActions();
541
+ const jobContext: RecurringJobContext = {
542
+ name: jobName,
543
+ meta,
544
+ body,
545
+ resource,
546
+ };
547
+ const baseActions = deps.getActions(jobContext);
481
548
  const systemPrompt = await deps.getSystemPrompt(jobUserEmail);
482
- const initialToolNames = deps.getInitialToolNames?.();
549
+ const initialToolNames = deps.getInitialToolNames?.(jobContext);
483
550
  // Only attach tool-search (and pay its schema cost) when the caller
484
551
  // actually supplied an initial subset to filter down to — otherwise
485
552
  // this is byte-for-byte the prior unfiltered behavior.