@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
@@ -1762,6 +1762,12 @@ export type AgentLoopFinalResponseGuardResult =
1762
1762
  | {
1763
1763
  retryMessage: string;
1764
1764
  fallbackMessage?: string;
1765
+ /**
1766
+ * Number of rejected text-only answers the model may correct before the
1767
+ * fallback is emitted. Defaults to one and is capped to keep a broken
1768
+ * guard/model combination from looping indefinitely.
1769
+ */
1770
+ maxRetries?: number;
1765
1771
  };
1766
1772
 
1767
1773
  export type AgentLoopFinalResponseGuard = (
@@ -3641,7 +3647,11 @@ export async function runAgentLoop(opts: {
3641
3647
  typeof guard === "string" ? guard : guard.retryMessage;
3642
3648
  const fallbackMessage =
3643
3649
  typeof guard === "string" ? guard : guard.fallbackMessage;
3644
- if (finalGuardRetries < 1) {
3650
+ const maxGuardRetries =
3651
+ typeof guard === "string"
3652
+ ? 1
3653
+ : Math.max(0, Math.min(3, Math.trunc(guard.maxRetries ?? 1)));
3654
+ if (finalGuardRetries < maxGuardRetries) {
3645
3655
  finalGuardRetries += 1;
3646
3656
  send({ type: "clear" });
3647
3657
  messages.push({
@@ -3776,11 +3786,13 @@ export async function runAgentLoop(opts: {
3776
3786
  const result = sourceSweepGuard.message;
3777
3787
  send({
3778
3788
  type: "tool_start",
3789
+ id: toolCall.id,
3779
3790
  tool: toolCall.name,
3780
3791
  input: toolCall.input as Record<string, string>,
3781
3792
  });
3782
3793
  send({
3783
3794
  type: "tool_done",
3795
+ id: toolCall.id,
3784
3796
  tool: toolCall.name,
3785
3797
  input: toolCall.input as Record<string, unknown>,
3786
3798
  result,
@@ -3800,11 +3812,13 @@ export async function runAgentLoop(opts: {
3800
3812
  const result = sourceSweepDelegationGuard;
3801
3813
  send({
3802
3814
  type: "tool_start",
3815
+ id: toolCall.id,
3803
3816
  tool: toolCall.name,
3804
3817
  input: toolCall.input as Record<string, string>,
3805
3818
  });
3806
3819
  send({
3807
3820
  type: "tool_done",
3821
+ id: toolCall.id,
3808
3822
  tool: toolCall.name,
3809
3823
  input: toolCall.input as Record<string, unknown>,
3810
3824
  result,
@@ -3826,11 +3840,13 @@ export async function runAgentLoop(opts: {
3826
3840
  );
3827
3841
  send({
3828
3842
  type: "tool_start",
3843
+ id: toolCall.id,
3829
3844
  tool: toolCall.name,
3830
3845
  input: toolCall.input as Record<string, string>,
3831
3846
  });
3832
3847
  send({
3833
3848
  type: "tool_done",
3849
+ id: toolCall.id,
3834
3850
  tool: toolCall.name,
3835
3851
  input: toolCall.input as Record<string, unknown>,
3836
3852
  result,
@@ -3875,6 +3891,7 @@ export async function runAgentLoop(opts: {
3875
3891
  if (mustApprove) {
3876
3892
  send({
3877
3893
  type: "tool_start",
3894
+ id: toolCall.id,
3878
3895
  tool: toolCall.name,
3879
3896
  input: toolCall.input as Record<string, string>,
3880
3897
  });
@@ -3913,6 +3930,7 @@ export async function runAgentLoop(opts: {
3913
3930
  `can run. The turn is paused; do not retry.`;
3914
3931
  send({
3915
3932
  type: "tool_done",
3933
+ id: toolCall.id,
3916
3934
  tool: toolCall.name,
3917
3935
  input: toolCall.input as Record<string, unknown>,
3918
3936
  result,
@@ -3973,11 +3991,13 @@ export async function runAgentLoop(opts: {
3973
3991
  recordedResult;
3974
3992
  send({
3975
3993
  type: "tool_start",
3994
+ id: toolCall.id,
3976
3995
  tool: toolCall.name,
3977
3996
  input: toolCall.input as Record<string, string>,
3978
3997
  });
3979
3998
  send({
3980
3999
  type: "tool_done",
4000
+ id: toolCall.id,
3981
4001
  tool: toolCall.name,
3982
4002
  input: toolCall.input as Record<string, unknown>,
3983
4003
  result,
@@ -4027,11 +4047,13 @@ export async function runAgentLoop(opts: {
4027
4047
  ledgerResult;
4028
4048
  send({
4029
4049
  type: "tool_start",
4050
+ id: toolCall.id,
4030
4051
  tool: toolCall.name,
4031
4052
  input: toolCall.input as Record<string, string>,
4032
4053
  });
4033
4054
  send({
4034
4055
  type: "tool_done",
4056
+ id: toolCall.id,
4035
4057
  tool: toolCall.name,
4036
4058
  input: toolCall.input as Record<string, unknown>,
4037
4059
  result,
@@ -4056,11 +4078,13 @@ export async function runAgentLoop(opts: {
4056
4078
  `or split the request into smaller pieces.`;
4057
4079
  send({
4058
4080
  type: "tool_start",
4081
+ id: toolCall.id,
4059
4082
  tool: toolCall.name,
4060
4083
  input: toolCall.input as Record<string, string>,
4061
4084
  });
4062
4085
  send({
4063
4086
  type: "tool_done",
4087
+ id: toolCall.id,
4064
4088
  tool: toolCall.name,
4065
4089
  input: toolCall.input as Record<string, unknown>,
4066
4090
  result,
@@ -4107,6 +4131,7 @@ export async function runAgentLoop(opts: {
4107
4131
 
4108
4132
  send({
4109
4133
  type: "tool_start",
4134
+ id: toolCall.id,
4110
4135
  tool: toolCall.name,
4111
4136
  input: toolCall.input as Record<string, string>,
4112
4137
  });
@@ -4122,6 +4147,7 @@ export async function runAgentLoop(opts: {
4122
4147
  );
4123
4148
  send({
4124
4149
  type: "tool_done",
4150
+ id: toolCall.id,
4125
4151
  tool: toolCall.name,
4126
4152
  input: toolCall.input as Record<string, unknown>,
4127
4153
  result,
@@ -4153,6 +4179,7 @@ export async function runAgentLoop(opts: {
4153
4179
  );
4154
4180
  send({
4155
4181
  type: "tool_done",
4182
+ id: toolCall.id,
4156
4183
  tool: toolCall.name,
4157
4184
  input: toolCall.input as Record<string, unknown>,
4158
4185
  result,
@@ -4184,6 +4211,7 @@ export async function runAgentLoop(opts: {
4184
4211
  `Previous result:\n${previousResult}`;
4185
4212
  send({
4186
4213
  type: "tool_done",
4214
+ id: toolCall.id,
4187
4215
  tool: toolCall.name,
4188
4216
  input: toolCall.input as Record<string, unknown>,
4189
4217
  result,
@@ -4213,6 +4241,7 @@ export async function runAgentLoop(opts: {
4213
4241
  const result = planModeBlockedMessage(toolCall.name);
4214
4242
  send({
4215
4243
  type: "tool_done",
4244
+ id: toolCall.id,
4216
4245
  tool: toolCall.name,
4217
4246
  input: toolCall.input as Record<string, unknown>,
4218
4247
  result,
@@ -4474,6 +4503,7 @@ export async function runAgentLoop(opts: {
4474
4503
 
4475
4504
  send({
4476
4505
  type: "tool_done",
4506
+ id: toolCall.id,
4477
4507
  tool: toolCall.name,
4478
4508
  input: toolCall.input as Record<string, unknown>,
4479
4509
  result,
@@ -6140,21 +6170,82 @@ export function createProductionAgentHandler(
6140
6170
  // DIAGNOSTIC-ONLY: bracket stored-model resolution (getStoredModelForEngine
6141
6171
  // settings read).
6142
6172
  workerStep("model_start");
6173
+ const storedModel =
6174
+ requestModel == null && configuredModel == null
6175
+ ? await getStoredModelForEngine(engine, { appId: options.appId })
6176
+ : undefined;
6143
6177
  const modelCandidate =
6144
- requestModel ??
6145
- configuredModel ??
6146
- (await getStoredModelForEngine(engine, { appId: options.appId })) ??
6147
- engine.defaultModel;
6178
+ requestModel ?? configuredModel ?? storedModel ?? engine.defaultModel;
6148
6179
  // DIAGNOSTIC-ONLY: stored-model resolution finished.
6149
6180
  workerStep("model_done");
6150
6181
  const model = normalizeModelForEngine(engine, modelCandidate);
6182
+ let effectiveModel = model;
6183
+ let modelSelectionSource:
6184
+ | "request"
6185
+ | "configured"
6186
+ | "stored"
6187
+ | "default"
6188
+ | "experiment" =
6189
+ requestModel != null
6190
+ ? "request"
6191
+ : configuredModel != null
6192
+ ? "configured"
6193
+ : storedModel != null
6194
+ ? "stored"
6195
+ : "default";
6196
+ let experimentAssignments: Array<{
6197
+ experimentId: string;
6198
+ variantId: string;
6199
+ }> = [];
6200
+
6201
+ // Database-backed experiments remain available to app operators. A
6202
+ // platform-wide hosted rollout is layered in only when no explicit or
6203
+ // persisted model selection exists, and only on the cross-provider Builder
6204
+ // engine. This preserves the model picker's user intent.
6205
+ try {
6206
+ if (ownerEmail) {
6207
+ const { resolveActiveExperimentConfig } =
6208
+ await import("../observability/experiments.js");
6209
+ const expConfig = await resolveActiveExperimentConfig(ownerEmail);
6210
+ if (expConfig) {
6211
+ experimentAssignments = [...expConfig.assignments];
6212
+ if (typeof expConfig.configs.model === "string") {
6213
+ effectiveModel = normalizeModelForEngine(
6214
+ engine,
6215
+ expConfig.configs.model,
6216
+ );
6217
+ modelSelectionSource = "experiment";
6218
+ }
6219
+ }
6220
+
6221
+ if (modelSelectionSource === "default") {
6222
+ const { resolveHostedDefaultModelExperiment } =
6223
+ await import("../observability/hosted-model-experiment.js");
6224
+ const hostedExperiment = resolveHostedDefaultModelExperiment({
6225
+ userId: ownerEmail,
6226
+ engineName: engine.name,
6227
+ isDefaultModelSelection: true,
6228
+ supportedModels: engine.supportedModels,
6229
+ });
6230
+ if (hostedExperiment) {
6231
+ effectiveModel = hostedExperiment.model;
6232
+ experimentAssignments.push(hostedExperiment.assignment);
6233
+ modelSelectionSource = "experiment";
6234
+ }
6235
+ }
6236
+ }
6237
+ } catch {
6238
+ // Experiments are best-effort. Model resolution must keep working if the
6239
+ // observability tables are unavailable during startup or migration.
6240
+ }
6241
+
6151
6242
  const reasoningEffort = resolveAgentRequestReasoningEffort({
6152
- model,
6243
+ model: effectiveModel,
6153
6244
  requestEffort,
6154
6245
  configuredEffort: options.reasoningEffort,
6155
6246
  });
6156
6247
 
6157
- options.onEngineResolved?.(engine, model);
6248
+ options.onEngineResolved?.(engine, effectiveModel);
6158
6249
 
6159
6250
  // One-line per-turn resolution log so it's obvious in dev which engine
6160
6251
  // is actually handling the request. `requestEngine` is what the client
@@ -6162,7 +6253,7 @@ export function createProductionAgentHandler(
6162
6253
  // Divergence between them is the usual cause of "status says builder but
6163
6254
  // no [builder-engine] log lines appear" confusion.
6164
6255
  console.log(
6165
- `[agent-chat] resolved engine=${engine.name} model=${model} requestEngine=${requestEngine ?? "(none)"}`,
6256
+ `[agent-chat] resolved engine=${engine.name} model=${effectiveModel} requestEngine=${requestEngine ?? "(none)"} modelSource=${modelSelectionSource}`,
6166
6257
  );
6167
6258
 
6168
6259
  if (
@@ -7467,25 +7558,6 @@ export function createProductionAgentHandler(
7467
7558
  }
7468
7559
  }
7469
7560
 
7470
- // Apply experiment variant overrides (A/B testing)
7471
- let effectiveModel = model;
7472
- try {
7473
- const { resolveActiveExperimentConfig } =
7474
- await import("../observability/experiments.js");
7475
- if (!ownerEmail) {
7476
- // Without an authenticated owner we can't resolve user-scoped experiments.
7477
- throw new Error("no authenticated user");
7478
- }
7479
- const expConfig = await resolveActiveExperimentConfig(ownerEmail);
7480
- if (expConfig) {
7481
- if (typeof expConfig.configs.model === "string") {
7482
- effectiveModel = expConfig.configs.model;
7483
- }
7484
- }
7485
- } catch {
7486
- // Experiments module unavailable — use default model
7487
- }
7488
-
7489
7561
  // TODO(processor-seam): thread `processors` from ProductionAgentOptions
7490
7562
  // through to runAgentLoop here once the handler exposes a way to
7491
7563
  // configure them (e.g. a `processors` field on ProductionAgentOptions
@@ -7572,6 +7644,14 @@ export function createProductionAgentHandler(
7572
7644
  threadId: threadId ?? null,
7573
7645
  userId: ownerEmail,
7574
7646
  config: obsConfig,
7647
+ metadata: {
7648
+ modelSelectionSource,
7649
+ ...(experimentAssignments.length > 0
7650
+ ? { experimentAssignments }
7651
+ : {}),
7652
+ },
7653
+ experimentAssignments,
7654
+ modelSelectionSource,
7575
7655
  classifyError: () => {
7576
7656
  if (
7577
7657
  agentLoopOpts.signal.aborted &&
@@ -1,5 +1,6 @@
1
1
  import type { ActionChatUIConfig } from "../action-ui.js";
2
2
  import {
3
+ isCredentialGapCodeAgentEvent,
3
4
  normalizeCodeAgentTranscript,
4
5
  type CodeAgentTranscriptEvent as CoreCodeAgentTranscriptEvent,
5
6
  type NormalizedCodeAgentStatusEvent,
@@ -152,9 +153,9 @@ export function buildAssistantMessage(
152
153
 
153
154
  if (event.type === "tool_start") {
154
155
  toolCallCounter += 1;
155
- const toolCallId = runId
156
- ? `${runId}:tc_${toolCallCounter}`
157
- : `tc_${toolCallCounter}`;
156
+ const toolCallId =
157
+ event.id?.trim() ||
158
+ (runId ? `${runId}:tc_${toolCallCounter}` : `tc_${toolCallCounter}`);
158
159
  const args = (event.input ?? {}) as Record<string, string>;
159
160
  content.push({
160
161
  type: "tool-call",
@@ -167,23 +168,47 @@ export function buildAssistantMessage(
167
168
  }
168
169
 
169
170
  if (event.type === "tool_done") {
170
- for (let i = content.length - 1; i >= 0; i--) {
171
- const part = content[i];
172
- if (
173
- part.type === "tool-call" &&
174
- part.toolName === event.tool &&
175
- part.result === undefined
176
- ) {
177
- part.result = event.result ?? "";
178
- if (event.isError !== undefined) part.isError = event.isError;
179
- if (event.completedSideEffect !== undefined) {
180
- part.completedSideEffect = event.completedSideEffect;
171
+ const eventToolCallId = event.id?.trim();
172
+ let matchingIndex = -1;
173
+
174
+ if (eventToolCallId) {
175
+ for (let i = content.length - 1; i >= 0; i--) {
176
+ const part = content[i];
177
+ if (
178
+ part.type === "tool-call" &&
179
+ part.toolCallId === eventToolCallId &&
180
+ part.result === undefined
181
+ ) {
182
+ matchingIndex = i;
183
+ break;
181
184
  }
182
- if (event.mcpApp) part.mcpApp = event.mcpApp;
183
- if (event.chatUI) part.chatUI = event.chatUI;
184
- break;
185
185
  }
186
186
  }
187
+
188
+ if (matchingIndex === -1) {
189
+ for (let i = content.length - 1; i >= 0; i--) {
190
+ const part = content[i];
191
+ if (
192
+ part.type === "tool-call" &&
193
+ part.toolName === event.tool &&
194
+ part.result === undefined
195
+ ) {
196
+ matchingIndex = i;
197
+ break;
198
+ }
199
+ }
200
+ }
201
+
202
+ const part = content[matchingIndex];
203
+ if (part?.type === "tool-call") {
204
+ part.result = event.result ?? "";
205
+ if (event.isError !== undefined) part.isError = event.isError;
206
+ if (event.completedSideEffect !== undefined) {
207
+ part.completedSideEffect = event.completedSideEffect;
208
+ }
209
+ if (event.mcpApp) part.mcpApp = event.mcpApp;
210
+ if (event.chatUI) part.chatUI = event.chatUI;
211
+ }
187
212
  continue;
188
213
  }
189
214
 
@@ -640,6 +665,7 @@ export interface CodeAgentThreadTranscriptEvent {
640
665
  metadata?: Record<string, unknown>;
641
666
  artifactPath?: string;
642
667
  artifactUrl?: string;
668
+ signal?: CoreCodeAgentTranscriptEvent["signal"];
643
669
  }
644
670
 
645
671
  export interface BuildRepositoryFromCodeAgentTranscriptOptions {
@@ -1070,6 +1096,7 @@ function toCoreCodeAgentTranscriptEvent(
1070
1096
  ...(event.artifactPath ? { artifactPath: event.artifactPath } : {}),
1071
1097
  ...(event.artifactUrl ? { artifactUrl: event.artifactUrl } : {}),
1072
1098
  },
1099
+ ...(event.signal ? { signal: event.signal } : {}),
1073
1100
  };
1074
1101
  }
1075
1102
 
@@ -1113,6 +1140,9 @@ function toolContentPartForCodeAgentTranscriptItem(
1113
1140
  ? { result: previewCodeAgentTranscriptValue(item.result) ?? "" }
1114
1141
  : {}),
1115
1142
  ...(item.structuredMeta ? { structuredMeta: item.structuredMeta } : {}),
1143
+ ...(item.pendingApprovalKey
1144
+ ? { approval: { approvalKey: item.pendingApprovalKey } }
1145
+ : {}),
1116
1146
  };
1117
1147
  }
1118
1148
 
@@ -1120,7 +1150,7 @@ function statusTextForCodeAgentTranscriptItem(
1120
1150
  item: NormalizedCodeAgentStatusEvent,
1121
1151
  options: BuildRepositoryFromCodeAgentTranscriptOptions,
1122
1152
  ): string | null {
1123
- if (options.hideCredentialMessages && isCredentialCodeAgentText(item.text)) {
1153
+ if (options.hideCredentialMessages && isCredentialGapCodeAgentEvent(item)) {
1124
1154
  return null;
1125
1155
  }
1126
1156
  if (item.statusKind === "artifact") {
@@ -1195,10 +1225,6 @@ function stringRecordValue(
1195
1225
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
1196
1226
  }
1197
1227
 
1198
- function isCredentialCodeAgentText(value: string): boolean {
1199
- return /No LLM provider key was found|Missing credentials/i.test(value);
1200
- }
1201
-
1202
1228
  export function upsertUserMessage(repo: any, userMsg: UserMessage): any {
1203
1229
  const nextRepo = normalizeThreadRepository(repo);
1204
1230
 
@@ -639,6 +639,14 @@ export interface ListThreadsOptions {
639
639
  /** When true, returns only threads with no scope (general chats). */
640
640
  unscopedOnly?: boolean;
641
641
  orgId?: string | null;
642
+ /**
643
+ * Include archived threads in the results. Defaults to false: archived
644
+ * threads (`archived_at` set via `setThreadArchived`) are hidden from the
645
+ * ordinary chat list/search so archiving actually removes a thread from
646
+ * view. Pass true for surfaces that explicitly need to see archived chats
647
+ * (e.g. an "Archived" filter or restoring one via `setThreadArchived`).
648
+ */
649
+ includeArchived?: boolean;
642
650
  }
643
651
 
644
652
  function chatThreadAccessSql(
@@ -686,6 +694,9 @@ export async function listThreads(
686
694
  );
687
695
  const filters: string[] = [access.sql, `message_count > 0`];
688
696
  const args: (string | number)[] = [...access.args];
697
+ if (!opts.includeArchived) {
698
+ filters.push(`archived_at IS NULL`);
699
+ }
689
700
  if (opts.scope) {
690
701
  filters.push(`scope_type = ? AND scope_id = ?`);
691
702
  args.push(opts.scope.type, opts.scope.id);
@@ -710,7 +721,12 @@ export async function searchThreads(
710
721
  ownerEmail: string,
711
722
  query: string,
712
723
  limit = 50,
713
- options: { scope?: { type: string; id: string }; orgId?: string | null } = {},
724
+ options: {
725
+ scope?: { type: string; id: string };
726
+ orgId?: string | null;
727
+ /** See `ListThreadsOptions.includeArchived` — defaults to false. */
728
+ includeArchived?: boolean;
729
+ } = {},
714
730
  ): Promise<ChatThreadSummary[]> {
715
731
  await ensureTable();
716
732
  const client = getDbExec();
@@ -728,6 +744,9 @@ export async function searchThreads(
728
744
  `(title LIKE ? ESCAPE '!' OR preview LIKE ? ESCAPE '!' OR thread_data LIKE ? ESCAPE '!')`,
729
745
  ];
730
746
  const args: (string | number)[] = [...access.args, pattern, pattern, pattern];
747
+ if (!options.includeArchived) {
748
+ filters.push(`archived_at IS NULL`);
749
+ }
731
750
  if (options.scope) {
732
751
  filters.push(`scope_type = ? AND scope_id = ?`);
733
752
  args.push(options.scope.type, options.scope.id);
@@ -215,6 +215,7 @@ export async function executeCodeAgentRun(
215
215
  runId: existing.id,
216
216
  kind: "status",
217
217
  message,
218
+ signal: "credential-gap",
218
219
  metadata: { status: "paused", phase: "missing-credentials" },
219
220
  });
220
221
  return updateCodeAgentRunRecord(existing.id, {
@@ -77,6 +77,17 @@ export type CodeAgentTranscriptEventKind =
77
77
  | "artifact"
78
78
  | "status";
79
79
 
80
+ /**
81
+ * Structured, machine-checkable marker for transcript events that need
82
+ * special handling in the UI beyond free-text matching. `"credential-gap"`
83
+ * marks the status event the executor appends when no LLM provider key (or
84
+ * Codex CLI login) is available; consumers should prefer this field over
85
+ * regex-matching `message` (see `isCredentialGapCodeAgentEvent` in
86
+ * `../code-agents/transcript-normalizer.js`). Optional so older, already
87
+ * persisted JSONL transcripts without the field keep parsing unchanged.
88
+ */
89
+ export type CodeAgentTranscriptEventSignal = "credential-gap";
90
+
80
91
  export interface CodeAgentTranscriptEvent {
81
92
  schemaVersion: 1;
82
93
  id: string;
@@ -85,6 +96,7 @@ export interface CodeAgentTranscriptEvent {
85
96
  message: string;
86
97
  createdAt: string;
87
98
  metadata?: Record<string, unknown>;
99
+ signal?: CodeAgentTranscriptEventSignal;
88
100
  }
89
101
 
90
102
  export interface CreateCodeAgentRunInput {
@@ -109,6 +121,7 @@ export interface AppendCodeAgentTranscriptEventInput {
109
121
  message: string;
110
122
  createdAt?: string;
111
123
  metadata?: Record<string, unknown>;
124
+ signal?: CodeAgentTranscriptEventSignal;
112
125
  }
113
126
 
114
127
  export interface QueueCodeAgentFollowUpInput {
@@ -294,6 +307,7 @@ export function appendCodeAgentTranscriptEvent(
294
307
  message: input.message,
295
308
  createdAt,
296
309
  metadata: input.metadata,
310
+ ...(input.signal ? { signal: input.signal } : {}),
297
311
  };
298
312
 
299
313
  fs.mkdirSync(codeAgentTranscriptsDir(), { recursive: true });
@@ -1458,32 +1458,39 @@ function AgentPanelInner({
1458
1458
  activeTab && (activeTabMessageCount > 0 || activeTab.status !== "idle");
1459
1459
 
1460
1460
  return (
1461
- <div className="pointer-events-none absolute inset-x-0 top-3 z-[60] flex justify-end px-3 sm:top-4 sm:px-4">
1462
- <div className="pointer-events-auto flex items-center gap-1">
1463
- {canShareActiveTab ? (
1464
- <ShareButton
1465
- resourceType="chat_thread"
1466
- resourceId={activeTab.id}
1467
- resourceTitle={activeTab.label || "Chat"}
1468
- shareUrl={getChatThreadShareUrl(activeTab.id)}
1469
- trigger="icon"
1470
- triggerClassName="h-8 w-8 border border-border bg-background/95 shadow-sm backdrop-blur hover:bg-accent"
1471
- />
1472
- ) : null}
1473
- <button
1474
- type="button"
1475
- data-agent-page-new-chat=""
1476
- aria-label={t("agentPanel.newChat")}
1477
- onClick={() => {
1478
- addTab();
1479
- }}
1480
- className="inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background/95 px-2.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1481
- >
1482
- <IconPlus size={14} />
1483
- <span>{t("agentPanel.newChat")}</span>
1484
- </button>
1461
+ <>
1462
+ <div
1463
+ aria-hidden="true"
1464
+ data-agent-page-chat-fade=""
1465
+ className="pointer-events-none absolute inset-x-0 top-0 z-50 h-16 bg-gradient-to-b from-background via-background/90 to-transparent opacity-0 transition-opacity duration-150"
1466
+ />
1467
+ <div className="pointer-events-none absolute inset-x-0 top-3 z-[60] flex justify-end px-3 sm:top-4 sm:px-4">
1468
+ <div className="pointer-events-auto flex items-center gap-1">
1469
+ {canShareActiveTab ? (
1470
+ <ShareButton
1471
+ resourceType="chat_thread"
1472
+ resourceId={activeTab.id}
1473
+ resourceTitle={activeTab.label || "Chat"}
1474
+ shareUrl={getChatThreadShareUrl(activeTab.id)}
1475
+ trigger="icon"
1476
+ triggerClassName="h-8 w-8 border border-border bg-background/95 shadow-sm backdrop-blur hover:bg-accent"
1477
+ />
1478
+ ) : null}
1479
+ <button
1480
+ type="button"
1481
+ data-agent-page-new-chat=""
1482
+ aria-label={t("agentPanel.newChat")}
1483
+ onClick={() => {
1484
+ addTab();
1485
+ }}
1486
+ className="inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background/95 px-2.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1487
+ >
1488
+ <IconPlus size={14} />
1489
+ <span>{t("agentPanel.newChat")}</span>
1490
+ </button>
1491
+ </div>
1485
1492
  </div>
1486
- </div>
1493
+ </>
1487
1494
  );
1488
1495
  },
1489
1496
  [getChatThreadShareUrl, t],