@agent-native/core 0.98.4 → 0.98.6

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 (314) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/durable-background-runs.mdx +37 -0
  4. package/corpus/core/docs/content/external-agents.mdx +62 -7
  5. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +38 -7
  6. package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +2 -0
  7. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +39 -7
  8. package/corpus/core/docs/content/locales/de-DE/tracking.mdx +2 -0
  9. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +39 -7
  10. package/corpus/core/docs/content/locales/es-ES/tracking.mdx +2 -0
  11. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +39 -7
  12. package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +4 -2
  13. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +38 -7
  14. package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +2 -0
  15. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +38 -7
  16. package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +2 -0
  17. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +38 -7
  18. package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +2 -0
  19. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +39 -7
  20. package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +2 -0
  21. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +37 -7
  22. package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +2 -0
  23. package/corpus/core/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
  24. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +37 -7
  25. package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +2 -0
  26. package/corpus/core/docs/content/tracking.mdx +2 -0
  27. package/corpus/core/package.json +1 -1
  28. package/corpus/core/src/a2a/handlers.ts +95 -21
  29. package/corpus/core/src/a2a/task-store.ts +65 -3
  30. package/corpus/core/src/agent/durable-background.ts +16 -16
  31. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +25 -8
  32. package/corpus/core/src/agent/engine/anthropic-engine.ts +17 -3
  33. package/corpus/core/src/agent/engine/output-tokens.ts +6 -6
  34. package/corpus/core/src/agent/production-agent.ts +200 -27
  35. package/corpus/core/src/agent/run-manager.ts +81 -1
  36. package/corpus/core/src/agent/run-store.ts +301 -41
  37. package/corpus/core/src/client/AgentPanel.tsx +6 -2
  38. package/corpus/core/src/client/AssistantChat.tsx +364 -69
  39. package/corpus/core/src/client/agent-chat-adapter.ts +169 -1
  40. package/corpus/core/src/client/chat/message-components.tsx +3 -0
  41. package/corpus/core/src/client/chat/tool-call-display.tsx +20 -36
  42. package/corpus/core/src/client/conversation/use-near-bottom-autoscroll.ts +7 -0
  43. package/corpus/core/src/client/extensions/AgentNativeExtensionFrame.tsx +2 -0
  44. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +2 -0
  45. package/corpus/core/src/client/extensions/ExtensionEditor.tsx +9 -2
  46. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +2 -0
  47. package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +2 -0
  48. package/corpus/core/src/client/extensions/portable-extension.ts +2 -0
  49. package/corpus/core/src/client/session-replay.ts +361 -29
  50. package/corpus/core/src/client/sse-event-processor.ts +43 -1
  51. package/corpus/core/src/demo/actions/toggle-demo-mode.ts +1 -1
  52. package/corpus/core/src/demo/config.ts +6 -6
  53. package/corpus/core/src/demo/fetch-interceptor.ts +13 -4
  54. package/corpus/core/src/demo/redact.ts +13 -149
  55. package/corpus/core/src/extensions/html-shell.ts +3 -0
  56. package/corpus/core/src/extensions/session-replay-iframe.ts +131 -0
  57. package/corpus/core/src/integrations/adapters/slack.ts +123 -13
  58. package/corpus/core/src/integrations/identity-links-store.ts +210 -0
  59. package/corpus/core/src/integrations/identity.ts +132 -0
  60. package/corpus/core/src/integrations/index.ts +6 -0
  61. package/corpus/core/src/integrations/plugin.ts +58 -1
  62. package/corpus/core/src/integrations/types.ts +7 -0
  63. package/corpus/core/src/mcp/build-server.ts +127 -22
  64. package/corpus/core/src/mcp/external-agent-policy.ts +18 -0
  65. package/corpus/core/src/mcp/index.ts +1 -0
  66. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +1 -20
  67. package/corpus/core/src/server/agent-chat/plugin-options.ts +12 -1
  68. package/corpus/core/src/server/agent-chat/script-entries.ts +20 -3
  69. package/corpus/core/src/server/agent-chat-plugin.ts +3 -0
  70. package/corpus/core/src/session-replay-iframe-protocol.ts +58 -0
  71. package/corpus/core/src/shared/reasoning-effort.ts +35 -0
  72. package/corpus/core/src/styles/agent-conversation.css +26 -0
  73. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  74. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
  75. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +30 -21
  76. package/corpus/templates/analytics/AGENTS.md +27 -0
  77. package/corpus/templates/analytics/actions/create-session-replay-agent-link.ts +0 -1
  78. package/corpus/templates/analytics/actions/get-error-issue.ts +1 -0
  79. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +1 -0
  80. package/corpus/templates/analytics/actions/get-session-replay-timeline.ts +40 -0
  81. package/corpus/templates/analytics/actions/list-error-issues.ts +13 -0
  82. package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -0
  83. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -0
  84. package/corpus/templates/analytics/app/global.css +2 -2
  85. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +41 -4
  86. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +52 -203
  87. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +69 -16
  88. package/corpus/templates/analytics/changelog/2026-07-12-analytics-chat-retries-before-showing-a-generic-no-data-mess.md +6 -0
  89. package/corpus/templates/analytics/changelog/2026-07-12-analytics-sessions-search-no-longer-loses-fast-keystrokes-an.md +6 -0
  90. package/corpus/templates/analytics/changelog/2026-07-12-chart-selection-respects-chat-state.md +6 -0
  91. package/corpus/templates/analytics/changelog/2026-07-12-connected-external-agents-can-now-look-up-sessions-error-iss.md +6 -0
  92. package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-anonymizes-error-reporting-emails.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-12-session-identities-stay-visible-when-demo-mode-is-off.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-12-session-replays-show-iframe-content.md +6 -0
  95. package/corpus/templates/analytics/netlify.toml +0 -3
  96. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +14 -0
  97. package/corpus/templates/analytics/server/lib/error-capture.ts +101 -2
  98. package/corpus/templates/analytics/server/lib/real-data-actions.ts +24 -0
  99. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +49 -17
  100. package/corpus/templates/analytics/server/lib/session-replay.ts +18 -16
  101. package/corpus/templates/analytics/server/plugins/agent-chat.ts +43 -6
  102. package/corpus/templates/analytics/server/plugins/db.ts +15 -0
  103. package/corpus/templates/assets/app/global.css +1 -1
  104. package/corpus/templates/brain/app/global.css +2 -2
  105. package/corpus/templates/brain/changelog/2026-07-12-fix-long-response-progress.md +6 -0
  106. package/corpus/templates/forms/app/components/CloudUpgrade.tsx +1 -1
  107. package/corpus/templates/forms/app/components/ThemeToggle.tsx +22 -6
  108. package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +5 -5
  109. package/corpus/templates/forms/app/components/builder/FieldRenderer.tsx +3 -3
  110. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +16 -16
  111. package/corpus/templates/forms/app/global.css +164 -13
  112. package/corpus/templates/forms/app/pages/AskPage.tsx +35 -13
  113. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +143 -67
  114. package/corpus/templates/forms/app/pages/FormFillPage.tsx +44 -11
  115. package/corpus/templates/forms/app/pages/FormsListPage.tsx +42 -24
  116. package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +28 -15
  117. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +46 -20
  118. package/corpus/templates/forms/app/routes/_app.settings.tsx +1 -1
  119. package/corpus/templates/forms/changelog/2026-07-12-ask-forms-suggestions-now-sit-beneath-the-composer-for-a-tig.md +6 -0
  120. package/corpus/templates/forms/changelog/2026-07-12-builder-response-tables-now-fill-the-view-without-a-redundan.md +6 -0
  121. package/corpus/templates/forms/changelog/2026-07-12-form-builder-surfaces-are-calmer-fields-stay-stable-on-hover.md +6 -0
  122. package/corpus/templates/forms/changelog/2026-07-12-response-tables-now-fill-the-available-pane-with-a-flexible-.md +6 -0
  123. package/corpus/templates/forms/changelog/2026-07-12-smoother-more-tactile-controls-throughout-animated-icon-and-.md +6 -0
  124. package/corpus/templates/mail/AGENTS.md +3 -1
  125. package/corpus/templates/mail/app/components/email/EmailThread.tsx +21 -13
  126. package/corpus/templates/mail/app/components/email/email-iframe-document.ts +18 -0
  127. package/corpus/templates/mail/server/lib/mail-integrations.ts +3 -0
  128. package/dist/a2a/handlers.d.ts.map +1 -1
  129. package/dist/a2a/handlers.js +84 -15
  130. package/dist/a2a/handlers.js.map +1 -1
  131. package/dist/a2a/task-store.d.ts +20 -1
  132. package/dist/a2a/task-store.d.ts.map +1 -1
  133. package/dist/a2a/task-store.js +57 -4
  134. package/dist/a2a/task-store.js.map +1 -1
  135. package/dist/agent/durable-background.d.ts +10 -10
  136. package/dist/agent/durable-background.d.ts.map +1 -1
  137. package/dist/agent/durable-background.js +16 -16
  138. package/dist/agent/durable-background.js.map +1 -1
  139. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  140. package/dist/agent/engine/ai-sdk-engine.js +19 -8
  141. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  142. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  143. package/dist/agent/engine/anthropic-engine.js +11 -3
  144. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  145. package/dist/agent/engine/output-tokens.js +6 -6
  146. package/dist/agent/engine/output-tokens.js.map +1 -1
  147. package/dist/agent/production-agent.d.ts +52 -0
  148. package/dist/agent/production-agent.d.ts.map +1 -1
  149. package/dist/agent/production-agent.js +154 -26
  150. package/dist/agent/production-agent.js.map +1 -1
  151. package/dist/agent/run-manager.d.ts +9 -0
  152. package/dist/agent/run-manager.d.ts.map +1 -1
  153. package/dist/agent/run-manager.js +71 -1
  154. package/dist/agent/run-manager.js.map +1 -1
  155. package/dist/agent/run-store.d.ts +9 -0
  156. package/dist/agent/run-store.d.ts.map +1 -1
  157. package/dist/agent/run-store.js +243 -48
  158. package/dist/agent/run-store.js.map +1 -1
  159. package/dist/client/AgentPanel.d.ts.map +1 -1
  160. package/dist/client/AgentPanel.js +6 -2
  161. package/dist/client/AgentPanel.js.map +1 -1
  162. package/dist/client/AssistantChat.d.ts +21 -0
  163. package/dist/client/AssistantChat.d.ts.map +1 -1
  164. package/dist/client/AssistantChat.js +298 -67
  165. package/dist/client/AssistantChat.js.map +1 -1
  166. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  167. package/dist/client/agent-chat-adapter.js +146 -1
  168. package/dist/client/agent-chat-adapter.js.map +1 -1
  169. package/dist/client/chat/message-components.d.ts.map +1 -1
  170. package/dist/client/chat/message-components.js +2 -1
  171. package/dist/client/chat/message-components.js.map +1 -1
  172. package/dist/client/chat/tool-call-display.d.ts +3 -1
  173. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  174. package/dist/client/chat/tool-call-display.js +13 -37
  175. package/dist/client/chat/tool-call-display.js.map +1 -1
  176. package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +1 -0
  177. package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
  178. package/dist/client/conversation/use-near-bottom-autoscroll.js +6 -0
  179. package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
  180. package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -1
  181. package/dist/client/extensions/AgentNativeExtensionFrame.js +2 -1
  182. package/dist/client/extensions/AgentNativeExtensionFrame.js.map +1 -1
  183. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  184. package/dist/client/extensions/EmbeddedExtension.js +2 -1
  185. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  186. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
  187. package/dist/client/extensions/ExtensionEditor.js +5 -2
  188. package/dist/client/extensions/ExtensionEditor.js.map +1 -1
  189. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  190. package/dist/client/extensions/ExtensionViewer.js +2 -1
  191. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  192. package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
  193. package/dist/client/extensions/InlineExtensionFrame.js +2 -1
  194. package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
  195. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  196. package/dist/client/extensions/portable-extension.js +2 -0
  197. package/dist/client/extensions/portable-extension.js.map +1 -1
  198. package/dist/client/session-replay.d.ts.map +1 -1
  199. package/dist/client/session-replay.js +308 -29
  200. package/dist/client/session-replay.js.map +1 -1
  201. package/dist/client/sse-event-processor.d.ts +5 -0
  202. package/dist/client/sse-event-processor.d.ts.map +1 -1
  203. package/dist/client/sse-event-processor.js +37 -1
  204. package/dist/client/sse-event-processor.js.map +1 -1
  205. package/dist/collab/routes.d.ts +1 -1
  206. package/dist/demo/actions/toggle-demo-mode.js +1 -1
  207. package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
  208. package/dist/demo/config.js +6 -6
  209. package/dist/demo/config.js.map +1 -1
  210. package/dist/demo/fetch-interceptor.d.ts.map +1 -1
  211. package/dist/demo/fetch-interceptor.js +13 -4
  212. package/dist/demo/fetch-interceptor.js.map +1 -1
  213. package/dist/demo/redact.d.ts +7 -7
  214. package/dist/demo/redact.d.ts.map +1 -1
  215. package/dist/demo/redact.js +8 -145
  216. package/dist/demo/redact.js.map +1 -1
  217. package/dist/extensions/html-shell.d.ts.map +1 -1
  218. package/dist/extensions/html-shell.js +2 -0
  219. package/dist/extensions/html-shell.js.map +1 -1
  220. package/dist/extensions/session-replay-iframe.d.ts +10 -0
  221. package/dist/extensions/session-replay-iframe.d.ts.map +1 -0
  222. package/dist/extensions/session-replay-iframe.js +121 -0
  223. package/dist/extensions/session-replay-iframe.js.map +1 -0
  224. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  225. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  226. package/dist/integrations/adapters/slack.js +98 -13
  227. package/dist/integrations/adapters/slack.js.map +1 -1
  228. package/dist/integrations/identity-links-store.d.ts +23 -0
  229. package/dist/integrations/identity-links-store.d.ts.map +1 -0
  230. package/dist/integrations/identity-links-store.js +158 -0
  231. package/dist/integrations/identity-links-store.js.map +1 -0
  232. package/dist/integrations/identity.d.ts +11 -0
  233. package/dist/integrations/identity.d.ts.map +1 -0
  234. package/dist/integrations/identity.js +100 -0
  235. package/dist/integrations/identity.js.map +1 -0
  236. package/dist/integrations/index.d.ts +2 -0
  237. package/dist/integrations/index.d.ts.map +1 -1
  238. package/dist/integrations/index.js +2 -0
  239. package/dist/integrations/index.js.map +1 -1
  240. package/dist/integrations/plugin.d.ts.map +1 -1
  241. package/dist/integrations/plugin.js +43 -1
  242. package/dist/integrations/plugin.js.map +1 -1
  243. package/dist/integrations/types.d.ts +6 -0
  244. package/dist/integrations/types.d.ts.map +1 -1
  245. package/dist/integrations/types.js.map +1 -1
  246. package/dist/mcp/build-server.d.ts +9 -2
  247. package/dist/mcp/build-server.d.ts.map +1 -1
  248. package/dist/mcp/build-server.js +97 -20
  249. package/dist/mcp/build-server.js.map +1 -1
  250. package/dist/mcp/external-agent-policy.d.ts +19 -0
  251. package/dist/mcp/external-agent-policy.d.ts.map +1 -0
  252. package/dist/mcp/external-agent-policy.js +2 -0
  253. package/dist/mcp/external-agent-policy.js.map +1 -0
  254. package/dist/mcp/index.d.ts +1 -0
  255. package/dist/mcp/index.d.ts.map +1 -1
  256. package/dist/mcp/index.js.map +1 -1
  257. package/dist/notifications/routes.d.ts +1 -1
  258. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  259. package/dist/scripts/agent-engines/list-agent-engines.js +1 -19
  260. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  261. package/dist/server/agent-chat/plugin-options.d.ts +11 -1
  262. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  263. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  264. package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
  265. package/dist/server/agent-chat/script-entries.js +16 -2
  266. package/dist/server/agent-chat/script-entries.js.map +1 -1
  267. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  268. package/dist/server/agent-chat-plugin.js +3 -0
  269. package/dist/server/agent-chat-plugin.js.map +1 -1
  270. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  271. package/dist/server/transcribe-voice.d.ts +1 -1
  272. package/dist/session-replay-iframe-protocol.d.ts +36 -0
  273. package/dist/session-replay-iframe-protocol.d.ts.map +1 -0
  274. package/dist/session-replay-iframe-protocol.js +24 -0
  275. package/dist/session-replay-iframe-protocol.js.map +1 -0
  276. package/dist/shared/reasoning-effort.d.ts +11 -0
  277. package/dist/shared/reasoning-effort.d.ts.map +1 -1
  278. package/dist/shared/reasoning-effort.js +36 -0
  279. package/dist/shared/reasoning-effort.js.map +1 -1
  280. package/dist/styles/agent-conversation.css +26 -0
  281. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  282. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
  283. package/docs/content/durable-background-runs.mdx +37 -0
  284. package/docs/content/external-agents.mdx +62 -7
  285. package/docs/content/locales/ar-SA/external-agents.mdx +38 -7
  286. package/docs/content/locales/ar-SA/tracking.mdx +2 -0
  287. package/docs/content/locales/de-DE/external-agents.mdx +39 -7
  288. package/docs/content/locales/de-DE/tracking.mdx +2 -0
  289. package/docs/content/locales/es-ES/external-agents.mdx +39 -7
  290. package/docs/content/locales/es-ES/tracking.mdx +2 -0
  291. package/docs/content/locales/fr-FR/external-agents.mdx +39 -7
  292. package/docs/content/locales/fr-FR/tracking.mdx +4 -2
  293. package/docs/content/locales/hi-IN/external-agents.mdx +38 -7
  294. package/docs/content/locales/hi-IN/tracking.mdx +2 -0
  295. package/docs/content/locales/ja-JP/external-agents.mdx +38 -7
  296. package/docs/content/locales/ja-JP/tracking.mdx +2 -0
  297. package/docs/content/locales/ko-KR/external-agents.mdx +38 -7
  298. package/docs/content/locales/ko-KR/tracking.mdx +2 -0
  299. package/docs/content/locales/pt-BR/external-agents.mdx +39 -7
  300. package/docs/content/locales/pt-BR/tracking.mdx +2 -0
  301. package/docs/content/locales/zh-CN/external-agents.mdx +37 -7
  302. package/docs/content/locales/zh-CN/tracking.mdx +2 -0
  303. package/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
  304. package/docs/content/locales/zh-TW/external-agents.mdx +37 -7
  305. package/docs/content/locales/zh-TW/tracking.mdx +2 -0
  306. package/docs/content/tracking.mdx +2 -0
  307. package/package.json +2 -2
  308. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  309. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
  310. package/corpus/core/src/observability/hosted-model-experiment.ts +0 -118
  311. package/dist/observability/hosted-model-experiment.d.ts +0 -39
  312. package/dist/observability/hosted-model-experiment.d.ts.map +0 -1
  313. package/dist/observability/hosted-model-experiment.js +0 -90
  314. package/dist/observability/hosted-model-experiment.js.map +0 -1
@@ -3,6 +3,7 @@
3
3
  * Enables cross-isolate access on Cloudflare Workers and
4
4
  * reliable reconnection after page refreshes.
5
5
  */
6
+ import type { DbExec } from "../db/client.js";
6
7
  import { getDbExec, intType, isPostgres } from "../db/client.js";
7
8
  import { ensureColumnExists, ensureTableExists } from "../db/ddl-guard.js";
8
9
  import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
@@ -146,6 +147,17 @@ export const UNCLAIMED_BACKGROUND_RUN_REDISPATCH_BOUND_MS = 5 * 60_000;
146
147
  */
147
148
  export const UNCLAIMED_BACKGROUND_RUN_FAST_SWEEP_MS = 20_000;
148
149
 
150
+ /**
151
+ * FIX 3 (durable-background incident) per-turn run-count ceiling for
152
+ * stale-run recovery — mirrors `chainServerDrivenContinuation`'s own ledger
153
+ * guard in production-agent.ts (`MAX_BACKGROUND_RUN_CONTINUATIONS + 5` = 25).
154
+ * Duplicated as a literal rather than imported: production-agent.ts already
155
+ * imports run-manager.ts, which imports this file, so a runtime import back
156
+ * from here would be circular. Keep this numerically in sync if that
157
+ * constant ever changes.
158
+ */
159
+ const STALE_RUN_RECOVERY_MAX_TURN_RUNS = 25;
160
+
149
161
  /**
150
162
  * Maximum time the stale reapers (`reapIfStale`, `reapAllStaleRuns`,
151
163
  * `cleanupOldRuns`'s heartbeat-stale pass) will suspend reaping a "running"
@@ -1175,6 +1187,15 @@ export const RUN_DIAG_STAGE = {
1175
1187
  * recovered by running the turn inline. The run still completes for the user.
1176
1188
  */
1177
1189
  foregroundInlineRecovery: "foreground_inline_recovery",
1190
+ /**
1191
+ * FIX 3 (durable-background incident): a stale-run reaper (`reapIfStale` /
1192
+ * `reapAllStaleRuns`) found this background chat-turn run dead (heartbeat
1193
+ * stale, no terminal event) and attempted server-owned recovery — detail
1194
+ * carries the outcome (a recovered successor's runId, or why recovery was
1195
+ * declined: not eligible, payload missing, a newer run already exists, or
1196
+ * the per-turn budget is exhausted). See `attemptStaleRunRecovery`.
1197
+ */
1198
+ staleRunRecoveryAttempted: "stale_run_recovery_attempted",
1178
1199
  } as const;
1179
1200
 
1180
1201
  export type RunDiagStage = (typeof RUN_DIAG_STAGE)[keyof typeof RUN_DIAG_STAGE];
@@ -1307,18 +1328,200 @@ export async function setRunInFlightMarker(
1307
1328
  }
1308
1329
  }
1309
1330
 
1331
+ /** A recovery successor row created by `attemptStaleRunRecovery`. */
1332
+ interface StaleRunRecoverySuccessor {
1333
+ successorRunId: string;
1334
+ threadId: string;
1335
+ turnId: string;
1336
+ }
1337
+
1310
1338
  /**
1311
- * If the given run is marked "running" in SQL but its heartbeat is stale
1312
- * (producer likely crashed), flip it to "errored" so watchers stop waiting.
1313
- * Returns true if the row was reaped.
1339
+ * FIX 3 (durable-background incident) discriminated outcome of a recovery
1340
+ * attempt recorded as a diag stage (except `not_background`, the
1341
+ * overwhelmingly common case for every ordinary foreground reap) so a
1342
+ * silently-died background worker's fate is diagnosable without bg-fn logs.
1314
1343
  */
1315
- export async function reapIfStale(
1344
+ type StaleRunRecoveryOutcome =
1345
+ | ({ outcome: "recovered" } & StaleRunRecoverySuccessor)
1346
+ | { outcome: "not_background" }
1347
+ | { outcome: "payload_missing" }
1348
+ | { outcome: "newer_run_exists" }
1349
+ | { outcome: "budget_exhausted" };
1350
+
1351
+ /**
1352
+ * Mirrors `production-agent.ts`'s `generateRunId` — duplicated (not
1353
+ * imported) to avoid a run-store.ts <-> production-agent.ts import cycle
1354
+ * (production-agent.ts already imports run-manager.ts, which imports this
1355
+ * file). Keep the format in sync if that one changes.
1356
+ */
1357
+ function generateRecoveryRunId(): string {
1358
+ return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1359
+ }
1360
+
1361
+ /**
1362
+ * FIX 3 (durable-background incident): when a stale-run reaper is about to
1363
+ * flip a BACKGROUND chat-turn run to errored/stale_run, attempt to keep the
1364
+ * TURN alive instead of leaving it dead — a background worker has no
1365
+ * connected client watching it, unlike a foreground run, so nothing else
1366
+ * would ever recover it. Inserts an UNCLAIMED successor row (same turnId,
1367
+ * `dispatch_payload` carried over from the dying run) that the existing
1368
+ * unclaimed-background-run sweep (`agent-chat-plugin.ts`, ~20s fast sweep)
1369
+ * picks up and redispatches automatically — this function never dispatches
1370
+ * anything itself beyond the best-effort immediate attempt its caller fires.
1371
+ *
1372
+ * `db` is threaded through (rather than calling `getDbExec()` internally)
1373
+ * so the caller can run this INSIDE the same transaction as the reap-to-
1374
+ * errored write — see `reapSingleStaleRun` for why that matters.
1375
+ *
1376
+ * Eligibility (ALL must hold, or this is a documented no-op — the caller's
1377
+ * normal loud stale_run failure proceeds unchanged):
1378
+ * - the row is a background chat-turn dispatch (`dispatch_mode` starting
1379
+ * with "background") — a foreground/foreground-self-chain run has a
1380
+ * connected client to recover it via its own `auto_continue` re-POST.
1381
+ * - its `dispatch_payload` is still present — without it there is nothing
1382
+ * to rehydrate the successor's request body from. Read HERE, before the
1383
+ * caller's terminal write (which NULLs `dispatch_payload` on every other
1384
+ * path), so it survives long enough to carry over.
1385
+ * - no newer run already exists for the same turn — avoids stacking a
1386
+ * second successor onto a turn a previous recovery (or a normal
1387
+ * `chainServerDrivenContinuation`) already continued. Combined with the
1388
+ * caller's own atomic "did I win the reap" gate, this guarantees AT MOST
1389
+ * ONE recovery successor per reaped run even under concurrent reapers.
1390
+ * - the per-turn run ledger (`countRunsForTurn`'s underlying query) has
1391
+ * room (`STALE_RUN_RECOVERY_MAX_TURN_RUNS`) — mirrors
1392
+ * `chainServerDrivenContinuation`'s own budget guard so a pathological
1393
+ * turn can't loop forever through reaper-driven recovery either.
1394
+ */
1395
+ async function attemptStaleRunRecovery(
1396
+ db: DbExec,
1397
+ runId: string,
1398
+ ): Promise<StaleRunRecoveryOutcome> {
1399
+ const { rows } = await db.execute({
1400
+ sql: `SELECT thread_id, turn_id, dispatch_mode, dispatch_payload, started_at FROM agent_runs WHERE id = ? LIMIT 1`,
1401
+ args: [runId],
1402
+ });
1403
+ const row = rows?.[0] as
1404
+ | {
1405
+ thread_id?: string | null;
1406
+ turn_id?: string | null;
1407
+ dispatch_mode?: string | null;
1408
+ dispatch_payload?: string | null;
1409
+ started_at?: number | string | null;
1410
+ }
1411
+ | undefined;
1412
+ const dispatchMode = row?.dispatch_mode ?? "";
1413
+ if (!row?.thread_id || !dispatchMode.startsWith("background")) {
1414
+ return { outcome: "not_background" };
1415
+ }
1416
+ const payload = row.dispatch_payload;
1417
+ if (typeof payload !== "string" || payload.length === 0) {
1418
+ return { outcome: "payload_missing" };
1419
+ }
1420
+ const threadId = row.thread_id;
1421
+ const turnId = row.turn_id ?? runId;
1422
+ const startedAt = Number(row.started_at) || 0;
1423
+
1424
+ const { rows: newerRows } = await db.execute({
1425
+ sql: `SELECT id FROM agent_runs WHERE turn_id = ? AND id != ? AND started_at > ? LIMIT 1`,
1426
+ args: [turnId, runId, startedAt],
1427
+ });
1428
+ if ((newerRows?.length ?? 0) > 0) {
1429
+ return { outcome: "newer_run_exists" };
1430
+ }
1431
+
1432
+ // Inline COUNT rather than the exported `countRunsForTurn` (which opens
1433
+ // its own `getDbExec()` connection) — this must read through the SAME `db`
1434
+ // handle as everything else here so it participates in the caller's
1435
+ // transaction when one is active.
1436
+ const { rows: countRows } = await db.execute({
1437
+ sql: `SELECT COUNT(*) AS run_count FROM agent_runs WHERE thread_id = ? AND turn_id = ?`,
1438
+ args: [threadId, turnId],
1439
+ });
1440
+ const turnRunCount = Number(
1441
+ (countRows?.[0] as { run_count?: unknown } | undefined)?.run_count,
1442
+ );
1443
+ if (
1444
+ Number.isFinite(turnRunCount) &&
1445
+ turnRunCount > STALE_RUN_RECOVERY_MAX_TURN_RUNS
1446
+ ) {
1447
+ return { outcome: "budget_exhausted" };
1448
+ }
1449
+
1450
+ const successorRunId = generateRecoveryRunId();
1451
+ const now = Date.now();
1452
+ await db.execute({
1453
+ sql: `INSERT INTO agent_runs (id, thread_id, status, started_at, heartbeat_at, last_progress_at, turn_id, dispatch_mode, dispatch_payload) VALUES (?, ?, 'running', ?, ?, ?, ?, 'background', ?) ON CONFLICT (id) DO NOTHING`,
1454
+ args: [successorRunId, threadId, now, now, now, turnId, payload],
1455
+ });
1456
+ return { outcome: "recovered", successorRunId, threadId, turnId };
1457
+ }
1458
+
1459
+ /**
1460
+ * FIX 3: best-effort immediate redispatch for a stale-run recovery
1461
+ * successor, mirroring the "Unclaimed background-run sweep" redispatch
1462
+ * marker in `agent-chat-plugin.ts` (deliberately omits `continuationCount`
1463
+ * — see that file's comment — so a reaper-recovered chunk starts a fresh
1464
+ * nested-dispatch segment at depth 0). Fire-and-forget and never awaited by
1465
+ * callers: the successor row is already durably persisted (unclaimed,
1466
+ * `dispatch_payload` set) regardless of whether this dispatch lands, so a
1467
+ * failure here just means the existing fast unclaimed-background-run sweep
1468
+ * (`UNCLAIMED_BACKGROUND_RUN_FAST_SWEEP_MS`, ~20s) picks it up instead — the
1469
+ * row is left claimable, never marked errored, by construction (this
1470
+ * function never writes to `agent_runs`).
1471
+ */
1472
+ function attemptStaleRunRecoveryDispatch(successorRunId: string): void {
1473
+ void (async () => {
1474
+ try {
1475
+ const [
1476
+ {
1477
+ AGENT_CHAT_BACKGROUND_RUN_FIELD,
1478
+ resolveAgentChatProcessRunDispatchPath,
1479
+ },
1480
+ { fireInternalDispatch },
1481
+ ] = await Promise.all([
1482
+ import("./durable-background.js"),
1483
+ import("../server/self-dispatch.js"),
1484
+ ]);
1485
+ await fireInternalDispatch({
1486
+ path: resolveAgentChatProcessRunDispatchPath(),
1487
+ taskId: successorRunId,
1488
+ body: {
1489
+ internalContinuation: true,
1490
+ [AGENT_CHAT_BACKGROUND_RUN_FIELD]: {
1491
+ runId: successorRunId,
1492
+ payloadRef: true,
1493
+ },
1494
+ },
1495
+ });
1496
+ } catch (err) {
1497
+ console.error(
1498
+ "[run-store] stale-run recovery redispatch attempt failed (leaving successor claimable for the sweep):",
1499
+ successorRunId,
1500
+ err instanceof Error ? err.message : err,
1501
+ );
1502
+ }
1503
+ })();
1504
+ }
1505
+
1506
+ /**
1507
+ * Shared reap-to-stale-error implementation for a SINGLE run, used by both
1508
+ * `reapIfStale` (per-row, hot read path) and `reapAllStaleRuns` (per-row loop
1509
+ * over a stale-row snapshot — a row's own staleness is re-checked at UPDATE
1510
+ * time exactly as the prior bulk UPDATE did, so a heartbeat that lands
1511
+ * between the snapshot SELECT and this call naturally excludes the row).
1512
+ *
1513
+ * FIX 3: wraps the reap-to-errored write together with the recovery-
1514
+ * successor insert (`attemptStaleRunRecovery`) in ONE transaction so a
1515
+ * client polling `/runs/active` mid-recovery can never observe "errored, no
1516
+ * successor" — both writes commit together or not at all. A recovery
1517
+ * failure (thrown inside the transaction callback) is caught locally and
1518
+ * never rolls back the reap itself: the reap is the critical path, recovery
1519
+ * is strictly additive.
1520
+ */
1521
+ async function reapSingleStaleRun(
1316
1522
  runId: string,
1317
1523
  maxStaleMs?: number,
1318
1524
  ): Promise<boolean> {
1319
- await ensureRunTables();
1320
- if (await reconcileTerminalRunFromEvents(runId)) return false;
1321
- const client = getDbExec();
1322
1525
  const completedAt = Date.now();
1323
1526
  // Background-dispatched runs get the wider stale window so a slow cold-start
1324
1527
  // isn't reaped; foreground runs keep the tight 15s window. An explicit
@@ -1336,8 +1539,7 @@ export async function reapIfStale(
1336
1539
  : // First `?` is backgroundAwareStaleCutoffSql's CAST param, second is
1337
1540
  // inFlightGraceSql's — both bound to the same "now".
1338
1541
  [completedAt, completedAt];
1339
- const { rowsAffected } = await client.execute({
1340
- sql: `UPDATE agent_runs
1542
+ const updateSql = `UPDATE agent_runs
1341
1543
  SET status = 'errored',
1342
1544
  completed_at = ?,
1343
1545
  error_code = ?,
@@ -1346,17 +1548,78 @@ export async function reapIfStale(
1346
1548
  WHERE id = ?
1347
1549
  AND status = 'running'
1348
1550
  AND ${terminalRunEventExclusionSql()}
1349
- AND ${staleClause}`,
1350
- args: [
1351
- completedAt,
1352
- STALE_RUN_ERROR_EVENT.errorCode,
1353
- STALE_RUN_ERROR_EVENT.details,
1354
- STALE_RUN_ERROR_EVENT.errorCode,
1551
+ AND ${staleClause}`;
1552
+ const updateArgs = [
1553
+ completedAt,
1554
+ STALE_RUN_ERROR_EVENT.errorCode,
1555
+ STALE_RUN_ERROR_EVENT.details,
1556
+ STALE_RUN_ERROR_EVENT.errorCode,
1557
+ runId,
1558
+ ...staleArgs,
1559
+ ];
1560
+
1561
+ const client = getDbExec();
1562
+ let reaped = false;
1563
+ let outcome: StaleRunRecoveryOutcome | null = null;
1564
+ if (client.transaction) {
1565
+ await client.transaction(async (tx) => {
1566
+ const { rowsAffected } = await tx.execute({
1567
+ sql: updateSql,
1568
+ args: updateArgs,
1569
+ });
1570
+ reaped = (rowsAffected ?? 0) > 0;
1571
+ if (reaped) {
1572
+ outcome = await attemptStaleRunRecovery(tx, runId).catch(() => null);
1573
+ }
1574
+ });
1575
+ } else {
1576
+ // No transaction primitive on this DbExec (every current implementation
1577
+ // provides one — see db/client.ts — so this is a defensive fallback,
1578
+ // not an expected path). Ordering the recovery design explicitly
1579
+ // tolerates: insert the successor FIRST, then flip the old row terminal
1580
+ // — a still-"running" old row plus an unclaimed successor is a safe
1581
+ // intermediate state; the reverse (errored with no successor briefly
1582
+ // visible) is not. Narrow, accepted gap versus the transactional path:
1583
+ // two concurrent reapers racing this exact fallback on the exact same
1584
+ // run could each pass the "no newer run" check before either inserts,
1585
+ // producing two successors for one turn.
1586
+ outcome = await attemptStaleRunRecovery(client, runId).catch(() => null);
1587
+ const { rowsAffected } = await client.execute({
1588
+ sql: updateSql,
1589
+ args: updateArgs,
1590
+ });
1591
+ reaped = (rowsAffected ?? 0) > 0;
1592
+ }
1593
+
1594
+ if (reaped && outcome && outcome.outcome !== "not_background") {
1595
+ const detail =
1596
+ outcome.outcome === "recovered"
1597
+ ? `recovered successorRunId=${outcome.successorRunId}`
1598
+ : `declined reason=${outcome.outcome}`;
1599
+ await recordRunDiagnostic(
1355
1600
  runId,
1356
- ...staleArgs,
1357
- ],
1358
- });
1359
- const reaped = (rowsAffected ?? 0) > 0;
1601
+ RUN_DIAG_STAGE.staleRunRecoveryAttempted,
1602
+ detail,
1603
+ ).catch(() => {});
1604
+ if (outcome.outcome === "recovered") {
1605
+ attemptStaleRunRecoveryDispatch(outcome.successorRunId);
1606
+ }
1607
+ }
1608
+ return reaped;
1609
+ }
1610
+
1611
+ /**
1612
+ * If the given run is marked "running" in SQL but its heartbeat is stale
1613
+ * (producer likely crashed), flip it to "errored" so watchers stop waiting.
1614
+ * Returns true if the row was reaped.
1615
+ */
1616
+ export async function reapIfStale(
1617
+ runId: string,
1618
+ maxStaleMs?: number,
1619
+ ): Promise<boolean> {
1620
+ await ensureRunTables();
1621
+ if (await reconcileTerminalRunFromEvents(runId)) return false;
1622
+ const reaped = await reapSingleStaleRun(runId, maxStaleMs);
1360
1623
  if (!reaped && (await reconcileTerminalRunFromEvents(runId))) return false;
1361
1624
  if (reaped) {
1362
1625
  await safeAppendTerminalRunEvent(
@@ -1919,27 +2182,24 @@ export async function reapAllStaleRuns(): Promise<number> {
1919
2182
  await reconcileTerminalRunFromEvents(id);
1920
2183
  }
1921
2184
  }
1922
- const completedAt = Date.now();
1923
- const { rowsAffected } = await client.execute({
1924
- sql: `UPDATE agent_runs
1925
- SET status = 'errored',
1926
- completed_at = ?,
1927
- error_code = ?,
1928
- error_detail = ?,
1929
- terminal_reason = ?
1930
- WHERE status = 'running'
1931
- AND ${terminalRunEventExclusionSql()}
1932
- AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}
1933
- AND ${inFlightGraceSql()}`,
1934
- args: [
1935
- completedAt,
1936
- STALE_RUN_ERROR_EVENT.errorCode,
1937
- STALE_RUN_ERROR_EVENT.details,
1938
- STALE_RUN_ERROR_EVENT.errorCode,
1939
- completedAt,
1940
- completedAt,
1941
- ],
1942
- });
2185
+ // FIX 3 (durable-background incident): reap each stale row individually
2186
+ // via the shared `reapSingleStaleRun` (rather than one bulk UPDATE) so a
2187
+ // background chat-turn run can be checked for server-owned recovery
2188
+ // (`attemptStaleRunRecovery`) before it goes terminal — a single bulk
2189
+ // statement can't express "insert a successor for row A but not row B".
2190
+ // `reapSingleStaleRun` re-applies the identical staleness clause per row
2191
+ // (same as the bulk UPDATE previously did), so a row whose heartbeat
2192
+ // landed between the SELECT above and this loop is still correctly
2193
+ // excluded. This function only runs once, at process startup (see
2194
+ // agent-chat-plugin.ts) — the extra per-row round trips are
2195
+ // inconsequential.
2196
+ let reapedCount = 0;
2197
+ for (const row of stale.rows) {
2198
+ const id = (row as { id?: unknown }).id;
2199
+ if (typeof id !== "string") continue;
2200
+ const reaped = await reapSingleStaleRun(id).catch(() => false);
2201
+ if (reaped) reapedCount += 1;
2202
+ }
1943
2203
  for (const row of stale.rows) {
1944
2204
  const id = (row as { id?: unknown }).id;
1945
2205
  if (typeof id === "string") {
@@ -1950,7 +2210,7 @@ export async function reapAllStaleRuns(): Promise<number> {
1950
2210
  );
1951
2211
  }
1952
2212
  }
1953
- return rowsAffected ?? 0;
2213
+ return reapedCount;
1954
2214
  }
1955
2215
 
1956
2216
  /** Delete old runs and expire stale "running" rows that haven't had activity
@@ -1806,10 +1806,12 @@ function AgentPanelInner({
1806
1806
  ".agent-tabs-scroll{scrollbar-width:none;-ms-overflow-style:none;}" +
1807
1807
  ".agent-tabs-scroll::-webkit-scrollbar{display:none;}" +
1808
1808
  `[data-agent-fullscreen='true'] .agent-thread-content,` +
1809
- `[data-agent-fullscreen='true'] .agent-running-activity,` +
1809
+ `[data-agent-fullscreen='true'] .agent-running-activity{` +
1810
+ `max-width:${FULLSCREEN_CONTENT_MAX_PX}px;` +
1811
+ `margin-left:auto;margin-right:auto;width:100%;}` +
1810
1812
  `[data-agent-fullscreen='true'] .agent-composer-area,` +
1811
1813
  `[data-agent-fullscreen='true'] .agent-plan-mode-callout{` +
1812
- `max-width:${FULLSCREEN_CONTENT_MAX_PX}px;` +
1814
+ `max-width:${FULLSCREEN_COMPOSER_MAX_PX}px;` +
1813
1815
  `margin-left:auto;margin-right:auto;width:100%;}`,
1814
1816
  }}
1815
1817
  />
@@ -1987,6 +1989,8 @@ const SIDEBAR_OVERLAY_Z_INDEX = 70;
1987
1989
  const SIDEBAR_FULLSCREEN_Z_INDEX = 90;
1988
1990
  /** Max width of the centered chat column in fullscreen mode (Claude-style). */
1989
1991
  const FULLSCREEN_CONTENT_MAX_PX = 570;
1992
+ /** Max width of the centered composer in fullscreen mode. */
1993
+ const FULLSCREEN_COMPOSER_MAX_PX = 684;
1990
1994
 
1991
1995
  function ResizeHandle({
1992
1996
  position,