@agent-native/core 0.132.2 → 0.133.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 (436) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +161 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/correlation.ts +11 -1
  5. package/corpus/core/src/a2a/types.ts +11 -3
  6. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +33 -46
  7. package/corpus/core/src/agent/engine/builder-engine.ts +22 -10
  8. package/corpus/core/src/agent/engine/error-detail.ts +184 -3
  9. package/corpus/core/src/agent/engine/failure-taxonomy.ts +123 -0
  10. package/corpus/core/src/agent/engine/index.ts +8 -0
  11. package/corpus/core/src/agent/engine/registry.ts +92 -8
  12. package/corpus/core/src/agent/engine/translate-ai-sdk.ts +17 -2
  13. package/corpus/core/src/agent/production-agent.ts +13 -33
  14. package/corpus/core/src/agent/run-loop-with-resume.ts +1 -1
  15. package/corpus/core/src/agent/run-manager.ts +43 -14
  16. package/corpus/core/src/agent/run-store.ts +30 -3
  17. package/corpus/core/src/agent/thread-data-builder.ts +36 -4
  18. package/corpus/core/src/cli/templates-meta.ts +13 -0
  19. package/corpus/core/src/cli/workspace-dev.ts +1 -0
  20. package/corpus/core/src/client/error-format.ts +12 -0
  21. package/corpus/core/src/credentials/index.ts +51 -12
  22. package/corpus/core/src/extensions/url-safety.ts +46 -12
  23. package/corpus/core/src/integrations/adapters/slack.ts +32 -10
  24. package/corpus/core/src/integrations/google-docs-poller.ts +12 -1
  25. package/corpus/core/src/integrations/installations-store.ts +30 -0
  26. package/corpus/core/src/integrations/plugin.ts +5 -5
  27. package/corpus/core/src/integrations/types.ts +7 -0
  28. package/corpus/core/src/integrations/webhook-handler.ts +24 -1
  29. package/corpus/core/src/jobs/background-automation-runner.ts +38 -1
  30. package/corpus/core/src/provider-api/index.ts +8 -2
  31. package/corpus/core/src/scripts/call-agent.ts +6 -0
  32. package/corpus/core/src/server/agent-chat-plugin.ts +30 -9
  33. package/corpus/core/src/server/agent-discovery.ts +2 -0
  34. package/corpus/core/src/server/builder-design-systems.ts +21 -0
  35. package/corpus/core/src/server/credential-provider.ts +51 -12
  36. package/corpus/core/src/server/index.ts +1 -0
  37. package/corpus/core/src/server/onboarding-html.ts +37 -2
  38. package/corpus/core/src/shared/reasoning-effort.ts +1 -1
  39. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
  40. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +6 -2
  41. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +39 -19
  42. package/corpus/templates/assets/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  43. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +30 -3
  44. package/corpus/templates/brain/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  45. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
  46. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +54 -25
  47. package/corpus/templates/calendar/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  48. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +30 -3
  49. package/corpus/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  50. package/corpus/templates/clips/app/components/library/library-layout.tsx +37 -21
  51. package/corpus/templates/clips/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  52. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +1 -1
  53. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +1 -1
  54. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +34 -17
  55. package/corpus/templates/content/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  56. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -2
  57. package/corpus/templates/design/actions/create-design-system.ts +1 -0
  58. package/corpus/templates/design/actions/set-default-design-system.ts +34 -26
  59. package/corpus/templates/design/app/components/layout/Sidebar.tsx +21 -4
  60. package/corpus/templates/design/app/i18n-data.ts +39 -0
  61. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +229 -12
  62. package/corpus/templates/design/app/pages/DesignSystems.tsx +10 -4
  63. package/corpus/templates/design/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  64. package/corpus/templates/design/changelog/2026-07-31-design-system-indexing-keeps-your-chosen-name-when-you-uploa.md +6 -0
  65. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +19 -4
  66. package/corpus/templates/dispatch/AGENTS.md +4 -0
  67. package/corpus/templates/factory/.agents/skills/actions/SKILL.md +562 -0
  68. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +198 -0
  69. package/corpus/templates/factory/.agents/skills/agent-native-docs/SKILL.md +115 -0
  70. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +182 -0
  71. package/corpus/templates/factory/.agents/skills/capture-learnings/SKILL.md +89 -0
  72. package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +221 -0
  73. package/corpus/templates/factory/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  74. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  75. package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +52 -0
  76. package/corpus/templates/factory/.agents/skills/feature-flags/SKILL.md +169 -0
  77. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +174 -0
  78. package/corpus/templates/factory/.agents/skills/real-time-sync/SKILL.md +232 -0
  79. package/corpus/templates/factory/.agents/skills/security/SKILL.md +324 -0
  80. package/corpus/templates/factory/.agents/skills/self-modifying-code/SKILL.md +119 -0
  81. package/corpus/templates/factory/.agents/skills/shadcn-ui/SKILL.md +123 -0
  82. package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +232 -0
  83. package/corpus/templates/factory/.agents/skills/storing-data/SKILL.md +178 -0
  84. package/corpus/templates/factory/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
  85. package/corpus/templates/factory/.env.example +24 -0
  86. package/corpus/templates/factory/.ignore +0 -0
  87. package/corpus/templates/factory/.oxfmtrc.json +8 -0
  88. package/corpus/templates/factory/AGENTS.md +90 -0
  89. package/corpus/templates/factory/CHANGELOG.md +10 -0
  90. package/corpus/templates/factory/DEVELOPING.md +185 -0
  91. package/corpus/templates/factory/README.md +64 -0
  92. package/corpus/templates/factory/_gitignore +42 -0
  93. package/corpus/templates/factory/actions/add-factory-comment.ts +55 -0
  94. package/corpus/templates/factory/actions/approve-factory-item.ts +214 -0
  95. package/corpus/templates/factory/actions/babysit-pull-request.ts +126 -0
  96. package/corpus/templates/factory/actions/evaluate-triage-item.ts +172 -0
  97. package/corpus/templates/factory/actions/get-factory-graph.ts +62 -0
  98. package/corpus/templates/factory/actions/get-triage-config.ts +42 -0
  99. package/corpus/templates/factory/actions/get-triage-item.ts +74 -0
  100. package/corpus/templates/factory/actions/hello.ts +13 -0
  101. package/corpus/templates/factory/actions/ingest-github-observation.ts +100 -0
  102. package/corpus/templates/factory/actions/list-factories.ts +40 -0
  103. package/corpus/templates/factory/actions/list-factory-comments.ts +40 -0
  104. package/corpus/templates/factory/actions/list-triage-items.ts +85 -0
  105. package/corpus/templates/factory/actions/list-triage-rules.ts +44 -0
  106. package/corpus/templates/factory/actions/navigate.ts +41 -0
  107. package/corpus/templates/factory/actions/poll-slack-channel.ts +118 -0
  108. package/corpus/templates/factory/actions/reconcile-triage-run.ts +179 -0
  109. package/corpus/templates/factory/actions/record-triage-feedback.ts +58 -0
  110. package/corpus/templates/factory/actions/run.ts +2 -0
  111. package/corpus/templates/factory/actions/save-factory-graph.ts +129 -0
  112. package/corpus/templates/factory/actions/save-triage-config.ts +67 -0
  113. package/corpus/templates/factory/actions/save-triage-rule.ts +89 -0
  114. package/corpus/templates/factory/actions/suggest-factory-rules.ts +70 -0
  115. package/corpus/templates/factory/actions/view-screen.ts +73 -0
  116. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +441 -0
  117. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +388 -0
  118. package/corpus/templates/factory/app/components/layout/Header.tsx +60 -0
  119. package/corpus/templates/factory/app/components/layout/Layout.tsx +190 -0
  120. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +538 -0
  121. package/corpus/templates/factory/app/components/triage/triage-status-pill.tsx +28 -0
  122. package/corpus/templates/factory/app/components/ui/button.tsx +1 -0
  123. package/corpus/templates/factory/app/components/ui/card.tsx +1 -0
  124. package/corpus/templates/factory/app/components/ui/dropdown-menu.tsx +1 -0
  125. package/corpus/templates/factory/app/components/ui/input.tsx +1 -0
  126. package/corpus/templates/factory/app/components/ui/label.tsx +1 -0
  127. package/corpus/templates/factory/app/components/ui/sheet.tsx +1 -0
  128. package/corpus/templates/factory/app/components/ui/textarea.tsx +1 -0
  129. package/corpus/templates/factory/app/components/ui/toolkit-provider.tsx +17 -0
  130. package/corpus/templates/factory/app/components/ui/tooltip.tsx +1 -0
  131. package/corpus/templates/factory/app/design-system.ts +3 -0
  132. package/corpus/templates/factory/app/entry.client.tsx +22 -0
  133. package/corpus/templates/factory/app/entry.server.tsx +10 -0
  134. package/corpus/templates/factory/app/global.css +93 -0
  135. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +113 -0
  136. package/corpus/templates/factory/app/i18n/en-US.ts +211 -0
  137. package/corpus/templates/factory/app/i18n/index.ts +34 -0
  138. package/corpus/templates/factory/app/i18n-data.ts +556 -0
  139. package/corpus/templates/factory/app/lib/agent-page.tsx +49 -0
  140. package/corpus/templates/factory/app/lib/app-config.ts +11 -0
  141. package/corpus/templates/factory/app/lib/tab-id.ts +1 -0
  142. package/corpus/templates/factory/app/lib/utils.ts +1 -0
  143. package/corpus/templates/factory/app/root.tsx +175 -0
  144. package/corpus/templates/factory/app/routes/_index.tsx +94 -0
  145. package/corpus/templates/factory/app/routes/agent.tsx +24 -0
  146. package/corpus/templates/factory/app/routes/chat.$threadId.tsx +1 -0
  147. package/corpus/templates/factory/app/routes/database.tsx +17 -0
  148. package/corpus/templates/factory/app/routes/extensions.$id.$slug.tsx +2 -0
  149. package/corpus/templates/factory/app/routes/extensions.$id.tsx +11 -0
  150. package/corpus/templates/factory/app/routes/extensions._index.tsx +11 -0
  151. package/corpus/templates/factory/app/routes/extensions.tsx +5 -0
  152. package/corpus/templates/factory/app/routes/factory.tsx +1003 -0
  153. package/corpus/templates/factory/app/routes/observability.tsx +19 -0
  154. package/corpus/templates/factory/app/routes/settings.tsx +89 -0
  155. package/corpus/templates/factory/app/routes/team.tsx +11 -0
  156. package/corpus/templates/factory/app/routes.ts +4 -0
  157. package/corpus/templates/factory/app/vite-env.d.ts +6 -0
  158. package/corpus/templates/factory/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  159. package/corpus/templates/factory/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  160. package/corpus/templates/factory/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
  161. package/corpus/templates/factory/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  162. package/corpus/templates/factory/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  163. package/corpus/templates/factory/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  164. package/corpus/templates/factory/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  165. package/corpus/templates/factory/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
  166. package/corpus/templates/factory/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  167. package/corpus/templates/factory/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  168. package/corpus/templates/factory/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
  169. package/corpus/templates/factory/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  170. package/corpus/templates/factory/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  171. package/corpus/templates/factory/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  172. package/corpus/templates/factory/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  173. package/corpus/templates/factory/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  174. package/corpus/templates/factory/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  175. package/corpus/templates/factory/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  176. package/corpus/templates/factory/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  177. package/corpus/templates/factory/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  178. package/corpus/templates/factory/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
  179. package/corpus/templates/factory/changelog/2026-07-31-added-an-inspectable-observe-only-factory-queue-for-slack-.md +6 -0
  180. package/corpus/templates/factory/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  181. package/corpus/templates/factory/components.json +20 -0
  182. package/corpus/templates/factory/data/sync-config.json +1 -0
  183. package/corpus/templates/factory/learnings.defaults.md +5 -0
  184. package/corpus/templates/factory/netlify.toml +10 -0
  185. package/corpus/templates/factory/package.json +105 -0
  186. package/corpus/templates/factory/public/agent-native-icon-dark.svg +10 -0
  187. package/corpus/templates/factory/public/agent-native-icon-light.svg +10 -0
  188. package/corpus/templates/factory/public/agent-native-logo-dark.svg +21 -0
  189. package/corpus/templates/factory/public/agent-native-logo-light.svg +21 -0
  190. package/corpus/templates/factory/public/favicon.svg +1 -0
  191. package/corpus/templates/factory/public/icon-180.svg +1 -0
  192. package/corpus/templates/factory/public/icon-192.svg +1 -0
  193. package/corpus/templates/factory/public/icon-512.svg +1 -0
  194. package/corpus/templates/factory/public/manifest.json +21 -0
  195. package/corpus/templates/factory/react-router.config.ts +7 -0
  196. package/corpus/templates/factory/server/connectors/credentials.ts +129 -0
  197. package/corpus/templates/factory/server/connectors/slack.ts +129 -0
  198. package/corpus/templates/factory/server/db/index.ts +6 -0
  199. package/corpus/templates/factory/server/db/schema.ts +193 -0
  200. package/corpus/templates/factory/server/factory-graph/contracts.ts +212 -0
  201. package/corpus/templates/factory/server/factory-graph/store.ts +121 -0
  202. package/corpus/templates/factory/server/lib/require-workspace-member.ts +55 -0
  203. package/corpus/templates/factory/server/middleware/auth.ts +15 -0
  204. package/corpus/templates/factory/server/plugins/agent-chat.ts +58 -0
  205. package/corpus/templates/factory/server/plugins/auth.ts +14 -0
  206. package/corpus/templates/factory/server/plugins/factory-migrations.ts +247 -0
  207. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +116 -0
  208. package/corpus/templates/factory/server/plugins/integrations.ts +46 -0
  209. package/corpus/templates/factory/server/routes/[...page].get.ts +5 -0
  210. package/corpus/templates/factory/server/routes/_agent-native/factory/builder-callback.post.ts +116 -0
  211. package/corpus/templates/factory/server/triage/ai-services-git.ts +197 -0
  212. package/corpus/templates/factory/server/triage/builder-executor.ts +118 -0
  213. package/corpus/templates/factory/server/triage/contracts.ts +164 -0
  214. package/corpus/templates/factory/server/triage/github-ingestion.ts +35 -0
  215. package/corpus/templates/factory/server/triage/guards.ts +121 -0
  216. package/corpus/templates/factory/server/triage/ids.ts +27 -0
  217. package/corpus/templates/factory/server/triage/pr-babysit.ts +100 -0
  218. package/corpus/templates/factory/server/triage/pr-monitor.ts +220 -0
  219. package/corpus/templates/factory/server/triage/slack-client.ts +55 -0
  220. package/corpus/templates/factory/server/triage/slack-poller.ts +152 -0
  221. package/corpus/templates/factory/ssr-entry.ts +15 -0
  222. package/corpus/templates/factory/tsconfig.json +21 -0
  223. package/corpus/templates/factory/vite.config.ts +19 -0
  224. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +55 -5
  225. package/corpus/templates/forms/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  226. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +6 -2
  227. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +21 -6
  228. package/corpus/templates/plan/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  229. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +12 -0
  230. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -0
  231. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +11 -0
  232. package/corpus/templates/slides/AGENTS.md +5 -6
  233. package/corpus/templates/slides/actions/_await-fit-check.ts +20 -10
  234. package/corpus/templates/slides/actions/add-slide.ts +17 -4
  235. package/corpus/templates/slides/actions/create-design-system.ts +16 -4
  236. package/corpus/templates/slides/actions/delete-design-system.ts +18 -2
  237. package/corpus/templates/slides/actions/get-layout-overflows.ts +88 -0
  238. package/corpus/templates/slides/actions/import-file.ts +1 -0
  239. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +8 -0
  240. package/corpus/templates/slides/actions/patch-deck.ts +48 -16
  241. package/corpus/templates/slides/actions/set-default-design-system.ts +49 -19
  242. package/corpus/templates/slides/actions/update-slide.ts +13 -3
  243. package/corpus/templates/slides/actions/view-screen.ts +87 -10
  244. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +155 -28
  245. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +21 -17
  246. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +849 -341
  247. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +102 -2
  248. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +13 -16
  249. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +60 -12
  250. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +11 -1
  251. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +66 -19
  252. package/corpus/templates/slides/app/i18n/en-US.ts +23 -0
  253. package/corpus/templates/slides/app/lib/normalize-slide-padding.ts +8 -15
  254. package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -3
  255. package/corpus/templates/slides/app/pages/DesignSystems.tsx +4 -3
  256. package/corpus/templates/slides/app/pages/Index.tsx +1 -1
  257. package/corpus/templates/slides/changelog/2026-07-31-design-system-details-now-show-their-source-and-indexing-sta.md +6 -0
  258. package/corpus/templates/slides/changelog/2026-07-31-design-system-indexing-keeps-your-chosen-name-when-you-uploa.md +6 -0
  259. package/corpus/templates/slides/changelog/2026-07-31-design-system-setup-keeps-focus-rings-visible-and-uses-a-sim.md +6 -0
  260. package/corpus/templates/slides/changelog/2026-07-31-skip-prompt-is-now-available-in-the-new-deck-prompt-header.md +6 -0
  261. package/corpus/templates/slides/changelog/2026-07-31-slides-now-detect-real-text-and-box-overflow-across-the-deck.md +6 -0
  262. package/corpus/templates/slides/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  263. package/corpus/templates/slides/server/handlers/design-systems.ts +1 -0
  264. package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +1 -0
  265. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +23 -4
  266. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
  267. package/corpus/templates/slides/server/plugins/creative-context.ts +16 -3
  268. package/corpus/templates/slides/server/workspace-defaults.ts +5 -1
  269. package/corpus/templates/slides/shared/slide-fit.ts +26 -0
  270. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +30 -3
  271. package/corpus/templates/tasks/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  272. package/dist/a2a/correlation.d.ts +4 -1
  273. package/dist/a2a/correlation.d.ts.map +1 -1
  274. package/dist/a2a/correlation.js +8 -1
  275. package/dist/a2a/correlation.js.map +1 -1
  276. package/dist/a2a/types.d.ts +11 -3
  277. package/dist/a2a/types.d.ts.map +1 -1
  278. package/dist/a2a/types.js.map +1 -1
  279. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  280. package/dist/agent/engine/ai-sdk-engine.js +27 -41
  281. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  282. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  283. package/dist/agent/engine/builder-engine.js +18 -8
  284. package/dist/agent/engine/builder-engine.js.map +1 -1
  285. package/dist/agent/engine/error-detail.d.ts +61 -0
  286. package/dist/agent/engine/error-detail.d.ts.map +1 -1
  287. package/dist/agent/engine/error-detail.js +145 -3
  288. package/dist/agent/engine/error-detail.js.map +1 -1
  289. package/dist/agent/engine/failure-taxonomy.d.ts +24 -0
  290. package/dist/agent/engine/failure-taxonomy.d.ts.map +1 -0
  291. package/dist/agent/engine/failure-taxonomy.js +88 -0
  292. package/dist/agent/engine/failure-taxonomy.js.map +1 -0
  293. package/dist/agent/engine/index.d.ts +2 -1
  294. package/dist/agent/engine/index.d.ts.map +1 -1
  295. package/dist/agent/engine/index.js +2 -1
  296. package/dist/agent/engine/index.js.map +1 -1
  297. package/dist/agent/engine/registry.d.ts +16 -0
  298. package/dist/agent/engine/registry.d.ts.map +1 -1
  299. package/dist/agent/engine/registry.js +71 -6
  300. package/dist/agent/engine/registry.js.map +1 -1
  301. package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
  302. package/dist/agent/engine/translate-ai-sdk.js +14 -2
  303. package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
  304. package/dist/agent/production-agent.d.ts.map +1 -1
  305. package/dist/agent/production-agent.js +13 -24
  306. package/dist/agent/production-agent.js.map +1 -1
  307. package/dist/agent/run-loop-with-resume.js +1 -1
  308. package/dist/agent/run-loop-with-resume.js.map +1 -1
  309. package/dist/agent/run-manager.d.ts.map +1 -1
  310. package/dist/agent/run-manager.js +39 -10
  311. package/dist/agent/run-manager.js.map +1 -1
  312. package/dist/agent/run-store.d.ts +10 -0
  313. package/dist/agent/run-store.d.ts.map +1 -1
  314. package/dist/agent/run-store.js +20 -3
  315. package/dist/agent/run-store.js.map +1 -1
  316. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  317. package/dist/agent/thread-data-builder.js +28 -4
  318. package/dist/agent/thread-data-builder.js.map +1 -1
  319. package/dist/cli/templates-meta.d.ts.map +1 -1
  320. package/dist/cli/templates-meta.js +13 -0
  321. package/dist/cli/templates-meta.js.map +1 -1
  322. package/dist/cli/workspace-dev.d.ts.map +1 -1
  323. package/dist/cli/workspace-dev.js +1 -0
  324. package/dist/cli/workspace-dev.js.map +1 -1
  325. package/dist/client/error-format.d.ts.map +1 -1
  326. package/dist/client/error-format.js +10 -0
  327. package/dist/client/error-format.js.map +1 -1
  328. package/dist/collab/awareness.d.ts +2 -2
  329. package/dist/collab/awareness.d.ts.map +1 -1
  330. package/dist/credentials/index.d.ts +9 -2
  331. package/dist/credentials/index.d.ts.map +1 -1
  332. package/dist/credentials/index.js +50 -8
  333. package/dist/credentials/index.js.map +1 -1
  334. package/dist/extensions/url-safety.d.ts.map +1 -1
  335. package/dist/extensions/url-safety.js +38 -12
  336. package/dist/extensions/url-safety.js.map +1 -1
  337. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  338. package/dist/integrations/adapters/slack.js +25 -8
  339. package/dist/integrations/adapters/slack.js.map +1 -1
  340. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  341. package/dist/integrations/google-docs-poller.js +13 -1
  342. package/dist/integrations/google-docs-poller.js.map +1 -1
  343. package/dist/integrations/installations-store.d.ts +10 -0
  344. package/dist/integrations/installations-store.d.ts.map +1 -1
  345. package/dist/integrations/installations-store.js +24 -0
  346. package/dist/integrations/installations-store.js.map +1 -1
  347. package/dist/integrations/plugin.d.ts.map +1 -1
  348. package/dist/integrations/plugin.js +6 -5
  349. package/dist/integrations/plugin.js.map +1 -1
  350. package/dist/integrations/types.d.ts +7 -0
  351. package/dist/integrations/types.d.ts.map +1 -1
  352. package/dist/integrations/types.js.map +1 -1
  353. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  354. package/dist/integrations/webhook-handler.js +16 -1
  355. package/dist/integrations/webhook-handler.js.map +1 -1
  356. package/dist/jobs/background-automation-runner.d.ts.map +1 -1
  357. package/dist/jobs/background-automation-runner.js +35 -2
  358. package/dist/jobs/background-automation-runner.js.map +1 -1
  359. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  360. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  361. package/dist/notifications/routes.d.ts +3 -3
  362. package/dist/observability/routes.d.ts +3 -3
  363. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  364. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  365. package/dist/provider-api/index.js +7 -3
  366. package/dist/provider-api/index.js.map +1 -1
  367. package/dist/scripts/call-agent.d.ts.map +1 -1
  368. package/dist/scripts/call-agent.js +6 -1
  369. package/dist/scripts/call-agent.js.map +1 -1
  370. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  371. package/dist/server/agent-chat-plugin.js +25 -7
  372. package/dist/server/agent-chat-plugin.js.map +1 -1
  373. package/dist/server/agent-discovery.d.ts +2 -0
  374. package/dist/server/agent-discovery.d.ts.map +1 -1
  375. package/dist/server/agent-discovery.js.map +1 -1
  376. package/dist/server/builder-design-systems.d.ts +4 -1
  377. package/dist/server/builder-design-systems.d.ts.map +1 -1
  378. package/dist/server/builder-design-systems.js +11 -1
  379. package/dist/server/builder-design-systems.js.map +1 -1
  380. package/dist/server/credential-provider.d.ts.map +1 -1
  381. package/dist/server/credential-provider.js +49 -12
  382. package/dist/server/credential-provider.js.map +1 -1
  383. package/dist/server/index.d.ts +1 -1
  384. package/dist/server/index.d.ts.map +1 -1
  385. package/dist/server/index.js.map +1 -1
  386. package/dist/server/onboarding-html.d.ts.map +1 -1
  387. package/dist/server/onboarding-html.js +37 -2
  388. package/dist/server/onboarding-html.js.map +1 -1
  389. package/dist/server/transcribe-voice.d.ts +1 -1
  390. package/dist/shared/reasoning-effort.d.ts +1 -0
  391. package/dist/shared/reasoning-effort.d.ts.map +1 -1
  392. package/dist/shared/reasoning-effort.js +1 -1
  393. package/dist/shared/reasoning-effort.js.map +1 -1
  394. package/dist/templates/chat/app/components/layout/Sidebar.tsx +30 -3
  395. package/dist/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  396. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
  397. package/package.json +1 -1
  398. package/src/a2a/correlation.ts +11 -1
  399. package/src/a2a/types.ts +11 -3
  400. package/src/agent/engine/ai-sdk-engine.ts +33 -46
  401. package/src/agent/engine/builder-engine.ts +22 -10
  402. package/src/agent/engine/error-detail.ts +184 -3
  403. package/src/agent/engine/failure-taxonomy.ts +123 -0
  404. package/src/agent/engine/index.ts +8 -0
  405. package/src/agent/engine/registry.ts +92 -8
  406. package/src/agent/engine/translate-ai-sdk.ts +17 -2
  407. package/src/agent/production-agent.ts +13 -33
  408. package/src/agent/run-loop-with-resume.ts +1 -1
  409. package/src/agent/run-manager.ts +43 -14
  410. package/src/agent/run-store.ts +30 -3
  411. package/src/agent/thread-data-builder.ts +36 -4
  412. package/src/cli/templates-meta.ts +13 -0
  413. package/src/cli/workspace-dev.ts +1 -0
  414. package/src/client/error-format.ts +12 -0
  415. package/src/credentials/index.ts +51 -12
  416. package/src/extensions/url-safety.ts +46 -12
  417. package/src/integrations/adapters/slack.ts +32 -10
  418. package/src/integrations/google-docs-poller.ts +12 -1
  419. package/src/integrations/installations-store.ts +30 -0
  420. package/src/integrations/plugin.ts +5 -5
  421. package/src/integrations/types.ts +7 -0
  422. package/src/integrations/webhook-handler.ts +24 -1
  423. package/src/jobs/background-automation-runner.ts +38 -1
  424. package/src/provider-api/index.ts +8 -2
  425. package/src/scripts/call-agent.ts +6 -0
  426. package/src/server/agent-chat-plugin.ts +30 -9
  427. package/src/server/agent-discovery.ts +2 -0
  428. package/src/server/builder-design-systems.ts +21 -0
  429. package/src/server/credential-provider.ts +51 -12
  430. package/src/server/index.ts +1 -0
  431. package/src/server/onboarding-html.ts +37 -2
  432. package/src/shared/reasoning-effort.ts +1 -1
  433. package/src/templates/chat/app/components/layout/Sidebar.tsx +30 -3
  434. package/src/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  435. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
  436. package/corpus/templates/clips/desktop/src/lib/media-device-selection.ts +0 -157
@@ -0,0 +1,123 @@
1
+ import { classifyTerminalErrorCode } from "./error-detail.js";
2
+
3
+ export const AGENT_FAILURE_TAXONOMY_CODES = [
4
+ "provider_network_error",
5
+ "provider_config_error",
6
+ "overloaded_error",
7
+ "authentication_error",
8
+ "unknown",
9
+ ] as const;
10
+
11
+ export type AgentFailureTaxonomyCode =
12
+ (typeof AGENT_FAILURE_TAXONOMY_CODES)[number];
13
+
14
+ export type AgentFailureRegime = "interactive" | "scheduled";
15
+
16
+ export interface AgentFailureTaxonomy {
17
+ code: AgentFailureTaxonomyCode;
18
+ label: string;
19
+ regime: AgentFailureRegime;
20
+ source: "error_code" | "error_detail" | "unknown";
21
+ }
22
+
23
+ const LABELS: Record<AgentFailureTaxonomyCode, string> = {
24
+ provider_network_error: "SSL/TLS provider transport drop",
25
+ provider_config_error: "Model reasoning_effort with tools",
26
+ overloaded_error: "Provider overloaded_error",
27
+ authentication_error: "Missing provider authentication",
28
+ unknown: "Unclassified failure",
29
+ };
30
+
31
+ function knownCode(value: unknown): AgentFailureTaxonomyCode | undefined {
32
+ const normalized =
33
+ typeof value === "string" ? value.trim().toLowerCase() : "";
34
+ if (
35
+ normalized === "provider_network_error" ||
36
+ normalized === "connection_error" ||
37
+ normalized === "network_error" ||
38
+ normalized === "ssl_error" ||
39
+ normalized === "tls_error"
40
+ ) {
41
+ return "provider_network_error";
42
+ }
43
+ if (
44
+ normalized === "provider_config_error" ||
45
+ normalized === "reasoning_effort_tools"
46
+ ) {
47
+ return "provider_config_error";
48
+ }
49
+ if (
50
+ normalized === "overloaded_error" ||
51
+ normalized === "provider_overloaded"
52
+ ) {
53
+ return "overloaded_error";
54
+ }
55
+ if (
56
+ normalized === "authentication_error" ||
57
+ normalized === "missing_authentication_header" ||
58
+ normalized === "http_401" ||
59
+ normalized === "unauthorized"
60
+ ) {
61
+ return "authentication_error";
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ /**
67
+ * Classify the production failure families used by the Factory triage queue.
68
+ * The regime is deliberately derived from the durable run id, not inferred
69
+ * from prose: scheduled runs use the `job-` namespace and interactive runs do
70
+ * not. This keeps a healthy chat sample from hiding a scheduled outage.
71
+ */
72
+ export function classifyAgentFailure(input: {
73
+ runId?: unknown;
74
+ errorCode?: unknown;
75
+ errorDetail?: unknown;
76
+ terminalReason?: unknown;
77
+ terminalEvent?: unknown;
78
+ regime?: AgentFailureRegime;
79
+ }): AgentFailureTaxonomy {
80
+ const regime =
81
+ input.regime ??
82
+ (typeof input.runId === "string" && input.runId.startsWith("job-")
83
+ ? "scheduled"
84
+ : "interactive");
85
+ const explicit = knownCode(input.errorCode);
86
+ if (explicit) {
87
+ return {
88
+ code: explicit,
89
+ label: LABELS[explicit],
90
+ regime,
91
+ source: "error_code",
92
+ };
93
+ }
94
+
95
+ const evidence = [
96
+ input.errorCode,
97
+ input.errorDetail,
98
+ input.terminalReason,
99
+ input.terminalEvent,
100
+ ].filter((value) => value !== undefined && value !== null);
101
+ for (const value of evidence) {
102
+ const text =
103
+ typeof value === "string"
104
+ ? value
105
+ : (JSON.stringify(value) ?? String(value));
106
+ const classified = knownCode(classifyTerminalErrorCode(text));
107
+ if (classified) {
108
+ return {
109
+ code: classified,
110
+ label: LABELS[classified],
111
+ regime,
112
+ source: "error_detail",
113
+ };
114
+ }
115
+ }
116
+
117
+ return {
118
+ code: "unknown",
119
+ label: LABELS.unknown,
120
+ regime,
121
+ source: "unknown",
122
+ };
123
+ }
@@ -25,6 +25,7 @@ export {
25
25
  getConfiguredEngineNameForRequest,
26
26
  getStoredModelForEngine,
27
27
  normalizeModelForEngine,
28
+ resolveDelegatedRunModel,
28
29
  resolveEnginePreservesCustomModels,
29
30
  type NormalizeModelOptions,
30
31
  detectEngineFromEnv,
@@ -54,3 +55,10 @@ export {
54
55
  } from "./anthropic-engine.js";
55
56
  export { createAISDKEngine, type AISDKProvider } from "./ai-sdk-engine.js";
56
57
  export { registerBuiltinEngines } from "./builtin.js";
58
+ export {
59
+ AGENT_FAILURE_TAXONOMY_CODES,
60
+ classifyAgentFailure,
61
+ type AgentFailureRegime,
62
+ type AgentFailureTaxonomy,
63
+ type AgentFailureTaxonomyCode,
64
+ } from "./failure-taxonomy.js";
@@ -13,6 +13,7 @@ import { createRequire } from "node:module";
13
13
  import {
14
14
  assertCredentialStoreReadable,
15
15
  canUseDeployCredentialFallbackForRequest,
16
+ getBuilderCredentialAuthFailure,
16
17
  getProviderCredentialAuthFailure,
17
18
  readDeployCredentialEnv,
18
19
  resolveBuilderCredentialsDetailed,
@@ -272,6 +273,70 @@ export function normalizeModelForEngine(
272
273
  );
273
274
  }
274
275
 
276
+ type ModelResolvableEngine = Pick<
277
+ AgentEngine,
278
+ "name" | "defaultModel" | "supportedModels" | "preserveCustomModels"
279
+ >;
280
+
281
+ /**
282
+ * Bound an untrusted, caller-supplied model preference to this engine's own
283
+ * catalog. Returns `undefined` — never a substitute — when the hint names
284
+ * anything the engine does not already offer, so a peer can never move the run
285
+ * to a different provider, an unknown id, or a capability tier this engine was
286
+ * not going to serve on its own.
287
+ */
288
+ function resolveModelHintForEngine(
289
+ engine: ModelResolvableEngine,
290
+ hint: string | null | undefined,
291
+ ): string | undefined {
292
+ const candidate = typeof hint === "string" ? hint.trim() : "";
293
+ if (!candidate || candidate === "auto") return undefined;
294
+ // An engine with no catalog, or one that passes custom ids through verbatim
295
+ // (an OpenAI-compatible gateway), cannot prove membership — so it takes no
296
+ // hint at all rather than forwarding an unverifiable id to a provider.
297
+ if (engine.preserveCustomModels || engine.supportedModels.length === 0) {
298
+ return undefined;
299
+ }
300
+ const normalized = normalizeModelForEngine(engine, candidate);
301
+ // `normalizeModelForEngine` answers `defaultModel` both for "this IS the
302
+ // default" and for "no idea what this is", so an unmatched hint is only
303
+ // distinguishable by re-checking the raw candidate. Anything else it returns
304
+ // is a real catalog hit.
305
+ const matched =
306
+ normalized === engine.defaultModel
307
+ ? engine.supportedModels.includes(candidate)
308
+ : engine.supportedModels.includes(normalized);
309
+ return matched ? normalized : undefined;
310
+ }
311
+
312
+ /**
313
+ * Model for a delegated (A2A) run, in strict precedence: the receiving app's
314
+ * explicit configuration, then its own stored setting, then the caller's hint,
315
+ * then the engine default. An app that pins a model keeps it; a hint only fills
316
+ * the gap where the receiver would otherwise take a default it never chose.
317
+ *
318
+ * A rejected hint is logged and dropped — a delegated run must never fail over
319
+ * a preference.
320
+ */
321
+ export function resolveDelegatedRunModel(
322
+ engine: ModelResolvableEngine,
323
+ options: {
324
+ explicitModel?: string | null;
325
+ storedModel?: string | null;
326
+ callerModelHint?: string | null;
327
+ },
328
+ ): string {
329
+ const own = options.explicitModel ?? options.storedModel;
330
+ if (own) return normalizeModelForEngine(engine, own);
331
+ const hinted = resolveModelHintForEngine(engine, options.callerModelHint);
332
+ if (!hinted && options.callerModelHint) {
333
+ console.log(
334
+ `[a2a] Ignoring caller model hint "${options.callerModelHint}" — not offered by engine ${engine.name}`,
335
+ );
336
+ }
337
+ return normalizeModelForEngine(engine, hinted ?? engine.defaultModel);
338
+ }
339
+
275
340
  /**
276
341
  * Whether models saved or read for this engine ENTRY should be preserved
277
342
  * verbatim instead of normalized against the built-in catalog.
@@ -360,10 +425,7 @@ export function detectEngineFromEnv(): AgentEngineEntry | null {
360
425
  return null;
361
426
  }
362
427
 
363
- async function envKeyUsableForEntry(
364
- entry: AgentEngineEntry,
365
- key: string,
366
- ): Promise<boolean> {
428
+ async function envKeyUsableForEntry(key: string): Promise<boolean> {
367
429
  if (
368
430
  !(
369
431
  canUseDeployCredentialFallbackForRequest(key) &&
@@ -372,19 +434,41 @@ async function envKeyUsableForEntry(
372
434
  ) {
373
435
  return false;
374
436
  }
375
- if (entry.name === "builder") {
376
- return true;
377
- }
378
437
  const value = readDeployCredentialEnv(key);
379
438
  if (!value) return false;
380
439
  return !(await getProviderCredentialAuthFailure({ key, value }));
381
440
  }
382
441
 
442
+ /**
443
+ * Builder's deploy-env fallback is checked as a pair, not per-key: the
444
+ * auth-failure marker is fingerprinted from privateKey+publicKey together
445
+ * (see `builderCredentialFingerprint`), so a single-key lookup can never
446
+ * match it. Without this, a rejected deploy-level Builder key would keep
447
+ * reporting "usable" through this env-only path forever — the same class of
448
+ * bug as the per-scope check in `credential-provider.ts`'s
449
+ * `isCompleteBuilderConnection`.
450
+ */
451
+ async function hasUsableBuilderEnvKeys(): Promise<boolean> {
452
+ const privateKey = canUseDeployCredentialFallbackForRequest(
453
+ "BUILDER_PRIVATE_KEY",
454
+ )
455
+ ? readDeployCredentialEnv("BUILDER_PRIVATE_KEY")
456
+ : null;
457
+ const publicKey = canUseDeployCredentialFallbackForRequest(
458
+ "BUILDER_PUBLIC_KEY",
459
+ )
460
+ ? readDeployCredentialEnv("BUILDER_PUBLIC_KEY")
461
+ : null;
462
+ if (!privateKey || !publicKey) return false;
463
+ return !(await getBuilderCredentialAuthFailure({ privateKey, publicKey }));
464
+ }
465
+
383
466
  async function hasUsableEnvKeys(entry: AgentEngineEntry): Promise<boolean> {
384
467
  if (!isAgentEnginePackageInstalled(entry)) return false;
385
468
  if (entry.requiredEnvVars.length === 0) return false;
469
+ if (entry.name === "builder") return hasUsableBuilderEnvKeys();
386
470
  for (const key of entry.requiredEnvVars) {
387
- if (!(await envKeyUsableForEntry(entry, key))) return false;
471
+ if (!(await envKeyUsableForEntry(key))) return false;
388
472
  }
389
473
  return true;
390
474
  }
@@ -7,6 +7,10 @@
7
7
  * `ModelMessage` shapes.
8
8
  */
9
9
 
10
+ import {
11
+ classifyProviderError,
12
+ describeErrorWithCauses,
13
+ } from "./error-detail.js";
10
14
  import { backfillEngineMessagesToolResults } from "./translate-anthropic.js";
11
15
  import type {
12
16
  EngineTool,
@@ -294,11 +298,22 @@ export function aiSdkPartToEngineEvents(part: any): EngineEvent[] {
294
298
  case "error": {
295
299
  const errMsg =
296
300
  part.error instanceof Error
297
- ? part.error.message
301
+ ? describeErrorWithCauses(part.error)
298
302
  : typeof part.error === "string"
299
303
  ? part.error
300
304
  : JSON.stringify(part.error);
301
- events.push({ type: "stop", reason: "error", error: errMsg });
305
+ // `streamText` reports a failed provider request as a stream part rather
306
+ // than a throw, so this is the arrival path for most provider HTTP
307
+ // failures. It used to emit the message alone, discarding the
308
+ // APICallError's statusCode/isRetryable — which is why they all landed as
309
+ // `unknown` and were retried only if their prose happened to match a
310
+ // keyword. Same classifier as the thrown path in ai-sdk-engine.ts.
311
+ events.push({
312
+ type: "stop",
313
+ reason: "error",
314
+ error: errMsg,
315
+ ...classifyProviderError(part.error),
316
+ });
302
317
  break;
303
318
  }
304
319
 
@@ -84,6 +84,7 @@ import {
84
84
  LLM_MISSING_CREDENTIALS_MESSAGE,
85
85
  userFacingLlmCredentialError,
86
86
  } from "./engine/credential-errors.js";
87
+ import { isProviderConnectionErrorMessage } from "./engine/error-detail.js";
87
88
  import {
88
89
  resolveEngine,
89
90
  registerBuiltinEngines,
@@ -1312,7 +1313,7 @@ export function isRetryableError(err: unknown): boolean {
1312
1313
  msg.includes("gateway error") ||
1313
1314
  msg.includes("socket hang up") ||
1314
1315
  msg.includes("connection reset") ||
1315
- hasProviderConnectionErrorMessage(msg) ||
1316
+ isProviderConnectionErrorMessage(msg) ||
1316
1317
  msg.includes("too many requests") ||
1317
1318
  msg.includes("timeout") ||
1318
1319
  msg.includes("gateway timeout") ||
@@ -1321,17 +1322,6 @@ export function isRetryableError(err: unknown): boolean {
1321
1322
  );
1322
1323
  }
1323
1324
 
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
-
1335
1325
  // ---------------------------------------------------------------------------
1336
1326
  // Context-window overflow recovery
1337
1327
  // ---------------------------------------------------------------------------
@@ -2264,7 +2254,7 @@ export function isResumableEngineError(err: unknown): boolean {
2264
2254
  text.includes("econnaborted") ||
2265
2255
  text.includes("fetch failed") ||
2266
2256
  text.includes("network error") ||
2267
- hasProviderConnectionErrorMessage(text) ||
2257
+ isProviderConnectionErrorMessage(text) ||
2268
2258
  text.includes("connection reset") ||
2269
2259
  text.includes("connection closed") ||
2270
2260
  text.includes("stream closed") ||
@@ -6104,7 +6094,7 @@ function isRecoverableContinuationError(event: {
6104
6094
  code === "http_529" ||
6105
6095
  code === "run_timeout" ||
6106
6096
  message.includes("timeout") ||
6107
- hasProviderConnectionErrorMessage(message) ||
6097
+ isProviderConnectionErrorMessage(message) ||
6108
6098
  message.includes("temporarily unavailable")
6109
6099
  );
6110
6100
  }
@@ -6432,7 +6422,7 @@ export async function runAgentLoopWithMainChatInternalContinuations(
6432
6422
  type: "error",
6433
6423
  error: RUN_BUDGET_EXHAUSTED_MESSAGE,
6434
6424
  errorCode: RUN_BUDGET_EXHAUSTED_ERROR_CODE,
6435
- recoverable: true,
6425
+ recoverable: false,
6436
6426
  });
6437
6427
  }
6438
6428
  return usage;
@@ -6661,9 +6651,9 @@ export async function claimBackgroundWorkerRunEarly(opts: {
6661
6651
  .join(" "),
6662
6652
  ).catch(() => {});
6663
6653
 
6664
- // A failed durable abort read is fail-closed: never let a worker execute
6665
- // after cancellation just because the database was temporarily unreadable.
6666
- if (await turnAborted(threadId, turnId).catch(() => true)) {
6654
+ // A failed durable abort read must surface as infrastructure failure. It is
6655
+ // not evidence that the user stopped the turn.
6656
+ if (await turnAborted(threadId, turnId)) {
6667
6657
  await abortRun(opts.runId, "user").catch(() => {});
6668
6658
  return { claimed: false, skipped: "turn-aborted" };
6669
6659
  }
@@ -6674,7 +6664,7 @@ export async function claimBackgroundWorkerRunEarly(opts: {
6674
6664
  }).catch(() => {});
6675
6665
  }
6676
6666
 
6677
- if (await turnAborted(threadId, turnId).catch(() => true)) {
6667
+ if (await turnAborted(threadId, turnId)) {
6678
6668
  await abortRun(opts.runId, "user").catch(() => {});
6679
6669
  return { claimed: false, skipped: "turn-aborted" };
6680
6670
  }
@@ -6687,7 +6677,7 @@ export async function claimBackgroundWorkerRunEarly(opts: {
6687
6677
 
6688
6678
  await record(opts.runId, RUN_DIAG_STAGE.workerClaimed).catch(() => {});
6689
6679
  await heartbeat(opts.runId).catch(() => {});
6690
- if (await turnAborted(threadId, turnId).catch(() => true)) {
6680
+ if (await turnAborted(threadId, turnId)) {
6691
6681
  await abortRun(opts.runId, "user").catch(() => {});
6692
6682
  return { claimed: false, skipped: "turn-aborted" };
6693
6683
  }
@@ -7143,11 +7133,7 @@ export async function chainServerDrivenContinuation(opts: {
7143
7133
  };
7144
7134
  delete continuationBody[AGENT_CHAT_BACKGROUND_RUN_FIELD];
7145
7135
  try {
7146
- if (
7147
- await d
7148
- .isTurnAborted(effectiveThreadId, effectiveTurnId)
7149
- .catch(() => true)
7150
- ) {
7136
+ if (await d.isTurnAborted(effectiveThreadId, effectiveTurnId)) {
7151
7137
  await d.markRunAborted(runId, "user").catch(() => {});
7152
7138
  return;
7153
7139
  }
@@ -7196,11 +7182,7 @@ export async function chainServerDrivenContinuation(opts: {
7196
7182
  insertErr instanceof Error ? insertErr.message : insertErr,
7197
7183
  );
7198
7184
  }
7199
- if (
7200
- await d
7201
- .isTurnAborted(effectiveThreadId, effectiveTurnId)
7202
- .catch(() => true)
7203
- ) {
7185
+ if (await d.isTurnAborted(effectiveThreadId, effectiveTurnId)) {
7204
7186
  if (nextRowInserted)
7205
7187
  await d.markRunAborted(nextRunId, "user").catch(() => {});
7206
7188
  await d.markRunAborted(runId, "user").catch(() => {});
@@ -8376,9 +8358,7 @@ export function createProductionAgentHandler(
8376
8358
  : runId;
8377
8359
  if (
8378
8360
  isBackgroundWorker &&
8379
- (await isTurnAborted(effectiveThreadId, effectiveTurnId).catch(
8380
- () => true,
8381
- ))
8361
+ (await isTurnAborted(effectiveThreadId, effectiveTurnId))
8382
8362
  ) {
8383
8363
  await markRunAborted(runId, "user").catch(() => {});
8384
8364
  return { ok: true, stopped: true };
@@ -605,7 +605,7 @@ export async function runAgentLoopDirectWithSoftTimeout(
605
605
  type: "error",
606
606
  error: RUN_BUDGET_EXHAUSTED_MESSAGE,
607
607
  errorCode: RUN_BUDGET_EXHAUSTED_ERROR_CODE,
608
- recoverable: true,
608
+ recoverable: false,
609
609
  });
610
610
  reportFinalOutcome({
611
611
  state: "failed",
@@ -4,6 +4,11 @@ import {
4
4
  LLM_MISSING_CREDENTIALS_ERROR_CODE,
5
5
  LLM_MISSING_CREDENTIALS_MESSAGE,
6
6
  } from "./engine/credential-errors.js";
7
+ import {
8
+ classifyTerminalErrorCode,
9
+ describeErrorWithCauses,
10
+ isProviderConnectionError,
11
+ } from "./engine/error-detail.js";
7
12
  import { EngineError } from "./engine/types.js";
8
13
  import {
9
14
  insertRun,
@@ -341,23 +346,16 @@ function getRunErrorMessage(err: unknown): string {
341
346
  return "Unknown error";
342
347
  }
343
348
 
344
- function isProviderConnectionErrorMessage(message: string): boolean {
345
- const normalized = message.trim().toLowerCase();
346
- return (
347
- normalized === "connection error." ||
348
- normalized.includes("cannot connect to api:")
349
- );
350
- }
351
-
352
349
  function getRunErrorCode(err: unknown): string | undefined {
353
350
  if (err instanceof EngineError) {
354
351
  if (err.errorCode) return err.errorCode;
355
352
  if (err.statusCode === 429) return PROVIDER_RATE_LIMITED_ERROR_CODE;
356
353
  }
357
- if (err instanceof Error && isProviderConnectionErrorMessage(err.message)) {
358
- return PROVIDER_NETWORK_ERROR_CODE;
359
- }
360
- return undefined;
354
+ if (isProviderConnectionError(err)) return PROVIDER_NETWORK_ERROR_CODE;
355
+ // The code rides the error EVENT to the client, which decides recovery from
356
+ // it — so an uncoded transport failure has to be classified here too, not
357
+ // only when the run row is persisted.
358
+ return classifyTerminalErrorCode(describeErrorWithCauses(err));
361
359
  }
362
360
 
363
361
  function getEngineRunErrorDetails(err: EngineError): string | undefined {
@@ -376,7 +374,7 @@ function shouldCaptureRunError(err: unknown): boolean {
376
374
  }
377
375
  if (!(err instanceof Error)) return true;
378
376
  if (/^40[13] status code\b/i.test(err.message)) return false;
379
- if (isProviderConnectionErrorMessage(err.message)) return false;
377
+ if (isProviderConnectionError(err)) return false;
380
378
  if (!errorCode) return true;
381
379
  const normalizedCode = errorCode.toLowerCase();
382
380
  return (
@@ -984,6 +982,13 @@ export function startRun(
984
982
  // false-stale-reap zombie scenario where the reaper flipped the row while
985
983
  // this isolate was briefly unable to heartbeat (DB latency / GC pause).
986
984
  let lastAbortCheck = Date.now() - 3000;
985
+ // A read failure here used to be indistinguishable from "not aborted" —
986
+ // exactly the coerced-to-false pattern that lets a real Stop go unseen for
987
+ // the rest of the run. Count consecutive failures like the heartbeat-write
988
+ // handler above; past the same threshold, fail closed (self-abort with a
989
+ // reason outside TURN_ENDING_ABORT_REASONS/RECOVERABLE_ABORT_REASONS, so it
990
+ // surfaces as a typed error) instead of silently retrying forever.
991
+ let consecutiveAbortCheckFailures = 0;
987
992
  const checkSqlAbort = () => {
988
993
  const now = Date.now();
989
994
  if (now - lastAbortCheck < 3000) return;
@@ -1004,7 +1009,26 @@ export function startRun(
1004
1009
  }
1005
1010
  }
1006
1011
  })
1007
- .catch(() => {});
1012
+ .then(() => {
1013
+ consecutiveAbortCheckFailures = 0;
1014
+ })
1015
+ .catch((error) => {
1016
+ consecutiveAbortCheckFailures += 1;
1017
+ if (consecutiveAbortCheckFailures >= 3) {
1018
+ captureError(error, {
1019
+ route: "/_agent-native/agent-chat",
1020
+ tags: {
1021
+ source: "agent-run-manager",
1022
+ phase: "abort-check",
1023
+ consecutiveFailures: String(consecutiveAbortCheckFailures),
1024
+ },
1025
+ extra: { runId, threadId },
1026
+ });
1027
+ if (!abort.signal.aborted) {
1028
+ abortInMemoryRun(run, "abort_check_unavailable");
1029
+ }
1030
+ }
1031
+ });
1008
1032
  };
1009
1033
 
1010
1034
  // Heartbeat: bump heartbeat_at every 1.5s so watchers can detect a dead
@@ -1456,6 +1480,11 @@ export function startRun(
1456
1480
  ? completionError.message
1457
1481
  : String(completionError));
1458
1482
  }
1483
+ // An engine that emitted an error event without a code has NOT told us
1484
+ // the failure is unclassifiable — it told us nothing. Recover the code
1485
+ // from the message before falling back to "unknown", which the client
1486
+ // reads as "do not attempt recovery".
1487
+ errorCode ??= classifyTerminalErrorCode(errorDetail);
1459
1488
  runTerminalErrorCode = errorCode ?? "unknown";
1460
1489
  runTerminalErrorDetail = errorDetail;
1461
1490
  await setRunError(runId, errorCode ?? "unknown", errorDetail);
@@ -946,6 +946,16 @@ export interface UnclaimedBackgroundRunRow {
946
946
  * pre-inserted — independent of any liveness bump a redispatch attempt
947
947
  * makes along the way. */
948
948
  startedAt: number;
949
+ /**
950
+ * Whether the row still carries the `dispatch_payload` a redispatched worker
951
+ * would rehydrate its request body from. Eligibility for this sweep does NOT
952
+ * imply it: a background row can reach the grace window having never had a
953
+ * payload at all. Redispatching one of those asserts `payloadRef: true` to a
954
+ * worker that then cannot rehydrate, so it kills the run as
955
+ * `dispatch_payload_missing` — a reason that reads like data loss for what is
956
+ * really an un-redispatchable handoff.
957
+ */
958
+ hasDispatchPayload: boolean;
949
959
  }
950
960
 
951
961
  /**
@@ -965,7 +975,10 @@ export async function listUnclaimedBackgroundRunRows(): Promise<
965
975
  const { rows } = await client.execute({
966
976
  // CAST keeps the ms-epoch param 64-bit on Postgres (see
967
977
  // backgroundAwareStaleCutoffSql for the int4-inference failure mode).
968
- sql: `SELECT id, started_at FROM agent_runs
978
+ // Report payload presence rather than filtering on it: a payload-less row
979
+ // must still be VISIBLE to the sweep so the slow pass can reap it. Filtering
980
+ // it out here would leave it `running` forever.
981
+ sql: `SELECT id, started_at, (dispatch_payload IS NOT NULL) AS has_dispatch_payload FROM agent_runs
969
982
  WHERE status = 'running'
970
983
  AND dispatch_mode = 'background'
971
984
  AND COALESCE(heartbeat_at, started_at) < (CAST(? AS BIGINT) - ${UNCLAIMED_BACKGROUND_RUN_GRACE_MS})`,
@@ -975,11 +988,15 @@ export async function listUnclaimedBackgroundRunRows(): Promise<
975
988
  for (const row of rows ?? []) {
976
989
  const id = (row as { id?: unknown }).id;
977
990
  const startedAt = (row as { started_at?: unknown }).started_at;
991
+ const hasPayload = (row as { has_dispatch_payload?: unknown })
992
+ .has_dispatch_payload;
978
993
  if (typeof id === "string" && id) {
979
994
  result.push({
980
995
  id,
981
996
  startedAt:
982
997
  typeof startedAt === "number" ? startedAt : Number(startedAt) || 0,
998
+ // SQLite returns 1/0 where Postgres returns a boolean.
999
+ hasDispatchPayload: hasPayload === true || hasPayload === 1,
983
1000
  });
984
1001
  }
985
1002
  }
@@ -1147,10 +1164,20 @@ export async function setRunTerminalReason(
1147
1164
  await ensureRunTables();
1148
1165
  const client = getDbExec();
1149
1166
  const reason = terminalReason.slice(0, 200);
1167
+ // Write-once for a row that is already terminal. Three writers in three
1168
+ // isolates race on this column — the mid-run checkpoint, the run-manager's
1169
+ // finalization, and the background worker's failure path — with no ordering
1170
+ // between them, and last-writer-wins let a late checkpoint relabel a row
1171
+ // another isolate had already finalized. That produced impossible rows
1172
+ // (status='errored' carrying a continuation reason, no error_code, no
1173
+ // terminal event) and misattributed 130 production runs to a failure mode
1174
+ // they never hit. A row still `running` has no honest reason yet, so it
1175
+ // stays writable; once one is recorded on a terminal row, it stands.
1176
+ const guard = `AND (status = 'running' OR terminal_reason IS NULL OR terminal_reason = '')`;
1150
1177
  await client.execute({
1151
1178
  sql: isContinuationTerminalReason(reason)
1152
- ? `UPDATE agent_runs SET terminal_reason = ?, status = CASE WHEN status = 'completed' THEN 'truncated' ELSE status END WHERE id = ?`
1153
- : `UPDATE agent_runs SET terminal_reason = ? WHERE id = ?`,
1179
+ ? `UPDATE agent_runs SET terminal_reason = ?, status = CASE WHEN status = 'completed' THEN 'truncated' ELSE status END WHERE id = ? ${guard}`
1180
+ : `UPDATE agent_runs SET terminal_reason = ? WHERE id = ? ${guard}`,
1154
1181
  args: [reason, runId],
1155
1182
  });
1156
1183
  } catch {