@agent-native/core 0.128.3 → 0.129.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 (350) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +91 -0
  3. package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  4. package/corpus/core/package.json +6 -3
  5. package/corpus/core/src/a2a/client.ts +18 -4
  6. package/corpus/core/src/a2a/server.ts +19 -3
  7. package/corpus/core/src/a2a/types.ts +7 -0
  8. package/corpus/core/src/action.ts +43 -16
  9. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -10
  10. package/corpus/core/src/agent/production-agent.ts +41 -3
  11. package/corpus/core/src/agent/thread-data-builder.ts +5 -0
  12. package/corpus/core/src/chat-threads/migrations.ts +53 -0
  13. package/corpus/core/src/cli/create.ts +4 -0
  14. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  15. package/corpus/core/src/cli/skills.ts +16 -0
  16. package/corpus/core/src/client/AgentPanel.tsx +9 -5
  17. package/corpus/core/src/client/AssistantChat.tsx +138 -46
  18. package/corpus/core/src/client/MultiTabAssistantChat.tsx +16 -10
  19. package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
  20. package/corpus/core/src/client/analytics.ts +159 -102
  21. package/corpus/core/src/client/chat/legacy-chart-shorthand.tsx +479 -0
  22. package/corpus/core/src/client/chat/markdown-renderer.tsx +23 -3
  23. package/corpus/core/src/client/chat/message-components.tsx +12 -5
  24. package/corpus/core/src/client/chat/runtime.ts +2 -1
  25. package/corpus/core/src/client/chat/tool-call-display.tsx +25 -2
  26. package/corpus/core/src/client/chat-model-groups.ts +3 -0
  27. package/corpus/core/src/client/client-status-requests.ts +161 -0
  28. package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  29. package/corpus/core/src/client/sse-event-processor.ts +15 -3
  30. package/corpus/core/src/client/use-agent-engine-configured.ts +54 -50
  31. package/corpus/core/src/client/use-chat-models.ts +16 -11
  32. package/corpus/core/src/client/use-chat-threads.ts +32 -9
  33. package/corpus/core/src/client/use-session.ts +4 -5
  34. package/corpus/core/src/db/client.ts +83 -8
  35. package/corpus/core/src/db/ensure-additive-columns.ts +31 -20
  36. package/corpus/core/src/db/migrations.ts +22 -2
  37. package/corpus/core/src/extensions/actions.ts +1 -1
  38. package/corpus/core/src/mcp/approval-store.ts +99 -0
  39. package/corpus/core/src/mcp/build-server.ts +579 -303
  40. package/corpus/core/src/mcp/embed-app.ts +1 -1
  41. package/corpus/core/src/mcp/oauth-client-metadata.ts +380 -0
  42. package/corpus/core/src/mcp/oauth-route.ts +57 -52
  43. package/corpus/core/src/mcp/screen-memory-stdio.ts +400 -378
  44. package/corpus/core/src/mcp/server.ts +29 -145
  45. package/corpus/core/src/mcp/stdio.ts +72 -80
  46. package/corpus/core/src/mcp-client/manager.ts +6 -10
  47. package/corpus/core/src/mcp-client/oauth-client.ts +127 -36
  48. package/corpus/core/src/mcp-client/oauth-routes.ts +16 -6
  49. package/corpus/core/src/mcp-client/routes.ts +6 -5
  50. package/corpus/core/src/server/agent-capabilities.ts +20 -0
  51. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +27 -0
  52. package/corpus/core/src/server/agent-chat-plugin.ts +18 -0
  53. package/corpus/core/src/server/transcribe-voice.ts +14 -5
  54. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  55. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  56. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  57. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  58. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +12 -0
  59. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +13 -2
  60. package/corpus/templates/analytics/actions/generate-chart.ts +2 -2
  61. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -10
  62. package/corpus/templates/analytics/app/global.css +0 -31
  63. package/corpus/templates/analytics/app/lib/sql-query.ts +46 -20
  64. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboard-loading-states-now-use-a-simpler-standar.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboards-now-load-first-party-charts-reliably-in.md +6 -0
  66. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +42 -16
  67. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
  68. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  69. package/corpus/templates/assets/.agents/skills/security/SKILL.md +13 -2
  70. package/corpus/templates/brain/.agents/skills/security/SKILL.md +13 -2
  71. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +13 -2
  72. package/corpus/templates/chat/.agents/skills/security/SKILL.md +13 -2
  73. package/corpus/templates/clips/.agents/skills/security/SKILL.md +13 -2
  74. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -6
  75. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -2
  76. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -1
  77. package/corpus/templates/clips/changelog/2026-07-28-copied-share-links-no-longer-leave-an-unnecessary-confirmati.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-07-28-playback-skip-controls-no-longer-show-overlapping-numbers.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-28-public-clips-share-buttons-now-use-a-clean-text-only-label.md +6 -0
  80. package/corpus/templates/clips/desktop/src/app.tsx +14 -17
  81. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +3 -1
  82. package/corpus/templates/clips/desktop/src/styles.css +13 -10
  83. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +2 -2
  84. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +36 -0
  85. package/corpus/templates/content/.agents/skills/security/SKILL.md +13 -2
  86. package/corpus/templates/content/actions/_builder-cms-read-client.ts +155 -34
  87. package/corpus/templates/content/actions/_builder-docs-client.ts +151 -30
  88. package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +5 -2
  89. package/corpus/templates/content/app/root.tsx +35 -14
  90. package/corpus/templates/content/changelog/2026-07-28-builder-backed-files-media-fields-now-accept-direct-file-url.md +6 -0
  91. package/corpus/templates/content/changelog/2026-07-28-builder-content-reads-now-negotiate-the-newest-mcp-protocol-.md +6 -0
  92. package/corpus/templates/content/changelog/2026-07-28-public-content-pages-load-faster-before-the-optional-ask-sid.md +6 -0
  93. package/corpus/templates/design/.agents/skills/security/SKILL.md +13 -2
  94. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +36 -16
  95. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +42 -3
  96. package/corpus/templates/design/actions/open-visual-edit.ts +2 -1
  97. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  98. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -15
  99. package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +314 -0
  100. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +88 -3
  101. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
  102. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +7 -6
  103. package/corpus/templates/design/app/i18n-data.ts +56 -11
  104. package/corpus/templates/design/app/i18n-responsive-interact.ts +183 -0
  105. package/corpus/templates/design/app/lib/agent-chat.ts +60 -0
  106. package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +89 -0
  107. package/corpus/templates/design/changelog/2026-07-28-design-edits-can-be-applied-through-the-host-coding-agent-fr.md +6 -0
  108. package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +6 -0
  109. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +6 -0
  110. package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +2 -2
  111. package/corpus/templates/design/changelog/2026-07-28-visual-edit-now-always-shows-the-live-running-app-in-the-can.md +6 -0
  112. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +13 -2
  113. package/corpus/templates/forms/.agents/skills/security/SKILL.md +13 -2
  114. package/corpus/templates/macros/.agents/skills/security/SKILL.md +13 -2
  115. package/corpus/templates/mail/.agents/skills/security/SKILL.md +13 -2
  116. package/corpus/templates/plan/.agents/skills/security/SKILL.md +13 -2
  117. package/corpus/templates/slides/.agents/skills/security/SKILL.md +13 -2
  118. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +13 -2
  119. package/corpus/toolkit/CHANGELOG.md +13 -0
  120. package/corpus/toolkit/package.json +1 -1
  121. package/corpus/toolkit/src/chat-history.css +1 -2
  122. package/corpus/toolkit/src/composer/TiptapComposer.tsx +45 -0
  123. package/dist/a2a/client.d.ts +7 -0
  124. package/dist/a2a/client.d.ts.map +1 -1
  125. package/dist/a2a/client.js +8 -3
  126. package/dist/a2a/client.js.map +1 -1
  127. package/dist/a2a/server.d.ts.map +1 -1
  128. package/dist/a2a/server.js +18 -3
  129. package/dist/a2a/server.js.map +1 -1
  130. package/dist/a2a/types.d.ts +7 -0
  131. package/dist/a2a/types.d.ts.map +1 -1
  132. package/dist/a2a/types.js.map +1 -1
  133. package/dist/action.d.ts +15 -1
  134. package/dist/action.d.ts.map +1 -1
  135. package/dist/action.js +39 -16
  136. package/dist/action.js.map +1 -1
  137. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  138. package/dist/agent/engine/ai-sdk-engine.js +14 -10
  139. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  140. package/dist/agent/production-agent.d.ts.map +1 -1
  141. package/dist/agent/production-agent.js +35 -7
  142. package/dist/agent/production-agent.js.map +1 -1
  143. package/dist/agent/thread-data-builder.d.ts +2 -0
  144. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  145. package/dist/agent/thread-data-builder.js +3 -0
  146. package/dist/agent/thread-data-builder.js.map +1 -1
  147. package/dist/chat-threads/migrations.d.ts +21 -0
  148. package/dist/chat-threads/migrations.d.ts.map +1 -0
  149. package/dist/chat-threads/migrations.js +45 -0
  150. package/dist/chat-threads/migrations.js.map +1 -0
  151. package/dist/cli/create.d.ts.map +1 -1
  152. package/dist/cli/create.js +4 -0
  153. package/dist/cli/create.js.map +1 -1
  154. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  155. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  156. package/dist/cli/skills-content/design-visual-edit-skill.js +36 -16
  157. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  158. package/dist/cli/skills.d.ts.map +1 -1
  159. package/dist/cli/skills.js +15 -0
  160. package/dist/cli/skills.js.map +1 -1
  161. package/dist/client/AgentPanel.d.ts.map +1 -1
  162. package/dist/client/AgentPanel.js +6 -5
  163. package/dist/client/AgentPanel.js.map +1 -1
  164. package/dist/client/AssistantChat.d.ts +37 -0
  165. package/dist/client/AssistantChat.d.ts.map +1 -1
  166. package/dist/client/AssistantChat.js +41 -8
  167. package/dist/client/AssistantChat.js.map +1 -1
  168. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  169. package/dist/client/MultiTabAssistantChat.js +11 -10
  170. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  171. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  172. package/dist/client/agent-chat-adapter.js +19 -3
  173. package/dist/client/agent-chat-adapter.js.map +1 -1
  174. package/dist/client/analytics.d.ts.map +1 -1
  175. package/dist/client/analytics.js +150 -101
  176. package/dist/client/analytics.js.map +1 -1
  177. package/dist/client/chat/legacy-chart-shorthand.d.ts +29 -0
  178. package/dist/client/chat/legacy-chart-shorthand.d.ts.map +1 -0
  179. package/dist/client/chat/legacy-chart-shorthand.js +330 -0
  180. package/dist/client/chat/legacy-chart-shorthand.js.map +1 -0
  181. package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
  182. package/dist/client/chat/markdown-renderer.js +8 -2
  183. package/dist/client/chat/markdown-renderer.js.map +1 -1
  184. package/dist/client/chat/message-components.d.ts +2 -1
  185. package/dist/client/chat/message-components.d.ts.map +1 -1
  186. package/dist/client/chat/message-components.js +11 -4
  187. package/dist/client/chat/message-components.js.map +1 -1
  188. package/dist/client/chat/runtime.d.ts.map +1 -1
  189. package/dist/client/chat/runtime.js +2 -1
  190. package/dist/client/chat/runtime.js.map +1 -1
  191. package/dist/client/chat/tool-call-display.d.ts +4 -1
  192. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  193. package/dist/client/chat/tool-call-display.js +14 -8
  194. package/dist/client/chat/tool-call-display.js.map +1 -1
  195. package/dist/client/chat-model-groups.d.ts.map +1 -1
  196. package/dist/client/chat-model-groups.js +3 -0
  197. package/dist/client/chat-model-groups.js.map +1 -1
  198. package/dist/client/client-status-requests.d.ts +14 -0
  199. package/dist/client/client-status-requests.d.ts.map +1 -0
  200. package/dist/client/client-status-requests.js +126 -0
  201. package/dist/client/client-status-requests.js.map +1 -0
  202. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  203. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  204. package/dist/client/sse-event-processor.d.ts +7 -0
  205. package/dist/client/sse-event-processor.d.ts.map +1 -1
  206. package/dist/client/sse-event-processor.js +8 -3
  207. package/dist/client/sse-event-processor.js.map +1 -1
  208. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  209. package/dist/client/use-agent-engine-configured.js +29 -43
  210. package/dist/client/use-agent-engine-configured.js.map +1 -1
  211. package/dist/client/use-chat-models.d.ts.map +1 -1
  212. package/dist/client/use-chat-models.js +11 -11
  213. package/dist/client/use-chat-models.js.map +1 -1
  214. package/dist/client/use-chat-threads.d.ts.map +1 -1
  215. package/dist/client/use-chat-threads.js +23 -7
  216. package/dist/client/use-chat-threads.js.map +1 -1
  217. package/dist/client/use-session.d.ts.map +1 -1
  218. package/dist/client/use-session.js +4 -4
  219. package/dist/client/use-session.js.map +1 -1
  220. package/dist/collab/awareness.d.ts +2 -2
  221. package/dist/collab/awareness.d.ts.map +1 -1
  222. package/dist/db/client.d.ts.map +1 -1
  223. package/dist/db/client.js +48 -4
  224. package/dist/db/client.js.map +1 -1
  225. package/dist/db/ensure-additive-columns.d.ts.map +1 -1
  226. package/dist/db/ensure-additive-columns.js +32 -21
  227. package/dist/db/ensure-additive-columns.js.map +1 -1
  228. package/dist/db/migrations.d.ts +9 -0
  229. package/dist/db/migrations.d.ts.map +1 -1
  230. package/dist/db/migrations.js +14 -3
  231. package/dist/db/migrations.js.map +1 -1
  232. package/dist/extensions/actions.js +1 -1
  233. package/dist/extensions/actions.js.map +1 -1
  234. package/dist/mcp/approval-store.d.ts +16 -0
  235. package/dist/mcp/approval-store.d.ts.map +1 -0
  236. package/dist/mcp/approval-store.js +85 -0
  237. package/dist/mcp/approval-store.js.map +1 -0
  238. package/dist/mcp/build-server.d.ts +1 -34
  239. package/dist/mcp/build-server.d.ts.map +1 -1
  240. package/dist/mcp/build-server.js +220 -23
  241. package/dist/mcp/build-server.js.map +1 -1
  242. package/dist/mcp/embed-app.js +1 -1
  243. package/dist/mcp/embed-app.js.map +1 -1
  244. package/dist/mcp/oauth-client-metadata.d.ts +15 -0
  245. package/dist/mcp/oauth-client-metadata.d.ts.map +1 -0
  246. package/dist/mcp/oauth-client-metadata.js +297 -0
  247. package/dist/mcp/oauth-client-metadata.js.map +1 -0
  248. package/dist/mcp/oauth-route.d.ts.map +1 -1
  249. package/dist/mcp/oauth-route.js +39 -53
  250. package/dist/mcp/oauth-route.js.map +1 -1
  251. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  252. package/dist/mcp/screen-memory-stdio.js +304 -296
  253. package/dist/mcp/screen-memory-stdio.js.map +1 -1
  254. package/dist/mcp/server.d.ts +12 -25
  255. package/dist/mcp/server.d.ts.map +1 -1
  256. package/dist/mcp/server.js +24 -133
  257. package/dist/mcp/server.js.map +1 -1
  258. package/dist/mcp/stdio.d.ts.map +1 -1
  259. package/dist/mcp/stdio.js +36 -35
  260. package/dist/mcp/stdio.js.map +1 -1
  261. package/dist/mcp-client/manager.d.ts.map +1 -1
  262. package/dist/mcp-client/manager.js +6 -6
  263. package/dist/mcp-client/manager.js.map +1 -1
  264. package/dist/mcp-client/oauth-client.d.ts +13 -17
  265. package/dist/mcp-client/oauth-client.d.ts.map +1 -1
  266. package/dist/mcp-client/oauth-client.js +74 -16
  267. package/dist/mcp-client/oauth-client.js.map +1 -1
  268. package/dist/mcp-client/oauth-routes.d.ts +2 -2
  269. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  270. package/dist/mcp-client/oauth-routes.js +16 -6
  271. package/dist/mcp-client/oauth-routes.js.map +1 -1
  272. package/dist/mcp-client/routes.js +5 -5
  273. package/dist/mcp-client/routes.js.map +1 -1
  274. package/dist/notifications/routes.d.ts +1 -1
  275. package/dist/progress/routes.d.ts +1 -1
  276. package/dist/resources/handlers.d.ts +1 -1
  277. package/dist/secrets/routes.d.ts +9 -9
  278. package/dist/server/agent-capabilities.d.ts.map +1 -1
  279. package/dist/server/agent-capabilities.js +21 -0
  280. package/dist/server/agent-capabilities.js.map +1 -1
  281. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -0
  282. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  283. package/dist/server/agent-chat/action-filters-a2a.js +16 -0
  284. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  285. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  286. package/dist/server/agent-chat-plugin.js +11 -1
  287. package/dist/server/agent-chat-plugin.js.map +1 -1
  288. package/dist/server/transcribe-voice.js +12 -6
  289. package/dist/server/transcribe-voice.js.map +1 -1
  290. package/dist/templates/chat/.agents/skills/security/SKILL.md +13 -2
  291. package/dist/templates/default/.agents/skills/security/SKILL.md +13 -2
  292. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  293. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  294. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  295. package/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  296. package/package.json +7 -4
  297. package/src/a2a/client.ts +18 -4
  298. package/src/a2a/server.ts +19 -3
  299. package/src/a2a/types.ts +7 -0
  300. package/src/action.ts +43 -16
  301. package/src/agent/engine/ai-sdk-engine.ts +14 -10
  302. package/src/agent/production-agent.ts +41 -3
  303. package/src/agent/thread-data-builder.ts +5 -0
  304. package/src/chat-threads/migrations.ts +53 -0
  305. package/src/cli/create.ts +4 -0
  306. package/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  307. package/src/cli/skills.ts +16 -0
  308. package/src/client/AgentPanel.tsx +9 -5
  309. package/src/client/AssistantChat.tsx +138 -46
  310. package/src/client/MultiTabAssistantChat.tsx +16 -10
  311. package/src/client/agent-chat-adapter.ts +21 -3
  312. package/src/client/analytics.ts +159 -102
  313. package/src/client/chat/legacy-chart-shorthand.tsx +479 -0
  314. package/src/client/chat/markdown-renderer.tsx +23 -3
  315. package/src/client/chat/message-components.tsx +12 -5
  316. package/src/client/chat/runtime.ts +2 -1
  317. package/src/client/chat/tool-call-display.tsx +25 -2
  318. package/src/client/chat-model-groups.ts +3 -0
  319. package/src/client/client-status-requests.ts +161 -0
  320. package/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  321. package/src/client/sse-event-processor.ts +15 -3
  322. package/src/client/use-agent-engine-configured.ts +54 -50
  323. package/src/client/use-chat-models.ts +16 -11
  324. package/src/client/use-chat-threads.ts +32 -9
  325. package/src/client/use-session.ts +4 -5
  326. package/src/db/client.ts +83 -8
  327. package/src/db/ensure-additive-columns.ts +31 -20
  328. package/src/db/migrations.ts +22 -2
  329. package/src/extensions/actions.ts +1 -1
  330. package/src/mcp/approval-store.ts +99 -0
  331. package/src/mcp/build-server.ts +579 -303
  332. package/src/mcp/embed-app.ts +1 -1
  333. package/src/mcp/oauth-client-metadata.ts +380 -0
  334. package/src/mcp/oauth-route.ts +57 -52
  335. package/src/mcp/screen-memory-stdio.ts +400 -378
  336. package/src/mcp/server.ts +29 -145
  337. package/src/mcp/stdio.ts +72 -80
  338. package/src/mcp-client/manager.ts +6 -10
  339. package/src/mcp-client/oauth-client.ts +127 -36
  340. package/src/mcp-client/oauth-routes.ts +16 -6
  341. package/src/mcp-client/routes.ts +6 -5
  342. package/src/server/agent-capabilities.ts +20 -0
  343. package/src/server/agent-chat/action-filters-a2a.ts +27 -0
  344. package/src/server/agent-chat-plugin.ts +18 -0
  345. package/src/server/transcribe-voice.ts +14 -5
  346. package/src/templates/chat/.agents/skills/security/SKILL.md +13 -2
  347. package/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  348. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  349. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  350. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
@@ -0,0 +1,479 @@
1
+ // Some tool schemas expose chart parameters named `type`/`title`/`labels`/
2
+ // `data` (e.g. a chart-generation action). Models occasionally regress to
3
+ // typing those parameter names as a bare chat line instead of calling the
4
+ // tool or emitting a real ```embed fence, e.g.:
5
+ // /chart type=bar title="..." labels=["Mon","Tue"] data=[5,8]
6
+ // That has no real markdown syntax and would otherwise render as inert text.
7
+ // This module detects that generic shape (any "/word ... labels=[...]
8
+ // data=[...]" line) and renders a best-effort inline chart so the user still
9
+ // sees something useful, without hardcoding any single template's tool name.
10
+
11
+ import React from "react";
12
+
13
+ export const LEGACY_CHART_SHORTHAND_LANG = "chart-shorthand";
14
+
15
+ const LEGACY_CHART_PALETTE = [
16
+ "#6366f1",
17
+ "#22c55e",
18
+ "#f59e0b",
19
+ "#ef4444",
20
+ "#0ea5e9",
21
+ "#a855f7",
22
+ ];
23
+
24
+ const SAFE_HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
25
+ const BACKSLASH = String.fromCharCode(92);
26
+ const MAX_LINE_LENGTH = 20_000;
27
+ const MAX_LABELS = 60;
28
+ const MAX_SERIES = 6;
29
+
30
+ export interface LegacyChartShorthand {
31
+ type: "bar" | "line" | "area";
32
+ title: string;
33
+ labels: string[];
34
+ series: { label: string; data: number[]; color?: string }[];
35
+ }
36
+
37
+ // Extracts a balanced JSON array/object starting exactly at `startIndex`
38
+ // (which must point at "["), tracking string/escape state so brackets or
39
+ // braces inside JSON string values don't confuse the boundary.
40
+ function extractBalancedArrayAt(
41
+ text: string,
42
+ startIndex: number,
43
+ ): string | null {
44
+ if (text[startIndex] !== "[") return null;
45
+ let depth = 0;
46
+ let inString = false;
47
+ for (let idx = startIndex; idx < text.length; idx++) {
48
+ const ch = text[idx];
49
+ if (inString) {
50
+ if (ch === "\\")
51
+ idx++; // skip escaped character, including \"
52
+ else if (ch === '"') inString = false;
53
+ continue;
54
+ }
55
+ if (ch === '"') inString = true;
56
+ else if (ch === "[" || ch === "{") depth++;
57
+ else if (ch === "]" || ch === "}") {
58
+ depth--;
59
+ if (depth === 0) return text.slice(startIndex, idx + 1);
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+
65
+ // Marks which character indices fall inside a JSON/quoted-string literal so
66
+ // key-token scanning can ignore false matches such as a key= that's part of
67
+ // a title string, or embedded in a label's own text, rather than a real
68
+ // assignment in the line.
69
+ function computeStringMask(text: string): boolean[] {
70
+ const mask = new Array<boolean>(text.length).fill(false);
71
+ let inString = false;
72
+ for (let i = 0; i < text.length; i++) {
73
+ mask[i] = inString;
74
+ const ch = text[i];
75
+ if (inString) {
76
+ if (ch === BACKSLASH) {
77
+ i++;
78
+ if (i < text.length) mask[i] = true;
79
+ continue;
80
+ }
81
+ if (ch === '"') inString = false;
82
+ } else if (ch === '"') {
83
+ inString = true;
84
+ }
85
+ }
86
+ return mask;
87
+ }
88
+
89
+ // Finds the array value immediately following a `key=` token, scanning every
90
+ // occurrence of `key=` outside of quoted strings (not just the first) so a
91
+ // `key=` that appears inside an unrelated quoted string (e.g.
92
+ // title="data=quality", or a label literally containing "data=[1,2]") is
93
+ // skipped in favor of the real one. Only whitespace is allowed between
94
+ // `key=` and the `[`.
95
+ function extractArrayForKey(
96
+ text: string,
97
+ key: "labels" | "data",
98
+ ): string | null {
99
+ const mask = computeStringMask(text);
100
+ const re = new RegExp(`\\b${key}=`, "g");
101
+ let match: RegExpExecArray | null;
102
+ while ((match = re.exec(text))) {
103
+ if (mask[match.index]) continue; // this key= is inside a string literal
104
+ let idx = match.index + match[0].length;
105
+ while (idx < text.length && /\s/.test(text[idx])) idx++;
106
+ const arr = extractBalancedArrayAt(text, idx);
107
+ if (arr) return arr;
108
+ }
109
+ return null;
110
+ }
111
+
112
+ // Extracts the value of a `title="..."`/`title='...'` token, honoring
113
+ // backslash-escaped quotes and backslashes inside the string (e.g.
114
+ // title="Sales \"Metrics\"") instead of stopping at the first escaped quote.
115
+ function extractLegacyChartTitle(text: string): string {
116
+ const match = /\btitle=/.exec(text);
117
+ if (!match) return "";
118
+ let idx = match.index + match[0].length;
119
+ const quoteChar = text[idx];
120
+ if (quoteChar !== '"' && quoteChar !== "'") return "";
121
+ idx++;
122
+ let result = "";
123
+ while (idx < text.length) {
124
+ const ch = text[idx];
125
+ if (ch === BACKSLASH && idx + 1 < text.length) {
126
+ const next = text[idx + 1];
127
+ if (next === quoteChar || next === BACKSLASH) {
128
+ result += next;
129
+ idx += 2;
130
+ continue;
131
+ }
132
+ }
133
+ if (ch === quoteChar) return result;
134
+ result += ch;
135
+ idx++;
136
+ }
137
+ return "";
138
+ }
139
+
140
+ /**
141
+ * Cheap-ish pre-check so callers can skip the full parse on ordinary text.
142
+ * Requires `labels=`/`data=` to actually resolve to bracketed arrays (not
143
+ * just appear as substrings) so unrelated slash commands or API paths that
144
+ * happen to mention both words aren't diverted out of normal markdown
145
+ * rendering.
146
+ */
147
+ export function looksLikeLegacyChartShorthand(line: string): boolean {
148
+ const trimmed = line.trim();
149
+ if (trimmed.length === 0 || trimmed.length > MAX_LINE_LENGTH) return false;
150
+ if (!/^\/[a-zA-Z][\w-]*\b/.test(trimmed)) return false;
151
+ if (!/\blabels=/.test(trimmed) || !/\bdata=/.test(trimmed)) return false;
152
+ return (
153
+ extractArrayForKey(trimmed, "labels") !== null &&
154
+ extractArrayForKey(trimmed, "data") !== null
155
+ );
156
+ }
157
+
158
+ export function parseLegacyChartShorthand(
159
+ rawLine: string,
160
+ ): LegacyChartShorthand | null {
161
+ const trimmed = rawLine.trim();
162
+ if (!looksLikeLegacyChartShorthand(trimmed)) return null;
163
+
164
+ const typeMatch = trimmed.match(/\btype=(bar|line|area)\b/i);
165
+ const chartType =
166
+ (typeMatch?.[1].toLowerCase() as LegacyChartShorthand["type"]) || "bar";
167
+
168
+ const chartTitle = extractLegacyChartTitle(trimmed);
169
+
170
+ const labelsRaw = extractArrayForKey(trimmed, "labels");
171
+ const dataRaw = extractArrayForKey(trimmed, "data");
172
+ if (!labelsRaw || !dataRaw) return null;
173
+
174
+ let parsedLabels: unknown;
175
+ let parsedData: unknown;
176
+ try {
177
+ parsedLabels = JSON.parse(labelsRaw);
178
+ parsedData = JSON.parse(dataRaw);
179
+ } catch {
180
+ return null;
181
+ }
182
+ if (
183
+ !Array.isArray(parsedLabels) ||
184
+ parsedLabels.length === 0 ||
185
+ parsedLabels.length > MAX_LABELS ||
186
+ // Reject nested arrays/objects as labels — String(nestedArray) recurses
187
+ // through Array.prototype.join and can throw on deeply nested input.
188
+ !parsedLabels.every((l) =>
189
+ ["string", "number", "boolean"].includes(typeof l),
190
+ )
191
+ ) {
192
+ return null;
193
+ }
194
+ const safeLabels = parsedLabels.map((l) => String(l));
195
+
196
+ const isValidSeriesData = (values: unknown): values is number[] =>
197
+ Array.isArray(values) &&
198
+ values.length === safeLabels.length &&
199
+ values.every((v) => typeof v === "number" && Number.isFinite(v) && v >= 0);
200
+
201
+ const colorMatch = trimmed.match(/(?:^|\s)color=(#[0-9a-fA-F]{3,8})\b/);
202
+ const topLevelColor =
203
+ colorMatch && SAFE_HEX_COLOR.test(colorMatch[1])
204
+ ? colorMatch[1]
205
+ : undefined;
206
+
207
+ let chartSeries: LegacyChartShorthand["series"];
208
+ if (isValidSeriesData(parsedData)) {
209
+ chartSeries = [
210
+ { label: chartTitle || "Value", data: parsedData, color: topLevelColor },
211
+ ];
212
+ } else if (
213
+ Array.isArray(parsedData) &&
214
+ parsedData.length > 0 &&
215
+ // Reject rather than silently truncate an over-limit series count:
216
+ // presenting a partial chart as complete is worse than plain text.
217
+ parsedData.length <= MAX_SERIES &&
218
+ parsedData.every(
219
+ (d) =>
220
+ d &&
221
+ typeof d === "object" &&
222
+ isValidSeriesData((d as { data?: unknown }).data),
223
+ )
224
+ ) {
225
+ chartSeries = (
226
+ parsedData as { label?: unknown; data: number[]; color?: unknown }[]
227
+ ).map((d, idx) => ({
228
+ label: typeof d.label === "string" ? d.label : `Series ${idx + 1}`,
229
+ data: d.data,
230
+ color:
231
+ typeof d.color === "string" && SAFE_HEX_COLOR.test(d.color)
232
+ ? d.color
233
+ : undefined,
234
+ }));
235
+ } else {
236
+ return null;
237
+ }
238
+ if (chartSeries.length === 0) return null;
239
+ return {
240
+ type: chartType,
241
+ title: chartTitle,
242
+ labels: safeLabels,
243
+ series: chartSeries,
244
+ };
245
+ }
246
+
247
+ // Wraps any line matching the legacy shorthand shape in a fenced code block
248
+ // tagged `chart-shorthand`, so it routes through markdownComponents.pre()
249
+ // like any other language fence instead of rendering as inert prose.
250
+ // Fence/indented-code tracking mirrors ../../shared/markdown-block-split.ts
251
+ // (marker char + length, indented-code detection) so real code blocks —
252
+ // including ~~~ fences, longer-than-3 fences, and 4-space/tab indented code —
253
+ // are never mistaken for chat prose and rewritten.
254
+ export function wrapLegacyChartShorthandLines(markdown: string): string {
255
+ if (!markdown.includes("labels=") || !markdown.includes("data=")) {
256
+ return markdown;
257
+ }
258
+ let fenceMarker = ""; // non-empty while inside a ``` or ~~~ fence
259
+ const lines = markdown.split("\n");
260
+ const out: string[] = [];
261
+ for (const line of lines) {
262
+ // CommonMark: a line indented 4+ spaces (or a tab) is an indented code
263
+ // block, not a fence marker. Check this first so a literal four-space
264
+ // " ```" example in a message doesn't toggle fence state.
265
+ if (/^(?: {4}|\t)/.test(line)) {
266
+ out.push(line);
267
+ continue;
268
+ }
269
+ const trimmed = line.trimStart();
270
+ if (fenceMarker) {
271
+ out.push(line);
272
+ const closeMatch = /^(`{3,}|~{3,})\s*$/.exec(trimmed);
273
+ if (
274
+ closeMatch &&
275
+ closeMatch[1].charAt(0) === fenceMarker.charAt(0) &&
276
+ closeMatch[1].length >= fenceMarker.length
277
+ ) {
278
+ fenceMarker = "";
279
+ }
280
+ continue;
281
+ }
282
+ const openMatch = /^(`{3,}|~{3,})/.exec(trimmed);
283
+ if (openMatch) {
284
+ fenceMarker = openMatch[1].charAt(0).repeat(openMatch[1].length);
285
+ out.push(line);
286
+ continue;
287
+ }
288
+ if (looksLikeLegacyChartShorthand(line)) {
289
+ // Preserve the line's own indentation on the emitted fence so
290
+ // shorthand inside a list item or blockquote continuation stays part
291
+ // of that container instead of being dedented to a top-level block.
292
+ const leadingWs = line.match(/^\s*/)?.[0] ?? "";
293
+ out.push(
294
+ leadingWs + "```" + LEGACY_CHART_SHORTHAND_LANG,
295
+ leadingWs + line.trim(),
296
+ leadingWs + "```",
297
+ );
298
+ continue;
299
+ }
300
+ out.push(line);
301
+ }
302
+ return out.join("\n");
303
+ }
304
+
305
+ export function LegacyChartShorthandFallback({ text }: { text: string }) {
306
+ return <span style={{ whiteSpace: "pre-wrap" }}>{text}</span>;
307
+ }
308
+
309
+ export function LegacyChartShorthandChart({
310
+ parsed,
311
+ }: {
312
+ parsed: LegacyChartShorthand;
313
+ }) {
314
+ const { title, labels, series, type } = parsed;
315
+ const width = 640;
316
+ const height = 280;
317
+ const padding = { top: 16, right: 16, bottom: 44, left: 48 };
318
+ const innerW = width - padding.left - padding.right;
319
+ const innerH = height - padding.top - padding.bottom;
320
+ const maxVal = Math.max(
321
+ 1,
322
+ series.reduce(
323
+ (acc, s) => s.data.reduce((seriesAcc, v) => Math.max(seriesAcc, v), acc),
324
+ 0,
325
+ ),
326
+ );
327
+ const groupW = innerW / labels.length;
328
+ const gridLineCount = 4;
329
+ const gridLines = Array.from({ length: gridLineCount + 1 }, (_, g) => {
330
+ const y = padding.top + (innerH * g) / gridLineCount;
331
+ const val = Math.round(maxVal - (maxVal * g) / gridLineCount);
332
+ return { y, val };
333
+ });
334
+
335
+ const colorFor = (s: LegacyChartShorthand["series"][number], si: number) =>
336
+ s.color || LEGACY_CHART_PALETTE[si % LEGACY_CHART_PALETTE.length];
337
+
338
+ return (
339
+ <div className="my-4 rounded-lg border border-border bg-muted/20 p-3">
340
+ {title && (
341
+ <div className="mb-1 text-sm font-medium text-foreground">{title}</div>
342
+ )}
343
+ <svg
344
+ viewBox={`0 0 ${width} ${height}`}
345
+ className="w-full text-muted-foreground"
346
+ role="img"
347
+ aria-label={title || "Chart"}
348
+ >
349
+ {gridLines.map(({ y, val }, i) => (
350
+ <React.Fragment key={i}>
351
+ <line
352
+ x1={padding.left}
353
+ y1={y}
354
+ x2={width - padding.right}
355
+ y2={y}
356
+ stroke="currentColor"
357
+ strokeOpacity={0.1}
358
+ />
359
+ <text
360
+ x={padding.left - 8}
361
+ y={y + 4}
362
+ textAnchor="end"
363
+ fontSize={10}
364
+ fill="currentColor"
365
+ fillOpacity={0.6}
366
+ >
367
+ {val}
368
+ </text>
369
+ </React.Fragment>
370
+ ))}
371
+
372
+ {type === "bar" &&
373
+ labels.map((_, li) => {
374
+ const groupPad = groupW * 0.15;
375
+ const barsW = groupW - groupPad * 2;
376
+ const barW = barsW / series.length;
377
+ return series.map((s, si) => {
378
+ const value = s.data[li] ?? 0;
379
+ const barH = (Math.max(0, value) / maxVal) * innerH;
380
+ const x = padding.left + li * groupW + groupPad + si * barW;
381
+ const y = padding.top + (innerH - barH);
382
+ return (
383
+ <rect
384
+ key={`${li}-${si}`}
385
+ x={x}
386
+ y={y}
387
+ width={Math.max(1, barW - 2)}
388
+ height={Math.max(0, barH)}
389
+ fill={colorFor(s, si)}
390
+ rx={2}
391
+ />
392
+ );
393
+ });
394
+ })}
395
+
396
+ {type !== "bar" &&
397
+ series.map((s, si) => {
398
+ const coords = labels.map((_, li) => {
399
+ const value = s.data[li] ?? 0;
400
+ const x = padding.left + groupW * li + groupW / 2;
401
+ const y =
402
+ padding.top + innerH - (Math.max(0, value) / maxVal) * innerH;
403
+ return { x, y };
404
+ });
405
+ if (coords.length === 1) {
406
+ // A single point has no line segment to draw — render a marker
407
+ // instead of an invisible zero-length polyline.
408
+ return (
409
+ <circle
410
+ key={si}
411
+ cx={coords[0].x}
412
+ cy={coords[0].y}
413
+ r={4}
414
+ fill={colorFor(s, si)}
415
+ />
416
+ );
417
+ }
418
+ const points = coords
419
+ .map(({ x, y }) => `${x.toFixed(1)},${y.toFixed(1)}`)
420
+ .join(" ");
421
+ const baseY = padding.top + innerH;
422
+ const firstX = padding.left + groupW / 2;
423
+ const lastX =
424
+ padding.left + groupW * (labels.length - 1) + groupW / 2;
425
+ return (
426
+ <React.Fragment key={si}>
427
+ {type === "area" && (
428
+ <polygon
429
+ points={`${firstX.toFixed(1)},${baseY.toFixed(1)} ${points} ${lastX.toFixed(1)},${baseY.toFixed(1)}`}
430
+ fill={colorFor(s, si)}
431
+ fillOpacity={0.15}
432
+ />
433
+ )}
434
+ <polyline
435
+ points={points}
436
+ fill="none"
437
+ stroke={colorFor(s, si)}
438
+ strokeWidth={2}
439
+ />
440
+ </React.Fragment>
441
+ );
442
+ })}
443
+
444
+ {labels.map((label, li) => {
445
+ const x = padding.left + groupW * li + groupW / 2;
446
+ const y = height - padding.bottom + 16;
447
+ const truncated =
448
+ label.length > 12 ? `${label.slice(0, 11)}...` : label;
449
+ return (
450
+ <text
451
+ key={li}
452
+ x={x}
453
+ y={y}
454
+ textAnchor="middle"
455
+ fontSize={10}
456
+ fill="currentColor"
457
+ fillOpacity={0.7}
458
+ >
459
+ {truncated}
460
+ </text>
461
+ );
462
+ })}
463
+ </svg>
464
+ {series.length > 1 && (
465
+ <div className="mt-2 flex flex-wrap gap-3 text-xs text-muted-foreground">
466
+ {series.map((s, si) => (
467
+ <span key={si} className="inline-flex items-center gap-1">
468
+ <span
469
+ className="inline-block h-2 w-2 rounded-full"
470
+ style={{ background: colorFor(s, si) }}
471
+ />
472
+ {s.label}
473
+ </span>
474
+ ))}
475
+ </div>
476
+ )}
477
+ </div>
478
+ );
479
+ }
@@ -32,6 +32,13 @@ import { NEW_CHAT_ACTION_HREF } from "../error-format.js";
32
32
  import { HighlightedCodeBlock as SharedHighlightedCodeBlock } from "../HighlightedCodeBlock.js";
33
33
  import { IframeEmbed, parseEmbedBody } from "../IframeEmbed.js";
34
34
  import { cn } from "../utils.js";
35
+ import {
36
+ LEGACY_CHART_SHORTHAND_LANG,
37
+ LegacyChartShorthandChart,
38
+ LegacyChartShorthandFallback,
39
+ parseLegacyChartShorthand,
40
+ wrapLegacyChartShorthandLines,
41
+ } from "./legacy-chart-shorthand.js";
35
42
 
36
43
  // ─── Lazy markdown loader ────────────────────────────────────────────────────
37
44
  // react-markdown + remark-gfm are deferred so they stay off the critical path
@@ -284,6 +291,19 @@ export const markdownComponents = {
284
291
  <IframeEmbed {...(parsed as Parameters<typeof IframeEmbed>[0])} />
285
292
  );
286
293
  }
294
+ if (
295
+ new RegExp(`\\blanguage-${LEGACY_CHART_SHORTHAND_LANG}\\b`).test(
296
+ className,
297
+ )
298
+ ) {
299
+ const body = extractCodeText(childProps.children).replace(/\n$/, "");
300
+ const parsed = parseLegacyChartShorthand(body);
301
+ return parsed ? (
302
+ <LegacyChartShorthandChart parsed={parsed} />
303
+ ) : (
304
+ <LegacyChartShorthandFallback text={body} />
305
+ );
306
+ }
287
307
  const langMatch = className.match(/\blanguage-([\w+-]+)\b/);
288
308
  if (langMatch) {
289
309
  const code = extractCodeText(childProps.children).replace(/\n$/, "");
@@ -589,7 +609,7 @@ export const MemoizedMarkdownBlock = React.memo(function MemoizedMarkdownBlock({
589
609
  components={markdownComponents}
590
610
  urlTransform={markdownUrlTransform}
591
611
  >
592
- {blockText}
612
+ {wrapLegacyChartShorthandLines(blockText)}
593
613
  </ReactMarkdown>
594
614
  );
595
615
  });
@@ -642,7 +662,7 @@ export function SmoothMarkdownText({
642
662
  components={markdownComponents}
643
663
  urlTransform={markdownUrlTransform}
644
664
  >
645
- {split.tail}
665
+ {wrapLegacyChartShorthandLines(split.tail)}
646
666
  </ReactMarkdown>
647
667
  ) : null}
648
668
  </>
@@ -653,7 +673,7 @@ export function SmoothMarkdownText({
653
673
  components={markdownComponents}
654
674
  urlTransform={markdownUrlTransform}
655
675
  >
656
- {visibleText}
676
+ {wrapLegacyChartShorthandLines(visibleText)}
657
677
  </ReactMarkdown>
658
678
  )
659
679
  ) : (
@@ -948,6 +948,7 @@ export function assistantMessageHasCompletedCustomUi(
948
948
  type?: unknown;
949
949
  result?: unknown;
950
950
  isError?: unknown;
951
+ outcome?: unknown;
951
952
  activity?: unknown;
952
953
  chatUI?: unknown;
953
954
  mcpApp?: unknown;
@@ -956,7 +957,8 @@ export function assistantMessageHasCompletedCustomUi(
956
957
  record.type !== "tool-call" ||
957
958
  record.activity === true ||
958
959
  record.result === undefined ||
959
- record.isError === true
960
+ record.isError === true ||
961
+ record.outcome === "unknown"
960
962
  ) {
961
963
  continue;
962
964
  }
@@ -1085,13 +1087,20 @@ export function shouldShowAssistantWorkSummary({
1085
1087
  isComplete,
1086
1088
  hasCollapsibleWork,
1087
1089
  hasUnresolvedTool,
1090
+ chatRunning,
1088
1091
  }: {
1089
1092
  isLast: boolean;
1090
1093
  isComplete: boolean;
1091
1094
  hasCollapsibleWork: boolean;
1092
1095
  hasUnresolvedTool: boolean;
1096
+ chatRunning: boolean;
1093
1097
  }): boolean {
1094
- if (!hasCollapsibleWork || hasUnresolvedTool) return false;
1098
+ if (!hasCollapsibleWork) return false;
1099
+
1100
+ // An unresolved tool means "still working" only while the turn is actually
1101
+ // running. On a stalled or interrupted turn it used to hide the summary
1102
+ // forever, so the longest turns showed no "Worked for Xm Ys" at all.
1103
+ if (hasUnresolvedTool) return !(isLast && chatRunning);
1095
1104
 
1096
1105
  // Keep completed historical work wrapped while a later turn is running.
1097
1106
  // Removing the wrapper exposes/remounts ReasoningCell and resets its
@@ -1447,6 +1456,7 @@ export function AssistantMessage() {
1447
1456
  isComplete,
1448
1457
  hasCollapsibleWork,
1449
1458
  hasUnresolvedTool,
1459
+ chatRunning,
1450
1460
  });
1451
1461
  if (!showSummary) return <>{children}</>;
1452
1462
  return (
@@ -1510,9 +1520,6 @@ export function AssistantMessage() {
1510
1520
  variant="response"
1511
1521
  />
1512
1522
  )}
1513
- {isLast && hasUnresolvedTool && !chatRunning && (
1514
- <RunningActivityStatus label="Thinking" />
1515
- )}
1516
1523
  </div>
1517
1524
  {isComplete && (
1518
1525
  <div className="mt-1 flex items-center justify-between">
@@ -1656,7 +1656,7 @@ function applyRuntimeEventToContent(
1656
1656
  }
1657
1657
  if (typed.type === "error") {
1658
1658
  const normalized = normalizeChatError(typed.error, typed.code);
1659
- settleInterruptedToolCalls(content);
1659
+ settleInterruptedToolCalls(content, undefined, { includeActivity: true });
1660
1660
  content.push({
1661
1661
  type: "text",
1662
1662
  text: formatChatErrorText(typed.error, undefined, typed.code),
@@ -1677,6 +1677,7 @@ function applyRuntimeEventToContent(
1677
1677
  } as ChatModelRunResult;
1678
1678
  }
1679
1679
  if (typed.type === "done") {
1680
+ settleInterruptedToolCalls(content, undefined, { includeActivity: true });
1680
1681
  return {
1681
1682
  content: [...content],
1682
1683
  status: