@agent-native/core 0.131.9 → 0.132.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +99 -0
  3. package/corpus/core/docs/content/durable-background-runs.mdx +15 -6
  4. package/corpus/core/docs/design/durable-agent-runs.md +15 -11
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/activity.ts +127 -10
  7. package/corpus/core/src/a2a/index.ts +3 -0
  8. package/corpus/core/src/agent/durable-background.ts +38 -28
  9. package/corpus/core/src/agent/harness/background.ts +12 -2
  10. package/corpus/core/src/agent/production-agent.ts +18 -15
  11. package/corpus/core/src/agent/run-store.ts +45 -3
  12. package/corpus/core/src/application-state/handlers.ts +45 -0
  13. package/corpus/core/src/application-state/index.ts +3 -0
  14. package/corpus/core/src/application-state/store.ts +28 -11
  15. package/corpus/core/src/audit/redact.ts +104 -18
  16. package/corpus/core/src/client/AgentPanel.tsx +2 -7
  17. package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
  18. package/corpus/core/src/client/RunStuckBanner.tsx +29 -6
  19. package/corpus/core/src/client/agent-chat.ts +4 -9
  20. package/corpus/core/src/client/application-state.ts +115 -12
  21. package/corpus/core/src/client/dynamic-suggestions.ts +4 -12
  22. package/corpus/core/src/client/guided-questions.tsx +12 -23
  23. package/corpus/core/src/db/client.ts +148 -28
  24. package/corpus/core/src/db/create-get-db.ts +39 -6
  25. package/corpus/core/src/db/runtime-diagnostics.ts +41 -6
  26. package/corpus/core/src/deploy/build.ts +26 -33
  27. package/corpus/core/src/feature-flags/store.ts +10 -5
  28. package/corpus/core/src/integrations/a2a-continuations-store.ts +11 -0
  29. package/corpus/core/src/integrations/config-store.ts +13 -0
  30. package/corpus/core/src/integrations/google-docs-poller.ts +26 -2
  31. package/corpus/core/src/mcp-client/routes.ts +33 -2
  32. package/corpus/core/src/onboarding/plugin.ts +31 -25
  33. package/corpus/core/src/org/accept-pending.ts +2 -0
  34. package/corpus/core/src/org/auto-join-domain.ts +2 -0
  35. package/corpus/core/src/org/context.ts +35 -10
  36. package/corpus/core/src/org/handlers.ts +6 -0
  37. package/corpus/core/src/org/request-org-cache.ts +71 -0
  38. package/corpus/core/src/resources/store.ts +10 -9
  39. package/corpus/core/src/scripts/runner.ts +6 -3
  40. package/corpus/core/src/secrets/storage.ts +97 -5
  41. package/corpus/core/src/server/action-change.ts +33 -0
  42. package/corpus/core/src/server/action-routes.ts +11 -9
  43. package/corpus/core/src/server/agent-teams.ts +10 -2
  44. package/corpus/core/src/server/better-auth-instance.ts +39 -12
  45. package/corpus/core/src/server/core-routes-plugin.ts +19 -2
  46. package/corpus/core/src/server/credential-provider.ts +57 -14
  47. package/corpus/core/src/server/http-response-telemetry.ts +1 -0
  48. package/corpus/core/src/server/poll.ts +95 -57
  49. package/corpus/core/src/server/voice-providers-status.ts +19 -4
  50. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -0
  51. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
  52. package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +299 -0
  53. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +34 -0
  54. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +82 -0
  55. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +2 -2
  56. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +40 -34
  57. package/corpus/templates/analytics/app/hooks/use-user-pref.ts +3 -3
  58. package/corpus/templates/analytics/app/i18n-data.ts +40 -0
  59. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +18 -10
  60. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +14 -0
  61. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +1 -0
  62. package/corpus/templates/analytics/app/routes/chart.tsx +3 -0
  63. package/corpus/templates/analytics/changelog/2026-07-30-analytics-dashboards-now-expose-native-funnel-heatmap-callou.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-07-30-analytics-now-ships-reusable-native-v2-dashboard-manifests-w.md +6 -0
  65. package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +81 -1
  66. package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +457 -0
  67. package/corpus/templates/analytics/server/lib/report-chart-svg.ts +73 -2
  68. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
  69. package/corpus/templates/analytics/shared/dashboard-funnel.ts +83 -0
  70. package/corpus/templates/content/changelog/2026-07-30-long-running-delegated-requests-now-continue-reliably.md +6 -0
  71. package/corpus/templates/content/netlify.toml +3 -0
  72. package/corpus/templates/content/server/plugins/agent-chat.ts +1 -0
  73. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -1
  74. package/corpus/templates/slides/actions/patch-deck.ts +25 -1
  75. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +17 -4
  76. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +9 -1
  77. package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
  78. package/corpus/templates/slides/app/context/DeckContext.tsx +11 -3
  79. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +39 -11
  80. package/corpus/templates/slides/app/pages/Index.tsx +2 -2
  81. package/corpus/templates/slides/changelog/2026-07-30-slide-previews-keep-their-aspect-ratio-isolate-embedded-styl.md +6 -0
  82. package/corpus/templates/slides/changelog/2026-07-30-slides-no-longer-lose-decks-while-organization-access-is-loa.md +6 -0
  83. package/corpus/toolkit/CHANGELOG.md +8 -0
  84. package/corpus/toolkit/package.json +1 -1
  85. package/corpus/toolkit/src/composer/runtime-adapters.tsx +16 -11
  86. package/dist/a2a/activity.d.ts +22 -4
  87. package/dist/a2a/activity.d.ts.map +1 -1
  88. package/dist/a2a/activity.js +97 -11
  89. package/dist/a2a/activity.js.map +1 -1
  90. package/dist/a2a/index.d.ts +1 -1
  91. package/dist/a2a/index.d.ts.map +1 -1
  92. package/dist/a2a/index.js +1 -1
  93. package/dist/a2a/index.js.map +1 -1
  94. package/dist/agent/durable-background.d.ts +13 -8
  95. package/dist/agent/durable-background.d.ts.map +1 -1
  96. package/dist/agent/durable-background.js +36 -28
  97. package/dist/agent/durable-background.js.map +1 -1
  98. package/dist/agent/harness/background.d.ts.map +1 -1
  99. package/dist/agent/harness/background.js +12 -2
  100. package/dist/agent/harness/background.js.map +1 -1
  101. package/dist/agent/production-agent.d.ts.map +1 -1
  102. package/dist/agent/production-agent.js +18 -15
  103. package/dist/agent/production-agent.js.map +1 -1
  104. package/dist/agent/run-store.d.ts +2 -0
  105. package/dist/agent/run-store.d.ts.map +1 -1
  106. package/dist/agent/run-store.js +44 -3
  107. package/dist/agent/run-store.js.map +1 -1
  108. package/dist/application-state/handlers.d.ts +15 -0
  109. package/dist/application-state/handlers.d.ts.map +1 -1
  110. package/dist/application-state/handlers.js +42 -2
  111. package/dist/application-state/handlers.js.map +1 -1
  112. package/dist/application-state/index.d.ts +2 -2
  113. package/dist/application-state/index.d.ts.map +1 -1
  114. package/dist/application-state/index.js +2 -2
  115. package/dist/application-state/index.js.map +1 -1
  116. package/dist/application-state/store.d.ts +11 -0
  117. package/dist/application-state/store.d.ts.map +1 -1
  118. package/dist/application-state/store.js +23 -10
  119. package/dist/application-state/store.js.map +1 -1
  120. package/dist/audit/redact.d.ts +25 -2
  121. package/dist/audit/redact.d.ts.map +1 -1
  122. package/dist/audit/redact.js +85 -21
  123. package/dist/audit/redact.js.map +1 -1
  124. package/dist/client/AgentPanel.d.ts.map +1 -1
  125. package/dist/client/AgentPanel.js +2 -7
  126. package/dist/client/AgentPanel.js.map +1 -1
  127. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  128. package/dist/client/MultiTabAssistantChat.js +1 -1
  129. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  130. package/dist/client/RunStuckBanner.d.ts +11 -1
  131. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  132. package/dist/client/RunStuckBanner.js +20 -7
  133. package/dist/client/RunStuckBanner.js.map +1 -1
  134. package/dist/client/agent-chat.d.ts.map +1 -1
  135. package/dist/client/agent-chat.js +4 -6
  136. package/dist/client/agent-chat.js.map +1 -1
  137. package/dist/client/application-state.d.ts +11 -0
  138. package/dist/client/application-state.d.ts.map +1 -1
  139. package/dist/client/application-state.js +87 -9
  140. package/dist/client/application-state.js.map +1 -1
  141. package/dist/client/dynamic-suggestions.d.ts.map +1 -1
  142. package/dist/client/dynamic-suggestions.js +4 -13
  143. package/dist/client/dynamic-suggestions.js.map +1 -1
  144. package/dist/client/guided-questions.d.ts.map +1 -1
  145. package/dist/client/guided-questions.js +6 -22
  146. package/dist/client/guided-questions.js.map +1 -1
  147. package/dist/collab/awareness.d.ts +2 -2
  148. package/dist/collab/awareness.d.ts.map +1 -1
  149. package/dist/db/client.d.ts +57 -10
  150. package/dist/db/client.d.ts.map +1 -1
  151. package/dist/db/client.js +122 -31
  152. package/dist/db/client.js.map +1 -1
  153. package/dist/db/create-get-db.d.ts.map +1 -1
  154. package/dist/db/create-get-db.js +31 -7
  155. package/dist/db/create-get-db.js.map +1 -1
  156. package/dist/db/runtime-diagnostics.d.ts +1 -0
  157. package/dist/db/runtime-diagnostics.d.ts.map +1 -1
  158. package/dist/db/runtime-diagnostics.js +31 -6
  159. package/dist/db/runtime-diagnostics.js.map +1 -1
  160. package/dist/deploy/build.d.ts +5 -20
  161. package/dist/deploy/build.d.ts.map +1 -1
  162. package/dist/deploy/build.js +18 -30
  163. package/dist/deploy/build.js.map +1 -1
  164. package/dist/feature-flags/store.d.ts.map +1 -1
  165. package/dist/feature-flags/store.js +10 -5
  166. package/dist/feature-flags/store.js.map +1 -1
  167. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  168. package/dist/integrations/a2a-continuations-store.js +12 -0
  169. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  170. package/dist/integrations/config-store.d.ts +1 -0
  171. package/dist/integrations/config-store.d.ts.map +1 -1
  172. package/dist/integrations/config-store.js +11 -0
  173. package/dist/integrations/config-store.js.map +1 -1
  174. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  175. package/dist/integrations/google-docs-poller.js +19 -2
  176. package/dist/integrations/google-docs-poller.js.map +1 -1
  177. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  178. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  179. package/dist/mcp-client/routes.d.ts.map +1 -1
  180. package/dist/mcp-client/routes.js +30 -2
  181. package/dist/mcp-client/routes.js.map +1 -1
  182. package/dist/notifications/routes.d.ts +6 -6
  183. package/dist/observability/routes.d.ts +3 -3
  184. package/dist/onboarding/plugin.d.ts.map +1 -1
  185. package/dist/onboarding/plugin.js +12 -8
  186. package/dist/onboarding/plugin.js.map +1 -1
  187. package/dist/org/accept-pending.d.ts.map +1 -1
  188. package/dist/org/accept-pending.js +2 -0
  189. package/dist/org/accept-pending.js.map +1 -1
  190. package/dist/org/auto-join-domain.d.ts.map +1 -1
  191. package/dist/org/auto-join-domain.js +2 -0
  192. package/dist/org/auto-join-domain.js.map +1 -1
  193. package/dist/org/context.d.ts.map +1 -1
  194. package/dist/org/context.js +29 -9
  195. package/dist/org/context.js.map +1 -1
  196. package/dist/org/handlers.d.ts.map +1 -1
  197. package/dist/org/handlers.js +5 -0
  198. package/dist/org/handlers.js.map +1 -1
  199. package/dist/org/request-org-cache.d.ts +16 -0
  200. package/dist/org/request-org-cache.d.ts.map +1 -0
  201. package/dist/org/request-org-cache.js +62 -0
  202. package/dist/org/request-org-cache.js.map +1 -0
  203. package/dist/progress/routes.d.ts +1 -1
  204. package/dist/resources/handlers.d.ts +1 -1
  205. package/dist/resources/store.d.ts.map +1 -1
  206. package/dist/resources/store.js +11 -9
  207. package/dist/resources/store.js.map +1 -1
  208. package/dist/scripts/runner.d.ts.map +1 -1
  209. package/dist/scripts/runner.js +3 -3
  210. package/dist/scripts/runner.js.map +1 -1
  211. package/dist/secrets/storage.d.ts.map +1 -1
  212. package/dist/secrets/storage.js +82 -5
  213. package/dist/secrets/storage.js.map +1 -1
  214. package/dist/server/action-change.d.ts +20 -0
  215. package/dist/server/action-change.d.ts.map +1 -1
  216. package/dist/server/action-change.js +29 -0
  217. package/dist/server/action-change.js.map +1 -1
  218. package/dist/server/action-routes.d.ts.map +1 -1
  219. package/dist/server/action-routes.js +7 -8
  220. package/dist/server/action-routes.js.map +1 -1
  221. package/dist/server/agent-teams.d.ts.map +1 -1
  222. package/dist/server/agent-teams.js +10 -2
  223. package/dist/server/agent-teams.js.map +1 -1
  224. package/dist/server/better-auth-instance.d.ts.map +1 -1
  225. package/dist/server/better-auth-instance.js +31 -14
  226. package/dist/server/better-auth-instance.js.map +1 -1
  227. package/dist/server/core-routes-plugin.d.ts +1 -0
  228. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  229. package/dist/server/core-routes-plugin.js +18 -4
  230. package/dist/server/core-routes-plugin.js.map +1 -1
  231. package/dist/server/credential-provider.d.ts +16 -0
  232. package/dist/server/credential-provider.d.ts.map +1 -1
  233. package/dist/server/credential-provider.js +49 -13
  234. package/dist/server/credential-provider.js.map +1 -1
  235. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  236. package/dist/server/http-response-telemetry.js +1 -0
  237. package/dist/server/http-response-telemetry.js.map +1 -1
  238. package/dist/server/poll.d.ts +6 -0
  239. package/dist/server/poll.d.ts.map +1 -1
  240. package/dist/server/poll.js +84 -47
  241. package/dist/server/poll.js.map +1 -1
  242. package/dist/server/transcribe-voice.d.ts +1 -1
  243. package/dist/server/voice-providers-status.d.ts.map +1 -1
  244. package/dist/server/voice-providers-status.js +12 -3
  245. package/dist/server/voice-providers-status.js.map +1 -1
  246. package/docs/content/durable-background-runs.mdx +15 -6
  247. package/docs/design/durable-agent-runs.md +15 -11
  248. package/package.json +2 -2
  249. package/src/a2a/activity.ts +127 -10
  250. package/src/a2a/index.ts +3 -0
  251. package/src/agent/durable-background.ts +38 -28
  252. package/src/agent/harness/background.ts +12 -2
  253. package/src/agent/production-agent.ts +18 -15
  254. package/src/agent/run-store.ts +45 -3
  255. package/src/application-state/handlers.ts +45 -0
  256. package/src/application-state/index.ts +3 -0
  257. package/src/application-state/store.ts +28 -11
  258. package/src/audit/redact.ts +104 -18
  259. package/src/client/AgentPanel.tsx +2 -7
  260. package/src/client/MultiTabAssistantChat.tsx +3 -0
  261. package/src/client/RunStuckBanner.tsx +29 -6
  262. package/src/client/agent-chat.ts +4 -9
  263. package/src/client/application-state.ts +115 -12
  264. package/src/client/dynamic-suggestions.ts +4 -12
  265. package/src/client/guided-questions.tsx +12 -23
  266. package/src/db/client.ts +148 -28
  267. package/src/db/create-get-db.ts +39 -6
  268. package/src/db/runtime-diagnostics.ts +41 -6
  269. package/src/deploy/build.ts +26 -33
  270. package/src/feature-flags/store.ts +10 -5
  271. package/src/integrations/a2a-continuations-store.ts +11 -0
  272. package/src/integrations/config-store.ts +13 -0
  273. package/src/integrations/google-docs-poller.ts +26 -2
  274. package/src/mcp-client/routes.ts +33 -2
  275. package/src/onboarding/plugin.ts +31 -25
  276. package/src/org/accept-pending.ts +2 -0
  277. package/src/org/auto-join-domain.ts +2 -0
  278. package/src/org/context.ts +35 -10
  279. package/src/org/handlers.ts +6 -0
  280. package/src/org/request-org-cache.ts +71 -0
  281. package/src/resources/store.ts +10 -9
  282. package/src/scripts/runner.ts +6 -3
  283. package/src/secrets/storage.ts +97 -5
  284. package/src/server/action-change.ts +33 -0
  285. package/src/server/action-routes.ts +11 -9
  286. package/src/server/agent-teams.ts +10 -2
  287. package/src/server/better-auth-instance.ts +39 -12
  288. package/src/server/core-routes-plugin.ts +19 -2
  289. package/src/server/credential-provider.ts +57 -14
  290. package/src/server/http-response-telemetry.ts +1 -0
  291. package/src/server/poll.ts +95 -57
  292. package/src/server/voice-providers-status.ts +19 -4
@@ -1 +1 @@
1
- {"version":3,"file":"voice-providers-status.js","sourceRoot":"","sources":["../../src/server/voice-providers-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,iBAAiB,GAElB,MAAM,IAAI,CAAC;AAEZ,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EACL,mCAAmC,EACnC,aAAa,GACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAyB7D,MAAM,UAAU,iCAAiC;IAC/C,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAc,EAAE,EAAE;QACjD,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC;YAC/B,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QACzC,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,OAAO,EAAE,KAAK;YAC3B,CAAC,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;YAC9C,CAAC,CAAC,IAAI,CAAC;QACT,MAAM,cAAc,GAAG;YACrB,SAAS,EAAE,OAAO,EAAE,KAAK;YACzB,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,SAAS;SAClC,CAAC;QACF,MAAM,kBAAkB,GAAG,KAAK,EAAK,EAAoB,EAAc,EAAE,CACvE,cAAc,CAAC,SAAS;YACtB,CAAC,CAAC,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3C,CAAC,CAAC,EAAE,EAAE,CAAC;QAEX,KAAK,UAAU,MAAM,CAAC,GAAW;YAC/B,IAAI,CAAC;gBACH,IAAI,GAAG,KAAK,gCAAgC,EAAE,CAAC;oBAC7C,MAAM,QAAQ,GAAG,MAAM,gCAAgC,CAAC;wBACtD,SAAS,EAAE,OAAO,EAAE,KAAK;wBACzB,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,SAAS;qBAClC,CAAC,CAAC;oBACH,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;gBAC7D,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;gBACpE,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YACH,OAAO;gBACL,CAAC,MAAM,kBAAkB,CAAC,GAAG,EAAE,CAC7B,mCAAmC,EAAE,CACtC,CAAC,KAAK,IAAI,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;QAED,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC/D,MAAM,CAAC,gBAAgB,CAAC;YACxB,MAAM,CAAC,gBAAgB,CAAC;YACxB,MAAM,CAAC,cAAc,CAAC;YACtB,MAAM,CAAC,gCAAgC,CAAC;SACzC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAyB;YACnC,OAAO;YACP,MAAM;YACN,MAAM;YACN,IAAI;YACJ,cAAc;YACd,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,IAAI;SACb,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * GET /_agent-native/voice-providers/status\n *\n * Reports which voice transcription providers are configured for the\n * current user. The desktop Settings UI uses this to show \"Connect\" vs\n * \"Connected\" status pills next to each provider option.\n *\n * Resolution mirrors `transcribe-voice.ts`: we read request-scoped encrypted\n * secrets (user, org, workspace), with env fallback only outside authenticated\n * request contexts. Each lookup is wrapped in try/catch — one provider's\n * failure must never break the whole response.\n *\n * Returns booleans only — never the actual key material.\n */\nimport {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { getOrgContext } from \"../org/context.js\";\nimport { getSession } from \"./auth.js\";\nimport {\n resolveHasCompleteBuilderConnection,\n resolveSecret,\n} from \"./credential-provider.js\";\nimport { resolveGoogleRealtimeCredentials } from \"./google-realtime-session.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\n\nexport interface VoiceProvidersStatus {\n builder: boolean;\n gemini: boolean;\n openai: boolean;\n groq: boolean;\n /**\n * Google Speech-to-Text realtime streaming is BYOK-only for v1. This reports\n * whether a service-account credential is configured; the actual stream runs\n * through the dedicated WebSocket -> StreamingRecognize path, not the batch\n * transcribe route.\n */\n googleRealtime: boolean;\n /** Always true — the Web Speech API is available in WebKit-based clients. */\n browser: true;\n /**\n * Apple's SFSpeechRecognizer + AVAudioEngine, exposed by the Tauri\n * desktop client. Always reported as `true` from the server — the\n * desktop client gates this on macOS at the Tauri-command boundary, so\n * non-macOS hosts return a clear error instead of attempting to use it.\n */\n native: true;\n}\n\nexport function createVoiceProvidersStatusHandler() {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"GET\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n const session = await getSession(event).catch(() => null);\n const orgCtx = session?.email\n ? await getOrgContext(event).catch(() => null)\n : null;\n const requestContext = {\n userEmail: session?.email,\n orgId: orgCtx?.orgId ?? undefined,\n };\n const withRequestContext = async <T>(fn: () => Promise<T>): Promise<T> =>\n requestContext.userEmail\n ? runWithRequestContext(requestContext, fn)\n : fn();\n\n async function hasKey(key: string): Promise<boolean> {\n try {\n if (key === \"GOOGLE_APPLICATION_CREDENTIALS\") {\n const resolved = await resolveGoogleRealtimeCredentials({\n userEmail: session?.email,\n orgId: orgCtx?.orgId ?? undefined,\n });\n return typeof resolved === \"string\" && resolved.length > 0;\n }\n const resolved = await withRequestContext(() => resolveSecret(key));\n return typeof resolved === \"string\" && resolved.length > 0;\n } catch {\n return false;\n }\n }\n\n let builder = false;\n try {\n builder =\n (await withRequestContext(() =>\n resolveHasCompleteBuilderConnection(),\n )) === true;\n } catch {\n builder = false;\n }\n\n const [gemini, openai, groq, googleRealtime] = await Promise.all([\n hasKey(\"GEMINI_API_KEY\"),\n hasKey(\"OPENAI_API_KEY\"),\n hasKey(\"GROQ_API_KEY\"),\n hasKey(\"GOOGLE_APPLICATION_CREDENTIALS\"),\n ]);\n\n const status: VoiceProvidersStatus = {\n builder,\n gemini,\n openai,\n groq,\n googleRealtime,\n browser: true,\n native: true,\n };\n return status;\n });\n}\n"]}
1
+ {"version":3,"file":"voice-providers-status.js","sourceRoot":"","sources":["../../src/server/voice-providers-status.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GET /_agent-native/voice-providers/status\n *\n * Reports which voice transcription providers are configured for the\n * current user. The desktop Settings UI uses this to show \"Connect\" vs\n * \"Connected\" status pills next to each provider option.\n *\n * Resolution mirrors `transcribe-voice.ts`: we read request-scoped encrypted\n * secrets (user, org, workspace), with env fallback only outside authenticated\n * request contexts. Each lookup is wrapped in try/catch — one provider's\n * failure must never break the whole response.\n *\n * Returns booleans only — never the actual key material.\n */\nimport {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { getOrgContext } from \"../org/context.js\";\nimport { getSession } from \"./auth.js\";\nimport {\n prefetchSecrets,\n resolveHasCompleteBuilderConnection,\n resolveSecret,\n} from \"./credential-provider.js\";\nimport { resolveGoogleRealtimeCredentials } from \"./google-realtime-session.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\n\nexport interface VoiceProvidersStatus {\n builder: boolean;\n gemini: boolean;\n openai: boolean;\n groq: boolean;\n /**\n * Google Speech-to-Text realtime streaming is BYOK-only for v1. This reports\n * whether a service-account credential is configured; the actual stream runs\n * through the dedicated WebSocket -> StreamingRecognize path, not the batch\n * transcribe route.\n */\n googleRealtime: boolean;\n /** Always true — the Web Speech API is available in WebKit-based clients. */\n browser: true;\n /**\n * Apple's SFSpeechRecognizer + AVAudioEngine, exposed by the Tauri\n * desktop client. Always reported as `true` from the server — the\n * desktop client gates this on macOS at the Tauri-command boundary, so\n * non-macOS hosts return a clear error instead of attempting to use it.\n */\n native: true;\n}\n\nexport function createVoiceProvidersStatusHandler() {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"GET\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n const session = await getSession(event).catch(() => null);\n const orgCtx = session?.email\n ? await getOrgContext(event).catch(() => null)\n : null;\n const requestContext = {\n userEmail: session?.email,\n orgId: orgCtx?.orgId ?? undefined,\n };\n const withRequestContext = async <T>(fn: () => Promise<T>): Promise<T> =>\n requestContext.userEmail\n ? runWithRequestContext(requestContext, fn)\n : fn();\n\n async function hasKey(key: string): Promise<boolean> {\n try {\n if (key === \"GOOGLE_APPLICATION_CREDENTIALS\") {\n // Same identity, passed explicitly — the context is here only so this\n // scope sweep shares the prefetched per-request memo below.\n const resolved = await withRequestContext(() =>\n resolveGoogleRealtimeCredentials({\n userEmail: session?.email,\n orgId: orgCtx?.orgId ?? undefined,\n }),\n );\n return typeof resolved === \"string\" && resolved.length > 0;\n }\n const resolved = await withRequestContext(() => resolveSecret(key));\n return typeof resolved === \"string\" && resolved.length > 0;\n } catch {\n return false;\n }\n }\n\n // One read per scope for every key below, instead of one per key per scope.\n await withRequestContext(() =>\n prefetchSecrets([\n \"GEMINI_API_KEY\",\n \"OPENAI_API_KEY\",\n \"GROQ_API_KEY\",\n \"GOOGLE_APPLICATION_CREDENTIALS\",\n ]),\n );\n\n let builder = false;\n try {\n builder =\n (await withRequestContext(() =>\n resolveHasCompleteBuilderConnection(),\n )) === true;\n } catch {\n builder = false;\n }\n\n const [gemini, openai, groq, googleRealtime] = await Promise.all([\n hasKey(\"GEMINI_API_KEY\"),\n hasKey(\"OPENAI_API_KEY\"),\n hasKey(\"GROQ_API_KEY\"),\n hasKey(\"GOOGLE_APPLICATION_CREDENTIALS\"),\n ]);\n\n const status: VoiceProvidersStatus = {\n builder,\n gemini,\n openai,\n groq,\n googleRealtime,\n browser: true,\n native: true,\n };\n return status;\n });\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: "Durable Background Runs"
3
- description: "Long agent-chat turns can run on a separate 15-minute Netlify background function instead of the synchronous request, so multi-step work finishes server-side without the client babysitting continuations. Opt-in per app; a foreground circuit-breaker keeps chat working even if the worker never starts."
3
+ description: "Long agent-chat turns can run on a separate 15-minute Netlify background function instead of the synchronous request, so multi-step work finishes server-side without the client babysitting continuations. Enabled by default on deployed Netlify apps with an explicit opt-out; a foreground circuit-breaker keeps chat working even if the worker never starts."
4
4
  ---
5
5
 
6
6
  # Durable Background Runs
@@ -10,7 +10,8 @@ description: "Long agent-chat turns can run on a separate 15-minute Netlify back
10
10
  **Who is this for:** anyone deploying an agent-native app who wants long,
11
11
  multi-step agent turns to complete server-side, and anyone wondering what the
12
12
  `AGENT_CHAT_DURABLE_BACKGROUND` flag and the second Netlify function do. This
13
- is opt-in and entirely inside the framework there is no app code to write.
13
+ is enabled by default on deployed Netlify apps and entirely inside the
14
+ framework — there is no app code to write.
14
15
 
15
16
  </Callout>
16
17
 
@@ -108,14 +109,17 @@ normal API calls always run synchronously on `server`.
108
109
  The foreground chat POST decides **per turn**. It dispatches to the background
109
110
  function only when all three hold:
110
111
 
111
- - `AGENT_CHAT_DURABLE_BACKGROUND` is truthy (`true` / `1` / `yes` / `on`),
112
+ - the app is deployed on Netlify and `AGENT_CHAT_DURABLE_BACKGROUND` is not
113
+ explicitly falsy (`false` / `0` / `no` / `off`), or a non-Netlify hosted app
114
+ explicitly opts in with a truthy value (`true` / `1` / `yes` / `on`),
112
115
  - the app is running hosted (on the platform, not local dev), and
113
116
  - `A2A_SECRET` is set (the dispatch is an HMAC-signed self-call; without the
114
117
  secret it cannot be signed, so the turn stays inline).
115
118
 
116
- If any is missing, the turn runs inline exactly as before. The flag is read at
119
+ If the app is local, `A2A_SECRET` is missing, or the flag is explicitly
120
+ disabled, the turn runs inline exactly as before. The setting is read at
117
121
  **both build time** (to decide whether to emit the background function) and
118
- **runtime** (to decide whether to dispatch), so enabling it requires a redeploy.
122
+ **runtime** (to decide whether to dispatch), so changing it requires a redeploy.
119
123
 
120
124
  ## The lifecycle and the circuit-breaker {#lifecycle}
121
125
 
@@ -190,7 +194,12 @@ one long invocation, not purely additive.
190
194
 
191
195
  <Callout id="doc-block-gjoj2d" tone="info">
192
196
 
193
- Durable runs are opt-in per app and default OFF. Enable with AGENT_CHAT_DURABLE_BACKGROUND=true (plus A2A_SECRET) and redeploy. Verify a turn actually used the worker by checking that its run reaches dispatch_mode=background-processing rather than falling back to inline recovery.
197
+ Durable runs are enabled by default for deployed Netlify apps. Set
198
+ `AGENT_CHAT_DURABLE_BACKGROUND=false` to opt out, or set it to `true` on another
199
+ host. `A2A_SECRET` is still required. Redeploy after changing the setting, and
200
+ verify a turn actually used the worker by checking that its run reaches
201
+ `dispatch_mode=background-processing` rather than falling back to inline
202
+ recovery.
194
203
 
195
204
  </Callout>
196
205
 
@@ -200,7 +200,8 @@ itself checkpoint).
200
200
 
201
201
  > Status: **Phase 0 shipped. Phase 1 + Phase 2 implemented** (the host-agnostic
202
202
  > durable-background worker and the Netlify 15-min `-background` optimization),
203
- > behind `AGENT_CHAT_DURABLE_BACKGROUND`, off by default. Internal per-step
203
+ > default-on for deployed Netlify apps with an explicit opt-out via
204
+ > `AGENT_CHAT_DURABLE_BACKGROUND=false`. Internal per-step
204
205
  > checkpointing (the Option A core of Phase 1) is recommended-not-yet-built; the
205
206
  > worker today gets its durability from the long single invocation plus
206
207
  > server-chained continuations rather than per-step idempotent checkpoints. See
@@ -251,13 +252,14 @@ checkpointed and durable runs remove the ceiling itself.
251
252
 
252
253
  # Final layered architecture
253
254
 
254
- Status: implemented (flagged, off by default)
255
+ Status: implemented (default-on for deployed Netlify, explicit opt-out)
255
256
  Owner: core / run-manager + deploy
256
257
 
257
- Durable background runs ship as **two layers**. Both are gated behind
258
- `AGENT_CHAT_DURABLE_BACKGROUND` and default off; when off, the agent-chat run
259
- path and the deploy output are byte-for-byte the pre-existing synchronous
260
- behavior.
258
+ Durable background runs ship as **two layers**. Deployed Netlify apps enable the
259
+ path by default and can opt out with an explicit falsy
260
+ `AGENT_CHAT_DURABLE_BACKGROUND` value. Other hosted platforms retain explicit
261
+ opt-in. When off, the agent-chat run path and deploy output are byte-for-byte
262
+ the pre-existing synchronous behavior.
261
263
 
262
264
  ### Layer 1 (portable baseline) — host-agnostic durable execution
263
265
 
@@ -600,8 +602,9 @@ Already strong; make the new claim match:
600
602
 
601
603
  > Status: \*\*Slices 0–1 implemented and the Slice-3 background-aware stale window
602
604
  >
603
- > - background→background continuation chaining are implemented\*\*, all behind
604
- > `AGENT_CHAT_DURABLE_BACKGROUND` (off by default). The host-agnostic baseline
605
+ > - background→background continuation chaining are implemented\*\*, default-on
606
+ > for deployed Netlify apps with `AGENT_CHAT_DURABLE_BACKGROUND=false` as the
607
+ > opt-out. The host-agnostic baseline
605
608
  > (Layer 1) carries the run on any host; the Netlify `-background` emit (Layer 2)
606
609
  > is the deploy-time optimization. Slice 2's richer reconnect-first client UX and
607
610
  > the internal per-step checkpointing (Option A) remain follow-ups; Slice 4
@@ -618,9 +621,10 @@ just writes a run event. Dispatch to it from a temporary test route via
618
621
  stub writes an event to `agent_run_events` from the background function, visible
619
622
  via `GET /runs/:id/events`. This de-risks the only genuinely new infra.
620
623
 
621
- **Slice 1 — route the real chat loop through the background function (flagged).**
622
- Behind `AGENT_CHAT_DURABLE_BACKGROUND` (off by default; hosted + `A2A_SECRET`
623
- only): foreground handler inserts the run row, dispatches, returns the SSE
624
+ **Slice 1 — route the real chat loop through the background function.** On
625
+ deployed Netlify it is enabled by default; `AGENT_CHAT_DURABLE_BACKGROUND=false`
626
+ opts out. Other hosted platforms require explicit opt-in, and all hosts require
627
+ `A2A_SECRET`: the foreground handler inserts the run row, dispatches, returns the SSE
624
628
  stream from `subscribeToRun`. The background `_process-run` claims the run and
625
629
  calls the same `startRun` + `runAgentLoop` the inline path uses, with
626
630
  `softTimeoutMs ≈ 13min` (new `backgroundFunction` option in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.131.9",
3
+ "version": "0.132.0",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -396,7 +396,7 @@
396
396
  "yjs": "^13.6.31",
397
397
  "zod": "^4.3.6",
398
398
  "@agent-native/recap-cli": "0.5.1",
399
- "@agent-native/toolkit": "^0.12.0"
399
+ "@agent-native/toolkit": "^0.12.1"
400
400
  },
401
401
  "devDependencies": {
402
402
  "@ai-sdk/anthropic": "^3.0.71",
@@ -1,16 +1,27 @@
1
1
  import { sanitizeToolErrorText } from "../agent/tool-error-redaction.js";
2
2
  import type { AgentChatEvent } from "../agent/types.js";
3
+ import { redactArgsToJson, redactTextToSummary } from "../audit/redact.js";
3
4
  import type { DataPart, Part } from "./types.js";
4
5
 
5
6
  export const A2A_AGENT_ACTIVITY_KIND = "agent-native/agent-activity";
6
7
  export const A2A_AGENT_ACTIVITY_VERSION = 1;
7
8
  export const MAX_A2A_ACTIVITY_REASONING_CHARS = 32_768;
8
9
  export const MAX_A2A_ACTIVITY_RESPONSE_CHARS = 32_768;
9
- export const MAX_A2A_ACTIVITY_TOTAL_CHARS = 98_304;
10
10
  export const MAX_A2A_ACTIVITY_REASONING_SEGMENTS = 128;
11
11
  export const MAX_A2A_ACTIVITY_TOOL_CALLS = 64;
12
12
  export const MAX_A2A_ACTIVITY_TOOL_NAME_CHARS = 96;
13
13
  export const MAX_A2A_ACTIVITY_TOOL_ID_CHARS = 128;
14
+ export const MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS = 1024;
15
+ export const MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS = 512;
16
+ /**
17
+ * Shared ceiling for every recorded tool input/result across the snapshot. The
18
+ * per-call caps alone allow 64 × 1536 chars, which would push a busy run past
19
+ * `MAX_A2A_ACTIVITY_TOTAL_CHARS` and make `parseA2AAgentActivityPart` reject
20
+ * the whole snapshot — trading missing arguments for a missing trace.
21
+ */
22
+ export const MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS = 16_384;
23
+ export const MAX_A2A_ACTIVITY_TOTAL_CHARS =
24
+ 98_304 + MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS;
14
25
 
15
26
  export type A2AAgentActivityPhase =
16
27
  | "reasoning"
@@ -25,6 +36,13 @@ export interface A2AAgentActivityToolCall {
25
36
  name: string;
26
37
  id?: string;
27
38
  status: A2AAgentActivityToolStatus;
39
+ /**
40
+ * Redacted, size-capped JSON of the call's arguments. Absent when the call
41
+ * had none, or when the snapshot's shared payload budget was already spent.
42
+ */
43
+ input?: string;
44
+ /** Redacted, size-capped head of the tool result (`tool_done` only). */
45
+ result?: string;
28
46
  }
29
47
 
30
48
  export interface A2AAgentActivitySnapshot extends Record<string, unknown> {
@@ -68,9 +86,11 @@ export function createA2AAgentActivityState(
68
86
  }
69
87
 
70
88
  /**
71
- * Converts internal loop events into a bounded activity snapshot. It carries
72
- * only reasoning text already emitted to the local chat, never tool inputs or
73
- * results.
89
+ * Converts internal loop events into a bounded activity snapshot. Reasoning
90
+ * text is carried verbatim (it was already emitted to the local chat); tool
91
+ * arguments and results are carried only in redacted, size-capped form so the
92
+ * delegated run stays diagnosable without the snapshot becoming a secondary
93
+ * store of secrets or payloads.
74
94
  */
75
95
  export function applyA2AAgentActivityEvent(
76
96
  state: A2AAgentActivityState,
@@ -91,14 +111,26 @@ export function applyA2AAgentActivityEvent(
91
111
  );
92
112
  changed = true;
93
113
  break;
94
- case "tool_start":
114
+ case "tool_start": {
95
115
  next.activePhase = "tool";
96
- next.toolCalls = appendToolCall(next.toolCalls, toolFromEvent(event));
116
+ const input = captureToolInput(
117
+ event.input,
118
+ remainingPayloadBudget(next.toolCalls),
119
+ );
120
+ next.toolCalls = appendToolCall(next.toolCalls, {
121
+ ...toolFromEvent(event),
122
+ ...(input ? { input } : {}),
123
+ });
97
124
  changed = true;
98
125
  break;
126
+ }
99
127
  case "tool_done":
100
128
  next.activePhase = "tool";
101
- next.toolCalls = settleToolCall(next.toolCalls, toolFromEvent(event));
129
+ next.toolCalls = settleToolCall(
130
+ next.toolCalls,
131
+ toolFromEvent(event),
132
+ event,
133
+ );
102
134
  changed = true;
103
135
  break;
104
136
  case "text":
@@ -203,6 +235,49 @@ function toolFromEvent(
203
235
  };
204
236
  }
205
237
 
238
+ function payloadChars(tool: A2AAgentActivityToolCall): number {
239
+ return (tool.input?.length ?? 0) + (tool.result?.length ?? 0);
240
+ }
241
+
242
+ function remainingPayloadBudget(
243
+ current: A2AAgentActivityToolCall[],
244
+ excludeIndex = -1,
245
+ ): number {
246
+ const used = current.reduce(
247
+ (total, tool, index) =>
248
+ index === excludeIndex ? total : total + payloadChars(tool),
249
+ 0,
250
+ );
251
+ return Math.max(0, MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS - used);
252
+ }
253
+
254
+ /**
255
+ * Redacted, capped JSON of the arguments, or `undefined` when there is nothing
256
+ * to record or the shared budget cannot fit a whole (still-parseable) capture.
257
+ * A partial JSON string would be worse than none — the reader could not tell a
258
+ * clipped object from a different one.
259
+ */
260
+ function captureToolInput(input: unknown, budget: number): string | undefined {
261
+ const cap = Math.min(MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS, budget);
262
+ if (cap < MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS / 8) return undefined;
263
+ const json = redactArgsToJson(input, { maxJson: cap, maxString: 256 });
264
+ // JSON.stringify escapes control characters, so the result is already safe
265
+ // for the snapshot without a `sanitizeText` pass — which would rewrite
266
+ // `"token":"[redacted]"` into unparseable JSON.
267
+ if (!json || json === "{}" || json.length > cap) return undefined;
268
+ return json;
269
+ }
270
+
271
+ function captureToolResult(
272
+ result: string | undefined,
273
+ budget: number,
274
+ ): string | undefined {
275
+ const cap = Math.min(MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS, budget);
276
+ if (!result || cap <= 0) return undefined;
277
+ const summary = redactTextToSummary(result, cap);
278
+ return summary ? sanitizeText(summary, cap) || undefined : undefined;
279
+ }
280
+
206
281
  function appendBoundedSegment(
207
282
  current: string[],
208
283
  addition: string,
@@ -244,6 +319,7 @@ function appendToolCall(
244
319
  function settleToolCall(
245
320
  current: A2AAgentActivityToolCall[],
246
321
  tool: A2AAgentActivityToolCall,
322
+ event: Extract<AgentChatEvent, { type: "tool_done" }>,
247
323
  ): A2AAgentActivityToolCall[] {
248
324
  for (let index = current.length - 1; index >= 0; index -= 1) {
249
325
  const existing = current[index];
@@ -251,12 +327,32 @@ function settleToolCall(
251
327
  (tool.id && tool.id === existing.id) ||
252
328
  (!tool.id && tool.name === existing.name)
253
329
  ) {
330
+ // The matching `tool_start` already paid for its input; reuse it rather
331
+ // than re-capturing (and re-charging) the same arguments.
332
+ let budget = remainingPayloadBudget(current, index);
333
+ const input =
334
+ existing.input ?? captureToolInput(event.input, budget) ?? undefined;
335
+ budget -= input?.length ?? 0;
336
+ const result = captureToolResult(event.result, budget);
337
+ const settled: A2AAgentActivityToolCall = {
338
+ ...tool,
339
+ ...(input ? { input } : {}),
340
+ ...(result ? { result } : {}),
341
+ };
254
342
  return current.map((value, itemIndex) =>
255
- itemIndex === index ? tool : value,
343
+ itemIndex === index ? settled : value,
256
344
  );
257
345
  }
258
346
  }
259
- return appendToolCall(current, tool);
347
+ let budget = remainingPayloadBudget(current);
348
+ const input = captureToolInput(event.input, budget);
349
+ budget -= input?.length ?? 0;
350
+ const result = captureToolResult(event.result, budget);
351
+ return appendToolCall(current, {
352
+ ...tool,
353
+ ...(input ? { input } : {}),
354
+ ...(result ? { result } : {}),
355
+ });
260
356
  }
261
357
 
262
358
  function sanitizeText(value: string, maxChars: number): string {
@@ -320,12 +416,31 @@ function isSafeToolCall(value: unknown): value is A2AAgentActivityToolCall {
320
416
  if (!isRecord(value) || !isSafeToolName(value.name)) return false;
321
417
  return (
322
418
  (value.id === undefined || isSafeToolId(value.id)) &&
419
+ (value.input === undefined ||
420
+ isSafeCapturedText(value.input, MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS)) &&
421
+ (value.result === undefined ||
422
+ isSafeCapturedText(value.result, MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS)) &&
323
423
  (value.status === "running" ||
324
424
  value.status === "completed" ||
325
425
  value.status === "failed")
326
426
  );
327
427
  }
328
428
 
429
+ /**
430
+ * Captured input/result are producer-side redacted strings. Unlike reasoning
431
+ * text they are NOT re-sanitized on read: `sanitizeToolErrorText` rewrites
432
+ * `"token":"…"` into unparseable JSON, so a round-trip equality check would
433
+ * reject every legitimate capture. Bound the length and reject raw control
434
+ * characters instead.
435
+ */
436
+ function isSafeCapturedText(value: unknown, maxChars: number): value is string {
437
+ return (
438
+ typeof value === "string" &&
439
+ value.length <= maxChars &&
440
+ !/[\u0000-\u0008\u000b-\u001f\u007f]/.test(value)
441
+ );
442
+ }
443
+
329
444
  function isSafeText(value: unknown, maxChars: number): value is string {
330
445
  return (
331
446
  typeof value === "string" &&
@@ -365,7 +480,9 @@ function activityCharacterCount(data: Record<string, unknown>): number {
365
480
  return (
366
481
  total +
367
482
  (typeof tool.name === "string" ? tool.name.length : 0) +
368
- (typeof tool.id === "string" ? tool.id.length : 0)
483
+ (typeof tool.id === "string" ? tool.id.length : 0) +
484
+ (typeof tool.input === "string" ? tool.input.length : 0) +
485
+ (typeof tool.result === "string" ? tool.result.length : 0)
369
486
  );
370
487
  }, 0)
371
488
  : 0;
package/src/a2a/index.ts CHANGED
@@ -11,7 +11,10 @@ export {
11
11
  MAX_A2A_ACTIVITY_TOOL_CALLS,
12
12
  MAX_A2A_ACTIVITY_TOTAL_CHARS,
13
13
  MAX_A2A_ACTIVITY_TOOL_ID_CHARS,
14
+ MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS,
14
15
  MAX_A2A_ACTIVITY_TOOL_NAME_CHARS,
16
+ MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS,
17
+ MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS,
15
18
  applyA2AAgentActivityEvent,
16
19
  buildA2AAgentActivityPart,
17
20
  buildA2AAgentActivitySnapshot,
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Durable background agent-chat runs (Netlify background functions).
3
3
  *
4
- * Off by default. When enabled, a long in-app agent-chat turn is dispatched
4
+ * Enabled by default on deployed Netlify apps. When enabled, a long in-app
5
+ * agent-chat turn is dispatched
5
6
  * into a Netlify *background* function (15-min budget) instead of completing
6
7
  * synchronously under the ~40s soft-timeout. The foreground POST claims the
7
8
  * run slot, inserts the run row, fires an HMAC-signed self-dispatch to
@@ -18,18 +19,17 @@
18
19
  * GUARDRAIL: when `isAgentChatDurableBackgroundEnabled()` returns false, the
19
20
  * agent-chat handler must behave byte-for-byte like the current synchronous
20
21
  * path. The gate is true only when ALL of these hold:
21
- * 1. `AGENT_CHAT_DURABLE_BACKGROUND` env is explicitly enabled, or a
22
- * workspace app's agent-chat plugin opts in with `durableBackgroundRuns`
23
- * where the workspace deploy emits a per-app background function by
24
- * default. Single-template Netlify deploys must set the env flag because
25
- * that same flag controls whether `server-agent-background` is emitted.
22
+ * 1. The runtime is a deployed Netlify app and
23
+ * `AGENT_CHAT_DURABLE_BACKGROUND` is not explicitly disabled, or the
24
+ * existing env/app opt-in path is used on another hosted platform. Netlify
25
+ * deploys emit the background function by default; `false`, `0`, `no`, or
26
+ * `off` disables it.
26
27
  * 2. The runtime is hosted/serverless (local dev keeps the inline path so SSE
27
28
  * stays a single live stream and no second function is needed).
28
29
  * 3. `A2A_SECRET` is configured (the HMAC handoff is required to authenticate
29
30
  * the background dispatch; without it the dispatch can't be trusted).
30
31
  *
31
- * Opt-in keeps the blast radius small while the worker path is still being
32
- * proven. And even when enabled, a *dispatch failure degrades to an inline run*:
32
+ * Even when enabled, a *dispatch failure degrades to an inline run*:
33
33
  * if the self-dispatch self-POST can't be delivered (fast connection error or
34
34
  * fast non-2xx), the foreground handler runs the turn synchronously instead of
35
35
  * erroring (see `production-agent.ts` — the inline fallback claims the run row
@@ -120,7 +120,7 @@ function isNetlifyHostedRuntimeForDispatch(): boolean {
120
120
  // exposes AWS runtime variables, so keep the function-name fallback for older
121
121
  // deploys. Without either check a modern Netlify Function silently selects the
122
122
  // portable framework route even though the emitted background function exists.
123
- if (process.env.SITE_ID) return true; // guard:allow-env-credential -- Netlify's read-only public site identifier is a runtime host marker, not a user credential.
123
+ if (process.env.SITE_ID) return true; // guard:allow-env-credential - Netlify's read-only public site identifier is a runtime host marker, not a user credential.
124
124
  // Non-Netlify AWS falls back inline if the /.netlify/functions dispatch
125
125
  // fast-fails.
126
126
  return Boolean(process.env.AWS_LAMBDA_FUNCTION_NAME);
@@ -186,8 +186,9 @@ export function dispatchPathTargetsNetlifyBackgroundFunction(
186
186
  }
187
187
 
188
188
  /**
189
- * Env flag for durable background runs. DEFAULT-OFF (opt-in): unset means
190
- * disabled; an app opts IN with an explicit truthy value (`true`/`1`/`yes`/`on`).
189
+ * Env flag for durable background runs. On Netlify, unset means enabled and an
190
+ * explicit falsy value opts out. On other hosted platforms, apps still opt in
191
+ * with an explicit truthy value (`true`/`1`/`yes`/`on`).
191
192
  */
192
193
  export const AGENT_CHAT_DURABLE_BACKGROUND_ENV =
193
194
  "AGENT_CHAT_DURABLE_BACKGROUND";
@@ -233,6 +234,20 @@ export function isHostedRuntimeForDurableBackground(): boolean {
233
234
  );
234
235
  }
235
236
 
237
+ /**
238
+ * Netlify is the only host with the emitted 15-minute background function.
239
+ * Keep the framework-wide default scoped to deployed Netlify rather than
240
+ * silently enabling it on other hosted runtimes that may not have that worker.
241
+ */
242
+ export function isNetlifyHostedRuntimeForDurableBackground(): boolean {
243
+ if (process.env.NETLIFY_LOCAL === "true") return false;
244
+ if (process.env.NETLIFY === "false") return false;
245
+ return Boolean(
246
+ (process.env.NETLIFY && process.env.NETLIFY !== "false") ||
247
+ process.env.SITE_ID, // guard:allow-env-credential - Netlify's read-only public site identifier is a runtime host marker, not a user credential.
248
+ );
249
+ }
250
+
236
251
  /**
237
252
  * True when THIS process is actually executing inside a Netlify *background*
238
253
  * function (the long, 15-min-budget async function whose deployed name ends in
@@ -353,16 +368,10 @@ export function isDurableBackgroundFlagEnabled(): boolean {
353
368
  // can statically verify it against the allowlisted `AGENT_*` prefix. Keep this
354
369
  // in sync with AGENT_CHAT_DURABLE_BACKGROUND_ENV.
355
370
  //
356
- // DEFAULT-OFF (opt-in): durable background runs are still being hardened. A
357
- // premature fleet-wide default-on caused real-user incidents (Assets/Analytics
358
- // hit "Failed to dispatch" + stalls, 2026-06-24) because the async background
359
- // worker path is not yet proven end-to-end and the deploy-time env opt-out is
360
- // not reliably baked into a given deploy. So an unset/empty/unknown flag means
361
- // OFF; an app opts IN only with an explicit truthy value
362
- // (AGENT_CHAT_DURABLE_BACKGROUND=true). This still composes with the hosted +
363
- // A2A_SECRET gates below. Flip back to default-on only after the 15-min
364
- // background-function worker is verified live in production (see the
365
- // project_durable_bg_prod_verified memory).
371
+ // This parses only the explicit opt-in signal. Netlify's default-on behavior
372
+ // is composed separately in isAgentChatDurableBackgroundEnabled, while other
373
+ // hosted runtimes still require this truthy value. Empty and unknown values
374
+ // remain false so an explicit host opt-in cannot be inferred accidentally.
366
375
  const raw = process.env.AGENT_CHAT_DURABLE_BACKGROUND;
367
376
  if (raw == null) return false;
368
377
  const normalized = raw.trim().toLowerCase();
@@ -387,12 +396,10 @@ export function isDurableBackgroundFlagExplicitlyDisabled(): boolean {
387
396
  }
388
397
 
389
398
  /**
390
- * The single gate. True when the env flag is explicitly enabled, or a workspace
391
- * app opted in and has a per-app background-function target, AND the runtime is
392
- * hosted AND A2A_SECRET is configured. False otherwise and false means the
393
- * current synchronous behavior is used unchanged. Single-template Netlify app
394
- * opt-ins deliberately require the env flag too because that flag controls
395
- * whether the `server-agent-background` function exists in the deploy output.
399
+ * The single gate. On deployed Netlify, durable runs are enabled unless the env
400
+ * flag is explicitly falsy. Other hosted runtimes retain the explicit env/app
401
+ * opt-in path. In every case the runtime must be hosted and have A2A_SECRET.
402
+ * False means the current synchronous behavior is used unchanged.
396
403
  */
397
404
  export function isAgentChatDurableBackgroundEnabled(options?: {
398
405
  appOptIn?: boolean;
@@ -404,12 +411,15 @@ export function isAgentChatDurableBackgroundEnabled(options?: {
404
411
  // prevent.
405
412
  if (options?.appOptIn === false) return false;
406
413
  const envOptIn = isDurableBackgroundFlagEnabled();
414
+ const netlifyDefaultOptIn =
415
+ isNetlifyHostedRuntimeForDurableBackground() &&
416
+ !isDurableBackgroundFlagExplicitlyDisabled();
407
417
  const workspaceAppOptIn =
408
418
  options?.appOptIn === true &&
409
419
  !isDurableBackgroundFlagExplicitlyDisabled() &&
410
420
  resolveWorkspaceBackgroundFunctionUrlPath() !== null;
411
421
  return (
412
- (envOptIn || workspaceAppOptIn) &&
422
+ (envOptIn || netlifyDefaultOptIn || workspaceAppOptIn) &&
413
423
  isHostedRuntimeForDurableBackground() &&
414
424
  hasConfiguredA2ASecret()
415
425
  );
@@ -1,3 +1,4 @@
1
+ import { redactArgsToValue, redactTextToSummary } from "../../audit/redact.js";
1
2
  import type {
2
3
  BackgroundAgentControlInput,
3
4
  BackgroundAgentControlResult,
@@ -233,13 +234,22 @@ function summarizeAgentChatEvent(event: AgentChatEvent): {
233
234
  return {
234
235
  kind: "status",
235
236
  message: `Running ${event.tool}`,
236
- metadata: { type: "tool_start", tool: event.tool, input: event.input },
237
+ metadata: {
238
+ type: "tool_start",
239
+ tool: event.tool,
240
+ input: redactArgsToValue(event.input),
241
+ },
237
242
  };
238
243
  case "tool_done":
239
244
  return {
240
245
  kind: "artifact",
241
246
  message: event.result,
242
- metadata: { type: "tool_done", tool: event.tool },
247
+ metadata: {
248
+ type: "tool_done",
249
+ tool: event.tool,
250
+ input: redactArgsToValue(event.input),
251
+ result: redactTextToSummary(event.result),
252
+ },
243
253
  };
244
254
  case "error":
245
255
  return {
@@ -5673,23 +5673,26 @@ export async function runAgentLoop(opts: {
5673
5673
  result = `${result}\n\n${formatAgentWarningsForToolResult(agentWarnings)}`;
5674
5674
  }
5675
5675
 
5676
- // Auto-refresh the UI after a successful mutating tool call. Any action
5677
- // that isn't explicitly read-only is assumed to mutate. The client's
5678
- // useDbSync listener sees a change event with source:"action" and
5679
- // invalidates ["action"] queries so list-* / get-* refetch. This makes
5680
- // refresh after agent writes reliable without the model needing to
5681
- // remember to call `refresh-screen` itself.
5682
- if (!isError && actionEntry.readOnly !== true) {
5676
+ // Auto-refresh the UI after a successful mutating tool call. Any call
5677
+ // that isn't read-only by its own per-call Plan-mode effect, else the
5678
+ // action's readOnly flag is assumed to mutate. The client's useDbSync
5679
+ // listener sees a change event with source:"action" and invalidates
5680
+ // ["action"] queries so list-* / get-* refetch. This makes refresh after
5681
+ // agent writes reliable without the model needing to remember to call
5682
+ // `refresh-screen` itself.
5683
+ if (!isError) {
5683
5684
  try {
5684
- const { notifyActionChange } =
5685
+ const { actionCallIsReadOnly, notifyActionChange } =
5685
5686
  await import("../server/action-change.js");
5686
- const owner = opts.ownerEmail ?? getRequestUserEmail() ?? undefined;
5687
- const orgId = opts.orgId ?? getRequestOrgId() ?? undefined;
5688
- await notifyActionChange({
5689
- actionName: toolCall.name,
5690
- ...(owner ? { owner } : {}),
5691
- ...(orgId ? { orgId } : {}),
5692
- });
5687
+ if (!actionCallIsReadOnly(actionEntry, toolCall.input, false)) {
5688
+ const owner = opts.ownerEmail ?? getRequestUserEmail() ?? undefined;
5689
+ const orgId = opts.orgId ?? getRequestOrgId() ?? undefined;
5690
+ await notifyActionChange({
5691
+ actionName: toolCall.name,
5692
+ ...(owner ? { owner } : {}),
5693
+ ...(orgId ? { orgId } : {}),
5694
+ });
5695
+ }
5693
5696
  } catch {
5694
5697
  // poll module may be unavailable in non-server contexts — ignore
5695
5698
  }