@agent-native/core 0.96.0 → 0.98.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 (1241) hide show
  1. package/README.md +6 -0
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +71 -0
  4. package/corpus/core/docs/content/audit-log.mdx +2 -1
  5. package/corpus/core/docs/content/authentication.mdx +3 -3
  6. package/corpus/core/docs/content/automations.mdx +7 -7
  7. package/corpus/core/docs/content/creating-templates.mdx +1 -1
  8. package/corpus/core/docs/content/doctor.mdx +170 -0
  9. package/corpus/core/docs/content/external-agents.mdx +8 -7
  10. package/corpus/core/docs/content/internationalization.mdx +2 -1
  11. package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  12. package/corpus/core/docs/content/locales/ar-SA/authentication.mdx +2 -2
  13. package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
  14. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +8 -7
  15. package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  16. package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  17. package/corpus/core/docs/content/locales/ar-SA/mcp-protocol.mdx +1 -1
  18. package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  19. package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  20. package/corpus/core/docs/content/locales/ar-SA/security.mdx +10 -0
  21. package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
  22. package/corpus/core/docs/content/locales/de-DE/authentication.mdx +1 -1
  23. package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
  24. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +8 -7
  25. package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
  26. package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
  27. package/corpus/core/docs/content/locales/de-DE/mcp-protocol.mdx +1 -1
  28. package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  29. package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  30. package/corpus/core/docs/content/locales/de-DE/security.mdx +10 -0
  31. package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
  32. package/corpus/core/docs/content/locales/es-ES/authentication.mdx +2 -2
  33. package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
  34. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +8 -7
  35. package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
  36. package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
  37. package/corpus/core/docs/content/locales/es-ES/mcp-protocol.mdx +1 -1
  38. package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  39. package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  40. package/corpus/core/docs/content/locales/es-ES/security.mdx +10 -0
  41. package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  42. package/corpus/core/docs/content/locales/fr-FR/authentication.mdx +1 -1
  43. package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
  44. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +8 -7
  45. package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  46. package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  47. package/corpus/core/docs/content/locales/fr-FR/mcp-protocol.mdx +1 -1
  48. package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  49. package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  50. package/corpus/core/docs/content/locales/fr-FR/security.mdx +10 -0
  51. package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  52. package/corpus/core/docs/content/locales/hi-IN/authentication.mdx +2 -2
  53. package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
  54. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +8 -7
  55. package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  56. package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  57. package/corpus/core/docs/content/locales/hi-IN/mcp-protocol.mdx +1 -1
  58. package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  59. package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  60. package/corpus/core/docs/content/locales/hi-IN/security.mdx +10 -0
  61. package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  62. package/corpus/core/docs/content/locales/ja-JP/authentication.mdx +2 -2
  63. package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
  64. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +8 -7
  65. package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  66. package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  67. package/corpus/core/docs/content/locales/ja-JP/mcp-protocol.mdx +1 -1
  68. package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  69. package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  70. package/corpus/core/docs/content/locales/ja-JP/security.mdx +10 -0
  71. package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  72. package/corpus/core/docs/content/locales/ko-KR/authentication.mdx +2 -2
  73. package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
  74. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +8 -7
  75. package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  76. package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  77. package/corpus/core/docs/content/locales/ko-KR/mcp-protocol.mdx +1 -1
  78. package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  79. package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  80. package/corpus/core/docs/content/locales/ko-KR/security.mdx +10 -0
  81. package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  82. package/corpus/core/docs/content/locales/pt-BR/authentication.mdx +2 -2
  83. package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
  84. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +8 -7
  85. package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  86. package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  87. package/corpus/core/docs/content/locales/pt-BR/mcp-protocol.mdx +1 -1
  88. package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  89. package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  90. package/corpus/core/docs/content/locales/pt-BR/security.mdx +10 -0
  91. package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  92. package/corpus/core/docs/content/locales/zh-CN/authentication.mdx +2 -2
  93. package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
  94. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +8 -7
  95. package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  96. package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  97. package/corpus/core/docs/content/locales/zh-CN/mcp-protocol.mdx +1 -1
  98. package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  99. package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  100. package/corpus/core/docs/content/locales/zh-CN/security.mdx +10 -0
  101. package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  102. package/corpus/core/docs/content/locales/zh-TW/authentication.mdx +2 -2
  103. package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
  104. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +8 -7
  105. package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  106. package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  107. package/corpus/core/docs/content/locales/zh-TW/mcp-protocol.mdx +1 -1
  108. package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  109. package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  110. package/corpus/core/docs/content/locales/zh-TW/security.mdx +10 -0
  111. package/corpus/core/docs/content/mcp-protocol.mdx +1 -1
  112. package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
  113. package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
  114. package/corpus/core/docs/content/security.mdx +2 -0
  115. package/corpus/core/package.json +13 -2
  116. package/corpus/core/scripts/finalize-build.mjs +63 -3
  117. package/corpus/core/scripts/materialize-source-corpus.mjs +101 -15
  118. package/corpus/core/src/a2a/auth-policy.ts +33 -0
  119. package/corpus/core/src/action.ts +42 -0
  120. package/corpus/core/src/agent/durable-background.ts +15 -7
  121. package/corpus/core/src/agent/engine/builder-engine.ts +3 -0
  122. package/corpus/core/src/agent/engine/context-directives-transform.ts +69 -0
  123. package/corpus/core/src/agent/engine/continuation-dispatch-retry.ts +168 -0
  124. package/corpus/core/src/agent/engine/tool-call-journal-seed.ts +85 -0
  125. package/corpus/core/src/agent/production-agent.ts +129 -244
  126. package/corpus/core/src/agent/run-store.ts +13 -4
  127. package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
  128. package/corpus/core/src/audit/store.ts +8 -3
  129. package/corpus/core/src/audit/types.ts +3 -0
  130. package/corpus/core/src/cli/doctor.ts +402 -0
  131. package/corpus/core/src/cli/index.ts +56 -0
  132. package/corpus/core/src/cli/info.ts +3 -3
  133. package/corpus/core/src/cli/package-lifecycle.ts +821 -0
  134. package/corpus/core/src/cli/plan-local.ts +10 -2
  135. package/corpus/core/src/cli/skills-content/assets-skill.ts +83 -0
  136. package/corpus/core/src/cli/skills-content/canvas.ts +153 -0
  137. package/corpus/core/src/cli/skills-content/connection.ts +52 -0
  138. package/corpus/core/src/cli/skills-content/content-skill.ts +109 -0
  139. package/corpus/core/src/cli/skills-content/design-exploration-skill.ts +118 -0
  140. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +228 -0
  141. package/corpus/core/src/cli/skills-content/document-quality.ts +196 -0
  142. package/corpus/core/src/cli/skills-content/exemplar.ts +69 -0
  143. package/corpus/core/src/cli/skills-content/help.ts +77 -0
  144. package/corpus/core/src/cli/skills-content/index.ts +22 -0
  145. package/corpus/core/src/cli/skills-content/local-files.ts +95 -0
  146. package/corpus/core/src/cli/skills-content/plan-setup-auth.ts +80 -0
  147. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +470 -0
  148. package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +547 -0
  149. package/corpus/core/src/cli/skills-content/visualize-repo-skill.ts +93 -0
  150. package/corpus/core/src/cli/skills-content/wireframe.ts +349 -0
  151. package/corpus/core/src/cli/skills.ts +27 -2733
  152. package/corpus/core/src/client/AgentPanel.tsx +6 -5
  153. package/corpus/core/src/client/CommandMenu.tsx +16 -5
  154. package/corpus/core/src/client/FeedbackButton.tsx +2 -2
  155. package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
  156. package/corpus/core/src/client/app-providers.tsx +18 -5
  157. package/corpus/core/src/client/chat/tool-call-display.tsx +3 -3
  158. package/corpus/core/src/client/composer/PastedTextChip.tsx +1 -1
  159. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +143 -45
  160. package/corpus/core/src/client/composer/TiptapComposer.tsx +1 -1
  161. package/corpus/core/src/client/composer/VoiceButton.tsx +21 -5
  162. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +107 -10
  163. package/corpus/core/src/client/context-xray/ContextXRayPanel.tsx +5 -2
  164. package/corpus/core/src/client/extensions/ExtensionEditor.tsx +37 -4
  165. package/corpus/core/src/client/extensions/ExtensionQueryErrorState.tsx +47 -0
  166. package/corpus/core/src/client/extensions/ExtensionSlot.tsx +39 -10
  167. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +44 -6
  168. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +12 -3
  169. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +49 -11
  170. package/corpus/core/src/client/index.ts +0 -13
  171. package/corpus/core/src/client/progress/RunsTray.tsx +10 -3
  172. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +7 -0
  173. package/corpus/core/src/client/settings/useBuilderStatus.ts +6 -0
  174. package/corpus/core/src/client/tool-cells/EditCell.tsx +19 -16
  175. package/corpus/core/src/client/tool-cells/FilesChangedSummary.tsx +26 -23
  176. package/corpus/core/src/client/tool-cells/WriteCell.tsx +19 -16
  177. package/corpus/core/src/client/visual-style-controls.tsx +1 -1
  178. package/corpus/core/src/deploy/build.ts +36 -14
  179. package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
  180. package/corpus/core/src/guards/index.ts +23 -0
  181. package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
  182. package/corpus/core/src/guards/no-env-credentials.ts +229 -0
  183. package/corpus/core/src/guards/no-env-mutation.ts +114 -0
  184. package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
  185. package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
  186. package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
  187. package/corpus/core/src/guards/scan-utils.ts +105 -0
  188. package/corpus/core/src/guards/types.ts +30 -0
  189. package/corpus/core/src/integrations/google-docs-poller.ts +23 -2
  190. package/corpus/core/src/integrations/plugin.ts +10 -0
  191. package/corpus/core/src/integrations/webhook-handler.ts +27 -2
  192. package/corpus/core/src/jobs/scheduler.ts +28 -2
  193. package/corpus/core/src/localization/default-messages.ts +8 -1
  194. package/corpus/core/src/mcp/oauth-token.ts +6 -1
  195. package/corpus/core/src/observability/routes.ts +36 -0
  196. package/corpus/core/src/observability/sentiment.ts +261 -0
  197. package/corpus/core/src/observability/store.ts +23 -0
  198. package/corpus/core/src/observability/traces.ts +49 -63
  199. package/corpus/core/src/observability/tracking-identity.ts +55 -0
  200. package/corpus/core/src/observability/types.ts +13 -0
  201. package/corpus/core/src/org/accept-pending.ts +9 -1
  202. package/corpus/core/src/org/migrations.ts +41 -0
  203. package/corpus/core/src/package-lifecycle/index.ts +1 -0
  204. package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
  205. package/corpus/core/src/provider-api/custom-registry.ts +56 -0
  206. package/corpus/core/src/provider-api/index.ts +2 -0
  207. package/corpus/core/src/secrets/storage.ts +114 -30
  208. package/corpus/core/src/server/action-discovery.ts +4 -0
  209. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +199 -0
  210. package/corpus/core/src/server/agent-chat/browser-team-tools.ts +510 -0
  211. package/corpus/core/src/server/agent-chat/context-tools.ts +595 -0
  212. package/corpus/core/src/server/agent-chat/framework-prompts.ts +470 -0
  213. package/corpus/core/src/server/agent-chat/lazy-fs.ts +11 -0
  214. package/corpus/core/src/server/agent-chat/mcp-glue.ts +91 -0
  215. package/corpus/core/src/server/agent-chat/plugin-options.ts +350 -0
  216. package/corpus/core/src/server/agent-chat/process-run-failure.ts +62 -0
  217. package/corpus/core/src/server/agent-chat/prompt-resources.ts +688 -0
  218. package/corpus/core/src/server/agent-chat/recurring-jobs-runtime.ts +80 -0
  219. package/corpus/core/src/server/agent-chat/request-surface.ts +71 -0
  220. package/corpus/core/src/server/agent-chat/run-code-tools.ts +67 -0
  221. package/corpus/core/src/server/agent-chat/script-entries.ts +779 -0
  222. package/corpus/core/src/server/agent-chat/shared-thread.ts +301 -0
  223. package/corpus/core/src/server/agent-chat/skill-frontmatter.ts +41 -0
  224. package/corpus/core/src/server/agent-chat-plugin.ts +403 -4179
  225. package/corpus/core/src/server/agent-teams.ts +26 -3
  226. package/corpus/core/src/server/auth.ts +55 -57
  227. package/corpus/core/src/server/core-routes-plugin.ts +30 -14
  228. package/corpus/core/src/server/credential-provider.ts +22 -25
  229. package/corpus/core/src/server/csrf.ts +24 -5
  230. package/corpus/core/src/server/edge.ts +1 -0
  231. package/corpus/core/src/server/framework-request-handler.ts +34 -0
  232. package/corpus/core/src/server/google-realtime-session.ts +1 -36
  233. package/corpus/core/src/server/onboarding-html.ts +1 -16
  234. package/corpus/core/src/server/poll.ts +24 -2
  235. package/corpus/core/src/server/realtime-voice.ts +49 -5
  236. package/corpus/core/src/server/request-origin.ts +67 -0
  237. package/corpus/core/src/server/ssr-handler.ts +55 -2
  238. package/corpus/core/src/server/transcribe-voice.ts +1 -50
  239. package/corpus/core/src/sharing/access.ts +155 -11
  240. package/corpus/core/src/sharing/registry.ts +7 -0
  241. package/corpus/core/src/styles/agent-native.css +15 -14
  242. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +1 -1
  243. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -0
  244. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -0
  245. package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -0
  246. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -0
  247. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -0
  248. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -0
  249. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -0
  250. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -0
  251. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -0
  252. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +6 -0
  253. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +6 -0
  254. package/corpus/core/src/templates/default/package.json +2 -2
  255. package/corpus/core/src/templates/headless/package.json +0 -1
  256. package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  257. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +27 -29
  258. package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  259. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +2 -1
  260. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -1
  261. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +32 -0
  262. package/corpus/core/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +25 -0
  263. package/corpus/core/src/templates/workspace-core/package.json +0 -1
  264. package/corpus/core/src/templates/workspace-core/src/server/index.ts +4 -2
  265. package/corpus/core/src/templates/workspace-root/package.json +0 -1
  266. package/corpus/core/src/testing.ts +26 -0
  267. package/corpus/core/src/triggers/actions.ts +11 -3
  268. package/corpus/core/src/triggers/dispatcher.ts +23 -2
  269. package/corpus/core/src/triggers/types.ts +7 -1
  270. package/corpus/core/src/vite/client.ts +126 -2
  271. package/corpus/core/tsconfig.cli.json +1 -1
  272. package/corpus/core/tsconfig.json +5 -1
  273. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +2 -2
  274. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +1 -1
  275. package/corpus/templates/analytics/AGENTS.md +19 -6
  276. package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
  277. package/corpus/templates/analytics/actions/install-dashboard-template.ts +79 -43
  278. package/corpus/templates/analytics/actions/list-explorer-dashboards.ts +3 -3
  279. package/corpus/templates/analytics/actions/list-sql-dashboards.ts +8 -3
  280. package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
  281. package/corpus/templates/analytics/actions/rename-analysis.ts +8 -2
  282. package/corpus/templates/analytics/actions/rename-dashboard.ts +8 -7
  283. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
  284. package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
  285. package/corpus/templates/analytics/actions/view-screen.ts +4 -23
  286. package/corpus/templates/analytics/app/components/ResourceLoadError.tsx +42 -0
  287. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +85 -70
  288. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +107 -89
  289. package/corpus/templates/analytics/app/components/layout/SidebarLoadError.tsx +24 -0
  290. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +15 -5
  291. package/corpus/templates/analytics/app/i18n/zh-TW.ts +4 -0
  292. package/corpus/templates/analytics/app/i18n-data.ts +40 -0
  293. package/corpus/templates/analytics/app/pages/Settings.tsx +6 -2
  294. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/SqlPreview.tsx +36 -20
  295. package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +34 -20
  296. package/corpus/templates/analytics/app/pages/adhoc/explorer/use-explorer-config.ts +42 -41
  297. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +43 -36
  298. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +11 -2
  299. package/corpus/templates/analytics/app/root.tsx +13 -1
  300. package/corpus/templates/analytics/app/routes/_index.tsx +14 -2
  301. package/corpus/templates/analytics/changelog/2026-07-03-agent-llm-observability-dashboard.md +1 -1
  302. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
  303. package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
  304. package/corpus/templates/analytics/changelog/2026-07-11-agent-feedback-sentiment-dashboard.md +6 -0
  305. package/corpus/templates/analytics/changelog/2026-07-11-analytics-chats-now-start-faster-by-loading-metric-definitio.md +6 -0
  306. package/corpus/templates/analytics/changelog/2026-07-11-command-and-sql-previews-now-respond-without-lag.md +6 -0
  307. package/corpus/templates/analytics/changelog/2026-07-11-command-search-now-reports-loading-failures-and-lets-you-ret.md +6 -0
  308. package/corpus/templates/analytics/changelog/2026-07-11-dashboard-navigation-now-loads-faster-and-reports-request-fa.md +6 -0
  309. package/corpus/templates/analytics/changelog/2026-07-11-inferred-message-sentiment-dashboard.md +6 -0
  310. package/corpus/templates/analytics/changelog/2026-07-11-microphone-settings-from-realtime-voice-mode-now-open-the-vo.md +6 -0
  311. package/corpus/templates/analytics/changelog/2026-07-11-more-dashboard-edit-paths-are-now-safe-against-simultaneous-.md +6 -0
  312. package/corpus/templates/analytics/changelog/2026-07-11-renaming-a-dashboard-is-now-safe-against-simultaneous-agent-.md +6 -0
  313. package/corpus/templates/analytics/changelog/2026-07-11-renaming-an-analysis-is-now-safe-against-simultaneous-edits.md +6 -0
  314. package/corpus/templates/analytics/changelog/2026-07-11-workspace-mounted-analytics-now-opens-ask-reliably-and-route.md +6 -0
  315. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +37 -3
  316. package/corpus/templates/analytics/package.json +1 -2
  317. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +339 -1
  318. package/corpus/templates/analytics/server/lib/bigquery.ts +3 -2
  319. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -24
  320. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +0 -5
  321. package/corpus/templates/analytics/server/lib/dashboards-store.ts +443 -31
  322. package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +106 -34
  323. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +4 -1
  324. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -39
  325. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +1 -1
  326. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +30 -4
  327. package/corpus/templates/assets/app/routes/library.tsx +49 -5
  328. package/corpus/templates/assets/changelog/2026-07-11-library-load-failures-now-show-a-retry-action.md +6 -0
  329. package/corpus/templates/assets/package.json +1 -2
  330. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +1 -1
  331. package/corpus/templates/brain/package.json +1 -2
  332. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +1 -1
  333. package/corpus/templates/calendar/.env.example +5 -0
  334. package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +2 -2
  335. package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
  336. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  337. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +37 -1
  338. package/corpus/templates/calendar/app/pages/BookingsList.tsx +20 -3
  339. package/corpus/templates/calendar/changelog/2026-07-11-booking-load-failures-now-show-a-retry-action.md +6 -0
  340. package/corpus/templates/calendar/changelog/2026-07-11-event-cards-give-subtle-press-feedback-and-smoother-hover.md +6 -0
  341. package/corpus/templates/calendar/package.json +1 -2
  342. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -1
  343. package/corpus/templates/chat/package.json +1 -2
  344. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +1 -1
  345. package/corpus/templates/clips/actions/decline-invite.ts +8 -0
  346. package/corpus/templates/clips/app/components/library/folder-tree.tsx +209 -187
  347. package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +2 -2
  348. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +17 -5
  349. package/corpus/templates/clips/app/components/player/scrubber.tsx +5 -3
  350. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +2 -2
  351. package/corpus/templates/clips/app/components/workspace/insights-hub.tsx +119 -95
  352. package/corpus/templates/clips/app/global.css +45 -0
  353. package/corpus/templates/clips/app/root.tsx +4 -0
  354. package/corpus/templates/clips/app/routes/_app.notifications.tsx +23 -2
  355. package/corpus/templates/clips/app/routes/_app.trash.tsx +24 -2
  356. package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
  357. package/corpus/templates/clips/changelog/2026-07-11-controls-and-folders-now-move-more-smoothly.md +6 -0
  358. package/corpus/templates/clips/changelog/2026-07-11-declining-an-invitation-now-requires-being-its-recipient.md +6 -0
  359. package/corpus/templates/clips/changelog/2026-07-11-recording-overlays-are-lighter-weight-and-no-longer-cause-pa.md +6 -0
  360. package/corpus/templates/clips/changelog/2026-07-11-workspace-load-failures-now-show-a-retry-action.md +6 -0
  361. package/corpus/templates/clips/chrome-extension/package.json +3 -2
  362. package/corpus/templates/clips/chrome-extension/src/overlay.css +34 -5
  363. package/corpus/templates/clips/chrome-extension/src/styles.css +2 -2
  364. package/corpus/templates/clips/desktop/package.json +6 -4
  365. package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
  366. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
  367. package/corpus/templates/clips/desktop/src/styles.css +71 -26
  368. package/corpus/templates/clips/package.json +1 -2
  369. package/corpus/templates/clips/server/db/schema.ts +29 -17
  370. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  371. package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
  372. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +1 -1
  373. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
  374. package/corpus/templates/content/AGENTS.md +72 -62
  375. package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
  376. package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
  377. package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
  378. package/corpus/templates/content/actions/_position-utils.ts +76 -0
  379. package/corpus/templates/content/actions/_property-utils.ts +31 -20
  380. package/corpus/templates/content/actions/add-database-item.ts +52 -39
  381. package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
  382. package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
  383. package/corpus/templates/content/actions/configure-document-property.ts +36 -27
  384. package/corpus/templates/content/actions/create-content-database.ts +40 -30
  385. package/corpus/templates/content/actions/create-document.ts +39 -32
  386. package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
  387. package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
  388. package/corpus/templates/content/actions/move-document.ts +93 -61
  389. package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
  390. package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
  391. package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
  392. package/corpus/templates/content/app/components/QueryErrorState.tsx +39 -0
  393. package/corpus/templates/content/app/components/editor/LinkHoverPreview.tsx +1 -1
  394. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +323 -1467
  395. package/corpus/templates/content/app/components/editor/database/GalleryView.tsx +368 -0
  396. package/corpus/templates/content/app/components/editor/database/ListView.tsx +353 -0
  397. package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +580 -0
  398. package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
  399. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -2
  400. package/corpus/templates/content/app/global.css +19 -19
  401. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
  402. package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
  403. package/corpus/templates/content/app/i18n-data.ts +40 -0
  404. package/corpus/templates/content/app/root.tsx +2 -2
  405. package/corpus/templates/content/app/routes/_app._index.tsx +11 -1
  406. package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
  407. package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
  408. package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
  409. package/corpus/templates/content/changelog/2026-07-11-document-load-failures-now-show-a-retry-action.md +6 -0
  410. package/corpus/templates/content/changelog/2026-07-11-editor-toggle-feedback-now-feels-more-responsive.md +6 -0
  411. package/corpus/templates/content/changelog/2026-07-11-moving-documents-at-the-same-time-no-longer-scrambles-their-.md +6 -0
  412. package/corpus/templates/content/changelog/2026-07-11-public-document-chats-now-start-faster-while-keeping-full-do.md +6 -0
  413. package/corpus/templates/content/changelog/2026-07-11-reopening-a-comment-now-uses-consistent-permissions-everywhe.md +6 -0
  414. package/corpus/templates/content/changelog/2026-07-11-sidebar-resizing-now-tracks-your-cursor-instantly-instead-of.md +6 -0
  415. package/corpus/templates/content/package.json +1 -2
  416. package/corpus/templates/content/parity/matrix.md +25 -25
  417. package/corpus/templates/content/parity/matrix.ts +1 -0
  418. package/corpus/templates/content/server/lib/public-documents.ts +47 -10
  419. package/corpus/templates/content/server/routes/api/comments/[id].patch.ts +24 -24
  420. package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
  421. package/corpus/templates/content/shared/api.ts +16 -1
  422. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +1 -1
  423. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
  424. package/corpus/templates/design/AGENTS.md +12 -2
  425. package/corpus/templates/design/DEVELOPING.md +10 -6
  426. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
  427. package/corpus/templates/design/actions/duplicate-design.ts +31 -25
  428. package/corpus/templates/design/app/components/QueryErrorState.tsx +24 -0
  429. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +1 -1
  430. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +5 -2
  431. package/corpus/templates/design/app/components/design/EditPanel.tsx +326 -319
  432. package/corpus/templates/design/app/components/design/LayersPanel.tsx +198 -193
  433. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +11 -4
  434. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +1 -1
  435. package/corpus/templates/design/app/components/layout/Layout.tsx +1 -1
  436. package/corpus/templates/design/app/pages/DesignSystems.tsx +7 -1
  437. package/corpus/templates/design/app/pages/Index.tsx +14 -2
  438. package/corpus/templates/design/app/pages/Present.tsx +16 -3
  439. package/corpus/templates/design/app/pages/Templates.tsx +10 -4
  440. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +484 -0
  441. package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +378 -0
  442. package/corpus/templates/design/app/pages/design-editor/design-data-geometry-utils.ts +138 -0
  443. package/corpus/templates/design/app/pages/design-editor/dom-utils.ts +15 -0
  444. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +180 -0
  445. package/corpus/templates/design/app/pages/design-editor/html-layer-positioning.ts +226 -0
  446. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
  447. package/corpus/templates/design/app/pages/design-editor/png-export-render.ts +348 -0
  448. package/corpus/templates/design/app/pages/design-editor/screen-command-utils.ts +160 -0
  449. package/corpus/templates/design/app/pages/design-editor/text-edit-utils.ts +238 -0
  450. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +23 -1
  451. package/corpus/templates/design/app/pages/design-editor/types.ts +25 -0
  452. package/corpus/templates/design/app/root.tsx +10 -1
  453. package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
  454. package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
  455. package/corpus/templates/design/changelog/2026-07-11-canvas-context-menus-now-open-without-motion-jitter.md +6 -0
  456. package/corpus/templates/design/changelog/2026-07-11-design-load-failures-now-show-a-retry-action.md +6 -0
  457. package/corpus/templates/design/changelog/2026-07-11-inspector-tooltips-appear-instantly-as-you-sweep-across-icon.md +6 -0
  458. package/corpus/templates/design/package.json +1 -1
  459. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
  460. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +1 -1
  461. package/corpus/templates/dispatch/app/i18n/zh-TW.ts +10 -0
  462. package/corpus/templates/dispatch/app/i18n-data.ts +123 -0
  463. package/corpus/templates/dispatch/app/routes/integrations.tsx +163 -144
  464. package/corpus/templates/dispatch/changelog/2026-07-11-dispatch-mcp-connections-now-start-with-safer-app-access-and.md +6 -0
  465. package/corpus/templates/dispatch/changelog/2026-07-11-overview-and-chat-now-share-a-cleaner-more-stable-flow.md +6 -0
  466. package/corpus/templates/dispatch/changelog/2026-07-11-query-errors-now-show-a-retry-option.md +6 -0
  467. package/corpus/templates/dispatch/package.json +1 -2
  468. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +1 -1
  469. package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
  470. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +3 -3
  471. package/corpus/templates/forms/app/root.tsx +34 -8
  472. package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
  473. package/corpus/templates/forms/package.json +1 -2
  474. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +1 -1
  475. package/corpus/templates/macros/app/components/DailyProgress.tsx +25 -5
  476. package/corpus/templates/macros/app/components/QueryErrorState.tsx +31 -0
  477. package/corpus/templates/macros/app/components/WeightTracker.tsx +5 -1
  478. package/corpus/templates/macros/app/components/ui/button.tsx +1 -1
  479. package/corpus/templates/macros/app/components/ui/progress.tsx +1 -1
  480. package/corpus/templates/macros/app/components/ui/tabs.tsx +1 -1
  481. package/corpus/templates/macros/app/i18n/ar-SA.ts +2 -0
  482. package/corpus/templates/macros/app/i18n/de-DE.ts +2 -0
  483. package/corpus/templates/macros/app/i18n/en-US.ts +2 -0
  484. package/corpus/templates/macros/app/i18n/es-ES.ts +2 -0
  485. package/corpus/templates/macros/app/i18n/fr-FR.ts +2 -0
  486. package/corpus/templates/macros/app/i18n/hi-IN.ts +2 -0
  487. package/corpus/templates/macros/app/i18n/ja-JP.ts +2 -0
  488. package/corpus/templates/macros/app/i18n/ko-KR.ts +2 -0
  489. package/corpus/templates/macros/app/i18n/pt-BR.ts +2 -0
  490. package/corpus/templates/macros/app/i18n/zh-CN.ts +2 -0
  491. package/corpus/templates/macros/app/i18n/zh-TW.ts +2 -0
  492. package/corpus/templates/macros/app/routes/_index.tsx +22 -12
  493. package/corpus/templates/macros/app/routes/analytics.tsx +31 -10
  494. package/corpus/templates/macros/changelog/2026-07-11-daily-tracking-now-loads-and-updates-more-quickly.md +6 -0
  495. package/corpus/templates/macros/changelog/2026-07-11-data-load-failures-now-show-a-clear-error-with-a-retry-actio.md +6 -0
  496. package/corpus/templates/macros/package.json +1 -2
  497. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +1 -1
  498. package/corpus/templates/mail/.env.example +16 -11
  499. package/corpus/templates/mail/actions/list-emails.ts +39 -35
  500. package/corpus/templates/mail/app/components/email/ComposeModal.tsx +58 -6
  501. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +35 -2
  502. package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
  503. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +113 -104
  504. package/corpus/templates/mail/app/root.tsx +8 -10
  505. package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
  506. package/corpus/templates/mail/changelog/2026-07-11-compose-and-navigation-motion-now-feel-faster-and-smoother.md +6 -0
  507. package/corpus/templates/mail/changelog/2026-07-11-swipe-to-archive-or-snooze-now-responds-to-quick-flicks-not-.md +6 -0
  508. package/corpus/templates/mail/changelog/2026-07-11-the-assistant-now-sees-the-same-inbox-you-do-snoozed-mail-st.md +6 -0
  509. package/corpus/templates/mail/package.json +1 -2
  510. package/corpus/templates/mail/server/handlers/emails.ts +25 -69
  511. package/corpus/templates/mail/server/lib/list-inbox-emails.ts +169 -0
  512. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +1 -1
  513. package/corpus/templates/plan/.env.example +4 -0
  514. package/corpus/templates/plan/actions/create-plan-design.ts +89 -81
  515. package/corpus/templates/plan/actions/create-prototype-plan.ts +72 -59
  516. package/corpus/templates/plan/actions/create-ui-plan.ts +70 -55
  517. package/corpus/templates/plan/actions/create-visual-plan.ts +65 -51
  518. package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
  519. package/corpus/templates/plan/actions/update-local-plan-folder.ts +43 -30
  520. package/corpus/templates/plan/actions/update-visual-plan.ts +81 -64
  521. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +17 -0
  522. package/corpus/templates/plan/app/components/ui/tabs.tsx +1 -1
  523. package/corpus/templates/plan/app/i18n/ar-SA.ts +10 -0
  524. package/corpus/templates/plan/app/i18n/de-DE.ts +10 -0
  525. package/corpus/templates/plan/app/i18n/en-US.ts +10 -0
  526. package/corpus/templates/plan/app/i18n/es-ES.ts +10 -0
  527. package/corpus/templates/plan/app/i18n/fr-FR.ts +10 -0
  528. package/corpus/templates/plan/app/i18n/hi-IN.ts +10 -0
  529. package/corpus/templates/plan/app/i18n/ja-JP.ts +10 -0
  530. package/corpus/templates/plan/app/i18n/ko-KR.ts +10 -0
  531. package/corpus/templates/plan/app/i18n/pt-BR.ts +10 -0
  532. package/corpus/templates/plan/app/i18n/zh-CN.ts +10 -0
  533. package/corpus/templates/plan/app/i18n/zh-TW.ts +10 -0
  534. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +80 -1
  535. package/corpus/templates/plan/app/pages/PlansPage.tsx +152 -4
  536. package/corpus/templates/plan/app/root.tsx +11 -0
  537. package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
  538. package/corpus/templates/plan/changelog/2026-07-11-local-plans-now-explain-and-recover-from-browser-permission-.md +6 -0
  539. package/corpus/templates/plan/changelog/2026-07-11-plan-edits-now-save-atomically-and-can-no-longer-partially-a.md +6 -0
  540. package/corpus/templates/plan/changelog/2026-07-11-plan-lists-now-show-a-clear-error-with-a-retry-action-when-t.md +6 -0
  541. package/corpus/templates/plan/package.json +1 -2
  542. package/corpus/templates/plan/server/plans.ts +9 -2
  543. package/corpus/templates/plan/shared/plan-content.ts +546 -260
  544. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +1 -1
  545. package/corpus/templates/slides/actions/delete-slide-comment.ts +49 -0
  546. package/corpus/templates/slides/actions/list-decks.ts +28 -0
  547. package/corpus/templates/slides/actions/update-slide-comment.ts +93 -0
  548. package/corpus/templates/slides/actions/view-screen.ts +14 -6
  549. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +33 -10
  550. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
  551. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
  552. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +3 -5
  553. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +5 -9
  554. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
  555. package/corpus/templates/slides/app/components/editor/GenerateSlidesDialog.tsx +2 -2
  556. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
  557. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
  558. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
  559. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +4 -2
  560. package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +4 -3
  561. package/corpus/templates/slides/app/context/DeckContext.tsx +56 -4
  562. package/corpus/templates/slides/app/i18n/ar-SA.ts +6 -0
  563. package/corpus/templates/slides/app/i18n/de-DE.ts +6 -0
  564. package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
  565. package/corpus/templates/slides/app/i18n/es-ES.ts +6 -0
  566. package/corpus/templates/slides/app/i18n/fr-FR.ts +6 -0
  567. package/corpus/templates/slides/app/i18n/hi-IN.ts +6 -0
  568. package/corpus/templates/slides/app/i18n/ja-JP.ts +6 -0
  569. package/corpus/templates/slides/app/i18n/ko-KR.ts +6 -0
  570. package/corpus/templates/slides/app/i18n/pt-BR.ts +6 -0
  571. package/corpus/templates/slides/app/i18n/zh-CN.ts +5 -0
  572. package/corpus/templates/slides/app/i18n/zh-TW.ts +5 -0
  573. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
  574. package/corpus/templates/slides/app/pages/DesignSystems.tsx +23 -2
  575. package/corpus/templates/slides/app/pages/Index.tsx +31 -4
  576. package/corpus/templates/slides/app/root.tsx +0 -5
  577. package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
  578. package/corpus/templates/slides/changelog/2026-07-11-comment-load-failures-now-show-a-retry-action-instead-of-say.md +6 -0
  579. package/corpus/templates/slides/changelog/2026-07-11-comment-pins-now-clear-with-a-faster-smoother-confirmation.md +6 -0
  580. package/corpus/templates/slides/changelog/2026-07-11-deck-and-design-system-load-failures-now-show-a-retry-action.md +6 -0
  581. package/corpus/templates/slides/changelog/2026-07-11-editor-toggles-and-toolbar-menus-now-animate-smoothly.md +6 -0
  582. package/corpus/templates/slides/changelog/2026-07-11-the-assistant-can-now-resolve-reopen-and-delete-slide-commen.md +6 -0
  583. package/corpus/templates/slides/changelog/2026-07-11-viewing-slides-no-longer-loads-every-deck-s-full-contents-in.md +6 -0
  584. package/corpus/templates/slides/package.json +1 -2
  585. package/corpus/templates/slides/server/routes/api/comments/[id].delete.ts +29 -48
  586. package/corpus/templates/slides/server/routes/api/comments/[id].patch.ts +29 -65
  587. package/dist/a2a/auth-policy.d.ts +19 -0
  588. package/dist/a2a/auth-policy.d.ts.map +1 -1
  589. package/dist/a2a/auth-policy.js +32 -0
  590. package/dist/a2a/auth-policy.js.map +1 -1
  591. package/dist/action.d.ts +23 -0
  592. package/dist/action.d.ts.map +1 -1
  593. package/dist/action.js +13 -0
  594. package/dist/action.js.map +1 -1
  595. package/dist/agent/durable-background.d.ts +8 -5
  596. package/dist/agent/durable-background.d.ts.map +1 -1
  597. package/dist/agent/durable-background.js +15 -8
  598. package/dist/agent/durable-background.js.map +1 -1
  599. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  600. package/dist/agent/engine/builder-engine.js +3 -0
  601. package/dist/agent/engine/builder-engine.js.map +1 -1
  602. package/dist/agent/engine/context-directives-transform.d.ts +25 -0
  603. package/dist/agent/engine/context-directives-transform.d.ts.map +1 -0
  604. package/dist/agent/engine/context-directives-transform.js +53 -0
  605. package/dist/agent/engine/context-directives-transform.js.map +1 -0
  606. package/dist/agent/engine/continuation-dispatch-retry.d.ts +40 -0
  607. package/dist/agent/engine/continuation-dispatch-retry.d.ts.map +1 -0
  608. package/dist/agent/engine/continuation-dispatch-retry.js +109 -0
  609. package/dist/agent/engine/continuation-dispatch-retry.js.map +1 -0
  610. package/dist/agent/engine/tool-call-journal-seed.d.ts +46 -0
  611. package/dist/agent/engine/tool-call-journal-seed.d.ts.map +1 -0
  612. package/dist/agent/engine/tool-call-journal-seed.js +60 -0
  613. package/dist/agent/engine/tool-call-journal-seed.js.map +1 -0
  614. package/dist/agent/production-agent.d.ts +7 -0
  615. package/dist/agent/production-agent.d.ts.map +1 -1
  616. package/dist/agent/production-agent.js +102 -222
  617. package/dist/agent/production-agent.js.map +1 -1
  618. package/dist/agent/run-store.d.ts.map +1 -1
  619. package/dist/agent/run-store.js +9 -4
  620. package/dist/agent/run-store.js.map +1 -1
  621. package/dist/audit/actions/export-audit-events.d.ts +27 -0
  622. package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
  623. package/dist/audit/actions/export-audit-events.js +144 -0
  624. package/dist/audit/actions/export-audit-events.js.map +1 -0
  625. package/dist/audit/store.d.ts +1 -0
  626. package/dist/audit/store.d.ts.map +1 -1
  627. package/dist/audit/store.js +8 -3
  628. package/dist/audit/store.js.map +1 -1
  629. package/dist/audit/types.d.ts +3 -0
  630. package/dist/audit/types.d.ts.map +1 -1
  631. package/dist/audit/types.js.map +1 -1
  632. package/dist/cli/doctor.d.ts +83 -0
  633. package/dist/cli/doctor.d.ts.map +1 -0
  634. package/dist/cli/doctor.js +281 -0
  635. package/dist/cli/doctor.js.map +1 -0
  636. package/dist/cli/index.js +50 -0
  637. package/dist/cli/index.js.map +1 -1
  638. package/dist/cli/info.js +3 -3
  639. package/dist/cli/info.js.map +1 -1
  640. package/dist/cli/package-lifecycle.d.ts +50 -0
  641. package/dist/cli/package-lifecycle.d.ts.map +1 -0
  642. package/dist/cli/package-lifecycle.js +560 -0
  643. package/dist/cli/package-lifecycle.js.map +1 -0
  644. package/dist/cli/plan-local.d.ts.map +1 -1
  645. package/dist/cli/plan-local.js +8 -2
  646. package/dist/cli/plan-local.js.map +1 -1
  647. package/dist/cli/skills-content/assets-skill.d.ts +2 -0
  648. package/dist/cli/skills-content/assets-skill.d.ts.map +1 -0
  649. package/dist/cli/skills-content/assets-skill.js +84 -0
  650. package/dist/cli/skills-content/assets-skill.js.map +1 -0
  651. package/dist/cli/skills-content/canvas.d.ts +4 -0
  652. package/dist/cli/skills-content/canvas.d.ts.map +1 -0
  653. package/dist/cli/skills-content/canvas.js +152 -0
  654. package/dist/cli/skills-content/canvas.js.map +1 -0
  655. package/dist/cli/skills-content/connection.d.ts +2 -0
  656. package/dist/cli/skills-content/connection.d.ts.map +1 -0
  657. package/dist/cli/skills-content/connection.js +53 -0
  658. package/dist/cli/skills-content/connection.js.map +1 -0
  659. package/dist/cli/skills-content/content-skill.d.ts +2 -0
  660. package/dist/cli/skills-content/content-skill.d.ts.map +1 -0
  661. package/dist/cli/skills-content/content-skill.js +110 -0
  662. package/dist/cli/skills-content/content-skill.js.map +1 -0
  663. package/dist/cli/skills-content/design-exploration-skill.d.ts +2 -0
  664. package/dist/cli/skills-content/design-exploration-skill.d.ts.map +1 -0
  665. package/dist/cli/skills-content/design-exploration-skill.js +119 -0
  666. package/dist/cli/skills-content/design-exploration-skill.js.map +1 -0
  667. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +2 -0
  668. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -0
  669. package/dist/cli/skills-content/design-visual-edit-skill.js +229 -0
  670. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -0
  671. package/dist/cli/skills-content/document-quality.d.ts +4 -0
  672. package/dist/cli/skills-content/document-quality.d.ts.map +1 -0
  673. package/dist/cli/skills-content/document-quality.js +195 -0
  674. package/dist/cli/skills-content/document-quality.js.map +1 -0
  675. package/dist/cli/skills-content/exemplar.d.ts +4 -0
  676. package/dist/cli/skills-content/exemplar.d.ts.map +1 -0
  677. package/dist/cli/skills-content/exemplar.js +68 -0
  678. package/dist/cli/skills-content/exemplar.js.map +1 -0
  679. package/dist/cli/skills-content/help.d.ts +2 -0
  680. package/dist/cli/skills-content/help.d.ts.map +1 -0
  681. package/dist/cli/skills-content/help.js +78 -0
  682. package/dist/cli/skills-content/help.js.map +1 -0
  683. package/dist/cli/skills-content/index.d.ts +23 -0
  684. package/dist/cli/skills-content/index.d.ts.map +1 -0
  685. package/dist/cli/skills-content/index.js +23 -0
  686. package/dist/cli/skills-content/index.js.map +1 -0
  687. package/dist/cli/skills-content/local-files.d.ts +2 -0
  688. package/dist/cli/skills-content/local-files.d.ts.map +1 -0
  689. package/dist/cli/skills-content/local-files.js +96 -0
  690. package/dist/cli/skills-content/local-files.js.map +1 -0
  691. package/dist/cli/skills-content/plan-setup-auth.d.ts +9 -0
  692. package/dist/cli/skills-content/plan-setup-auth.d.ts.map +1 -0
  693. package/dist/cli/skills-content/plan-setup-auth.js +81 -0
  694. package/dist/cli/skills-content/plan-setup-auth.js.map +1 -0
  695. package/dist/cli/skills-content/visual-plan-skill.d.ts +2 -0
  696. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -0
  697. package/dist/cli/skills-content/visual-plan-skill.js +471 -0
  698. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -0
  699. package/dist/cli/skills-content/visual-recap-skill.d.ts +2 -0
  700. package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -0
  701. package/dist/cli/skills-content/visual-recap-skill.js +548 -0
  702. package/dist/cli/skills-content/visual-recap-skill.js.map +1 -0
  703. package/dist/cli/skills-content/visualize-repo-skill.d.ts +2 -0
  704. package/dist/cli/skills-content/visualize-repo-skill.d.ts.map +1 -0
  705. package/dist/cli/skills-content/visualize-repo-skill.js +94 -0
  706. package/dist/cli/skills-content/visualize-repo-skill.js.map +1 -0
  707. package/dist/cli/skills-content/wireframe.d.ts +4 -0
  708. package/dist/cli/skills-content/wireframe.d.ts.map +1 -0
  709. package/dist/cli/skills-content/wireframe.js +347 -0
  710. package/dist/cli/skills-content/wireframe.js.map +1 -0
  711. package/dist/cli/skills.d.ts +2 -10
  712. package/dist/cli/skills.d.ts.map +1 -1
  713. package/dist/cli/skills.js +2 -2710
  714. package/dist/cli/skills.js.map +1 -1
  715. package/dist/client/AgentPanel.d.ts.map +1 -1
  716. package/dist/client/AgentPanel.js +4 -2
  717. package/dist/client/AgentPanel.js.map +1 -1
  718. package/dist/client/CommandMenu.d.ts.map +1 -1
  719. package/dist/client/CommandMenu.js +15 -3
  720. package/dist/client/CommandMenu.js.map +1 -1
  721. package/dist/client/FeedbackButton.js +2 -2
  722. package/dist/client/FeedbackButton.js.map +1 -1
  723. package/dist/client/agent-chat-adapter.js +7 -3
  724. package/dist/client/agent-chat-adapter.js.map +1 -1
  725. package/dist/client/app-providers.d.ts +13 -5
  726. package/dist/client/app-providers.d.ts.map +1 -1
  727. package/dist/client/app-providers.js +9 -6
  728. package/dist/client/app-providers.js.map +1 -1
  729. package/dist/client/chat/tool-call-display.d.ts +4 -0
  730. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  731. package/dist/client/chat/tool-call-display.js +3 -3
  732. package/dist/client/chat/tool-call-display.js.map +1 -1
  733. package/dist/client/composer/PastedTextChip.js +1 -1
  734. package/dist/client/composer/PastedTextChip.js.map +1 -1
  735. package/dist/client/composer/RealtimeVoiceMode.d.ts +3 -1
  736. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  737. package/dist/client/composer/RealtimeVoiceMode.js +35 -14
  738. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  739. package/dist/client/composer/TiptapComposer.js +1 -1
  740. package/dist/client/composer/TiptapComposer.js.map +1 -1
  741. package/dist/client/composer/VoiceButton.d.ts +2 -0
  742. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  743. package/dist/client/composer/VoiceButton.js +9 -3
  744. package/dist/client/composer/VoiceButton.js.map +1 -1
  745. package/dist/client/composer/useRealtimeVoiceMode.d.ts +5 -0
  746. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  747. package/dist/client/composer/useRealtimeVoiceMode.js +91 -12
  748. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  749. package/dist/client/context-xray/ContextXRayPanel.d.ts.map +1 -1
  750. package/dist/client/context-xray/ContextXRayPanel.js +4 -1
  751. package/dist/client/context-xray/ContextXRayPanel.js.map +1 -1
  752. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
  753. package/dist/client/extensions/ExtensionEditor.js +15 -5
  754. package/dist/client/extensions/ExtensionEditor.js.map +1 -1
  755. package/dist/client/extensions/ExtensionQueryErrorState.d.ts +8 -0
  756. package/dist/client/extensions/ExtensionQueryErrorState.d.ts.map +1 -0
  757. package/dist/client/extensions/ExtensionQueryErrorState.js +11 -0
  758. package/dist/client/extensions/ExtensionQueryErrorState.js.map +1 -0
  759. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
  760. package/dist/client/extensions/ExtensionSlot.js +17 -7
  761. package/dist/client/extensions/ExtensionSlot.js.map +1 -1
  762. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  763. package/dist/client/extensions/ExtensionViewer.js +17 -7
  764. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  765. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  766. package/dist/client/extensions/ExtensionsListPage.js +5 -3
  767. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  768. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  769. package/dist/client/extensions/ExtensionsSidebarSection.js +21 -8
  770. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  771. package/dist/client/index.d.ts +0 -2
  772. package/dist/client/index.d.ts.map +1 -1
  773. package/dist/client/index.js +0 -5
  774. package/dist/client/index.js.map +1 -1
  775. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  776. package/dist/client/progress/RunsTray.js +7 -3
  777. package/dist/client/progress/RunsTray.js.map +1 -1
  778. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  779. package/dist/client/settings/SettingsTabsPage.js +4 -0
  780. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  781. package/dist/client/settings/useBuilderStatus.d.ts +2 -0
  782. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  783. package/dist/client/settings/useBuilderStatus.js +4 -0
  784. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  785. package/dist/client/tool-cells/EditCell.d.ts.map +1 -1
  786. package/dist/client/tool-cells/EditCell.js +2 -1
  787. package/dist/client/tool-cells/EditCell.js.map +1 -1
  788. package/dist/client/tool-cells/FilesChangedSummary.d.ts.map +1 -1
  789. package/dist/client/tool-cells/FilesChangedSummary.js +2 -1
  790. package/dist/client/tool-cells/FilesChangedSummary.js.map +1 -1
  791. package/dist/client/tool-cells/WriteCell.d.ts.map +1 -1
  792. package/dist/client/tool-cells/WriteCell.js +2 -1
  793. package/dist/client/tool-cells/WriteCell.js.map +1 -1
  794. package/dist/client/visual-style-controls.js +1 -1
  795. package/dist/client/visual-style-controls.js.map +1 -1
  796. package/dist/collab/routes.d.ts +1 -1
  797. package/dist/collab/struct-routes.d.ts +1 -1
  798. package/dist/deploy/build.d.ts.map +1 -1
  799. package/dist/deploy/build.js +36 -14
  800. package/dist/deploy/build.js.map +1 -1
  801. package/dist/guards/db-tool-scoping.d.ts +38 -0
  802. package/dist/guards/db-tool-scoping.d.ts.map +1 -0
  803. package/dist/guards/db-tool-scoping.js +114 -0
  804. package/dist/guards/db-tool-scoping.js.map +1 -0
  805. package/dist/guards/index.d.ts +23 -0
  806. package/dist/guards/index.d.ts.map +1 -0
  807. package/dist/guards/index.js +19 -0
  808. package/dist/guards/index.js.map +1 -0
  809. package/dist/guards/no-drizzle-push.d.ts +14 -0
  810. package/dist/guards/no-drizzle-push.d.ts.map +1 -0
  811. package/dist/guards/no-drizzle-push.js +97 -0
  812. package/dist/guards/no-drizzle-push.js.map +1 -0
  813. package/dist/guards/no-env-credentials.d.ts +36 -0
  814. package/dist/guards/no-env-credentials.d.ts.map +1 -0
  815. package/dist/guards/no-env-credentials.js +204 -0
  816. package/dist/guards/no-env-credentials.js.map +1 -0
  817. package/dist/guards/no-env-mutation.d.ts +23 -0
  818. package/dist/guards/no-env-mutation.d.ts.map +1 -0
  819. package/dist/guards/no-env-mutation.js +90 -0
  820. package/dist/guards/no-env-mutation.js.map +1 -0
  821. package/dist/guards/no-localhost-fallback.d.ts +30 -0
  822. package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
  823. package/dist/guards/no-localhost-fallback.js +105 -0
  824. package/dist/guards/no-localhost-fallback.js.map +1 -0
  825. package/dist/guards/no-unscoped-credentials.d.ts +28 -0
  826. package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
  827. package/dist/guards/no-unscoped-credentials.js +199 -0
  828. package/dist/guards/no-unscoped-credentials.js.map +1 -0
  829. package/dist/guards/no-unscoped-queries.d.ts +32 -0
  830. package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
  831. package/dist/guards/no-unscoped-queries.js +720 -0
  832. package/dist/guards/no-unscoped-queries.js.map +1 -0
  833. package/dist/guards/scan-utils.d.ts +28 -0
  834. package/dist/guards/scan-utils.d.ts.map +1 -0
  835. package/dist/guards/scan-utils.js +92 -0
  836. package/dist/guards/scan-utils.js.map +1 -0
  837. package/dist/guards/types.d.ts +28 -0
  838. package/dist/guards/types.d.ts.map +1 -0
  839. package/dist/guards/types.js +11 -0
  840. package/dist/guards/types.js.map +1 -0
  841. package/dist/integrations/google-docs-poller.d.ts +7 -0
  842. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  843. package/dist/integrations/google-docs-poller.js +13 -3
  844. package/dist/integrations/google-docs-poller.js.map +1 -1
  845. package/dist/integrations/plugin.d.ts.map +1 -1
  846. package/dist/integrations/plugin.js +10 -0
  847. package/dist/integrations/plugin.js.map +1 -1
  848. package/dist/integrations/webhook-handler.d.ts +11 -0
  849. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  850. package/dist/integrations/webhook-handler.js +16 -4
  851. package/dist/integrations/webhook-handler.js.map +1 -1
  852. package/dist/jobs/scheduler.d.ts +12 -0
  853. package/dist/jobs/scheduler.d.ts.map +1 -1
  854. package/dist/jobs/scheduler.js +13 -3
  855. package/dist/jobs/scheduler.js.map +1 -1
  856. package/dist/localization/default-messages.d.ts +7 -0
  857. package/dist/localization/default-messages.d.ts.map +1 -1
  858. package/dist/localization/default-messages.js +8 -1
  859. package/dist/localization/default-messages.js.map +1 -1
  860. package/dist/mcp/oauth-token.d.ts +1 -1
  861. package/dist/mcp/oauth-token.d.ts.map +1 -1
  862. package/dist/mcp/oauth-token.js +6 -1
  863. package/dist/mcp/oauth-token.js.map +1 -1
  864. package/dist/notifications/routes.d.ts +2 -2
  865. package/dist/observability/routes.d.ts.map +1 -1
  866. package/dist/observability/routes.js +32 -0
  867. package/dist/observability/routes.js.map +1 -1
  868. package/dist/observability/sentiment.d.ts +42 -0
  869. package/dist/observability/sentiment.d.ts.map +1 -0
  870. package/dist/observability/sentiment.js +196 -0
  871. package/dist/observability/sentiment.js.map +1 -0
  872. package/dist/observability/store.d.ts +5 -0
  873. package/dist/observability/store.d.ts.map +1 -1
  874. package/dist/observability/store.js +17 -0
  875. package/dist/observability/store.js.map +1 -1
  876. package/dist/observability/traces.d.ts +2 -0
  877. package/dist/observability/traces.d.ts.map +1 -1
  878. package/dist/observability/traces.js +42 -57
  879. package/dist/observability/traces.js.map +1 -1
  880. package/dist/observability/tracking-identity.d.ts +3 -0
  881. package/dist/observability/tracking-identity.d.ts.map +1 -0
  882. package/dist/observability/tracking-identity.js +51 -0
  883. package/dist/observability/tracking-identity.js.map +1 -0
  884. package/dist/observability/types.d.ts +10 -0
  885. package/dist/observability/types.d.ts.map +1 -1
  886. package/dist/observability/types.js +3 -0
  887. package/dist/observability/types.js.map +1 -1
  888. package/dist/org/accept-pending.d.ts.map +1 -1
  889. package/dist/org/accept-pending.js +9 -1
  890. package/dist/org/accept-pending.js.map +1 -1
  891. package/dist/org/migrations.d.ts +7 -2
  892. package/dist/org/migrations.d.ts.map +1 -1
  893. package/dist/org/migrations.js +41 -0
  894. package/dist/org/migrations.js.map +1 -1
  895. package/dist/package-lifecycle/index.d.ts +2 -0
  896. package/dist/package-lifecycle/index.d.ts.map +1 -0
  897. package/dist/package-lifecycle/index.js +2 -0
  898. package/dist/package-lifecycle/index.js.map +1 -0
  899. package/dist/package-lifecycle/manifest.d.ts +24 -0
  900. package/dist/package-lifecycle/manifest.d.ts.map +1 -0
  901. package/dist/package-lifecycle/manifest.js +81 -0
  902. package/dist/package-lifecycle/manifest.js.map +1 -0
  903. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  904. package/dist/provider-api/custom-registry.d.ts +32 -1
  905. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  906. package/dist/provider-api/custom-registry.js +41 -1
  907. package/dist/provider-api/custom-registry.js.map +1 -1
  908. package/dist/provider-api/index.d.ts +1 -1
  909. package/dist/provider-api/index.d.ts.map +1 -1
  910. package/dist/provider-api/index.js +1 -1
  911. package/dist/provider-api/index.js.map +1 -1
  912. package/dist/resources/handlers.d.ts +1 -1
  913. package/dist/secrets/storage.d.ts +6 -0
  914. package/dist/secrets/storage.d.ts.map +1 -1
  915. package/dist/secrets/storage.js +105 -29
  916. package/dist/secrets/storage.js.map +1 -1
  917. package/dist/server/action-discovery.d.ts.map +1 -1
  918. package/dist/server/action-discovery.js +4 -0
  919. package/dist/server/action-discovery.js.map +1 -1
  920. package/dist/server/agent-chat/action-filters-a2a.d.ts +50 -0
  921. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -0
  922. package/dist/server/agent-chat/action-filters-a2a.js +127 -0
  923. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -0
  924. package/dist/server/agent-chat/browser-team-tools.d.ts +21 -0
  925. package/dist/server/agent-chat/browser-team-tools.d.ts.map +1 -0
  926. package/dist/server/agent-chat/browser-team-tools.js +417 -0
  927. package/dist/server/agent-chat/browser-team-tools.js.map +1 -0
  928. package/dist/server/agent-chat/context-tools.d.ts +33 -0
  929. package/dist/server/agent-chat/context-tools.d.ts.map +1 -0
  930. package/dist/server/agent-chat/context-tools.js +520 -0
  931. package/dist/server/agent-chat/context-tools.js.map +1 -0
  932. package/dist/server/agent-chat/framework-prompts.d.ts +65 -0
  933. package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -0
  934. package/dist/server/agent-chat/framework-prompts.js +403 -0
  935. package/dist/server/agent-chat/framework-prompts.js.map +1 -0
  936. package/dist/server/agent-chat/lazy-fs.d.ts +2 -0
  937. package/dist/server/agent-chat/lazy-fs.d.ts.map +1 -0
  938. package/dist/server/agent-chat/lazy-fs.js +11 -0
  939. package/dist/server/agent-chat/lazy-fs.js.map +1 -0
  940. package/dist/server/agent-chat/mcp-glue.d.ts +9 -0
  941. package/dist/server/agent-chat/mcp-glue.d.ts.map +1 -0
  942. package/dist/server/agent-chat/mcp-glue.js +63 -0
  943. package/dist/server/agent-chat/mcp-glue.js.map +1 -0
  944. package/dist/server/agent-chat/plugin-options.d.ts +322 -0
  945. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -0
  946. package/dist/server/agent-chat/plugin-options.js +2 -0
  947. package/dist/server/agent-chat/plugin-options.js.map +1 -0
  948. package/dist/server/agent-chat/process-run-failure.d.ts +12 -0
  949. package/dist/server/agent-chat/process-run-failure.d.ts.map +1 -0
  950. package/dist/server/agent-chat/process-run-failure.js +24 -0
  951. package/dist/server/agent-chat/process-run-failure.js.map +1 -0
  952. package/dist/server/agent-chat/prompt-resources.d.ts +28 -0
  953. package/dist/server/agent-chat/prompt-resources.d.ts.map +1 -0
  954. package/dist/server/agent-chat/prompt-resources.js +514 -0
  955. package/dist/server/agent-chat/prompt-resources.js.map +1 -0
  956. package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +5 -0
  957. package/dist/server/agent-chat/recurring-jobs-runtime.d.ts.map +1 -0
  958. package/dist/server/agent-chat/recurring-jobs-runtime.js +53 -0
  959. package/dist/server/agent-chat/recurring-jobs-runtime.js.map +1 -0
  960. package/dist/server/agent-chat/request-surface.d.ts +8 -0
  961. package/dist/server/agent-chat/request-surface.d.ts.map +1 -0
  962. package/dist/server/agent-chat/request-surface.js +56 -0
  963. package/dist/server/agent-chat/request-surface.js.map +1 -0
  964. package/dist/server/agent-chat/run-code-tools.d.ts +17 -0
  965. package/dist/server/agent-chat/run-code-tools.d.ts.map +1 -0
  966. package/dist/server/agent-chat/run-code-tools.js +55 -0
  967. package/dist/server/agent-chat/run-code-tools.js.map +1 -0
  968. package/dist/server/agent-chat/script-entries.d.ts +47 -0
  969. package/dist/server/agent-chat/script-entries.d.ts.map +1 -0
  970. package/dist/server/agent-chat/script-entries.js +633 -0
  971. package/dist/server/agent-chat/script-entries.js.map +1 -0
  972. package/dist/server/agent-chat/shared-thread.d.ts +33 -0
  973. package/dist/server/agent-chat/shared-thread.d.ts.map +1 -0
  974. package/dist/server/agent-chat/shared-thread.js +242 -0
  975. package/dist/server/agent-chat/shared-thread.js.map +1 -0
  976. package/dist/server/agent-chat/skill-frontmatter.d.ts +7 -0
  977. package/dist/server/agent-chat/skill-frontmatter.d.ts.map +1 -0
  978. package/dist/server/agent-chat/skill-frontmatter.js +26 -0
  979. package/dist/server/agent-chat/skill-frontmatter.js.map +1 -0
  980. package/dist/server/agent-chat-plugin.d.ts +45 -486
  981. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  982. package/dist/server/agent-chat-plugin.js +217 -3015
  983. package/dist/server/agent-chat-plugin.js.map +1 -1
  984. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  985. package/dist/server/agent-teams.d.ts +10 -0
  986. package/dist/server/agent-teams.d.ts.map +1 -1
  987. package/dist/server/agent-teams.js +13 -3
  988. package/dist/server/agent-teams.js.map +1 -1
  989. package/dist/server/auth.d.ts.map +1 -1
  990. package/dist/server/auth.js +50 -52
  991. package/dist/server/auth.js.map +1 -1
  992. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  993. package/dist/server/core-routes-plugin.js +23 -12
  994. package/dist/server/core-routes-plugin.js.map +1 -1
  995. package/dist/server/credential-provider.d.ts.map +1 -1
  996. package/dist/server/credential-provider.js +22 -21
  997. package/dist/server/credential-provider.js.map +1 -1
  998. package/dist/server/csrf.d.ts.map +1 -1
  999. package/dist/server/csrf.js +18 -5
  1000. package/dist/server/csrf.js.map +1 -1
  1001. package/dist/server/edge.d.ts +1 -0
  1002. package/dist/server/edge.d.ts.map +1 -1
  1003. package/dist/server/edge.js +1 -0
  1004. package/dist/server/edge.js.map +1 -1
  1005. package/dist/server/framework-request-handler.d.ts.map +1 -1
  1006. package/dist/server/framework-request-handler.js +34 -0
  1007. package/dist/server/framework-request-handler.js.map +1 -1
  1008. package/dist/server/google-realtime-session.d.ts.map +1 -1
  1009. package/dist/server/google-realtime-session.js +2 -34
  1010. package/dist/server/google-realtime-session.js.map +1 -1
  1011. package/dist/server/onboarding-html.d.ts.map +1 -1
  1012. package/dist/server/onboarding-html.js +1 -16
  1013. package/dist/server/onboarding-html.js.map +1 -1
  1014. package/dist/server/poll.d.ts.map +1 -1
  1015. package/dist/server/poll.js +24 -2
  1016. package/dist/server/poll.js.map +1 -1
  1017. package/dist/server/realtime-voice.d.ts +3 -0
  1018. package/dist/server/realtime-voice.d.ts.map +1 -1
  1019. package/dist/server/realtime-voice.js +38 -5
  1020. package/dist/server/realtime-voice.js.map +1 -1
  1021. package/dist/server/request-origin.d.ts +10 -0
  1022. package/dist/server/request-origin.d.ts.map +1 -0
  1023. package/dist/server/request-origin.js +63 -0
  1024. package/dist/server/request-origin.js.map +1 -0
  1025. package/dist/server/ssr-handler.d.ts.map +1 -1
  1026. package/dist/server/ssr-handler.js +47 -2
  1027. package/dist/server/ssr-handler.js.map +1 -1
  1028. package/dist/server/transcribe-voice.d.ts.map +1 -1
  1029. package/dist/server/transcribe-voice.js +2 -48
  1030. package/dist/server/transcribe-voice.js.map +1 -1
  1031. package/dist/sharing/access.d.ts +63 -3
  1032. package/dist/sharing/access.d.ts.map +1 -1
  1033. package/dist/sharing/access.js +53 -15
  1034. package/dist/sharing/access.js.map +1 -1
  1035. package/dist/sharing/registry.d.ts +7 -0
  1036. package/dist/sharing/registry.d.ts.map +1 -1
  1037. package/dist/sharing/registry.js.map +1 -1
  1038. package/dist/styles/agent-native.css +15 -14
  1039. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +1 -1
  1040. package/dist/templates/default/app/i18n/ar-SA.ts +6 -0
  1041. package/dist/templates/default/app/i18n/de-DE.ts +6 -0
  1042. package/dist/templates/default/app/i18n/en-US.ts +6 -0
  1043. package/dist/templates/default/app/i18n/es-ES.ts +6 -0
  1044. package/dist/templates/default/app/i18n/fr-FR.ts +6 -0
  1045. package/dist/templates/default/app/i18n/hi-IN.ts +6 -0
  1046. package/dist/templates/default/app/i18n/ja-JP.ts +6 -0
  1047. package/dist/templates/default/app/i18n/ko-KR.ts +6 -0
  1048. package/dist/templates/default/app/i18n/pt-BR.ts +6 -0
  1049. package/dist/templates/default/app/i18n/zh-CN.ts +6 -0
  1050. package/dist/templates/default/app/i18n/zh-TW.ts +6 -0
  1051. package/dist/templates/default/package.json +2 -2
  1052. package/dist/templates/headless/package.json +0 -1
  1053. package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  1054. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +27 -29
  1055. package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  1056. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +2 -1
  1057. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -1
  1058. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +32 -0
  1059. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +25 -0
  1060. package/dist/templates/workspace-core/package.json +0 -1
  1061. package/dist/templates/workspace-core/src/server/index.ts +4 -2
  1062. package/dist/templates/workspace-root/package.json +0 -1
  1063. package/dist/testing.d.ts +16 -0
  1064. package/dist/testing.d.ts.map +1 -0
  1065. package/dist/testing.js +16 -0
  1066. package/dist/testing.js.map +1 -0
  1067. package/dist/triggers/actions.d.ts.map +1 -1
  1068. package/dist/triggers/actions.js +8 -3
  1069. package/dist/triggers/actions.js.map +1 -1
  1070. package/dist/triggers/dispatcher.d.ts +7 -0
  1071. package/dist/triggers/dispatcher.d.ts.map +1 -1
  1072. package/dist/triggers/dispatcher.js +13 -3
  1073. package/dist/triggers/dispatcher.js.map +1 -1
  1074. package/dist/triggers/types.d.ts +7 -1
  1075. package/dist/triggers/types.d.ts.map +1 -1
  1076. package/dist/triggers/types.js.map +1 -1
  1077. package/dist/vite/client.d.ts +16 -1
  1078. package/dist/vite/client.d.ts.map +1 -1
  1079. package/dist/vite/client.js +105 -2
  1080. package/dist/vite/client.js.map +1 -1
  1081. package/docs/content/audit-log.mdx +2 -1
  1082. package/docs/content/authentication.mdx +3 -3
  1083. package/docs/content/automations.mdx +7 -7
  1084. package/docs/content/creating-templates.mdx +1 -1
  1085. package/docs/content/doctor.mdx +170 -0
  1086. package/docs/content/external-agents.mdx +8 -7
  1087. package/docs/content/internationalization.mdx +2 -1
  1088. package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  1089. package/docs/content/locales/ar-SA/authentication.mdx +2 -2
  1090. package/docs/content/locales/ar-SA/automations.mdx +7 -7
  1091. package/docs/content/locales/ar-SA/external-agents.mdx +8 -7
  1092. package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  1093. package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  1094. package/docs/content/locales/ar-SA/mcp-protocol.mdx +1 -1
  1095. package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  1096. package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  1097. package/docs/content/locales/ar-SA/security.mdx +10 -0
  1098. package/docs/content/locales/de-DE/audit-log.mdx +2 -1
  1099. package/docs/content/locales/de-DE/authentication.mdx +1 -1
  1100. package/docs/content/locales/de-DE/automations.mdx +7 -7
  1101. package/docs/content/locales/de-DE/external-agents.mdx +8 -7
  1102. package/docs/content/locales/de-DE/getting-started.mdx +7 -1
  1103. package/docs/content/locales/de-DE/internationalization.mdx +1 -1
  1104. package/docs/content/locales/de-DE/mcp-protocol.mdx +1 -1
  1105. package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  1106. package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  1107. package/docs/content/locales/de-DE/security.mdx +10 -0
  1108. package/docs/content/locales/es-ES/audit-log.mdx +2 -1
  1109. package/docs/content/locales/es-ES/authentication.mdx +2 -2
  1110. package/docs/content/locales/es-ES/automations.mdx +7 -7
  1111. package/docs/content/locales/es-ES/external-agents.mdx +8 -7
  1112. package/docs/content/locales/es-ES/getting-started.mdx +7 -1
  1113. package/docs/content/locales/es-ES/internationalization.mdx +1 -1
  1114. package/docs/content/locales/es-ES/mcp-protocol.mdx +1 -1
  1115. package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  1116. package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  1117. package/docs/content/locales/es-ES/security.mdx +10 -0
  1118. package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  1119. package/docs/content/locales/fr-FR/authentication.mdx +1 -1
  1120. package/docs/content/locales/fr-FR/automations.mdx +7 -7
  1121. package/docs/content/locales/fr-FR/external-agents.mdx +8 -7
  1122. package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  1123. package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  1124. package/docs/content/locales/fr-FR/mcp-protocol.mdx +1 -1
  1125. package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  1126. package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  1127. package/docs/content/locales/fr-FR/security.mdx +10 -0
  1128. package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  1129. package/docs/content/locales/hi-IN/authentication.mdx +2 -2
  1130. package/docs/content/locales/hi-IN/automations.mdx +7 -7
  1131. package/docs/content/locales/hi-IN/external-agents.mdx +8 -7
  1132. package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  1133. package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  1134. package/docs/content/locales/hi-IN/mcp-protocol.mdx +1 -1
  1135. package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  1136. package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  1137. package/docs/content/locales/hi-IN/security.mdx +10 -0
  1138. package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  1139. package/docs/content/locales/ja-JP/authentication.mdx +2 -2
  1140. package/docs/content/locales/ja-JP/automations.mdx +7 -7
  1141. package/docs/content/locales/ja-JP/external-agents.mdx +8 -7
  1142. package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  1143. package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  1144. package/docs/content/locales/ja-JP/mcp-protocol.mdx +1 -1
  1145. package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  1146. package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  1147. package/docs/content/locales/ja-JP/security.mdx +10 -0
  1148. package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  1149. package/docs/content/locales/ko-KR/authentication.mdx +2 -2
  1150. package/docs/content/locales/ko-KR/automations.mdx +7 -7
  1151. package/docs/content/locales/ko-KR/external-agents.mdx +8 -7
  1152. package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  1153. package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  1154. package/docs/content/locales/ko-KR/mcp-protocol.mdx +1 -1
  1155. package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  1156. package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  1157. package/docs/content/locales/ko-KR/security.mdx +10 -0
  1158. package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  1159. package/docs/content/locales/pt-BR/authentication.mdx +2 -2
  1160. package/docs/content/locales/pt-BR/automations.mdx +7 -7
  1161. package/docs/content/locales/pt-BR/external-agents.mdx +8 -7
  1162. package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  1163. package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  1164. package/docs/content/locales/pt-BR/mcp-protocol.mdx +1 -1
  1165. package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  1166. package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  1167. package/docs/content/locales/pt-BR/security.mdx +10 -0
  1168. package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  1169. package/docs/content/locales/zh-CN/authentication.mdx +2 -2
  1170. package/docs/content/locales/zh-CN/automations.mdx +7 -7
  1171. package/docs/content/locales/zh-CN/external-agents.mdx +8 -7
  1172. package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  1173. package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  1174. package/docs/content/locales/zh-CN/mcp-protocol.mdx +1 -1
  1175. package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  1176. package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  1177. package/docs/content/locales/zh-CN/security.mdx +10 -0
  1178. package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  1179. package/docs/content/locales/zh-TW/authentication.mdx +2 -2
  1180. package/docs/content/locales/zh-TW/automations.mdx +7 -7
  1181. package/docs/content/locales/zh-TW/external-agents.mdx +8 -7
  1182. package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  1183. package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  1184. package/docs/content/locales/zh-TW/mcp-protocol.mdx +1 -1
  1185. package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  1186. package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  1187. package/docs/content/locales/zh-TW/security.mdx +10 -0
  1188. package/docs/content/mcp-protocol.mdx +1 -1
  1189. package/docs/content/package-lifecycle.mdx +51 -0
  1190. package/docs/content/sandbox-adapters.mdx +16 -5
  1191. package/docs/content/security.mdx +2 -0
  1192. package/package.json +14 -3
  1193. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +1 -1
  1194. package/src/templates/default/app/i18n/ar-SA.ts +6 -0
  1195. package/src/templates/default/app/i18n/de-DE.ts +6 -0
  1196. package/src/templates/default/app/i18n/en-US.ts +6 -0
  1197. package/src/templates/default/app/i18n/es-ES.ts +6 -0
  1198. package/src/templates/default/app/i18n/fr-FR.ts +6 -0
  1199. package/src/templates/default/app/i18n/hi-IN.ts +6 -0
  1200. package/src/templates/default/app/i18n/ja-JP.ts +6 -0
  1201. package/src/templates/default/app/i18n/ko-KR.ts +6 -0
  1202. package/src/templates/default/app/i18n/pt-BR.ts +6 -0
  1203. package/src/templates/default/app/i18n/zh-CN.ts +6 -0
  1204. package/src/templates/default/app/i18n/zh-TW.ts +6 -0
  1205. package/src/templates/default/package.json +2 -2
  1206. package/src/templates/headless/package.json +0 -1
  1207. package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  1208. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +27 -29
  1209. package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  1210. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +2 -1
  1211. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -1
  1212. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +32 -0
  1213. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +25 -0
  1214. package/src/templates/workspace-core/package.json +0 -1
  1215. package/src/templates/workspace-core/src/server/index.ts +4 -2
  1216. package/src/templates/workspace-root/package.json +0 -1
  1217. package/corpus/core/src/client/ProductionAgentPanel.tsx +0 -9
  1218. package/corpus/core/src/client/auth-redirect-url.ts +0 -23
  1219. package/corpus/core/src/client/useProductionAgent.ts +0 -253
  1220. package/corpus/core/src/shared/auth-redirect-url.ts +0 -1
  1221. package/corpus/templates/analytics/app/pages/Index.tsx +0 -16
  1222. package/corpus/templates/analytics/seeds/dashboards/agent-observability-llm.json +0 -173
  1223. package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
  1224. package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
  1225. package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
  1226. package/dist/client/ProductionAgentPanel.d.ts +0 -6
  1227. package/dist/client/ProductionAgentPanel.d.ts.map +0 -1
  1228. package/dist/client/ProductionAgentPanel.js +0 -6
  1229. package/dist/client/ProductionAgentPanel.js.map +0 -1
  1230. package/dist/client/auth-redirect-url.d.ts +0 -2
  1231. package/dist/client/auth-redirect-url.d.ts.map +0 -1
  1232. package/dist/client/auth-redirect-url.js +0 -19
  1233. package/dist/client/auth-redirect-url.js.map +0 -1
  1234. package/dist/client/useProductionAgent.d.ts +0 -24
  1235. package/dist/client/useProductionAgent.d.ts.map +0 -1
  1236. package/dist/client/useProductionAgent.js +0 -179
  1237. package/dist/client/useProductionAgent.js.map +0 -1
  1238. package/dist/shared/auth-redirect-url.d.ts +0 -2
  1239. package/dist/shared/auth-redirect-url.d.ts.map +0 -1
  1240. package/dist/shared/auth-redirect-url.js +0 -2
  1241. package/dist/shared/auth-redirect-url.js.map +0 -1
@@ -8,25 +8,18 @@ import {
8
8
  getMethod,
9
9
  getQuery,
10
10
  getHeader,
11
+ getRequestIP,
11
12
  type H3Event,
12
13
  } from "h3";
13
14
 
14
- import {
15
- appendA2AArtifactLinks,
16
- buildA2ARecoverableArtifactMessage,
17
- type A2AArtifactResponseOptions,
18
- type A2AToolResultSummary,
19
- } from "../a2a/artifact-response.js";
15
+ import { buildA2ARecoverableArtifactMessage } from "../a2a/artifact-response.js";
20
16
  import {
21
17
  hasConfiguredA2ASecret,
22
- isA2AProductionRuntime,
18
+ isLoopbackAddress,
19
+ isTrustedLocalRuntime,
23
20
  } from "../a2a/auth-policy.js";
24
- import {
25
- applyAgentTextEventToBuffer,
26
- collectFinalResponseTextFromAgentEvents,
27
- } from "../a2a/response-text.js";
21
+ import { applyAgentTextEventToBuffer } from "../a2a/response-text.js";
28
22
  import { updateTaskStatusMessage } from "../a2a/task-store.js";
29
- import { ACTION_CHAT_UI_DATA_WIDGET_RENDERER } from "../action-ui.js";
30
23
  import type { ActionHttpConfig } from "../action.js";
31
24
  import {
32
25
  canUpdateAgentAppModelDefaultSettings,
@@ -54,16 +47,11 @@ import {
54
47
  listAgentEngines,
55
48
  registerBuiltinEngines,
56
49
  } from "../agent/engine/index.js";
57
- import type {
58
- AgentEngine,
59
- EngineMessage,
60
- EngineTool,
61
- } from "../agent/engine/types.js";
50
+ import type { EngineMessage } from "../agent/engine/types.js";
62
51
  import {
63
52
  createProductionAgentHandler,
64
53
  actionsToEngineTools,
65
54
  executeAgentToolCall,
66
- filterInitialEngineTools,
67
55
  getActiveRunForThreadAsync,
68
56
  abortRunDurably,
69
57
  subscribeToRun,
@@ -74,17 +62,7 @@ import {
74
62
  callerHasRunAccess,
75
63
  callerHasThreadAccess,
76
64
  } from "../agent/run-ownership.js";
77
- import type { AgentRunSummary } from "../agent/run-store.js";
78
- import {
79
- CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT,
80
- ensureTerminalRunEvent,
81
- readBackgroundRunClaim,
82
- recordRunDiagnostic,
83
- RUN_DIAG_STAGE,
84
- setRunError,
85
- setRunTerminalReason,
86
- updateRunStatusIfRunning,
87
- } from "../agent/run-store.js";
65
+ import { readBackgroundRunClaim } from "../agent/run-store.js";
88
66
  import {
89
67
  buildCurrentTimeUserContext,
90
68
  buildRuntimeContextPrompt,
@@ -92,4040 +70,244 @@ import {
92
70
  import {
93
71
  buildAssistantMessage,
94
72
  buildUserMessage,
95
- extractThreadMeta,
96
- foldAssistantTurn,
97
- mergeThreadDataForClientSave,
98
- normalizeThreadRepository,
99
- upsertUserMessage,
100
- } from "../agent/thread-data-builder.js";
101
- import { attachToolSearch } from "../agent/tool-search.js";
102
- import type {
103
- AgentChatAttachment,
104
- AgentChatEvent,
105
- AgentChatReference,
106
- ActionTool,
107
- MentionProvider,
108
- } from "../agent/types.js";
109
- import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
110
- import {
111
- createThread,
112
- forkThread,
113
- getThread,
114
- registerChatThreadsShareable,
115
- resolveThreadAccess,
116
- listThreads,
117
- searchThreads,
118
- renameThread,
119
- createThreadShareLink,
120
- getThreadByShareToken,
121
- getThreadShareState,
122
- revokeThreadShareLink,
123
- setThreadArchived,
124
- setThreadPinned,
125
- setThreadScope,
126
- updateThreadData,
127
- withThreadDataLock,
128
- deleteThread,
129
- setThreadQueuedMessages,
130
- type ChatThread,
131
- type ChatThreadScope,
132
- type ForkThreadSourceSnapshot,
133
- } from "../chat-threads/store.js";
134
- import { dataWidgetResultSchema } from "../data-widgets/index.js";
135
- import { createDbAdminAgentTools } from "../db-admin/agent-tools.js";
136
- import {
137
- verifyInternalToken,
138
- extractBearerToken,
139
- } from "../integrations/internal-token.js";
140
- import {
141
- McpClientManager,
142
- loadMcpConfig,
143
- autoDetectMcpConfig,
144
- mcpToolsToActionEntries,
145
- syncMcpActionEntries,
146
- mountMcpServersRoutes,
147
- mountMcpHubRoutes,
148
- buildMergedConfig,
149
- startMcpConfigRefresh,
150
- areBuiltinMcpCapabilitiesSupported,
151
- setBuiltinMcpCapabilityEnabled,
152
- getHubStatus,
153
- isHubServeEnabled,
154
- type BuiltinMcpCapabilityId,
155
- } from "../mcp-client/index.js";
156
- import { setProgressPreListHook } from "../progress/store.js";
157
- import {
158
- getFrontmatterValue,
159
- getSkillNameFromPath,
160
- parseFrontmatter,
161
- } from "../resources/metadata.js";
162
- import {
163
- resourceList,
164
- resourceListAccessible,
165
- resourceGet,
166
- resourceGetByPath,
167
- ensurePersonalDefaults,
168
- organizationIdFromResourceOwner,
169
- sharedResourceOwner,
170
- SHARED_OWNER,
171
- WORKSPACE_OWNER,
172
- } from "../resources/store.js";
173
- import {
174
- normalizeDatabaseToolsMode,
175
- type DatabaseToolsMode,
176
- type DatabaseToolsOption,
177
- } from "../scripts/db/tool-mode.js";
178
- import { dbExecToolParameters } from "../scripts/db/tool-schemas.js";
179
- import { getSetting, putSetting } from "../settings/store.js";
180
- import { discoverAgents } from "./agent-discovery.js";
181
- import {
182
- resolveAgentRunOwnerContext,
183
- runWithAgentRunContext,
184
- seedBackgroundAgentRunOwnerContext,
185
- type AgentRunOwnerContext,
186
- } from "./agent-run-context.js";
187
- import {
188
- AGENT_TEAM_PROCESS_RUN_PATH,
189
- getCurrentDelegationDepth,
190
- processAgentTeamRun,
191
- reconcileAgentTeamRunsForOwner,
192
- } from "./agent-teams.js";
193
- import { withConfiguredAppBasePath } from "./app-base-path.js";
194
- import { getSession } from "./auth.js";
195
- import {
196
- getBuilderBrowserConnectUrlForOwner,
197
- resolveBuilderBranchProjectId,
198
- } from "./builder-browser.js";
199
- import { captureError } from "./capture-error.js";
200
- import { captureCliOutput } from "./cli-capture.js";
201
- import {
202
- getH3App,
203
- markDefaultPluginProvided,
204
- trackPluginInit,
205
- } from "./framework-request-handler.js";
206
- import { getOrigin } from "./google-oauth.js";
207
- import { readBody } from "./h3-helpers.js";
208
- import {
209
- buildFrameworkCore,
210
- buildFrameworkCoreCompact,
211
- FIRST_SESSION_PERSONALIZATION,
212
- getModelFamilyOverlay,
213
- type PromptExamples,
214
- } from "./prompts/index.js";
215
- import { mountRealtimeVoiceRoutes } from "./realtime-voice.js";
216
- import {
217
- runWithRequestContext,
218
- getRequestOrgId,
219
- getRequestUserEmail,
220
- getRequestRunContext,
221
- ensureRequestRunContext,
222
- } from "./request-context.js";
223
- import { loadSchemaPromptBlock } from "./schema-prompt.js";
224
-
225
- // Lazy fs — loaded via dynamic import() on first use.
226
- // This avoids require() which bundlers convert to createRequire(import.meta.url)
227
- // that crashes on CF Workers where import.meta.url is undefined.
228
- let _fs: typeof import("fs") | undefined;
229
- async function lazyFs(): Promise<typeof import("fs")> {
230
- if (!_fs) {
231
- _fs = await import("node:fs");
232
- }
233
- return _fs;
234
- }
235
-
236
- const SHARED_PROMPT_RESOURCE_MAX_CHARS = 30_000;
237
- const COMPACT_PROMPT_RESOURCE_MAX_CHARS = 12_000;
238
- const MAX_ACTION_SUMMARY_DESCRIPTION_CHARS = 140;
239
-
240
- function sanitizeSharedThread(thread: ChatThread): {
241
- id: string;
242
- title: string;
243
- preview: string;
244
- messageCount: number;
245
- createdAt: number;
246
- updatedAt: number;
247
- scope: { type: string; label?: string } | null;
248
- messages: Array<{
249
- id?: string;
250
- role: "user" | "assistant" | "system";
251
- text: string;
252
- createdAt?: string | number;
253
- }>;
254
- } {
255
- let repo: any = {};
256
- try {
257
- repo = normalizeThreadRepository(JSON.parse(thread.threadData));
258
- } catch {
259
- repo = {};
260
- }
261
- const messages = Array.isArray(repo.messages)
262
- ? repo.messages
263
- .map((entry: unknown) => sanitizeSharedMessage(entry))
264
- .filter(
265
- (
266
- entry: unknown,
267
- ): entry is NonNullable<ReturnType<typeof sanitizeSharedMessage>> =>
268
- entry != null,
269
- )
270
- : [];
271
- return {
272
- id: thread.id,
273
- title: thread.title,
274
- preview: thread.preview,
275
- messageCount: thread.messageCount,
276
- createdAt: thread.createdAt,
277
- updatedAt: thread.updatedAt,
278
- scope: thread.scope
279
- ? {
280
- type: thread.scope.type,
281
- ...(thread.scope.label ? { label: thread.scope.label } : {}),
282
- }
283
- : null,
284
- messages,
285
- };
286
- }
287
-
288
- type SanitizedSharedThread = ReturnType<typeof sanitizeSharedThread>;
289
-
290
- export interface SharedThreadRouteDependencies {
291
- getThreadByShareToken: (token: string) => Promise<ChatThread | null>;
292
- listRunsForThread: (
293
- threadId: string,
294
- options?: { limit?: number },
295
- ) => Promise<AgentRunSummary[]>;
296
- }
297
-
298
- function escapeSharedThreadHtml(value: unknown): string {
299
- return String(value ?? "")
300
- .replace(/&/g, "&amp;")
301
- .replace(/</g, "&lt;")
302
- .replace(/>/g, "&gt;")
303
- .replace(/"/g, "&quot;")
304
- .replace(/'/g, "&#39;");
305
- }
306
-
307
- function wantsSharedThreadHtml(event: H3Event): boolean {
308
- const accept = getHeader(event, "accept")?.toLowerCase() ?? "";
309
- return accept.includes("text/html") && !accept.includes("application/json");
310
- }
311
-
312
- function formatSharedThreadTime(value: string | number | null | undefined) {
313
- if (value == null) return "";
314
- const date =
315
- typeof value === "number"
316
- ? new Date(value)
317
- : Number.isFinite(Number(value))
318
- ? new Date(Number(value))
319
- : new Date(value);
320
- return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString();
321
- }
322
-
323
- function renderSharedThreadHtml(
324
- thread: SanitizedSharedThread,
325
- runs: AgentRunSummary[],
326
- ): string {
327
- const title = thread.title || "Shared agent session";
328
- const messages = thread.messages
329
- .map((message) => {
330
- const time = formatSharedThreadTime(message.createdAt);
331
- return `<article class="message ${escapeSharedThreadHtml(message.role)}">
332
- <div class="meta">
333
- <span>${escapeSharedThreadHtml(message.role)}</span>
334
- ${time ? `<time>${escapeSharedThreadHtml(time)}</time>` : ""}
335
- </div>
336
- <pre>${escapeSharedThreadHtml(message.text)}</pre>
337
- </article>`;
338
- })
339
- .join("");
340
- const runsHtml = runs.length
341
- ? `<section class="runs" aria-label="Recent runs">
342
- <h2>Recent runs</h2>
343
- <ol>${runs
344
- .map((run) => {
345
- const started = formatSharedThreadTime(run.startedAt);
346
- const completed = formatSharedThreadTime(run.completedAt);
347
- const detail = [
348
- started ? `started ${started}` : "",
349
- completed ? `completed ${completed}` : "",
350
- run.errorCode ? `error ${run.errorCode}` : "",
351
- run.abortReason ? `aborted ${run.abortReason}` : "",
352
- ]
353
- .filter(Boolean)
354
- .join(" · ");
355
- return `<li><strong>${escapeSharedThreadHtml(run.status)}</strong>${detail ? `<span>${escapeSharedThreadHtml(detail)}</span>` : ""}</li>`;
356
- })
357
- .join("")}</ol>
358
- </section>`
359
- : "";
360
-
361
- return `<!doctype html>
362
- <html lang="en">
363
- <head>
364
- <meta charset="utf-8" />
365
- <meta name="viewport" content="width=device-width, initial-scale=1" />
366
- <meta name="robots" content="noindex, nofollow" />
367
- <meta name="referrer" content="no-referrer" />
368
- <title>${escapeSharedThreadHtml(title)}</title>
369
- <style>
370
- :root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f7f7f5; color: #1d1d1b; }
371
- * { box-sizing: border-box; }
372
- body { margin: 0; }
373
- main { width: min(920px, calc(100vw - 32px)); margin: 0 auto; padding: 44px 0 64px; }
374
- header { border-bottom: 1px solid rgba(0,0,0,.12); padding-bottom: 24px; margin-bottom: 28px; }
375
- .eyebrow { margin: 0 0 10px; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #676760; }
376
- h1 { margin: 0; font-size: 42px; line-height: 1.08; letter-spacing: 0; }
377
- .summary { margin: 14px 0 0; color: #5c5c55; line-height: 1.6; max-width: 760px; }
378
- .message { border: 1px solid rgba(0,0,0,.12); border-radius: 8px; background: rgba(255,255,255,.72); margin: 14px 0; padding: 16px; }
379
- .message.assistant { border-left: 4px solid #2563eb; }
380
- .message.user { border-left: 4px solid #0f766e; }
381
- .message.system { border-left: 4px solid #737373; }
382
- .meta { display: flex; gap: 12px; justify-content: space-between; color: #676760; font-size: 12px; font-weight: 700; text-transform: uppercase; }
383
- pre { margin: 12px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; line-height: 1.6; }
384
- .empty, .runs { margin-top: 28px; color: #676760; }
385
- .runs h2 { font-size: 16px; margin: 0 0 12px; }
386
- .runs ol { margin: 0; padding-left: 20px; }
387
- .runs li { margin: 8px 0; }
388
- .runs span { color: #676760; margin-left: 8px; }
389
- @media (prefers-color-scheme: dark) {
390
- :root { background: #11110f; color: #f5f5ef; }
391
- header, .message { border-color: rgba(255,255,255,.15); }
392
- .message { background: rgba(255,255,255,.06); }
393
- .summary, .eyebrow, .meta, .empty, .runs, .runs span { color: #b9b9ad; }
394
- }
395
- @media (max-width: 640px) {
396
- h1 { font-size: 32px; }
397
- main { width: min(100vw - 24px, 920px); padding-top: 32px; }
398
- }
399
- </style>
400
- </head>
401
- <body>
402
- <main>
403
- <header>
404
- <p class="eyebrow">Read-only shared agent session</p>
405
- <h1>${escapeSharedThreadHtml(title)}</h1>
406
- <p class="summary">${escapeSharedThreadHtml(thread.preview || `${thread.messageCount} message${thread.messageCount === 1 ? "" : "s"}`)}</p>
407
- </header>
408
- ${messages || '<p class="empty">No transcript messages were shared.</p>'}
409
- ${runsHtml}
410
- </main>
411
- </body>
412
- </html>`;
413
- }
414
-
415
- function renderSharedThreadErrorHtml(status: number, message: string): string {
416
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="robots" content="noindex, nofollow" /><title>${status} ${escapeSharedThreadHtml(message)}</title><style>body{margin:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#f7f7f5;color:#1d1d1b}main{max-width:720px;margin:0 auto;padding:64px 24px}p{color:#676760;line-height:1.6}@media(prefers-color-scheme:dark){body{background:#11110f;color:#f5f5ef}p{color:#b9b9ad}}</style></head><body><main><h1>${status}</h1><p>${escapeSharedThreadHtml(message)}</p></main></body></html>`;
417
- }
418
-
419
- function sharedThreadError(event: H3Event, status: number, message: string) {
420
- setResponseStatus(event, status);
421
- setResponseHeader(event, "Cache-Control", "private, no-store");
422
- setResponseHeader(event, "X-Robots-Tag", "noindex, nofollow");
423
- if (wantsSharedThreadHtml(event)) {
424
- setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
425
- return renderSharedThreadErrorHtml(status, message);
426
- }
427
- return { error: message };
428
- }
429
-
430
- export async function handleSharedThreadRequest(
431
- event: H3Event,
432
- deps: SharedThreadRouteDependencies,
433
- ) {
434
- const method = getMethod(event);
435
- if (method !== "GET") {
436
- return sharedThreadError(event, 405, "Method not allowed");
437
- }
438
-
439
- const token = parseSharedThreadToken(event);
440
- if (!token) {
441
- return sharedThreadError(event, 400, "Share token is required");
442
- }
443
-
444
- const thread = await deps.getThreadByShareToken(token);
445
- if (!thread) {
446
- return sharedThreadError(event, 404, "Shared thread not found");
447
- }
448
-
449
- const runs = await deps.listRunsForThread(thread.id, { limit: 10 });
450
- const payload = {
451
- thread: sanitizeSharedThread(thread),
452
- runs,
453
- };
454
- setResponseHeader(event, "Cache-Control", "private, no-store");
455
- setResponseHeader(event, "X-Robots-Tag", "noindex, nofollow");
456
- if (wantsSharedThreadHtml(event)) {
457
- setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
458
- return renderSharedThreadHtml(payload.thread, runs);
459
- }
460
- setResponseHeader(event, "Content-Type", "application/json");
461
- return payload;
462
- }
463
-
464
- function sanitizeSharedMessage(entry: unknown): {
465
- id?: string;
466
- role: "user" | "assistant" | "system";
467
- text: string;
468
- createdAt?: string | number;
469
- } | null {
470
- if (!entry || typeof entry !== "object") return null;
471
- const raw = entry as Record<string, unknown>;
472
- const message =
473
- raw.message && typeof raw.message === "object"
474
- ? (raw.message as Record<string, unknown>)
475
- : raw;
476
- const role = message.role;
477
- if (role !== "user" && role !== "assistant" && role !== "system") {
478
- return null;
479
- }
480
- const text = sharedTextFromContent(message.content).trim();
481
- if (!text) return null;
482
- const id = typeof message.id === "string" ? message.id : undefined;
483
- const createdAt =
484
- typeof message.createdAt === "string" ||
485
- typeof message.createdAt === "number"
486
- ? message.createdAt
487
- : undefined;
488
- return {
489
- ...(id ? { id } : {}),
490
- role,
491
- text,
492
- ...(createdAt !== undefined ? { createdAt } : {}),
493
- };
494
- }
495
-
496
- function sharedTextFromContent(content: unknown): string {
497
- if (typeof content === "string") return content;
498
- if (!Array.isArray(content)) return "";
499
- const parts: string[] = [];
500
- for (const part of content) {
501
- if (!part || typeof part !== "object") continue;
502
- const r = part as Record<string, unknown>;
503
- if (r.type !== "text") continue;
504
- if (typeof r.text === "string") parts.push(r.text);
505
- }
506
- return parts.join("");
507
- }
508
-
509
- function parseSharedThreadToken(event: H3Event): string | null {
510
- const candidates = [event.path, event.node?.req?.url].filter(
511
- (value): value is string => typeof value === "string" && value.length > 0,
512
- );
513
- for (const candidate of candidates) {
514
- const path = candidate.split("?")[0];
515
- const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
516
- const sharedIndex = parts.lastIndexOf("shared");
517
- if (sharedIndex >= 0 && parts[sharedIndex + 1]) {
518
- return decodeURIComponent(parts[sharedIndex + 1]);
519
- }
520
- if (parts[0]) return decodeURIComponent(parts[0]);
521
- }
522
- return null;
523
- }
524
-
525
- function compactPromptLine(value: string, maxChars: number): string {
526
- const line = value.replace(/\s+/g, " ").trim();
527
- if (line.length <= maxChars) return line;
528
- return `${line.slice(0, maxChars - 1)}…`;
529
- }
530
- const SHARED_RESOURCE_INDEX_LIMIT = 40;
531
-
532
- function normalizeResourcePathForPrompt(path: string): string {
533
- return path.replace(/^\/+/, "").trim();
534
- }
535
-
536
- function resourceToolHint(
537
- action: "list" | "read" | "effective" | "write" | "delete" | "promote",
538
- extra?: string,
539
- ): string {
540
- return `Use the \`resources\` tool with \`action: "${action}"\`${extra ? `, ${extra}` : ""}.`;
541
- }
542
-
543
- function skillDocsSlug(name: string): string {
544
- return `skill-${name
545
- .trim()
546
- .toLowerCase()
547
- .replace(/[^a-z0-9]+/g, "-")
548
- .replace(/^-+|-+$/g, "")}`;
549
- }
550
-
551
- function ensureSentence(value: string): string {
552
- const trimmed = value.trim();
553
- if (!trimmed) return "";
554
- return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
555
- }
556
-
557
- function escapeXmlAttribute(value: string): string {
558
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
559
- }
560
-
561
- function truncatePromptResourceContent(
562
- content: string,
563
- path: string,
564
- maxChars = SHARED_PROMPT_RESOURCE_MAX_CHARS,
565
- readHint?: string,
566
- ): string {
567
- const trimmed = content.trim();
568
- if (trimmed.length <= maxChars) return trimmed;
569
- const omitted = trimmed.length - maxChars;
570
- const hint =
571
- readHint ??
572
- resourceToolHint(
573
- "read",
574
- `\`path: "${path}"\` and the resource's \`scope\` for the full content`,
575
- );
576
- return `${trimmed.slice(0, maxChars)}\n\n[Resource ${path} truncated after ${maxChars.toLocaleString()} characters; ${omitted.toLocaleString()} characters omitted. ${hint}]`;
577
- }
578
-
579
- function promptResourceBlock(input: {
580
- name: string;
581
- scope: string;
582
- content: string;
583
- path?: string;
584
- maxChars?: number;
585
- readHint?: string;
586
- }): string | null {
587
- const normalizedPath = input.path
588
- ? normalizeResourcePathForPrompt(input.path)
589
- : undefined;
590
- const content = truncatePromptResourceContent(
591
- input.content,
592
- normalizedPath ?? input.name,
593
- input.maxChars,
594
- input.readHint,
595
- );
596
- if (!content) return null;
597
- const pathAttr = normalizedPath
598
- ? ` path="${escapeXmlAttribute(normalizedPath)}"`
599
- : "";
600
- return `<resource name="${escapeXmlAttribute(input.name)}" scope="${escapeXmlAttribute(input.scope)}"${pathAttr}>\n${content}\n</resource>`;
601
- }
602
-
603
- function isAutoLoadedInstructionPath(path: string): boolean {
604
- const normalized = normalizeResourcePathForPrompt(path);
605
- return normalized.startsWith("instructions/") && normalized.endsWith(".md");
606
- }
607
-
608
- function isSpecialPromptResourcePath(path: string): boolean {
609
- const normalized = normalizeResourcePathForPrompt(path);
610
- return (
611
- normalized === "AGENTS.md" ||
612
- normalized === "LEARNINGS.md" ||
613
- normalized.startsWith("instructions/") ||
614
- normalized.startsWith("skills/") ||
615
- normalized.startsWith("agents/") ||
616
- normalized.startsWith("remote-agents/") ||
617
- normalized.startsWith("jobs/") ||
618
- normalized.startsWith("memory/")
619
- );
620
- }
621
-
622
- function isTextLikeResource(mimeType: string): boolean {
623
- return (
624
- mimeType.startsWith("text/") ||
625
- mimeType === "application/json" ||
626
- mimeType === "application/yaml" ||
627
- mimeType === "application/x-yaml"
628
- );
629
- }
630
-
631
- function getResourceSummaryFromContent(content: string): string | null {
632
- const frontmatter = parseFrontmatter(content);
633
- const title =
634
- getFrontmatterValue(frontmatter, "title") ||
635
- getFrontmatterValue(frontmatter, "name");
636
- const description = getFrontmatterValue(frontmatter, "description");
637
- if (title && description) return `${title}: ${description}`;
638
- if (title) return title;
639
- if (description) return description;
640
-
641
- const heading = content
642
- .split(/\r?\n/)
643
- .map((line) => line.trim())
644
- .find((line) => /^#{1,3}\s+\S/.test(line));
645
- if (heading) return heading.replace(/^#{1,3}\s+/, "").trim();
646
- return null;
647
- }
648
-
649
- function resourceScopeForOwner(owner: string, currentOwner?: string): string {
650
- if (owner === WORKSPACE_OWNER) return "workspace";
651
- if (owner === SHARED_OWNER || organizationIdFromResourceOwner(owner)) {
652
- return "shared";
653
- }
654
- if (currentOwner && owner === currentOwner) return "personal";
655
- return "resource";
656
- }
657
-
658
- async function loadAgentsResourceForPrompt(
659
- owner: string,
660
- scope: string,
661
- maxChars = SHARED_PROMPT_RESOURCE_MAX_CHARS,
662
- ): Promise<string | null> {
663
- try {
664
- const agents = await resourceGetByPath(owner, "AGENTS.md");
665
- if (!agents?.content?.trim()) return null;
666
- return promptResourceBlock({
667
- name: "AGENTS.md",
668
- scope,
669
- path: "AGENTS.md",
670
- content: agents.content,
671
- maxChars,
672
- });
673
- } catch {
674
- return null;
675
- }
676
- }
677
-
678
- async function loadInstructionResourcesForPrompt(
679
- owner: string,
680
- scope: string,
681
- maxChars = SHARED_PROMPT_RESOURCE_MAX_CHARS,
682
- ): Promise<string[]> {
683
- try {
684
- const resources = await resourceList(owner, "instructions/");
685
- const blocks: string[] = [];
686
- const sorted = resources
687
- .filter((resource) => isAutoLoadedInstructionPath(resource.path))
688
- .sort((a, b) => a.path.localeCompare(b.path));
689
- for (const resource of sorted) {
690
- const full = await resourceGet(resource.id).catch(() => null);
691
- if (!full?.content?.trim()) continue;
692
- const block = promptResourceBlock({
693
- name: resource.path,
694
- scope,
695
- path: resource.path,
696
- content: full.content,
697
- maxChars,
698
- });
699
- if (block) blocks.push(block);
700
- }
701
- return blocks;
702
- } catch {
703
- return [];
704
- }
705
- }
706
-
707
- async function loadResourceSkillsPromptBlock(
708
- owner: string,
709
- orgId?: string | null,
710
- ): Promise<string | null> {
711
- try {
712
- const organizationOwner = sharedResourceOwner(orgId);
713
- const resources =
714
- owner === SHARED_OWNER
715
- ? [
716
- ...(await resourceList(SHARED_OWNER, "skills/")),
717
- ...(await resourceList(WORKSPACE_OWNER, "skills/")),
718
- ]
719
- : await resourceListAccessible(owner, "skills/", { orgId });
720
- const sorted = resources.sort((a, b) => {
721
- const ownerOrder =
722
- (a.owner === owner
723
- ? 0
724
- : a.owner === organizationOwner
725
- ? 1
726
- : a.owner === SHARED_OWNER
727
- ? 2
728
- : a.owner === WORKSPACE_OWNER
729
- ? 3
730
- : 4) -
731
- (b.owner === owner
732
- ? 0
733
- : b.owner === organizationOwner
734
- ? 1
735
- : b.owner === SHARED_OWNER
736
- ? 2
737
- : b.owner === WORKSPACE_OWNER
738
- ? 3
739
- : 4);
740
- if (ownerOrder !== 0) return ownerOrder;
741
- return a.path.localeCompare(b.path);
742
- });
743
- const seen = new Set<string>();
744
- const lines: string[] = [];
745
- for (const resource of sorted) {
746
- const full = await resourceGet(resource.id).catch(() => null);
747
- if (!full?.content) continue;
748
- const meta = parseSkillFrontmatter(full.content);
749
- if (meta.userInvocable === false) continue;
750
- if (meta.scope === "dev") continue;
751
- const name = meta.name || getSkillNameFromPath(resource.path);
752
- if (!name || seen.has(name)) continue;
753
- seen.add(name);
754
- const scope = resourceScopeForOwner(resource.owner, owner);
755
- const description = meta.description || "(no description)";
756
- lines.push(
757
- `- \`${name}\` at resource \`${resource.path}\` (${scope}) - ${ensureSentence(description)} ${resourceToolHint(
758
- "read",
759
- `\`path: "${resource.path}"\` and \`scope: "${scope}"\` before starting a task it applies to`,
760
- )}`,
761
- );
762
- }
763
- if (lines.length === 0) return null;
764
- return `<resource-skills>\nThe following workspace skills are available in addition to codebase skills. They may come from SQL resources, Dispatch workspace resources, or local file mode. Read a matching skill before starting a task it applies to.\n\n${lines.join("\n")}\n</resource-skills>`;
765
- } catch {
766
- return null;
767
- }
768
- }
769
-
770
- async function loadResourceIndexForPrompt(
771
- owner: string,
772
- scope: "workspace" | "shared",
773
- ): Promise<string | null> {
774
- try {
775
- const resources = (await resourceList(owner))
776
- .filter(
777
- (resource) =>
778
- !isSpecialPromptResourcePath(resource.path) &&
779
- isTextLikeResource(resource.mimeType),
780
- )
781
- .sort((a, b) => a.path.localeCompare(b.path));
782
- if (resources.length === 0) return null;
783
-
784
- const listed = resources.slice(0, SHARED_RESOURCE_INDEX_LIMIT);
785
- const lines: string[] = [];
786
- for (const resource of listed) {
787
- const full = await resourceGet(resource.id).catch(() => null);
788
- const summary = full?.content
789
- ? getResourceSummaryFromContent(full.content)
790
- : null;
791
- lines.push(`- \`${resource.path}\`${summary ? ` - ${summary}` : ""}`);
792
- }
793
- if (resources.length > listed.length) {
794
- lines.push(
795
- `- ...${resources.length - listed.length} more ${scope} resources. ${resourceToolHint(
796
- "list",
797
- `\`scope: "${scope}"\` to inspect them`,
798
- )}`,
799
- );
800
- }
801
-
802
- const label =
803
- scope === "workspace"
804
- ? "Workspace reference resources are inherited by every app and are available for company, brand, positioning, persona, product, or domain context."
805
- : "Shared app/organization reference resources are available for app-specific or team context.";
806
- return `<workspace-resources scope="${scope}">\n${label} ${resourceToolHint(
807
- "read",
808
- `\`path: <path>\` and \`scope: "${scope}"\` when a task may depend on them`,
809
- )} Do not assume their contents without reading the relevant file.\n\n${lines.join("\n")}\n</workspace-resources>`;
810
- } catch {
811
- return null;
812
- }
813
- }
814
-
815
- /**
816
- * Wraps a core CLI script (that writes to console.log) as a ActionEntry
817
- * by capturing stdout. Uses an AsyncLocalStorage-backed capture so
818
- * concurrent tool calls do not corrupt the global console/stdout pointers
819
- * (see `cli-capture.ts`).
820
- */
821
- function wrapCliScript(
822
- tool: ActionTool,
823
- cliDefault: (args: string[]) => Promise<void>,
824
- opts?: { readOnly?: boolean },
825
- ): ActionEntry {
826
- return {
827
- tool,
828
- ...(opts?.readOnly ? { readOnly: true as const } : {}),
829
- run: async (args: Record<string, string>): Promise<string> => {
830
- const cliArgs: string[] = [];
831
- for (const [k, v] of Object.entries(args)) {
832
- const raw = v as unknown;
833
- const value =
834
- raw != null && typeof raw === "object"
835
- ? JSON.stringify(raw)
836
- : String(raw);
837
- cliArgs.push(`--${k}`, value);
838
- }
839
- return captureCliOutput(() => cliDefault(cliArgs));
840
- },
841
- };
842
- }
843
-
844
- function filterReadOnlyActions(
845
- actions: Record<string, ActionEntry>,
846
- ): Record<string, ActionEntry> {
847
- return Object.fromEntries(
848
- Object.entries(actions).filter(([, entry]) => entry.readOnly === true),
849
- );
850
- }
851
-
852
- /** Drop actions that opted out of agent exposure via `agentTool: false`. They
853
- * remain callable from the frontend / HTTP (mounted separately from this
854
- * agent tool surface — see `httpActions`) but never appear in any agent tool
855
- * list (in-app assistant, MCP, A2A, job/trigger runners) or actions prompt.
856
- * Default-allow: only an explicit `false` is excluded. */
857
- function filterAgentTools(
858
- actions: Record<string, ActionEntry>,
859
- ): Record<string, ActionEntry> {
860
- return Object.fromEntries(
861
- Object.entries(actions).filter(([, entry]) => entry.agentTool !== false),
862
- );
863
- }
864
-
865
- function filterPublicAgentActions(
866
- actions: Record<string, ActionEntry>,
867
- ): Record<string, ActionEntry> {
868
- return Object.fromEntries(
869
- Object.entries(actions).filter(([, entry]) => {
870
- const config = entry.publicAgent;
871
- return (
872
- config?.expose === true &&
873
- config.readOnly === true &&
874
- config.requiresAuth !== true &&
875
- config.isConsequential !== true
876
- );
877
- }),
878
- );
879
- }
880
-
881
- export function buildPublicAgentA2ASkills(
882
- actions: Record<string, ActionEntry>,
883
- ): Array<{
884
- id: string;
885
- name: string;
886
- description: string;
887
- publicAgent: ActionEntry["publicAgent"];
888
- }> {
889
- return Object.entries(filterPublicAgentActions(actions)).map(
890
- ([name, entry]) => ({
891
- id: name,
892
- name,
893
- description: entry.tool.description,
894
- publicAgent: entry.publicAgent,
895
- }),
896
- );
897
- }
898
-
899
- function resolveArtifactBaseUrl(event: any | undefined): string | undefined {
900
- const fromEnv =
901
- process.env.APP_URL ||
902
- process.env.URL ||
903
- process.env.DEPLOY_URL ||
904
- process.env.BETTER_AUTH_URL;
905
- if (fromEnv) return withConfiguredAppBasePath(String(fromEnv));
906
-
907
- try {
908
- const proto = getHeader(event, "x-forwarded-proto") || "https";
909
- const host = getHeader(event, "host");
910
- if (host) return withConfiguredAppBasePath(`${proto}://${host}`);
911
- } catch {}
912
-
913
- return undefined;
914
- }
915
-
916
- export function assembleA2AFinalResponse(
917
- events: readonly AgentChatEvent[],
918
- toolResults: readonly A2AToolResultSummary[],
919
- options: A2AArtifactResponseOptions & { event?: any } = {},
920
- ): { responseText: string; finalText: string } {
921
- const terminalError = getA2ATerminalErrorEvent(events);
922
- const responseText = collectFinalResponseTextFromAgentEvents(events, {
923
- fallbackToPreToolText: !terminalError,
924
- });
925
- const finalText = appendA2AArtifactLinks(responseText, [...toolResults], {
926
- baseUrl: options.baseUrl ?? resolveArtifactBaseUrl(options.event),
927
- includeReferencedArtifacts: true,
928
- });
929
- if (terminalError && !finalText.trim()) {
930
- throw new Error(formatA2ATerminalError(terminalError));
931
- }
932
- return { responseText, finalText };
933
- }
934
-
935
- function getA2ATerminalErrorEvent(
936
- events: readonly AgentChatEvent[],
937
- ): Extract<AgentChatEvent, { type: "error" }> | null {
938
- for (let i = events.length - 1; i >= 0; i--) {
939
- const event = events[i];
940
- if (event.type === "clear") continue;
941
- if (event.type === "done") return null;
942
- if (event.type === "error") return event;
943
- if (event.type === "auto_continue") {
944
- return {
945
- type: "error",
946
- error: `Agent stopped before finishing (${event.reason}).`,
947
- errorCode: event.reason,
948
- recoverable: true,
949
- };
950
- }
951
- }
952
- return null;
953
- }
954
-
955
- function formatA2ATerminalError(
956
- event: Extract<AgentChatEvent, { type: "error" }>,
957
- ): string {
958
- const parts = [
959
- event.error || "Agent failed before producing a final response.",
960
- event.errorCode ? `code: ${event.errorCode}` : "",
961
- event.details ? `details: ${event.details}` : "",
962
- ].filter(Boolean);
963
- return parts.join("\n");
964
- }
965
-
966
- /**
967
- * Creates the `get-framework-context` tool. Returns detailed instructions
968
- * for framework capabilities that are summarized in the compact prompt.
969
- * The agent calls this on-demand when it needs specifics about embeds,
970
- * agent teams, recurring jobs, etc.
971
- */
972
- function createFrameworkContextEntry(): Record<string, ActionEntry> {
973
- const topicList = Object.keys(FRAMEWORK_CONTEXT_SECTIONS).join(", ");
974
- return {
975
- "get-framework-context": {
976
- tool: {
977
- description: `Read detailed framework instructions for a specific capability. Available topics: ${topicList}. Call with topic="all" to get everything.`,
978
- parameters: {
979
- type: "object" as const,
980
- properties: {
981
- topic: {
982
- type: "string",
983
- description: `Topic to read. One of: ${topicList}, or "all" for everything.`,
984
- },
985
- },
986
- required: ["topic"],
987
- },
988
- },
989
- run: async (args: Record<string, string>) => {
990
- const topic = String(args.topic ?? "all").toLowerCase();
991
- if (topic === "all") {
992
- return Object.values(FRAMEWORK_CONTEXT_SECTIONS).join("\n\n");
993
- }
994
- const section = FRAMEWORK_CONTEXT_SECTIONS[topic];
995
- if (!section) {
996
- return `Unknown topic "${topic}". Available: ${topicList}`;
997
- }
998
- return section;
999
- },
1000
- readOnly: true,
1001
- },
1002
- };
1003
- }
1004
-
1005
- /**
1006
- * Creates the `refresh-screen` tool. Writes a bump to `application_state`
1007
- * under a well-known key; the client's `useDbSync` watches for this and
1008
- * invalidates react-query caches so the on-screen UI re-fetches its data
1009
- * without a full page reload.
1010
- *
1011
- * This is the standard way for the agent to say "the data on the screen
1012
- * just changed, please refresh it" — e.g. after editing a dashboard config,
1013
- * updating a form schema, or mutating a row that the current view renders.
1014
- */
1015
- function createRefreshScreenEntry(): Record<string, ActionEntry> {
1016
- return {
1017
- "refresh-screen": {
1018
- // Writes __screen_refresh__ to application_state, which emits its own
1019
- // distinct `screen-refresh` poll event. Don't double-emit a generic
1020
- // `action` event on top of that.
1021
- readOnly: true,
1022
- tool: {
1023
- description:
1024
- "Manually refresh the user's current screen. The framework ALREADY auto-refreshes after any successful mutating action tool call (template actions and any enabled raw DB write tools) — you do NOT need to call this after a normal action. Use it only when (a) you mutated data via a path the framework can't detect (e.g. a direct write to an external system the app mirrors), or (b) you want to pass a `scope` hint so the UI narrows which queries to refetch. The UI re-fetches its queries without a full page reload.",
1025
- parameters: {
1026
- type: "object",
1027
- properties: {
1028
- scope: {
1029
- type: "string",
1030
- description:
1031
- "Optional hint describing what changed (e.g. 'dashboard', 'form', 'settings'). Templates may use it to narrow which queries to invalidate; if omitted, all queries are invalidated.",
1032
- },
1033
- },
1034
- },
1035
- },
1036
- run: async (args) => {
1037
- const { writeAppState } =
1038
- await import("../application-state/script-helpers.js");
1039
- const nonce = Date.now();
1040
- const scope = typeof args?.scope === "string" ? args.scope : undefined;
1041
- await writeAppState(SCREEN_REFRESH_KEY, {
1042
- nonce,
1043
- ...(scope ? { scope } : {}),
1044
- });
1045
- return `refreshed${scope ? ` (scope: ${scope})` : ""}`;
1046
- },
1047
- },
1048
- };
1049
- }
1050
-
1051
- /** Well-known application-state key used by the refresh-screen tool. */
1052
- const SCREEN_REFRESH_KEY = "__screen_refresh__";
1053
- const SAFE_BROWSER_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;
1054
-
1055
- function appStateKeyForBrowserTab(key: string, browserTabId: unknown): string {
1056
- if (typeof browserTabId !== "string") return key;
1057
- const trimmed = browserTabId.trim();
1058
- return SAFE_BROWSER_TAB_ID_RE.test(trimmed) ? `${key}:${trimmed}` : key;
1059
- }
1060
-
1061
- /**
1062
- * In-memory rate-limit tracker for `/generate-title`. Keyed by user email,
1063
- * value is recent invocation timestamps within the rolling window. Stale
1064
- * entries are pruned on read.
1065
- */
1066
- const generateTitleRateLimit = new Map<string, number[]>();
1067
-
1068
- /**
1069
- * Creates the `set-search-params` / `set-url-path` tools. Writes a one-shot
1070
- * URL command to application_state; the client's URLSync component applies
1071
- * it via react-router (no full page reload) and then deletes the command.
1072
- *
1073
- * This is how the agent edits URL state — filter query params, route
1074
- * changes, hash — without needing a per-template navigate action. The
1075
- * current URL is visible to the agent via the auto-injected `<current-url>`
1076
- * block, which includes parsed search params.
1077
- */
1078
- function createUrlTools(): Record<string, ActionEntry> {
1079
- return {
1080
- "set-search-params": {
1081
- // Writes __set_url__ to application_state, which the app-state watcher
1082
- // already surfaces as a poll event. No need to double-emit.
1083
- readOnly: true,
1084
- tool: {
1085
- description:
1086
- "Update the URL query string on the user's current page. Use this to change dashboard/list filters, search terms, or any other state the app stores in `?foo=bar` style query params. One-shot — the UI applies it in ~1s without a page reload. See the current URL + parsed search params in the auto-injected `<current-url>` block. Keys are the exact query param names as they appear in the URL (e.g. `f_pubDateStart`, not just `pubDateStart`). Set a value to null or empty string to clear that param. By default merges over existing params — pass `merge: false` to replace them all.",
1087
- parameters: {
1088
- type: "object",
1089
- properties: {
1090
- params: {
1091
- type: "object",
1092
- description:
1093
- 'Map of query param → value. Each value is a string, or null/"" to clear. Example: {"f_pubDateStart": null, "f_cadence": "MONTH"}.',
1094
- },
1095
- merge: {
1096
- type: "string",
1097
- description:
1098
- '"true" (default) merges over existing params; "false" replaces them entirely.',
1099
- enum: ["true", "false"],
1100
- },
1101
- },
1102
- required: ["params"],
1103
- },
1104
- },
1105
- run: async (args) => {
1106
- const params = (args?.params ?? {}) as unknown as Record<
1107
- string,
1108
- string | null
1109
- >;
1110
- const merge = (args as any)?.merge !== "false";
1111
- const { writeAppState } =
1112
- await import("../application-state/script-helpers.js");
1113
- await writeAppState(
1114
- appStateKeyForBrowserTab(
1115
- "__set_url__",
1116
- getRequestRunContext()?.browserTabId,
1117
- ),
1118
- {
1119
- searchParams: params,
1120
- mergeSearchParams: merge,
1121
- // Unique-per-write token. The client's URLSync hook dedups by this
1122
- // so a fire-and-forget DELETE that loses its race against the next
1123
- // polling refetch can't cause the same URL command to be applied
1124
- // repeatedly (which caused the editor to bounce between slides
1125
- // when an agent turn errored partway through).
1126
- _writeId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1127
- },
1128
- );
1129
- const keys = Object.keys(params);
1130
- return `set-search-params: ${keys.length} key${keys.length === 1 ? "" : "s"}${merge ? "" : " (replace)"}`;
1131
- },
1132
- },
1133
- "set-url-path": {
1134
- // Same as set-search-params — writes application_state, already emits
1135
- // via the app-state watcher.
1136
- readOnly: true,
1137
- tool: {
1138
- description:
1139
- "Navigate the user to a different pathname, optionally also setting search params. For most template-specific routing prefer the template's `navigate` action if it exists — this is the generic fallback. One-shot, applied by the client without a page reload.",
1140
- parameters: {
1141
- type: "object",
1142
- properties: {
1143
- pathname: {
1144
- type: "string",
1145
- description: "New URL pathname (e.g. '/adhoc/weekly').",
1146
- },
1147
- params: {
1148
- type: "object",
1149
- description:
1150
- 'Optional query params to set alongside the path change. String values set, null/"" clears.',
1151
- },
1152
- merge: {
1153
- type: "string",
1154
- description:
1155
- '"true" (default) merges over existing params; "false" starts fresh.',
1156
- enum: ["true", "false"],
1157
- },
1158
- },
1159
- required: ["pathname"],
1160
- },
1161
- },
1162
- run: async (args) => {
1163
- const pathname = String(args?.pathname ?? "");
1164
- if (!pathname.startsWith("/")) {
1165
- return "Error: pathname must start with '/'.";
1166
- }
1167
- const params = (args?.params ?? {}) as unknown as Record<
1168
- string,
1169
- string | null
1170
- >;
1171
- const merge = (args as any)?.merge !== "false";
1172
- const { writeAppState } =
1173
- await import("../application-state/script-helpers.js");
1174
- await writeAppState(
1175
- appStateKeyForBrowserTab(
1176
- "__set_url__",
1177
- getRequestRunContext()?.browserTabId,
1178
- ),
1179
- {
1180
- pathname,
1181
- searchParams: params,
1182
- mergeSearchParams: merge,
1183
- // See note in set-search-params: unique-per-write dedup token so a
1184
- // race between GET and consume-DELETE in URLSync can't re-apply
1185
- // this command.
1186
- _writeId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1187
- },
1188
- );
1189
- return `set-url-path: ${pathname}`;
1190
- },
1191
- },
1192
- "ask-question": {
1193
- tool: {
1194
- description:
1195
- "Ask the user a multiple-choice clarifying question and render it inline in the chat. Use this ONLY when you are genuinely blocked on a decision you cannot resolve from context and a wrong guess would be costly — an ambiguous metric, date range, or grain; a real fork in approach. Present 2-5 concrete options and mark the most likely one recommended. Do NOT use it for confirmations, for things the user already specified, or to dodge easy work you could just do. Ask at most once per turn. Calling this yields the turn: stop and wait for the user's answer.",
1196
- parameters: {
1197
- type: "object",
1198
- properties: {
1199
- question: {
1200
- type: "string",
1201
- description:
1202
- "The complete question to ask the user. Clear, specific, ends with a question mark.",
1203
- },
1204
- header: {
1205
- type: "string",
1206
- description:
1207
- 'Optional very short label (max ~12 chars) shown as a chip/heading above the question, e.g. "Date range", "Approach", "Library".',
1208
- },
1209
- options: {
1210
- type: "string",
1211
- description:
1212
- 'A JSON array of 2-4 distinct, mutually-exclusive options (unless `allowMultiple` is true), each `{ "label": string, "value"?: string, "description"?: string, "preview"?: string, "recommended"?: boolean }`. `label` is 1-5 words; `description` explains the trade-off; `preview` is optional content (mockup, code snippet, short comparison) rendered under the option. `value` defaults to `label` when omitted. Mark the most likely option `"recommended": true`. Do NOT add an "Other" option — free text is provided automatically when `allowFreeText` is on.',
1213
- },
1214
- allowFreeText: {
1215
- type: "string",
1216
- description:
1217
- 'Whether the user may also type a free-text "Other" answer. "true" (default) or "false".',
1218
- enum: ["true", "false"],
1219
- },
1220
- allowMultiple: {
1221
- type: "string",
1222
- description:
1223
- 'Whether the user may select more than one option (multi-select). "true" or "false" (default).',
1224
- enum: ["true", "false"],
1225
- },
1226
- },
1227
- required: ["question", "options"],
1228
- },
1229
- },
1230
- run: async (args) => {
1231
- const question = String(args?.question ?? "").trim();
1232
- if (!question) return "Error: 'question' is required.";
1233
- const header = String(args?.header ?? "").trim();
1234
- const allowMultiple = String(args?.allowMultiple ?? "") === "true";
1235
- const allowFreeText = String(args?.allowFreeText ?? "true") !== "false";
1236
-
1237
- let parsedOptions: unknown;
1238
- try {
1239
- parsedOptions = JSON.parse(String(args?.options ?? "[]"));
1240
- } catch {
1241
- return "Error: 'options' must be a JSON array of { label, value?, description?, recommended? }.";
1242
- }
1243
- if (!Array.isArray(parsedOptions) || parsedOptions.length === 0) {
1244
- return "Error: 'options' must be a non-empty JSON array of { label, value?, description?, recommended? }.";
1245
- }
1246
-
1247
- type AskOption = {
1248
- label: string;
1249
- value: string;
1250
- description?: string;
1251
- preview?: string;
1252
- recommended?: boolean;
1253
- };
1254
- const options = parsedOptions
1255
- .map((raw): AskOption | null => {
1256
- const opt = (raw ?? {}) as Record<string, unknown>;
1257
- const label =
1258
- typeof opt.label === "string" && opt.label.trim()
1259
- ? opt.label.trim()
1260
- : typeof opt.value === "string"
1261
- ? String(opt.value).trim()
1262
- : "";
1263
- if (!label) return null;
1264
- const value =
1265
- typeof opt.value === "string" && opt.value.trim()
1266
- ? opt.value.trim()
1267
- : label;
1268
- const option: AskOption = { label, value };
1269
- if (typeof opt.description === "string" && opt.description.trim()) {
1270
- option.description = opt.description.trim();
1271
- }
1272
- if (typeof opt.preview === "string" && opt.preview.trim()) {
1273
- option.preview = opt.preview;
1274
- }
1275
- if (opt.recommended === true) option.recommended = true;
1276
- return option;
1277
- })
1278
- .filter((opt): opt is AskOption => opt !== null);
1279
- if (options.length === 0) {
1280
- return "Error: 'options' must contain at least one option with a label.";
1281
- }
1282
-
1283
- // Shape must match the GuidedQuestionFlow renderer in
1284
- // client/guided-questions.tsx: a `text-options` question whose options
1285
- // carry `value`, with `multiSelect` for multi-pick and `allowOther` for
1286
- // free text. The renderer otherwise injects "Explore"/"Decide" options,
1287
- // which would be noise for a focused clarifying question, so disable them.
1288
- const payload = {
1289
- questions: [
1290
- {
1291
- id: "q1",
1292
- type: "text-options" as const,
1293
- question,
1294
- ...(header ? { header } : {}),
1295
- required: !allowFreeText,
1296
- multiSelect: allowMultiple,
1297
- allowOther: allowFreeText,
1298
- includeExplore: false,
1299
- includeDecide: false,
1300
- options,
1301
- },
1302
- ],
1303
- };
1304
-
1305
- const { writeAppState } =
1306
- await import("../application-state/script-helpers.js");
1307
- await writeAppState(
1308
- appStateKeyForBrowserTab(
1309
- "guided-questions",
1310
- getRequestRunContext()?.browserTabId,
1311
- ),
1312
- payload,
1313
- );
1314
- return "Asked the user a clarifying question and rendered it in the chat. Stop here and wait for their answer — do not proceed or assume an answer.";
1315
- },
1316
- },
1317
- };
1318
- }
1319
-
1320
- function createDataWidgetActionEntries(): Record<string, ActionEntry> {
1321
- return {
1322
- "render-data-widget": {
1323
- readOnly: true,
1324
- parallelSafe: true,
1325
- chatUI: {
1326
- renderer: ACTION_CHAT_UI_DATA_WIDGET_RENDERER,
1327
- title: "Data widget",
1328
- description: "Render a validated native data table or chart in chat.",
1329
- },
1330
- tool: {
1331
- description:
1332
- "Render a native Agent-Native chat data widget from compact, real data you already retrieved or the user provided. Use this for in-chat tables, charts, graphs, trends, and compact reports when no domain-specific action already returns a native widget. Never fabricate rows or metrics just to make a chart.",
1333
- parameters: {
1334
- type: "object",
1335
- properties: {
1336
- widget: {
1337
- type: "string",
1338
- enum: ["data-table", "data-chart", "data-insights"],
1339
- description:
1340
- "Widget kind. Use data-chart for a chart, data-table for a table, or data-insights for a combined summary/chart/table card.",
1341
- },
1342
- widgetId: {
1343
- type: "string",
1344
- description: "Optional stable widget identifier.",
1345
- },
1346
- title: {
1347
- type: "string",
1348
- description: "Optional widget title.",
1349
- },
1350
- summary: {
1351
- type: "object",
1352
- description:
1353
- "Optional scalar summary values for data-insights cards.",
1354
- },
1355
- display: {
1356
- type: "object",
1357
- description:
1358
- "Optional display metadata: title, description, primaryAction.",
1359
- },
1360
- table: {
1361
- type: "object",
1362
- description:
1363
- "For data-table/data-insights: { title?, columns: [{ key, label, align? }], rows, totalRows?, sampledRows?, truncated? }.",
1364
- },
1365
- chartSeries: {
1366
- type: "object",
1367
- description:
1368
- "For data-chart/data-insights: { type: 'bar'|'line'|'area', title?, xKey, series: [{ key, label, color? }], data, sampled? }.",
1369
- },
1370
- },
1371
- required: ["widget"],
1372
- },
1373
- },
1374
- run: async (args) => dataWidgetResultSchema.parse(args),
1375
- },
1376
- };
1377
- }
1378
-
1379
- /**
1380
- * Creates db-* tools (db-query, db-exec, db-patch, db-schema) as native tools.
1381
- * By default these let the agent inspect the app's own SQL database; raw SQL
1382
- * writes are only exposed when the app explicitly opts into write mode.
1383
- * Scoping to the current user/org is enforced automatically in production via
1384
- * temp views.
1385
- *
1386
- * In dev mode template actions are invoked via bash and the agent can call
1387
- * `pnpm action db-query ...` — but in production there is no bash, so these
1388
- * must be registered as native tools for the agent to reach the app DB at all.
1389
- */
1390
- async function createDbScriptEntries(
1391
- mode: DatabaseToolsMode = "read",
1392
- options: { extensionTools?: boolean } = {},
1393
- ): Promise<Record<string, ActionEntry>> {
1394
- try {
1395
- if (mode === "off") return {};
1396
- const extensionQueryGuidance =
1397
- options.extensionTools === false
1398
- ? "Extension management tools are disabled for this app; do not query or mutate the legacy tools table as a workaround."
1399
- : "For extension management, use list-extensions, update-extension, hide-extension, or delete-extension instead of querying the legacy tools table.";
1400
- const [schemaMod, queryMod] = await Promise.all([
1401
- import("../scripts/db/schema.js"),
1402
- import("../scripts/db/query.js"),
1403
- ]);
1404
- const [execMod, patchMod] =
1405
- mode === "write"
1406
- ? await Promise.all([
1407
- import("../scripts/db/exec.js"),
1408
- import("../scripts/db/patch.js"),
1409
- ])
1410
- : [null, null];
1411
-
1412
- const entries: Record<string, ActionEntry> = {
1413
- "db-schema": wrapCliScript(
1414
- {
1415
- description:
1416
- "Show the app's SQL schema — all tables, columns, types, indexes, and foreign keys. Use this to understand the data model before querying.",
1417
- parameters: {
1418
- type: "object",
1419
- properties: {
1420
- format: {
1421
- type: "string",
1422
- description: 'Output format: "json" or "text" (default: text)',
1423
- enum: ["json", "text"],
1424
- },
1425
- },
1426
- },
1427
- },
1428
- schemaMod.default,
1429
- { readOnly: true },
1430
- ),
1431
- "db-query": wrapCliScript(
1432
- {
1433
- description: `Read from the app's own SQL database ONLY. Runs a SELECT against the app's internal tables (settings, application_state, template tables). Results are auto-scoped to the current user/org. IMPORTANT: This tool CANNOT access external data sources like data warehouses, CRMs, issue trackers, analytics platforms, calendars, mail, docs, or other third-party services. For those, use the relevant template/provider action, MCP connector, or provider-api-catalog/provider-api-docs/provider-api-request when available. If the user names a provider, that named provider wins; do not substitute a warehouse or app database copy unless they explicitly ask for it. If a table isn't in the app schema, don't try db-query — use the data-source-specific action. ${extensionQueryGuidance}`,
1434
- parameters: {
1435
- type: "object",
1436
- properties: {
1437
- sql: {
1438
- type: "string",
1439
- description:
1440
- "SELECT query to run, e.g. \"SELECT key, value FROM settings WHERE key LIKE 'sql-dashboard-%'\"",
1441
- },
1442
- args: {
1443
- type: "string",
1444
- description:
1445
- 'Optional JSON array of positional bind args for parameterized placeholders. Example: \'["draft","form-123"]\'',
1446
- },
1447
- format: {
1448
- type: "string",
1449
- description: 'Output format: "json" or "text" (default: text)',
1450
- enum: ["json", "text"],
1451
- },
1452
- limit: {
1453
- type: "string",
1454
- description:
1455
- "Append LIMIT N if the query doesn't already have one",
1456
- },
1457
- },
1458
- required: ["sql"],
1459
- },
1460
- },
1461
- queryMod.default,
1462
- { readOnly: true },
1463
- ),
1464
- };
1465
-
1466
- if (execMod && patchMod) {
1467
- entries["db-exec"] = wrapCliScript(
1468
- {
1469
- description:
1470
- "Write to the app's own SQL database ONLY. Runs INSERT / UPDATE / DELETE / REPLACE against the app's internal tables. For multiple related writes, pass `statements` so they run sequentially in one transaction instead of issuing several db-exec calls. Writes are auto-scoped to the current user/org, and `owner_email` / `org_id` are auto-injected on INSERT. Schema changes (CREATE/ALTER/DROP) are blocked. Never use this to backfill missing data for a read/analysis request or to create/modify users, members, roles, permissions, admin flags, or ownership; use a dedicated app action or reviewed code. IMPORTANT: This tool CANNOT write to external data sources like BigQuery, HubSpot, etc. For external services, use the appropriate template action.",
1471
- parameters: dbExecToolParameters(),
1472
- },
1473
- execMod.default,
1474
- );
1475
- entries["db-patch"] = wrapCliScript(
1476
- {
1477
- description:
1478
- "Surgical patch on a large text/JSON column in the app's SQL database. Two modes: (1) text find/replace via `find`/`replace`/`edits` — best for small edits to documents, slide HTML, etc. (2) structural JSON ops via `json-ops` — STRONGLY PREFERRED when the column is JSON (dashboard configs, form schemas, slide decks) because it avoids all the brace/quote/comma surgery that text find/replace requires. Use `json-ops` to set/remove values at a JSON Pointer path, or to move/insert array items — e.g. reorder dashboard panels, add a filter, rename a field. Targets exactly one row (narrow `where` by primary key). Same per-user/org scoping as db-exec.",
1479
- parameters: {
1480
- type: "object",
1481
- properties: {
1482
- table: {
1483
- type: "string",
1484
- description: "Table name (e.g. 'settings')",
1485
- },
1486
- column: {
1487
- type: "string",
1488
- description:
1489
- "Text/JSON column to patch (e.g. 'value' for settings)",
1490
- },
1491
- where: {
1492
- type: "string",
1493
- description:
1494
- "WHERE clause that matches exactly one row (e.g. \"key = 'o:org1:sql-dashboard-foo'\")",
1495
- },
1496
- find: {
1497
- type: "string",
1498
- description:
1499
- "Text mode: substring to find. Must match EXACTLY ONE occurrence by default (like Claude Code's Edit tool). If 0 matches, you get 'NOT FOUND'. If >1 matches, you get surrounding context for each match — widen `find` with unique context and retry. Use `all: \"true\"` to replace every occurrence.",
1500
- },
1501
- replace: {
1502
- type: "string",
1503
- description: "Text mode: replacement substring",
1504
- },
1505
- edits: {
1506
- type: "string",
1507
- description:
1508
- 'Text mode batch: JSON array of {find, replace} pairs. Same uniqueness rule applies to each `find`. Example: \'[{"find":"a","replace":"b"}]\'',
1509
- },
1510
- "json-ops": {
1511
- type: "string",
1512
- description:
1513
- 'JSON mode: JSON array of structural ops. Each op is {op, path, value?, from?}. `op` is one of "set", "remove", "insert", "move", "move-before". `path` / `from` use JSON Pointer ("/panels/3/title"). Examples — reorder: \'[{"op":"move","from":"/panels/7","path":"/panels/1"}]\'; edit field: \'[{"op":"set","path":"/panels/0/title","value":"New"}]\'; delete filter: \'[{"op":"remove","path":"/filters/2"}]\'; add panel: \'[{"op":"insert","path":"/panels/0","value":{"id":"p","title":"..."}}]\'. Much safer than text find/replace for JSON columns.',
1514
- },
1515
- all: {
1516
- type: "string",
1517
- description:
1518
- 'Text mode: set to "true" to replace every occurrence of each `find` (default requires exactly one match)',
1519
- enum: ["true"],
1520
- },
1521
- },
1522
- required: ["table", "column", "where"],
1523
- },
1524
- },
1525
- patchMod.default,
1526
- );
1527
- }
1528
-
1529
- return entries;
1530
- } catch {
1531
- return {};
1532
- }
1533
- }
1534
-
1535
- /**
1536
- * Creates read-only package lookup tools so agents can inspect version-matched
1537
- * framework docs and source bundled in @agent-native/core at runtime.
1538
- */
1539
- async function createDocsScriptEntries(): Promise<Record<string, ActionEntry>> {
1540
- const entries: Record<string, ActionEntry> = {};
1541
-
1542
- try {
1543
- const mod = await import("../scripts/docs/search.js");
1544
- entries["docs-search"] = wrapCliScript(
1545
- {
1546
- description:
1547
- "Search and read agent-native framework documentation, bundled AGENTS.md, and codebase skills. Use --list to see all pages, --query to search, --slug to read a specific page. Codebase skill pages use slugs like skill-<name>.",
1548
- parameters: {
1549
- type: "object",
1550
- properties: {
1551
- query: {
1552
- type: "string",
1553
- description:
1554
- "Search term to find relevant docs (e.g. 'actions', 'authentication', 'database')",
1555
- },
1556
- slug: {
1557
- type: "string",
1558
- description:
1559
- "Read a specific doc page by slug (e.g. 'actions', 'authentication', 'database')",
1560
- },
1561
- list: {
1562
- type: "string",
1563
- description: 'Set to "true" to list all available doc pages',
1564
- enum: ["true"],
1565
- },
1566
- },
1567
- },
1568
- },
1569
- mod.default,
1570
- { readOnly: true },
1571
- );
1572
- } catch {
1573
- // Keep source-search available if docs-search fails during a partial build.
1574
- }
1575
-
1576
- try {
1577
- const mod = await import("../scripts/docs/source-search.js");
1578
- entries["source-search"] = wrapCliScript(
1579
- {
1580
- description:
1581
- "Search and read the packaged Agent Native source corpus under node_modules/@agent-native/core/corpus. Use --list for sections, --query to search core/template source, and --path to read a file.",
1582
- parameters: {
1583
- type: "object",
1584
- properties: {
1585
- query: {
1586
- type: "string",
1587
- description:
1588
- "Search term to find relevant core or template source (e.g. 'defineAction', 'useActionQuery', 'view-screen').",
1589
- },
1590
- path: {
1591
- type: "string",
1592
- description:
1593
- "Read a specific corpus file or list a directory (e.g. 'templates/plan/AGENTS.md' or 'core/src/action.ts').",
1594
- },
1595
- list: {
1596
- type: "string",
1597
- description: 'Set to "true" to list corpus sections',
1598
- enum: ["true"],
1599
- },
1600
- },
1601
- },
1602
- },
1603
- mod.default,
1604
- { readOnly: true },
1605
- );
1606
- } catch {
1607
- // Older package installs may not have the corpus/search script yet.
1608
- }
1609
-
1610
- return entries;
1611
- }
1612
-
1613
- /**
1614
- * Creates resource ScriptEntries available in both prod and dev modes.
1615
- */
1616
- function shouldDefaultResourceWriteToWorkspace(path: string): boolean {
1617
- const normalized = path.replace(/^\/+/, "");
1618
- return (
1619
- normalized === "AGENTS.md" ||
1620
- normalized === "LEARNINGS.md" ||
1621
- normalized.startsWith("memory/") ||
1622
- normalized.startsWith("skills/") ||
1623
- normalized.startsWith("jobs/") ||
1624
- normalized.startsWith("agents/") ||
1625
- normalized.startsWith("remote-agents/")
1626
- );
1627
- }
1628
-
1629
- async function createResourceScriptEntries(): Promise<
1630
- Record<string, ActionEntry>
1631
- > {
1632
- try {
1633
- const [list, read, effective, write, del, saveMem, delMem, store] =
1634
- await Promise.all([
1635
- import("../scripts/resources/list.js"),
1636
- import("../scripts/resources/read.js"),
1637
- import("../scripts/resources/effective.js"),
1638
- import("../scripts/resources/write.js"),
1639
- import("../scripts/resources/delete.js"),
1640
- import("../scripts/resources/save-memory.js"),
1641
- import("../scripts/resources/delete-memory.js"),
1642
- import("../resources/store.js"),
1643
- ]);
1644
-
1645
- // Wrap each CLI runner so it captures stdout and converts args properly
1646
- const listEntry = wrapCliScript(
1647
- {
1648
- description: "",
1649
- parameters: { type: "object" as const, properties: {} },
1650
- },
1651
- list.default,
1652
- { readOnly: true },
1653
- );
1654
- const readEntry = wrapCliScript(
1655
- {
1656
- description: "",
1657
- parameters: { type: "object" as const, properties: {} },
1658
- },
1659
- read.default,
1660
- { readOnly: true },
1661
- );
1662
- const writeEntry = wrapCliScript(
1663
- {
1664
- description: "",
1665
- parameters: { type: "object" as const, properties: {} },
1666
- },
1667
- write.default,
1668
- );
1669
- const effectiveEntry = wrapCliScript(
1670
- {
1671
- description: "",
1672
- parameters: { type: "object" as const, properties: {} },
1673
- },
1674
- effective.default,
1675
- { readOnly: true },
1676
- );
1677
- const deleteEntry = wrapCliScript(
1678
- {
1679
- description: "",
1680
- parameters: { type: "object" as const, properties: {} },
1681
- },
1682
- del.default,
1683
- );
1684
-
1685
- return {
1686
- resources: {
1687
- tool: {
1688
- description:
1689
- 'Manage workspace resources. Actions: "list" (browse visible files), "read" (get contents), "effective" (show workspace -> organization/app -> personal inheritance for a path), "write" (create/update personal or shared; workspace only for local file mode control files), "promote" (make agent scratch visible), "delete" (remove personal or shared; workspace only for local file mode control files). Agent scratch writes are hidden from the Workspace view by default; use visibility="workspace" only for files the user explicitly wants to keep/manage.',
1690
- parameters: {
1691
- type: "object",
1692
- properties: {
1693
- action: {
1694
- type: "string",
1695
- description: "The operation to perform",
1696
- enum: [
1697
- "list",
1698
- "read",
1699
- "effective",
1700
- "write",
1701
- "promote",
1702
- "delete",
1703
- ],
1704
- },
1705
- path: {
1706
- type: "string",
1707
- description:
1708
- "Resource path (e.g. 'LEARNINGS.md', 'notes/ideas.md'). Required for read/write/delete.",
1709
- },
1710
- content: {
1711
- type: "string",
1712
- description: "Content to write. Required for write.",
1713
- },
1714
- scope: {
1715
- type: "string",
1716
- description:
1717
- "personal, shared, workspace, or all (default varies by action). Workspace is read-only when inherited from Dispatch; in local file mode AGENTS.md, agent-native.json, mcp.config.json, .mcp.json, and skills/ are writable.",
1718
- enum: ["personal", "shared", "workspace", "all"],
1719
- },
1720
- prefix: {
1721
- type: "string",
1722
- description:
1723
- "Filter by path prefix when listing (e.g. 'notes/')",
1724
- },
1725
- mime: {
1726
- type: "string",
1727
- description:
1728
- "MIME type for write (default: inferred from extension)",
1729
- },
1730
- format: {
1731
- type: "string",
1732
- description:
1733
- 'Output format for list: "json" or "text" (default: text)',
1734
- enum: ["json", "text"],
1735
- },
1736
- visibility: {
1737
- type: "string",
1738
- description:
1739
- 'Visibility for write: "agent_scratch" for internal working files, "workspace" for user-requested files. Defaults to agent_scratch except durable instruction/skill/job/memory paths.',
1740
- enum: ["workspace", "agent_scratch"],
1741
- },
1742
- includeAgentScratch: {
1743
- type: "boolean",
1744
- description: "Include hidden agent scratch files when listing.",
1745
- },
1746
- },
1747
- required: ["action"],
1748
- },
1749
- },
1750
- run: async (args: Record<string, string>) => {
1751
- const { action: a, ...rest } = args;
1752
- if (a === "list") return listEntry.run(rest);
1753
- if (a === "read") {
1754
- if (!rest.path) return "Error: path is required for read";
1755
- return readEntry.run(rest);
1756
- }
1757
- if (a === "effective") {
1758
- if (!rest.path) return "Error: path is required for effective";
1759
- return effectiveEntry.run(rest);
1760
- }
1761
- if (a === "write") {
1762
- if (
1763
- !rest.path ||
1764
- rest.content === undefined ||
1765
- rest.content === null
1766
- )
1767
- return "Error: path and content are required for write";
1768
- rest.createdBy = "agent";
1769
- rest.visibility =
1770
- rest.visibility ??
1771
- (shouldDefaultResourceWriteToWorkspace(String(rest.path))
1772
- ? "workspace"
1773
- : "agent_scratch");
1774
- const runCtx = getRequestRunContext();
1775
- if (runCtx?.threadId) rest.threadId = runCtx.threadId;
1776
- return writeEntry.run(rest);
1777
- }
1778
- if (a === "promote") {
1779
- if (!rest.path) return "Error: path is required for promote";
1780
- const scope = rest.scope ?? "personal";
1781
- if (scope === "workspace" || scope === "all") {
1782
- return "Error: promote supports personal or shared scope only";
1783
- }
1784
- const owner =
1785
- scope === "shared"
1786
- ? store.sharedResourceOwner(getRequestOrgId())
1787
- : (getRequestRunContext()?.owner ??
1788
- getRequestUserEmail() ??
1789
- process.env.AGENT_USER_EMAIL);
1790
- if (!owner) {
1791
- return "Error: promote requires an authenticated user";
1792
- }
1793
- const resource = await store.resourceGetByPath(
1794
- owner,
1795
- String(rest.path),
1796
- );
1797
- if (!resource) {
1798
- return `Resource not found: ${rest.path}`;
1799
- }
1800
- const promoted = await store.resourcePut(
1801
- owner,
1802
- resource.path,
1803
- resource.content,
1804
- resource.mimeType,
1805
- {
1806
- createdBy: resource.createdBy,
1807
- visibility: "workspace",
1808
- threadId: resource.threadId,
1809
- runId: resource.runId,
1810
- expiresAt: null,
1811
- metadata: resource.metadata,
1812
- },
1813
- );
1814
- return `Promoted resource: ${promoted.path}`;
1815
- }
1816
- if (a === "delete") {
1817
- if (!rest.path) return "Error: path is required for delete";
1818
- return deleteEntry.run(rest);
1819
- }
1820
- return `Error: unknown action "${a}". Use: list, read, write, promote, delete`;
1821
- },
1822
- },
1823
- "save-memory": wrapCliScript(
1824
- {
1825
- description:
1826
- "Save a memory for future conversations. Creates or updates a memory file and its index entry. Use proactively when you learn preferences, corrections, project context, or references.",
1827
- parameters: {
1828
- type: "object",
1829
- properties: {
1830
- name: {
1831
- type: "string",
1832
- description:
1833
- "Short kebab-case identifier (e.g. 'coding-style', 'deploy-process'). Used as the filename.",
1834
- },
1835
- type: {
1836
- type: "string",
1837
- description: "Memory category",
1838
- enum: ["user", "feedback", "project", "reference"],
1839
- },
1840
- description: {
1841
- type: "string",
1842
- description:
1843
- "One-line summary shown in the memory index (keep under 80 chars)",
1844
- },
1845
- content: {
1846
- type: "string",
1847
- description:
1848
- "The memory content in markdown. For updates, read first and provide full updated content.",
1849
- },
1850
- },
1851
- required: ["name", "type", "description", "content"],
1852
- },
1853
- },
1854
- saveMem.default,
1855
- ),
1856
- "delete-memory": wrapCliScript(
1857
- {
1858
- description:
1859
- "Delete a memory entry and remove it from the memory index.",
1860
- parameters: {
1861
- type: "object",
1862
- properties: {
1863
- name: {
1864
- type: "string",
1865
- description: "The memory name to delete (e.g. 'coding-style')",
1866
- },
1867
- },
1868
- required: ["name"],
1869
- },
1870
- },
1871
- delMem.default,
1872
- ),
1873
- };
1874
- } catch {
1875
- // Resources not available — skip silently
1876
- return {};
1877
- }
1878
- }
1879
-
1880
- /**
1881
- * Creates a unified chat-history ActionEntry that dispatches to search, open,
1882
- * rename, or lightweight organization actions.
1883
- */
1884
- async function createChatScriptEntries(): Promise<Record<string, ActionEntry>> {
1885
- try {
1886
- const [searchMod, openMod] = await Promise.all([
1887
- import("../scripts/chat/search-chats.js"),
1888
- import("../scripts/chat/open-chat.js"),
1889
- ]);
1890
-
1891
- const searchEntry = wrapCliScript(
1892
- {
1893
- description: "Search or list past agent chat threads.",
1894
- parameters: {
1895
- type: "object",
1896
- properties: {
1897
- query: {
1898
- type: "string",
1899
- description:
1900
- "Search term to find chats by title, preview, or content",
1901
- },
1902
- limit: {
1903
- type: "string",
1904
- description: "Max number of results (default: 20)",
1905
- },
1906
- format: {
1907
- type: "string",
1908
- description: "Output format",
1909
- enum: ["json", "text"],
1910
- },
1911
- includeArchived: {
1912
- type: "boolean",
1913
- description:
1914
- "Also include archived chats in the results. Archived chats are excluded by default.",
1915
- },
1916
- },
1917
- },
1918
- },
1919
- searchMod.default,
1920
- );
1921
-
1922
- const openEntry = wrapCliScript(
1923
- {
1924
- description: "Open a chat thread in the UI.",
1925
- parameters: {
1926
- type: "object",
1927
- properties: {
1928
- id: {
1929
- type: "string",
1930
- description: "The chat thread ID to open",
1931
- },
1932
- },
1933
- required: ["id"],
1934
- },
1935
- },
1936
- openMod.default,
1937
- );
1938
-
1939
- return {
1940
- "chat-history": {
1941
- tool: {
1942
- description:
1943
- "Manage past agent chat threads. Use action 'search' to find previous conversations by keyword, 'open' to open a thread in the UI, or 'rename'/'pin'/'unpin'/'archive' to organize history.",
1944
- parameters: {
1945
- type: "object",
1946
- properties: {
1947
- action: {
1948
- type: "string",
1949
- description: "The operation to perform",
1950
- enum: ["search", "open", "rename", "pin", "unpin", "archive"],
1951
- },
1952
- query: {
1953
- type: "string",
1954
- description:
1955
- "(search) Search term to find chats by title, preview, or content",
1956
- },
1957
- limit: {
1958
- type: "string",
1959
- description: "(search) Max number of results (default: 20)",
1960
- },
1961
- format: {
1962
- type: "string",
1963
- description: "(search) Output format",
1964
- enum: ["json", "text"],
1965
- },
1966
- includeArchived: {
1967
- type: "boolean",
1968
- description:
1969
- "(search) Also include archived chats in the results. Archived chats are excluded by default, matching the 'archive' action's effect on the chat list.",
1970
- },
1971
- id: {
1972
- type: "string",
1973
- description:
1974
- "(open, rename, pin, unpin, archive) The chat thread ID to manage",
1975
- },
1976
- title: {
1977
- type: "string",
1978
- description: "(rename) New chat title",
1979
- },
1980
- },
1981
- required: ["action"],
1982
- },
1983
- },
1984
- run: async (args) => {
1985
- if (args?.action === "open") {
1986
- return openEntry.run(args);
1987
- }
1988
- if (
1989
- args?.action === "rename" ||
1990
- args?.action === "pin" ||
1991
- args?.action === "unpin" ||
1992
- args?.action === "archive"
1993
- ) {
1994
- const id = typeof args?.id === "string" ? args.id : "";
1995
- if (!id) return "Missing required id.";
1996
- const owner =
1997
- getRequestRunContext()?.owner ?? getRequestUserEmail() ?? "";
1998
- if (!owner) return "No authenticated user is available.";
1999
- const thread = await resolveThreadAccess(owner, id, "editor", {
2000
- orgId: getRequestOrgId(),
2001
- });
2002
- if (!thread) {
2003
- return `Chat thread "${id}" not found.`;
2004
- }
2005
- const title = thread.title || thread.preview || "(untitled)";
2006
- if (args.action === "rename") {
2007
- const nextTitle =
2008
- typeof args?.title === "string"
2009
- ? args.title.replace(/\s+/g, " ").trim().slice(0, 160)
2010
- : "";
2011
- if (!nextTitle) return "Missing required title.";
2012
- const renamed = await renameThread(id, nextTitle);
2013
- if (!renamed) return `Chat thread "${id}" could not be renamed.`;
2014
- return `Renamed chat "${title}" to "${nextTitle}".`;
2015
- }
2016
- if (args.action === "archive") {
2017
- const archived = await setThreadArchived(id, true);
2018
- if (!archived)
2019
- return `Chat thread "${id}" could not be archived.`;
2020
- return `Archived chat: ${title}`;
2021
- }
2022
- const pinned = await setThreadPinned(id, args.action === "pin");
2023
- if (!pinned) return `Chat thread "${id}" could not be updated.`;
2024
- return `${args.action === "pin" ? "Pinned" : "Unpinned"} chat: ${title}`;
2025
- }
2026
- return searchEntry.run(args);
2027
- },
2028
- },
2029
- };
2030
- } catch {
2031
- return {};
2032
- }
2033
- }
2034
-
2035
- /**
2036
- * Creates the consolidated manage-agent-engine tool (list / set / test).
2037
- * Let the agent inspect and configure the active LLM engine.
2038
- */
2039
- async function createAgentEngineScriptEntries(
2040
- appId?: string,
2041
- ): Promise<Record<string, ActionEntry>> {
2042
- try {
2043
- const mod = await import("../scripts/agent-engines/manage-agent-engine.js");
2044
-
2045
- return {
2046
- "manage-agent-engine": {
2047
- tool: mod.tool,
2048
- run: (args) =>
2049
- mod.run({
2050
- ...args,
2051
- appId:
2052
- typeof args.appId === "string" && args.appId.trim()
2053
- ? args.appId
2054
- : (appId ?? ""),
2055
- }),
2056
- },
2057
- };
2058
- } catch {
2059
- return {};
2060
- }
2061
- }
2062
-
2063
- /**
2064
- * Creates the manage-agent-loop-settings tool. Lets the agent inspect and
2065
- * configure the loop step limit it may hit on long-running work.
2066
- */
2067
- async function createAgentLoopSettingsScriptEntries(): Promise<
2068
- Record<string, ActionEntry>
2069
- > {
2070
- try {
2071
- const mod = await import("../scripts/manage-agent-loop-settings.js");
2072
-
2073
- return {
2074
- "manage-agent-loop-settings": { tool: mod.tool, run: mod.run },
2075
- };
2076
- } catch {
2077
- return {};
2078
- }
2079
- }
2080
-
2081
- /**
2082
- * Creates the call-agent ActionEntry for cross-agent A2A communication.
2083
- * Binds selfAppId so the agent cannot call itself via call-agent.
2084
- */
2085
- async function createCallAgentScriptEntry(
2086
- selfAppId?: string,
2087
- ): Promise<Record<string, ActionEntry>> {
2088
- try {
2089
- const mod = await import("../scripts/call-agent.js");
2090
- return {
2091
- "call-agent": {
2092
- tool: mod.tool,
2093
- run: (args, context) => mod.run(args, context, selfAppId),
2094
- },
2095
- };
2096
- } catch {
2097
- return {};
2098
- }
2099
- }
2100
-
2101
- function createBuilderBrowserTool(deps: {
2102
- getOrigin: () => string;
2103
- getOwner?: () => string | null | undefined;
2104
- extensionTools?: boolean;
2105
- }): Record<string, ActionEntry> {
2106
- const extensionRequestGuidance =
2107
- deps.extensionTools === false
2108
- ? "Do NOT call this for requests to create or edit user-authored extensions/widgets/dashboards/calculators/mini-apps; extension tools are disabled for this app, and Builder is only for source-code changes to the host app. "
2109
- : "Do NOT call this for a self-contained extension/widget/dashboard/calculator/mini-app or an extension that fits an existing named slot; those use create-extension/update-extension instead. If the requested result requires changing the host UI or placing UI where no named slot exists, it IS a source-code change: call this through the normal flow even when the user describes it as an extension. Never stop at saying an extension cannot do it. ";
2110
- const setBuiltinForCurrentUser = async (
2111
- id: BuiltinMcpCapabilityId,
2112
- enabled: boolean,
2113
- ) => {
2114
- const email = getRequestUserEmail();
2115
- if (!email) {
2116
- return {
2117
- ok: false,
2118
- error: "not-signed-in",
2119
- message: "You must be signed in to change built-in MCP tools.",
2120
- };
2121
- }
2122
- const enabledIds = await setBuiltinMcpCapabilityEnabled(
2123
- "user",
2124
- email,
2125
- id,
2126
- enabled,
2127
- );
2128
- const manager = getGlobalMcpManager();
2129
- if (manager) {
2130
- await manager.reconfigure(await buildMergedConfig());
2131
- }
2132
- return { ok: true, enabledIds: enabledIds ?? [] };
2133
- };
2134
-
2135
- const entries: Record<string, ActionEntry> = {
2136
- "connect-builder": {
2137
- tool: {
2138
- description: `Render a Builder.io card inline in the chat. Call this as the first step (no code exploration or planning needed) when the user asks to modify the APP'S OWN SOURCE CODE: add a feature, change the UI chrome, edit a React component, add a route, add an integration, fix a bug in the app itself, or anything else that requires source-file edits while in hosted/production mode. ${extensionRequestGuidance}Do NOT call this for content the app is meant to produce — creating a video, generating a design, drafting an email, building a slide deck, making a dashboard, etc. — those run through the app's own domain actions, not Builder. Do NOT mention 'click Send to Builder' in your response unless this card is already in the conversation. The tool result includes \`builderEnabled\`; treat \`true\` as "Builder Cloud Agents can take the code-change handoff" and \`false\` as "this still needs a code change, but no Builder Cloud Agent can run here." If Builder is connected and Builder Cloud Agents are available, the card shows a 'Send to Builder' button that hands the work off to Builder's cloud agent and returns a branch URL. If \`builderEnabled\` is false, the card still renders but shows the code-change fallback: "This requires a code change. Edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way you like." Never tell the user to enable Builder Cloud Agents in Builder org settings or beta settings, and do not claim the Builder card has everything, is pre-loaded for handoff, or can run the cloud agent when \`builderEnabled\` is false. When you call this for a code-change request, pass the user's request verbatim as the \`prompt\` arg so the card can forward it to Builder unchanged when cloud agents are available.`,
2139
- parameters: {
2140
- type: "object",
2141
- properties: {
2142
- prompt: {
2143
- type: "string",
2144
- description:
2145
- "The user's feature / change request, verbatim. Forwarded to Builder's cloud agent when the user clicks Send. Omit only for generic 'connect Builder' requests that aren't tied to a specific code change.",
2146
- },
2147
- },
2148
- },
2149
- },
2150
- run: async (args) => {
2151
- const { getBuilderCredentialAuthFailure, resolveBuilderCredentials } =
2152
- await import("./credential-provider.js");
2153
- const creds = await resolveBuilderCredentials();
2154
- const authFailure = await getBuilderCredentialAuthFailure(creds);
2155
- const configured = !!(
2156
- creds.privateKey &&
2157
- creds.publicKey &&
2158
- !authFailure
2159
- );
2160
- const branchProjectId = await resolveBuilderBranchProjectId();
2161
- const prompt = typeof args?.prompt === "string" ? args.prompt : "";
2162
- const origin = deps.getOrigin();
2163
- const ownerEmail = deps.getOwner?.() ?? getRequestUserEmail();
2164
- return JSON.stringify({
2165
- kind: "connect-builder-card",
2166
- configured,
2167
- builderEnabled: !!branchProjectId,
2168
- connectUrl: getBuilderBrowserConnectUrlForOwner(origin, ownerEmail),
2169
- orgName: creds.orgName || null,
2170
- prompt,
2171
- });
2172
- },
2173
- },
2174
- "set-browser-control": {
2175
- tool: {
2176
- description:
2177
- "Enable or disable built-in browser-control MCP tools for the current user. Call this when the user asks to test, screenshot, inspect, or interact with a web page and browser tools are not available; confirm once before enabling. Prefer the chrome-devtools backend for live logged-in Chrome, and use playwright when an isolated browser is better.",
2178
- parameters: {
2179
- type: "object",
2180
- properties: {
2181
- enabled: {
2182
- type: "boolean",
2183
- description: "Whether browser-control tools should be enabled.",
2184
- },
2185
- backend: {
2186
- type: "string",
2187
- enum: ["chrome-devtools", "playwright"],
2188
- description:
2189
- "Browser backend to enable. Defaults to chrome-devtools.",
2190
- },
2191
- },
2192
- required: ["enabled"],
2193
- },
2194
- },
2195
- run: async (args) => {
2196
- const parsed =
2197
- args && typeof args === "object"
2198
- ? (args as Record<string, unknown>)
2199
- : {};
2200
- const enabled = parsed.enabled !== false;
2201
- const requestedBackend =
2202
- typeof parsed.backend === "string" ? parsed.backend : undefined;
2203
- const backend =
2204
- requestedBackend === "playwright" ? "playwright" : "chrome-devtools";
2205
- const targetId: BuiltinMcpCapabilityId =
2206
- backend === "playwright"
2207
- ? "browser-playwright"
2208
- : "browser-chrome-devtools";
2209
-
2210
- if (!enabled) {
2211
- const chrome = await setBuiltinForCurrentUser(
2212
- "browser-chrome-devtools",
2213
- false,
2214
- );
2215
- if (!chrome.ok) return JSON.stringify(chrome);
2216
- const playwright = await setBuiltinForCurrentUser(
2217
- "browser-playwright",
2218
- false,
2219
- );
2220
- return JSON.stringify({
2221
- ...playwright,
2222
- enabled: false,
2223
- message: "Browser-control MCP tools are disabled.",
2224
- });
2225
- }
2226
-
2227
- const result = await setBuiltinForCurrentUser(targetId, true);
2228
- return JSON.stringify({
2229
- ...result,
2230
- enabled: true,
2231
- backend,
2232
- message:
2233
- backend === "chrome-devtools"
2234
- ? "Chrome DevTools MCP is enabled. Browser tools will be available on the next action when Chrome remote debugging is available."
2235
- : "Playwright MCP is enabled. Browser tools will be available on the next action in an isolated Playwright browser.",
2236
- });
2237
- },
2238
- },
2239
- "set-computer-use": {
2240
- tool: {
2241
- description:
2242
- "Enable or disable built-in Computer Use MCP tools for the current user. Call only after the user explicitly asks to let the agent control local desktop apps. macOS may require Screen Recording and Accessibility permissions.",
2243
- parameters: {
2244
- type: "object",
2245
- properties: {
2246
- enabled: {
2247
- type: "boolean",
2248
- description: "Whether Computer Use tools should be enabled.",
2249
- },
2250
- },
2251
- required: ["enabled"],
2252
- },
2253
- },
2254
- run: async (args) => {
2255
- const parsed =
2256
- args && typeof args === "object"
2257
- ? (args as Record<string, unknown>)
2258
- : {};
2259
- const enabled = parsed.enabled !== false;
2260
- if (enabled && process.platform !== "darwin") {
2261
- return JSON.stringify({
2262
- ok: false,
2263
- error: "unsupported-platform",
2264
- message: "Computer Use is currently available only on macOS.",
2265
- });
2266
- }
2267
- const result = await setBuiltinForCurrentUser("computer-use", enabled);
2268
- return JSON.stringify({
2269
- ...result,
2270
- enabled,
2271
- message: enabled
2272
- ? "Computer Use MCP is enabled. If macOS prompts, grant Screen Recording and Accessibility permission in System Settings > Privacy & Security."
2273
- : "Computer Use MCP is disabled.",
2274
- });
2275
- },
2276
- },
2277
- "activate-browser": {
2278
- tool: {
2279
- description:
2280
- "Activate browser automation tools. Call this when you need to interact with a real browser — e.g. to extract design tokens from a rendered page, take screenshots, read computed styles from JS-heavy sites, or test a live URL. After activation, chrome-devtools MCP tools (navigate, click, evaluate_script, take_screenshot, etc.) become available on your next action. Requires Builder.io connection.",
2281
- parameters: {
2282
- type: "object",
2283
- properties: {
2284
- sessionId: {
2285
- type: "string",
2286
- description:
2287
- "Optional session identifier for the browser connection. Auto-generated if omitted.",
2288
- },
2289
- },
2290
- },
2291
- },
2292
- run: async (args) => {
2293
- const { resolveBuilderCredentials } =
2294
- await import("./credential-provider.js");
2295
- const creds = await resolveBuilderCredentials();
2296
- if (!creds.privateKey || !creds.publicKey) {
2297
- return JSON.stringify({
2298
- error: "builder-not-connected",
2299
- message:
2300
- "Builder.io is not connected. Call `connect-builder` first to enable browser automation.",
2301
- });
2302
- }
2303
-
2304
- const { requestBuilderBrowserConnection } =
2305
- await import("./builder-browser.js");
2306
- const sessionId =
2307
- (typeof args?.sessionId === "string" && args.sessionId) ||
2308
- `an-browser-${Date.now()}`;
2309
-
2310
- let connection: Record<string, unknown>;
2311
- try {
2312
- connection = await requestBuilderBrowserConnection({ sessionId });
2313
- } catch (err: any) {
2314
- return JSON.stringify({
2315
- error: "browser-connection-failed",
2316
- message: `Failed to get browser connection: ${err?.message ?? err}`,
2317
- });
2318
- }
2319
-
2320
- const wsUrl = connection.wsUrl as string;
2321
- if (!wsUrl) {
2322
- return JSON.stringify({
2323
- error: "no-ws-url",
2324
- message: "Browser connection did not return a WebSocket URL.",
2325
- });
2326
- }
2327
-
2328
- const manager = getGlobalMcpManager();
2329
- if (!manager) {
2330
- return JSON.stringify({
2331
- error: "no-mcp-manager",
2332
- message: "MCP manager is not available.",
2333
- });
2334
- }
2335
-
2336
- // Add chrome-devtools-mcp server pointing at the provisioned browser
2337
- const currentConfig = manager.getConfig();
2338
- const servers = { ...(currentConfig?.servers ?? {}) };
2339
- servers["chrome-devtools"] = {
2340
- command: "npx",
2341
- args: [
2342
- "-y",
2343
- "chrome-devtools-mcp@0.26.0",
2344
- "--wsEndpoint",
2345
- wsUrl,
2346
- "--categoryEmulation=false",
2347
- ],
2348
- type: "stdio",
2349
- } as any;
2350
-
2351
- await manager.reconfigure({
2352
- servers,
2353
- source: currentConfig?.source ?? "runtime",
2354
- });
2355
-
2356
- return JSON.stringify({
2357
- success: true,
2358
- message:
2359
- "Browser activated. Chrome DevTools MCP tools (mcp__chrome-devtools__*) are now available. Use them on your next action to navigate pages, read DOM, take screenshots, evaluate JavaScript, etc.",
2360
- wsUrl,
2361
- sessionId,
2362
- });
2363
- },
2364
- },
2365
- };
2366
-
2367
- if (!areBuiltinMcpCapabilitiesSupported()) {
2368
- delete entries["set-browser-control"];
2369
- delete entries["set-computer-use"];
2370
- }
2371
-
2372
- return entries;
2373
- }
2374
-
2375
- /**
2376
- * Creates the unified `agent-teams` tool that consolidates all sub-agent
2377
- * orchestration behind a single tool with an `action` parameter.
2378
- */
2379
- function createTeamTools(deps: {
2380
- getOwner: () => string;
2381
- getSystemPrompt: () => string;
2382
- getActions: () => Record<string, ActionEntry>;
2383
- getEngine: () => AgentEngine;
2384
- getModel: () => string;
2385
- getParentThreadId: () => string;
2386
- getSend: () =>
2387
- | ((event: import("../agent/types.js").AgentChatEvent) => void)
2388
- | null;
2389
- }): Record<string, ActionEntry> {
2390
- return {
2391
- "agent-teams": {
2392
- tool: {
2393
- description:
2394
- "Manage background sub-agent tasks. Use action 'spawn' to start a new sub-agent, 'status' to check progress, 'read-result' to get a finished task's output, 'send' to message a running sub-agent, or 'list' to see all tasks. A successful spawn only means the task started and is running; do not report it as finished until status/read-result shows a terminal status.",
2395
- parameters: {
2396
- type: "object",
2397
- properties: {
2398
- action: {
2399
- type: "string",
2400
- enum: ["spawn", "status", "read-result", "send", "list"],
2401
- description: "The operation to perform",
2402
- },
2403
- task: {
2404
- type: "string",
2405
- description:
2406
- "(spawn) Clear description of what the sub-agent should accomplish",
2407
- },
2408
- instructions: {
2409
- type: "string",
2410
- description:
2411
- "(spawn) Optional additional instructions or context for the sub-agent",
2412
- },
2413
- name: {
2414
- type: "string",
2415
- description:
2416
- "(spawn) Short name for the sub-agent tab (e.g. 'Research', 'Draft email'). If omitted, derived from the task.",
2417
- },
2418
- agent: {
2419
- type: "string",
2420
- description:
2421
- "(spawn) Optional custom agent profile from agents/*.md to use for this task.",
2422
- },
2423
- taskId: {
2424
- type: "string",
2425
- description:
2426
- "(status, read-result, send) The task ID returned by a previous spawn",
2427
- },
2428
- message: {
2429
- type: "string",
2430
- description: "(send) Message to send to the sub-agent",
2431
- },
2432
- },
2433
- required: ["action"],
2434
- },
2435
- },
2436
- run: async (args: Record<string, string>) => {
2437
- const action = args.action;
2438
-
2439
- // ── spawn ──────────────────────────────────────────────
2440
- if (action === "spawn") {
2441
- if (!args.task) throw new Error("'task' is required for spawn");
2442
- // Capture the send function NOW (at spawn time) so that
2443
- // concurrent runs don't clobber each other's send reference.
2444
- const capturedSend = deps.getSend();
2445
- const { spawnTask } = await import("./agent-teams.js");
2446
- // Filter out the team tool so sub-agents can't spawn sub-agents
2447
- const subAgentActions = Object.fromEntries(
2448
- Object.entries(deps.getActions()).filter(
2449
- ([name]) => name !== "agent-teams",
2450
- ),
2451
- );
2452
- let instructions = args.instructions;
2453
- let selectedModel = deps.getModel();
2454
- let selectedName = args.name || "";
2455
- if (args.agent) {
2456
- const { findAccessibleCustomAgent } =
2457
- await import("../resources/agents.js");
2458
- const profile = await findAccessibleCustomAgent(
2459
- deps.getOwner(),
2460
- args.agent,
2461
- );
2462
- if (!profile) {
2463
- throw new Error(`Custom agent not found: ${args.agent}`);
2464
- }
2465
- const profileInstructions =
2466
- `## Custom Agent Profile: ${profile.name}\n\n` +
2467
- (profile.description ? `${profile.description}\n\n` : "") +
2468
- profile.instructions;
2469
- instructions = instructions
2470
- ? `${profileInstructions}\n\n## Extra Task Context\n\n${instructions}`
2471
- : profileInstructions;
2472
- selectedModel = profile.model ?? selectedModel;
2473
- selectedName = selectedName || profile.name;
2474
- }
2475
- const task = await spawnTask({
2476
- description: args.task,
2477
- instructions,
2478
- ownerEmail: deps.getOwner(),
2479
- systemPrompt: deps.getSystemPrompt(),
2480
- actions: subAgentActions,
2481
- engine: deps.getEngine(),
2482
- model: selectedModel,
2483
- name: selectedName || undefined,
2484
- parentThreadId: deps.getParentThreadId(),
2485
- parentSend: (event) => {
2486
- if (capturedSend) capturedSend(event);
2487
- },
2488
- });
2489
- return JSON.stringify({
2490
- taskId: task.taskId,
2491
- threadId: task.threadId,
2492
- runId: task.runId,
2493
- status: task.status,
2494
- parentThreadId: task.parentThreadId,
2495
- state: "launched_pending_completion",
2496
- message:
2497
- "Sub-agent launched and is still running. Use status or read-result later; do not describe this task as completed from the spawn response alone.",
2498
- description: task.description,
2499
- name: task.name ?? selectedName,
2500
- });
2501
- }
2502
-
2503
- // ── status ─────────────────────────────────────────────
2504
- if (action === "status") {
2505
- if (!args.taskId) throw new Error("'taskId' is required for status");
2506
- const { getTask } = await import("./agent-teams.js");
2507
- const task = await getTask(args.taskId);
2508
- if (!task) return JSON.stringify({ error: "Task not found" });
2509
- return JSON.stringify({
2510
- taskId: task.taskId,
2511
- threadId: task.threadId,
2512
- parentThreadId: task.parentThreadId,
2513
- status: task.status,
2514
- description: task.description,
2515
- name: task.name,
2516
- preview: task.preview,
2517
- currentStep: task.currentStep,
2518
- summary: task.summary,
2519
- });
2520
- }
2521
-
2522
- // ── read-result ────────────────────────────────────────
2523
- if (action === "read-result") {
2524
- if (!args.taskId)
2525
- throw new Error("'taskId' is required for read-result");
2526
- const { getTask } = await import("./agent-teams.js");
2527
- const task = await getTask(args.taskId);
2528
- if (!task) return JSON.stringify({ error: "Task not found" });
2529
- if (task.status === "running") {
2530
- return JSON.stringify({
2531
- status: "running",
2532
- taskId: task.taskId,
2533
- threadId: task.threadId,
2534
- parentThreadId: task.parentThreadId,
2535
- name: task.name,
2536
- preview: task.preview,
2537
- message:
2538
- "Task is still running. Do not report it as complete; use status/read-result later.",
2539
- });
2540
- }
2541
- return JSON.stringify({
2542
- taskId: task.taskId,
2543
- threadId: task.threadId,
2544
- parentThreadId: task.parentThreadId,
2545
- name: task.name,
2546
- status: task.status,
2547
- summary: task.summary,
2548
- preview: task.preview,
2549
- });
2550
- }
2551
-
2552
- // ── send ───────────────────────────────────────────────
2553
- if (action === "send") {
2554
- if (!args.taskId) throw new Error("'taskId' is required for send");
2555
- if (!args.message) throw new Error("'message' is required for send");
2556
- const { sendToTask } = await import("./agent-teams.js");
2557
- const result = await sendToTask(args.taskId, args.message);
2558
- return JSON.stringify(result);
2559
- }
2560
-
2561
- // ── list ───────────────────────────────────────────────
2562
- if (action === "list") {
2563
- const { listTasks } = await import("./agent-teams.js");
2564
- const tasks = await listTasks();
2565
- if (tasks.length === 0) {
2566
- return "No sub-agent tasks.";
2567
- }
2568
- return JSON.stringify(
2569
- tasks.map((t) => ({
2570
- taskId: t.taskId,
2571
- threadId: t.threadId,
2572
- parentThreadId: t.parentThreadId,
2573
- name: t.name,
2574
- description: t.description,
2575
- status: t.status,
2576
- currentStep: t.currentStep,
2577
- hasResult: t.summary.length > 0,
2578
- })),
2579
- null,
2580
- 2,
2581
- );
2582
- }
2583
-
2584
- throw new Error(
2585
- `Unknown action '${action}'. Use one of: spawn, status, read-result, send, list`,
2586
- );
2587
- },
2588
- },
2589
- };
2590
- }
2591
-
2592
- type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
2593
-
2594
- export interface AgentChatPluginOptions {
2595
- /** Template-specific actions (email ops, booking ops, etc.) */
2596
- actions?:
2597
- | Record<string, ActionEntry>
2598
- | (() =>
2599
- | Record<string, ActionEntry>
2600
- | Promise<Record<string, ActionEntry>>);
2601
- /** @deprecated Use `actions` instead */
2602
- scripts?:
2603
- | Record<string, ActionEntry>
2604
- | (() =>
2605
- | Record<string, ActionEntry>
2606
- | Promise<Record<string, ActionEntry>>);
2607
- /** System prompt for the agent. A sensible default is provided. */
2608
- systemPrompt?: string;
2609
- /** Additional system prompt prepended in dev mode */
2610
- devSystemPrompt?: string;
2611
- /** Model to use. Defaults to the resolved engine's default model. */
2612
- model?: string;
2613
- /** Optional per-app agent run chunk budget in milliseconds. Defaults to
2614
- * AGENT_RUN_SOFT_TIMEOUT_MS when set, otherwise no framework-imposed
2615
- * timeout. When reached, long runs continue through the hidden continuation
2616
- * path instead of surfacing a timeout warning. */
2617
- runSoftTimeoutMs?: number;
2618
- /** Optional per-app run-manager no-progress watchdog in milliseconds. */
2619
- runNoProgressTimeoutMs?: number;
2620
- /**
2621
- * Opt this app into Netlify durable background-function agent-chat runs. This
2622
- * gives hosted agent turns the 15-minute async-function budget when the app's
2623
- * Netlify build also emits the background function.
2624
- */
2625
- durableBackgroundRuns?: boolean;
2626
- /** Anthropic API key. Falls back to ANTHROPIC_API_KEY env var */
2627
- apiKey?: string;
2628
- /**
2629
- * Agent engine to use. Can be a pre-constructed AgentEngine, a registered
2630
- * engine name (e.g. "anthropic", "ai-sdk:openai"), or an object with name
2631
- * and config. Defaults to the "anthropic" engine using ANTHROPIC_API_KEY.
2632
- */
2633
- engine?:
2634
- | import("../agent/engine/types.js").AgentEngine
2635
- | string
2636
- | { name: string; config: Record<string, unknown> };
2637
- /** Route path. Default: /_agent-native/agent-chat */
2638
- path?: string;
2639
- /** Custom mention providers for @-tagging template entities */
2640
- mentionProviders?:
2641
- | Record<string, MentionProvider>
2642
- | (() =>
2643
- | Record<string, MentionProvider>
2644
- | Promise<Record<string, MentionProvider>>);
2645
- /** App ID used to exclude self from agent discovery (e.g., "mail", "calendar") */
2646
- appId?: string;
2647
- /** Optional MCP server branding surfaced during the initialize handshake. */
2648
- mcpServerInfo?: {
2649
- /** Human-facing title. Defaults to the capitalized app id/name. */
2650
- title?: string;
2651
- /** Host-facing description. Defaults to "Agent-native <app> agent". */
2652
- description?: string;
2653
- /** Canonical app URL. Relative URLs are resolved against the request origin. */
2654
- websiteUrl?: string;
2655
- /** App icons. Relative `src` values are resolved against the request origin. */
2656
- icons?: Array<{
2657
- src: string;
2658
- mimeType?: string;
2659
- sizes?: string[];
2660
- theme?: "light" | "dark";
2661
- }>;
2662
- };
2663
- /**
2664
- * Optional callback to resolve the org ID for the current request.
2665
- * When provided, the resolved value is set as AGENT_ORG_ID env var so
2666
- * that db-query/db-exec automatically scope by org_id in addition to
2667
- * owner_email.
2668
- *
2669
- * If not provided, the framework automatically uses `session.orgId` from
2670
- * Better Auth's active organization. Only provide this callback when you
2671
- * need custom org resolution logic (e.g., Atlassian org mapping).
2672
- */
2673
- resolveOrgId?: (event: any) => string | null | Promise<string | null>;
2674
- /**
2675
- * Optional owner resolver for public/anonymous chat surfaces. When the
2676
- * normal app session is missing, this callback may return a synthetic
2677
- * owner id for a narrowly-scoped public request (for example, a public
2678
- * shared document page). Anonymous requests use a read-only tool set by
2679
- * default so public viewers cannot mutate app data through the agent.
2680
- */
2681
- anonymousOwner?: (event: any) => string | null | Promise<string | null>;
2682
- /**
2683
- * Keep anonymous-owner requests on read-only template actions. Defaults to
2684
- * true. Only disable for single-tenant apps that intentionally allow public
2685
- * agent mutations.
2686
- */
2687
- anonymousReadOnly?: boolean;
2688
- /**
2689
- * Optional callback to append template-specific context to the system
2690
- * prompt on each request. Runs after AGENTS.md / skills / memory are
2691
- * loaded and before the schema block — use it to inject dynamic SQL
2692
- * context like a data dictionary, active feature flags, or whatever
2693
- * the agent should know about *right now* for this user/org.
2694
- *
2695
- * Return `null` or an empty string to skip. The string you return is
2696
- * appended verbatim, so wrap it in your own XML tags (e.g.
2697
- * `<data-dictionary>…</data-dictionary>`) to keep the prompt scannable.
2698
- *
2699
- * Called on every request in every prompt variant (lean, lazy, full).
2700
- * Templates that want to suppress it in a particular mode should return
2701
- * `null` from the callback based on their own logic.
2702
- */
2703
- extraContext?: (
2704
- event: any,
2705
- owner: string,
2706
- ) => string | null | Promise<string | null>;
2707
- /**
2708
- * Optional final-answer guard. Templates can use this to require a
2709
- * corrective retry before accepting a text-only final answer, e.g. forcing
2710
- * real data-source tool calls for analytics requests.
2711
- */
2712
- finalResponseGuard?: import("../agent/production-agent.js").AgentLoopFinalResponseGuard;
2713
- /**
2714
- * Optional per-template request normalizer. Runs after authentication and
2715
- * before the model sees the message, so apps can translate chat attachments
2716
- * into template-native file handles while preserving the user's visible text.
2717
- */
2718
- prepareRequest?: (details: {
2719
- event: any;
2720
- ownerEmail: string | null;
2721
- message: string;
2722
- displayMessage?: string;
2723
- attachments: AgentChatAttachment[];
2724
- references: AgentChatReference[];
2725
- threadId?: string;
2726
- internalContinuation?: boolean;
2727
- mode: "act" | "plan";
2728
- }) =>
2729
- | void
2730
- | {
2731
- message?: string;
2732
- displayMessage?: string;
2733
- attachments?: AgentChatAttachment[];
2734
- }
2735
- | Promise<void | {
2736
- message?: string;
2737
- displayMessage?: string;
2738
- attachments?: AgentChatAttachment[];
2739
- }>;
2740
- /**
2741
- * Use ONLY the template's `systemPrompt` and the actions list — skip the
2742
- * framework prompt wrapper, resource loading (AGENTS.md/LEARNINGS.md/
2743
- * memory), the SQL schema block, and the workspace files/skills/agents
2744
- * inventory. Intended for minimal or voice-first apps where a long,
2745
- * generic preamble adds latency and iteration noise without adding value.
2746
- *
2747
- * When set, the same lean prompt is used in both dev and prod modes. In
2748
- * dev mode the tool registry is ALSO swapped to the template's actions
2749
- * (same set as prod) — the dev-only bash/db-exec/file-system tools
2750
- * and the resource/docs/chat/team/job/browser scripts are dropped. The
2751
- * lean system prompt has no bash-usage guidance, so routing actions
2752
- * through bash would break. If you need the full dev tool surface,
2753
- * leave this off.
2754
- */
2755
- leanPrompt?: boolean;
2756
- /**
2757
- * Skip auto-injecting the workspace files/skills/agents inventory on the
2758
- * first message of a conversation while keeping the normal prompt, resources,
2759
- * and tool surface. Use this for domain-focused apps where broad workspace
2760
- * inventory is mostly latency/noise unless the user explicitly references it.
2761
- *
2762
- * `leanPrompt: true` implies this.
2763
- */
2764
- skipFilesContext?: boolean;
2765
- /**
2766
- * Initial native tool schemas to send to the LLM provider. When set, the
2767
- * agent starts with only these tools plus `tool-search`; the live registry
2768
- * remains searchable, and matching schemas from `tool-search` results are
2769
- * loaded into the next model request. Use this for domain-focused apps that
2770
- * have a few common actions and many rare framework utilities. Common
2771
- * provider/corpus/code-execution tools are promoted automatically when present
2772
- * in the current app/mode registry, so broad integration guidance stays
2773
- * immediately callable without overloading apps that do not expose those
2774
- * tools.
2775
- */
2776
- initialToolNames?: string[];
2777
- /**
2778
- * Use a compact system prompt with on-demand context loading. The system
2779
- * prompt includes essential behavioral rules and action signatures, but
2780
- * defers verbose framework details, SQL schema, skills, learnings, and
2781
- * memory behind tools (`get-framework-context`, `db-schema`,
2782
- * `resources` (action: read)). The agent fetches these on-demand when needed.
2783
- *
2784
- * This reduces the system prompt by ~60-70%, significantly improving
2785
- * time-to-first-token and reducing "thinking" time. The agent retains
2786
- * all capabilities — it just loads context lazily instead of upfront.
2787
- *
2788
- * Defaults to `true`. Set to `false` to use the original full prompt.
2789
- * Ignored when `leanPrompt` is set (lean mode is even more minimal).
2790
- */
2791
- lazyContext?: boolean;
2792
- /**
2793
- * In dev mode, register the template's actions as native tools the agent
2794
- * can call directly with structured JSON args — skipping the default
2795
- * `bash(command="pnpm action <name> ...")` indirection.
2796
- *
2797
- * The default dev behavior shells out because it "mirrors how Claude Code
2798
- * works locally" and reduces empty-object tool calls for templates with
2799
- * simple string args. But templates whose actions take structured data
2800
- * (objects, arrays, nested JSON) can't round-trip those cleanly through
2801
- * the CLI parser — stringified JSON on the way in, loss of type fidelity
2802
- * on the way out.
2803
- *
2804
- * Set to `true` to get the same tool surface in dev that production uses.
2805
- * `leanPrompt: true` implies this already (lean mode has no bash-usage
2806
- * guidance, so actions must be native). Set this flag without
2807
- * `leanPrompt` when you want native actions AND the full system prompt.
2808
- *
2809
- * Defaults to `false`.
2810
- */
2811
- nativeActionsInDev?: boolean;
2812
- /**
2813
- * Expose raw SQL/native database tools to the app agent.
2814
- *
2815
- * Defaults to `"read"`: `db-schema`/`db-query` are available for inspection,
2816
- * while writes route through typed app actions. Set to `"write"` (also
2817
- * `true`) to expose `db-exec`/`db-patch` for scoped raw SQL maintenance.
2818
- * Set to `"off"` (also `false`) for chat-first apps that want agents to use
2819
- * typed actions only.
2820
- */
2821
- databaseTools?: DatabaseToolsOption;
2822
- /**
2823
- * Expose framework extension management actions (`create-extension`,
2824
- * `update-extension`, `list-extensions`, etc.) to the app agent. Defaults to
2825
- * true. Set to false for apps that do not want the LLM to create or manage
2826
- * sandboxed extension mini-apps, even though the core extension routes may
2827
- * still be mounted for other surfaces.
2828
- */
2829
- extensionTools?: boolean;
2830
- /**
2831
- * Optional A2A-only deterministic response path. Runs after inbound A2A text
2832
- * and user context are resolved, but before an agent engine/model is loaded.
2833
- * Return a message to complete the A2A task without invoking the LLM, or
2834
- * null/undefined to continue through the normal agent loop.
2835
- */
2836
- a2aMessageFallback?: (details: {
2837
- message: import("../a2a/types.js").Message;
2838
- text: string;
2839
- context: import("../a2a/types.js").A2AHandlerContext;
2840
- userEmail: string | undefined;
2841
- }) =>
2842
- | import("../a2a/types.js").Message
2843
- | string
2844
- | null
2845
- | undefined
2846
- | Promise<import("../a2a/types.js").Message | string | null | undefined>;
2847
- /**
2848
- * Optional injectable prompt examples for core rules (rule 5 auto-refresh
2849
- * examples and rule 8 external provider names). When absent, generic
2850
- * placeholders are used so no template-specific names appear in the core
2851
- * prompt by default.
2852
- *
2853
- * - `providerActions`: external provider action names this template exposes
2854
- * (e.g. `["warehouse-query", "crm-records"]` for a template).
2855
- * - `appActions`: representative template action names for rule 5's refresh
2856
- * examples (e.g. `["log-meal", "update-form"]` for a forms template).
2857
- */
2858
- promptExamples?: PromptExamples;
2859
- /**
2860
- * Curated allow-list of action names to serve external **connector** clients
2861
- * on a hosted multi-tenant deployment.
2862
- *
2863
- * Whenever this list is non-empty it is active by default for **every**
2864
- * caller (hosted connectors, code/stdio clients, and the local CLI): external
2865
- * MCP clients see (and can call) only these actions plus the builtin
2866
- * cross-app tools (`list_apps`, `open_app`, `ask_app`, `create_embed_session`).
2867
- * Calls to any tool outside the list are rejected with "Unknown tool".
2868
- * This prevents the full ~105-tool catalog from bloating external-agent
2869
- * context windows and removes footguns (db-exec, seed-*, extension suite,
2870
- * browser-session tools) from connectors. It is no longer gated behind an
2871
- * environment variable, and the catalog is never inferred from the client.
2872
- *
2873
- * `tool-search` stays available so any trimmed tool is reachable on demand.
2874
- * Callers who need the full surface up front opt in explicitly with
2875
- * `agent-native connect --full-catalog` (embeds a `catalog_scope: "full"`
2876
- * claim in their connect-minted JWT) or the deployment-wide
2877
- * `AGENT_NATIVE_MCP_FULL_CATALOG=1` env override.
2878
- *
2879
- * Declare here rather than in MCPConfig directly; the plugin copies it through.
2880
- */
2881
- connectorCatalog?: string[];
2882
-
2883
- /**
2884
- * Skip mounting the remote MCP protocol route.
2885
- *
2886
- * Most apps should leave this off so agent chat, A2A, and MCP share one
2887
- * runtime. Hosted apps with a dedicated early MCP plugin can set this to
2888
- * true so their external connector does not depend on the heavier chat
2889
- * plugin initialization path.
2890
- */
2891
- disableMcp?: boolean;
2892
-
2893
- /**
2894
- * Code-execution capability for the production agent.
2895
- *
2896
- * - `"off"` (default) — no code-execution tools in production.
2897
- * - `"sandboxed"` — registers the `run-code` tool (isolated Node.js sandbox
2898
- * with a bridge to allowlisted registered tools). Safe for shared or
2899
- * hosted deployments.
2900
- * - `"trusted"` — registers both the full coding tool registry
2901
- * (bash / read / edit / write) and the `run-code` sandbox. Only use in
2902
- * single-tenant or operator-controlled deployments where full shell access
2903
- * to the host machine is intentional.
2904
- *
2905
- * The `AGENT_PROD_CODE_EXECUTION` environment variable (`"trusted"`,
2906
- * `"sandboxed"`, or `"off"`) takes precedence over this option, allowing
2907
- * per-deployment overrides without code changes.
2908
- *
2909
- * Dev-mode behavior is unchanged — both the coding tools and `run-code` are
2910
- * always available when the environment allows toggling.
2911
- */
2912
- codeExecution?: {
2913
- production?: "off" | "sandboxed" | "trusted";
2914
- /**
2915
- * Extra registered-tool names the sandbox bridge may forward (beyond the
2916
- * default allowlist: provider-api-request, provider-api-docs,
2917
- * provider-api-catalog, web-request).
2918
- */
2919
- bridgeTools?: string[];
2920
- };
2921
-
2922
- /**
2923
- * App-level default tool-call limits. Individual actions override these with
2924
- * their own `timeoutMs` / `maxResultChars` declarations.
2925
- */
2926
- toolLimits?: { timeoutMs?: number; maxResultChars?: number };
2927
- }
2928
-
2929
- type A2AAgentLoopRunner = typeof runAgentLoopDirectWithSoftTimeout;
2930
-
2931
- /**
2932
- * Run an A2A-delegated agent turn with the same final-response guard used by
2933
- * the app's interactive chat surface.
2934
- *
2935
- * Keeping this in one helper prevents delegated calls from silently bypassing
2936
- * template guarantees such as Analytics' requirement to query real data
2937
- * before presenting metrics or exhaustive provider conclusions.
2938
- */
2939
- export function runA2AAgentLoop(
2940
- runOptions: Parameters<A2AAgentLoopRunner>[0],
2941
- pluginOptions: Pick<
2942
- AgentChatPluginOptions,
2943
- "finalResponseGuard" | "runSoftTimeoutMs"
2944
- >,
2945
- timeoutOptions: Parameters<A2AAgentLoopRunner>[2],
2946
- runner: A2AAgentLoopRunner = runAgentLoopDirectWithSoftTimeout,
2947
- ) {
2948
- return runner(
2949
- {
2950
- ...runOptions,
2951
- finalResponseGuard: pluginOptions.finalResponseGuard,
2952
- },
2953
- pluginOptions.runSoftTimeoutMs,
2954
- timeoutOptions,
2955
- );
2956
- }
2957
-
2958
- /**
2959
- * Keep delegated A2A turns on the same compact initial tool surface as
2960
- * interactive chat. `tool-search` remains in the initial set, while the
2961
- * complete registry is supplied separately so the run loop can load a matched
2962
- * schema after a tool-search result.
2963
- */
2964
- export function createA2AEngineToolSurface(
2965
- availableTools: EngineTool[],
2966
- initialToolNames?: string[],
2967
- ): { tools: EngineTool[]; availableTools: EngineTool[] } {
2968
- return {
2969
- tools: filterInitialEngineTools(availableTools, initialToolNames),
2970
- availableTools,
2971
- };
2972
- }
2973
-
2974
- /**
2975
- * Verbose framework sections returned by the `get-framework-context` tool.
2976
- * Keyed by topic so the agent can request specific sections.
2977
- * Not template-specific — lives outside buildFrameworkPrompts().
2978
- */
2979
- const FRAMEWORK_CONTEXT_SECTIONS: Record<string, string> = {
2980
- embeds: `### Inline Embeds
2981
-
2982
- You can embed an interactive view inline in your chat reply by writing an \`embed\` fenced code block. The chat renderer swaps the fence for a sandboxed iframe pointing at a route inside this app.
2983
-
2984
- Syntax:
2985
-
2986
- \`\`\`\`
2987
- \`\`\`embed
2988
- src: /some/path?param=value
2989
- aspect: 16/9
2990
- title: Optional label
2991
- \`\`\`
2992
- \`\`\`\`
2993
-
2994
- Keys:
2995
- - \`src\` (required) — **must be a same-origin path starting with \`/\`**. Cross-origin URLs are blocked. No \`javascript:\` or \`data:\` URLs.
2996
- - \`aspect\` (optional) — one of \`16/9\` (default), \`4/3\`, \`3/2\`, \`2/1\`, \`21/9\`, \`1/1\`.
2997
- - \`title\` (optional) — accessible label / hover tooltip.
2998
- - \`height\` (optional) — fixed pixel height when aspect ratio isn't a good fit.
2999
-
3000
- Use for charts, visualizations, previews. Don't use for simple text/tables or external sites.`,
3001
-
3002
- "chat-history": `### Chat History
3003
-
3004
- You can search and restore previous chat conversations using \`chat-history\`:
3005
- - \`chat-history\` (action: "search") — Search or list past chat threads by keyword. Archived threads are excluded by default; pass \`includeArchived: true\` to also see them.
3006
- - \`chat-history\` (action: "open") — Open a chat thread in the UI as a new tab and focus it
3007
- - \`chat-history\` (actions: "rename", "pin", "unpin", "archive") — Organize a known chat thread by ID. Archiving a thread hides it from the default chat list and search.
3008
-
3009
- When the user asks to find a previous conversation, use \`chat-history\` with action "search" first to find matching threads, then action "open" to restore the one they want.`,
3010
-
3011
- "agent-teams": `### Agent Teams — Orchestration
3012
-
3013
- You can delegate to background sub-agents with the \`agent-teams\` tool:
3014
- - \`agent-teams\` (action: "spawn") — Launch a sub-agent on a task. It runs in its own thread with a clean context while you stay available; a live preview card appears in the chat. The spawn result confirms launch only, not completion. Optionally pass a custom agent profile from \`agents/*.md\` via the \`agent\` parameter.
3015
- - \`agent-teams\` (action: "status") — Check a running sub-agent's progress.
3016
- - \`agent-teams\` (action: "read-result") — Read a finished sub-agent's output.
3017
- - \`agent-teams\` (action: "send") — Message a running sub-agent.
3018
- - \`agent-teams\` (action: "list") — List sub-agent tasks.
3019
-
3020
- Sub-agents inherit all of your template tools but **cannot spawn sub-agents themselves** — only you orchestrate.
3021
-
3022
- **User intent phrases.** If the user asks you to use a "sub-agent" or "background agent", that is explicit delegation intent. Also treat phrases like "run these in the background", "kick off the rest", "run the queued items", "batch run these jobs", "parallelize this", or "start the next batch" as delegation intent when the context is a set of independent work items.
3023
-
3024
- **Spawn is not completion.** A successful \`spawn\` call means the sub-agent started and is running. Tell the user it started, show the task id if useful, and then use \`status\`/\`list\`/\`read-result\` to monitor it. Never say the delegated task "completed", "ran successfully", or "finished" until \`status\` or \`read-result\` reports \`completed\` or \`errored\`. If a task is still running, say that plainly.
3025
-
3026
- **Default to doing the work yourself in this thread.** A sub-agent costs real tokens and adds a merge step, so reach for one only when delegation clearly pays for that overhead.
3027
-
3028
- **Delegate ONE sub-agent** when a task is self-contained and heavy: deep research, long multi-step content generation, or a noisy scan whose intermediate steps would clutter this thread. The sub-agent gets its own clean context and hands you back a distilled result.
3029
-
3030
- **Fan out to MULTIPLE sub-agents ONLY** for units of work that are genuinely independent and don't depend on each other's decisions — e.g. research three unrelated competitors, summarize five separate threads, draft sections that don't reference one another. Each gets a distinct slice.
3031
-
3032
- **Do NOT parallelize tightly-coupled work** — one cohesive artifact, edits that must agree with each other, or anything needing a single consistent voice or style. Parallel sub-agents can't see each other's choices and their outputs will clash. For coupled work, do it yourself, or chain sub-agents one at a time, feeding each result into the next.
3033
-
3034
- **Cap fan-out:** aim for 1, use up to ~3, and go beyond that only for clearly independent bulk work. More sub-agents means more tokens and a harder merge.
3035
-
3036
- **Briefing contract.** A sub-agent starts in a fresh thread and can only see the brief you give it — it cannot see this conversation or ask you to clarify before it runs. Make every brief self-contained:
3037
- 1. **Objective** — what "done" looks like, in a sentence or two.
3038
- 2. **Context** — the specific facts it needs from this conversation: IDs, names, the user's actual goal, constraints, prior decisions. Paste the specifics; don't assume it knows them.
3039
- 3. **Output format** — what to return and how (e.g. "a 3-bullet summary with source links", "the drafted email body only") so the result drops cleanly into your synthesis.
3040
- 4. **Boundaries** — what NOT to do, and for parallel sub-agents, which slice is theirs so they don't overlap.
3041
-
3042
- Put the objective and output format in \`task\`; put longer context in \`instructions\`.
3043
-
3044
- **Synthesis discipline.** After the sub-agents you depend on finish, poll \`status\`/\`list\` until they're complete, then pull each one's output with \`read-result\`. Do NOT paste their outputs back to back. Read all results, reconcile any disagreements, de-duplicate, and write ONE integrated answer in your own voice. If two sub-agents conflict, resolve it or flag the discrepancy explicitly rather than presenting both. When findings came from distinct investigations, briefly note which finding came from where so the user can trust the merge.`,
3045
-
3046
- "recurring-jobs": `### Recurring Jobs
3047
-
3048
- You can create recurring jobs that run on a cron schedule. Jobs are resource files under \`jobs/\`.
3049
-
3050
- - \`manage-jobs\` (action: "create") — Create a new recurring job with a cron schedule and instructions
3051
- - \`manage-jobs\` (action: "list") — List all recurring jobs and their status
3052
- - \`manage-jobs\` (action: "update") — Update a job's schedule, instructions, or toggle enabled/disabled
3053
- - Delete a job with the \`resources\` tool: \`action: "delete"\`, \`path: "jobs/<name>.md"\`
3054
-
3055
- Convert natural language to 5-field cron format:
3056
- - "every morning" / "daily at 9am" → \`0 9 * * *\`
3057
- - "every weekday at 9am" → \`0 9 * * 1-5\`
3058
- - "every hour" → \`0 * * * *\`
3059
- - "every monday at 9am" → \`0 9 * * 1\`
3060
-
3061
- #### Suggesting "Save as automation"
3062
-
3063
- When you finish a task that has obvious recurring value — daily inbox triage, weekly metrics summaries, archive sweeps, status digests, anything the user would plausibly want re-run on a fresh cadence — close the response with ONE short line offering to save it. Examples:
3064
-
3065
- - After "Summarize my unread emails": _"Want me to run this every morning?"_
3066
- - After "What's our top traffic source this week": _"Want a weekly digest on Mondays?"_
3067
- - After "Archive emails older than 30 days": _"Should I run this every Sunday?"_
3068
-
3069
- If the user says yes, call \`manage-jobs\` (action: "create") with the original prompt as the job's instructions and the cadence they confirmed.
3070
-
3071
- Do NOT add this offer for one-shot work: lookups (find Alice, what's the schema, who reported X), single drafts/replies, navigation requests, or any task whose value is in the moment. Skip it when the prompt is already explicitly recurring (the user said "every morning…" — you'd be asking what they already told you). One short sentence at most; do not turn it into a list of cadence options.`,
3072
-
3073
- builder: `### Connecting Builder.io
3074
-
3075
- When the user asks to connect Builder.io or you hit a "Builder not configured" error, call the \`connect-builder\` tool. It renders a Connect/code-change card inline — do NOT write out multi-step setup instructions yourself. Inspect the returned \`builderEnabled\` flag: \`true\` means Builder Cloud Agents can take the code-change handoff, while \`false\` means this requires a code change and the user should edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way they like. If Builder Cloud Agents are not available for this workspace, never send the user to Builder org settings or beta settings.`,
3076
-
3077
- browser: `### Browser Automation
3078
-
3079
- You can activate a real Chrome browser via Builder.io for tasks that need full page rendering:
3080
- - Extracting design tokens from JS-heavy or SPA websites (computed styles, rendered colors/fonts)
3081
- - Taking screenshots of live pages
3082
- - Testing interactive flows on deployed URLs
3083
- - Reading content from pages that require JavaScript execution
3084
-
3085
- **How to use:**
3086
- 1. In local development, call \`set-browser-control\` with \`{"enabled":true,"backend":"chrome-devtools"}\` after confirming once with the user. In production, use \`activate-browser\` for Builder-provisioned Chrome.
3087
- 2. On your next action, use \`mcp__chrome-devtools__navigate_page\`, \`mcp__chrome-devtools__evaluate_script\`, \`mcp__chrome-devtools__take_screenshot\`, etc.
3088
- 3. If Builder is not connected, call \`connect-builder\` first
3089
-
3090
- **When to recommend browser automation:**
3091
- - User wants to import a design system from a URL (JS-rendered sites give almost no useful data from plain HTML fetch)
3092
- - User asks you to check how a deployed site looks or behaves
3093
- - Any task involving reading computed/rendered page state
3094
- - When \`web-request\` returns minimal/skeleton HTML from a modern SPA
3095
-
3096
- Prefer \`web-request\` for simple API calls and static pages. Use browser automation when you need the real rendered page.`,
3097
-
3098
- "call-agent": `### call-agent — External Apps Only
3099
-
3100
- The \`call-agent\` tool sends a message to a DIFFERENT, separately-deployed app's agent (A2A protocol). It is **not** for calling actions within the current app.
3101
-
3102
- **NEVER use \`call-agent\` to:**
3103
- - Call your own app by name
3104
- - Perform tasks you can accomplish with your own registered tools
3105
-
3106
- **ONLY use \`call-agent\` when:**
3107
- - The user explicitly asks you to communicate with a different app
3108
- - You need data that only another deployed app can provide
3109
- - You need brand-consistent generated media and this app does not have a native generation action; call agent "assets" and keep returned asset IDs and URLs verbatim
3110
-
3111
- If \`call-agent\` says a downstream agent accepted the subtask and will post its result separately, do not call that same agent again for the same subtask. Continue any remaining work and answer with the completed results you have.`,
3112
-
3113
- memory: `### Structured Memory
3114
-
3115
- Your memory index (\`memory/MEMORY.md\`) is loaded at the start of every conversation.
3116
-
3117
- **Tools:**
3118
- - \`save-memory\` — Create or update a memory (name, type, description, content)
3119
- - \`delete-memory\` — Remove a memory and its index entry
3120
- - \`resources\` with \`action: "read"\` and \`path: "memory/<name>.md"\` — Read the full content of a specific memory
3121
-
3122
- **Memory types:** user, feedback, project, reference
3123
-
3124
- **When to save (proactively):**
3125
- - User corrects your approach → \`feedback\`
3126
- - User shares preferences → \`user\`
3127
- - Non-obvious pattern or gotcha → \`feedback\`
3128
- - Personal context (contacts, team) → \`user\`
3129
- - Project context to track → \`project\`
3130
-
3131
- **Rules:**
3132
- - Don't save things obvious from code or standard framework behavior
3133
- - When updating, read first and merge — don't overwrite
3134
- - Keep descriptions concise
3135
- - One memory per logical topic`,
3136
-
3137
- "sql-tools": `### SQL Tools
3138
-
3139
- When database tools are enabled, \`db-schema\` refreshes the schema and \`db-query\` runs read-only SELECT queries with current user/org scoping. Raw SQL write tools are only available when the app explicitly opts into database write tools; by default, writes go through typed app actions. Some apps configure database tools as read-only or off; only use tools that are actually present in your tool list.
3140
-
3141
- - \`db-schema\` — refresh the full schema with indexes and foreign keys
3142
- - \`db-query\` — run a SELECT (read-only; results already filtered to the current user/org)
3143
- - \`db-exec\` — only when present: run INSERT / UPDATE / DELETE / REPLACE for scoped maintenance. For normal product data writes, use a typed app action instead.
3144
- - \`db-patch\` — only when present: surgical search-and-replace on a large text column. If a typed app action exists for the resource, use that action.
3145
-
3146
- ### When to pick which SQL tool
3147
- - A template-specific action exists for the table → use that action (it encodes business rules and pushes live Yjs updates)
3148
- - Read data → \`db-query\`. Never re-add \`WHERE owner_email = ...\` — scoping already applies it.
3149
- - Raw write tools are present and no app action exists → use \`db-exec\` or \`db-patch\` only for deliberate maintenance, not normal product workflows.
3150
-
3151
- ### External data sources vs the app database
3152
- The \`db-*\` tools ONLY query the app's own SQL database. They do NOT reach external data warehouses. If the user asks about tables NOT in the schema, use the appropriate template action instead.`,
3153
- };
3154
-
3155
- /**
3156
- * Framework-level instructions injected into every agent's system prompt.
3157
- * Prompt text lives in packages/core/src/server/prompts/ so this file stays
3158
- * focused on routing and assembly logic.
3159
- *
3160
- * buildFrameworkPrompts() is called once per plugin instantiation (not per
3161
- * request) with the template's promptExamples, producing the four assembled
3162
- * prompt strings used at request time.
3163
- */
3164
- function buildFrameworkPrompts(
3165
- examples?: PromptExamples,
3166
- options?: { databaseTools?: DatabaseToolsOption; extensionTools?: boolean },
3167
- ): {
3168
- FRAMEWORK_CORE: string;
3169
- FRAMEWORK_CORE_COMPACT: string;
3170
- PROD_FRAMEWORK_PROMPT: string;
3171
- DEV_FRAMEWORK_PROMPT: string;
3172
- PROD_FRAMEWORK_PROMPT_COMPACT: string;
3173
- DEV_FRAMEWORK_PROMPT_COMPACT: string;
3174
- } {
3175
- // Note: FIRST_SESSION_PERSONALIZATION is NOT appended here — it is injected
3176
- // at per-request prompt-assembly time only for new threads (no prior messages).
3177
- // This prevents the ~1.5KB block from appearing on every request forever.
3178
- const FRAMEWORK_CORE = buildFrameworkCore(examples, options);
3179
- const FRAMEWORK_CORE_COMPACT = buildFrameworkCoreCompact(examples, options);
3180
- const extensionToolsEnabled = options?.extensionTools !== false;
3181
- const planModeArtifactList = extensionToolsEnabled
3182
- ? "source-code handoffs and app-created artifacts such as extensions, widgets, dashboards, calculators, mini-apps, documents, designs, slides, or videos"
3183
- : "source-code handoffs and app-created artifacts such as documents, designs, slides, or videos";
3184
- const planModeBlockedTools = extensionToolsEnabled
3185
- ? "`render-inline-extension`, `create-extension`, `update-extension`, `connect-builder`, or any action that creates, updates, deletes, sends, publishes, or persists data"
3186
- : "`connect-builder`, or any action that creates, updates, deletes, sends, publishes, or persists data";
3187
- const extensionConnectBuilderGuard = extensionToolsEnabled
3188
- ? "If the complete request can be satisfied by a self-contained extension or an existing named slot, use `render-inline-extension`, `create-extension`, `show-extension-inline`, or `update-extension` instead. If the exact placement or behavior requires changing the host UI or no suitable slot exists, continue with the normal `connect-builder` source-change flow even if the user called it an extension; never stop at saying extensions cannot do it."
3189
- : "Because extension tools are disabled, do NOT invent an extension workflow. Only use `connect-builder` when the request genuinely requires changing the host app's source code.";
3190
- const extensionInstructionsFull = extensionToolsEnabled
3191
- ? `### Generative UI and Extensions (Mini-Apps)
3192
-
3193
- In Act mode, if the user asks for generated interactive UI in chat, choose the smallest extension action that matches the lifetime:
3194
-
3195
- - For a **one-time inline UI** that answers the current chat turn (knobs, controls, pickers, calculators, temporary dashboards, visualizers), call \`render-inline-extension\` immediately with a self-contained Alpine.js HTML body. It renders inside the transcript and is not saved.
3196
- - For a **reusable or saved UI** (an extension/widget/dashboard/calculator/mini-app the user can reopen from Extensions), call \`create-extension\` with a self-contained Alpine.js HTML body. It saves to the Extensions view and also renders inline in chat.
3197
- - To **reuse an existing saved extension inline**, call \`show-extension-inline\` with its id, or a search string when the id is unknown.
3198
-
3199
- These are **NOT** source-code changes and do **NOT** go through \`connect-builder\`. Extensions are sandboxed mini-apps — no source files are touched, no PR is opened, no build is required. Saved extensions can be edited later via \`update-extension\`.
3200
-
3201
- If the app exposes native actions or instructions for dashboards, reports, analyses, charts, documents, decks, or other domain artifacts, use those app-native actions first. Choose an extension only when the user explicitly asks for an extension/custom mini-app, or when the app's native artifact format cannot faithfully express the requested interaction.
3202
-
3203
- Keep \`create-extension\` payloads compact enough to finish quickly. For complex extensions, create a useful working v1 first, then call \`update-extension\` with focused edits for refinements instead of trying to assemble one enormous initial tool input.
3204
-
3205
- Generated UI content can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send(...)/sendToAgentChat(...). Use appAction() for app data writes, and dbQuery() only for read-only inspection of known app SQL tables. It can receive chat inputs through slotContext/window.onSlotContext. Use agentNative.ui.output for passive current values from knobs, sliders, selections, and controls; it writes application state at \`inline-ui:<extensionId>:output\` scoped to the inline extension id returned by \`render-inline-extension\` or \`show-extension-inline\`. When the user later says "use that value", "apply the current setting", or similar, read it with \`readAppState("inline-ui:<id>:output")\` instead of asking them to send it again. Use agentNative.chat.send for visible submit/apply actions that should put a message into chat. Transient extensionData is browser-local and not agent-readable, synced, promoted, or garbage-collected; use application_state/appFetch, appAction, ui.output, or chat.send for anything the agent or app must observe. Use semantic Tailwind classes like bg-background, text-foreground, bg-primary, border-border, and text-muted-foreground so the UI inherits the parent app theme.
3206
-
3207
- If the user asks to change, edit, fix, style, rename, or add behavior to an existing extension/widget/dashboard/calculator/mini-app, use the current extension id from \`<current-screen>\` or \`<current-url>\` when present. Call \`get-extension\` only if you need to inspect its content, then \`update-extension\` with that id. After one content read, keep the body in working memory and move to focused \`update-extension\` \`edits\`/\`patches\`; do not loop on repeated \`get-extension\` + \`run-code\` string scans before writing. Use \`list-extensions\` only when no current id/name is available. Existing extension edits are SQL data updates, not source-code changes, even when the request says "change the UI" or "fix this". Do **NOT** call \`connect-builder\` for existing extension edits.
3208
-
3209
- In Act mode, when in doubt — if the request asks for a new small interactive utility and does not need reuse, choose \`render-inline-extension\`; if it mentions saving/reuse or asks for an extension/widget/dashboard/calculator/mini-app, choose \`create-extension\`. If it references an existing one or the current extension page, choose \`update-extension\`. Do **not** preface the call with planning text like "let me build the dashboard…" — just call the right extension action directly.
3210
-
3211
- Note: "extension" is the user-facing primitive (the sandboxed Alpine.js mini-app). Don't confuse it with the LLM concept of "tools" (function calls) — those are how you invoke ANY action, including \`create-extension\` itself.
3212
-
3213
- For existing extensions, use \`get-extension\` or \`update-extension\` directly when \`<current-screen>\` or \`<current-url>\` provides an \`extensionId\`. Use \`list-extensions\` only to browse or resolve an unknown name. If the user wants a shared extension removed only from their view, use \`hide-extension\` — do not query or mutate the legacy \`tools\` table directly.
3214
-
3215
- ### Extensions vs. Code Changes — Pick the Right Path
3216
-
3217
- Route by the exact outcome, not by whether the user calls it an extension. Extensions render in their own sandboxed iframe, either on their own page or inside an existing named slot. They CANNOT change the host app's nav, restyle or inject elements into existing native components, replace built-in views, or render at an arbitrary location that has no slot.
3218
-
3219
- <routing>
3220
- | The request is for… | Path |
3221
- | ---------------------------------------------------------------- | ----------------------------- |
3222
- | A one-off interactive answer inside chat (controls, picker, calculator, temporary visualizer) | \`render-inline-extension\` — inline only |
3223
- | A new self-contained surface (widget, dashboard, calculator, viewer, list, tracker) | \`create-extension\` — ships instantly, no PR |
3224
- | Loading a saved extension inside chat | \`show-extension-inline\` |
3225
- | Editing an existing extension (fix, restyle, rename, add behavior) | \`update-extension\` |
3226
- | The host app's own chrome (nav bar, sidebar, layout, routes, shipped components, existing styles, business logic) | \`connect-builder\` — a real source-code change |
3227
- | UI inside or beside a native component where no named slot exists | \`connect-builder\` — add the native UI or a suitable slot in source |
3228
- | Ambiguous, satisfiable either way (e.g. "give me an unread view") | \`render-inline-extension\` for chat-only, \`create-extension\` for reusable |
3229
- </routing>
3230
-
3231
- If an extension could only approximate the request in a different location, do not silently downgrade the requirement and do not end with "extensions cannot do that." Briefly explain the boundary, then follow the normal source-code handoff so the app can still be customized fully.
3232
-
3233
- Worked examples: "a widget showing unread emails grouped by sender", "a tracker for my newsletter subscriptions", "a custom kanban board with drag-and-drop rules the app does not have" → \`create-extension\`. "Add an Unread tab to the left navigation", "show local time beside every native Calendar attendee row", "make the subject lines wrap", "change the inbox grouping logic", "add a field to the compose form" → \`connect-builder\`.`
3234
- : `### Extensions Disabled
3235
-
3236
- Extension creation and management tools are disabled for this app. Do not claim you can create, edit, hide, or delete Agent-Native extensions unless the template exposes its own typed action for that workflow. For requests that would otherwise be handled as an extension/widget/dashboard/calculator mini-app, explain that this app has disabled extension tools and use the app's available actions instead.`;
3237
- const extensionInstructionsCompact = extensionToolsEnabled
3238
- ? `### Generative UI and Extensions (Mini-Apps)
3239
-
3240
- In Act mode, if the user asks for generated interactive UI in chat, call \`render-inline-extension\` for one-time inline controls/knobs/calculators/visualizers that do not need saving. If the user asks for an **extension**, **widget**, **dashboard**, **calculator**, or **mini-app** that should be reusable or saved, call \`create-extension\` with a self-contained Alpine.js HTML body. To load a saved extension inline, call \`show-extension-inline\`. These are NOT code changes — extensions are sandboxed mini-apps. Do not preface with "let me build…" — just call the right extension action.
3241
-
3242
- Use app-native artifact actions first when they exist for dashboards, reports, analyses, charts, documents, decks, or similar domain artifacts. Pick \`create-extension\` only for explicit extension/custom mini-app requests or for behavior the native artifact format cannot support.
3243
-
3244
- Keep the first \`create-extension\` call compact and working. If the request is complex, create the v1 first and then refine with focused \`update-extension\` edits.
3245
-
3246
- Generated UI can read chat inputs from slotContext/window.onSlotContext, see/update app state through appFetch/appAction, use extensionData, record passive current values through agentNative.ui.output(value, opts?), and send visible results through agentNative.chat.send(...) or sendToAgentChat(...). ui.output writes \`inline-ui:<extensionId>:output\` in application state; when the user asks to use the current slider/selection/value, read \`readAppState("inline-ui:<id>:output")\`. Transient extensionData is browser-local only, so do not rely on it for values the agent or app must observe. Use semantic Tailwind theme classes.
3247
-
3248
- If the user asks to change, edit, fix, style, rename, or add behavior to an existing extension/widget/dashboard/calculator/mini-app, use the current extension id from \`<current-screen>\` or \`<current-url>\` when present. Call \`get-extension\` only if you need to inspect its content, then \`update-extension\` with that id. After one content read, use focused \`update-extension\` \`edits\`/\`patches\`; do not repeatedly re-read and scan the same HTML with \`run-code\` before writing. Use \`list-extensions\` only when no current id/name is available. Existing extension edits are SQL data updates, not source-code changes. Do NOT call \`connect-builder\` for them.
3249
-
3250
- For existing extensions, use \`get-extension\` or \`update-extension\` directly when \`<current-screen>\` or \`<current-url>\` provides an \`extensionId\`. Use \`list-extensions\` only to browse or resolve an unknown name. Use \`hide-extension\` when the user wants a shared extension removed only from their own view. Do not query the legacy \`tools\` table directly.
3251
-
3252
- ### Extensions vs. Code Changes — Pick the Right Path
3253
-
3254
- If the user wants a **one-off interactive answer in chat**, use \`render-inline-extension\`. If they want a **new reusable self-contained surface** (custom widget, dashboard, list, viewer, calculator), use \`create-extension\` — extensions ship instantly without a PR. Extensions can render only on their own page or in an existing named slot; they cannot inject UI into arbitrary native components. If the exact request changes host chrome, native components, layout, styles, routes, business logic, or needs placement where no slot exists, treat it as a source-code change and use the normal \`connect-builder\` flow even if the user called it an extension. Never stop at "extensions cannot do that" or silently offer a different placement; explain the boundary briefly and continue the code-change handoff.`
3255
- : `### Extensions Disabled
3256
-
3257
- Extension creation and management tools are disabled for this app. Do not claim you can create, edit, hide, or delete Agent-Native extensions unless the template exposes its own typed action for that workflow.`;
3258
-
3259
- const PROD_FRAMEWORK_PROMPT = `## Agent-Native Framework — Production Mode
3260
-
3261
- You are an AI agent in an agent-native application, running in **production mode**.
3262
-
3263
- The agent and the UI are equal partners — everything the UI can do, you can do via your tools, and vice versa. They share the same SQL database and stay in sync automatically.
3264
-
3265
- **In production mode, you operate through registered actions exposed as tools.** These are your capabilities — use them to read data, take actions, and help the user. You cannot edit source code or access the filesystem directly. Your tools are the app's API.
3266
-
3267
- ### Plan Mode
3268
-
3269
- If the current turn is in Plan mode, plan before anything gets written. This applies to ${planModeArtifactList}. Use only read-only tools, clarify the goal when needed, and return a concrete plan for approval. Do not call ${planModeBlockedTools} until the user switches back to Act mode.
3270
-
3271
- ${extensionInstructionsFull}
3272
-
3273
- ### Code Changes Not Available — Call \`connect-builder\` Immediately
3274
-
3275
- ${extensionConnectBuilderGuard}
3276
-
3277
- In Act mode, when the user asks you to change the UI, modify code, add a feature, fix a bug in the app itself, change styles, add a hook, create a component, add a route, add an integration, or anything else that requires editing source files — you MUST take exactly these steps, in order:
3278
-
3279
- 1. Briefly acknowledge the user's specific request in their own terms — one short clause naming what they asked for (e.g. "Got it — wider subject lines in the email list."). Do NOT restate the request verbatim, do NOT add a generic preamble, and do NOT promise outcomes. Skip this step entirely if the user already knows you're handing off (e.g. they said "send this to Builder").
3280
- 2. Call the \`connect-builder\` tool, passing the user's full request verbatim as the \`prompt\` argument. This renders an inline card. The returned \`builderEnabled\` flag is authoritative: \`true\` means Builder Cloud Agents can take the code-change handoff; \`false\` means this still requires a code change, but no Builder Cloud Agent can run from this workspace. If Builder is connected and \`builderEnabled\` is true, the card hands the prompt off to Builder's cloud agent on one click and returns a branch URL. If Builder is not connected and \`builderEnabled\` is true, it shows the Connect Builder flow. If \`builderEnabled\` is false, it still shows the card, but as a code-change fallback instead of a runnable cloud handoff.
3281
- 3. After the card renders, inspect the tool result and write one sentence that frames the next click around what the user just asked — not as a Builder pitch. If \`builderEnabled\` is true and the card can send, say something like "Click Send to Builder and it'll wire that up for you." If the user needs to connect first, say something like "Connect Builder once and it'll handle this and future changes." If \`builderEnabled\` is false, say: "This requires a code change. Edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way you like." Do NOT say "click Send to Builder", mention a branch URL, say the card is pre-loaded, or imply a Builder Cloud Agent can run when \`builderEnabled\` is false.
3282
-
3283
- When \`builderEnabled\` is false, Builder Cloud Agents are not self-serve from Builder org settings. Do NOT tell the user to go to Builder org settings, beta features, "Cloud Agents", or "AI Agents" to enable them. The only allowed guidance is the card's code-change fallback: edit locally, use Agent Native Desktop, or use Builder.io to edit this code in the cloud and continue customizing the app any way you like.
3284
-
3285
- **Act-mode handoff — do exactly this:** The moment a code change is requested, call \`connect-builder\` with the user's verbatim request, then write one sentence framing the next click. That is the whole job: Builder plans and edits in its own sandbox, so the planning, file-mapping, and code all happen there, not here. Keep your turn to the call plus that one sentence — skip code exploration, implementation plans, \`resources\` writes of specs, and sub-agents (they have no code-editing tools either). You don't need filesystem access to recommend Builder, so don't reach for it or list tools you lack.
3286
-
3287
- Builder Cloud Agents are the recommended path when \`builderEnabled\` is true because they are one-click and run in the cloud. Local dev, Agent Native Desktop, and Builder.io cloud editing are the fallback when \`builderEnabled\` is false.
3288
- ${FRAMEWORK_CORE}`;
3289
-
3290
- const DEV_FRAMEWORK_PROMPT = `## Agent-Native Framework — Development Mode
3291
-
3292
- You are an AI agent in an agent-native application, running in **development mode**.
3293
-
3294
- The agent and the UI are equal partners — everything the UI can do, you can do via tools/scripts, and vice versa. They share the same SQL database and stay in sync automatically.
3295
-
3296
- **In development mode, you have full local access — use it with senior-engineer judgment** (read before you edit, keep changes scoped, verify before you claim done):
3297
- - Run any shell command via the \`bash\` tool (node, curl, pnpm, rg, git, etc.), including arbitrary code: \`bash({ command: 'node -e "console.log(1+1)"' })\`
3298
- - Read and write any file on the filesystem; edit source, install packages, modify the app
3299
- - Query and modify the database
3300
- - Call external APIs (via bash with curl, or via scripts)
3301
-
3302
- When no dedicated tool/action exists for what you need, reach for \`bash\` — e.g. \`bash({ command: 'curl -s https://api.example.com/data' })\`.
3303
-
3304
- **Template-specific actions are invoked via bash, NOT as direct tools.** In dev mode, the only tools registered as native tool calls are framework-level utilities (bash, read, edit, write, database, resources, chat, teams, jobs). Anything from the template's \`actions/\` directory must be run through bash: \`bash({ command: 'pnpm action <name> --arg value' })\`. The "Available Actions" section below shows the exact CLI syntax for each one — copy that command verbatim and pass it to \`bash\`. Do not try to call template actions by name as if they were tools; they will not appear in your tool list.
3305
-
3306
- When editing code, follow the agent-native architecture:
3307
- - Every feature needs all four areas: UI + scripts + skills/instructions + application-state sync
3308
- - All SQL must be dialect-agnostic (works on SQLite and Postgres)
3309
- - No Node.js-specific APIs in server routes (must work on Cloudflare Workers, etc.)
3310
- - Use shadcn/ui components and Tabler Icons for all UI work
3311
- ${FRAMEWORK_CORE}`;
3312
-
3313
- const PROD_FRAMEWORK_PROMPT_COMPACT = `## Agent-Native Framework — Production Mode
3314
-
3315
- You are an AI agent in an agent-native application, running in **production mode**.
3316
-
3317
- The agent and the UI are equal partners — everything the UI can do, you can do via your tools, and vice versa. They share the same SQL database and stay in sync automatically.
3318
-
3319
- **In production mode, you operate through registered actions exposed as tools.** These are your capabilities — use them to read data, take actions, and help the user. You cannot edit source code or access the filesystem directly. Your tools are the app's API.
3320
-
3321
- ### Plan Mode
3322
-
3323
- If the turn is in Plan mode, plan before anything gets written — including ${planModeArtifactList}. Use read-only tools only and do not call ${planModeBlockedTools} until the user switches back to Act mode.
3324
-
3325
- ${extensionInstructionsCompact}
3326
-
3327
- ### Code Changes — Call \`connect-builder\`
3328
-
3329
- In Act mode, when a request genuinely needs a source-code edit (per the rules above), do this in order: (1) briefly acknowledge the user's specific ask in their own terms — one short clause, no preamble — then (2) call \`connect-builder\` with the user's request as the \`prompt\`, then (3) inspect \`builderEnabled\` in the result and write one sentence framing the next click around what they asked for, not a Builder pitch. When \`builderEnabled\` is true, Builder Cloud Agents can take the handoff; when \`builderEnabled\` is false, say this requires a code change and they can edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way they like. Do NOT write code, list files, make plans, or spawn sub-agents. Mention \`npx agent-native create <app-name>\`, Agent Native Desktop, or the code-change fallback when Builder Cloud Agents aren't available. Never tell users to enable Builder Cloud Agents from Builder org settings, beta features, "Cloud Agents", or "AI Agents"; they are not self-serve there.
3330
- ${FRAMEWORK_CORE_COMPACT}`;
3331
-
3332
- const DEV_FRAMEWORK_PROMPT_COMPACT = `## Agent-Native Framework — Development Mode
3333
-
3334
- You are an AI agent in an agent-native application, running in **development mode**.
3335
-
3336
- The agent and the UI are equal partners — everything the UI can do, you can do via tools/scripts, and vice versa. They share the same SQL database and stay in sync automatically.
3337
-
3338
- **In development mode, you have full local access** — shell, filesystem, database, external APIs, source edits, and package installs. Use it with senior-engineer judgment: read before you edit, keep changes scoped, verify before you claim done.
3339
-
3340
- **Template-specific actions are invoked via bash, NOT as direct tools.** Run them with: \`bash({ command: 'pnpm action <name> --arg value' })\`. See the "Available Actions" section below for CLI syntax.
3341
-
3342
- When editing code, follow the agent-native architecture:
3343
- - Every feature needs all four areas: UI + scripts + skills/instructions + application-state sync
3344
- - All SQL must be dialect-agnostic (works on SQLite and Postgres)
3345
- - No Node.js-specific APIs in server routes (must work on Cloudflare Workers, etc.)
3346
- - Use shadcn/ui components and Tabler Icons for all UI work
3347
- ${FRAMEWORK_CORE_COMPACT}`;
3348
-
3349
- return {
3350
- FRAMEWORK_CORE,
3351
- FRAMEWORK_CORE_COMPACT,
3352
- PROD_FRAMEWORK_PROMPT,
3353
- DEV_FRAMEWORK_PROMPT,
3354
- PROD_FRAMEWORK_PROMPT_COMPACT,
3355
- DEV_FRAMEWORK_PROMPT_COMPACT,
3356
- };
3357
- }
3358
-
3359
- export const _agentChatPromptSectionsForTests = (() => {
3360
- // Built with default (no template-specific) examples for test stability.
3361
- const {
3362
- FRAMEWORK_CORE: frameworkCore,
3363
- FRAMEWORK_CORE_COMPACT: frameworkCoreCompact,
3364
- } = buildFrameworkPrompts();
3365
- return {
3366
- frameworkCore,
3367
- frameworkCoreCompact,
3368
- frameworkContextSections: FRAMEWORK_CONTEXT_SECTIONS,
3369
- buildFrameworkPrompts,
3370
- generateActionsPrompt,
3371
- createDataWidgetActionEntries,
3372
- };
3373
- })();
3374
-
3375
- /**
3376
- * Pre-load the agent's context: AGENTS.md (workspace/template/runtime
3377
- * instructions), the skills index, shared LEARNINGS.md (team notes), a shared
3378
- * resource index, and memory/MEMORY.md (personal structured memory index).
3379
- * These all get appended to the system prompt so the agent has everything it
3380
- * needs from the first turn.
3381
- *
3382
- * Six sources are layered:
3383
- *
3384
- * 1. `<workspace>` — AGENTS.md from the enterprise workspace core.
3385
- * 2. `<template>` — AGENTS.md + skills index from the Vite plugin bundle.
3386
- * 3. `<workspace>` — SQL workspace AGENTS.md and instructions/*.md.
3387
- * Runtime global defaults managed from Dispatch and inherited by apps.
3388
- * 4. `<app-default>` — legacy app-wide SQL defaults.
3389
- * 5. `<shared>` — organization AGENTS.md, instructions, and LEARNINGS.md.
3390
- * These are isolated by active org and override app/workspace defaults.
3391
- * 6. `<personal>` — memory/MEMORY.md from the SQL personal scope. The
3392
- * current user's structured memory index.
3393
- *
3394
- * Each source is read independently — no copying between them. Editing
3395
- * AGENTS.md and restarting the server is all it takes; Vite HMR invalidates
3396
- * the bundle in dev so changes land instantly.
3397
- */
3398
- export async function loadResourcesForPrompt(
3399
- owner: string,
3400
- compact = false,
3401
- selfAppId?: string,
3402
- orgId: string | null = getRequestOrgId() ?? null,
3403
- ): Promise<string> {
3404
- await ensurePersonalDefaults(owner);
3405
-
3406
- const sections: string[] = [];
3407
- const promptResourceMaxChars = compact
3408
- ? COMPACT_PROMPT_RESOURCE_MAX_CHARS
3409
- : SHARED_PROMPT_RESOURCE_MAX_CHARS;
3410
-
3411
- // 1. Workspace AGENTS.md + skills merged into the template bundle.
3412
- try {
3413
- const { loadAgentsBundle, generateSkillsPromptBlock, getRuntimeSkills } =
3414
- await import("./agents-bundle.js");
3415
- const bundle = await loadAgentsBundle();
3416
-
3417
- // Workspace-core AGENTS.md (enterprise-wide instructions), if present.
3418
- if (bundle.workspaceAgentsMd && bundle.workspaceAgentsMd.trim()) {
3419
- const block = promptResourceBlock({
3420
- name: "AGENTS.md",
3421
- scope: "workspace",
3422
- path: "AGENTS.md",
3423
- content: bundle.workspaceAgentsMd,
3424
- maxChars: promptResourceMaxChars,
3425
- readHint:
3426
- 'Use docs-search --slug "agents-workspace" to read the full workspace AGENTS.md.',
3427
- });
3428
- if (block) sections.push(block);
3429
- }
3430
-
3431
- // 2. Template AGENTS.md — always included (critical template instructions).
3432
- if (bundle.agentsMd.trim()) {
3433
- const block = promptResourceBlock({
3434
- name: "AGENTS.md",
3435
- scope: "template",
3436
- path: "AGENTS.md",
3437
- content: bundle.agentsMd,
3438
- maxChars: promptResourceMaxChars,
3439
- readHint:
3440
- 'Use docs-search --slug "agents-template" to read the full template AGENTS.md.',
3441
- });
3442
- if (block) sections.push(block);
3443
- }
3444
-
3445
- // In compact mode, skip the full skills block — the agent can use
3446
- // `docs-search` to find skills when it needs them. Either way, `scope: dev`
3447
- // skills are excluded: they're for the human's coding agent, not runtime.
3448
- const runtimeSkills = getRuntimeSkills(bundle);
3449
- if (!compact) {
3450
- const skillsBlock = generateSkillsPromptBlock(bundle);
3451
- if (skillsBlock) sections.push(skillsBlock);
3452
- } else if (runtimeSkills.length > 0) {
3453
- const lines = runtimeSkills.map((s) => {
3454
- const description = s.meta.description?.trim()
3455
- ? ` - ${ensureSentence(s.meta.description)}`
3456
- : "";
3457
- return `- \`${s.meta.name}\`${description} Read with \`docs-search --slug "${skillDocsSlug(s.meta.name)}"\` before starting a task it applies to.`;
3458
- });
3459
- sections.push(
3460
- `<skills-summary>\nCodebase skills bundled from \`.agents/skills/\` (or legacy \`.agent/skills/\`) are available as docs-search pages. Do not use MCP resource reads for these skills.\n\n${lines.join("\n")}\n</skills-summary>`,
3461
- );
3462
- }
3463
- } catch {}
3464
-
3465
- // 3. Runtime workspace resources. These are global defaults inherited by
3466
- // every app in the workspace, not copied into app scopes. They may come from
3467
- // SQL, Dispatch, or local file mode.
3468
- const workspaceAgents = await loadAgentsResourceForPrompt(
3469
- WORKSPACE_OWNER,
3470
- "workspace",
3471
- promptResourceMaxChars,
3472
- );
3473
- if (workspaceAgents) sections.push(workspaceAgents);
3474
- sections.push(
3475
- ...(await loadInstructionResourcesForPrompt(
3476
- WORKSPACE_OWNER,
3477
- "workspace-instruction",
3478
- promptResourceMaxChars,
3479
- )),
3480
- );
3481
-
3482
- const organizationOwner = sharedResourceOwner(orgId);
3483
-
3484
- // 4. Legacy app-wide defaults. Existing deployments keep their seeded
3485
- // shared guidance as an inherited fallback; organization writes never
3486
- // mutate this owner.
3487
- const appDefaultAgents = await loadAgentsResourceForPrompt(
3488
- SHARED_OWNER,
3489
- organizationOwner === SHARED_OWNER ? "shared" : "app-default",
3490
- promptResourceMaxChars,
3491
- );
3492
- if (appDefaultAgents) sections.push(appDefaultAgents);
3493
- sections.push(
3494
- ...(await loadInstructionResourcesForPrompt(
3495
- SHARED_OWNER,
3496
- organizationOwner === SHARED_OWNER
3497
- ? "shared-instruction"
3498
- : "app-default-instruction",
3499
- promptResourceMaxChars,
3500
- )),
3501
- );
3502
-
3503
- // 5. Active organization resources. These are the durable team rules and
3504
- // learnings that Slack/integration runs share with interactive app chat.
3505
- if (organizationOwner !== SHARED_OWNER) {
3506
- const organizationAgents = await loadAgentsResourceForPrompt(
3507
- organizationOwner,
3508
- "shared",
3509
- promptResourceMaxChars,
3510
- );
3511
- if (organizationAgents) sections.push(organizationAgents);
3512
- sections.push(
3513
- ...(await loadInstructionResourcesForPrompt(
3514
- organizationOwner,
3515
- "shared-instruction",
3516
- promptResourceMaxChars,
3517
- )),
3518
- );
3519
- }
3520
-
3521
- // 6. Personal SQL resources. These come last in the instruction stack so a
3522
- // user can narrow or override organization/app and workspace defaults.
3523
- if (owner !== SHARED_OWNER && owner !== WORKSPACE_OWNER) {
3524
- const personalAgents = await loadAgentsResourceForPrompt(
3525
- owner,
3526
- "personal",
3527
- promptResourceMaxChars,
3528
- );
3529
- if (personalAgents) sections.push(personalAgents);
3530
- sections.push(
3531
- ...(await loadInstructionResourcesForPrompt(
3532
- owner,
3533
- "personal-instruction",
3534
- promptResourceMaxChars,
3535
- )),
3536
- );
3537
- }
3538
-
3539
- const resourceSkillsBlock = await loadResourceSkillsPromptBlock(owner, orgId);
3540
- if (resourceSkillsBlock) sections.push(resourceSkillsBlock);
3541
-
3542
- let sharedLearnings: Awaited<ReturnType<typeof resourceGetByPath>> = null;
3543
- try {
3544
- sharedLearnings =
3545
- (organizationOwner !== SHARED_OWNER
3546
- ? await resourceGetByPath(organizationOwner, "LEARNINGS.md")
3547
- : null) ?? (await resourceGetByPath(SHARED_OWNER, "LEARNINGS.md"));
3548
- } catch {}
3549
-
3550
- if (compact) {
3551
- // Integration/Slack turns use compact context, but organization learnings
3552
- // are operational routing input, not optional background. Preload the
3553
- // bounded shared file so canonical destinations/fields are available on
3554
- // the first turn; keep personal memory on-demand to preserve the compact
3555
- // budget.
3556
- if (sharedLearnings?.content?.trim()) {
3557
- const block = promptResourceBlock({
3558
- name: "LEARNINGS.md",
3559
- scope: "shared",
3560
- path: "LEARNINGS.md",
3561
- content: sharedLearnings.content,
3562
- maxChars: COMPACT_PROMPT_RESOURCE_MAX_CHARS,
3563
- });
3564
- if (block) sections.push(block);
3565
- }
3566
- sections.push(
3567
- `<context-note>Organization learnings above and your personal memory (memory/MEMORY.md) are available via the \`resources\` tool. Save durable team facts and routing conventions to shared LEARNINGS.md; keep personal preferences in save-memory.</context-note>`,
3568
- );
3569
- } else {
3570
- // LEARNINGS.md from SQL (template-level instructions are in AGENTS.md above).
3571
- if (sharedLearnings?.content?.trim()) {
3572
- sections.push(
3573
- `<resource name="LEARNINGS.md" scope="shared">\n${sharedLearnings.content.trim()}\n</resource>`,
3574
- );
3575
- }
3576
-
3577
- // 3. Personal memory index (skip if owner is the shared sentinel)
3578
- if (owner !== SHARED_OWNER) {
3579
- try {
3580
- const memoryIndex = await resourceGetByPath(owner, "memory/MEMORY.md");
3581
- if (memoryIndex?.content?.trim()) {
3582
- sections.push(
3583
- `<resource name="memory/MEMORY.md" scope="personal">\n${memoryIndex.content.trim()}\n</resource>`,
3584
- );
3585
- }
3586
- } catch {}
3587
- }
3588
- }
3589
-
3590
- const workspaceResourceIndex = await loadResourceIndexForPrompt(
3591
- WORKSPACE_OWNER,
3592
- "workspace",
3593
- );
3594
- if (workspaceResourceIndex) sections.push(workspaceResourceIndex);
3595
-
3596
- const appDefaultResourceIndex = await loadResourceIndexForPrompt(
3597
- SHARED_OWNER,
3598
- "shared",
3599
- );
3600
- if (appDefaultResourceIndex) sections.push(appDefaultResourceIndex);
3601
- if (organizationOwner !== SHARED_OWNER) {
3602
- const organizationResourceIndex = await loadResourceIndexForPrompt(
3603
- organizationOwner,
3604
- "shared",
3605
- );
3606
- if (organizationResourceIndex) sections.push(organizationResourceIndex);
3607
- }
3608
-
3609
- try {
3610
- const agents = (await discoverAgents(selfAppId)).slice(0, 30);
3611
- if (agents.length > 0) {
3612
- const lines = agents.map(
3613
- (agent) =>
3614
- `- ${agent.name} (${agent.id}) — ${agent.description || "Connected A2A app"}`,
3615
- );
3616
- sections.push(
3617
- `<available-apps>\nWorkspace apps available over A2A/call-agent:\n${lines.join("\n")}\n\nUse \`call-agent\` with the app id when another app owns the work or data. Use tool-search or app-specific actions for details only when needed.\n</available-apps>`,
3618
- );
3619
- }
3620
- } catch {
3621
- // Agent discovery is helpful context, not required for the run.
3622
- }
3623
-
3624
- if (sections.length === 0) return "";
3625
- return (
3626
- "\n\nThe following resources contain template-specific instructions and user context. Use the information in them to help the user.\n\n" +
3627
- sections.join("\n\n")
3628
- );
3629
- }
3630
-
3631
- /**
3632
- * Build the per-request SQL-schema context block. Reads AGENT_ORG_ID live
3633
- * from the environment so scheduler/A2A/HTTP call sites all see whatever
3634
- * org was just resolved for this request.
3635
- */
3636
- async function buildSchemaBlock(
3637
- owner: string,
3638
- databaseTools: DatabaseToolsOption = "read",
3639
- ): Promise<string> {
3640
- try {
3641
- return await loadSchemaPromptBlock({
3642
- owner,
3643
- orgId: getRequestOrgId() ?? null,
3644
- databaseTools,
3645
- });
3646
- } catch {
3647
- return "";
3648
- }
3649
- }
3650
-
3651
- /**
3652
- * Generates a system prompt section describing registered template actions.
3653
- * This helps the agent prefer template-specific actions over raw db-query/db-exec.
3654
- *
3655
- * Two output modes:
3656
- *
3657
- * - `"tool"` — used in production, where template actions are registered
3658
- * as native Anthropic tools. Output reads `name(arg*: type; ...) — desc`.
3659
- * - `"cli"` — used in dev, where template actions are NOT registered as
3660
- * native tools and must be invoked via `bash(command="pnpm action ...")`.
3661
- * Output reads `pnpm action name --arg <type> [--opt <type>] — desc`.
3662
- */
3663
- function generateActionsPrompt(
3664
- registry: Record<string, ActionEntry>,
3665
- mode: "cli" | "tool" = "tool",
3666
- ): string {
3667
- if (!registry || Object.keys(registry).length === 0) return "";
3668
-
3669
- const actionEntries = Object.entries(registry);
3670
- const nativeWidgetNote = (entry: ActionEntry) =>
3671
- entry.chatUI && typeof entry.chatUI.renderer === "string"
3672
- ? ` Native chat widget: \`${entry.chatUI.renderer}\`.`
3673
- : "";
3674
-
3675
- if (mode === "tool") {
3676
- const summaryLines = actionEntries.map(([name, entry]) => {
3677
- const desc = compactPromptLine(
3678
- entry.tool.description,
3679
- MAX_ACTION_SUMMARY_DESCRIPTION_CHARS,
3680
- );
3681
- return `- \`${name}\` — ${desc}${nativeWidgetNote(entry)}`;
3682
- });
3683
-
3684
- return `\n\n## Available Actions
73
+ extractThreadMeta,
74
+ foldAssistantTurn,
75
+ mergeThreadDataForClientSave,
76
+ upsertUserMessage,
77
+ } from "../agent/thread-data-builder.js";
78
+ import { attachToolSearch } from "../agent/tool-search.js";
79
+ import type {
80
+ AgentChatAttachment,
81
+ AgentChatEvent,
82
+ MentionProvider,
83
+ } from "../agent/types.js";
84
+ import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
85
+ import { appStateGet } from "../application-state/store.js";
86
+ import {
87
+ createThread,
88
+ forkThread,
89
+ getThread,
90
+ registerChatThreadsShareable,
91
+ resolveThreadAccess,
92
+ listThreads,
93
+ searchThreads,
94
+ renameThread,
95
+ createThreadShareLink,
96
+ getThreadByShareToken,
97
+ getThreadShareState,
98
+ revokeThreadShareLink,
99
+ setThreadArchived,
100
+ setThreadPinned,
101
+ setThreadScope,
102
+ updateThreadData,
103
+ withThreadDataLock,
104
+ deleteThread,
105
+ setThreadQueuedMessages,
106
+ type ChatThreadScope,
107
+ type ForkThreadSourceSnapshot,
108
+ } from "../chat-threads/store.js";
109
+ import { createDbAdminAgentTools } from "../db-admin/agent-tools.js";
110
+ import {
111
+ verifyInternalToken,
112
+ extractBearerToken,
113
+ } from "../integrations/internal-token.js";
114
+ import {
115
+ McpClientManager,
116
+ loadMcpConfig,
117
+ autoDetectMcpConfig,
118
+ mcpToolsToActionEntries,
119
+ syncMcpActionEntries,
120
+ mountMcpServersRoutes,
121
+ mountMcpHubRoutes,
122
+ buildMergedConfig,
123
+ startMcpConfigRefresh,
124
+ getHubStatus,
125
+ isHubServeEnabled,
126
+ } from "../mcp-client/index.js";
127
+ import { setProgressPreListHook } from "../progress/store.js";
128
+ import { getSkillNameFromPath } from "../resources/metadata.js";
129
+ import {
130
+ resourceList,
131
+ resourceListAccessible,
132
+ resourceGet,
133
+ ensurePersonalDefaults,
134
+ SHARED_OWNER,
135
+ WORKSPACE_OWNER,
136
+ } from "../resources/store.js";
137
+ import { normalizeDatabaseToolsMode } from "../scripts/db/tool-mode.js";
138
+ import { getSetting, putSetting } from "../settings/store.js";
139
+ import {
140
+ handleSharedThreadRequest,
141
+ type SharedThreadRouteDependencies,
142
+ } from "./agent-chat/shared-thread.js";
143
+ import { discoverAgents } from "./agent-discovery.js";
144
+ import {
145
+ resolveAgentRunOwnerContext,
146
+ runWithAgentRunContext,
147
+ seedBackgroundAgentRunOwnerContext,
148
+ type AgentRunOwnerContext,
149
+ } from "./agent-run-context.js";
150
+ import {
151
+ AGENT_TEAM_PROCESS_RUN_PATH,
152
+ getCurrentDelegationDepth,
153
+ processAgentTeamRun,
154
+ reconcileAgentTeamRunsForOwner,
155
+ } from "./agent-teams.js";
156
+ import { getSession } from "./auth.js";
157
+ import { captureError } from "./capture-error.js";
158
+ import {
159
+ getH3App,
160
+ markDefaultPluginProvided,
161
+ trackPluginInit,
162
+ } from "./framework-request-handler.js";
163
+ import { getOrigin } from "./google-oauth.js";
164
+ import { readBody } from "./h3-helpers.js";
165
+ import {
166
+ FIRST_SESSION_PERSONALIZATION,
167
+ getModelFamilyOverlay,
168
+ } from "./prompts/index.js";
169
+ import { mountRealtimeVoiceRoutes } from "./realtime-voice.js";
170
+ import {
171
+ runWithRequestContext,
172
+ getRequestOrgId,
173
+ getRequestUserEmail,
174
+ getRequestRunContext,
175
+ ensureRequestRunContext,
176
+ } from "./request-context.js";
3685
177
 
3686
- **Use these actions directly as tool calls.** They handle database access, validation, and business logic internally. The native tool schemas contain the full parameter details.
178
+ export { handleSharedThreadRequest };
179
+ export type { SharedThreadRouteDependencies };
3687
180
 
3688
- ${summaryLines.join("\n")}`;
181
+ // Lazy fs — loaded via dynamic import() on first use.
182
+ // This avoids require() which bundlers convert to createRequire(import.meta.url)
183
+ // that crashes on CF Workers where import.meta.url is undefined.
184
+ let _fs: typeof import("fs") | undefined;
185
+ async function lazyFs(): Promise<typeof import("fs")> {
186
+ if (!_fs) {
187
+ _fs = await import("node:fs");
3689
188
  }
3690
-
3691
- const lines = actionEntries.map(([name, entry]) => {
3692
- const desc = entry.tool.description;
3693
- const params = entry.tool.parameters?.properties;
3694
- const requiredFields = new Set(entry.tool.parameters?.required ?? []);
3695
-
3696
- // CLI mode: emit `pnpm action <name> --required <type> [--optional <type>]`
3697
- if (!params || Object.keys(params).length === 0) {
3698
- return `- \`pnpm action ${name}\` — ${desc}${nativeWidgetNote(entry)}`;
3699
- }
3700
- const entries = Object.entries(params);
3701
- // Required first (alphabetical), then optional (alphabetical)
3702
- entries.sort(([a], [b]) => {
3703
- const ar = requiredFields.has(a) ? 0 : 1;
3704
- const br = requiredFields.has(b) ? 0 : 1;
3705
- if (ar !== br) return ar - br;
3706
- return a.localeCompare(b);
3707
- });
3708
- const required: string[] = [];
3709
- const optional: string[] = [];
3710
- const requiredNames: string[] = [];
3711
- for (const [k, v] of entries) {
3712
- const type = (v as { type?: string }).type ?? "any";
3713
- const flag = `--${k} <${type}>`;
3714
- if (requiredFields.has(k)) {
3715
- required.push(flag);
3716
- requiredNames.push(`--${k}`);
3717
- } else {
3718
- optional.push(`[${flag}]`);
3719
- }
3720
- }
3721
- const cmd = ["pnpm action " + name, ...required, ...optional].join(" ");
3722
- const requiredNote =
3723
- requiredNames.length > 0 ? ` Required: ${requiredNames.join(", ")}.` : "";
3724
- return `- \`${cmd}\` — ${desc}.${requiredNote}${nativeWidgetNote(entry)}`;
3725
- });
3726
-
3727
- return `\n\n## Available Actions
3728
-
3729
- **These template actions are NOT exposed as direct tools in dev mode. To run any of them, use the \`bash\` tool with the exact command shown below.** Example: \`bash(command="pnpm action add-slide --deckId abc --content 'Hello'")\`.
3730
-
3731
- Do NOT try to call these by name as if they were tools — they will not exist in your tool list. Always go through \`bash\`.
3732
-
3733
- ${lines.join("\n")}`;
189
+ return _fs;
3734
190
  }
3735
191
 
3736
- function generateCorpusToolsPrompt(
3737
- registry: Record<string, ActionEntry>,
3738
- ): string {
3739
- const hasProviderApi = "provider-api-request" in registry;
3740
- const hasProviderCorpusJob = "provider-corpus-job" in registry;
3741
- const providerDiscoveryTools = [
3742
- "provider-api-catalog" in registry ? "`provider-api-catalog`" : null,
3743
- "provider-api-docs" in registry ? "`provider-api-docs`" : null,
3744
- ].filter(Boolean);
3745
- const hasRunCode = "run-code" in registry;
3746
- const hasStagedDataset = "query-staged-dataset" in registry;
3747
- if (
3748
- !hasProviderApi &&
3749
- !hasProviderCorpusJob &&
3750
- !hasRunCode &&
3751
- !hasStagedDataset
3752
- )
3753
- return "";
3754
-
3755
- const available = [
3756
- ...providerDiscoveryTools,
3757
- hasProviderApi ? "`provider-api-request`" : null,
3758
- hasProviderCorpusJob ? "`provider-corpus-job`" : null,
3759
- hasStagedDataset ? "`query-staged-dataset`" : null,
3760
- hasRunCode ? "`run-code`" : null,
3761
- ].filter(Boolean);
3762
-
3763
- return `\n\n## Broad Provider And Corpus Workflows
3764
-
3765
- Available corpus-capable tools: ${available.join(", ")}.
3766
-
3767
- For broad provider searches, raw API access, multi-page cohorts, cross-source joins, classification/counting over records, or absence-sensitive answers, do not stop at a bounded shortcut action. Use the provider's broad API/search/list surface, fetch every relevant page or an explicit bounded cohort, stage/save large responses when needed, and reduce the corpus with durable corpus jobs, staged-dataset queries, or code execution.
3768
-
3769
- When \`provider-corpus-job\` is available, prefer it for transcript/message/ticket/issue/document scans that may exceed one turn, need provider-side backoff, or need a defensible "not found" conclusion. Use operation="start" with mode="paginated-search" for any paginated provider endpoint, or mode="batch-search" when a prior cohort of ids/records must feed a second provider endpoint. Continue paused jobs with operation="continue" until status is completed or quota_wait, then read operation="results". In run-code, prefer providerFetchAll() for short cursor/page/offset pagination and providerRequest() when response status, headers, or truncation metadata matters. Report source, filters, row counts, pagination/truncation, failed pages, quota_wait times, and remaining gaps.`;
3770
- }
192
+ // ---------------------------------------------------------------------------
193
+ // The bulk of this file's former implementation now lives in focused sibling
194
+ // modules under `./agent-chat/`. This file re-imports them (and re-exports
195
+ // the ones that were already part of the public surface) so
196
+ // `createAgentChatPlugin` below stays a thinner orchestrator.
197
+ // ---------------------------------------------------------------------------
198
+ import {
199
+ createA2AEngineToolSurface,
200
+ filterAgentTools,
201
+ filterPublicAgentActions,
202
+ filterReadOnlyActions,
203
+ resolveInitialToolNames,
204
+ runA2AAgentLoop,
205
+ assembleA2AFinalResponse,
206
+ buildPublicAgentA2ASkills,
207
+ resolveArtifactBaseUrl,
208
+ } from "./agent-chat/action-filters-a2a.js";
209
+ import {
210
+ createBuilderBrowserTool,
211
+ createTeamTools,
212
+ } from "./agent-chat/browser-team-tools.js";
213
+ import {
214
+ createDataWidgetActionEntries,
215
+ createFrameworkContextEntry,
216
+ createRefreshScreenEntry,
217
+ createUrlTools,
218
+ } from "./agent-chat/context-tools.js";
219
+ import {
220
+ _agentChatPromptSectionsForTests,
221
+ buildFrameworkPrompts,
222
+ buildSchemaBlock,
223
+ collectFiles,
224
+ corpusToolNamesTaughtByPrompt,
225
+ generateActionsPrompt,
226
+ generateCorpusToolsPrompt,
227
+ } from "./agent-chat/framework-prompts.js";
228
+ import {
229
+ type AgentChatPluginOptions,
230
+ type NitroPluginDef,
231
+ } from "./agent-chat/plugin-options.js";
232
+ import { finalizeClaimedAgentChatProcessRunFailure } from "./agent-chat/process-run-failure.js";
233
+ import {
234
+ loadResourcesForPrompt,
235
+ resourceScopeForOwner,
236
+ } from "./agent-chat/prompt-resources.js";
237
+ import { shouldDisableRecurringJobsRuntime } from "./agent-chat/recurring-jobs-runtime.js";
238
+ import {
239
+ isLocalhost,
240
+ shouldBlockInProductCodeEditingSurface,
241
+ } from "./agent-chat/request-surface.js";
242
+ import { loadRunCodeToolEntries } from "./agent-chat/run-code-tools.js";
243
+ import {
244
+ createAgentEngineScriptEntries,
245
+ createAgentLoopSettingsScriptEntries,
246
+ createCallAgentScriptEntry,
247
+ createChatScriptEntries,
248
+ createDbScriptEntries,
249
+ createDocsScriptEntries,
250
+ createResourceScriptEntries,
251
+ } from "./agent-chat/script-entries.js";
252
+ import { parseSkillFrontmatter } from "./agent-chat/skill-frontmatter.js";
253
+
254
+ export { loadResourcesForPrompt };
255
+ export { _agentChatPromptSectionsForTests };
256
+ export { buildPublicAgentA2ASkills };
257
+ export { assembleA2AFinalResponse };
258
+ export type { AgentChatPluginOptions };
259
+ export { runA2AAgentLoop };
260
+ export { createA2AEngineToolSurface };
261
+ export { shouldBlockInProductCodeEditingSurface };
262
+ export { loadRunCodeToolEntries };
263
+ export { shouldDisableRecurringJobsRuntime };
264
+ export { finalizeClaimedAgentChatProcessRunFailure };
3771
265
 
3772
266
  /**
3773
- * Creates a Nitro plugin that mounts the agent chat endpoint.
3774
- *
3775
- * In dev mode (NODE_ENV !== "production"), automatically includes
3776
- * file system, bash, and database tools alongside any template-specific actions.
267
+ * Returns whether `owner` has already finished (or explicitly skipped) the
268
+ * First-Session Personalization flow, via the owner-scoped
269
+ * `application_state` "personalization" flag the agent itself writes
270
+ * (`writeAppState("personalization", { done: true })` see
271
+ * FIRST_SESSION_PERSONALIZATION in prompts/framework-core.ts).
3777
272
  *
3778
- * Usage in templates:
3779
- * ```ts
3780
- * // server/plugins/agent-chat.ts
3781
- * import { readBody, createAgentChatPlugin } from "@agent-native/core/server";
3782
- * import { scriptRegistry } from "../../scripts/registry.js";
3783
- *
3784
- * export default createAgentChatPlugin({
3785
- * scripts: scriptRegistry,
3786
- * systemPrompt: "You are an email assistant...",
3787
- * });
3788
- * ```
273
+ * This used to be gated on "does this thread have prior messages", which
274
+ * flips false the instant a second request comes in for the SAME thread —
275
+ * making turn 2's system prompt diverge from turn 1's and invalidating the
276
+ * prompt-cache prefix on every thread's second request. The flow itself
277
+ * spans two turns (turn 1 asks the personalization questions and waits;
278
+ * turn 2 answers them and only then writes the "done" flag), so this flag
279
+ * is still false when BOTH turns' system prompts are assembled — turn 1
280
+ * and turn 2 come out byte-identical. It only flips once the flow
281
+ * completes, and it never flips back, so every later turn (and every
282
+ * later thread the same owner creates) stays consistent from then on. As
283
+ * a bonus, it also fixes a latent waste: the old gate re-included the
284
+ * ~1.5KB block on turn 1 of every new thread a user ever created, even
285
+ * long after they'd completed personalization once.
3789
286
  */
3790
- async function collectFiles(
3791
- dir: string,
3792
- prefix: string,
3793
- depth: number,
3794
- results: Array<{ path: string; name: string; type: "file" | "folder" }>,
3795
- ): Promise<void> {
3796
- if (depth > 4 || results.length >= 500) return;
3797
- const skip = new Set([
3798
- "node_modules",
3799
- ".git",
3800
- ".next",
3801
- ".output",
3802
- "dist",
3803
- ".cache",
3804
- ".turbo",
3805
- "data",
3806
- ]);
3807
- let entries: import("fs").Dirent[];
3808
- try {
3809
- const fs = await lazyFs();
3810
- entries = fs.readdirSync(dir, { withFileTypes: true });
3811
- } catch {
3812
- return;
3813
- }
3814
- for (const entry of entries) {
3815
- if (results.length >= 500) return;
3816
- if (skip.has(entry.name) || entry.name.startsWith(".")) continue;
3817
- const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
3818
- const isDir = entry.isDirectory();
3819
- results.push({
3820
- path: relPath,
3821
- name: entry.name,
3822
- type: isDir ? "folder" : "file",
3823
- });
3824
- if (isDir)
3825
- await collectFiles(
3826
- nodePath.join(dir, entry.name),
3827
- relPath,
3828
- depth + 1,
3829
- results,
3830
- );
3831
- }
3832
- }
3833
-
3834
- function parseSkillFrontmatter(content: string): {
3835
- name?: string;
3836
- description?: string;
3837
- userInvocable?: boolean;
3838
- scope?: "runtime" | "dev" | "both";
3839
- } {
3840
- const frontmatter = parseFrontmatter(content);
3841
- const userInvocable = getFrontmatterValue(frontmatter, "user-invocable");
3842
- const scope = normalizeSkillFrontmatterScope(
3843
- getFrontmatterValue(frontmatter, "scope"),
3844
- );
3845
- return {
3846
- name: getFrontmatterValue(frontmatter, "name"),
3847
- description: getFrontmatterValue(frontmatter, "description"),
3848
- scope,
3849
- userInvocable:
3850
- userInvocable === undefined
3851
- ? undefined
3852
- : userInvocable.toLowerCase() === "true",
3853
- };
3854
- }
3855
-
3856
- function normalizeSkillFrontmatterScope(
3857
- value: string | undefined,
3858
- ): "runtime" | "dev" | "both" | undefined {
3859
- if (!value) return undefined;
3860
- const normalized = value.trim().toLowerCase();
3861
- if (
3862
- normalized === "runtime" ||
3863
- normalized === "dev" ||
3864
- normalized === "both"
3865
- ) {
3866
- return normalized;
3867
- }
3868
- return "both";
3869
- }
3870
-
3871
- function isLocalhost(event: any): boolean {
287
+ export async function hasCompletedFirstSessionPersonalization(
288
+ owner: string,
289
+ ): Promise<boolean> {
3872
290
  try {
3873
- const host =
3874
- event.node?.req?.headers?.host || event.headers?.get?.("host") || "";
3875
- const hostname = host.split(":")[0];
3876
- return (
3877
- hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1"
3878
- );
291
+ const state = await appStateGet(owner, "personalization");
292
+ return state?.done === true;
3879
293
  } catch {
294
+ // Fail open to "not done" — same default as a brand-new user (block
295
+ // shown) rather than silently skipping personalization because of a
296
+ // transient appstate read error.
3880
297
  return false;
3881
298
  }
3882
299
  }
3883
300
 
3884
- type AgentChatRequestSurface = "app" | "dev-frame" | "desktop";
3885
-
3886
- function normalizeAgentChatRequestSurface(
3887
- value: string | null | undefined,
3888
- ): AgentChatRequestSurface | null {
3889
- const normalized = (value ?? "").trim().toLowerCase();
3890
- if (
3891
- normalized === "app" ||
3892
- normalized === "dev-frame" ||
3893
- normalized === "desktop"
3894
- ) {
3895
- return normalized;
3896
- }
3897
- return null;
3898
- }
3899
-
3900
- function isBrowserUserAgent(userAgent: string | null | undefined): boolean {
3901
- return /Mozilla\/|Chrome\/|Safari\/|Firefox\/|Edg\//i.test(userAgent ?? "");
3902
- }
3903
-
3904
- export function shouldBlockInProductCodeEditingSurface(input: {
3905
- surface?: string | null;
3906
- userAgent?: string | null;
3907
- host?: string | null;
3908
- }): boolean {
3909
- const surface = normalizeAgentChatRequestSurface(input.surface);
3910
- if (surface === "dev-frame") return false;
3911
- if (surface === "desktop") return false;
3912
- if (surface === "app") return true;
3913
-
3914
- // Legacy clients used to send `frame` for any iframe, which includes the
3915
- // app-rendered sidebar inside preview frames. Treat unknown explicit surface
3916
- // values as app-owned so they cannot accidentally receive dev code tools.
3917
- if (input.surface && input.surface.trim()) return true;
3918
-
3919
- const userAgent = input.userAgent ?? "";
3920
- if (/AgentNativeDesktop/i.test(userAgent)) return false;
3921
-
3922
- // Missing header from an older browser client. Be conservative for browser
3923
- // UAs on any host, because preview URLs can be non-local while still running
3924
- // a dev-mode app whose in-product chat would be reloaded by source edits.
3925
- if (isBrowserUserAgent(userAgent)) return true;
3926
-
3927
- const host = (input.host ?? "").toLowerCase();
3928
- const hostname = host.split(":")[0] ?? "";
3929
- return (
3930
- hostname === "localhost" ||
3931
- hostname === "127.0.0.1" ||
3932
- hostname === "::1" ||
3933
- hostname === "[::1]"
3934
- );
3935
- }
3936
-
3937
- /**
3938
- * Load the sandboxed code-execution tool entries for one action registry:
3939
- * `run-code` plus its `get-code-execution` poll companion. The poll tool is
3940
- * registered ALONGSIDE run-code everywhere run-code appears, so the durable
3941
- * background-execution guidance run-code emits ("check it with
3942
- * get-code-execution") always points at a callable tool. Returns an empty
3943
- * registry when the coding module is unavailable (e.g. bundled browser
3944
- * build), mirroring the prior silent-skip behavior.
3945
- *
3946
- * Exported for tests — the plugin init calls this for the prod, lean, and dev
3947
- * tool bags, so a spec on this helper pins the real registration wiring.
3948
- */
3949
- export async function loadRunCodeToolEntries(
3950
- supplier: () => Record<string, ActionEntry>,
3951
- runCodeOptions?: { bridgeTools?: string[] },
3952
- ): Promise<Record<string, ActionEntry>> {
3953
- try {
3954
- const { createRunCodeEntry, createGetCodeExecutionEntry } =
3955
- await import("../coding-tools/run-code.js");
3956
- const entries: Record<string, ActionEntry> = {
3957
- "run-code": createRunCodeEntry(supplier, runCodeOptions),
3958
- "get-code-execution": createGetCodeExecutionEntry(),
3959
- };
3960
-
3961
- // Data programs: stored JS scripts executed through this same run-code
3962
- // sandbox, cached in SQL, and rendered by dashboard panels. Registered
3963
- // identically to run-code — same try/dynamic-import guard, silently
3964
- // skipped when the module is unavailable (e.g. bundled browser build) —
3965
- // so every app gets the primitive without per-template wiring.
3966
- try {
3967
- const { initDataPrograms, createDataProgramActions } =
3968
- await import("../data-programs/index.js");
3969
- const appId = resolveDataProgramsAppId();
3970
- initDataPrograms({ appId, getActions: supplier });
3971
- Object.assign(
3972
- entries,
3973
- createDataProgramActions({ appId, getActions: supplier }),
3974
- );
3975
- } catch {
3976
- // Module unavailable — skip silently, mirroring the run-code guard above.
3977
- }
3978
-
3979
- return entries;
3980
- } catch {
3981
- // Module unavailable (e.g. bundled browser build) — skip silently.
3982
- return {};
3983
- }
3984
- }
3985
-
3986
301
  /**
3987
- * Resolve the stable app identity data programs are scoped under. Mirrors
3988
- * the precedent in `cli/agent.ts` (`AGENT_NATIVE_APP_ID` env override, then
3989
- * `APP_ID`, then a fixed fallback) — data programs don't need a per-call
3990
- * agent-supplied appId the way staged datasets do (staged datasets are
3991
- * scratch space the agent explicitly stages into); a data program is a
3992
- * persisted resource scoped to "this app deployment".
302
+ * In-memory rate-limit tracker for `/generate-title`. Keyed by user email,
303
+ * value is recent invocation timestamps within the rolling window. Stale
304
+ * entries are pruned on read.
3993
305
  */
3994
- function resolveDataProgramsAppId(): string {
3995
- return (
3996
- process.env.AGENT_NATIVE_APP_ID?.trim() ||
3997
- process.env.APP_ID?.trim() ||
3998
- process.env.APP_NAME?.trim() ||
3999
- "app"
4000
- );
4001
- }
4002
-
4003
- type RecurringJobsRuntimeEnvKey =
4004
- | "AGENT_NATIVE_DISABLE_RECURRING_JOBS"
4005
- | "AGENT_NATIVE_ENABLE_LOCAL_RECURRING_JOBS"
4006
- | "APP_URL"
4007
- | "BETTER_AUTH_URL"
4008
- | "DEPLOY_URL"
4009
- | "NODE_ENV"
4010
- | "URL"
4011
- | "VITE_APP_URL"
4012
- | "VITE_WORKSPACE_GATEWAY_URL"
4013
- | "WORKSPACE_GATEWAY_URL";
4014
-
4015
- type RecurringJobsRuntimeEnv = Partial<
4016
- Record<RecurringJobsRuntimeEnvKey, string | undefined>
4017
- >;
4018
-
4019
- function isTruthyEnv(value: string | undefined): boolean {
4020
- return /^(1|true|yes|on)$/i.test(value?.trim() ?? "");
4021
- }
4022
-
4023
- function isLoopbackAppUrl(value: string | undefined): boolean {
4024
- const raw = value?.trim();
4025
- if (!raw) return false;
4026
-
4027
- const candidates = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw)
4028
- ? [raw]
4029
- : [raw, `http://${raw}`];
4030
- for (const candidate of candidates) {
4031
- try {
4032
- const url = new URL(candidate);
4033
- const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
4034
- if (
4035
- host === "localhost" ||
4036
- host === "127.0.0.1" ||
4037
- host === "0.0.0.0" ||
4038
- host === "::1" ||
4039
- host === "tauri.localhost" ||
4040
- host.endsWith(".localhost")
4041
- ) {
4042
- return true;
4043
- }
4044
- } catch {}
4045
- }
4046
-
4047
- return false;
4048
- }
4049
-
4050
- export function shouldDisableRecurringJobsRuntime(
4051
- env: RecurringJobsRuntimeEnv = process.env,
4052
- ): boolean {
4053
- if (isTruthyEnv(env.AGENT_NATIVE_DISABLE_RECURRING_JOBS)) return true;
4054
-
4055
- const isLocalRuntime =
4056
- env.NODE_ENV === "development" ||
4057
- env.NODE_ENV === "test" ||
4058
- [
4059
- env.APP_URL,
4060
- env.BETTER_AUTH_URL,
4061
- env.DEPLOY_URL,
4062
- env.URL,
4063
- env.VITE_APP_URL,
4064
- env.VITE_WORKSPACE_GATEWAY_URL,
4065
- env.WORKSPACE_GATEWAY_URL,
4066
- ].some(isLoopbackAppUrl);
4067
-
4068
- if (
4069
- isLocalRuntime &&
4070
- isTruthyEnv(env.AGENT_NATIVE_ENABLE_LOCAL_RECURRING_JOBS)
4071
- ) {
4072
- return false;
4073
- }
4074
-
4075
- return isLocalRuntime;
4076
- }
4077
-
4078
- type AgentChatProcessRunFailureDeps = {
4079
- readBackgroundRunClaim?: typeof readBackgroundRunClaim;
4080
- recordRunDiagnostic?: typeof recordRunDiagnostic;
4081
- setRunError?: typeof setRunError;
4082
- setRunTerminalReason?: typeof setRunTerminalReason;
4083
- updateRunStatusIfRunning?: typeof updateRunStatusIfRunning;
4084
- ensureTerminalRunEvent?: typeof ensureTerminalRunEvent;
4085
- };
4086
-
4087
- export async function finalizeClaimedAgentChatProcessRunFailure(
4088
- runId: string,
4089
- err: unknown,
4090
- deps: AgentChatProcessRunFailureDeps = {},
4091
- ): Promise<boolean> {
4092
- const readClaim = deps.readBackgroundRunClaim ?? readBackgroundRunClaim;
4093
- const record = deps.recordRunDiagnostic ?? recordRunDiagnostic;
4094
- const setError = deps.setRunError ?? setRunError;
4095
- const setTerminalReason = deps.setRunTerminalReason ?? setRunTerminalReason;
4096
- const updateStatus =
4097
- deps.updateRunStatusIfRunning ?? updateRunStatusIfRunning;
4098
- const ensureTerminal = deps.ensureTerminalRunEvent ?? ensureTerminalRunEvent;
4099
- const message = err instanceof Error ? err.message : String(err);
4100
-
4101
- await record(runId, RUN_DIAG_STAGE.routeThrew, message).catch(() => {});
4102
-
4103
- const claim = await readClaim(runId).catch(() => null);
4104
- if (
4105
- claim?.status !== "running" ||
4106
- claim.dispatchMode !== "background-processing"
4107
- ) {
4108
- return false;
4109
- }
306
+ const generateTitleRateLimit = new Map<string, number[]>();
4110
307
 
4111
- await setError(
4112
- runId,
4113
- CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.errorCode,
4114
- `${CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.details} setupError=${message}`,
4115
- ).catch(() => {});
4116
- const statusUpdated = await updateStatus(runId, "errored").catch(() => false);
4117
- if (statusUpdated) {
4118
- await setTerminalReason(
4119
- runId,
4120
- CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.errorCode,
4121
- ).catch(() => {});
4122
- }
4123
- await ensureTerminal(
4124
- runId,
4125
- CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT,
4126
- ).catch(() => {});
4127
- return true;
4128
- }
308
+ /** Only sweep drained rate-limit entries once the map grows past this size,
309
+ * so the common small-map case stays O(1). */
310
+ const RATE_LIMIT_SWEEP_THRESHOLD = 1000;
4129
311
 
4130
312
  export function createAgentChatPlugin(
4131
313
  options?: AgentChatPluginOptions,
@@ -4317,8 +499,9 @@ export function createAgentChatPlugin(
4317
499
  const refreshScreenTool = createRefreshScreenEntry();
4318
500
  const frameworkContextTool = createFrameworkContextEntry();
4319
501
  const leanPrompt = options?.leanPrompt === true;
4320
- const skipFilesContext = leanPrompt || options?.skipFilesContext === true;
4321
502
  const lazyContext = options?.lazyContext !== false && !leanPrompt;
503
+ const skipFilesContext =
504
+ leanPrompt || (options?.skipFilesContext ?? lazyContext);
4322
505
  const urlTools = createUrlTools();
4323
506
  const engineScripts = await createAgentEngineScriptEntries(
4324
507
  options?.appId,
@@ -4499,6 +682,18 @@ export function createAgentChatPlugin(
4499
682
  // `*All` sets are reserved for `httpActions`, which keeps agent-hidden
4500
683
  // actions reachable from the frontend / HTTP.
4501
684
  const templateScripts = filterAgentTools(templateScriptsAll);
685
+ // Compact is the safe default for every app, including generated and
686
+ // third-party apps that have not curated a starter list yet. Keep the
687
+ // app's own action surface immediately callable; framework, provider,
688
+ // and MCP tools remain discoverable through tool-search.
689
+ //
690
+ // This is the template-only baseline; the final `effectiveInitialToolNames`
691
+ // (below, after the run-code tools are built) additionally folds in
692
+ // whatever `generateCorpusToolsPrompt` teaches by name for this request.
693
+ const templateInitialToolNames = resolveInitialToolNames(
694
+ templateScripts,
695
+ options?.initialToolNames,
696
+ );
4502
697
  const discoveredActions = filterAgentTools(discoveredActionsAll);
4503
698
  // Per-request owner is read from the AsyncLocalStorage run context
4504
699
  // (populated by prepareRun). Module-scope `let` would race across
@@ -4756,6 +951,37 @@ export function createAgentChatPlugin(
4756
951
  })
4757
952
  : {};
4758
953
 
954
+ // Registry `generateCorpusToolsPrompt` (below) reads from to decide what
955
+ // it teaches by name — kept as one value so the prompt text and the
956
+ // initial-tool-set expansion just below can never drift apart.
957
+ const corpusPromptRegistry = {
958
+ ...templateScripts,
959
+ ...(canToggle
960
+ ? devRunCodeTool
961
+ : resolvedProdCodeExec !== "off"
962
+ ? runCodeTool
963
+ : {}),
964
+ };
965
+ // `generateCorpusToolsPrompt` teaches provider-api-request /
966
+ // provider-corpus-job / query-staged-dataset / run-code BY NAME
967
+ // whenever they're registered, regardless of whether they made the
968
+ // curated initial-tool-names list — a prior comment here (since
969
+ // removed) warned this exact mismatch "caused prod runs to waste
970
+ // turns": the model reads about a tool in the system prompt, tries to
971
+ // call it immediately, and finds it missing from the very first
972
+ // engine request until it calls tool-search. Fold in exactly the tool
973
+ // names the corpus prompt would announce for THIS registry so the two
974
+ // always agree. `corpusToolNamesTaughtByPrompt` returns [] for apps
975
+ // that never emit the corpus prompt (no provider/run-code tools
976
+ // registered), so this never silently expands the initial set for
977
+ // apps that don't teach these tools by name.
978
+ const corpusToolNames =
979
+ corpusToolNamesTaughtByPrompt(corpusPromptRegistry);
980
+ const effectiveInitialToolNames =
981
+ corpusToolNames.length > 0
982
+ ? [...new Set([...templateInitialToolNames, ...corpusToolNames])]
983
+ : templateInitialToolNames;
984
+
4759
985
  const resolveExtraContext = async (
4760
986
  event: any,
4761
987
  owner: string,
@@ -5110,7 +1336,7 @@ export function createAgentChatPlugin(
5110
1336
 
5111
1337
  const a2aToolSurface = createA2AEngineToolSurface(
5112
1338
  actionsToEngineTools(a2aActions),
5113
- options?.initialToolNames,
1339
+ effectiveInitialToolNames,
5114
1340
  );
5115
1341
 
5116
1342
  // Precise current time rides the user message (not the cached
@@ -5227,16 +1453,13 @@ export function createAgentChatPlugin(
5227
1453
  // Dev: actions are invoked via bash — emit `pnpm action name --arg <type>`
5228
1454
  // and include discoveredActions too, since those are also missing
5229
1455
  // from the dev tool registry.
5230
- const corpusToolsPrompt = generateCorpusToolsPrompt({
5231
- ...templateScripts,
5232
- ...(canToggle
5233
- ? devRunCodeTool
5234
- : resolvedProdCodeExec !== "off"
5235
- ? runCodeTool
5236
- : {}),
5237
- });
1456
+ const corpusToolsPrompt = generateCorpusToolsPrompt(corpusPromptRegistry);
5238
1457
  const prodActionsPrompt =
5239
- generateActionsPrompt(templateScripts, "tool") + corpusToolsPrompt;
1458
+ generateActionsPrompt(
1459
+ templateScripts,
1460
+ "tool",
1461
+ lazyContext ? effectiveInitialToolNames : undefined,
1462
+ ) + corpusToolsPrompt;
5240
1463
  const devActionsPrompt =
5241
1464
  generateActionsPrompt(
5242
1465
  { ...discoveredActions, ...templateScripts },
@@ -5341,7 +1564,18 @@ export function createAgentChatPlugin(
5341
1564
  },
5342
1565
  );
5343
1566
 
5344
- const mcpTools = actionsToEngineTools(mcpActions);
1567
+ // Same compact initial-tool surface as interactive chat and A2A:
1568
+ // template actions + the small framework default set stay visible
1569
+ // on the first request; everything else (provider, MCP, extension
1570
+ // schemas) is reachable through the attached `tool-search` entry
1571
+ // via `runAgentLoop`'s mid-run tool expansion. Without this, every
1572
+ // external host calling `ask_app` (MCP) paid for a near-full
1573
+ // catalog on its very first request, undermining the compact MCP
1574
+ // catalog this surface is supposed to keep external callers on.
1575
+ const mcpToolSurface = createA2AEngineToolSurface(
1576
+ actionsToEngineTools(mcpActions),
1577
+ effectiveInitialToolNames,
1578
+ );
5345
1579
 
5346
1580
  const resources = await loadResourcesForPrompt(
5347
1581
  SHARED_OWNER,
@@ -5386,7 +1620,8 @@ export function createAgentChatPlugin(
5386
1620
  engine: mcpEngine,
5387
1621
  model,
5388
1622
  systemPrompt,
5389
- tools: mcpTools,
1623
+ tools: mcpToolSurface.tools,
1624
+ availableTools: mcpToolSurface.availableTools,
5390
1625
  messages: [
5391
1626
  {
5392
1627
  role: "user",
@@ -5983,7 +2218,11 @@ export function createAgentChatPlugin(
5983
2218
  const leanBasePrompt = (options?.systemPrompt ?? "") + prodActionsPrompt;
5984
2219
  const anonymousReadOnlyPrompt =
5985
2220
  (options?.systemPrompt ?? PROD_FRAMEWORK_PROMPT_COMPACT) +
5986
- generateActionsPrompt(filterReadOnlyActions(templateScripts), "tool") +
2221
+ generateActionsPrompt(
2222
+ filterReadOnlyActions(templateScripts),
2223
+ "tool",
2224
+ lazyContext ? effectiveInitialToolNames : undefined,
2225
+ ) +
5987
2226
  "\n\nYou are answering from a public shared page. Treat the visible resource as read-only: do not create, edit, delete, comment on, share, or otherwise mutate app data. If the user asks for a change, describe what you would change or suggest signing in to edit.";
5988
2227
 
5989
2228
  // Per-request preamble shared by both prod and dev handlers. Resolves
@@ -6026,20 +2265,6 @@ export function createAgentChatPlugin(
6026
2265
  return getModelFamilyOverlay(model);
6027
2266
  };
6028
2267
 
6029
- /**
6030
- * Returns whether this request is for a brand-new thread (no prior
6031
- * messages). Used to gate the First-Session Personalization block:
6032
- * once a thread has history the block is dead weight on every request.
6033
- *
6034
- * prepareRequest stashes `details.threadId` as `_requestThreadId` on
6035
- * runCtx before systemPrompt is assembled. A new thread has no threadId
6036
- * (the UI passes undefined until the first message creates the thread).
6037
- */
6038
- const isNewThread = (): boolean => {
6039
- const runCtx = ensureRequestRunContext();
6040
- return !(runCtx as any)?._requestThreadId;
6041
- };
6042
-
6043
2268
  const runtimeContextForEvent = (event: any): string => {
6044
2269
  const tzRaw = getHeader(event, "x-user-timezone");
6045
2270
  const timezone =
@@ -6102,11 +2327,14 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6102
2327
  )
6103
2328
  ? APP_RENDERED_CHAT_NO_DIRECT_CODE_PROMPT
6104
2329
  : "";
6105
- // Personalization block: only include on new threads (no prior messages).
6106
- // Once a thread exists, the agent has already run (or skipped) the flow.
6107
- const personalizationBlock = isNewThread()
6108
- ? FIRST_SESSION_PERSONALIZATION
6109
- : "";
2330
+ // Personalization block: included until this owner has finished (or
2331
+ // skipped) the flow see hasCompletedFirstSessionPersonalization
2332
+ // for why this is gated on the owner-scoped appstate flag rather
2333
+ // than "is this a new thread" (keeps turn 1 and turn 2 identical).
2334
+ const personalizationBlock =
2335
+ (await hasCompletedFirstSessionPersonalization(owner))
2336
+ ? ""
2337
+ : FIRST_SESSION_PERSONALIZATION;
6110
2338
  // Per-model overlay: nudge GPT/Gemini engines toward our behavioral norms.
6111
2339
  const modelOverlay = resolveModelOverlay();
6112
2340
  // Stable-first ordering: base prompt / schema / extra come before
@@ -6155,12 +2383,6 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6155
2383
  durableBackgroundRuns: options?.durableBackgroundRuns,
6156
2384
  finalResponseGuard: options?.finalResponseGuard,
6157
2385
  prepareRequest: async (details) => {
6158
- // Stash the threadId on runCtx so systemPrompt can check isNewThread().
6159
- // prepareRequest fires before systemPromptPromise starts, so this is safe.
6160
- const runCtxForPrepare = ensureRequestRunContext();
6161
- if (runCtxForPrepare && details.threadId) {
6162
- (runCtxForPrepare as any)._requestThreadId = details.threadId;
6163
- }
6164
2386
  if (details.threadId && details.ownerEmail) {
6165
2387
  const existingThread = await getThread(details.threadId);
6166
2388
  if (existingThread) {
@@ -6217,7 +2439,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6217
2439
  };
6218
2440
  },
6219
2441
  skipFilesContext,
6220
- initialToolNames: options?.initialToolNames,
2442
+ initialToolNames: effectiveInitialToolNames,
6221
2443
  ...(options?.toolLimits ? { toolLimits: options.toolLimits } : {}),
6222
2444
  onEngineResolved: (engine, model) => {
6223
2445
  const runCtx = ensureRequestRunContext();
@@ -6265,7 +2487,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6265
2487
  finalResponseGuard: options?.finalResponseGuard,
6266
2488
  prepareRequest: options?.prepareRequest,
6267
2489
  skipFilesContext: true,
6268
- initialToolNames: options?.initialToolNames,
2490
+ initialToolNames: effectiveInitialToolNames,
6269
2491
  onEngineResolved: (engine, model) => {
6270
2492
  const runCtx = ensureRequestRunContext();
6271
2493
  if (runCtx) {
@@ -6360,9 +2582,10 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6360
2582
  systemPrompt: async (event: any) => {
6361
2583
  const { owner, extra } = await prepareRun(event);
6362
2584
  const runtimeContext = runtimeContextForEvent(event);
6363
- const personalizationBlock = isNewThread()
6364
- ? FIRST_SESSION_PERSONALIZATION
6365
- : "";
2585
+ const personalizationBlock =
2586
+ (await hasCompletedFirstSessionPersonalization(owner))
2587
+ ? ""
2588
+ : FIRST_SESSION_PERSONALIZATION;
6366
2589
  const modelOverlay = resolveModelOverlay();
6367
2590
  // Stable-first ordering: runtimeContext (day-granular) is
6368
2591
  // appended LAST so a day rollover invalidates as little of the
@@ -6400,10 +2623,6 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6400
2623
  durableBackgroundRuns: options?.durableBackgroundRuns,
6401
2624
  finalResponseGuard: options?.finalResponseGuard,
6402
2625
  prepareRequest: async (details) => {
6403
- const runCtxForPrepare = ensureRequestRunContext();
6404
- if (runCtxForPrepare && details.threadId) {
6405
- (runCtxForPrepare as any)._requestThreadId = details.threadId;
6406
- }
6407
2626
  if (details.threadId && details.ownerEmail) {
6408
2627
  const existingThread = await getThread(details.threadId);
6409
2628
  if (existingThread) {
@@ -6424,7 +2643,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6424
2643
  return options?.prepareRequest?.(details);
6425
2644
  },
6426
2645
  skipFilesContext,
6427
- initialToolNames: options?.initialToolNames,
2646
+ initialToolNames: effectiveInitialToolNames,
6428
2647
  ...(options?.toolLimits ? { toolLimits: options.toolLimits } : {}),
6429
2648
  onEngineResolved: (engine, model) => {
6430
2649
  const runCtx = ensureRequestRunContext();
@@ -6551,12 +2770,17 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6551
2770
  setResponseStatus(event, 401);
6552
2771
  return { error: "Invalid or expired processor token" };
6553
2772
  }
6554
- } else if (isA2AProductionRuntime()) {
6555
- setResponseStatus(event, 503);
6556
- return {
6557
- error:
6558
- "Agent Teams processor not configured — set A2A_SECRET on this deployment.",
6559
- };
2773
+ } else {
2774
+ const loopback = isLoopbackAddress(
2775
+ getRequestIP(event, { xForwardedFor: false }),
2776
+ );
2777
+ if (!isTrustedLocalRuntime({ loopback })) {
2778
+ setResponseStatus(event, 503);
2779
+ return {
2780
+ error:
2781
+ "Agent Teams processor not configured — set A2A_SECRET on this deployment (or A2A_ALLOW_UNSIGNED_INTERNAL=1 for trusted local dev).",
2782
+ };
2783
+ }
6560
2784
  }
6561
2785
 
6562
2786
  try {
@@ -6593,6 +2817,19 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6593
2817
  engine.defaultModel ??
6594
2818
  resolvedModel;
6595
2819
  const model = normalizeModelForEngine(engine, modelCandidate);
2820
+ // Intentionally NOT setting `initialToolNames` here (unlike
2821
+ // schedulerDeps/dispatcher above). `AgentTeamRunConfig.actions`
2822
+ // (buildSubAgentActions()) is already a small curated registry
2823
+ // — it excludes jobTools/automationTools/notificationTools/
2824
+ // progressTools/fetchTool/webSearchTool/toolActions, the exact
2825
+ // bloat those two surfaces needed to defer. There is no larger
2826
+ // catalog behind it for tool-search to expand into: it IS the
2827
+ // sub-agent's entire tool set. Worse, `buildSubAgentSystemPrompt`
2828
+ // (agent-teams.ts) literally lists every key of `actions` as
2829
+ // "Your available actions (...) work directly" — filtering to
2830
+ // anything narrower would contradict what the sub-agent was
2831
+ // just told, and filtering to the full set would only add a
2832
+ // tool-search schema with nothing new for it to find.
6596
2833
  return {
6597
2834
  baseSystemPrompt: basePrompt,
6598
2835
  actions: buildSubAgentActions(),
@@ -7224,6 +3461,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
7224
3461
  setResponseHeader(event, "Content-Type", "application/x-ndjson");
7225
3462
  setResponseHeader(event, "Cache-Control", "no-cache");
7226
3463
 
3464
+ // Lets `cancel()` signal in-flight source work to stop early on
3465
+ // client disconnect, instead of only being noticed the next time
3466
+ // `flush()`'s `controller.enqueue` throws.
3467
+ const mentionsAbort = new AbortController();
3468
+
7227
3469
  const stream = new ReadableStream({
7228
3470
  start(controller) {
7229
3471
  return runWithRequestContext(
@@ -7235,7 +3477,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
7235
3477
  );
7236
3478
  },
7237
3479
  cancel() {
7238
- // Client disconnected — stop enqueuing
3480
+ // Client disconnected — stop enqueuing and let in-flight source
3481
+ // work short-circuit instead of running to completion unseen.
3482
+ mentionsAbort.abort();
7239
3483
  },
7240
3484
  });
7241
3485
 
@@ -7246,10 +3490,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
7246
3490
  ) {
7247
3491
  const MAX_RESULTS = 50;
7248
3492
  let totalSent = 0;
7249
- let cancelled = false;
3493
+ let cancelled = mentionsAbort.signal.aborted;
7250
3494
 
7251
3495
  const flush = (batch: MentionItemResponse[]) => {
7252
- if (cancelled) return;
3496
+ if (cancelled || mentionsAbort.signal.aborted) {
3497
+ cancelled = true;
3498
+ return;
3499
+ }
7253
3500
  const filtered = batch.filter(matchesQuery);
7254
3501
  if (filtered.length === 0) return;
7255
3502
  const remaining = MAX_RESULTS - totalSent;
@@ -7332,6 +3579,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
7332
3579
 
7333
3580
  // 3. Custom mention providers (each flushes independently)
7334
3581
  for (const [key, provider] of Object.entries(mentionProviders)) {
3582
+ // Client already disconnected — don't spawn more provider work.
3583
+ if (mentionsAbort.signal.aborted) break;
7335
3584
  sources.push(
7336
3585
  (async () => {
7337
3586
  try {
@@ -7419,7 +3668,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
7419
3668
  );
7420
3669
 
7421
3670
  await Promise.all(sources);
7422
- if (!cancelled) controller.close();
3671
+ if (!cancelled && !mentionsAbort.signal.aborted) {
3672
+ controller.close();
3673
+ }
7423
3674
  }
7424
3675
  }),
7425
3676
  );
@@ -7450,6 +3701,19 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
7450
3701
  recent.push(now);
7451
3702
  generateTitleRateLimit.set(ownerEmail, recent);
7452
3703
 
3704
+ // Opportunistic eviction: keep the map from growing unbounded by
3705
+ // the count of distinct users on a long-lived process. Drop any
3706
+ // key whose window has fully drained (no timestamps within the
3707
+ // rolling window).
3708
+ if (generateTitleRateLimit.size > RATE_LIMIT_SWEEP_THRESHOLD) {
3709
+ for (const [email, times] of generateTitleRateLimit) {
3710
+ if (email === ownerEmail) continue;
3711
+ if (times.every((t) => now - t >= limitWindowMs)) {
3712
+ generateTitleRateLimit.delete(email);
3713
+ }
3714
+ }
3715
+ }
3716
+
7453
3717
  const body = await readBody(event);
7454
3718
  const message = body?.message;
7455
3719
  if (!message || typeof message !== "string") {
@@ -8438,10 +4702,14 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
8438
4702
 
8439
4703
  // Validate + HMAC-authenticate the self-dispatch and prepare the
8440
4704
  // background-worker body. Pure decision (unit-tested in
8441
- // durable-background.spec.ts); the route only wires it to h3.
4705
+ // durable-background.spec.ts); the route only wires it to h3. This
4706
+ // handler DOES have the h3 event, so it can see the real socket
4707
+ // peer — thread the loopback signal through so unsigned local-dev
4708
+ // self-dispatch (no A2A_SECRET) still works over 127.0.0.1/::1.
8442
4709
  const prepared = prepareProcessRunRequest(
8443
4710
  processBody,
8444
4711
  getHeader(event, "authorization"),
4712
+ isLoopbackAddress(getRequestIP(event, { xForwardedFor: false })),
8445
4713
  );
8446
4714
  if (!prepared.ok) {
8447
4715
  // DIAGNOSTIC: record the auth/validation failure ONTO the run
@@ -8688,6 +4956,19 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
8688
4956
  : await buildSchemaBlock(owner, databaseToolsMode);
8689
4957
  return basePrompt + resources + schemaBlock;
8690
4958
  },
4959
+ // `basePrompt` above is the same prompt the interactive chat
4960
+ // handler builds, so it teaches the same template actions plus
4961
+ // `manage-jobs` (Extended Capabilities / recurring jobs) and
4962
+ // `manage-progress` (SHARED_RULE_14) BY NAME — both are present in
4963
+ // getActions() via jobTools/progressTools. Keep the job runner's
4964
+ // first request on the same compact surface as interactive chat
4965
+ // instead of the full jobTools/automationTools/notificationTools/
4966
+ // fetchTool/webSearchTool/toolActions catalog every tick.
4967
+ getInitialToolNames: () => [
4968
+ ...effectiveInitialToolNames,
4969
+ "manage-jobs",
4970
+ "manage-progress",
4971
+ ],
8691
4972
  apiKey: options?.apiKey,
8692
4973
  model: options?.model,
8693
4974
  appId: options?.appId,
@@ -8994,6 +5275,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
8994
5275
  : await buildSchemaBlock(owner, databaseToolsMode);
8995
5276
  return basePrompt + resources + schemaBlock;
8996
5277
  },
5278
+ // See the matching comment on schedulerDeps.getInitialToolNames
5279
+ // above — same shared `basePrompt`, same reasoning.
5280
+ getInitialToolNames: () => [
5281
+ ...effectiveInitialToolNames,
5282
+ "manage-jobs",
5283
+ "manage-progress",
5284
+ ],
8997
5285
  apiKey: options?.apiKey,
8998
5286
  model: options?.model,
8999
5287
  appId: options?.appId,
@@ -9041,77 +5329,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
9041
5329
  */
9042
5330
  export const defaultAgentChatPlugin: NitroPluginDef = createAgentChatPlugin();
9043
5331
 
9044
- // ---------------------------------------------------------------------------
9045
- // MCP client glue — a shared manager reference + a /_agent-native/mcp/status
9046
- // route so onboarding / settings UIs can see which MCP servers are live.
9047
- // ---------------------------------------------------------------------------
9048
-
9049
- let _globalMcpManager: McpClientManager | null = null;
9050
-
9051
- function setGlobalMcpManager(manager: McpClientManager): void {
9052
- _globalMcpManager = manager;
9053
- }
9054
-
9055
- /** Internal: access the current process's MCP client manager, if any. */
9056
- export function getGlobalMcpManager(): McpClientManager | null {
9057
- return _globalMcpManager;
9058
- }
9059
-
9060
- /** Internal: reload the process's MCP client manager after persisted settings change. */
9061
- export async function refreshGlobalMcpManager(): Promise<boolean> {
9062
- const manager = getGlobalMcpManager();
9063
- if (!manager) return false;
9064
- await manager.reconfigure(await buildMergedConfig());
9065
- return true;
9066
- }
9067
-
9068
- function mountMcpHubStatusRoute(nitroApp: any): void {
9069
- const mountedApps: WeakSet<object> = ((
9070
- globalThis as any
9071
- ).__agentNativeMcpHubStatusMountedApps ??= new WeakSet<object>());
9072
- if (mountedApps.has(nitroApp)) return;
9073
- mountedApps.add(nitroApp);
9074
- try {
9075
- getH3App(nitroApp).use(
9076
- "/_agent-native/mcp/hub/status",
9077
- defineEventHandler(async (event) => {
9078
- if (getMethod(event) !== "GET") {
9079
- setResponseStatus(event, 405);
9080
- return { error: "Method not allowed" };
9081
- }
9082
- setResponseHeader(event, "Content-Type", "application/json");
9083
- return getHubStatus();
9084
- }),
9085
- );
9086
- } catch (err: any) {
9087
- console.warn(
9088
- `[mcp-client] Failed to mount /_agent-native/mcp/hub/status: ${err?.message ?? err}`,
9089
- );
9090
- }
9091
- }
9092
-
9093
- function mountMcpStatusRoute(nitroApp: any, manager: McpClientManager): void {
9094
- // Idempotent per Nitro app; dev-all may host multiple templates in one process.
9095
- const mountedApps: WeakSet<object> = ((
9096
- globalThis as any
9097
- ).__agentNativeMcpStatusMountedApps ??= new WeakSet<object>());
9098
- if (mountedApps.has(nitroApp)) return;
9099
- mountedApps.add(nitroApp);
9100
- try {
9101
- getH3App(nitroApp).use(
9102
- "/_agent-native/mcp/status",
9103
- defineEventHandler(async (event) => {
9104
- if (getMethod(event) !== "GET") {
9105
- setResponseStatus(event, 405);
9106
- return { error: "Method not allowed" };
9107
- }
9108
- setResponseHeader(event, "Content-Type", "application/json");
9109
- return manager.getStatus();
9110
- }),
9111
- );
9112
- } catch (err: any) {
9113
- console.warn(
9114
- `[mcp-client] Failed to mount /_agent-native/mcp/status: ${err?.message ?? err}`,
9115
- );
9116
- }
9117
- }
5332
+ import {
5333
+ setGlobalMcpManager,
5334
+ getGlobalMcpManager,
5335
+ refreshGlobalMcpManager,
5336
+ mountMcpHubStatusRoute,
5337
+ mountMcpStatusRoute,
5338
+ } from "./agent-chat/mcp-glue.js";
5339
+
5340
+ export { getGlobalMcpManager };
5341
+ export { refreshGlobalMcpManager };