@agent-native/core 0.108.0 → 0.109.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (368) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +25 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +162 -17
  5. package/corpus/core/src/a2a/correlation.ts +50 -0
  6. package/corpus/core/src/a2a/handlers.ts +200 -14
  7. package/corpus/core/src/a2a/index.ts +7 -1
  8. package/corpus/core/src/a2a/invoke.ts +77 -1
  9. package/corpus/core/src/a2a/server.ts +13 -1
  10. package/corpus/core/src/a2a/task-store.ts +146 -6
  11. package/corpus/core/src/a2a/types.ts +32 -0
  12. package/corpus/core/src/action.ts +5 -2
  13. package/corpus/core/src/agent/production-agent.ts +33 -4
  14. package/corpus/core/src/client/AssistantChat.tsx +648 -603
  15. package/corpus/core/src/client/MultiTabAssistantChat.tsx +31 -4
  16. package/corpus/core/src/client/agent-chat-adapter.ts +23 -37
  17. package/corpus/core/src/client/agent-sidebar-state.ts +4 -0
  18. package/corpus/core/src/client/analytics.ts +84 -0
  19. package/corpus/core/src/client/chat/message-components.tsx +94 -22
  20. package/corpus/core/src/client/chat/tool-call-display.tsx +170 -47
  21. package/corpus/core/src/client/chat/use-agent-chat-lifecycle-tracking.ts +74 -0
  22. package/corpus/core/src/client/chat-thread-url.ts +11 -0
  23. package/corpus/core/src/client/error-capture.ts +12 -0
  24. package/corpus/core/src/client/extensions/ExtensionSlot.tsx +37 -1
  25. package/corpus/core/src/client/index.ts +2 -0
  26. package/corpus/core/src/client/route-chunk-recovery.ts +20 -8
  27. package/corpus/core/src/client/session-replay.ts +31 -0
  28. package/corpus/core/src/client/use-run-stuck-detection.ts +27 -1
  29. package/corpus/core/src/db/client.ts +55 -0
  30. package/corpus/core/src/extensions/actions.ts +2 -14
  31. package/corpus/core/src/extensions/store.ts +1 -1
  32. package/corpus/core/src/localization/default-messages.ts +3 -0
  33. package/corpus/core/src/mcp/build-server.ts +2 -2
  34. package/corpus/core/src/observability/traces.ts +172 -19
  35. package/corpus/core/src/progress/actions.ts +4 -2
  36. package/corpus/core/src/provider-api/index.ts +205 -105
  37. package/corpus/core/src/scripts/call-agent.ts +190 -19
  38. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +112 -25
  39. package/corpus/core/src/server/agent-chat-plugin.ts +136 -4
  40. package/corpus/core/src/styles/agent-conversation.css +7 -0
  41. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +20 -0
  42. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +20 -0
  43. package/corpus/core/src/templates/default/app/i18n/en-US.ts +20 -0
  44. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +20 -0
  45. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +21 -0
  46. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +19 -0
  47. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +20 -0
  48. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +19 -0
  49. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +20 -0
  50. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +19 -0
  51. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +19 -0
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +49 -0
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +5 -0
  54. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +7 -1
  55. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +13 -0
  56. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  57. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +29 -10
  58. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +54 -23
  59. package/corpus/templates/analytics/.builder/skills/apollo/SKILL.md +19 -41
  60. package/corpus/templates/analytics/.builder/skills/commonroom/SKILL.md +20 -35
  61. package/corpus/templates/analytics/.builder/skills/github/SKILL.md +23 -67
  62. package/corpus/templates/analytics/.builder/skills/pylon/SKILL.md +25 -43
  63. package/corpus/templates/analytics/AGENTS.md +22 -13
  64. package/corpus/templates/analytics/actions/account-deep-dive.ts +1 -0
  65. package/corpus/templates/analytics/actions/configure-gong-native-insights.ts +29 -0
  66. package/corpus/templates/analytics/actions/content-calendar-schema.ts +8 -4
  67. package/corpus/templates/analytics/actions/content-calendar.ts +12 -4
  68. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +2 -1
  69. package/corpus/templates/analytics/actions/gong-calls.ts +113 -22
  70. package/corpus/templates/analytics/actions/gong-native-insights.ts +166 -0
  71. package/corpus/templates/analytics/actions/hubspot-deals.ts +190 -46
  72. package/corpus/templates/analytics/actions/pylon-issues.ts +123 -0
  73. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  74. package/corpus/templates/analytics/actions/slack-messages.ts +179 -27
  75. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +48 -152
  76. package/corpus/templates/analytics/app/components/ui/chart.tsx +1 -1
  77. package/corpus/templates/analytics/app/hooks/use-chart-tooltip-flip.ts +21 -6
  78. package/corpus/templates/analytics/app/i18n/zh-TW.ts +13 -0
  79. package/corpus/templates/analytics/app/i18n-data.ts +134 -0
  80. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +181 -30
  81. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +6 -0
  82. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +4 -3
  83. package/corpus/templates/analytics/changelog/2026-07-17-analytics-now-includes-a-default-alert-for-spikes-in-stuck-agent-chats.md +6 -0
  84. package/corpus/templates/analytics/changelog/2026-07-17-dashboard-editors-can-add-shared-extensions-or-opt-into-per-.md +6 -0
  85. package/corpus/templates/analytics/changelog/2026-07-17-dashboard-extensions-show-a-loading-skeleton.md +6 -0
  86. package/corpus/templates/analytics/changelog/2026-07-17-errors-now-group-repeat-failures-consistently-and-keep-priva.md +6 -0
  87. package/corpus/templates/analytics/changelog/2026-07-17-gong-transcript-reviews-now-batch-bounded-call-windows-for-f.md +6 -0
  88. package/corpus/templates/analytics/changelog/2026-07-17-hubspot-deal-filters-now-run-in-hubspot-before-paging-result.md +6 -0
  89. package/corpus/templates/analytics/changelog/2026-07-17-notion-content-calendars-now-discover-the-matching-database-.md +6 -0
  90. package/corpus/templates/analytics/changelog/2026-07-17-pylon-dashboard-reads-now-use-the-paginated-issue-search-api.md +6 -0
  91. package/corpus/templates/analytics/changelog/2026-07-17-slack-reads-no-longer-join-channels-and-report-pagination-coverage.md +6 -0
  92. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +23 -14
  93. package/corpus/templates/analytics/docs/schemas/tracked-events.md +5 -1
  94. package/corpus/templates/analytics/server/handlers/notion.ts +14 -3
  95. package/corpus/templates/analytics/server/handlers/slack.ts +182 -29
  96. package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +3 -3
  97. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +2 -6
  98. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +209 -87
  99. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +6 -2
  100. package/corpus/templates/analytics/server/lib/error-capture.ts +75 -8
  101. package/corpus/templates/analytics/server/lib/gong-native-policy.ts +72 -0
  102. package/corpus/templates/analytics/server/lib/gong.ts +54 -66
  103. package/corpus/templates/analytics/server/lib/hubspot.ts +40 -3
  104. package/corpus/templates/analytics/server/lib/notion.ts +83 -8
  105. package/corpus/templates/analytics/server/lib/real-data-actions.ts +4 -0
  106. package/corpus/templates/analytics/server/lib/slack.ts +242 -90
  107. package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -3
  108. package/corpus/templates/analytics/server/routes/api/pylon/accounts.get.ts +42 -1
  109. package/corpus/templates/analytics/server/routes/api/pylon/issues.get.ts +71 -1
  110. package/corpus/templates/analytics/server/routes/api/test-connection.post.ts +44 -4
  111. package/corpus/templates/clips/actions/finalize-recording.ts +541 -62
  112. package/corpus/templates/clips/app/components/library/bulk-action-toolbar.tsx +19 -1
  113. package/corpus/templates/clips/app/components/library/create-folder-dialog.tsx +123 -0
  114. package/corpus/templates/clips/app/components/library/library-grid.tsx +42 -4
  115. package/corpus/templates/clips/app/components/library/recording-card.tsx +30 -0
  116. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +17 -2
  117. package/corpus/templates/clips/app/components/player/video-player.tsx +14 -2
  118. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +2 -2
  119. package/corpus/templates/clips/app/entry.client.tsx +6 -1
  120. package/corpus/templates/clips/app/i18n/ar-SA.ts +12 -1
  121. package/corpus/templates/clips/app/i18n/de-DE.ts +12 -1
  122. package/corpus/templates/clips/app/i18n/en-US.ts +12 -1
  123. package/corpus/templates/clips/app/i18n/es-ES.ts +11 -1
  124. package/corpus/templates/clips/app/i18n/fr-FR.ts +11 -1
  125. package/corpus/templates/clips/app/i18n/hi-IN.ts +10 -1
  126. package/corpus/templates/clips/app/i18n/ja-JP.ts +12 -1
  127. package/corpus/templates/clips/app/i18n/ko-KR.ts +11 -1
  128. package/corpus/templates/clips/app/i18n/pt-BR.ts +11 -1
  129. package/corpus/templates/clips/app/i18n/zh-CN.ts +11 -1
  130. package/corpus/templates/clips/app/i18n/zh-TW.ts +11 -1
  131. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +72 -1
  132. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -1
  133. package/corpus/templates/clips/app/routes/record.tsx +3 -3
  134. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -3
  135. package/corpus/templates/clips/changelog/2026-07-17-choose-which-workspace-receives-new-clips-recordings-and-des.md +6 -0
  136. package/corpus/templates/clips/changelog/2026-07-17-clips-now-retries-storage-verification-in-the-background-ins.md +6 -0
  137. package/corpus/templates/clips/changelog/2026-07-17-create-folders-directly-from-the-move-menu.md +6 -0
  138. package/corpus/templates/clips/changelog/2026-07-17-failed-desktop-uploads-now-offer-a-direct-way-to-reopen-clip.md +6 -0
  139. package/corpus/templates/clips/changelog/2026-07-17-fixed-broken-thumbnail-icons-appearing-over-playable-clip-em.md +6 -0
  140. package/corpus/templates/clips/changelog/2026-07-17-playback-comments-stay-readable-at-faster-playback-speeds.md +6 -0
  141. package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +3 -0
  142. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +3 -3
  143. package/corpus/templates/clips/desktop/src/app.tsx +19 -7
  144. package/corpus/templates/clips/desktop/src/lib/recorder.ts +111 -13
  145. package/corpus/templates/clips/desktop/src/lib/upload-verification.ts +16 -7
  146. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -4
  147. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/live_upload.rs +32 -15
  148. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +45 -18
  149. package/corpus/templates/clips/server/jobs/media-verification.ts +130 -0
  150. package/corpus/templates/clips/server/lib/builder-media-compression.ts +15 -0
  151. package/corpus/templates/clips/server/lib/media-verification-state.ts +38 -0
  152. package/corpus/templates/clips/server/lib/player-thumbnail-url.ts +31 -0
  153. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +10 -3
  154. package/corpus/templates/clips/server/plugins/auth.ts +5 -5
  155. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  156. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +12 -2
  157. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +6 -1
  158. package/corpus/templates/clips/server/routes/api/thumbnail/[recordingId].get.ts +340 -0
  159. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +24 -7
  160. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +91 -7
  161. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +14 -0
  162. package/corpus/templates/clips/shared/finalize-recovery.ts +61 -2
  163. package/corpus/templates/clips/shared/share-meta.ts +1 -1
  164. package/corpus/templates/content/app/entry.client.tsx +6 -1
  165. package/corpus/templates/dispatch/AGENTS.md +5 -0
  166. package/corpus/templates/dispatch/changelog/2026-07-17-apps-page-links-to-other-available-apps.md +6 -0
  167. package/corpus/templates/dispatch/changelog/2026-07-17-curated-template-remixes.md +6 -0
  168. package/corpus/templates/mail/app/entry.client.tsx +6 -1
  169. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +11 -7
  170. package/corpus/templates/slides/.env.example +0 -1
  171. package/corpus/templates/slides/AGENTS.md +4 -0
  172. package/corpus/templates/slides/DEVELOPING.md +2 -2
  173. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +1 -1
  174. package/corpus/templates/slides/actions/logo-config.ts +6 -7
  175. package/corpus/templates/slides/actions/search-images.ts +11 -39
  176. package/corpus/templates/slides/actions/search-logos.ts +5 -52
  177. package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +1 -1
  178. package/corpus/templates/slides/app/components/editor/LogoSearchPanel.tsx +16 -26
  179. package/corpus/templates/slides/app/entry.client.tsx +6 -1
  180. package/corpus/templates/slides/server/lib/media-search-providers.ts +216 -0
  181. package/corpus/templates/slides/server/register-secrets.ts +30 -0
  182. package/dist/a2a/client.d.ts +47 -1
  183. package/dist/a2a/client.d.ts.map +1 -1
  184. package/dist/a2a/client.js +94 -12
  185. package/dist/a2a/client.js.map +1 -1
  186. package/dist/a2a/correlation.d.ts +10 -0
  187. package/dist/a2a/correlation.d.ts.map +1 -0
  188. package/dist/a2a/correlation.js +40 -0
  189. package/dist/a2a/correlation.js.map +1 -0
  190. package/dist/a2a/handlers.d.ts.map +1 -1
  191. package/dist/a2a/handlers.js +147 -15
  192. package/dist/a2a/handlers.js.map +1 -1
  193. package/dist/a2a/index.d.ts +4 -4
  194. package/dist/a2a/index.d.ts.map +1 -1
  195. package/dist/a2a/index.js +2 -2
  196. package/dist/a2a/index.js.map +1 -1
  197. package/dist/a2a/invoke.d.ts +29 -2
  198. package/dist/a2a/invoke.d.ts.map +1 -1
  199. package/dist/a2a/invoke.js +33 -1
  200. package/dist/a2a/invoke.js.map +1 -1
  201. package/dist/a2a/server.d.ts.map +1 -1
  202. package/dist/a2a/server.js +14 -1
  203. package/dist/a2a/server.js.map +1 -1
  204. package/dist/a2a/task-store.d.ts +12 -1
  205. package/dist/a2a/task-store.d.ts.map +1 -1
  206. package/dist/a2a/task-store.js +96 -6
  207. package/dist/a2a/task-store.js.map +1 -1
  208. package/dist/a2a/types.d.ts +27 -0
  209. package/dist/a2a/types.d.ts.map +1 -1
  210. package/dist/a2a/types.js.map +1 -1
  211. package/dist/action.d.ts +5 -2
  212. package/dist/action.d.ts.map +1 -1
  213. package/dist/action.js.map +1 -1
  214. package/dist/agent/production-agent.d.ts +14 -0
  215. package/dist/agent/production-agent.d.ts.map +1 -1
  216. package/dist/agent/production-agent.js +19 -3
  217. package/dist/agent/production-agent.js.map +1 -1
  218. package/dist/client/AssistantChat.d.ts +14 -1
  219. package/dist/client/AssistantChat.d.ts.map +1 -1
  220. package/dist/client/AssistantChat.js +158 -136
  221. package/dist/client/AssistantChat.js.map +1 -1
  222. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  223. package/dist/client/MultiTabAssistantChat.js +27 -4
  224. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  225. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  226. package/dist/client/agent-chat-adapter.js +21 -39
  227. package/dist/client/agent-chat-adapter.js.map +1 -1
  228. package/dist/client/agent-sidebar-state.d.ts +1 -0
  229. package/dist/client/agent-sidebar-state.d.ts.map +1 -1
  230. package/dist/client/agent-sidebar-state.js +4 -0
  231. package/dist/client/agent-sidebar-state.js.map +1 -1
  232. package/dist/client/analytics.d.ts +14 -0
  233. package/dist/client/analytics.d.ts.map +1 -1
  234. package/dist/client/analytics.js +67 -0
  235. package/dist/client/analytics.js.map +1 -1
  236. package/dist/client/chat/message-components.d.ts +8 -0
  237. package/dist/client/chat/message-components.d.ts.map +1 -1
  238. package/dist/client/chat/message-components.js +62 -19
  239. package/dist/client/chat/message-components.js.map +1 -1
  240. package/dist/client/chat/tool-call-display.d.ts +6 -0
  241. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  242. package/dist/client/chat/tool-call-display.js +83 -14
  243. package/dist/client/chat/tool-call-display.js.map +1 -1
  244. package/dist/client/chat/use-agent-chat-lifecycle-tracking.d.ts +8 -0
  245. package/dist/client/chat/use-agent-chat-lifecycle-tracking.d.ts.map +1 -0
  246. package/dist/client/chat/use-agent-chat-lifecycle-tracking.js +51 -0
  247. package/dist/client/chat/use-agent-chat-lifecycle-tracking.js.map +1 -0
  248. package/dist/client/chat-thread-url.d.ts +2 -0
  249. package/dist/client/chat-thread-url.d.ts.map +1 -0
  250. package/dist/client/chat-thread-url.js +12 -0
  251. package/dist/client/chat-thread-url.js.map +1 -0
  252. package/dist/client/error-capture.d.ts.map +1 -1
  253. package/dist/client/error-capture.js +7 -0
  254. package/dist/client/error-capture.js.map +1 -1
  255. package/dist/client/extensions/ExtensionSlot.d.ts +3 -1
  256. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
  257. package/dist/client/extensions/ExtensionSlot.js +28 -3
  258. package/dist/client/extensions/ExtensionSlot.js.map +1 -1
  259. package/dist/client/index.d.ts +1 -1
  260. package/dist/client/index.d.ts.map +1 -1
  261. package/dist/client/index.js +1 -1
  262. package/dist/client/index.js.map +1 -1
  263. package/dist/client/route-chunk-recovery.d.ts.map +1 -1
  264. package/dist/client/route-chunk-recovery.js +14 -9
  265. package/dist/client/route-chunk-recovery.js.map +1 -1
  266. package/dist/client/session-replay.d.ts +15 -0
  267. package/dist/client/session-replay.d.ts.map +1 -1
  268. package/dist/client/session-replay.js +20 -0
  269. package/dist/client/session-replay.js.map +1 -1
  270. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  271. package/dist/client/use-run-stuck-detection.js +25 -1
  272. package/dist/client/use-run-stuck-detection.js.map +1 -1
  273. package/dist/db/client.d.ts +7 -0
  274. package/dist/db/client.d.ts.map +1 -1
  275. package/dist/db/client.js +33 -0
  276. package/dist/db/client.js.map +1 -1
  277. package/dist/extensions/actions.d.ts.map +1 -1
  278. package/dist/extensions/actions.js +2 -10
  279. package/dist/extensions/actions.js.map +1 -1
  280. package/dist/extensions/store.js +1 -1
  281. package/dist/extensions/store.js.map +1 -1
  282. package/dist/localization/default-messages.d.ts +3 -0
  283. package/dist/localization/default-messages.d.ts.map +1 -1
  284. package/dist/localization/default-messages.js +3 -0
  285. package/dist/localization/default-messages.js.map +1 -1
  286. package/dist/mcp/build-server.d.ts +2 -0
  287. package/dist/mcp/build-server.d.ts.map +1 -1
  288. package/dist/mcp/build-server.js +2 -2
  289. package/dist/mcp/build-server.js.map +1 -1
  290. package/dist/notifications/routes.d.ts +2 -2
  291. package/dist/observability/routes.d.ts +5 -5
  292. package/dist/observability/traces.d.ts +9 -0
  293. package/dist/observability/traces.d.ts.map +1 -1
  294. package/dist/observability/traces.js +137 -19
  295. package/dist/observability/traces.js.map +1 -1
  296. package/dist/progress/actions.d.ts.map +1 -1
  297. package/dist/progress/actions.js +3 -2
  298. package/dist/progress/actions.js.map +1 -1
  299. package/dist/progress/routes.d.ts +1 -1
  300. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  301. package/dist/provider-api/index.d.ts.map +1 -1
  302. package/dist/provider-api/index.js +91 -8
  303. package/dist/provider-api/index.js.map +1 -1
  304. package/dist/scripts/call-agent.d.ts.map +1 -1
  305. package/dist/scripts/call-agent.js +140 -17
  306. package/dist/scripts/call-agent.js.map +1 -1
  307. package/dist/secrets/routes.d.ts +9 -9
  308. package/dist/server/agent-chat/action-filters-a2a.d.ts +24 -2
  309. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  310. package/dist/server/agent-chat/action-filters-a2a.js +62 -7
  311. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  312. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  313. package/dist/server/agent-chat-plugin.js +118 -5
  314. package/dist/server/agent-chat-plugin.js.map +1 -1
  315. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  316. package/dist/server/transcribe-voice.d.ts +1 -1
  317. package/dist/styles/agent-conversation.css +7 -0
  318. package/dist/templates/default/app/i18n/ar-SA.ts +20 -0
  319. package/dist/templates/default/app/i18n/de-DE.ts +20 -0
  320. package/dist/templates/default/app/i18n/en-US.ts +20 -0
  321. package/dist/templates/default/app/i18n/es-ES.ts +20 -0
  322. package/dist/templates/default/app/i18n/fr-FR.ts +21 -0
  323. package/dist/templates/default/app/i18n/hi-IN.ts +19 -0
  324. package/dist/templates/default/app/i18n/ja-JP.ts +20 -0
  325. package/dist/templates/default/app/i18n/ko-KR.ts +19 -0
  326. package/dist/templates/default/app/i18n/pt-BR.ts +20 -0
  327. package/dist/templates/default/app/i18n/zh-CN.ts +19 -0
  328. package/dist/templates/default/app/i18n/zh-TW.ts +19 -0
  329. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +49 -0
  330. package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +5 -0
  331. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +7 -1
  332. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +13 -0
  333. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  334. package/package.json +1 -1
  335. package/src/templates/default/app/i18n/ar-SA.ts +20 -0
  336. package/src/templates/default/app/i18n/de-DE.ts +20 -0
  337. package/src/templates/default/app/i18n/en-US.ts +20 -0
  338. package/src/templates/default/app/i18n/es-ES.ts +20 -0
  339. package/src/templates/default/app/i18n/fr-FR.ts +21 -0
  340. package/src/templates/default/app/i18n/hi-IN.ts +19 -0
  341. package/src/templates/default/app/i18n/ja-JP.ts +20 -0
  342. package/src/templates/default/app/i18n/ko-KR.ts +19 -0
  343. package/src/templates/default/app/i18n/pt-BR.ts +20 -0
  344. package/src/templates/default/app/i18n/zh-CN.ts +19 -0
  345. package/src/templates/default/app/i18n/zh-TW.ts +19 -0
  346. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +49 -0
  347. package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +5 -0
  348. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +7 -1
  349. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +13 -0
  350. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  351. package/corpus/templates/analytics/server/handlers/github.ts +0 -200
  352. package/corpus/templates/analytics/server/handlers/pylon.ts +0 -43
  353. package/corpus/templates/analytics/server/lib/apollo.ts +0 -167
  354. package/corpus/templates/analytics/server/lib/commonroom.ts +0 -149
  355. package/corpus/templates/analytics/server/lib/github.ts +0 -660
  356. package/corpus/templates/analytics/server/lib/mixpanel.ts +0 -156
  357. package/corpus/templates/analytics/server/lib/posthog.ts +0 -194
  358. package/corpus/templates/analytics/server/lib/pylon.ts +0 -109
  359. package/corpus/templates/analytics/server/routes/api/github/graphql.post.ts +0 -1
  360. package/corpus/templates/analytics/server/routes/api/github/issue.get.ts +0 -1
  361. package/corpus/templates/analytics/server/routes/api/github/org-prs.get.ts +0 -1
  362. package/corpus/templates/analytics/server/routes/api/github/pr.get.ts +0 -1
  363. package/corpus/templates/analytics/server/routes/api/github/prs.get.ts +0 -1
  364. package/corpus/templates/analytics/server/routes/api/github/search.get.ts +0 -1
  365. package/corpus/templates/slides/actions/fetch-logos.ts +0 -90
  366. package/corpus/templates/slides/actions/image-search.ts +0 -64
  367. package/corpus/templates/slides/actions/import-github.ts +0 -182
  368. package/corpus/templates/slides/actions/logo-lookup.ts +0 -113
@@ -1 +1 @@
1
- {"version":3,"file":"use-run-stuck-detection.d.ts","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oDAAoD;IACpD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iEAAiE;IACjE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iGAAiG;IACjG,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,2BAA2B;IAC1C,qEAAqE;IACrE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC;;;;;OAKG;IACH,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,eAAO,MAAM,qCAAqC,SAAU,CAAC;AAC7D,eAAO,MAAM,0CAA0C,QAAc,CAAC;AA8BtE,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,OAAc,EACd,gBAA6C,EAC7C,0BAAkE,EAClE,8BAA2E,EAC3E,cAAyC,EACzC,MAAM,GACP,EAAE,2BAA2B,GAAG,aAAa,CA4P7C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,WAEzB,MAAM,WAAU,MAAM,KAAY,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBzE"}
1
+ {"version":3,"file":"use-run-stuck-detection.d.ts","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oDAAoD;IACpD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iEAAiE;IACjE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iGAAiG;IACjG,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,2BAA2B;IAC1C,qEAAqE;IACrE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC;;;;;OAKG;IACH,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,eAAO,MAAM,qCAAqC,SAAU,CAAC;AAC7D,eAAO,MAAM,0CAA0C,QAAc,CAAC;AA+BtE,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,OAAc,EACd,gBAA6C,EAC7C,0BAAkE,EAClE,8BAA2E,EAC3E,cAAyC,EACzC,MAAM,GACP,EAAE,2BAA2B,GAAG,aAAa,CAqR7C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,WAEzB,MAAM,WAAU,MAAM,KAAY,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBzE"}
@@ -5,6 +5,7 @@ export const DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS = 180_000;
5
5
  export const DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS = 13 * 60_000;
6
6
  const DEFAULT_POLL_INTERVAL_MS = 5_000;
7
7
  const IDLE_BACKOFF_INTERVAL_MS = 15_000;
8
+ const MAX_POLL_ERROR_BACKOFF_MS = 30_000;
8
9
  const FRESH_BACKGROUND_HEARTBEAT_MS = 30_000;
9
10
  const EMPTY_STATE = {
10
11
  isStuck: false,
@@ -30,6 +31,18 @@ export function useRunStuckDetection({ threadId, enabled = true, stuckThresholdM
30
31
  let timer = null;
31
32
  let snapshotTransitionTimer = null;
32
33
  let snapshotVersion = 0;
34
+ let consecutivePollFailures = 0;
35
+ const pollFailureDelay = () => {
36
+ consecutivePollFailures += 1;
37
+ return Math.min(Math.max(pollIntervalMs, 1) * 2 ** consecutivePollFailures, MAX_POLL_ERROR_BACKOFF_MS);
38
+ };
39
+ const clearObservedRun = () => {
40
+ snapshotVersion += 1;
41
+ if (snapshotTransitionTimer)
42
+ clearTimeout(snapshotTransitionTimer);
43
+ snapshotTransitionTimer = null;
44
+ setState(EMPTY_STATE);
45
+ };
33
46
  const effectiveThresholdFor = (dispatchMode, heartbeatSinceMs) => {
34
47
  const liveBackgroundWorker = dispatchMode === "background-processing" &&
35
48
  heartbeatSinceMs != null &&
@@ -120,6 +133,7 @@ export function useRunStuckDetection({ threadId, enabled = true, stuckThresholdM
120
133
  if (cancelled)
121
134
  return;
122
135
  if (res.ok) {
136
+ consecutivePollFailures = 0;
123
137
  const data = (await res.json());
124
138
  const lastProgressAt = data.lastProgressAt ?? null;
125
139
  // Measure elapsed against the SERVER clock (serverNow) rather than the
@@ -175,9 +189,19 @@ export function useRunStuckDetection({ threadId, enabled = true, stuckThresholdM
175
189
  nextDelay = IDLE_BACKOFF_INTERVAL_MS;
176
190
  }
177
191
  }
192
+ else if (res.status === 429 || res.status >= 500) {
193
+ nextDelay = pollFailureDelay();
194
+ }
195
+ else {
196
+ consecutivePollFailures = 0;
197
+ clearObservedRun();
198
+ nextDelay = MAX_POLL_ERROR_BACKOFF_MS;
199
+ }
178
200
  }
179
201
  catch {
180
- // Network blip — leave previous state. Next tick will retry.
202
+ // Network/503 blip — leave previous state and ease polling pressure
203
+ // while the server or database recovers.
204
+ nextDelay = pollFailureDelay();
181
205
  }
182
206
  if (!cancelled) {
183
207
  timer = setTimeout(poll, nextDelay);
@@ -1 +1 @@
1
- {"version":3,"file":"use-run-stuck-detection.js","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState, useCallback } from \"react\";\n\nimport { agentNativePath } from \"./api-path.js\";\n\n/**\n * Per-thread chat run health, derived from the durable `last_progress_at`\n * timestamp on the server. Drives the user-visible \"this chat looks stuck\"\n * affordance — distinct from the silent reconnect logic in\n * `agent-chat-adapter.ts`, which keeps trying in the background. When\n * automatic recovery isn't making progress (for whatever reason), this\n * hook surfaces a Retry / Cancel button to the user instead of leaving\n * them staring at a frozen spinner.\n */\nexport interface RunStuckState {\n /** True when an active run hasn't emitted an event for `stuckThresholdMs`. */\n isStuck: boolean;\n /** ID of the active run, or null when nothing is in flight. */\n runId: string | null;\n /** Server-side run status (\"running\" / \"completed\" / \"errored\" / etc.). */\n status: string | null;\n /** Server timestamp (ms) of the last emitted event, or null if none yet. */\n lastProgressAt: number | null;\n /** Milliseconds since `lastProgressAt`, or null. */\n stuckSinceMs: number | null;\n /** Server timestamp (ms) of the last process-alive heartbeat. */\n heartbeatAt: number | null;\n /** Milliseconds since `heartbeatAt`, computed against the server clock. */\n heartbeatSinceMs: number | null;\n /** How the run was dispatched/continued, e.g. foreground-self-chain or background-processing. */\n dispatchMode: string | null;\n /**\n * Server-authoritative: true when the run holds an open tool call or A2A\n * `agent_call` delegation (`in_flight_since` marker set). Preferred over the\n * client-side proxy for deciding whether Retry (which aborts the run) is\n * safe to offer. Null when the server bundle predates this field.\n */\n hasInFlightWork: boolean | null;\n}\n\nexport interface UseRunStuckDetectionOptions {\n /** The thread to monitor. Pass null/undefined to disable polling. */\n threadId: string | null | undefined;\n /**\n * Set false to skip scheduling the poll loop entirely — used to gate\n * polling to only the active chat tab when multiple tabs are mounted\n * (inactive tabs are kept alive via display:none, not unmounted).\n * Defaults to true.\n */\n enabled?: boolean;\n /**\n * Threshold above which an in-flight FOREGROUND run is considered stuck.\n * The default sits comfortably above the adapter's 75s no-progress\n * reconnect — by then automatic recovery has already had its chance.\n */\n stuckThresholdMs?: number;\n /**\n * Threshold for BACKGROUND-dispatched runs (dispatchMode starts with\n * \"background\"). The server owns recovery for these — its run-manager\n * no-progress backstop (150s) and unclaimed-run sweep act first — so the\n * user-facing \"stuck\" affordance is a late fallback, not a race against\n * them. Selected inside the hook because the dispatch mode is only known\n * from the same poll response that computes the elapsed time.\n */\n backgroundStuckThresholdMs?: number;\n /**\n * Legacy upper bound for a claimed durable background worker that is still\n * sending fresh process heartbeats. Informational quiet-run UI is never\n * delayed past `backgroundStuckThresholdMs`; this option can only request an\n * earlier notice for live workers. Default 13 minutes.\n */\n liveBackgroundStuckThresholdMs?: number;\n /** Poll interval. Default 5_000ms. */\n pollIntervalMs?: number;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n}\n\nconst DEFAULT_STUCK_THRESHOLD_MS = 90_000;\nexport const DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS = 180_000;\nexport const DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS = 13 * 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst IDLE_BACKOFF_INTERVAL_MS = 15_000;\nconst FRESH_BACKGROUND_HEARTBEAT_MS = 30_000;\n\ninterface ActiveRunResponse {\n active: boolean;\n runId?: string;\n status?: string;\n heartbeatAt: number | null;\n lastProgressAt?: number | null;\n dispatchMode?: string | null;\n /** Server clock at response time, used to compute elapsed server-relative. */\n serverNow?: number;\n /** True when the run holds an open tool/A2A call (in_flight_since marker). */\n hasInFlightWork?: boolean;\n}\n\nconst EMPTY_STATE: RunStuckState = {\n isStuck: false,\n runId: null,\n status: null,\n lastProgressAt: null,\n stuckSinceMs: null,\n heartbeatAt: null,\n heartbeatSinceMs: null,\n dispatchMode: null,\n hasInFlightWork: null,\n};\n\nexport function useRunStuckDetection({\n threadId,\n enabled = true,\n stuckThresholdMs = DEFAULT_STUCK_THRESHOLD_MS,\n backgroundStuckThresholdMs = DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS,\n liveBackgroundStuckThresholdMs = DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n apiUrl,\n}: UseRunStuckDetectionOptions): RunStuckState {\n const [state, setState] = useState<RunStuckState>(EMPTY_STATE);\n\n useEffect(() => {\n // Reset on every thread change so the previous thread's stuck banner\n // doesn't bleed onto the new one before the first poll completes.\n setState(EMPTY_STATE);\n if (!threadId || !enabled) return;\n\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let snapshotTransitionTimer: ReturnType<typeof setTimeout> | null = null;\n let snapshotVersion = 0;\n\n type RunHealthSnapshot = {\n active: boolean;\n runId: string | null;\n status: string | null;\n lastProgressAt: number | null;\n stuckSinceMs: number | null;\n heartbeatAt: number | null;\n heartbeatSinceMs: number | null;\n dispatchMode: string | null;\n };\n\n const effectiveThresholdFor = (\n dispatchMode: string | null,\n heartbeatSinceMs: number | null,\n ) => {\n const liveBackgroundWorker =\n dispatchMode === \"background-processing\" &&\n heartbeatSinceMs != null &&\n heartbeatSinceMs >= 0 &&\n heartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS;\n const serverContinued =\n dispatchMode === \"foreground-self-chain\" ||\n dispatchMode?.startsWith(\"background\") === true;\n return liveBackgroundWorker\n ? Math.min(backgroundStuckThresholdMs, liveBackgroundStuckThresholdMs)\n : serverContinued\n ? backgroundStuckThresholdMs\n : stuckThresholdMs;\n };\n\n const scheduleSnapshotTransition = (\n snapshot: RunHealthSnapshot,\n observedAtMs: number,\n version: number,\n ) => {\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n snapshotTransitionTimer = null;\n if (\n cancelled ||\n version !== snapshotVersion ||\n !snapshot.active ||\n snapshot.status !== \"running\" ||\n snapshot.runId == null\n ) {\n return;\n }\n\n const currentElapsedMs = Math.max(0, Date.now() - observedAtMs);\n const currentHeartbeatSinceMs =\n snapshot.heartbeatSinceMs == null\n ? null\n : snapshot.heartbeatSinceMs + currentElapsedMs;\n const currentStuckSinceMs =\n snapshot.stuckSinceMs == null\n ? null\n : snapshot.stuckSinceMs + currentElapsedMs;\n const effectiveThresholdMs = effectiveThresholdFor(\n snapshot.dispatchMode,\n currentHeartbeatSinceMs,\n );\n const currentlyStuck = Boolean(\n currentStuckSinceMs != null &&\n currentStuckSinceMs > effectiveThresholdMs,\n );\n const transitionDelaysMs: number[] = [];\n if (\n currentHeartbeatSinceMs != null &&\n currentHeartbeatSinceMs >= 0 &&\n currentHeartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS\n ) {\n transitionDelaysMs.push(\n FRESH_BACKGROUND_HEARTBEAT_MS - currentHeartbeatSinceMs + 1,\n );\n }\n if (currentStuckSinceMs != null && !currentlyStuck) {\n transitionDelaysMs.push(\n Math.max(1, effectiveThresholdMs - currentStuckSinceMs + 1),\n );\n }\n if (transitionDelaysMs.length === 0) return;\n\n // Both ages originate from the server clock. Advance those snapshots by\n // only a local elapsed duration, which remains safe when client/server\n // wall clocks differ. Wake at the earlier semantic boundary, then\n // reschedule if the other boundary is still ahead.\n const delayMs = Math.max(1, Math.min(...transitionDelaysMs));\n snapshotTransitionTimer = setTimeout(() => {\n snapshotTransitionTimer = null;\n if (cancelled || version !== snapshotVersion) return;\n const elapsedSinceObservationMs = Math.max(\n 0,\n Date.now() - observedAtMs,\n );\n const nextHeartbeatSinceMs =\n snapshot.heartbeatSinceMs == null\n ? null\n : snapshot.heartbeatSinceMs + elapsedSinceObservationMs;\n const nextStuckSinceMs =\n snapshot.stuckSinceMs == null\n ? null\n : snapshot.stuckSinceMs + elapsedSinceObservationMs;\n const nextEffectiveThresholdMs = effectiveThresholdFor(\n snapshot.dispatchMode,\n nextHeartbeatSinceMs,\n );\n const nextIsStuck = Boolean(\n nextStuckSinceMs != null &&\n nextStuckSinceMs > nextEffectiveThresholdMs,\n );\n setState((current) => {\n if (\n version !== snapshotVersion ||\n current.runId !== snapshot.runId ||\n current.lastProgressAt !== snapshot.lastProgressAt ||\n current.heartbeatAt !== snapshot.heartbeatAt\n ) {\n return current;\n }\n return {\n ...current,\n isStuck: nextIsStuck,\n stuckSinceMs: nextStuckSinceMs,\n heartbeatSinceMs: nextHeartbeatSinceMs,\n };\n });\n scheduleSnapshotTransition(snapshot, observedAtMs, version);\n }, delayMs);\n };\n\n const poll = async () => {\n if (cancelled) return;\n let nextDelay = pollIntervalMs;\n try {\n const res = await fetch(\n `${base}/runs/active?threadId=${encodeURIComponent(threadId)}`,\n { credentials: \"same-origin\" },\n );\n if (cancelled) return;\n if (res.ok) {\n const data = (await res.json()) as ActiveRunResponse;\n const lastProgressAt = data.lastProgressAt ?? null;\n // Measure elapsed against the SERVER clock (serverNow) rather than the\n // client's Date.now(). lastProgressAt is a server timestamp, so client\n // clock skew of more than stuckThresholdMs would otherwise mark every\n // run stuck (clock ahead) or never stuck (clock behind). Fall back to\n // the client clock for older bundles that don't send serverNow.\n const nowMs = data.serverNow ?? Date.now();\n const stuckSinceMs =\n lastProgressAt != null ? nowMs - lastProgressAt : null;\n const heartbeatAt = data.heartbeatAt ?? null;\n const heartbeatSinceMs =\n heartbeatAt != null ? nowMs - heartbeatAt : null;\n const dispatchMode =\n typeof data.dispatchMode === \"string\" ? data.dispatchMode : null;\n // A claimed durable worker with a fresh heartbeat can legitimately\n // be waiting on a bounded long-running tool/sub-agent call. Still\n // surface informational status at the normal background threshold;\n // RunStuckBanner uses the fresh heartbeat to withhold Retry while\n // keeping an explicit Cancel available. The legacy live-worker\n // threshold may make that notice earlier, but never later.\n const effectiveThresholdMs = effectiveThresholdFor(\n dispatchMode,\n heartbeatSinceMs,\n );\n const isStuck = Boolean(\n data.active &&\n data.status === \"running\" &&\n stuckSinceMs != null &&\n stuckSinceMs > effectiveThresholdMs,\n );\n const observedAtMs = Date.now();\n const version = ++snapshotVersion;\n scheduleSnapshotTransition(\n {\n active: data.active,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n },\n observedAtMs,\n version,\n );\n setState({\n isStuck,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n hasInFlightWork:\n typeof data.hasInFlightWork === \"boolean\"\n ? data.hasInFlightWork\n : null,\n });\n // Back off polling when nothing is in flight — there's no point\n // hammering the endpoint while the chat is idle. We still poll\n // occasionally so a fresh run started in another tab is picked up.\n if (!data.active || data.status !== \"running\") {\n nextDelay = IDLE_BACKOFF_INTERVAL_MS;\n }\n }\n } catch {\n // Network blip — leave previous state. Next tick will retry.\n }\n if (!cancelled) {\n timer = setTimeout(poll, nextDelay);\n }\n };\n\n // Stagger the first poll so a freshly-started run isn't immediately\n // classified as stuck before the server has had a chance to record\n // any progress events.\n timer = setTimeout(poll, 2_000);\n\n return () => {\n cancelled = true;\n snapshotVersion += 1;\n if (timer) clearTimeout(timer);\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n };\n }, [\n threadId,\n enabled,\n stuckThresholdMs,\n backgroundStuckThresholdMs,\n liveBackgroundStuckThresholdMs,\n pollIntervalMs,\n apiUrl,\n ]);\n\n return state;\n}\n\n/**\n * POST `/runs/:id/abort` so the server flips the run to \"aborted\" and the\n * adapter's reconnect loop exits cleanly. Returns the run id that was\n * aborted (or null on failure) so callers can correlate observability\n * events. Best-effort — failures are swallowed, since the user's intent\n * is already captured locally.\n */\nexport function useAbortRun(apiUrl?: string) {\n return useCallback(\n async (runId: string, reason: string = \"user\"): Promise<string | null> => {\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n try {\n const res = await fetch(\n `${base}/runs/${encodeURIComponent(runId)}/abort`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify({ reason }),\n },\n );\n if (!res.ok) return null;\n return runId;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n}\n"]}
1
+ {"version":3,"file":"use-run-stuck-detection.js","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState, useCallback } from \"react\";\n\nimport { agentNativePath } from \"./api-path.js\";\n\n/**\n * Per-thread chat run health, derived from the durable `last_progress_at`\n * timestamp on the server. Drives the user-visible \"this chat looks stuck\"\n * affordance — distinct from the silent reconnect logic in\n * `agent-chat-adapter.ts`, which keeps trying in the background. When\n * automatic recovery isn't making progress (for whatever reason), this\n * hook surfaces a Retry / Cancel button to the user instead of leaving\n * them staring at a frozen spinner.\n */\nexport interface RunStuckState {\n /** True when an active run hasn't emitted an event for `stuckThresholdMs`. */\n isStuck: boolean;\n /** ID of the active run, or null when nothing is in flight. */\n runId: string | null;\n /** Server-side run status (\"running\" / \"completed\" / \"errored\" / etc.). */\n status: string | null;\n /** Server timestamp (ms) of the last emitted event, or null if none yet. */\n lastProgressAt: number | null;\n /** Milliseconds since `lastProgressAt`, or null. */\n stuckSinceMs: number | null;\n /** Server timestamp (ms) of the last process-alive heartbeat. */\n heartbeatAt: number | null;\n /** Milliseconds since `heartbeatAt`, computed against the server clock. */\n heartbeatSinceMs: number | null;\n /** How the run was dispatched/continued, e.g. foreground-self-chain or background-processing. */\n dispatchMode: string | null;\n /**\n * Server-authoritative: true when the run holds an open tool call or A2A\n * `agent_call` delegation (`in_flight_since` marker set). Preferred over the\n * client-side proxy for deciding whether Retry (which aborts the run) is\n * safe to offer. Null when the server bundle predates this field.\n */\n hasInFlightWork: boolean | null;\n}\n\nexport interface UseRunStuckDetectionOptions {\n /** The thread to monitor. Pass null/undefined to disable polling. */\n threadId: string | null | undefined;\n /**\n * Set false to skip scheduling the poll loop entirely — used to gate\n * polling to only the active chat tab when multiple tabs are mounted\n * (inactive tabs are kept alive via display:none, not unmounted).\n * Defaults to true.\n */\n enabled?: boolean;\n /**\n * Threshold above which an in-flight FOREGROUND run is considered stuck.\n * The default sits comfortably above the adapter's 75s no-progress\n * reconnect — by then automatic recovery has already had its chance.\n */\n stuckThresholdMs?: number;\n /**\n * Threshold for BACKGROUND-dispatched runs (dispatchMode starts with\n * \"background\"). The server owns recovery for these — its run-manager\n * no-progress backstop (150s) and unclaimed-run sweep act first — so the\n * user-facing \"stuck\" affordance is a late fallback, not a race against\n * them. Selected inside the hook because the dispatch mode is only known\n * from the same poll response that computes the elapsed time.\n */\n backgroundStuckThresholdMs?: number;\n /**\n * Legacy upper bound for a claimed durable background worker that is still\n * sending fresh process heartbeats. Informational quiet-run UI is never\n * delayed past `backgroundStuckThresholdMs`; this option can only request an\n * earlier notice for live workers. Default 13 minutes.\n */\n liveBackgroundStuckThresholdMs?: number;\n /** Poll interval. Default 5_000ms. */\n pollIntervalMs?: number;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n}\n\nconst DEFAULT_STUCK_THRESHOLD_MS = 90_000;\nexport const DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS = 180_000;\nexport const DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS = 13 * 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst IDLE_BACKOFF_INTERVAL_MS = 15_000;\nconst MAX_POLL_ERROR_BACKOFF_MS = 30_000;\nconst FRESH_BACKGROUND_HEARTBEAT_MS = 30_000;\n\ninterface ActiveRunResponse {\n active: boolean;\n runId?: string;\n status?: string;\n heartbeatAt: number | null;\n lastProgressAt?: number | null;\n dispatchMode?: string | null;\n /** Server clock at response time, used to compute elapsed server-relative. */\n serverNow?: number;\n /** True when the run holds an open tool/A2A call (in_flight_since marker). */\n hasInFlightWork?: boolean;\n}\n\nconst EMPTY_STATE: RunStuckState = {\n isStuck: false,\n runId: null,\n status: null,\n lastProgressAt: null,\n stuckSinceMs: null,\n heartbeatAt: null,\n heartbeatSinceMs: null,\n dispatchMode: null,\n hasInFlightWork: null,\n};\n\nexport function useRunStuckDetection({\n threadId,\n enabled = true,\n stuckThresholdMs = DEFAULT_STUCK_THRESHOLD_MS,\n backgroundStuckThresholdMs = DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS,\n liveBackgroundStuckThresholdMs = DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n apiUrl,\n}: UseRunStuckDetectionOptions): RunStuckState {\n const [state, setState] = useState<RunStuckState>(EMPTY_STATE);\n\n useEffect(() => {\n // Reset on every thread change so the previous thread's stuck banner\n // doesn't bleed onto the new one before the first poll completes.\n setState(EMPTY_STATE);\n if (!threadId || !enabled) return;\n\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let snapshotTransitionTimer: ReturnType<typeof setTimeout> | null = null;\n let snapshotVersion = 0;\n let consecutivePollFailures = 0;\n\n const pollFailureDelay = () => {\n consecutivePollFailures += 1;\n return Math.min(\n Math.max(pollIntervalMs, 1) * 2 ** consecutivePollFailures,\n MAX_POLL_ERROR_BACKOFF_MS,\n );\n };\n\n const clearObservedRun = () => {\n snapshotVersion += 1;\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n snapshotTransitionTimer = null;\n setState(EMPTY_STATE);\n };\n\n type RunHealthSnapshot = {\n active: boolean;\n runId: string | null;\n status: string | null;\n lastProgressAt: number | null;\n stuckSinceMs: number | null;\n heartbeatAt: number | null;\n heartbeatSinceMs: number | null;\n dispatchMode: string | null;\n };\n\n const effectiveThresholdFor = (\n dispatchMode: string | null,\n heartbeatSinceMs: number | null,\n ) => {\n const liveBackgroundWorker =\n dispatchMode === \"background-processing\" &&\n heartbeatSinceMs != null &&\n heartbeatSinceMs >= 0 &&\n heartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS;\n const serverContinued =\n dispatchMode === \"foreground-self-chain\" ||\n dispatchMode?.startsWith(\"background\") === true;\n return liveBackgroundWorker\n ? Math.min(backgroundStuckThresholdMs, liveBackgroundStuckThresholdMs)\n : serverContinued\n ? backgroundStuckThresholdMs\n : stuckThresholdMs;\n };\n\n const scheduleSnapshotTransition = (\n snapshot: RunHealthSnapshot,\n observedAtMs: number,\n version: number,\n ) => {\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n snapshotTransitionTimer = null;\n if (\n cancelled ||\n version !== snapshotVersion ||\n !snapshot.active ||\n snapshot.status !== \"running\" ||\n snapshot.runId == null\n ) {\n return;\n }\n\n const currentElapsedMs = Math.max(0, Date.now() - observedAtMs);\n const currentHeartbeatSinceMs =\n snapshot.heartbeatSinceMs == null\n ? null\n : snapshot.heartbeatSinceMs + currentElapsedMs;\n const currentStuckSinceMs =\n snapshot.stuckSinceMs == null\n ? null\n : snapshot.stuckSinceMs + currentElapsedMs;\n const effectiveThresholdMs = effectiveThresholdFor(\n snapshot.dispatchMode,\n currentHeartbeatSinceMs,\n );\n const currentlyStuck = Boolean(\n currentStuckSinceMs != null &&\n currentStuckSinceMs > effectiveThresholdMs,\n );\n const transitionDelaysMs: number[] = [];\n if (\n currentHeartbeatSinceMs != null &&\n currentHeartbeatSinceMs >= 0 &&\n currentHeartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS\n ) {\n transitionDelaysMs.push(\n FRESH_BACKGROUND_HEARTBEAT_MS - currentHeartbeatSinceMs + 1,\n );\n }\n if (currentStuckSinceMs != null && !currentlyStuck) {\n transitionDelaysMs.push(\n Math.max(1, effectiveThresholdMs - currentStuckSinceMs + 1),\n );\n }\n if (transitionDelaysMs.length === 0) return;\n\n // Both ages originate from the server clock. Advance those snapshots by\n // only a local elapsed duration, which remains safe when client/server\n // wall clocks differ. Wake at the earlier semantic boundary, then\n // reschedule if the other boundary is still ahead.\n const delayMs = Math.max(1, Math.min(...transitionDelaysMs));\n snapshotTransitionTimer = setTimeout(() => {\n snapshotTransitionTimer = null;\n if (cancelled || version !== snapshotVersion) return;\n const elapsedSinceObservationMs = Math.max(\n 0,\n Date.now() - observedAtMs,\n );\n const nextHeartbeatSinceMs =\n snapshot.heartbeatSinceMs == null\n ? null\n : snapshot.heartbeatSinceMs + elapsedSinceObservationMs;\n const nextStuckSinceMs =\n snapshot.stuckSinceMs == null\n ? null\n : snapshot.stuckSinceMs + elapsedSinceObservationMs;\n const nextEffectiveThresholdMs = effectiveThresholdFor(\n snapshot.dispatchMode,\n nextHeartbeatSinceMs,\n );\n const nextIsStuck = Boolean(\n nextStuckSinceMs != null &&\n nextStuckSinceMs > nextEffectiveThresholdMs,\n );\n setState((current) => {\n if (\n version !== snapshotVersion ||\n current.runId !== snapshot.runId ||\n current.lastProgressAt !== snapshot.lastProgressAt ||\n current.heartbeatAt !== snapshot.heartbeatAt\n ) {\n return current;\n }\n return {\n ...current,\n isStuck: nextIsStuck,\n stuckSinceMs: nextStuckSinceMs,\n heartbeatSinceMs: nextHeartbeatSinceMs,\n };\n });\n scheduleSnapshotTransition(snapshot, observedAtMs, version);\n }, delayMs);\n };\n\n const poll = async () => {\n if (cancelled) return;\n let nextDelay = pollIntervalMs;\n try {\n const res = await fetch(\n `${base}/runs/active?threadId=${encodeURIComponent(threadId)}`,\n { credentials: \"same-origin\" },\n );\n if (cancelled) return;\n if (res.ok) {\n consecutivePollFailures = 0;\n const data = (await res.json()) as ActiveRunResponse;\n const lastProgressAt = data.lastProgressAt ?? null;\n // Measure elapsed against the SERVER clock (serverNow) rather than the\n // client's Date.now(). lastProgressAt is a server timestamp, so client\n // clock skew of more than stuckThresholdMs would otherwise mark every\n // run stuck (clock ahead) or never stuck (clock behind). Fall back to\n // the client clock for older bundles that don't send serverNow.\n const nowMs = data.serverNow ?? Date.now();\n const stuckSinceMs =\n lastProgressAt != null ? nowMs - lastProgressAt : null;\n const heartbeatAt = data.heartbeatAt ?? null;\n const heartbeatSinceMs =\n heartbeatAt != null ? nowMs - heartbeatAt : null;\n const dispatchMode =\n typeof data.dispatchMode === \"string\" ? data.dispatchMode : null;\n // A claimed durable worker with a fresh heartbeat can legitimately\n // be waiting on a bounded long-running tool/sub-agent call. Still\n // surface informational status at the normal background threshold;\n // RunStuckBanner uses the fresh heartbeat to withhold Retry while\n // keeping an explicit Cancel available. The legacy live-worker\n // threshold may make that notice earlier, but never later.\n const effectiveThresholdMs = effectiveThresholdFor(\n dispatchMode,\n heartbeatSinceMs,\n );\n const isStuck = Boolean(\n data.active &&\n data.status === \"running\" &&\n stuckSinceMs != null &&\n stuckSinceMs > effectiveThresholdMs,\n );\n const observedAtMs = Date.now();\n const version = ++snapshotVersion;\n scheduleSnapshotTransition(\n {\n active: data.active,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n },\n observedAtMs,\n version,\n );\n setState({\n isStuck,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n hasInFlightWork:\n typeof data.hasInFlightWork === \"boolean\"\n ? data.hasInFlightWork\n : null,\n });\n // Back off polling when nothing is in flight — there's no point\n // hammering the endpoint while the chat is idle. We still poll\n // occasionally so a fresh run started in another tab is picked up.\n if (!data.active || data.status !== \"running\") {\n nextDelay = IDLE_BACKOFF_INTERVAL_MS;\n }\n } else if (res.status === 429 || res.status >= 500) {\n nextDelay = pollFailureDelay();\n } else {\n consecutivePollFailures = 0;\n clearObservedRun();\n nextDelay = MAX_POLL_ERROR_BACKOFF_MS;\n }\n } catch {\n // Network/503 blip — leave previous state and ease polling pressure\n // while the server or database recovers.\n nextDelay = pollFailureDelay();\n }\n if (!cancelled) {\n timer = setTimeout(poll, nextDelay);\n }\n };\n\n // Stagger the first poll so a freshly-started run isn't immediately\n // classified as stuck before the server has had a chance to record\n // any progress events.\n timer = setTimeout(poll, 2_000);\n\n return () => {\n cancelled = true;\n snapshotVersion += 1;\n if (timer) clearTimeout(timer);\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n };\n }, [\n threadId,\n enabled,\n stuckThresholdMs,\n backgroundStuckThresholdMs,\n liveBackgroundStuckThresholdMs,\n pollIntervalMs,\n apiUrl,\n ]);\n\n return state;\n}\n\n/**\n * POST `/runs/:id/abort` so the server flips the run to \"aborted\" and the\n * adapter's reconnect loop exits cleanly. Returns the run id that was\n * aborted (or null on failure) so callers can correlate observability\n * events. Best-effort — failures are swallowed, since the user's intent\n * is already captured locally.\n */\nexport function useAbortRun(apiUrl?: string) {\n return useCallback(\n async (runId: string, reason: string = \"user\"): Promise<string | null> => {\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n try {\n const res = await fetch(\n `${base}/runs/${encodeURIComponent(runId)}/abort`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify({ reason }),\n },\n );\n if (!res.ok) return null;\n return runId;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n}\n"]}
@@ -118,6 +118,13 @@ export declare function isLocalDatabase(): boolean;
118
118
  export declare function intType(): string;
119
119
  export declare function sqliteToPostgresParams(sql: string): string;
120
120
  export declare function isConnectionError(err: any): boolean;
121
+ /**
122
+ * Classify database failures that should temporarily shed request load.
123
+ * Statement timeouts are not included in isConnectionError() because retrying
124
+ * every timed-out mutation would not be safe, but request handlers can still
125
+ * return a retryable service-unavailable response for them.
126
+ */
127
+ export declare function isTransientDatabaseError(err: unknown): boolean;
121
128
  export declare function retryOnConnectionError<T>(fn: () => Promise<T>, maxAttempts?: number): Promise<T>;
122
129
  /**
123
130
  * Max wall time for a single DB op (init or query) before we treat it as a
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
package/dist/db/client.js CHANGED
@@ -560,6 +560,39 @@ export function isConnectionError(err) {
560
560
  const msg = String(err.message || err.cause?.message || "");
561
561
  return /ECONNRESET|ETIMEDOUT|EPIPE|connection.*(closed|ended|terminated)|socket hang up|websocket/i.test(msg);
562
562
  }
563
+ /**
564
+ * Classify database failures that should temporarily shed request load.
565
+ * Statement timeouts are not included in isConnectionError() because retrying
566
+ * every timed-out mutation would not be safe, but request handlers can still
567
+ * return a retryable service-unavailable response for them.
568
+ */
569
+ export function isTransientDatabaseError(err) {
570
+ const error = err;
571
+ const code = String(error?.code ?? error?.cause?.code ?? "");
572
+ if (code === "ECHECKOUTTIMEOUT" ||
573
+ code === "53300" ||
574
+ code === "57014" ||
575
+ /^08/.test(code) ||
576
+ /^57P0[123]$/.test(code)) {
577
+ return true;
578
+ }
579
+ const message = [error?.message, error?.cause?.message]
580
+ .filter((value) => typeof value === "string")
581
+ .join(" ");
582
+ if (/\bstatement timeout\b|\bdb (?:query|connect) timed out\b/i.test(message)) {
583
+ return true;
584
+ }
585
+ const databaseSurface = [
586
+ error?.name,
587
+ error?.cause?.name,
588
+ error?.stack,
589
+ error?.cause?.stack,
590
+ ]
591
+ .filter((value) => typeof value === "string")
592
+ .join(" ");
593
+ return (isConnectionError(error) &&
594
+ /@neondatabase|@libsql|\bpostgres(?:ql)?\b|\bpg-pool\b|drizzle-orm|\/db\/client\.[cm]?[jt]s/i.test(databaseSurface));
595
+ }
563
596
  export async function retryOnConnectionError(fn, maxAttempts = 3) {
564
597
  let last;
565
598
  for (let attempt = 0; attempt < maxAttempts; attempt++) {