@agent-native/core 0.95.0 → 0.96.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 (316) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +35 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +13 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +18 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +16 -0
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +17 -0
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +16 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +17 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +15 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +16 -0
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +11 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +12 -0
  13. package/corpus/core/docs/content/template-design.mdx +16 -0
  14. package/corpus/core/package.json +4 -2
  15. package/corpus/core/src/a2a/client.ts +11 -1
  16. package/corpus/core/src/agent/production-agent.ts +107 -27
  17. package/corpus/core/src/agent/thread-data-builder.ts +48 -22
  18. package/corpus/core/src/chat-threads/store.ts +20 -1
  19. package/corpus/core/src/cli/code-agent-executor.ts +1 -0
  20. package/corpus/core/src/cli/code-agent-runs.ts +14 -0
  21. package/corpus/core/src/client/AgentPanel.tsx +32 -25
  22. package/corpus/core/src/client/AssistantChat.tsx +286 -45
  23. package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
  24. package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
  25. package/corpus/core/src/client/chat/index.ts +6 -0
  26. package/corpus/core/src/client/chat/tool-call-display.tsx +38 -4
  27. package/corpus/core/src/client/chat/use-reconnect-reader-owner.ts +28 -0
  28. package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
  29. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
  30. package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
  31. package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
  32. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
  33. package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
  34. package/corpus/core/src/client/guided-questions.tsx +11 -2
  35. package/corpus/core/src/client/index.ts +11 -0
  36. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +6 -7
  37. package/corpus/core/src/client/sse-event-processor.ts +90 -21
  38. package/corpus/core/src/client/use-chat-threads.ts +10 -1
  39. package/corpus/core/src/client/voice-provider-status.ts +44 -0
  40. package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
  41. package/corpus/core/src/connections/catalog.ts +2 -2
  42. package/corpus/core/src/deploy/build.ts +12 -8
  43. package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
  44. package/corpus/core/src/integrations/adapters/slack.ts +74 -21
  45. package/corpus/core/src/integrations/catalog.ts +2 -2
  46. package/corpus/core/src/integrations/webhook-handler.ts +14 -0
  47. package/corpus/core/src/localization/default-messages.ts +6 -0
  48. package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
  49. package/corpus/core/src/observability/routes.ts +17 -0
  50. package/corpus/core/src/observability/store.ts +4 -1
  51. package/corpus/core/src/observability/traces.ts +41 -2
  52. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
  53. package/corpus/core/src/scripts/call-agent.ts +53 -3
  54. package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
  55. package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
  56. package/corpus/core/src/server/agent-chat-plugin.ts +41 -6
  57. package/corpus/core/src/server/auth.ts +1 -1
  58. package/corpus/core/src/server/core-routes-plugin.ts +15 -0
  59. package/corpus/core/src/server/credential-provider.ts +7 -3
  60. package/corpus/core/src/server/realtime-voice.ts +71 -24
  61. package/corpus/core/src/server/voice-providers-status.ts +4 -3
  62. package/corpus/core/src/shared/cache-control.ts +4 -4
  63. package/corpus/core/src/styles/agent-native.css +1 -0
  64. package/corpus/core/src/styles/chat-history-list.css +313 -0
  65. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  66. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  67. package/corpus/core/src/vite/client.ts +1 -0
  68. package/corpus/templates/analytics/AGENTS.md +7 -0
  69. package/corpus/templates/analytics/actions/bigquery.ts +26 -2
  70. package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
  71. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
  72. package/corpus/templates/analytics/app/global.css +10 -0
  73. package/corpus/templates/analytics/app/lib/data-sources.ts +5 -4
  74. package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
  75. package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
  76. package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
  77. package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
  78. package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
  79. package/corpus/templates/analytics/changelog/2026-07-10-slack-token-setup-marked-legacy.md +6 -0
  80. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
  81. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
  82. package/corpus/templates/analytics/server/lib/bigquery.ts +90 -14
  83. package/corpus/templates/analytics/server/lib/credential-keys.ts +6 -2
  84. package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
  85. package/corpus/templates/brain/changelog/2026-07-10-slack-backfill-token-marked-legacy.md +6 -0
  86. package/corpus/templates/brain/server/register-secrets.ts +2 -2
  87. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -1
  88. package/corpus/templates/clips/app/components/library/page-header.tsx +19 -3
  89. package/corpus/templates/clips/app/routes/_app.library._index.tsx +16 -12
  90. package/corpus/templates/clips/changelog/2026-07-10-new-recording-appears-once-when-the-library-sidebar-is-open.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-07-10-slack-token-fallback-guidance.md +6 -0
  92. package/corpus/templates/clips/server/register-secrets.ts +1 -1
  93. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +0 -66
  94. package/corpus/templates/content/app/routes/_app.local-files.tsx +7 -47
  95. package/corpus/templates/content/changelog/2026-07-10-local-folders-now-wait-for-an-explicit-pull-before-content-r.md +6 -0
  96. package/corpus/templates/content/package.json +1 -1
  97. package/corpus/templates/design/actions/generate-design.ts +128 -31
  98. package/corpus/templates/design/actions/present-design-variants.ts +37 -9
  99. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +9 -3
  100. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +32 -10
  101. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +2 -2
  102. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -1
  103. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -5
  104. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  105. package/corpus/templates/design/app/global.css +10 -0
  106. package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
  107. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  108. package/corpus/templates/design/app/i18n-data.ts +21 -11
  109. package/corpus/templates/design/changelog/2026-07-10-editor-panels-are-easier-to-distinguish-and-design-intake-qu.md +6 -0
  110. package/corpus/templates/design/changelog/2026-07-10-interact-mode-now-stays-on-the-full-canvas-matches-edit-visu.md +6 -0
  111. package/corpus/templates/design/changelog/2026-07-10-new-web-designs-open-at-desktop-size-and-adapt-across-mobile.md +6 -0
  112. package/corpus/templates/design/changelog/2026-07-10-tweaks-now-link-to-full-documentation.md +6 -0
  113. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  114. package/corpus/templates/mail/app/i18n/ar-SA.ts +3 -3
  115. package/corpus/templates/mail/app/i18n/de-DE.ts +3 -3
  116. package/corpus/templates/mail/app/i18n/en-US.ts +4 -3
  117. package/corpus/templates/mail/app/i18n/es-ES.ts +3 -3
  118. package/corpus/templates/mail/app/i18n/fr-FR.ts +3 -3
  119. package/corpus/templates/mail/app/i18n/hi-IN.ts +3 -3
  120. package/corpus/templates/mail/app/i18n/ja-JP.ts +3 -3
  121. package/corpus/templates/mail/app/i18n/ko-KR.ts +3 -3
  122. package/corpus/templates/mail/app/i18n/pt-BR.ts +3 -3
  123. package/corpus/templates/mail/app/i18n/zh-CN.ts +3 -3
  124. package/corpus/templates/mail/app/i18n/zh-TW.ts +3 -3
  125. package/corpus/templates/mail/changelog/2026-07-10-slack-intake-marked-legacy.md +6 -0
  126. package/corpus/templates/mail/server/lib/env-config.ts +5 -4
  127. package/corpus/templates/plan/package.json +1 -1
  128. package/dist/a2a/client.d.ts +7 -0
  129. package/dist/a2a/client.d.ts.map +1 -1
  130. package/dist/a2a/client.js +2 -1
  131. package/dist/a2a/client.js.map +1 -1
  132. package/dist/agent/production-agent.d.ts +6 -0
  133. package/dist/agent/production-agent.d.ts.map +1 -1
  134. package/dist/agent/production-agent.js +83 -26
  135. package/dist/agent/production-agent.js.map +1 -1
  136. package/dist/agent/thread-data-builder.d.ts +1 -0
  137. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  138. package/dist/agent/thread-data-builder.js +42 -23
  139. package/dist/agent/thread-data-builder.js.map +1 -1
  140. package/dist/chat-threads/store.d.ts +10 -0
  141. package/dist/chat-threads/store.d.ts.map +1 -1
  142. package/dist/chat-threads/store.js +6 -0
  143. package/dist/chat-threads/store.js.map +1 -1
  144. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  145. package/dist/cli/code-agent-executor.js +1 -0
  146. package/dist/cli/code-agent-executor.js.map +1 -1
  147. package/dist/cli/code-agent-runs.d.ts +12 -0
  148. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  149. package/dist/cli/code-agent-runs.js +1 -0
  150. package/dist/cli/code-agent-runs.js.map +1 -1
  151. package/dist/client/AgentPanel.d.ts.map +1 -1
  152. package/dist/client/AgentPanel.js +3 -3
  153. package/dist/client/AgentPanel.js.map +1 -1
  154. package/dist/client/AssistantChat.d.ts +14 -1
  155. package/dist/client/AssistantChat.d.ts.map +1 -1
  156. package/dist/client/AssistantChat.js +207 -32
  157. package/dist/client/AssistantChat.js.map +1 -1
  158. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  159. package/dist/client/MultiTabAssistantChat.js +83 -10
  160. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  161. package/dist/client/chat/ChatHistoryList.d.ts +88 -0
  162. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
  163. package/dist/client/chat/ChatHistoryList.js +83 -0
  164. package/dist/client/chat/ChatHistoryList.js.map +1 -0
  165. package/dist/client/chat/index.d.ts +1 -0
  166. package/dist/client/chat/index.d.ts.map +1 -1
  167. package/dist/client/chat/index.js +1 -0
  168. package/dist/client/chat/index.js.map +1 -1
  169. package/dist/client/chat/tool-call-display.d.ts +13 -1
  170. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  171. package/dist/client/chat/tool-call-display.js +11 -3
  172. package/dist/client/chat/tool-call-display.js.map +1 -1
  173. package/dist/client/chat/use-reconnect-reader-owner.d.ts +11 -0
  174. package/dist/client/chat/use-reconnect-reader-owner.d.ts.map +1 -0
  175. package/dist/client/chat/use-reconnect-reader-owner.js +21 -0
  176. package/dist/client/chat/use-reconnect-reader-owner.js.map +1 -0
  177. package/dist/client/code-agent-chat-adapter.d.ts +17 -1
  178. package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
  179. package/dist/client/code-agent-chat-adapter.js +47 -3
  180. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  181. package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
  182. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  183. package/dist/client/composer/RealtimeVoiceMode.js +5 -3
  184. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  185. package/dist/client/composer/TiptapComposer.d.ts +25 -2
  186. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  187. package/dist/client/composer/TiptapComposer.js +96 -5
  188. package/dist/client/composer/TiptapComposer.js.map +1 -1
  189. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  190. package/dist/client/composer/VoiceButton.js +21 -1
  191. package/dist/client/composer/VoiceButton.js.map +1 -1
  192. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  193. package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
  194. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  195. package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
  196. package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
  197. package/dist/client/conversation/code-agent-transcript.js +3 -5
  198. package/dist/client/conversation/code-agent-transcript.js.map +1 -1
  199. package/dist/client/guided-questions.d.ts.map +1 -1
  200. package/dist/client/guided-questions.js +7 -2
  201. package/dist/client/guided-questions.js.map +1 -1
  202. package/dist/client/index.d.ts +3 -1
  203. package/dist/client/index.d.ts.map +1 -1
  204. package/dist/client/index.js +3 -1
  205. package/dist/client/index.js.map +1 -1
  206. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  207. package/dist/client/integrations/IntegrationsPanel.js +5 -7
  208. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  209. package/dist/client/sse-event-processor.d.ts.map +1 -1
  210. package/dist/client/sse-event-processor.js +67 -20
  211. package/dist/client/sse-event-processor.js.map +1 -1
  212. package/dist/client/use-chat-threads.d.ts.map +1 -1
  213. package/dist/client/use-chat-threads.js +9 -1
  214. package/dist/client/use-chat-threads.js.map +1 -1
  215. package/dist/client/voice-provider-status.d.ts +10 -0
  216. package/dist/client/voice-provider-status.d.ts.map +1 -0
  217. package/dist/client/voice-provider-status.js +32 -0
  218. package/dist/client/voice-provider-status.js.map +1 -0
  219. package/dist/code-agents/transcript-normalizer.d.ts +29 -0
  220. package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
  221. package/dist/code-agents/transcript-normalizer.js +55 -0
  222. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  223. package/dist/collab/struct-routes.d.ts +1 -1
  224. package/dist/connections/catalog.d.ts +2 -2
  225. package/dist/connections/catalog.js +2 -2
  226. package/dist/connections/catalog.js.map +1 -1
  227. package/dist/deploy/build.d.ts.map +1 -1
  228. package/dist/deploy/build.js +12 -8
  229. package/dist/deploy/build.js.map +1 -1
  230. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  231. package/dist/integrations/a2a-continuation-processor.js +25 -5
  232. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  233. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  234. package/dist/integrations/adapters/slack.js +55 -21
  235. package/dist/integrations/adapters/slack.js.map +1 -1
  236. package/dist/integrations/catalog.js +2 -2
  237. package/dist/integrations/catalog.js.map +1 -1
  238. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  239. package/dist/integrations/webhook-handler.js +11 -0
  240. package/dist/integrations/webhook-handler.js.map +1 -1
  241. package/dist/localization/default-messages.d.ts +5 -0
  242. package/dist/localization/default-messages.d.ts.map +1 -1
  243. package/dist/localization/default-messages.js +5 -0
  244. package/dist/localization/default-messages.js.map +1 -1
  245. package/dist/observability/hosted-model-experiment.d.ts +39 -0
  246. package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
  247. package/dist/observability/hosted-model-experiment.js +90 -0
  248. package/dist/observability/hosted-model-experiment.js.map +1 -0
  249. package/dist/observability/routes.d.ts.map +1 -1
  250. package/dist/observability/routes.js +16 -0
  251. package/dist/observability/routes.js.map +1 -1
  252. package/dist/observability/store.d.ts.map +1 -1
  253. package/dist/observability/store.js +4 -1
  254. package/dist/observability/store.js.map +1 -1
  255. package/dist/observability/traces.d.ts +6 -0
  256. package/dist/observability/traces.d.ts.map +1 -1
  257. package/dist/observability/traces.js +28 -2
  258. package/dist/observability/traces.js.map +1 -1
  259. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  260. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  261. package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
  262. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  263. package/dist/scripts/call-agent.d.ts.map +1 -1
  264. package/dist/scripts/call-agent.js +43 -3
  265. package/dist/scripts/call-agent.js.map +1 -1
  266. package/dist/scripts/chat/search-chats.d.ts +1 -1
  267. package/dist/scripts/chat/search-chats.d.ts.map +1 -1
  268. package/dist/scripts/chat/search-chats.js +11 -8
  269. package/dist/scripts/chat/search-chats.js.map +1 -1
  270. package/dist/secrets/register-framework-secrets.js +1 -1
  271. package/dist/secrets/register-framework-secrets.js.map +1 -1
  272. package/dist/server/agent-chat-plugin.d.ts +11 -0
  273. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  274. package/dist/server/agent-chat-plugin.js +27 -6
  275. package/dist/server/agent-chat-plugin.js.map +1 -1
  276. package/dist/server/auth.js +1 -1
  277. package/dist/server/auth.js.map +1 -1
  278. package/dist/server/core-routes-plugin.d.ts +3 -0
  279. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  280. package/dist/server/core-routes-plugin.js +13 -0
  281. package/dist/server/core-routes-plugin.js.map +1 -1
  282. package/dist/server/credential-provider.d.ts +5 -3
  283. package/dist/server/credential-provider.d.ts.map +1 -1
  284. package/dist/server/credential-provider.js +7 -3
  285. package/dist/server/credential-provider.js.map +1 -1
  286. package/dist/server/realtime-voice.d.ts.map +1 -1
  287. package/dist/server/realtime-voice.js +55 -21
  288. package/dist/server/realtime-voice.js.map +1 -1
  289. package/dist/server/voice-providers-status.d.ts.map +1 -1
  290. package/dist/server/voice-providers-status.js +2 -3
  291. package/dist/server/voice-providers-status.js.map +1 -1
  292. package/dist/shared/cache-control.d.ts +6 -6
  293. package/dist/shared/cache-control.d.ts.map +1 -1
  294. package/dist/shared/cache-control.js +3 -3
  295. package/dist/shared/cache-control.js.map +1 -1
  296. package/dist/styles/agent-native.css +1 -0
  297. package/dist/styles/chat-history-list.css +313 -0
  298. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  299. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  300. package/dist/vite/client.d.ts.map +1 -1
  301. package/dist/vite/client.js +1 -0
  302. package/dist/vite/client.js.map +1 -1
  303. package/docs/content/locales/ar-SA/template-design.mdx +13 -0
  304. package/docs/content/locales/de-DE/template-design.mdx +18 -0
  305. package/docs/content/locales/es-ES/template-design.mdx +16 -0
  306. package/docs/content/locales/fr-FR/template-design.mdx +17 -0
  307. package/docs/content/locales/hi-IN/template-design.mdx +16 -0
  308. package/docs/content/locales/ja-JP/template-design.mdx +17 -0
  309. package/docs/content/locales/ko-KR/template-design.mdx +15 -0
  310. package/docs/content/locales/pt-BR/template-design.mdx +16 -0
  311. package/docs/content/locales/zh-CN/template-design.mdx +11 -0
  312. package/docs/content/locales/zh-TW/template-design.mdx +12 -0
  313. package/docs/content/template-design.mdx +16 -0
  314. package/package.json +4 -2
  315. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  316. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
@@ -216,6 +216,12 @@ const messages = {
216
216
  promptTitle: "Talk to your app",
217
217
  promptDescription:
218
218
  "Voice mode keeps listening while the agent navigates and takes actions.",
219
+ setupTitle: "Set up voice mode",
220
+ setupDescription:
221
+ "Connect Builder to use managed voice with credits, or use your own OpenAI API key.",
222
+ connectBuilder: "Connect Builder",
223
+ useOpenAiKey: "Use OpenAI API key",
224
+ startWithOpenAiKey: "Start with OpenAI key",
219
225
  start: "Start voice mode",
220
226
  keepDictating: "Keep dictating",
221
227
  showChat: "Show chat",
@@ -0,0 +1,118 @@
1
+ export const HOSTED_DEFAULT_MODEL_EXPERIMENT_ID =
2
+ "hosted-default-model-sonnet-5-vs-gpt-5-6-luna-2026-07";
3
+
4
+ export const HOSTED_DEFAULT_MODEL_CONTROL = {
5
+ id: "sonnet-5",
6
+ model: "claude-sonnet-5",
7
+ weight: 80,
8
+ } as const;
9
+
10
+ export const HOSTED_DEFAULT_MODEL_TREATMENT = {
11
+ id: "gpt-5-6-luna",
12
+ model: "gpt-5-6-luna",
13
+ weight: 20,
14
+ } as const;
15
+
16
+ export interface HostedDefaultModelExperimentAssignment {
17
+ experimentId: string;
18
+ variantId: string;
19
+ }
20
+
21
+ export interface HostedDefaultModelExperimentResolution {
22
+ model: string;
23
+ assignment: HostedDefaultModelExperimentAssignment;
24
+ }
25
+
26
+ type HostedExperimentEnv = Record<string, string | undefined>;
27
+
28
+ const FIRST_PARTY_HOST_SUFFIX = ".agent-native.com";
29
+ const ENABLED_VALUES = new Set(["1", "true", "on", "yes"]);
30
+ const DISABLED_VALUES = new Set(["0", "false", "off", "no"]);
31
+
32
+ function hostnameFromUrl(value: string | undefined): string | null {
33
+ const trimmed = value?.trim();
34
+ if (!trimmed) return null;
35
+ try {
36
+ const url = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)
37
+ ? new URL(trimmed)
38
+ : new URL(`https://${trimmed}`);
39
+ return url.hostname.toLowerCase();
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * The rollout is automatic only on first-party hosted app domains. Operators
47
+ * can explicitly enable or disable it for preview and emergency rollback.
48
+ */
49
+ export function isHostedDefaultModelExperimentEnabled(
50
+ env: HostedExperimentEnv = process.env,
51
+ ): boolean {
52
+ const override =
53
+ env.AGENT_NATIVE_HOSTED_MODEL_EXPERIMENT?.trim().toLowerCase();
54
+ if (override && DISABLED_VALUES.has(override)) return false;
55
+ if (override && ENABLED_VALUES.has(override)) return true;
56
+
57
+ const hostnames = [
58
+ env.APP_URL,
59
+ env.BETTER_AUTH_URL,
60
+ env.URL,
61
+ env.DEPLOY_URL,
62
+ env.VERCEL_PROJECT_PRODUCTION_URL,
63
+ env.VERCEL_URL,
64
+ ]
65
+ .map(hostnameFromUrl)
66
+ .filter((hostname): hostname is string => Boolean(hostname));
67
+
68
+ return hostnames.some(
69
+ (hostname) =>
70
+ hostname === "agent-native.com" ||
71
+ hostname.endsWith(FIRST_PARTY_HOST_SUFFIX),
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Stable cross-app bucket: the same authenticated user receives the same
77
+ * variant in every hosted template for the life of this experiment id.
78
+ */
79
+ export function hostedDefaultModelExperimentBucket(userId: string): number {
80
+ const input = `${HOSTED_DEFAULT_MODEL_EXPERIMENT_ID}:${userId.trim().toLowerCase()}`;
81
+ let hash = 0x811c9dc5;
82
+ for (let index = 0; index < input.length; index++) {
83
+ hash ^= input.charCodeAt(index);
84
+ hash = Math.imul(hash, 0x01000193);
85
+ }
86
+ return (hash >>> 0) % 100;
87
+ }
88
+
89
+ export function resolveHostedDefaultModelExperiment(args: {
90
+ userId: string | null | undefined;
91
+ engineName: string;
92
+ isDefaultModelSelection: boolean;
93
+ supportedModels?: readonly string[];
94
+ env?: HostedExperimentEnv;
95
+ }): HostedDefaultModelExperimentResolution | null {
96
+ const userId = args.userId?.trim();
97
+ if (!userId) return null;
98
+ if (args.engineName !== "builder") return null;
99
+ if (!args.isDefaultModelSelection) return null;
100
+ if (!isHostedDefaultModelExperimentEnabled(args.env)) return null;
101
+
102
+ const variant =
103
+ hostedDefaultModelExperimentBucket(userId) <
104
+ HOSTED_DEFAULT_MODEL_TREATMENT.weight
105
+ ? HOSTED_DEFAULT_MODEL_TREATMENT
106
+ : HOSTED_DEFAULT_MODEL_CONTROL;
107
+ if (args.supportedModels && !args.supportedModels.includes(variant.model)) {
108
+ return null;
109
+ }
110
+
111
+ return {
112
+ model: variant.model,
113
+ assignment: {
114
+ experimentId: HOSTED_DEFAULT_MODEL_EXPERIMENT_ID,
115
+ variantId: variant.id,
116
+ },
117
+ };
118
+ }
@@ -69,6 +69,18 @@ async function resolveOwner(event: H3Event): Promise<string> {
69
69
  return session.email;
70
70
  }
71
71
 
72
+ function canManageExperiments(ownerEmail: string): boolean {
73
+ // Local development keeps the built-in dashboard usable without additional
74
+ // setup. Hosted deployments fail closed unless the operator supplies an
75
+ // explicit allowlist, because experiments affect every user in the app.
76
+ if (process.env.NODE_ENV !== "production") return true;
77
+ const admins = (process.env.AGENT_NATIVE_EXPERIMENT_ADMIN_EMAILS ?? "")
78
+ .split(",")
79
+ .map((email) => email.trim().toLowerCase())
80
+ .filter(Boolean);
81
+ return admins.includes(ownerEmail.trim().toLowerCase());
82
+ }
83
+
72
84
  function parseSince(q: Record<string, any>): number {
73
85
  const raw = q.since;
74
86
  if (typeof raw === "string" && raw.length > 0) {
@@ -236,6 +248,11 @@ export function createObservabilityHandler() {
236
248
  return getEvalStats(parseSince(q), { userId: owner });
237
249
  }
238
250
 
251
+ if (parts[0] === "experiments" && !canManageExperiments(owner)) {
252
+ setResponseStatus(event, 403);
253
+ return { error: "Experiment administrator access required" };
254
+ }
255
+
239
256
  // POST /experiments — create experiment. Records the calling user as
240
257
  // the owner so subsequent PUT / POST results require the same caller.
241
258
  if (method === "POST" && parts.length === 1 && parts[0] === "experiments") {
@@ -972,7 +972,10 @@ export async function updateExperiment(
972
972
  sets.push("status = ?");
973
973
  args.push(updates.status);
974
974
  if (updates.status === "running" && !updates.endedAt) {
975
- sets.push("started_at = ?");
975
+ // Preserve the original exposure window when a paused experiment
976
+ // resumes; resetting it would silently discard the first run period from
977
+ // the results.
978
+ sets.push("started_at = COALESCE(started_at, ?)");
976
979
  args.push(Date.now());
977
980
  }
978
981
  }
@@ -100,6 +100,11 @@ function emitLlmGenerationTrackingEvent(args: {
100
100
  successfulTools: number;
101
101
  failedTools: number;
102
102
  createdAt: number;
103
+ experimentAssignments?: Array<{
104
+ experimentId: string;
105
+ variantId: string;
106
+ }>;
107
+ modelSelectionSource?: string;
103
108
  }): void {
104
109
  const provider = llmProviderFromEngine(args.engineName, args.model);
105
110
  const costUsd = costUsdFromCenticents(args.costCentsX100);
@@ -126,6 +131,7 @@ function emitLlmGenerationTrackingEvent(args: {
126
131
  tool_calls: args.toolCalls,
127
132
  successful_tools: args.successfulTools,
128
133
  failed_tools: args.failedTools,
134
+ model_selection_source: args.modelSelectionSource,
129
135
  created_at: new Date(args.createdAt).toISOString(),
130
136
  created_at_ms: args.createdAt,
131
137
  $ai_trace_id: args.runId,
@@ -145,6 +151,18 @@ function emitLlmGenerationTrackingEvent(args: {
145
151
  $ai_request_count: 1,
146
152
  $ai_total_cost_usd: costUsd,
147
153
  };
154
+ if (args.experimentAssignments?.length) {
155
+ properties.experiment_ids = args.experimentAssignments
156
+ .map((assignment) => assignment.experimentId)
157
+ .join(",");
158
+ properties.experiment_variants = args.experimentAssignments
159
+ .map((assignment) => assignment.variantId)
160
+ .join(",");
161
+ if (args.experimentAssignments.length === 1) {
162
+ properties.experiment_id = args.experimentAssignments[0].experimentId;
163
+ properties.experiment_variant = args.experimentAssignments[0].variantId;
164
+ }
165
+ }
148
166
  if (error) properties.error_message = error;
149
167
 
150
168
  for (const key of Object.keys(properties)) {
@@ -245,6 +263,12 @@ export async function instrumentAgentLoop(opts: {
245
263
  * reads. */
246
264
  userId: string | null;
247
265
  config: ObservabilityConfig;
266
+ metadata?: Record<string, unknown> | null;
267
+ experimentAssignments?: Array<{
268
+ experimentId: string;
269
+ variantId: string;
270
+ }>;
271
+ modelSelectionSource?: string;
248
272
  classifyError?: (error: unknown) =>
249
273
  | {
250
274
  status?: "success" | "error";
@@ -268,6 +292,15 @@ export async function instrumentAgentLoop(opts: {
268
292
  "agent.thread_id": threadId ?? undefined,
269
293
  "agent.user_id": userId ?? undefined,
270
294
  "agent.model": loopOpts.model,
295
+ "agent.model_selection_source": opts.modelSelectionSource,
296
+ "agent.experiment_id":
297
+ opts.experimentAssignments?.length === 1
298
+ ? opts.experimentAssignments[0].experimentId
299
+ : undefined,
300
+ "agent.experiment_variant":
301
+ opts.experimentAssignments?.length === 1
302
+ ? opts.experimentAssignments[0].variantId
303
+ : undefined,
271
304
  });
272
305
 
273
306
  const spans: TraceSpan[] = [];
@@ -424,7 +457,7 @@ export async function instrumentAgentLoop(opts: {
424
457
  let usage: AgentLoopUsage | undefined;
425
458
  let runStatus: "success" | "error" = "success";
426
459
  let errorMessage: string | null = null;
427
- let runMetadata: Record<string, unknown> | null = null;
460
+ let runMetadata: Record<string, unknown> | null = opts.metadata ?? null;
428
461
  try {
429
462
  usage = await runAgentLoop({ ...loopOpts, send: instrumentedSend });
430
463
  } catch (err: any) {
@@ -434,7 +467,11 @@ export async function instrumentAgentLoop(opts: {
434
467
  classification?.errorMessage === undefined
435
468
  ? (err?.message ?? String(err))
436
469
  : classification.errorMessage;
437
- runMetadata = classification?.metadata ?? null;
470
+ const errorMetadata = classification?.metadata ?? null;
471
+ runMetadata =
472
+ runMetadata || errorMetadata
473
+ ? { ...(runMetadata ?? {}), ...(errorMetadata ?? {}) }
474
+ : null;
438
475
  throw err;
439
476
  } finally {
440
477
  const runEnd = Date.now();
@@ -501,6 +538,8 @@ export async function instrumentAgentLoop(opts: {
501
538
  successfulTools,
502
539
  failedTools,
503
540
  createdAt: runStart,
541
+ experimentAssignments: opts.experimentAssignments,
542
+ modelSelectionSource: opts.modelSelectionSource,
504
543
  });
505
544
  }
506
545
 
@@ -15,6 +15,8 @@ import {
15
15
  normalizeModelForEngine,
16
16
  } from "../../agent/engine/index.js";
17
17
  import type { ActionTool } from "../../agent/types.js";
18
+ import { resolveHostedDefaultModelExperiment } from "../../observability/hosted-model-experiment.js";
19
+ import { getRequestUserEmail } from "../../server/request-context.js";
18
20
  import { getSetting } from "../../settings/index.js";
19
21
 
20
22
  export const tool: ActionTool = {
@@ -82,13 +84,23 @@ export async function run(args: Record<string, string> = {}): Promise<string> {
82
84
  ? current?.model
83
85
  : undefined;
84
86
  const currentEngineName = currentEntry?.name ?? "anthropic";
85
- const currentModel =
87
+ let currentModel =
86
88
  currentEntry && !envUnavailable
87
89
  ? normalizeModelForEngine(
88
90
  currentEntry,
89
91
  currentModelCandidate ?? currentEntry.defaultModel,
90
92
  )
91
93
  : (currentModelCandidate ?? DEFAULT_MODEL);
94
+ const hostedExperiment =
95
+ currentEntry && !currentModelCandidate
96
+ ? resolveHostedDefaultModelExperiment({
97
+ userId: getRequestUserEmail(),
98
+ engineName: currentEntry.name,
99
+ isDefaultModelSelection: true,
100
+ supportedModels: currentEntry.supportedModels,
101
+ })
102
+ : null;
103
+ if (hostedExperiment) currentModel = hostedExperiment.model;
92
104
 
93
105
  const result = {
94
106
  engines: engines.map((e) => ({
@@ -107,6 +119,12 @@ export async function run(args: Record<string, string> = {}): Promise<string> {
107
119
  : {
108
120
  engine: currentEngineName,
109
121
  model: currentModel,
122
+ ...(hostedExperiment
123
+ ? {
124
+ modelSelectionSource: "experiment",
125
+ experimentAssignment: hostedExperiment.assignment,
126
+ }
127
+ : {}),
110
128
  },
111
129
  };
112
130
 
@@ -321,7 +321,14 @@ export async function run(
321
321
  orgDomain: callerOrgDomain,
322
322
  orgSecret: callerOrgSecret,
323
323
  onUpdate: onRemotePollUpdate,
324
- ...(callTimeoutMs ? { timeoutMs: callTimeoutMs } : {}),
324
+ ...(callTimeoutMs
325
+ ? {
326
+ timeoutMs: callTimeoutMs,
327
+ // Integration callers must keep the timeout task id so the
328
+ // catch below can enqueue durable continuation polling.
329
+ returnRecoverableArtifactsOnTimeout: false,
330
+ }
331
+ : {}),
325
332
  });
326
333
  responseText =
327
334
  formatDownstreamLlmCredentialFailure(agent.name, responseText) ??
@@ -348,8 +355,23 @@ export async function run(
348
355
  `The ${agent.name} agent accepted this delegated subtask and will post its own final result to the originating integration thread automatically. ` +
349
356
  `Do not call ${agent.name} again for this same subtask. Continue any other requested work, then answer with the completed results you have; if needed, mention that ${agent.name} is posting its result separately.`;
350
357
  } else {
351
- const reason = pollErr?.message ?? "unknown error";
352
- responseText = `The ${agent.name} agent is taking longer than expected and didn't reply in time. (${reason})`;
358
+ // The normal integration path must preserve the timeout task id so
359
+ // it can enqueue a durable continuation. If that enqueue fails,
360
+ // do not hide receiver-verified artifacts that were already
361
+ // returned with the last poll; this mirrors callAgent's default
362
+ // timeout behavior without treating arbitrary remote status text
363
+ // as a completed response.
364
+ const recoverableArtifactText =
365
+ extractRecoverableTimeoutArtifactText(pollErr);
366
+ if (recoverableArtifactText) {
367
+ responseText = expandRelativeUrls(
368
+ recoverableArtifactText,
369
+ agent.url,
370
+ );
371
+ } else {
372
+ const reason = pollErr?.message ?? "unknown error";
373
+ responseText = `The ${agent.name} agent is taking longer than expected and didn't reply in time. (${reason})`;
374
+ }
353
375
  }
354
376
  } else {
355
377
  const reason = pollErr?.message ?? "unknown error";
@@ -490,6 +512,34 @@ function getA2ATaskTimeoutTaskId(err: unknown): string | null {
490
512
  return match?.[1] ?? null;
491
513
  }
492
514
 
515
+ /**
516
+ * Mirrors the A2A client's default timeout recovery for the exceptional case
517
+ * where an integration cannot enqueue a durable continuation. Only an
518
+ * explicitly receiver-marked task message is safe to surface as a completed
519
+ * partial result; ordinary working-state text remains a timeout failure.
520
+ */
521
+ function extractRecoverableTimeoutArtifactText(err: unknown): string {
522
+ const candidate = err as
523
+ | { lastTask?: Task | unknown; name?: unknown }
524
+ | null
525
+ | undefined;
526
+ const lastTask =
527
+ err instanceof A2ATaskTimeoutError
528
+ ? err.lastTask
529
+ : candidate?.name === "A2ATaskTimeoutError"
530
+ ? (candidate.lastTask as Task | undefined)
531
+ : undefined;
532
+ const message = lastTask?.status?.message;
533
+ if (!message?.metadata?.agentNativeRecoverableArtifacts) return "";
534
+
535
+ return message.parts
536
+ .filter(
537
+ (part): part is { type: "text"; text: string } => part.type === "text",
538
+ )
539
+ .map((part) => part.text)
540
+ .join("\n");
541
+ }
542
+
493
543
  async function formatExistingIntegrationContinuationIfRetry(
494
544
  agent: {
495
545
  name: string;
@@ -4,7 +4,7 @@
4
4
  * Search or list past agent chat threads.
5
5
  *
6
6
  * Usage:
7
- * pnpm action search-chats [--query "search term"] [--limit N] [--format json]
7
+ * pnpm action search-chats [--query "search term"] [--limit N] [--format json] [--includeArchived]
8
8
  */
9
9
 
10
10
  import { searchThreads, listThreads } from "../../chat-threads/store.js";
@@ -40,15 +40,17 @@ export default async function searchChats(args: string[]): Promise<void> {
40
40
  console.log(`Usage: pnpm action search-chats [options]
41
41
 
42
42
  Options:
43
- --query <text> Search chats by title, preview, or content
44
- --limit N Max results (default: 20)
45
- --format json Output as JSON
46
- --help Show this help message
43
+ --query <text> Search chats by title, preview, or content
44
+ --limit N Max results (default: 20)
45
+ --format json Output as JSON
46
+ --includeArchived Also include archived chats (excluded by default)
47
+ --help Show this help message
47
48
 
48
49
  Examples:
49
50
  pnpm action search-chats --query "email setup"
50
51
  pnpm action search-chats --limit 5
51
- pnpm action search-chats --format json`);
52
+ pnpm action search-chats --format json
53
+ pnpm action search-chats --includeArchived`);
52
54
  return;
53
55
  }
54
56
 
@@ -56,10 +58,11 @@ Examples:
56
58
  const limit = parsed.limit ? parseInt(parsed.limit, 10) : 20;
57
59
  if (isNaN(limit) || limit < 1) fail("--limit must be a positive integer");
58
60
 
61
+ const includeArchived = parsed.includeArchived === "true";
59
62
  const query = parsed.query;
60
63
  const threads = query
61
- ? await searchThreads(owner, query, limit)
62
- : await listThreads(owner, limit, 0);
64
+ ? await searchThreads(owner, query, limit, { includeArchived })
65
+ : await listThreads(owner, { limit, offset: 0, includeArchived });
63
66
 
64
67
  if (parsed.format === "json") {
65
68
  console.log(
@@ -22,7 +22,7 @@ export function registerFrameworkSecrets(): void {
22
22
  key: "OPENAI_API_KEY",
23
23
  label: "OpenAI API key",
24
24
  description:
25
- "Enables realtime voice conversations with your app and optional OpenAI transcription.",
25
+ "Optional fallback for realtime voice when Builder is not connected, and for OpenAI transcription.",
26
26
  docsUrl: "https://platform.openai.com/api-keys",
27
27
  scope: "user",
28
28
  kind: "api-key",
@@ -54,11 +54,16 @@ import {
54
54
  listAgentEngines,
55
55
  registerBuiltinEngines,
56
56
  } from "../agent/engine/index.js";
57
- import type { AgentEngine, EngineMessage } from "../agent/engine/types.js";
57
+ import type {
58
+ AgentEngine,
59
+ EngineMessage,
60
+ EngineTool,
61
+ } from "../agent/engine/types.js";
58
62
  import {
59
63
  createProductionAgentHandler,
60
64
  actionsToEngineTools,
61
65
  executeAgentToolCall,
66
+ filterInitialEngineTools,
62
67
  getActiveRunForThreadAsync,
63
68
  abortRunDurably,
64
69
  subscribeToRun,
@@ -1903,6 +1908,11 @@ async function createChatScriptEntries(): Promise<Record<string, ActionEntry>> {
1903
1908
  description: "Output format",
1904
1909
  enum: ["json", "text"],
1905
1910
  },
1911
+ includeArchived: {
1912
+ type: "boolean",
1913
+ description:
1914
+ "Also include archived chats in the results. Archived chats are excluded by default.",
1915
+ },
1906
1916
  },
1907
1917
  },
1908
1918
  },
@@ -1953,6 +1963,11 @@ async function createChatScriptEntries(): Promise<Record<string, ActionEntry>> {
1953
1963
  description: "(search) Output format",
1954
1964
  enum: ["json", "text"],
1955
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
+ },
1956
1971
  id: {
1957
1972
  type: "string",
1958
1973
  description:
@@ -2940,6 +2955,22 @@ export function runA2AAgentLoop(
2940
2955
  );
2941
2956
  }
2942
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
+
2943
2974
  /**
2944
2975
  * Verbose framework sections returned by the `get-framework-context` tool.
2945
2976
  * Keyed by topic so the agent can request specific sections.
@@ -2971,9 +3002,9 @@ Use for charts, visualizations, previews. Don't use for simple text/tables or ex
2971
3002
  "chat-history": `### Chat History
2972
3003
 
2973
3004
  You can search and restore previous chat conversations using \`chat-history\`:
2974
- - \`chat-history\` (action: "search") — Search or list past chat threads by keyword
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.
2975
3006
  - \`chat-history\` (action: "open") — Open a chat thread in the UI as a new tab and focus it
2976
- - \`chat-history\` (actions: "rename", "pin", "unpin", "archive") — Organize a known chat thread by ID
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.
2977
3008
 
2978
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.`,
2979
3010
 
@@ -5077,7 +5108,10 @@ export function createAgentChatPlugin(
5077
5108
  },
5078
5109
  );
5079
5110
 
5080
- const a2aTools = actionsToEngineTools(a2aActions);
5111
+ const a2aToolSurface = createA2AEngineToolSurface(
5112
+ actionsToEngineTools(a2aActions),
5113
+ options?.initialToolNames,
5114
+ );
5081
5115
 
5082
5116
  // Precise current time rides the user message (not the cached
5083
5117
  // system-prompt prefix) — same pattern as the interactive handler.
@@ -5098,7 +5132,7 @@ export function createAgentChatPlugin(
5098
5132
  const controller = new AbortController();
5099
5133
 
5100
5134
  console.log(
5101
- `[A2A] Starting agent loop: ${a2aTools.length} tools, prompt ${systemPrompt.length} chars`,
5135
+ `[A2A] Starting agent loop: ${a2aToolSurface.tools.length}/${a2aToolSurface.availableTools.length} initial tools, prompt ${systemPrompt.length} chars`,
5102
5136
  );
5103
5137
 
5104
5138
  await runA2AAgentLoop(
@@ -5106,7 +5140,8 @@ export function createAgentChatPlugin(
5106
5140
  engine: a2aEngine,
5107
5141
  model,
5108
5142
  systemPrompt,
5109
- tools: a2aTools,
5143
+ tools: a2aToolSurface.tools,
5144
+ availableTools: a2aToolSurface.availableTools,
5110
5145
  messages: a2aMessages,
5111
5146
  actions: a2aActions,
5112
5147
  send: (event) => {
@@ -1627,7 +1627,7 @@ function loginHtmlResponse(loginHtml: string, event: H3Event): Response {
1627
1627
  headers: {
1628
1628
  "Content-Type": "text/html; charset=utf-8",
1629
1629
  // The sign-in document is part of the public server shell. Keep it on the
1630
- // same short-fresh/long-SWR CDN policy as React Router SSR so hosted
1630
+ // same long-fresh/long-SWR CDN policy as React Router SSR so hosted
1631
1631
  // template roots do not invoke origin just to render anonymous login UI.
1632
1632
  // The login HTML is env-INDEPENDENT (a Google-only app always renders a
1633
1633
  // working button), so a cached copy is never "wrong" — never downgrade
@@ -298,6 +298,7 @@ const BUILDER_WAITLIST_USE_CASES = new Set([
298
298
  BUILDER_WAITLIST_DEFAULT_USE_CASE,
299
299
  "design_publish_app",
300
300
  "docs_build_online_waitlist",
301
+ "docs_edit_online_waitlist",
301
302
  ]);
302
303
  const BUILDER_WAITLIST_FORM_TIMEOUT_MS = 8000;
303
304
  const BUILDER_WAITLIST_TEXT_LIMIT = 4000;
@@ -320,6 +321,7 @@ export interface BuilderWaitlistBody {
320
321
  appUrl?: unknown;
321
322
  pageUrl?: unknown;
322
323
  source?: unknown;
324
+ template?: unknown;
323
325
  useCase?: unknown;
324
326
  }
325
327
 
@@ -352,6 +354,13 @@ function normalizeBuilderWaitlistUseCase(value: unknown): string {
352
354
  : BUILDER_WAITLIST_DEFAULT_USE_CASE;
353
355
  }
354
356
 
357
+ function normalizeBuilderWaitlistTemplate(value: unknown): string | undefined {
358
+ const template = cleanBuilderWaitlistText(value, 100);
359
+ return template && /^[a-z0-9][a-z0-9-]{0,99}$/.test(template)
360
+ ? template
361
+ : undefined;
362
+ }
363
+
355
364
  function isValidWaitlistEmail(email: string): boolean {
356
365
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
357
366
  }
@@ -506,6 +515,7 @@ export function buildBuilderWaitlistFormPayload(
506
515
  getOrigin(event);
507
516
  const source =
508
517
  cleanBuilderWaitlistText(body.source, 100) ?? BUILDER_WAITLIST_FORM_SOURCE;
518
+ const template = normalizeBuilderWaitlistTemplate(body.template);
509
519
  const useCase = normalizeBuilderWaitlistUseCase(body.useCase);
510
520
 
511
521
  return {
@@ -515,6 +525,7 @@ export function buildBuilderWaitlistFormPayload(
515
525
  appUrl,
516
526
  prompt: cleanBuilderWaitlistText(body.prompt),
517
527
  source,
528
+ template,
518
529
  useCase,
519
530
  },
520
531
  _hp: "",
@@ -522,6 +533,7 @@ export function buildBuilderWaitlistFormPayload(
522
533
  submitterEmail: sessionEmail,
523
534
  pageUrl: appUrl,
524
535
  source,
536
+ template,
525
537
  useCase,
526
538
  },
527
539
  };
@@ -2021,6 +2033,7 @@ export function createCoreRoutesPlugin(
2021
2033
  body,
2022
2034
  );
2023
2035
  const waitlistSource = waitlistPayload.data.source;
2036
+ const waitlistTemplate = waitlistPayload.data.template;
2024
2037
  const waitlistUseCase = waitlistPayload.data.useCase;
2025
2038
  let formSubmission: { submitted: boolean; formId?: string };
2026
2039
  try {
@@ -2039,6 +2052,7 @@ export function createCoreRoutesPlugin(
2039
2052
  err instanceof Error ? err.message : "unknown_waitlist_error",
2040
2053
  source: waitlistSource,
2041
2054
  stage: "waitlist",
2055
+ template: waitlistTemplate ?? null,
2042
2056
  useCase: waitlistUseCase,
2043
2057
  },
2044
2058
  );
@@ -2057,6 +2071,7 @@ export function createCoreRoutesPlugin(
2057
2071
  formSubmitted: formSubmission.submitted,
2058
2072
  source: waitlistSource,
2059
2073
  stage: "waitlist",
2074
+ template: waitlistTemplate ?? null,
2060
2075
  useCase: waitlistUseCase,
2061
2076
  },
2062
2077
  );
@@ -78,6 +78,8 @@ const APP_PROVIDED_DEPLOY_CREDENTIAL_KEYS = new Set([
78
78
  "OPENAI_API_KEY",
79
79
  "OPENAI_BASE_URL",
80
80
  "OPENROUTER_API_KEY",
81
+ "GOOGLE_CLIENT_ID",
82
+ "GOOGLE_CLIENT_SECRET",
81
83
  "GOOGLE_GENERATIVE_AI_API_KEY",
82
84
  "GROQ_API_KEY",
83
85
  "MISTRAL_API_KEY",
@@ -93,9 +95,11 @@ function isAppProvidedDeployCredentialKey(key: string | undefined): boolean {
93
95
  * single-tenant contexts. In hosted production with a shared database, every
94
96
  * signed-in user needs their own user/org/workspace credential for
95
97
  * identity-bearing provider keys so one deploy key does not silently
96
- * impersonate another tenant. App-provided LLM provider keys are different:
97
- * they intentionally let the app developer pay for model usage for users of
98
- * that deployed app, so key-aware callers may use those env vars.
98
+ * impersonate another tenant. App-provided service credentials are different:
99
+ * they configure the deployed app itself rather than identifying a user. This
100
+ * includes LLM keys that let the app developer pay for model usage and OAuth
101
+ * client credentials whose per-user identity remains in scoped OAuth tokens.
102
+ * Key-aware callers may use those env vars.
99
103
  *
100
104
  * @deprecated Use `canUseDeployCredentialFallbackForRequest()` for generic
101
105
  * provider secrets. This stricter helper remains for legacy call sites with