@agent-native/core 0.131.5 → 0.131.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +70 -0
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +13 -6
  4. package/corpus/core/docs/content/cloneable-saas.mdx +5 -5
  5. package/corpus/core/docs/content/creating-templates.mdx +9 -0
  6. package/corpus/core/docs/content/deployment.mdx +18 -12
  7. package/corpus/core/docs/content/doctor.mdx +8 -1
  8. package/corpus/core/docs/content/drop-in-agent.mdx +1 -1
  9. package/corpus/core/docs/content/extensions.mdx +18 -18
  10. package/corpus/core/docs/content/faq.mdx +4 -4
  11. package/corpus/core/docs/content/key-concepts.mdx +30 -21
  12. package/corpus/core/docs/content/security.mdx +2 -2
  13. package/corpus/core/docs/content/template-brain-developers.mdx +4 -1
  14. package/corpus/core/docs/content/template-mail-developers.mdx +4 -1
  15. package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/a2a/client.ts +449 -66
  18. package/corpus/core/src/a2a/correlation.ts +31 -0
  19. package/corpus/core/src/a2a/invoke.ts +4 -2
  20. package/corpus/core/src/a2a/types.ts +4 -0
  21. package/corpus/core/src/action.ts +3 -0
  22. package/corpus/core/src/agent/action-continuation-guidance.ts +2 -1
  23. package/corpus/core/src/agent/engine/registry.ts +98 -6
  24. package/corpus/core/src/agent/production-agent.ts +151 -18
  25. package/corpus/core/src/agent/run-loop-with-resume.ts +190 -3
  26. package/corpus/core/src/agent/run-manager.ts +117 -41
  27. package/corpus/core/src/agent/thread-debug-history.ts +86 -0
  28. package/corpus/core/src/agent/types.ts +3 -1
  29. package/corpus/core/src/cli/create.ts +11 -1
  30. package/corpus/core/src/cli/templates-meta.ts +2 -2
  31. package/corpus/core/src/client/agent-chat-adapter.ts +91 -13
  32. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +11 -9
  33. package/corpus/core/src/client/sse-event-processor.ts +17 -3
  34. package/corpus/core/src/db/client.ts +57 -17
  35. package/corpus/core/src/guards/no-env-credentials.ts +2 -0
  36. package/corpus/core/src/integrations/adapters/slack.ts +8 -3
  37. package/corpus/core/src/localization/default-messages.ts +44 -0
  38. package/corpus/core/src/observability/traces.ts +78 -5
  39. package/corpus/core/src/scripts/call-agent.ts +294 -11
  40. package/corpus/core/src/scripts/describe-workspace-apps.ts +2 -2
  41. package/corpus/core/src/secrets/crypto.ts +156 -35
  42. package/corpus/core/src/secrets/storage.ts +64 -26
  43. package/corpus/core/src/server/agent-capabilities.ts +1 -1
  44. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +113 -7
  45. package/corpus/core/src/server/agent-chat/context-tools.ts +2 -0
  46. package/corpus/core/src/server/agent-chat/plugin-options.ts +34 -1
  47. package/corpus/core/src/server/agent-chat/prompt-resources.ts +1 -1
  48. package/corpus/core/src/server/agent-chat-plugin.ts +91 -59
  49. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  50. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  51. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  54. package/corpus/core/src/vite/client.ts +1 -0
  55. package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +5 -4
  56. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +6 -4
  57. package/corpus/templates/analytics/AGENTS.md +7 -4
  58. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -3
  59. package/corpus/templates/analytics/changelog/2026-07-30-delegated-analytics-questions-now-return-a-useful-current-me.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-07-30-scheduled-dashboard-emails-complete-every-panel-without-pool.md +6 -0
  61. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +28 -1
  62. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +18 -11
  63. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +4 -4
  64. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -5
  65. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +6 -2
  66. package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +11 -4
  67. package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +49 -31
  68. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -8
  69. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +9 -3
  70. package/corpus/templates/analytics/server/lib/production-serverless-runtime.ts +11 -0
  71. package/corpus/templates/analytics/server/lib/real-data-actions.ts +10 -0
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -2
  73. package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +2 -1
  74. package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +2 -1
  75. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -12
  76. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +54 -6
  77. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +20 -9
  78. package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +33 -1
  79. package/corpus/templates/brain/AGENTS.md +3 -1
  80. package/corpus/templates/brain/README.md +4 -1
  81. package/corpus/templates/brain/actions/_schemas.ts +30 -8
  82. package/corpus/templates/brain/actions/ask-brain.ts +124 -14
  83. package/corpus/templates/brain/actions/create-source.ts +18 -2
  84. package/corpus/templates/brain/actions/enqueue-captures-distillation.ts +8 -123
  85. package/corpus/templates/brain/actions/enqueue-distillation.ts +6 -127
  86. package/corpus/templates/brain/actions/import-capture.ts +17 -5
  87. package/corpus/templates/brain/actions/import-transcript.ts +17 -5
  88. package/corpus/templates/brain/actions/update-source.ts +25 -5
  89. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  90. package/corpus/templates/brain/changelog/2026-07-30-blessed-resources-can-feed-cited-company-answers.md +6 -0
  91. package/corpus/templates/brain/changelog/2026-07-30-the-left-sidebar-no-longer-shows-a-blank-organization-placeh.md +6 -0
  92. package/corpus/templates/brain/server/lib/brain.ts +49 -19
  93. package/corpus/templates/brain/server/lib/distillation-queue.ts +147 -0
  94. package/corpus/templates/brain/server/lib/source-policy.ts +264 -0
  95. package/corpus/templates/brain/server/plugins/agent-chat.ts +3 -2
  96. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +116 -19
  97. package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +51 -0
  98. package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
  99. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +3 -1
  100. package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +6 -0
  101. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +72 -8
  102. package/corpus/templates/content/changelog/2026-07-30-the-slash-command-hint-now-stays-on-only-the-active-empty-li.md +6 -0
  103. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +506 -11
  104. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +148 -17
  105. package/corpus/templates/design/app/components/design/canvas-interactions/design-canvas-interactions.ts +357 -0
  106. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +7 -466
  107. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +15 -311
  108. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +19 -997
  109. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +8 -1216
  110. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +31 -38
  111. package/corpus/templates/design/package.json +0 -1
  112. package/corpus/templates/dispatch/AGENTS.md +8 -0
  113. package/corpus/templates/dispatch/DEVELOPING.md +29 -0
  114. package/corpus/templates/dispatch/actions/view-screen.ts +34 -2
  115. package/corpus/templates/dispatch/app/i18n-data.ts +457 -0
  116. package/corpus/templates/dispatch/changelog/2026-07-30-delegated-dispatch-work-continues-reliably-in-the-background.md +6 -0
  117. package/corpus/templates/dispatch/changelog/2026-07-30-find-failed-runs-across-apps.md +6 -0
  118. package/corpus/templates/dispatch/netlify.toml +3 -0
  119. package/corpus/templates/plan/actions/get-visual-plan.ts +10 -6
  120. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +117 -1
  121. package/corpus/templates/plan/actions/update-visual-plan.ts +56 -14
  122. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
  123. package/corpus/templates/plan/changelog/2026-07-30-plan-agents-recover-with-targeted-writes.md +6 -0
  124. package/corpus/templates/plan/shared/plan-content.ts +35 -0
  125. package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +8 -12
  126. package/corpus/templates/slides/AGENTS.md +4 -4
  127. package/corpus/templates/slides/actions/create-deck.ts +1 -1
  128. package/corpus/templates/slides/actions/duplicate-deck.ts +19 -4
  129. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -10
  130. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +12 -1
  131. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +30 -13
  132. package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +35 -0
  133. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +63 -56
  134. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -76
  135. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1110 -292
  136. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +53 -0
  137. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +235 -18
  138. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +4 -1
  139. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +11 -0
  140. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +129 -0
  141. package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +70 -0
  142. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +519 -65
  143. package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +36 -426
  144. package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +19 -458
  145. package/corpus/templates/slides/app/context/DeckContext.tsx +179 -72
  146. package/corpus/templates/slides/app/global.css +105 -0
  147. package/corpus/templates/slides/app/i18n/en-US.ts +8 -0
  148. package/corpus/templates/slides/app/lib/deck-editor-loading.ts +26 -0
  149. package/corpus/templates/slides/app/lib/mermaid-blocks.ts +27 -0
  150. package/corpus/templates/slides/app/pages/DeckEditor.tsx +71 -38
  151. package/corpus/templates/slides/app/pages/Index.tsx +98 -41
  152. package/corpus/templates/slides/changelog/2026-07-30-deck-loading-no-longer-flashes-an-unavailable-message.md +6 -0
  153. package/corpus/templates/slides/changelog/2026-07-30-duplicating-a-deck-from-the-deck-list-now-opens-the-copy-imm.md +6 -0
  154. package/corpus/templates/slides/changelog/2026-07-30-layout-overflow-warning-is-readable-and-dismissible.md +6 -0
  155. package/corpus/templates/slides/changelog/2026-07-30-new-decks-now-start-with-an-empty-slide-list-when-no-slides-.md +6 -0
  156. package/corpus/templates/slides/changelog/2026-07-30-shared-canvas-annotations.md +6 -0
  157. package/corpus/templates/slides/changelog/2026-07-30-slide-objects-can-be-moved-as-a-group-copied-and-pasted-with.md +6 -0
  158. package/corpus/templates/slides/changelog/2026-07-30-slide-text-editing-and-object-controls-now-match-google-slides.md +6 -0
  159. package/corpus/templates/slides/changelog/2026-07-30-style-panel-now-scrolls-with-speaker-notes-open-shows-the-sl.md +6 -0
  160. package/corpus/templates/slides/changelog/2026-07-30-top-toolbar-controls-now-use-consistent-sizing-with-undo-and.md +6 -0
  161. package/corpus/templates/slides/netlify.toml +3 -1
  162. package/corpus/templates/slides/package.json +0 -1
  163. package/corpus/templates/slides/server/lib/chat-attachments.ts +18 -0
  164. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
  165. package/corpus/templates/slides/vite.config.ts +0 -1
  166. package/corpus/toolkit/CHANGELOG.md +13 -0
  167. package/corpus/toolkit/README.md +19 -0
  168. package/corpus/toolkit/agent-native.eject.json +38 -0
  169. package/corpus/toolkit/package.json +29 -1
  170. package/corpus/toolkit/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
  171. package/corpus/toolkit/src/canvas-annotations/DrawOverlay.tsx +1233 -0
  172. package/corpus/toolkit/src/canvas-annotations/index.ts +15 -0
  173. package/corpus/toolkit/src/canvas-annotations/types.ts +14 -0
  174. package/corpus/toolkit/src/canvas-interactions/canvas-interactions.ts +933 -0
  175. package/corpus/toolkit/src/canvas-interactions/index.ts +67 -0
  176. package/corpus/toolkit/src/design-tweaks/scrub-input-utils.ts +311 -0
  177. package/corpus/toolkit/src/design-tweaks/scrub-input.tsx +491 -0
  178. package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +26 -171
  179. package/corpus/toolkit/src/index.ts +1 -0
  180. package/dist/a2a/client.d.ts +23 -0
  181. package/dist/a2a/client.d.ts.map +1 -1
  182. package/dist/a2a/client.js +309 -44
  183. package/dist/a2a/client.js.map +1 -1
  184. package/dist/a2a/correlation.d.ts +1 -0
  185. package/dist/a2a/correlation.d.ts.map +1 -1
  186. package/dist/a2a/correlation.js +27 -0
  187. package/dist/a2a/correlation.js.map +1 -1
  188. package/dist/a2a/invoke.d.ts.map +1 -1
  189. package/dist/a2a/invoke.js +4 -2
  190. package/dist/a2a/invoke.js.map +1 -1
  191. package/dist/a2a/types.d.ts +4 -0
  192. package/dist/a2a/types.d.ts.map +1 -1
  193. package/dist/a2a/types.js.map +1 -1
  194. package/dist/action.d.ts +3 -0
  195. package/dist/action.d.ts.map +1 -1
  196. package/dist/action.js.map +1 -1
  197. package/dist/agent/action-continuation-guidance.d.ts.map +1 -1
  198. package/dist/agent/action-continuation-guidance.js +2 -1
  199. package/dist/agent/action-continuation-guidance.js.map +1 -1
  200. package/dist/agent/engine/registry.d.ts.map +1 -1
  201. package/dist/agent/engine/registry.js +82 -8
  202. package/dist/agent/engine/registry.js.map +1 -1
  203. package/dist/agent/production-agent.d.ts +40 -1
  204. package/dist/agent/production-agent.d.ts.map +1 -1
  205. package/dist/agent/production-agent.js +113 -15
  206. package/dist/agent/production-agent.js.map +1 -1
  207. package/dist/agent/run-loop-with-resume.d.ts +21 -0
  208. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  209. package/dist/agent/run-loop-with-resume.js +163 -4
  210. package/dist/agent/run-loop-with-resume.js.map +1 -1
  211. package/dist/agent/run-manager.d.ts +16 -9
  212. package/dist/agent/run-manager.d.ts.map +1 -1
  213. package/dist/agent/run-manager.js +93 -42
  214. package/dist/agent/run-manager.js.map +1 -1
  215. package/dist/agent/thread-debug-history.d.ts +10 -0
  216. package/dist/agent/thread-debug-history.d.ts.map +1 -0
  217. package/dist/agent/thread-debug-history.js +68 -0
  218. package/dist/agent/thread-debug-history.js.map +1 -0
  219. package/dist/agent/types.d.ts +3 -1
  220. package/dist/agent/types.d.ts.map +1 -1
  221. package/dist/agent/types.js.map +1 -1
  222. package/dist/cli/create.d.ts.map +1 -1
  223. package/dist/cli/create.js +6 -1
  224. package/dist/cli/create.js.map +1 -1
  225. package/dist/cli/templates-meta.js +2 -2
  226. package/dist/cli/templates-meta.js.map +1 -1
  227. package/dist/client/agent-chat-adapter.d.ts +1 -1
  228. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  229. package/dist/client/agent-chat-adapter.js +76 -13
  230. package/dist/client/agent-chat-adapter.js.map +1 -1
  231. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  232. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  233. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  234. package/dist/client/sse-event-processor.d.ts.map +1 -1
  235. package/dist/client/sse-event-processor.js +12 -3
  236. package/dist/client/sse-event-processor.js.map +1 -1
  237. package/dist/collab/routes.d.ts +1 -1
  238. package/dist/db/client.d.ts.map +1 -1
  239. package/dist/db/client.js +48 -17
  240. package/dist/db/client.js.map +1 -1
  241. package/dist/guards/no-env-credentials.d.ts.map +1 -1
  242. package/dist/guards/no-env-credentials.js +2 -0
  243. package/dist/guards/no-env-credentials.js.map +1 -1
  244. package/dist/integrations/adapters/slack.js +7 -3
  245. package/dist/integrations/adapters/slack.js.map +1 -1
  246. package/dist/localization/default-messages.d.ts +43 -0
  247. package/dist/localization/default-messages.d.ts.map +1 -1
  248. package/dist/localization/default-messages.js +43 -0
  249. package/dist/localization/default-messages.js.map +1 -1
  250. package/dist/observability/traces.d.ts +3 -1
  251. package/dist/observability/traces.d.ts.map +1 -1
  252. package/dist/observability/traces.js +73 -4
  253. package/dist/observability/traces.js.map +1 -1
  254. package/dist/progress/routes.d.ts +1 -1
  255. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  256. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  257. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  258. package/dist/scripts/call-agent.d.ts.map +1 -1
  259. package/dist/scripts/call-agent.js +237 -12
  260. package/dist/scripts/call-agent.js.map +1 -1
  261. package/dist/scripts/describe-workspace-apps.js +2 -2
  262. package/dist/scripts/describe-workspace-apps.js.map +1 -1
  263. package/dist/secrets/crypto.d.ts +36 -21
  264. package/dist/secrets/crypto.d.ts.map +1 -1
  265. package/dist/secrets/crypto.js +134 -33
  266. package/dist/secrets/crypto.js.map +1 -1
  267. package/dist/secrets/storage.d.ts +6 -6
  268. package/dist/secrets/storage.d.ts.map +1 -1
  269. package/dist/secrets/storage.js +43 -26
  270. package/dist/secrets/storage.js.map +1 -1
  271. package/dist/server/agent-capabilities.js +1 -1
  272. package/dist/server/agent-capabilities.js.map +1 -1
  273. package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -3
  274. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  275. package/dist/server/agent-chat/action-filters-a2a.js +94 -3
  276. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  277. package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
  278. package/dist/server/agent-chat/context-tools.js +2 -0
  279. package/dist/server/agent-chat/context-tools.js.map +1 -1
  280. package/dist/server/agent-chat/plugin-options.d.ts +23 -0
  281. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  282. package/dist/server/agent-chat/plugin-options.js +4 -1
  283. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  284. package/dist/server/agent-chat/prompt-resources.js +1 -1
  285. package/dist/server/agent-chat/prompt-resources.js.map +1 -1
  286. package/dist/server/agent-chat-plugin.d.ts +2 -1
  287. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  288. package/dist/server/agent-chat-plugin.js +79 -56
  289. package/dist/server/agent-chat-plugin.js.map +1 -1
  290. package/dist/server/realtime-token.d.ts +1 -1
  291. package/dist/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  292. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  293. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  294. package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  295. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  296. package/dist/vite/client.d.ts.map +1 -1
  297. package/dist/vite/client.js +1 -0
  298. package/dist/vite/client.js.map +1 -1
  299. package/docs/content/agent-native-toolkit.mdx +13 -6
  300. package/docs/content/cloneable-saas.mdx +5 -5
  301. package/docs/content/creating-templates.mdx +9 -0
  302. package/docs/content/deployment.mdx +18 -12
  303. package/docs/content/doctor.mdx +8 -1
  304. package/docs/content/drop-in-agent.mdx +1 -1
  305. package/docs/content/extensions.mdx +18 -18
  306. package/docs/content/faq.mdx +4 -4
  307. package/docs/content/key-concepts.mdx +30 -21
  308. package/docs/content/security.mdx +2 -2
  309. package/docs/content/template-brain-developers.mdx +4 -1
  310. package/docs/content/template-mail-developers.mdx +4 -1
  311. package/docs/content/what-is-agent-native.mdx +6 -6
  312. package/package.json +3 -3
  313. package/src/a2a/client.ts +449 -66
  314. package/src/a2a/correlation.ts +31 -0
  315. package/src/a2a/invoke.ts +4 -2
  316. package/src/a2a/types.ts +4 -0
  317. package/src/action.ts +3 -0
  318. package/src/agent/action-continuation-guidance.ts +2 -1
  319. package/src/agent/engine/registry.ts +98 -6
  320. package/src/agent/production-agent.ts +151 -18
  321. package/src/agent/run-loop-with-resume.ts +190 -3
  322. package/src/agent/run-manager.ts +117 -41
  323. package/src/agent/thread-debug-history.ts +86 -0
  324. package/src/agent/types.ts +3 -1
  325. package/src/cli/create.ts +11 -1
  326. package/src/cli/templates-meta.ts +2 -2
  327. package/src/client/agent-chat-adapter.ts +91 -13
  328. package/src/client/resources/McpIntegrationDialog.tsx +11 -9
  329. package/src/client/sse-event-processor.ts +17 -3
  330. package/src/db/client.ts +57 -17
  331. package/src/guards/no-env-credentials.ts +2 -0
  332. package/src/integrations/adapters/slack.ts +8 -3
  333. package/src/localization/default-messages.ts +44 -0
  334. package/src/observability/traces.ts +78 -5
  335. package/src/scripts/call-agent.ts +294 -11
  336. package/src/scripts/describe-workspace-apps.ts +2 -2
  337. package/src/secrets/crypto.ts +156 -35
  338. package/src/secrets/storage.ts +64 -26
  339. package/src/server/agent-capabilities.ts +1 -1
  340. package/src/server/agent-chat/action-filters-a2a.ts +113 -7
  341. package/src/server/agent-chat/context-tools.ts +2 -0
  342. package/src/server/agent-chat/plugin-options.ts +34 -1
  343. package/src/server/agent-chat/prompt-resources.ts +1 -1
  344. package/src/server/agent-chat-plugin.ts +91 -59
  345. package/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  346. package/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  347. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  348. package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  349. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  350. package/src/vite/client.ts +1 -0
@@ -1,6 +1,7 @@
1
1
  import type { A2ACorrelationMetadata } from "./types.js";
2
2
 
3
3
  export const MAX_A2A_CORRELATION_VALUE_CHARS = 200;
4
+ export const MAX_A2A_DELEGATION_HOPS = 3;
4
5
 
5
6
  const APP_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
6
7
  const CORRELATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
@@ -40,11 +41,41 @@ export function sanitizeA2ACorrelationMetadata(
40
41
  const parentRunId = sanitizeA2ACorrelationId(metadata.parentRunId);
41
42
  const parentTurnId = sanitizeA2ACorrelationId(metadata.parentTurnId);
42
43
  const invocationId = sanitizeA2ACorrelationId(metadata.invocationId);
44
+ const providedDelegationDepth =
45
+ typeof metadata.delegationDepth === "number" &&
46
+ Number.isInteger(metadata.delegationDepth) &&
47
+ metadata.delegationDepth >= 0 &&
48
+ metadata.delegationDepth <= MAX_A2A_DELEGATION_HOPS
49
+ ? metadata.delegationDepth
50
+ : undefined;
51
+ const visitedApps = Array.isArray(metadata.visitedApps)
52
+ ? [
53
+ ...new Set(
54
+ metadata.visitedApps
55
+ .map((value) => boundedIdentifier(value, APP_ID_PATTERN))
56
+ .filter((value): value is string => !!value),
57
+ ),
58
+ ].slice(0, MAX_A2A_DELEGATION_HOPS + 1)
59
+ : [];
60
+ const pathDepth = Math.min(MAX_A2A_DELEGATION_HOPS, visitedApps.length);
61
+ // Damaged lineage must never reset a nested call to depth zero. Derive at
62
+ // least the valid path length, and fail closed at the hop limit when a peer
63
+ // supplied an explicit but invalid depth.
64
+ const delegationDepth =
65
+ providedDelegationDepth !== undefined
66
+ ? Math.max(providedDelegationDepth, pathDepth)
67
+ : metadata.delegationDepth !== undefined
68
+ ? MAX_A2A_DELEGATION_HOPS
69
+ : visitedApps.length > 0
70
+ ? pathDepth
71
+ : undefined;
43
72
  return {
44
73
  ...(callerApp ? { callerApp } : {}),
45
74
  ...(callerThreadId ? { callerThreadId } : {}),
46
75
  ...(parentRunId ? { parentRunId } : {}),
47
76
  ...(parentTurnId ? { parentTurnId } : {}),
48
77
  ...(invocationId ? { invocationId } : {}),
78
+ ...(delegationDepth !== undefined ? { delegationDepth } : {}),
79
+ ...(visitedApps.length > 0 ? { visitedApps } : {}),
49
80
  };
50
81
  }
package/src/a2a/invoke.ts CHANGED
@@ -263,9 +263,11 @@ export function buildAgentInvocationPrompt(
263
263
  ): string {
264
264
  return (
265
265
  `${prompt}\n\n` +
266
- `[Note: this request comes from another app via A2A. The caller cannot see your local UI, resource list, or navigation - only the literal text you put in your reply. ` +
266
+ `<a2a-caller-hint>\n` +
267
+ `This request comes from another app via A2A. The caller cannot see your local UI, resource list, or navigation - only the literal text you put in your reply. ` +
267
268
  `If you create or reference a deck/document/design/dashboard/resource, include its FULLY-QUALIFIED URL (e.g. ${agentUrl.replace(/\/$/, "")}/<path>/<id>) in your reply, not a relative path. ` +
268
- `Use only artifact IDs and URL paths returned by successful actions - never invent slugs, IDs, or hosts.]`
269
+ `Use only artifact IDs and URL paths returned by successful actions - never invent slugs, IDs, or hosts.\n` +
270
+ `</a2a-caller-hint>`
269
271
  );
270
272
  }
271
273
 
package/src/a2a/types.ts CHANGED
@@ -170,6 +170,10 @@ export interface A2ACorrelationMetadata {
170
170
  parentRunId?: string;
171
171
  parentTurnId?: string;
172
172
  invocationId?: string;
173
+ /** Number of cross-app edges already traversed by this logical request. */
174
+ delegationDepth?: number;
175
+ /** Bounded app ids already visited, used only for cycle prevention. */
176
+ visitedApps?: string[];
173
177
  }
174
178
 
175
179
  // --- Framework config ---
package/src/action.ts CHANGED
@@ -81,6 +81,9 @@ export interface ActionRunContext {
81
81
  networkProtocol?: "a2a" | "mcp" | "provider-api";
82
82
  networkId?: string;
83
83
  networkPeer?: string;
84
+ /** Bounded cross-app lineage used to prevent recursive delegation cycles. */
85
+ delegationDepth?: number;
86
+ visitedApps?: string[];
84
87
  /**
85
88
  * Attachments submitted with the current agent turn (pasted text blocks,
86
89
  * uploaded files, images), exactly as the server received them — with full,
@@ -21,8 +21,9 @@ export function incrementalActionGuidance(tool: string): string | undefined {
21
21
  case "create-prototype-plan":
22
22
  return "create the plan with its core sections or first screen, then expand it with `update-visual-plan`/`patch-visual-plan-source` follow-up edits";
23
23
  case "update-visual-plan":
24
+ return "apply one or a few small `update-visual-plan` `contentPatches` such as `append-block`, `update-rich-text`, or a text patch; do not resend the full plan or switch to a source-control workflow";
24
25
  case "patch-visual-plan-source":
25
- return "apply smaller, targeted `patch-visual-plan-source` edits rather than rewriting the whole plan in one call";
26
+ return "apply small targeted `patch-visual-plan-source` MDX AST edits; do not use `replace-file`, and do not use this source action for a live plan edit";
26
27
  case "update-dashboard":
27
28
  return "save a small dashboard first, then add panels one at a time with `update-dashboard` incremental `ops` edits instead of authoring the whole config in one call";
28
29
  default:
@@ -600,12 +600,92 @@ async function resolveUsableProviderSecret(
600
600
  return authFailure ? null : value;
601
601
  }
602
602
 
603
+ /**
604
+ * Return true only when the supplied key can be positively identified as a
605
+ * usable credential for a different registered provider.
606
+ *
607
+ * `ResolveEngineConfig.apiKey` is public and may be an opaque caller-provided
608
+ * key, so automatic selection must not silently replace it merely because a
609
+ * stored credential also exists. Delegated/internal callers, however,
610
+ * historically pass the current "active" provider key before the registry
611
+ * selects an app-default engine. Exact in-memory comparison lets us correct
612
+ * that proven mismatch without guessing from provider-specific key prefixes
613
+ * or exposing either value.
614
+ */
615
+ async function apiKeyBelongsToDifferentProvider(
616
+ apiKey: string,
617
+ selectedEntry: AgentEngineEntry,
618
+ ): Promise<boolean> {
619
+ const selectedEnvVars = new Set(selectedEntry.requiredEnvVars);
620
+ const checkedEnvVars = new Set<string>();
621
+ for (const entry of _registry.values()) {
622
+ if (entry === selectedEntry || entry.name === "builder") continue;
623
+ for (const key of entry.requiredEnvVars) {
624
+ if (selectedEnvVars.has(key) || checkedEnvVars.has(key)) continue;
625
+ checkedEnvVars.add(key);
626
+ try {
627
+ if ((await resolveUsableProviderSecret(key)) === apiKey) return true;
628
+ } catch {
629
+ // An unrelated provider store failure is not proof that the explicit
630
+ // key belongs elsewhere. Preserve the caller's key and let the
631
+ // selected provider's own preflight report any relevant failure.
632
+ }
633
+ }
634
+ }
635
+ return false;
636
+ }
637
+
603
638
  async function engineCreateConfigForEntry(
604
639
  entry: AgentEngineEntry,
605
640
  apiKey: string | undefined,
606
641
  extra?: Record<string, unknown>,
642
+ preferResolvedCredential = false,
607
643
  ): Promise<Record<string, unknown>> {
608
644
  const safeExtra = { ...(extra ?? {}) };
645
+ let matchingApiKey = apiKey;
646
+ // Automatic engine selection must also select that engine's credential.
647
+ // Callers historically passed one untagged "active" key before the registry
648
+ // chose an engine, which could hand an Anthropic key to an app-default
649
+ // OpenAI engine (or vice versa). Explicit engineOption branches retain their
650
+ // paired key. Automatic branches replace only a key proven to belong to a
651
+ // different configured provider; opaque caller-supplied keys keep the public
652
+ // `ResolveEngineConfig.apiKey` contract.
653
+ if (
654
+ preferResolvedCredential &&
655
+ entry.name !== "builder" &&
656
+ entry.requiredEnvVars.length > 0
657
+ ) {
658
+ let resolvedMatchingCredential: string | undefined;
659
+ let matchingCredentialUsesDeployFallback = false;
660
+ for (const key of entry.requiredEnvVars) {
661
+ const resolved = (await resolveUsableProviderSecret(key)) ?? undefined;
662
+ if (!resolved) continue;
663
+ resolvedMatchingCredential = resolved;
664
+ matchingCredentialUsesDeployFallback =
665
+ canUseDeployCredentialFallbackForRequest(key) &&
666
+ readDeployCredentialEnv(key) === resolved;
667
+ break;
668
+ }
669
+
670
+ const suppliedKeyMatchesSelected =
671
+ matchingApiKey !== undefined &&
672
+ matchingApiKey === resolvedMatchingCredential;
673
+ const suppliedKeyBelongsElsewhere =
674
+ matchingApiKey !== undefined &&
675
+ !suppliedKeyMatchesSelected &&
676
+ (await apiKeyBelongsToDifferentProvider(matchingApiKey, entry));
677
+
678
+ if (matchingApiKey === undefined || suppliedKeyBelongsElsewhere) {
679
+ // Keep deploy-only credentials implicit so provider SDKs retain their
680
+ // established env-fallback behavior. Scoped credentials must be passed
681
+ // explicitly. A proven different-provider key is replaced or cleared;
682
+ // an opaque caller-supplied key is preserved by the branch above.
683
+ matchingApiKey =
684
+ resolvedMatchingCredential && !matchingCredentialUsesDeployFallback
685
+ ? resolvedMatchingCredential
686
+ : undefined;
687
+ }
688
+ }
609
689
  if (entry.name === "ai-sdk:openai") {
610
690
  if (typeof safeExtra.baseURL === "string" && safeExtra.baseUrl == null) {
611
691
  safeExtra.baseUrl = normalizeOpenAiBaseUrl(safeExtra.baseURL);
@@ -615,7 +695,7 @@ async function engineCreateConfigForEntry(
615
695
  if (baseUrl) safeExtra.baseUrl = baseUrl;
616
696
  }
617
697
  }
618
- return engineCreateConfig(entry, apiKey, safeExtra);
698
+ return engineCreateConfig(entry, matchingApiKey, safeExtra);
619
699
  }
620
700
 
621
701
  /**
@@ -816,7 +896,9 @@ export async function resolveEngine(
816
896
  const entry = _registry.get(envEngine);
817
897
  if (entry) {
818
898
  assertAgentEnginePackageInstalled(entry);
819
- return entry.create(await engineCreateConfigForEntry(entry, apiKey));
899
+ return entry.create(
900
+ await engineCreateConfigForEntry(entry, apiKey, undefined, true),
901
+ );
820
902
  }
821
903
  }
822
904
 
@@ -824,7 +906,9 @@ export async function resolveEngine(
824
906
  if (appDefault?.engine) {
825
907
  const entry = _registry.get(appDefault.engine);
826
908
  if (entry && (await isStoredEngineUsableForRequest(appDefault, entry))) {
827
- return entry.create(await engineCreateConfigForEntry(entry, apiKey));
909
+ return entry.create(
910
+ await engineCreateConfigForEntry(entry, apiKey, undefined, true),
911
+ );
828
912
  }
829
913
  }
830
914
 
@@ -857,6 +941,7 @@ export async function resolveEngine(
857
941
  stripInlineApiKeyConfig(
858
942
  storedConfig as Record<string, unknown> | undefined,
859
943
  ),
944
+ true,
860
945
  ),
861
946
  );
862
947
  }
@@ -864,7 +949,12 @@ export async function resolveEngine(
864
949
 
865
950
  if (detectedFromUser) {
866
951
  return detectedFromUser.create(
867
- await engineCreateConfigForEntry(detectedFromUser, apiKey),
952
+ await engineCreateConfigForEntry(
953
+ detectedFromUser,
954
+ apiKey,
955
+ undefined,
956
+ true,
957
+ ),
868
958
  );
869
959
  }
870
960
 
@@ -873,7 +963,9 @@ export async function resolveEngine(
873
963
  // auth-failure markers so a rejected deploy key cannot permanently win.
874
964
  const detected = await detectEngineFromEnvForRequest();
875
965
  if (detected) {
876
- return detected.create(await engineCreateConfigForEntry(detected, apiKey));
966
+ return detected.create(
967
+ await engineCreateConfigForEntry(detected, apiKey, undefined, true),
968
+ );
877
969
  }
878
970
 
879
971
  // 9. Default: anthropic
@@ -884,7 +976,7 @@ export async function resolveEngine(
884
976
  );
885
977
  }
886
978
  return anthropicEntry.create(
887
- await engineCreateConfigForEntry(anthropicEntry, apiKey),
979
+ await engineCreateConfigForEntry(anthropicEntry, apiKey, undefined, true),
888
980
  );
889
981
  }
890
982
 
@@ -1068,7 +1068,12 @@ export interface ProductionAgentOptions {
1068
1068
  * `maxResultChars` takes precedence; this sets the fallback for actions
1069
1069
  * that don't declare their own limits.
1070
1070
  */
1071
- toolLimits?: { timeoutMs?: number; maxResultChars?: number };
1071
+ toolLimits?: {
1072
+ timeoutMs?: number;
1073
+ maxResultChars?: number;
1074
+ /** Absolute cap applied after action-level overrides. */
1075
+ hardMaxResultChars?: number;
1076
+ };
1072
1077
  }
1073
1078
 
1074
1079
  export async function resolveAgentOwnerEmail(
@@ -1307,11 +1312,7 @@ export function isRetryableError(err: unknown): boolean {
1307
1312
  msg.includes("gateway error") ||
1308
1313
  msg.includes("socket hang up") ||
1309
1314
  msg.includes("connection reset") ||
1310
- // Anthropic SDK APIConnectionError default message is exactly
1311
- // "Connection error." — Builder gateway often forwards it as a stop
1312
- // event with no structured code. Without this match the run fails in
1313
- // ~3s and the client recovery loop storms POSTs.
1314
- msg.includes("connection error") ||
1315
+ hasProviderConnectionErrorMessage(msg) ||
1315
1316
  msg.includes("too many requests") ||
1316
1317
  msg.includes("timeout") ||
1317
1318
  msg.includes("gateway timeout") ||
@@ -1320,6 +1321,17 @@ export function isRetryableError(err: unknown): boolean {
1320
1321
  );
1321
1322
  }
1322
1323
 
1324
+ function hasProviderConnectionErrorMessage(message: string): boolean {
1325
+ const normalized = message.toLowerCase();
1326
+ // Anthropic's APIConnectionError uses "Connection error."; AI SDK wraps
1327
+ // OpenAI TLS failures as "Cannot connect to API". Both can cross a worker
1328
+ // boundary without their structured EngineError metadata.
1329
+ return (
1330
+ normalized.includes("connection error") ||
1331
+ normalized.includes("cannot connect to api")
1332
+ );
1333
+ }
1334
+
1323
1335
  // ---------------------------------------------------------------------------
1324
1336
  // Context-window overflow recovery
1325
1337
  // ---------------------------------------------------------------------------
@@ -2064,6 +2076,18 @@ export interface AgentLoopUsage {
2064
2076
  firstEngineEventAtMs?: number;
2065
2077
  }
2066
2078
 
2079
+ /** Machine-readable terminal state for one agent-loop attempt. */
2080
+ export type AgentLoopOutcome =
2081
+ | { state: "completed" }
2082
+ | { state: "input_required"; code: string; message: string }
2083
+ | {
2084
+ state: "failed";
2085
+ code: string;
2086
+ retryable: boolean;
2087
+ message: string;
2088
+ }
2089
+ | { state: "canceled"; message?: string };
2090
+
2067
2091
  export interface AgentLoopToolCallSummary {
2068
2092
  name: string;
2069
2093
  input: unknown;
@@ -2152,7 +2176,7 @@ export type AgentLoopContinuationReason =
2152
2176
 
2153
2177
  export function appendAgentLoopContinuation(
2154
2178
  messages: EngineMessage[],
2155
- reason: AgentLoopContinuationReason,
2179
+ reason: AgentLoopContinuationReason | "rate_limited",
2156
2180
  options: { actionPreparationTool?: string } = {},
2157
2181
  ) {
2158
2182
  const note =
@@ -2164,11 +2188,13 @@ export function appendAgentLoopContinuation(
2164
2188
  ? "The previous stream ended before the agent sent a final completion signal."
2165
2189
  : reason === "gateway_timeout"
2166
2190
  ? "The previous LLM call hit an upstream gateway timeout before the response finished streaming."
2167
- : reason === "network_interrupted"
2168
- ? "The previous LLM call was cut off by a transport-level interruption (socket dropped, connection reset, or stream closed unexpectedly)."
2169
- : reason === "no_progress"
2170
- ? "The previous run stopped producing progress events while the connection stayed open."
2171
- : "The previous run reached an internal execution budget.";
2191
+ : reason === "rate_limited"
2192
+ ? "The previous LLM call was temporarily rate limited after exhausting its short provider retry budget."
2193
+ : reason === "network_interrupted"
2194
+ ? "The previous LLM call was cut off by a transport-level interruption (socket dropped, connection reset, or stream closed unexpectedly)."
2195
+ : reason === "no_progress"
2196
+ ? "The previous run stopped producing progress events while the connection stayed open."
2197
+ : "The previous run reached an internal execution budget.";
2172
2198
  const actionInputNote = options.actionPreparationTool
2173
2199
  ? actionPreparationContinuationNote(options.actionPreparationTool)
2174
2200
  : "";
@@ -2238,7 +2264,7 @@ export function isResumableEngineError(err: unknown): boolean {
2238
2264
  text.includes("econnaborted") ||
2239
2265
  text.includes("fetch failed") ||
2240
2266
  text.includes("network error") ||
2241
- text.includes("connection error") ||
2267
+ hasProviderConnectionErrorMessage(text) ||
2242
2268
  text.includes("connection reset") ||
2243
2269
  text.includes("connection closed") ||
2244
2270
  text.includes("stream closed") ||
@@ -2251,6 +2277,34 @@ export function isResumableEngineError(err: unknown): boolean {
2251
2277
  );
2252
2278
  }
2253
2279
 
2280
+ /**
2281
+ * True only for transient provider throttling that may recover after one
2282
+ * cooled-down background continuation. This is deliberately narrower than
2283
+ * `isRetryableError`: daily/account caps are terminal, and generic retryable
2284
+ * transport errors keep using the normal resumable-error path.
2285
+ *
2286
+ * The engine has already spent its short in-call retry budget before this
2287
+ * helper is consulted. Callers must add their own small continuation cap so a
2288
+ * sustained provider limit cannot turn into an unbounded request storm.
2289
+ */
2290
+ export function isTransientProviderRateLimitError(err: unknown): boolean {
2291
+ if (!(err instanceof EngineError)) return false;
2292
+ const code = (err.errorCode ?? "").toLowerCase();
2293
+ const text = errorSearchText(err);
2294
+
2295
+ if (
2296
+ code === "rate_limit_exceeded" ||
2297
+ text.includes("daily gateway request cap")
2298
+ ) {
2299
+ return false;
2300
+ }
2301
+ if (err.statusCode === 429 || err.statusCode === 529) {
2302
+ return true;
2303
+ }
2304
+ if (code === "http_429" || code === "http_529") return true;
2305
+ return false;
2306
+ }
2307
+
2254
2308
  /**
2255
2309
  * Map a resumable error to the most descriptive continuation reason. Used
2256
2310
  * when surfacing the resume to the agent and to clients via the
@@ -2825,6 +2879,9 @@ export interface ExecuteAgentToolCallOptions {
2825
2879
  networkProtocol?: "a2a" | "mcp" | "provider-api";
2826
2880
  networkId?: string;
2827
2881
  networkPeer?: string;
2882
+ /** Bounded cross-app lineage used to prevent recursive delegation cycles. */
2883
+ delegationDepth?: number;
2884
+ visitedApps?: string[];
2828
2885
  threadId?: string;
2829
2886
  turnId?: string;
2830
2887
  approvedToolCalls?: string[];
@@ -3735,6 +3792,8 @@ export async function runAgentLoop(opts: {
3735
3792
  signal: AbortSignal;
3736
3793
  /** Observe usage as each model response reports it, including aborted loops. */
3737
3794
  onUsage?: (usage: AgentLoopUsage) => void;
3795
+ /** Observe the attempt's typed terminal state separately from chat events. */
3796
+ onOutcome?: (outcome: AgentLoopOutcome) => void;
3738
3797
  ownerEmail?: string | null;
3739
3798
  orgId?: string | null;
3740
3799
  /** Action invocation attribution. Defaults to the normal agent tool loop. */
@@ -3753,6 +3812,9 @@ export async function runAgentLoop(opts: {
3753
3812
  networkProtocol?: "a2a" | "mcp" | "provider-api";
3754
3813
  networkId?: string;
3755
3814
  networkPeer?: string;
3815
+ /** Bounded cross-app lineage used to prevent recursive delegation cycles. */
3816
+ delegationDepth?: number;
3817
+ visitedApps?: string[];
3756
3818
  /**
3757
3819
  * Attachments submitted with this turn (pasted text, files, images), passed
3758
3820
  * through to each tool's `ActionRunContext.attachments` so actions can
@@ -3792,7 +3854,12 @@ export async function runAgentLoop(opts: {
3792
3854
  * App-level default limits applied to every tool call unless the individual
3793
3855
  * ActionEntry overrides them with its own timeoutMs / maxResultChars.
3794
3856
  */
3795
- toolLimits?: { timeoutMs?: number; maxResultChars?: number };
3857
+ toolLimits?: {
3858
+ timeoutMs?: number;
3859
+ maxResultChars?: number;
3860
+ /** Absolute cap applied after action-level overrides. */
3861
+ hardMaxResultChars?: number;
3862
+ };
3796
3863
  /**
3797
3864
  * Stable approval keys granted by a human for actions declared
3798
3865
  * `needsApproval`. A call whose key is present here runs even though the
@@ -3821,6 +3888,16 @@ export async function runAgentLoop(opts: {
3821
3888
  send,
3822
3889
  signal,
3823
3890
  } = opts;
3891
+ let outcomeReported = false;
3892
+ const reportOutcome = (outcome: AgentLoopOutcome) => {
3893
+ if (outcomeReported) return;
3894
+ outcomeReported = true;
3895
+ try {
3896
+ opts.onOutcome?.(outcome);
3897
+ } catch {
3898
+ // Outcome observers are telemetry/protocol adapters and cannot alter the run.
3899
+ }
3900
+ };
3824
3901
  const budgetStartedAt = opts.budgetStartedAt ?? Date.now();
3825
3902
  const finalResponseGuardRequestText =
3826
3903
  opts.finalResponseGuardRequestText ??
@@ -3951,6 +4028,7 @@ export async function runAgentLoop(opts: {
3951
4028
  // `loop_limit` and `done` share one terminal-event slot in run-manager, so a
3952
4029
  // trailing `done` would overwrite the boundary the continuation logic reads.
3953
4030
  let endedAtLoopLimit = false;
4031
+ let terminalActionStop: { message: string; errorCode?: string } | null = null;
3954
4032
  // Overridden (raised tokens, lowered effort) only after an empty-final-
3955
4033
  // response retry below — kept separate from `opts.maxOutputTokens`/
3956
4034
  // `opts.reasoningEffort` so the very first attempt is unaffected and later
@@ -5044,10 +5122,17 @@ export async function runAgentLoop(opts: {
5044
5122
  runToolTimeoutCeilingMs > 0
5045
5123
  ? Math.min(requestedToolTimeoutMs, runToolTimeoutCeilingMs)
5046
5124
  : requestedToolTimeoutMs;
5047
- const toolMaxResultChars =
5125
+ const configuredToolMaxResultChars =
5048
5126
  actionEntry.maxResultChars ??
5049
5127
  opts.toolLimits?.maxResultChars ??
5050
5128
  DEFAULT_TOOL_RESULT_CHARS;
5129
+ const toolMaxResultChars =
5130
+ typeof opts.toolLimits?.hardMaxResultChars === "number"
5131
+ ? Math.min(
5132
+ configuredToolMaxResultChars,
5133
+ opts.toolLimits.hardMaxResultChars,
5134
+ )
5135
+ : configuredToolMaxResultChars;
5051
5136
 
5052
5137
  // TOOL-CALL JOURNAL HARD-BLOCK (resume safety, tool-layer enforcement).
5053
5138
  // The prompt-level resume journal already TELLS a resuming model not to
@@ -5406,6 +5491,8 @@ export async function runAgentLoop(opts: {
5406
5491
  networkProtocol: opts.networkProtocol,
5407
5492
  networkId: opts.networkId,
5408
5493
  networkPeer: opts.networkPeer,
5494
+ delegationDepth: opts.delegationDepth,
5495
+ visitedApps: opts.visitedApps,
5409
5496
  attachments: opts.attachments,
5410
5497
  signal,
5411
5498
  // Audit attribution: the action name + the agent thread/turn that
@@ -5702,6 +5789,7 @@ export async function runAgentLoop(opts: {
5702
5789
  message: string;
5703
5790
  errorCode?: string;
5704
5791
  };
5792
+ terminalActionStop = stop;
5705
5793
  send({ type: "text", text: stop.message });
5706
5794
  break;
5707
5795
  }
@@ -5712,6 +5800,9 @@ export async function runAgentLoop(opts: {
5712
5800
  // ended on a guardrail, not a clean turn. The result hook still fires below
5713
5801
  // so processors can observe the (halted) final text.
5714
5802
  if (tripwire) {
5803
+ // TypeScript cannot see assignments performed by the processor callback
5804
+ // inside the loop, so preserve the runtime narrowing explicitly here.
5805
+ const terminalTripwire = tripwire as TripWire;
5715
5806
  if (processorChain) {
5716
5807
  try {
5717
5808
  await processorChain.runResult(
@@ -5724,10 +5815,24 @@ export async function runAgentLoop(opts: {
5724
5815
  // A result-hook abort is a no-op: the run is already halting.
5725
5816
  }
5726
5817
  }
5818
+ reportOutcome({
5819
+ state: "failed",
5820
+ code:
5821
+ terminalTripwire.processor === "run-input-token-budget"
5822
+ ? "budget_exhausted"
5823
+ : terminalTripwire.processor
5824
+ ? `guardrail:${terminalTripwire.processor}`
5825
+ : "guardrail",
5826
+ // Re-running the same request with the same deterministic guardrail
5827
+ // would reproduce the stop. A caller can issue a smaller follow-up, but
5828
+ // must not automatically replay this turn.
5829
+ retryable: false,
5830
+ message: terminalTripwire.message,
5831
+ });
5727
5832
  return usage;
5728
5833
  }
5729
5834
 
5730
- if (!signal.aborted && !endedAtLoopLimit) {
5835
+ if (!signal.aborted && !endedAtLoopLimit && !terminalActionStop) {
5731
5836
  // In-loop processor seam (result hook). Fires once at clean run end with the
5732
5837
  // final assistant text so processors (e.g. a proof-of-done gate) can record
5733
5838
  // a verdict. A result-hook abort cannot un-finish a completed run, so a
@@ -5772,6 +5877,32 @@ export async function runAgentLoop(opts: {
5772
5877
  }
5773
5878
  }
5774
5879
  }
5880
+
5881
+ if (signal.aborted) {
5882
+ reportOutcome({ state: "canceled", message: "Agent run was aborted." });
5883
+ } else if (endedAtLoopLimit) {
5884
+ reportOutcome({
5885
+ state: "failed",
5886
+ code: "loop_limit",
5887
+ retryable: false,
5888
+ message: `Agent stopped after ${maxIterations} iterations.`,
5889
+ });
5890
+ } else if (terminalActionStop?.errorCode === "needs-approval") {
5891
+ reportOutcome({
5892
+ state: "input_required",
5893
+ code: "needs_approval",
5894
+ message: terminalActionStop.message,
5895
+ });
5896
+ } else if (terminalActionStop) {
5897
+ reportOutcome({
5898
+ state: "failed",
5899
+ code: terminalActionStop.errorCode ?? "tool_failed",
5900
+ retryable: false,
5901
+ message: terminalActionStop.message,
5902
+ });
5903
+ } else {
5904
+ reportOutcome({ state: "completed" });
5905
+ }
5775
5906
  return usage;
5776
5907
  }
5777
5908
 
@@ -5806,7 +5937,7 @@ function isRecoverableContinuationError(event: {
5806
5937
  code === "http_529" ||
5807
5938
  code === "run_timeout" ||
5808
5939
  message.includes("timeout") ||
5809
- message.includes("connection error") ||
5940
+ hasProviderConnectionErrorMessage(message) ||
5810
5941
  message.includes("temporarily unavailable")
5811
5942
  );
5812
5943
  }
@@ -7103,7 +7234,9 @@ function progressStepFromAgentChatEvent(event: AgentChatEvent): string | null {
7103
7234
  ? `Calling ${event.agent}.`
7104
7235
  : event.status === "done"
7105
7236
  ? `Finished ${event.agent}.`
7106
- : `${event.agent} failed.`;
7237
+ : event.status === "pending"
7238
+ ? `${event.agent} is still working.`
7239
+ : `${event.agent} failed.`;
7107
7240
  case "agent_task":
7108
7241
  return event.status === "running"
7109
7242
  ? "Started background task."