@agent-native/core 0.128.4 → 0.129.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +90 -0
  3. package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  4. package/corpus/core/package.json +6 -3
  5. package/corpus/core/src/a2a/client.ts +18 -4
  6. package/corpus/core/src/a2a/server.ts +19 -3
  7. package/corpus/core/src/a2a/types.ts +7 -0
  8. package/corpus/core/src/action.ts +43 -16
  9. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -10
  10. package/corpus/core/src/agent/production-agent.ts +41 -3
  11. package/corpus/core/src/agent/thread-data-builder.ts +5 -0
  12. package/corpus/core/src/chat-threads/migrations.ts +53 -0
  13. package/corpus/core/src/cli/create.ts +4 -0
  14. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  15. package/corpus/core/src/cli/skills.ts +16 -0
  16. package/corpus/core/src/client/AgentPanel.tsx +9 -5
  17. package/corpus/core/src/client/AssistantChat.tsx +138 -46
  18. package/corpus/core/src/client/MultiTabAssistantChat.tsx +16 -10
  19. package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
  20. package/corpus/core/src/client/analytics.ts +159 -102
  21. package/corpus/core/src/client/chat/message-components.tsx +12 -5
  22. package/corpus/core/src/client/chat/runtime.ts +2 -1
  23. package/corpus/core/src/client/chat/tool-call-display.tsx +25 -2
  24. package/corpus/core/src/client/chat-model-groups.ts +3 -0
  25. package/corpus/core/src/client/client-status-requests.ts +161 -0
  26. package/corpus/core/src/client/org/OrgSwitcher.tsx +4 -4
  27. package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  28. package/corpus/core/src/client/sse-event-processor.ts +15 -3
  29. package/corpus/core/src/client/use-agent-engine-configured.ts +54 -50
  30. package/corpus/core/src/client/use-chat-models.ts +16 -11
  31. package/corpus/core/src/client/use-chat-threads.ts +32 -9
  32. package/corpus/core/src/client/use-session.ts +4 -5
  33. package/corpus/core/src/db/client.ts +83 -8
  34. package/corpus/core/src/db/ensure-additive-columns.ts +31 -20
  35. package/corpus/core/src/db/migrations.ts +22 -2
  36. package/corpus/core/src/mcp/approval-store.ts +99 -0
  37. package/corpus/core/src/mcp/build-server.ts +579 -303
  38. package/corpus/core/src/mcp/embed-app.ts +1 -1
  39. package/corpus/core/src/mcp/oauth-client-metadata.ts +380 -0
  40. package/corpus/core/src/mcp/oauth-route.ts +57 -52
  41. package/corpus/core/src/mcp/screen-memory-stdio.ts +400 -378
  42. package/corpus/core/src/mcp/server.ts +29 -145
  43. package/corpus/core/src/mcp/stdio.ts +72 -80
  44. package/corpus/core/src/mcp-client/manager.ts +6 -10
  45. package/corpus/core/src/mcp-client/oauth-client.ts +127 -36
  46. package/corpus/core/src/mcp-client/oauth-routes.ts +16 -6
  47. package/corpus/core/src/mcp-client/routes.ts +6 -5
  48. package/corpus/core/src/server/agent-capabilities.ts +20 -0
  49. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +27 -0
  50. package/corpus/core/src/server/agent-chat-plugin.ts +18 -0
  51. package/corpus/core/src/server/transcribe-voice.ts +14 -5
  52. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  54. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  55. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  56. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +13 -2
  57. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -10
  58. package/corpus/templates/analytics/app/global.css +0 -31
  59. package/corpus/templates/analytics/app/lib/sql-query.ts +46 -20
  60. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboard-loading-states-now-use-a-simpler-standar.md +6 -0
  61. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboards-now-load-first-party-charts-reliably-in.md +6 -0
  62. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +42 -16
  63. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
  64. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  65. package/corpus/templates/assets/.agents/skills/security/SKILL.md +13 -2
  66. package/corpus/templates/brain/.agents/skills/security/SKILL.md +13 -2
  67. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +13 -2
  68. package/corpus/templates/chat/.agents/skills/security/SKILL.md +13 -2
  69. package/corpus/templates/chat/README.md +0 -2
  70. package/corpus/templates/clips/.agents/skills/security/SKILL.md +13 -2
  71. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -6
  72. package/corpus/templates/clips/app/components/player/recording-views-badge.tsx +6 -2
  73. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +25 -10
  74. package/corpus/templates/clips/app/components/sharing/viewed-by-popover.tsx +28 -6
  75. package/corpus/templates/clips/app/lib/use-visible-avatar-url.ts +34 -0
  76. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -2
  77. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -1
  78. package/corpus/templates/clips/changelog/2026-07-28-copied-share-links-no-longer-leave-an-unnecessary-confirmati.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-28-playback-skip-controls-no-longer-show-overlapping-numbers.md +6 -0
  80. package/corpus/templates/clips/changelog/2026-07-28-profile-avatars-now-appear-in-viewer-and-share-access-lists.md +6 -0
  81. package/corpus/templates/clips/changelog/2026-07-28-public-clips-share-buttons-now-use-a-clean-text-only-label.md +6 -0
  82. package/corpus/templates/clips/desktop/src/app.tsx +14 -17
  83. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +3 -1
  84. package/corpus/templates/clips/desktop/src/styles.css +13 -10
  85. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +2 -2
  86. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +36 -0
  87. package/corpus/templates/content/.agents/skills/security/SKILL.md +13 -2
  88. package/corpus/templates/content/actions/_builder-cms-read-client.ts +155 -34
  89. package/corpus/templates/content/actions/_builder-docs-client.ts +151 -30
  90. package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +5 -2
  91. package/corpus/templates/content/app/root.tsx +35 -14
  92. package/corpus/templates/content/changelog/2026-07-28-builder-backed-files-media-fields-now-accept-direct-file-url.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-28-builder-content-reads-now-negotiate-the-newest-mcp-protocol-.md +6 -0
  94. package/corpus/templates/content/changelog/2026-07-28-public-content-pages-load-faster-before-the-optional-ask-sid.md +6 -0
  95. package/corpus/templates/design/.agents/skills/security/SKILL.md +13 -2
  96. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +36 -16
  97. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +42 -3
  98. package/corpus/templates/design/actions/open-visual-edit.ts +2 -1
  99. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  100. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -15
  101. package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +314 -0
  102. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +88 -3
  103. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
  104. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +7 -6
  105. package/corpus/templates/design/app/i18n-data.ts +56 -11
  106. package/corpus/templates/design/app/i18n-responsive-interact.ts +183 -0
  107. package/corpus/templates/design/app/lib/agent-chat.ts +60 -0
  108. package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +89 -0
  109. package/corpus/templates/design/changelog/2026-07-28-design-edits-can-be-applied-through-the-host-coding-agent-fr.md +6 -0
  110. package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +6 -0
  111. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +6 -0
  112. package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +2 -2
  113. package/corpus/templates/design/changelog/2026-07-28-visual-edit-now-always-shows-the-live-running-app-in-the-can.md +6 -0
  114. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +13 -2
  115. package/corpus/templates/forms/.agents/skills/security/SKILL.md +13 -2
  116. package/corpus/templates/macros/.agents/skills/security/SKILL.md +13 -2
  117. package/corpus/templates/mail/.agents/skills/security/SKILL.md +13 -2
  118. package/corpus/templates/plan/.agents/skills/security/SKILL.md +13 -2
  119. package/corpus/templates/slides/.agents/skills/security/SKILL.md +13 -2
  120. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +13 -2
  121. package/corpus/toolkit/CHANGELOG.md +13 -0
  122. package/corpus/toolkit/package.json +1 -1
  123. package/corpus/toolkit/src/chat-history.css +1 -2
  124. package/corpus/toolkit/src/composer/TiptapComposer.tsx +45 -0
  125. package/dist/a2a/client.d.ts +7 -0
  126. package/dist/a2a/client.d.ts.map +1 -1
  127. package/dist/a2a/client.js +8 -3
  128. package/dist/a2a/client.js.map +1 -1
  129. package/dist/a2a/server.d.ts.map +1 -1
  130. package/dist/a2a/server.js +18 -3
  131. package/dist/a2a/server.js.map +1 -1
  132. package/dist/a2a/types.d.ts +7 -0
  133. package/dist/a2a/types.d.ts.map +1 -1
  134. package/dist/a2a/types.js.map +1 -1
  135. package/dist/action.d.ts +15 -1
  136. package/dist/action.d.ts.map +1 -1
  137. package/dist/action.js +39 -16
  138. package/dist/action.js.map +1 -1
  139. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  140. package/dist/agent/engine/ai-sdk-engine.js +14 -10
  141. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  142. package/dist/agent/production-agent.d.ts.map +1 -1
  143. package/dist/agent/production-agent.js +35 -7
  144. package/dist/agent/production-agent.js.map +1 -1
  145. package/dist/agent/thread-data-builder.d.ts +2 -0
  146. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  147. package/dist/agent/thread-data-builder.js +3 -0
  148. package/dist/agent/thread-data-builder.js.map +1 -1
  149. package/dist/chat-threads/migrations.d.ts +21 -0
  150. package/dist/chat-threads/migrations.d.ts.map +1 -0
  151. package/dist/chat-threads/migrations.js +45 -0
  152. package/dist/chat-threads/migrations.js.map +1 -0
  153. package/dist/cli/create.d.ts.map +1 -1
  154. package/dist/cli/create.js +4 -0
  155. package/dist/cli/create.js.map +1 -1
  156. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  157. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  158. package/dist/cli/skills-content/design-visual-edit-skill.js +36 -16
  159. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  160. package/dist/cli/skills.d.ts.map +1 -1
  161. package/dist/cli/skills.js +15 -0
  162. package/dist/cli/skills.js.map +1 -1
  163. package/dist/client/AgentPanel.d.ts.map +1 -1
  164. package/dist/client/AgentPanel.js +6 -5
  165. package/dist/client/AgentPanel.js.map +1 -1
  166. package/dist/client/AssistantChat.d.ts +37 -0
  167. package/dist/client/AssistantChat.d.ts.map +1 -1
  168. package/dist/client/AssistantChat.js +41 -8
  169. package/dist/client/AssistantChat.js.map +1 -1
  170. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  171. package/dist/client/MultiTabAssistantChat.js +11 -10
  172. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  173. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  174. package/dist/client/agent-chat-adapter.js +19 -3
  175. package/dist/client/agent-chat-adapter.js.map +1 -1
  176. package/dist/client/analytics.d.ts.map +1 -1
  177. package/dist/client/analytics.js +150 -101
  178. package/dist/client/analytics.js.map +1 -1
  179. package/dist/client/chat/message-components.d.ts +2 -1
  180. package/dist/client/chat/message-components.d.ts.map +1 -1
  181. package/dist/client/chat/message-components.js +11 -4
  182. package/dist/client/chat/message-components.js.map +1 -1
  183. package/dist/client/chat/runtime.d.ts.map +1 -1
  184. package/dist/client/chat/runtime.js +2 -1
  185. package/dist/client/chat/runtime.js.map +1 -1
  186. package/dist/client/chat/tool-call-display.d.ts +4 -1
  187. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  188. package/dist/client/chat/tool-call-display.js +14 -8
  189. package/dist/client/chat/tool-call-display.js.map +1 -1
  190. package/dist/client/chat-model-groups.d.ts.map +1 -1
  191. package/dist/client/chat-model-groups.js +3 -0
  192. package/dist/client/chat-model-groups.js.map +1 -1
  193. package/dist/client/client-status-requests.d.ts +14 -0
  194. package/dist/client/client-status-requests.d.ts.map +1 -0
  195. package/dist/client/client-status-requests.js +126 -0
  196. package/dist/client/client-status-requests.js.map +1 -0
  197. package/dist/client/org/OrgSwitcher.js +2 -2
  198. package/dist/client/org/OrgSwitcher.js.map +1 -1
  199. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  200. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  201. package/dist/client/sse-event-processor.d.ts +7 -0
  202. package/dist/client/sse-event-processor.d.ts.map +1 -1
  203. package/dist/client/sse-event-processor.js +8 -3
  204. package/dist/client/sse-event-processor.js.map +1 -1
  205. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  206. package/dist/client/use-agent-engine-configured.js +29 -43
  207. package/dist/client/use-agent-engine-configured.js.map +1 -1
  208. package/dist/client/use-chat-models.d.ts.map +1 -1
  209. package/dist/client/use-chat-models.js +11 -11
  210. package/dist/client/use-chat-models.js.map +1 -1
  211. package/dist/client/use-chat-threads.d.ts.map +1 -1
  212. package/dist/client/use-chat-threads.js +23 -7
  213. package/dist/client/use-chat-threads.js.map +1 -1
  214. package/dist/client/use-session.d.ts.map +1 -1
  215. package/dist/client/use-session.js +4 -4
  216. package/dist/client/use-session.js.map +1 -1
  217. package/dist/collab/awareness.d.ts +2 -2
  218. package/dist/collab/awareness.d.ts.map +1 -1
  219. package/dist/db/client.d.ts.map +1 -1
  220. package/dist/db/client.js +48 -4
  221. package/dist/db/client.js.map +1 -1
  222. package/dist/db/ensure-additive-columns.d.ts.map +1 -1
  223. package/dist/db/ensure-additive-columns.js +32 -21
  224. package/dist/db/ensure-additive-columns.js.map +1 -1
  225. package/dist/db/migrations.d.ts +9 -0
  226. package/dist/db/migrations.d.ts.map +1 -1
  227. package/dist/db/migrations.js +14 -3
  228. package/dist/db/migrations.js.map +1 -1
  229. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  230. package/dist/mcp/approval-store.d.ts +16 -0
  231. package/dist/mcp/approval-store.d.ts.map +1 -0
  232. package/dist/mcp/approval-store.js +85 -0
  233. package/dist/mcp/approval-store.js.map +1 -0
  234. package/dist/mcp/build-server.d.ts +1 -34
  235. package/dist/mcp/build-server.d.ts.map +1 -1
  236. package/dist/mcp/build-server.js +220 -23
  237. package/dist/mcp/build-server.js.map +1 -1
  238. package/dist/mcp/embed-app.js +1 -1
  239. package/dist/mcp/embed-app.js.map +1 -1
  240. package/dist/mcp/oauth-client-metadata.d.ts +15 -0
  241. package/dist/mcp/oauth-client-metadata.d.ts.map +1 -0
  242. package/dist/mcp/oauth-client-metadata.js +297 -0
  243. package/dist/mcp/oauth-client-metadata.js.map +1 -0
  244. package/dist/mcp/oauth-route.d.ts.map +1 -1
  245. package/dist/mcp/oauth-route.js +39 -53
  246. package/dist/mcp/oauth-route.js.map +1 -1
  247. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  248. package/dist/mcp/screen-memory-stdio.js +304 -296
  249. package/dist/mcp/screen-memory-stdio.js.map +1 -1
  250. package/dist/mcp/server.d.ts +12 -25
  251. package/dist/mcp/server.d.ts.map +1 -1
  252. package/dist/mcp/server.js +24 -133
  253. package/dist/mcp/server.js.map +1 -1
  254. package/dist/mcp/stdio.d.ts.map +1 -1
  255. package/dist/mcp/stdio.js +36 -35
  256. package/dist/mcp/stdio.js.map +1 -1
  257. package/dist/mcp-client/manager.d.ts.map +1 -1
  258. package/dist/mcp-client/manager.js +6 -6
  259. package/dist/mcp-client/manager.js.map +1 -1
  260. package/dist/mcp-client/oauth-client.d.ts +13 -17
  261. package/dist/mcp-client/oauth-client.d.ts.map +1 -1
  262. package/dist/mcp-client/oauth-client.js +74 -16
  263. package/dist/mcp-client/oauth-client.js.map +1 -1
  264. package/dist/mcp-client/oauth-routes.d.ts +2 -2
  265. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  266. package/dist/mcp-client/oauth-routes.js +16 -6
  267. package/dist/mcp-client/oauth-routes.js.map +1 -1
  268. package/dist/mcp-client/routes.js +5 -5
  269. package/dist/mcp-client/routes.js.map +1 -1
  270. package/dist/observability/routes.d.ts +3 -3
  271. package/dist/progress/routes.d.ts +1 -1
  272. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  273. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  274. package/dist/resources/handlers.d.ts +1 -1
  275. package/dist/secrets/routes.d.ts +9 -9
  276. package/dist/server/agent-capabilities.d.ts.map +1 -1
  277. package/dist/server/agent-capabilities.js +21 -0
  278. package/dist/server/agent-capabilities.js.map +1 -1
  279. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -0
  280. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  281. package/dist/server/agent-chat/action-filters-a2a.js +16 -0
  282. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  283. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  284. package/dist/server/agent-chat-plugin.js +11 -1
  285. package/dist/server/agent-chat-plugin.js.map +1 -1
  286. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  287. package/dist/server/realtime-token.d.ts +1 -1
  288. package/dist/server/transcribe-voice.d.ts +1 -1
  289. package/dist/server/transcribe-voice.js +12 -6
  290. package/dist/server/transcribe-voice.js.map +1 -1
  291. package/dist/templates/chat/.agents/skills/security/SKILL.md +13 -2
  292. package/dist/templates/chat/README.md +0 -2
  293. package/dist/templates/default/.agents/skills/security/SKILL.md +13 -2
  294. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  295. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  296. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  297. package/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  298. package/package.json +7 -4
  299. package/src/a2a/client.ts +18 -4
  300. package/src/a2a/server.ts +19 -3
  301. package/src/a2a/types.ts +7 -0
  302. package/src/action.ts +43 -16
  303. package/src/agent/engine/ai-sdk-engine.ts +14 -10
  304. package/src/agent/production-agent.ts +41 -3
  305. package/src/agent/thread-data-builder.ts +5 -0
  306. package/src/chat-threads/migrations.ts +53 -0
  307. package/src/cli/create.ts +4 -0
  308. package/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  309. package/src/cli/skills.ts +16 -0
  310. package/src/client/AgentPanel.tsx +9 -5
  311. package/src/client/AssistantChat.tsx +138 -46
  312. package/src/client/MultiTabAssistantChat.tsx +16 -10
  313. package/src/client/agent-chat-adapter.ts +21 -3
  314. package/src/client/analytics.ts +159 -102
  315. package/src/client/chat/message-components.tsx +12 -5
  316. package/src/client/chat/runtime.ts +2 -1
  317. package/src/client/chat/tool-call-display.tsx +25 -2
  318. package/src/client/chat-model-groups.ts +3 -0
  319. package/src/client/client-status-requests.ts +161 -0
  320. package/src/client/org/OrgSwitcher.tsx +4 -4
  321. package/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  322. package/src/client/sse-event-processor.ts +15 -3
  323. package/src/client/use-agent-engine-configured.ts +54 -50
  324. package/src/client/use-chat-models.ts +16 -11
  325. package/src/client/use-chat-threads.ts +32 -9
  326. package/src/client/use-session.ts +4 -5
  327. package/src/db/client.ts +83 -8
  328. package/src/db/ensure-additive-columns.ts +31 -20
  329. package/src/db/migrations.ts +22 -2
  330. package/src/mcp/approval-store.ts +99 -0
  331. package/src/mcp/build-server.ts +579 -303
  332. package/src/mcp/embed-app.ts +1 -1
  333. package/src/mcp/oauth-client-metadata.ts +380 -0
  334. package/src/mcp/oauth-route.ts +57 -52
  335. package/src/mcp/screen-memory-stdio.ts +400 -378
  336. package/src/mcp/server.ts +29 -145
  337. package/src/mcp/stdio.ts +72 -80
  338. package/src/mcp-client/manager.ts +6 -10
  339. package/src/mcp-client/oauth-client.ts +127 -36
  340. package/src/mcp-client/oauth-routes.ts +16 -6
  341. package/src/mcp-client/routes.ts +6 -5
  342. package/src/server/agent-capabilities.ts +20 -0
  343. package/src/server/agent-chat/action-filters-a2a.ts +27 -0
  344. package/src/server/agent-chat-plugin.ts +18 -0
  345. package/src/server/transcribe-voice.ts +14 -5
  346. package/src/templates/chat/.agents/skills/security/SKILL.md +13 -2
  347. package/src/templates/chat/README.md +0 -2
  348. package/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  349. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  350. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  351. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
@@ -10,15 +10,17 @@
10
10
  import {
11
11
  auth,
12
12
  refreshAuthorization,
13
+ validateAuthorizationResponseIssuer,
14
+ type AuthorizationServerMetadata,
15
+ type OAuthClientInformationContext,
16
+ type OAuthClientMetadata,
13
17
  type OAuthClientProvider,
14
- } from "@modelcontextprotocol/sdk/client/auth.js";
15
- import type {
16
- AuthorizationServerMetadata,
17
- OAuthClientInformationMixed,
18
- OAuthClientMetadata,
18
+ type OAuthDiscoveryState,
19
19
  OAuthProtectedResourceMetadata,
20
+ type StoredOAuthClientInformation,
21
+ type StoredOAuthTokens,
20
22
  OAuthTokens,
21
- } from "@modelcontextprotocol/sdk/shared/auth.js";
23
+ } from "@modelcontextprotocol/client";
22
24
 
23
25
  import {
24
26
  deleteOAuthTokens,
@@ -131,18 +133,13 @@ function credentialOwner(options: { scope: "user" | "org"; scopeId: string }) {
131
133
  return `${options.scope}:${options.scopeId}`;
132
134
  }
133
135
 
134
- export interface McpOAuthDiscoveryState {
135
- authorizationServerUrl: string;
136
- authorizationServerMetadata?: AuthorizationServerMetadata;
137
- resourceMetadata?: OAuthProtectedResourceMetadata;
138
- resourceMetadataUrl?: string;
139
- }
136
+ export type McpOAuthDiscoveryState = OAuthDiscoveryState;
140
137
 
141
138
  export interface McpOAuthCredentialBundle {
142
139
  serverUrl: string;
143
- clientInformation: OAuthClientInformationMixed;
140
+ clientInformation: StoredOAuthClientInformation;
144
141
  discoveryState?: McpOAuthDiscoveryState;
145
- tokens: OAuthTokens;
142
+ tokens: StoredOAuthTokens;
146
143
  tokenExpiresAt?: number;
147
144
  }
148
145
 
@@ -150,7 +147,7 @@ export interface McpOAuthStartResult {
150
147
  authorizationUrl: URL;
151
148
  codeVerifier: string;
152
149
  state: string;
153
- clientInformation: OAuthClientInformationMixed;
150
+ clientInformation: StoredOAuthClientInformation;
154
151
  discoveryState?: McpOAuthDiscoveryState;
155
152
  }
156
153
 
@@ -162,11 +159,50 @@ export interface McpOAuthProviderOptions {
162
159
  serverUrl: string;
163
160
  redirectUrl: string;
164
161
  state: string;
165
- clientInformation?: OAuthClientInformationMixed;
162
+ clientInformation?: StoredOAuthClientInformation;
166
163
  codeVerifier?: string;
167
164
  discoveryState?: McpOAuthDiscoveryState;
168
165
  }
169
166
 
167
+ function issuerForDiscovery(
168
+ discovery: McpOAuthDiscoveryState | undefined,
169
+ ): string | undefined {
170
+ const issuer = discovery?.authorizationServerMetadata?.issuer;
171
+ return typeof issuer === "string" && issuer ? issuer : undefined;
172
+ }
173
+
174
+ function withIssuer<T extends { issuer?: string }>(
175
+ value: T,
176
+ context: OAuthClientInformationContext | undefined,
177
+ ): T {
178
+ if (value.issuer && context?.issuer && value.issuer !== context.issuer) {
179
+ throw new Error("MCP OAuth credential issuer does not match discovery");
180
+ }
181
+ const issuer = context?.issuer ?? value.issuer;
182
+ return issuer ? { ...value, issuer } : value;
183
+ }
184
+
185
+ function isBoundToIssuer(
186
+ value: { issuer?: string } | undefined,
187
+ context: OAuthClientInformationContext | undefined,
188
+ ): boolean {
189
+ return !context?.issuer || value?.issuer === context.issuer;
190
+ }
191
+
192
+ function applicationTypeForRedirect(redirectUrl: string): "native" | "web" {
193
+ const url = new URL(redirectUrl);
194
+ if (
195
+ url.protocol !== "https:" ||
196
+ url.hostname === "localhost" ||
197
+ url.hostname === "127.0.0.1" ||
198
+ url.hostname === "::1" ||
199
+ url.hostname === "[::1]"
200
+ ) {
201
+ return "native";
202
+ }
203
+ return "web";
204
+ }
205
+
170
206
  /**
171
207
  * A small adapter around the MCP SDK's OAuth provider interface. The route
172
208
  * stores the adapter's state in an encrypted, short-lived browser cookie; the
@@ -176,8 +212,8 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
176
212
  private readonly redirectUrlValue: string;
177
213
  private readonly stateValue: string;
178
214
  private readonly metadata: OAuthClientMetadata;
179
- private clientInfo?: OAuthClientInformationMixed;
180
- private savedTokens?: OAuthTokens;
215
+ private clientInfo?: StoredOAuthClientInformation;
216
+ private savedTokens?: StoredOAuthTokens;
181
217
  private savedCodeVerifier?: string;
182
218
  private savedDiscovery?: McpOAuthDiscoveryState;
183
219
  private authorizationUrl?: URL;
@@ -188,11 +224,24 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
188
224
  this.clientInfo = options.clientInformation;
189
225
  this.savedCodeVerifier = options.codeVerifier;
190
226
  this.savedDiscovery = options.discoveryState;
227
+ const recordedIssuer = issuerForDiscovery(this.savedDiscovery);
228
+ if (
229
+ this.clientInfo &&
230
+ !this.clientInfo.issuer &&
231
+ recordedIssuer &&
232
+ this.savedCodeVerifier
233
+ ) {
234
+ // A callback flow persists registration, discovery, PKCE, and state in
235
+ // one encrypted cookie. Binding that pre-v2 in-flight registration to
236
+ // its recorded issuer is safe; durable credentials are never inferred.
237
+ this.clientInfo = { ...this.clientInfo, issuer: recordedIssuer };
238
+ }
191
239
  this.metadata = {
192
240
  redirect_uris: [options.redirectUrl],
193
241
  token_endpoint_auth_method: "none",
194
- grant_types: ["authorization_code"],
242
+ grant_types: ["authorization_code", "refresh_token"],
195
243
  response_types: ["code"],
244
+ application_type: applicationTypeForRedirect(options.redirectUrl),
196
245
  client_name: "Agent Native MCP connector",
197
246
  };
198
247
  }
@@ -209,20 +258,34 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
209
258
  return this.stateValue;
210
259
  }
211
260
 
212
- clientInformation(): OAuthClientInformationMixed | undefined {
213
- return this.clientInfo;
261
+ clientInformation(
262
+ context?: OAuthClientInformationContext,
263
+ ): StoredOAuthClientInformation | undefined {
264
+ return isBoundToIssuer(this.clientInfo, context)
265
+ ? this.clientInfo
266
+ : undefined;
214
267
  }
215
268
 
216
- saveClientInformation(info: OAuthClientInformationMixed): void {
217
- this.clientInfo = info;
269
+ saveClientInformation(
270
+ info: StoredOAuthClientInformation,
271
+ context?: OAuthClientInformationContext,
272
+ ): void {
273
+ this.clientInfo = withIssuer(info, context);
218
274
  }
219
275
 
220
- tokens(): OAuthTokens | undefined {
221
- return this.savedTokens;
276
+ tokens(
277
+ context?: OAuthClientInformationContext,
278
+ ): StoredOAuthTokens | undefined {
279
+ return isBoundToIssuer(this.savedTokens, context)
280
+ ? this.savedTokens
281
+ : undefined;
222
282
  }
223
283
 
224
- saveTokens(tokens: OAuthTokens): void {
225
- this.savedTokens = tokens;
284
+ saveTokens(
285
+ tokens: StoredOAuthTokens,
286
+ context?: OAuthClientInformationContext,
287
+ ): void {
288
+ this.savedTokens = withIssuer(tokens, context);
226
289
  }
227
290
 
228
291
  redirectToAuthorization(authorizationUrl: URL): void {
@@ -269,7 +332,7 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
269
332
  return this.savedTokens;
270
333
  }
271
334
 
272
- get savedClientInformation(): OAuthClientInformationMixed | undefined {
335
+ get savedClientInformation(): StoredOAuthClientInformation | undefined {
273
336
  return this.clientInfo;
274
337
  }
275
338
  }
@@ -301,13 +364,17 @@ export async function startMcpOAuthAuthorization(
301
364
  }
302
365
 
303
366
  export async function finishMcpOAuthAuthorization(
304
- options: McpOAuthProviderOptions & { authorizationCode: string },
367
+ options: McpOAuthProviderOptions & {
368
+ authorizationCode: string;
369
+ iss?: string;
370
+ },
305
371
  ): Promise<McpOAuthCallbackResult> {
306
372
  checkedRemoteUrl(options.serverUrl, "server");
307
373
  const provider = new McpOAuthClientProvider(options);
308
374
  const result = await auth(provider, {
309
375
  serverUrl: options.serverUrl,
310
376
  authorizationCode: options.authorizationCode,
377
+ iss: options.iss,
311
378
  fetchFn: guardedOAuthFetch(),
312
379
  });
313
380
  if (result !== "AUTHORIZED" || !provider.savedTokensValue) {
@@ -325,6 +392,19 @@ export async function finishMcpOAuthAuthorization(
325
392
  };
326
393
  }
327
394
 
395
+ export function validateMcpOAuthCallbackIssuer(
396
+ discoveryState: McpOAuthDiscoveryState | undefined,
397
+ iss: string | undefined,
398
+ ): void {
399
+ validateAuthorizationResponseIssuer({
400
+ iss,
401
+ expectedIssuer: issuerForDiscovery(discoveryState),
402
+ issParameterSupported:
403
+ discoveryState?.authorizationServerMetadata
404
+ ?.authorization_response_iss_parameter_supported === true,
405
+ });
406
+ }
407
+
328
408
  export function tokenExpiresAt(tokens: OAuthTokens): number | undefined {
329
409
  const expiresIn = Number(tokens.expires_in);
330
410
  if (!Number.isFinite(expiresIn) || expiresIn <= 0) return undefined;
@@ -406,20 +486,30 @@ export async function getMcpOAuthAccessToken(options: {
406
486
  if (!credentials || credentials.serverUrl !== options.serverUrl) return null;
407
487
 
408
488
  const accessToken = credentials.tokens.access_token;
489
+ const now = Date.now();
409
490
  if (
410
491
  typeof credentials.tokenExpiresAt !== "number" ||
411
- credentials.tokenExpiresAt - Date.now() > TOKEN_EXPIRY_SKEW_MS
492
+ credentials.tokenExpiresAt - now > TOKEN_EXPIRY_SKEW_MS
412
493
  ) {
413
494
  return accessToken;
414
495
  }
496
+ const tokenIsExpired = credentials.tokenExpiresAt <= now;
415
497
 
416
498
  const refreshToken = credentials.tokens.refresh_token;
417
499
  const discovery = credentials.discoveryState;
418
500
  if (!refreshToken || !discovery?.authorizationServerUrl) {
419
- return accessToken;
501
+ return tokenIsExpired ? null : accessToken;
420
502
  }
421
503
 
422
504
  try {
505
+ const expectedIssuer = issuerForDiscovery(discovery);
506
+ if (
507
+ !expectedIssuer ||
508
+ credentials.clientInformation.issuer !== expectedIssuer ||
509
+ credentials.tokens.issuer !== expectedIssuer
510
+ ) {
511
+ return null;
512
+ }
423
513
  const resource = discovery.resourceMetadata?.resource
424
514
  ? checkedRemoteUrl(discovery.resourceMetadata.resource, "resource")
425
515
  : undefined;
@@ -434,9 +524,10 @@ export async function getMcpOAuthAccessToken(options: {
434
524
  resource,
435
525
  fetchFn: guardedOAuthFetch(),
436
526
  });
437
- const nextTokens: OAuthTokens = {
527
+ const nextTokens: StoredOAuthTokens = {
438
528
  ...credentials.tokens,
439
529
  ...refreshed,
530
+ issuer: expectedIssuer,
440
531
  ...(refreshed.refresh_token
441
532
  ? { refresh_token: refreshed.refresh_token }
442
533
  : credentials.tokens.refresh_token
@@ -454,9 +545,9 @@ export async function getMcpOAuthAccessToken(options: {
454
545
  });
455
546
  return nextTokens.access_token;
456
547
  } catch {
457
- // Keep the old token so the MCP request can return the provider's normal
458
- // auth error. A single expired connector must not remove every server from
459
- // the process-wide manager during a config refresh.
460
- return accessToken;
548
+ // A still-valid token can survive a transient refresh failure. Once it has
549
+ // expired, omit it so callers surface reauthorization instead of retrying
550
+ // a credential that can never authenticate.
551
+ return tokenIsExpired ? null : accessToken;
461
552
  }
462
553
  }
@@ -1,6 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
 
3
- import type { OAuthClientInformationMixed } from "@modelcontextprotocol/sdk/shared/auth.js";
3
+ import type { StoredOAuthClientInformation } from "@modelcontextprotocol/client";
4
4
  import {
5
5
  deleteCookie,
6
6
  defineEventHandler,
@@ -23,6 +23,7 @@ import {
23
23
  finishMcpOAuthAuthorization,
24
24
  startMcpOAuthAuthorization,
25
25
  type McpOAuthDiscoveryState,
26
+ validateMcpOAuthCallbackIssuer,
26
27
  } from "./oauth-client.js";
27
28
  import {
28
29
  addOAuthRemoteServer,
@@ -48,7 +49,7 @@ export interface McpOAuthFlow {
48
49
  redirectUri: string;
49
50
  state: string;
50
51
  codeVerifier: string;
51
- clientInformation: OAuthClientInformationMixed;
52
+ clientInformation: StoredOAuthClientInformation;
52
53
  discoveryState?: McpOAuthDiscoveryState;
53
54
  returnUrl?: string;
54
55
  expiresAt: number;
@@ -214,22 +215,30 @@ async function handleMcpOAuthCallback(
214
215
  const query = getQuery(event);
215
216
  const code = text(query.code);
216
217
  const state = text(query.state);
218
+ const iss = text(query.iss);
217
219
  const providerError = text(query.error);
218
220
  const flow = readMcpOAuthFlowCookie(event);
219
221
  clearMcpOAuthFlowCookies(event);
220
- if (providerError || !code || !state) {
221
- setResponseStatus(event, 400);
222
- return { error: "MCP OAuth authorization was not completed." };
223
- }
224
222
  const org =
225
223
  flow?.scope === "org" ? await getOrgContext(event).catch(() => null) : null;
226
224
  if (
225
+ !state ||
227
226
  !flow ||
228
227
  !isValidMcpOAuthFlow(flow, session.email, org?.orgId ?? undefined, state)
229
228
  ) {
230
229
  setResponseStatus(event, 400);
231
230
  return { error: "MCP OAuth state is invalid or expired." };
232
231
  }
232
+ try {
233
+ validateMcpOAuthCallbackIssuer(flow.discoveryState, iss);
234
+ } catch {
235
+ setResponseStatus(event, 400);
236
+ return { error: "MCP OAuth authorization response issuer is invalid." };
237
+ }
238
+ if (providerError || !code) {
239
+ setResponseStatus(event, 400);
240
+ return { error: "MCP OAuth authorization was not completed." };
241
+ }
233
242
  if (flow.scope === "org" && !isOrgAdmin(org?.role)) {
234
243
  setResponseStatus(event, 403);
235
244
  return {
@@ -250,6 +259,7 @@ async function handleMcpOAuthCallback(
250
259
  codeVerifier: flow.codeVerifier,
251
260
  discoveryState: flow.discoveryState,
252
261
  authorizationCode: code,
262
+ iss,
253
263
  }),
254
264
  );
255
265
  const result = await addOAuthRemoteServer(flow.scope, flow.scopeId, {
@@ -1055,10 +1055,8 @@ async function tryConnect(
1055
1055
  | { ok: false; error: string }
1056
1056
  > {
1057
1057
  try {
1058
- const [{ Client }, { StreamableHTTPClientTransport }] = await Promise.all([
1059
- import("@modelcontextprotocol/sdk/client/index.js"),
1060
- import("@modelcontextprotocol/sdk/client/streamableHttp.js"),
1061
- ]);
1058
+ const { Client, StreamableHTTPClientTransport } =
1059
+ await import("@modelcontextprotocol/client");
1062
1060
  const requestInit: Record<string, unknown> = {};
1063
1061
  if (headers && Object.keys(headers).length > 0) {
1064
1062
  requestInit.headers = headers;
@@ -1068,7 +1066,10 @@ async function tryConnect(
1068
1066
  });
1069
1067
  const client = new Client(
1070
1068
  { name: "agent-native-mcp-client-test", version: "1.0.0" },
1071
- { capabilities: {} },
1069
+ {
1070
+ capabilities: {},
1071
+ versionNegotiation: { mode: "auto" },
1072
+ },
1072
1073
  );
1073
1074
  try {
1074
1075
  await client.connect(transport);
@@ -1,6 +1,7 @@
1
1
  import { A2AClient } from "../a2a/client.js";
2
2
  import type { AgentCard, AgentSkill } from "../a2a/types.js";
3
3
  import { type DiscoveredAgent } from "./agent-discovery.js";
4
+ import { getRequestUserEmail } from "./request-context.js";
4
5
 
5
6
  /** Matches the `<available-apps>` prompt block so both surfaces agree. */
6
7
  export const MAX_APPS = 30;
@@ -33,8 +34,27 @@ export async function loadCapabilities(
33
34
  agent: DiscoveredAgent,
34
35
  ): Promise<PeerCapabilities> {
35
36
  try {
37
+ // Discover as ourselves. An anonymous card lists only publicly-safe
38
+ // actions, which never overlap the set `actions/invoke` accepts, so an
39
+ // unauthenticated probe reports every sibling as having no callable
40
+ // actions and pushes the caller into open-ended delegation.
41
+ let token: string | undefined;
42
+ try {
43
+ const { signA2AToken } = await import("../a2a/client.js");
44
+ const email = getRequestUserEmail();
45
+ if (email) {
46
+ token = await signA2AToken(email, undefined, undefined, {
47
+ preferGlobalSecret: true,
48
+ audience: new URL(agent.url).origin,
49
+ });
50
+ }
51
+ } catch {
52
+ // No signable identity (no secret, no session) — fall back to the
53
+ // anonymous card rather than failing discovery outright.
54
+ }
36
55
  const card = await new A2AClient(agent.url).getAgentCard({
37
56
  timeoutMs: CARD_TIMEOUT_MS,
57
+ ...(token ? { token } : {}),
38
58
  });
39
59
  return {
40
60
  agent,
@@ -119,6 +119,33 @@ export function buildPublicAgentA2ASkills(
119
119
  );
120
120
  }
121
121
 
122
+ /**
123
+ * Skills for a caller with a verified A2A identity: exactly what
124
+ * `filterDirectA2AActions` will execute. Kept separate from
125
+ * `buildPublicAgentA2ASkills` because the public card may only name actions
126
+ * with `requiresAuth !== true` while direct invocation requires
127
+ * `requiresAuth === true` — advertising the public set alone tells siblings
128
+ * an app has nothing callable when it does.
129
+ */
130
+ export function buildAuthenticatedAgentA2ASkills(
131
+ actions: Record<string, ActionEntry>,
132
+ options: Pick<AgentChatPluginOptions, "connectorCatalog" | "externalAgents">,
133
+ ): Array<{
134
+ id: string;
135
+ name: string;
136
+ description: string;
137
+ publicAgent: ActionEntry["publicAgent"];
138
+ }> {
139
+ return Object.entries(filterDirectA2AActions(actions, options)).map(
140
+ ([name, entry]) => ({
141
+ id: name,
142
+ name,
143
+ description: entry.tool.description,
144
+ publicAgent: entry.publicAgent,
145
+ }),
146
+ );
147
+ }
148
+
122
149
  export function resolveArtifactBaseUrl(
123
150
  event: any | undefined,
124
151
  ): string | undefined {
@@ -102,6 +102,7 @@ import type {
102
102
  MentionProvider,
103
103
  } from "../agent/types.js";
104
104
  import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
105
+ import { runChatThreadDataMigrations } from "../chat-threads/migrations.js";
105
106
  import {
106
107
  createThread,
107
108
  forkThread,
@@ -280,6 +281,7 @@ import {
280
281
  runMCPAgentLoop,
281
282
  assembleA2AFinalResponse,
282
283
  buildPublicAgentA2ASkills,
284
+ buildAuthenticatedAgentA2ASkills,
283
285
  resolveArtifactBaseUrl,
284
286
  } from "./agent-chat/action-filters-a2a.js";
285
287
  import {
@@ -1318,6 +1320,10 @@ export function createAgentChatPlugin(
1318
1320
  : "Agent",
1319
1321
  description: `Agent-native ${options?.appId ?? "app"} agent`,
1320
1322
  skills: buildPublicAgentA2ASkills(allScripts),
1323
+ authenticatedSkills: buildAuthenticatedAgentA2ASkills(
1324
+ mcpFullActions ?? allScripts,
1325
+ options ?? {},
1326
+ ),
1321
1327
  publicSkillsOnly: true,
1322
1328
  streaming: true,
1323
1329
  durableBackgroundRuns: options?.durableBackgroundRuns,
@@ -5952,6 +5958,18 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5952
5958
  }, 20_000); // Start 20s after init (after the agent-teams sweep)
5953
5959
  })();
5954
5960
 
5961
+ // ─── Legacy chat-thread message_count repair ──────────────────────
5962
+ // Long-lived processes apply this name-tracked data migration once.
5963
+ // Serverless isolates skip it: launching a full thread_data scan from
5964
+ // every cold start would recreate the connection stampede this repair
5965
+ // was extracted from table bootstrap to prevent.
5966
+ void runChatThreadDataMigrations(nitroApp).catch((err: unknown) => {
5967
+ console.error(
5968
+ "[chat-threads] legacy message_count repair failed — retrying on the next long-lived boot:",
5969
+ err,
5970
+ );
5971
+ });
5972
+
5955
5973
  // ─── Trigger Dispatcher (event-based automations) ─────────────────
5956
5974
  if (disableRecurringJobsRuntime) {
5957
5975
  if (process.env.DEBUG) {
@@ -49,10 +49,10 @@ const GROQ_URL = "https://api.groq.com/openai/v1/audio/transcriptions";
49
49
  const GROQ_CHAT_URL = "https://api.groq.com/openai/v1/chat/completions";
50
50
  const GROQ_MODEL = "whisper-large-v3-turbo";
51
51
  const GROQ_CLEANUP_MODEL = "llama-3.3-70b-versatile";
52
- const OPENAI_MODEL = "whisper-1";
52
+ const OPENAI_MODEL = "gpt-transcribe";
53
53
  const OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
54
54
  const OPENAI_CLEANUP_MODEL = "gpt-5.6-luna";
55
- const MAX_AUDIO_BYTES = 25 * 1024 * 1024; // Whisper hard limit.
55
+ const MAX_AUDIO_BYTES = 25 * 1024 * 1024; // OpenAI audio upload hard limit.
56
56
  // Hard cap for the dictation-cleanup text path (`sanitizeTranscriptText`
57
57
  // below). This endpoint has no `task` field — it's always the Hold-Fn
58
58
  // dictation cleanup pass — but a long session (many minutes across
@@ -497,8 +497,8 @@ export function createTranscribeVoiceHandler() {
497
497
  }
498
498
 
499
499
  /**
500
- * Posts the audio to a Whisper-compatible OpenAI-style endpoint (Groq or
501
- * OpenAI itself) and returns `{ text }` / `{ error }` shaped like the
500
+ * Posts the audio to an OpenAI-style transcription endpoint (Groq or OpenAI)
501
+ * and returns `{ text }` / `{ error }` shaped like the
502
502
  * other branches in `createTranscribeVoiceHandler`. Hoisted so the
503
503
  * strict-Groq preference path and the auto fallback chain share one
504
504
  * implementation.
@@ -538,7 +538,16 @@ async function callWhisperCompat({
538
538
  );
539
539
  form.append("model", provider.model);
540
540
  form.append("response_format", "json");
541
- if (language) form.append("language", language);
541
+ if (language) {
542
+ if (provider.name === "openai") {
543
+ form.append(
544
+ "languages[]",
545
+ language.split("-")[0]?.toLowerCase() ?? language,
546
+ );
547
+ } else {
548
+ form.append("language", language);
549
+ }
550
+ }
542
551
  if (instructions) form.append("prompt", instructions);
543
552
 
544
553
  const controller = new AbortController();
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
163
163
  proves *someone* is signed in. To restrict an operation to some teammates, set
164
164
  `authorize` on the `defineAction`. It wraps `run`, so it applies at every
165
165
  dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
166
- `needsApproval`, which is honoured only in the agent loop.
166
+ `needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
167
+ support elicitation, but is not an authorization boundary for every dispatch
168
+ site.
167
169
 
168
170
  ```ts
169
171
  import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
@@ -203,7 +205,16 @@ export default defineAction({
203
205
  });
204
206
  ```
205
207
 
206
- When the gate is truthy and the call is not yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's stable `approvalKey`; only then does the action run. A predicate gates conditionally (e.g. only external recipients) and **fails closed** — a throw is treated as "approval required".
208
+ When the gate is truthy and the call is not yet approved, the agent loop emits
209
+ an `approval_required` event and **stops the turn — `run()` never executes**.
210
+ The human approves via the chat UI's Approve affordance, which re-issues the
211
+ turn with the call's stable `approvalKey`; only then does the action run. MCP
212
+ 2026 hosts receive an `input_required` approval request bound to the exact
213
+ caller, action, and arguments. The signed response state is backed by a durable,
214
+ single-use grant, so denial, expiry, replay, missing host support, or invalid
215
+ state fails closed before `run()`. A predicate gates conditionally (e.g. only
216
+ external recipients) and **fails closed** — a throw is treated as "approval
217
+ required".
207
218
 
208
219
  Rules:
209
220
 
@@ -30,5 +30,3 @@ pnpm dev
30
30
  ```
31
31
 
32
32
  Full docs: [agent-native.com/docs/template-chat](https://agent-native.com/docs/template-chat).
33
-
34
- <!-- vendor-branch mirror end-to-end test — safe to remove -->
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
163
163
  proves *someone* is signed in. To restrict an operation to some teammates, set
164
164
  `authorize` on the `defineAction`. It wraps `run`, so it applies at every
165
165
  dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
166
- `needsApproval`, which is honoured only in the agent loop.
166
+ `needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
167
+ support elicitation, but is not an authorization boundary for every dispatch
168
+ site.
167
169
 
168
170
  ```ts
169
171
  import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
@@ -203,7 +205,16 @@ export default defineAction({
203
205
  });
204
206
  ```
205
207
 
206
- When the gate is truthy and the call is not yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's stable `approvalKey`; only then does the action run. A predicate gates conditionally (e.g. only external recipients) and **fails closed** — a throw is treated as "approval required".
208
+ When the gate is truthy and the call is not yet approved, the agent loop emits
209
+ an `approval_required` event and **stops the turn — `run()` never executes**.
210
+ The human approves via the chat UI's Approve affordance, which re-issues the
211
+ turn with the call's stable `approvalKey`; only then does the action run. MCP
212
+ 2026 hosts receive an `input_required` approval request bound to the exact
213
+ caller, action, and arguments. The signed response state is backed by a durable,
214
+ single-use grant, so denial, expiry, replay, missing host support, or invalid
215
+ state fails closed before `run()`. A predicate gates conditionally (e.g. only
216
+ external recipients) and **fails closed** — a throw is treated as "approval
217
+ required".
207
218
 
208
219
  Rules:
209
220
 
@@ -39,8 +39,11 @@ Auth is powered by **Better Auth** with account-first design. Every new user cre
39
39
  Every app's `/mcp` endpoint is also a standard protected MCP
40
40
  resource. OAuth-capable hosts connect with the remote MCP URL only, receive a
41
41
  `WWW-Authenticate` challenge, discover `/.well-known/oauth-protected-resource`
42
- and `/.well-known/oauth-authorization-server`, dynamically register a public
43
- client, and complete authorization-code + PKCE at
42
+ and `/.well-known/oauth-authorization-server`, then use a validated Client ID
43
+ Metadata Document when the client id is an HTTPS URL or dynamically register a
44
+ public client otherwise. URL-like client ids never fall back to dynamic
45
+ registration when their metadata is missing or invalid. Clients complete
46
+ authorization-code + PKCE at
44
47
  `/mcp/oauth/authorize` / `/mcp/oauth/token`.
45
48
  Access tokens are audience-bound to the exact MCP URL and carry user/org
46
49
  identity plus `mcp:read`, `mcp:write`, `mcp:apps`, and/or `offline_access`;
@@ -44,6 +44,24 @@ can still use `npx @agent-native/core connect <url>`, which mints a per-user,
44
44
  scoped, revocable token from a logged-in browser session; no shared secret is
45
45
  copied.
46
46
 
47
+ The framework serves MCP `2026-07-28` natively over stateless, per-request HTTP
48
+ and keeps the established stateless path for 2025-era clients. New MCP client
49
+ code uses the split `@modelcontextprotocol/client` package with
50
+ `versionNegotiation: { mode: "auto" }`; do not pin a protocol header or
51
+ hand-send `initialize`. OAuth callback handlers preserve `state` and pass the
52
+ RFC 9207 `iss` parameter to the v2 SDK for validation before exchanging a
53
+ code. The authorization server accepts HTTPS Client ID Metadata Documents after
54
+ strict URL, SSRF, size, redirect, schema, and exact callback validation; URL-like
55
+ client ids never fall back to dynamic registration. Traditional non-URL clients
56
+ continue to use public-client dynamic registration.
57
+
58
+ Actions marked `needsApproval` use MCP 2026 `input_required` elicitation. The
59
+ approval is bound to the authenticated caller, exact action, exact arguments,
60
+ and expiry, and its durable grant is consumed atomically before the action runs.
61
+ If the host cannot complete the approval exchange, the predicate throws, the
62
+ state is invalid or replayed, or durable storage is unavailable, the action
63
+ fails closed.
64
+
47
65
  Claude and ChatGPT can cache custom connector tool/resource metadata. After
48
66
  changing MCP App metadata or the shared `embedApp()` shell, validate with a
49
67
  fresh tool call; if the host still behaves like the old descriptor, reconnect