@agent-native/core 0.119.3 → 0.119.5

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 (277) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +21 -0
  3. package/corpus/core/package.json +3 -3
  4. package/corpus/core/src/agent/engine/index.ts +2 -0
  5. package/corpus/core/src/agent/engine/registry.ts +95 -2
  6. package/corpus/core/src/agent/production-agent.ts +107 -125
  7. package/corpus/core/src/agent/run-manager.ts +45 -2
  8. package/corpus/core/src/agent/thread-data-builder.ts +85 -8
  9. package/corpus/core/src/client/AssistantChat.tsx +37 -9
  10. package/corpus/core/src/client/chat/message-components.tsx +9 -1
  11. package/corpus/core/src/client/chat/repo-helpers.ts +77 -0
  12. package/corpus/core/src/client/chat/tool-call-display.tsx +51 -15
  13. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +8 -0
  14. package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +5 -1
  15. package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +9 -1
  16. package/corpus/core/src/server/agent-chat/framework-prompts.ts +3 -1
  17. package/corpus/core/src/server/agent-chat/plugin-options.ts +9 -0
  18. package/corpus/core/src/server/agent-chat-plugin.ts +12 -3
  19. package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +512 -0
  20. package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +189 -0
  21. package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +115 -0
  22. package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
  23. package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +89 -0
  24. package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +221 -0
  25. package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  26. package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  27. package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +169 -0
  28. package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +160 -0
  29. package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +232 -0
  30. package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +280 -0
  31. package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +118 -0
  32. package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +119 -0
  33. package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +100 -0
  34. package/corpus/core/src/templates/chat/.env.example +5 -0
  35. package/corpus/core/src/templates/chat/.ignore +0 -0
  36. package/corpus/core/src/templates/chat/.oxfmtrc.json +8 -0
  37. package/corpus/core/src/templates/chat/AGENTS.md +69 -0
  38. package/corpus/core/src/templates/chat/CHANGELOG.md +10 -0
  39. package/corpus/core/src/templates/chat/DEVELOPING.md +185 -0
  40. package/corpus/core/src/templates/chat/README.md +32 -0
  41. package/corpus/core/src/templates/chat/_gitignore +43 -0
  42. package/corpus/core/src/templates/chat/actions/hello.ts +13 -0
  43. package/corpus/core/src/templates/chat/actions/navigate.ts +41 -0
  44. package/corpus/core/src/templates/chat/actions/run.ts +2 -0
  45. package/corpus/core/src/templates/chat/actions/view-screen.ts +31 -0
  46. package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +60 -0
  47. package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +185 -0
  48. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +450 -0
  49. package/corpus/core/src/templates/chat/app/components/ui/button.tsx +1 -0
  50. package/corpus/core/src/templates/chat/app/components/ui/card.tsx +1 -0
  51. package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
  52. package/corpus/core/src/templates/chat/app/components/ui/input.tsx +1 -0
  53. package/corpus/core/src/templates/chat/app/components/ui/label.tsx +1 -0
  54. package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +1 -0
  55. package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +17 -0
  56. package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +1 -0
  57. package/corpus/core/src/templates/chat/app/design-system.ts +3 -0
  58. package/corpus/core/src/templates/chat/app/entry.client.tsx +19 -0
  59. package/corpus/core/src/templates/chat/app/entry.server.tsx +10 -0
  60. package/corpus/core/src/templates/chat/app/global.css +93 -0
  61. package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +93 -0
  62. package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +68 -0
  63. package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +71 -0
  64. package/corpus/core/src/templates/chat/app/i18n/en-US.ts +70 -0
  65. package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +71 -0
  66. package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +71 -0
  67. package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +69 -0
  68. package/corpus/core/src/templates/chat/app/i18n/index.ts +34 -0
  69. package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +70 -0
  70. package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +70 -0
  71. package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +71 -0
  72. package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +66 -0
  73. package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +66 -0
  74. package/corpus/core/src/templates/chat/app/i18n-data.ts +556 -0
  75. package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +49 -0
  76. package/corpus/core/src/templates/chat/app/lib/app-config.ts +11 -0
  77. package/corpus/core/src/templates/chat/app/lib/tab-id.ts +1 -0
  78. package/corpus/core/src/templates/chat/app/lib/utils.ts +1 -0
  79. package/corpus/core/src/templates/chat/app/root.tsx +173 -0
  80. package/corpus/core/src/templates/chat/app/routes/_index.tsx +94 -0
  81. package/corpus/core/src/templates/chat/app/routes/agent.tsx +24 -0
  82. package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +1 -0
  83. package/corpus/core/src/templates/chat/app/routes/database.tsx +17 -0
  84. package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +2 -0
  85. package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +11 -0
  86. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +11 -0
  87. package/corpus/core/src/templates/chat/app/routes/extensions.tsx +5 -0
  88. package/corpus/core/src/templates/chat/app/routes/observability.tsx +19 -0
  89. package/corpus/core/src/templates/chat/app/routes/settings.tsx +89 -0
  90. package/corpus/core/src/templates/chat/app/routes/team.tsx +11 -0
  91. package/corpus/core/src/templates/chat/app/routes.ts +4 -0
  92. package/corpus/core/src/templates/chat/app/vite-env.d.ts +6 -0
  93. package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  94. package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  95. package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
  96. package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  97. package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  98. package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  99. package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  100. package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
  101. package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  102. package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  103. package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
  104. package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  105. package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  106. package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  107. package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  108. package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  109. package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  110. package/corpus/core/src/templates/chat/components.json +20 -0
  111. package/corpus/core/src/templates/chat/learnings.defaults.md +5 -0
  112. package/corpus/core/src/templates/chat/netlify.toml +10 -0
  113. package/corpus/core/src/templates/chat/package.json +92 -0
  114. package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +10 -0
  115. package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +10 -0
  116. package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +21 -0
  117. package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +21 -0
  118. package/corpus/core/src/templates/chat/public/favicon.svg +1 -0
  119. package/corpus/core/src/templates/chat/public/icon-180.svg +1 -0
  120. package/corpus/core/src/templates/chat/public/icon-192.svg +1 -0
  121. package/corpus/core/src/templates/chat/public/icon-512.svg +1 -0
  122. package/corpus/core/src/templates/chat/public/manifest.json +21 -0
  123. package/corpus/core/src/templates/chat/react-router.config.ts +7 -0
  124. package/corpus/core/src/templates/chat/server/middleware/auth.ts +15 -0
  125. package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +21 -0
  126. package/corpus/core/src/templates/chat/server/plugins/auth.ts +17 -0
  127. package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +5 -0
  128. package/corpus/core/src/templates/chat/ssr-entry.ts +15 -0
  129. package/corpus/core/src/templates/chat/tsconfig.json +21 -0
  130. package/corpus/core/src/templates/chat/vite.config.ts +19 -0
  131. package/corpus/templates/analytics/AGENTS.md +19 -11
  132. package/corpus/templates/analytics/actions/list-data-dictionary.ts +3 -1
  133. package/corpus/templates/analytics/actions/save-data-dictionary-entry.ts +14 -0
  134. package/corpus/templates/analytics/actions/search-analytics-query-catalog.ts +41 -0
  135. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +13 -0
  136. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +12 -11
  137. package/corpus/templates/analytics/app/pages/adhoc/DashboardSkeleton.tsx +3 -3
  138. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-chart-loading-placeholders-are-easier-to-see-in-da.md +6 -0
  139. package/corpus/templates/analytics/changelog/2026-07-23-routine-analytics-lookups-reuse-existing-query-definitions.md +6 -0
  140. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +1 -12
  141. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +405 -0
  142. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -16
  143. package/corpus/templates/content/changelog/2026-07-23-database-toolbar-sort-and-filter-controls-open-reliably.md +6 -0
  144. package/corpus/templates/forms/app/lib/public-form-link.ts +14 -0
  145. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +6 -14
  146. package/corpus/templates/forms/changelog/2026-07-23-published-form-links-can-be-copied-without-being-asked-to-re.md +6 -0
  147. package/corpus/templates/mail/changelog/2026-07-23-scheduled-emails-now-send-only-once-when-multiple-requests-r.md +6 -0
  148. package/corpus/templates/mail/server/lib/jobs.ts +13 -4
  149. package/corpus/templates/mail/server/plugins/mail-jobs.ts +1 -1
  150. package/corpus/templates/slides/actions/patch-deck.ts +2 -1
  151. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +7 -18
  152. package/corpus/templates/slides/app/lib/agent-visible-message.ts +28 -0
  153. package/corpus/templates/slides/app/pages/Index.tsx +6 -34
  154. package/corpus/templates/slides/changelog/2026-07-23-full-prompts-stay-visible-in-chat-when-creating-decks-or-add.md +6 -0
  155. package/corpus/templates/slides/changelog/2026-07-23-opening-or-refining-a-deck-no-longer-crashes-with-a-blank-er.md +6 -0
  156. package/corpus/templates/slides/shared/aspect-ratios.ts +4 -1
  157. package/corpus/templates/tasks/.oxfmtrc.json +8 -0
  158. package/corpus/templates/tasks/AGENTS.md +34 -41
  159. package/corpus/templates/tasks/README.md +13 -6
  160. package/corpus/templates/tasks/actions/bulk-delete-inbox-items.ts +10 -8
  161. package/corpus/templates/tasks/actions/bulk-delete-tasks.ts +10 -8
  162. package/corpus/templates/tasks/actions/bulk-mark-inbox-items-ready.ts +1 -1
  163. package/corpus/templates/tasks/actions/bulk-update-tasks.ts +12 -10
  164. package/corpus/templates/tasks/actions/create-inbox-item.ts +7 -4
  165. package/corpus/templates/tasks/actions/create-task.ts +2 -1
  166. package/corpus/templates/tasks/actions/delete-inbox-item.ts +6 -4
  167. package/corpus/templates/tasks/actions/delete-task.ts +1 -1
  168. package/corpus/templates/tasks/actions/list-inbox-items.ts +4 -2
  169. package/corpus/templates/tasks/actions/list-visible-task-fields.ts +3 -1
  170. package/corpus/templates/tasks/actions/mark-inbox-item-ready.ts +6 -4
  171. package/corpus/templates/tasks/actions/navigate.ts +16 -14
  172. package/corpus/templates/tasks/actions/reorder-inbox-items.ts +9 -7
  173. package/corpus/templates/tasks/actions/update-inbox-item.ts +6 -4
  174. package/corpus/templates/tasks/actions/update-task.ts +12 -9
  175. package/corpus/templates/tasks/actions/update-visible-task-fields.ts +10 -8
  176. package/corpus/templates/tasks/actions/view-screen.ts +1 -1
  177. package/corpus/templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx +15 -0
  178. package/corpus/templates/tasks/changelog/2026-07-23-task-detail-extensions.md +6 -0
  179. package/corpus/templates/tasks/drizzle.config.ts +3 -0
  180. package/corpus/templates/tasks/package.json +4 -1
  181. package/corpus/templates/tasks/server/db/schema.ts +0 -10
  182. package/corpus/templates/tasks/server/db/test-tasks-table.ts +0 -5
  183. package/corpus/templates/tasks/server/plugins/agent-chat.ts +1 -1
  184. package/corpus/templates/tasks/server/plugins/db.ts +43 -10
  185. package/corpus/templates/tasks/server/user-config/store.ts +42 -81
  186. package/corpus/templates/tasks/tsconfig.json +1 -2
  187. package/corpus/templates/tasks/vitest.config.ts +10 -1
  188. package/corpus/toolkit/CHANGELOG.md +13 -0
  189. package/corpus/toolkit/package.json +1 -1
  190. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +23 -2
  191. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +68 -2
  192. package/corpus/toolkit/src/editor/index.ts +1 -0
  193. package/corpus/toolkit/src/editor.css +75 -0
  194. package/corpus/toolkit/src/ui/button.tsx +3 -2
  195. package/dist/agent/engine/index.d.ts +1 -1
  196. package/dist/agent/engine/index.d.ts.map +1 -1
  197. package/dist/agent/engine/index.js +1 -1
  198. package/dist/agent/engine/index.js.map +1 -1
  199. package/dist/agent/engine/registry.d.ts +32 -1
  200. package/dist/agent/engine/registry.d.ts.map +1 -1
  201. package/dist/agent/engine/registry.js +75 -3
  202. package/dist/agent/engine/registry.js.map +1 -1
  203. package/dist/agent/production-agent.d.ts.map +1 -1
  204. package/dist/agent/production-agent.js +96 -115
  205. package/dist/agent/production-agent.js.map +1 -1
  206. package/dist/agent/run-manager.d.ts +9 -1
  207. package/dist/agent/run-manager.d.ts.map +1 -1
  208. package/dist/agent/run-manager.js +26 -0
  209. package/dist/agent/run-manager.js.map +1 -1
  210. package/dist/agent/thread-data-builder.d.ts +2 -0
  211. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  212. package/dist/agent/thread-data-builder.js +69 -9
  213. package/dist/agent/thread-data-builder.js.map +1 -1
  214. package/dist/client/AssistantChat.d.ts.map +1 -1
  215. package/dist/client/AssistantChat.js +32 -10
  216. package/dist/client/AssistantChat.js.map +1 -1
  217. package/dist/client/chat/message-components.d.ts +2 -1
  218. package/dist/client/chat/message-components.d.ts.map +1 -1
  219. package/dist/client/chat/message-components.js +9 -3
  220. package/dist/client/chat/message-components.js.map +1 -1
  221. package/dist/client/chat/repo-helpers.d.ts +4 -0
  222. package/dist/client/chat/repo-helpers.d.ts.map +1 -1
  223. package/dist/client/chat/repo-helpers.js +61 -0
  224. package/dist/client/chat/repo-helpers.js.map +1 -1
  225. package/dist/client/chat/tool-call-display.d.ts +2 -1
  226. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  227. package/dist/client/chat/tool-call-display.js +10 -9
  228. package/dist/client/chat/tool-call-display.js.map +1 -1
  229. package/dist/collab/struct-routes.d.ts +1 -1
  230. package/dist/observability/routes.d.ts +5 -5
  231. package/dist/progress/routes.d.ts +1 -1
  232. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  233. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  234. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  235. package/dist/resources/handlers.d.ts +1 -1
  236. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  237. package/dist/scripts/agent-engines/list-agent-engines.js +8 -2
  238. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  239. package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
  240. package/dist/scripts/agent-engines/manage-agent-engine.js +5 -2
  241. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  242. package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
  243. package/dist/scripts/agent-engines/set-agent-engine.js +9 -2
  244. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  245. package/dist/secrets/routes.d.ts +9 -9
  246. package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
  247. package/dist/server/agent-chat/framework-prompts.js +3 -1
  248. package/dist/server/agent-chat/framework-prompts.js.map +1 -1
  249. package/dist/server/agent-chat/plugin-options.d.ts +9 -0
  250. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  251. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  252. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  253. package/dist/server/agent-chat-plugin.js +11 -2
  254. package/dist/server/agent-chat-plugin.js.map +1 -1
  255. package/dist/server/realtime-token.d.ts +1 -1
  256. package/dist/server/transcribe-voice.d.ts +1 -1
  257. package/package.json +4 -4
  258. package/src/agent/engine/index.ts +2 -0
  259. package/src/agent/engine/registry.ts +95 -2
  260. package/src/agent/production-agent.ts +107 -125
  261. package/src/agent/run-manager.ts +45 -2
  262. package/src/agent/thread-data-builder.ts +85 -8
  263. package/src/client/AssistantChat.tsx +37 -9
  264. package/src/client/chat/message-components.tsx +9 -1
  265. package/src/client/chat/repo-helpers.ts +77 -0
  266. package/src/client/chat/tool-call-display.tsx +51 -15
  267. package/src/scripts/agent-engines/list-agent-engines.ts +8 -0
  268. package/src/scripts/agent-engines/manage-agent-engine.ts +5 -1
  269. package/src/scripts/agent-engines/set-agent-engine.ts +9 -1
  270. package/src/server/agent-chat/framework-prompts.ts +3 -1
  271. package/src/server/agent-chat/plugin-options.ts +9 -0
  272. package/src/server/agent-chat-plugin.ts +12 -3
  273. package/corpus/templates/tasks/.prettierrc +0 -5
  274. package/src/templates/chat/actions/hello.spec.ts +0 -23
  275. package/src/templates/chat/actions/navigate.spec.ts +0 -68
  276. package/src/templates/chat/actions/view-screen.spec.ts +0 -40
  277. package/src/templates/chat/app/lib/agent-page.spec.tsx +0 -61
@@ -26,9 +26,9 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
26
26
  expiresAt?: undefined;
27
27
  ttlSeconds?: undefined;
28
28
  } | {
29
- error?: undefined;
30
29
  token: string;
31
30
  expiresAt: string;
32
31
  ttlSeconds: number;
32
+ error?: undefined;
33
33
  }>>;
34
34
  //# sourceMappingURL=realtime-token.d.ts.map
@@ -20,7 +20,7 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- error?: undefined;
24
23
  text: string;
24
+ error?: undefined;
25
25
  }>>;
26
26
  //# sourceMappingURL=transcribe-voice.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.119.3",
3
+ "version": "0.119.5",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -362,7 +362,7 @@
362
362
  "highlight.js": "^11.11.1",
363
363
  "i18next": "26.3.1",
364
364
  "isbot": "^5",
365
- "jiti": "^2.6.1",
365
+ "jiti": "^2.7.0",
366
366
  "jose": "^6.2.2",
367
367
  "linkedom": "0.18.12",
368
368
  "lowlight": "^3.3.0",
@@ -370,7 +370,7 @@
370
370
  "nanoid": "^5.1.9",
371
371
  "next-themes": "^0.4.6",
372
372
  "nf3": "0.3.17",
373
- "nitro": "3.0.260415-beta",
373
+ "nitro": "3.0.260610-beta",
374
374
  "p-limit": "^7.3.0",
375
375
  "prettier": "^3.8.3",
376
376
  "react-i18next": "17.0.8",
@@ -390,7 +390,7 @@
390
390
  "yjs": "^13.6.31",
391
391
  "zod": "^4.3.6",
392
392
  "@agent-native/recap-cli": "0.5.1",
393
- "@agent-native/toolkit": "^0.10.2"
393
+ "@agent-native/toolkit": "^0.10.4"
394
394
  },
395
395
  "devDependencies": {
396
396
  "@ai-sdk/anthropic": "^3.0.71",
@@ -25,6 +25,8 @@ export {
25
25
  getConfiguredEngineNameForRequest,
26
26
  getStoredModelForEngine,
27
27
  normalizeModelForEngine,
28
+ resolveEnginePreservesCustomModels,
29
+ type NormalizeModelOptions,
28
30
  detectEngineFromEnv,
29
31
  detectEngineFromEnvForRequest,
30
32
  detectEngineFromUserSecrets,
@@ -96,6 +96,42 @@ function packageNameFromInstallSpecifier(specifier: string): string | null {
96
96
  return versionIndex === -1 ? trimmed : trimmed.slice(0, versionIndex);
97
97
  }
98
98
 
99
+ /**
100
+ * True only when there is positive evidence this module is executing from a
101
+ * bundled serverless function where optional dependencies were inlined into the
102
+ * bundle and are therefore NOT resolvable via `require.resolve` — even though
103
+ * the dynamic `import()` the engine uses to load them still works.
104
+ *
105
+ * Deliberately narrow. The Nitro Vercel/Netlify presets (which agent-native's
106
+ * own `deploy` command emits) inline optional peers and always set these env
107
+ * markers, so they are a reliable signal. Other serverless runtimes — a
108
+ * container on Cloud Run / Google Cloud Functions (`K_SERVICE` /
109
+ * `FUNCTION_TARGET`), or a plain AWS Lambda — commonly ship a real
110
+ * `node_modules` where `require.resolve` is authoritative; there a resolve miss
111
+ * means the package is genuinely absent and must NOT be masked. Those runtimes
112
+ * are still covered *when the code is actually bundled*, via the module-path
113
+ * check below, which stays false for a normal `node_modules` layout.
114
+ */
115
+ function isBundledServerlessRuntime(): boolean {
116
+ const env = process.env;
117
+ // Nitro's Vercel/Netlify presets inline optional peers into the function
118
+ // bundle; these platforms always set these markers.
119
+ if (env.VERCEL || env.NETLIFY) return true;
120
+ // Otherwise require direct evidence that this module is running from inside a
121
+ // bundle output directory (Vercel's `/var/task`, Nitro's `.output/server`,
122
+ // inlined `_libs`). This is the real signal that `require.resolve` cannot be
123
+ // trusted; it stays false for normal `node_modules` layouts (dev, tests, and
124
+ // container/Lambda/Cloud Run deploys that ship their dependencies), so a
125
+ // genuine "package not installed" miss still surfaces there.
126
+ try {
127
+ return /[\\/](?:_libs|\.vercel|\.netlify|\.output)[\\/]|\/var\/task\//.test(
128
+ import.meta.url ?? "",
129
+ );
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+
99
135
  function canResolvePackage(packageName: string): boolean {
100
136
  const cached = _packageAvailabilityCache.get(packageName);
101
137
  if (cached !== undefined) return cached;
@@ -104,7 +140,15 @@ function canResolvePackage(packageName: string): boolean {
104
140
  require.resolve(packageName);
105
141
  available = true;
106
142
  } catch {
107
- available = false;
143
+ // Bundled serverless runtimes (e.g. Nitro on Vercel/Netlify) inline optional
144
+ // provider packages into the function bundle, so require.resolve cannot find
145
+ // them even though the dynamic `import()` the engine actually uses to load
146
+ // them works. Treat them as available there and let the engine's own import
147
+ // be the real gate — it already fails with a clear "pnpm add …" message when
148
+ // the package is genuinely missing. Without this, every engine-usability
149
+ // gate rejects the AI-SDK engines at runtime and the agent silently falls
150
+ // back to the native Anthropic engine.
151
+ available = isBundledServerlessRuntime();
108
152
  }
109
153
  _packageAvailabilityCache.set(packageName, available);
110
154
  return available;
@@ -179,17 +223,41 @@ function findLatestSupportedVersionMatch(
179
223
  return best?.model;
180
224
  }
181
225
 
226
+ export interface NormalizeModelOptions {
227
+ /**
228
+ * Force unrecognized (custom) model IDs to be kept verbatim, as if
229
+ * `engine.preserveCustomModels` were set on a live engine instance.
230
+ *
231
+ * The settings actions call `normalizeModelForEngine` with a static registry
232
+ * ENTRY, which never carries the runtime `preserveCustomModels` flag — that
233
+ * is only set on the engine INSTANCE created with an OpenAI-compatible
234
+ * `baseUrl`. They resolve the capability with
235
+ * {@link resolveEnginePreservesCustomModels} and pass it here so a gateway
236
+ * model (e.g. an Ollama `gemma4`) is not rewritten to the OpenAI default on
237
+ * save/read. First-party OpenAI (no gateway) leaves this unset, so an unknown
238
+ * or invalid model still normalizes to a supported one.
239
+ */
240
+ preserveCustomModels?: boolean;
241
+ }
242
+
182
243
  export function normalizeModelForEngine(
183
244
  engine: Pick<
184
245
  AgentEngine,
185
246
  "name" | "defaultModel" | "supportedModels" | "preserveCustomModels"
186
247
  >,
187
248
  model: string | null | undefined,
249
+ options: NormalizeModelOptions = {},
188
250
  ): string {
189
251
  const candidate = typeof model === "string" ? model.trim() : "";
190
252
  if (!candidate) return engine.defaultModel;
191
253
 
192
- if (engine.preserveCustomModels) return candidate;
254
+ // Preserve custom IDs verbatim BEFORE any catalog/version matching, so a
255
+ // version-shaped gateway model that happens to share a family with a
256
+ // built-in model (e.g. `gpt-5.4` on an OpenAI-compatible endpoint) is not
257
+ // rewritten to a catalog entry.
258
+ if (engine.preserveCustomModels || options.preserveCustomModels) {
259
+ return candidate;
260
+ }
193
261
 
194
262
  if (engine.supportedModels.length === 0) return candidate;
195
263
 
@@ -203,6 +271,31 @@ export function normalizeModelForEngine(
203
271
  );
204
272
  }
205
273
 
274
+ /**
275
+ * Whether models saved or read for this engine ENTRY should be preserved
276
+ * verbatim instead of normalized against the built-in catalog.
277
+ *
278
+ * `normalizeModelForEngine` honors a live engine's `preserveCustomModels`, but
279
+ * that flag is only set on an AI SDK engine INSTANCE when the OpenAI provider
280
+ * is pointed at an OpenAI-compatible gateway (a custom base URL — e.g. Ollama
281
+ * Cloud or LiteLLM), whose model IDs are not in the built-in OpenAI catalog.
282
+ * The static registry entry the settings actions pass to
283
+ * `normalizeModelForEngine` cannot carry that runtime flag, so this async
284
+ * helper reproduces the same decision — `ai-sdk:openai` AND a resolved base URL
285
+ * — from the request's stored/deploy config. First-party OpenAI (no gateway)
286
+ * returns false so an unknown/invalid model still normalizes to a supported one.
287
+ */
288
+ export async function resolveEnginePreservesCustomModels(
289
+ entry: Pick<AgentEngineEntry, "name">,
290
+ ): Promise<boolean> {
291
+ if (entry.name !== "ai-sdk:openai") return false;
292
+ try {
293
+ return Boolean(await resolveOpenAiBaseUrl());
294
+ } catch {
295
+ return false;
296
+ }
297
+ }
298
+
206
299
  function assertAgentEnginePackageInstalled(entry: AgentEngineEntry): void {
207
300
  if (isAgentEnginePackageInstalled(entry)) return;
208
301
  const installHint = entry.installPackage
@@ -7746,18 +7746,6 @@ export function createProductionAgentHandler(
7746
7746
  }).catch(() => {});
7747
7747
  }
7748
7748
 
7749
- // The background worker must AWAIT the run to completion before returning,
7750
- // or Netlify freezes/kills the function the instant the handler returns and
7751
- // the detached run dies mid-turn (mirrors the agent-teams processor, which
7752
- // wraps startRun in `await new Promise(resolve => startRun(..., onComplete:
7753
- // () => resolve()))`). We resolve this when the run's onComplete fires.
7754
- let resolveBackgroundRunDone: (() => void) | null = null;
7755
- const backgroundRunDone = isBackgroundWorker
7756
- ? new Promise<void>((resolve) => {
7757
- resolveBackgroundRunDone = resolve;
7758
- })
7759
- : null;
7760
-
7761
7749
  const baseHandleRunComplete =
7762
7750
  options.onRunComplete || trackedProgressRunId
7763
7751
  ? async (run: ActiveRun) => {
@@ -7771,119 +7759,114 @@ export function createProductionAgentHandler(
7771
7759
  }
7772
7760
  : undefined;
7773
7761
 
7774
- // Wrap so the background worker is unblocked even when there is no app
7775
- // onRunComplete / tracked-progress callback configured. Also installed for
7776
- // a foreground run eligible for self-chain that path needs this same
7777
- // wrapper to fire the continuation dispatch below.
7762
+ // Install the completion callback for background continuation even when
7763
+ // there is no app onRunComplete / tracked-progress callback configured.
7764
+ // The foreground self-chain path uses the same continuation dispatch.
7778
7765
  const handleRunComplete =
7779
7766
  isBackgroundWorker || foregroundSelfChainEligible || baseHandleRunComplete
7780
7767
  ? async (run: ActiveRun) => {
7781
- try {
7782
- // DIAGNOSTIC: a background worker that completed in an errored
7783
- // state threw inside the loop. Record it (with the last error
7784
- // event's message when available) so the failure cause is
7785
- // readable from the client. Skipped for clean completions and for
7786
- // recoverable soft-timeout boundaries (those chain a continuation
7787
- // below, they did not "throw").
7788
- if (
7789
- isBackgroundWorker &&
7790
- run.status === "errored" &&
7791
- !willChainBackgroundContinuation(run)
7792
- ) {
7793
- const errEvent = [...run.events]
7794
- .reverse()
7795
- .find((e) => e.event.type === "error")?.event as
7796
- | { error?: string; errorCode?: string }
7797
- | undefined;
7798
- await recordRunDiagnostic(
7799
- run.runId,
7800
- RUN_DIAG_STAGE.workerThrew,
7801
- errEvent?.errorCode || errEvent?.error
7802
- ? `${errEvent.errorCode ?? ""} ${errEvent.error ?? ""}`.trim()
7803
- : "run ended in errored state",
7804
- ).catch(() => {});
7805
- }
7806
- // Persist the (partial) assistant turn to thread_data FIRST the
7807
- // server-driven continuation below rebuilds from it, so it must be
7808
- // committed before we re-fire.
7809
- await baseHandleRunComplete?.(run);
7810
-
7811
- // Server-driven background→background continuation. If this chunk
7812
- // hit its soft-timeout still unfinished (ended at an auto_continue
7813
- // / loop_limit / recoverable boundary), chain the next chunk by
7814
- // re-firing the `_process-run` self-dispatch with mode "continue"
7815
- // (carried as internalContinuation + an incremented count),
7816
- // instead of relying on the client to re-POST. Mirrors the
7817
- // agent-teams `fireInternalDispatch({ body: { mode: "continue" }})`
7818
- // chain. Bounded by MAX_BACKGROUND_RUN_CONTINUATIONS. Aborted /
7819
- // user-stopped runs do NOT chain.
7820
- // Self-chain server-side for EVERY durable worker, not only the
7821
- // ones inside a `-background` function. Server-driven
7822
- // continuation is the whole point of durable background: the run
7823
- // must survive the client disconnecting (closed tab), so it
7824
- // cannot depend on the browser re-POSTing `auto_continue`. A
7825
- // worker on the regular ~60s function — a Netlify routing miss,
7826
- // or a non-Netlify host (Vercel/Cloudflare/Render/Fly) that
7827
- // never emits a `-background` function checkpoints at the 40s
7828
- // soft-timeout and self-dispatches the next 40s chunk; a worker
7829
- // in a real `-background` function chains ~13-min chunks. Only
7830
- // the per-chunk BUDGET differs by function type (gated by
7831
- // `runsInBackgroundFunction` at the startRun call below); the
7832
- // continuation itself must stay server-driven on both. (The
7833
- // self-chain is only reachable when the initial dispatch already
7834
- // succeeded a dispatch fast-fail degrades to the inline
7835
- // foreground fallback, which is not a worker and rides the
7836
- // connected client's auto_continue instead.)
7837
- if (willChainBackgroundContinuation(run)) {
7838
- // Full handoff discipline lives in
7839
- // `chainServerDrivenContinuation` (exported + unit-tested):
7840
- // per-turn SQL run budget, successor row PRE-INSERTED before
7841
- // the dispatch, fully awaited dispatch with retries, loud
7842
- // diag/terminal marking on failure. Never throws.
7843
- await chainServerDrivenContinuation({
7844
- event,
7845
- run,
7846
- effectiveThreadId,
7847
- effectiveTurnId,
7848
- requestBody: body as unknown as Record<string, unknown>,
7849
- backgroundContinuationCount,
7850
- // Re-evaluate the durable gate rather than keying off
7851
- // isBackgroundWorker: a successor chunk of a FOREGROUND
7852
- // self-chain re-enters as a worker too, and must keep
7853
- // chaining via the regular function the Netlify
7854
- // `-background` function is only emitted into the deploy
7855
- // output when the durable flag is on.
7856
- //
7857
- // `&& !runsInBackgroundFunction`: a worker PROVEN to already
7858
- // be executing inside the real `-background` function must
7859
- // never dispatch back to that same function's own URL — a
7860
- // background function invoking itself by URL from inside a
7861
- // live invocation is a documented Netlify platform
7862
- // limitation (404), unlike the initial foreground→background
7863
- // dispatch or a worker that landed on the regular function
7864
- // (a genuinely different function calling the background
7865
- // one, which works). This only changes the dispatch TARGET
7866
- // for that one proven-in-bg-function case by this point
7867
- // (with the in-process resumable-error resume above) it is
7868
- // reached only when the chunk genuinely exhausted its
7869
- // budget, so falling back to the regular `_process-run`
7870
- // function (40s clamp) here is the correct, safe target.
7871
- chainViaDurableBackground:
7872
- isAgentChatDurableBackgroundEnabled({
7873
- appOptIn: options.durableBackgroundRuns,
7874
- }) && !runsInBackgroundFunction,
7875
- // Only changes the retry BUDGET, never the dispatch target
7876
- // above: a worker proven in a real background function has
7877
- // minutes of remaining wall clock and no connected-client
7878
- // fallback, so it must not be demoted to the foreground's
7879
- // 2-attempt budget just because it was forced onto the
7880
- // regular-function dispatch target. See
7881
- // `resolveContinuationDispatchBudget`.
7882
- workerProvenInBackgroundFunction: runsInBackgroundFunction,
7883
- });
7884
- }
7885
- } finally {
7886
- resolveBackgroundRunDone?.();
7768
+ // DIAGNOSTIC: a background worker that completed in an errored
7769
+ // state threw inside the loop. Record it (with the last error
7770
+ // event's message when available) so the failure cause is
7771
+ // readable from the client. Skipped for clean completions and for
7772
+ // recoverable soft-timeout boundaries (those chain a continuation
7773
+ // below, they did not "throw").
7774
+ if (
7775
+ isBackgroundWorker &&
7776
+ run.status === "errored" &&
7777
+ !willChainBackgroundContinuation(run)
7778
+ ) {
7779
+ const errEvent = [...run.events]
7780
+ .reverse()
7781
+ .find((e) => e.event.type === "error")?.event as
7782
+ | { error?: string; errorCode?: string }
7783
+ | undefined;
7784
+ await recordRunDiagnostic(
7785
+ run.runId,
7786
+ RUN_DIAG_STAGE.workerThrew,
7787
+ errEvent?.errorCode || errEvent?.error
7788
+ ? `${errEvent.errorCode ?? ""} ${errEvent.error ?? ""}`.trim()
7789
+ : "run ended in errored state",
7790
+ ).catch(() => {});
7791
+ }
7792
+ // Persist the (partial) assistant turn to thread_data FIRST — the
7793
+ // server-driven continuation below rebuilds from it, so it must be
7794
+ // committed before we re-fire.
7795
+ await baseHandleRunComplete?.(run);
7796
+
7797
+ // Server-driven background→background continuation. If this chunk
7798
+ // hit its soft-timeout still unfinished (ended at an auto_continue
7799
+ // / loop_limit / recoverable boundary), chain the next chunk by
7800
+ // re-firing the `_process-run` self-dispatch with mode "continue"
7801
+ // (carried as internalContinuation + an incremented count),
7802
+ // instead of relying on the client to re-POST. Mirrors the
7803
+ // agent-teams `fireInternalDispatch({ body: { mode: "continue" }})`
7804
+ // chain. Bounded by MAX_BACKGROUND_RUN_CONTINUATIONS. Aborted /
7805
+ // user-stopped runs do NOT chain.
7806
+ // Self-chain server-side for EVERY durable worker, not only the
7807
+ // ones inside a `-background` function. Server-driven
7808
+ // continuation is the whole point of durable background: the run
7809
+ // must survive the client disconnecting (closed tab), so it
7810
+ // cannot depend on the browser re-POSTing `auto_continue`. A
7811
+ // worker on the regular ~60s function — a Netlify routing miss,
7812
+ // or a non-Netlify host (Vercel/Cloudflare/Render/Fly) that
7813
+ // never emits a `-background` function checkpoints at the 40s
7814
+ // soft-timeout and self-dispatches the next 40s chunk; a worker
7815
+ // in a real `-background` function chains ~13-min chunks. Only
7816
+ // the per-chunk BUDGET differs by function type (gated by
7817
+ // `runsInBackgroundFunction` at the startRun call below); the
7818
+ // continuation itself must stay server-driven on both. (The
7819
+ // self-chain is only reachable when the initial dispatch already
7820
+ // succeeded a dispatch fast-fail degrades to the inline
7821
+ // foreground fallback, which is not a worker and rides the
7822
+ // connected client's auto_continue instead.)
7823
+ if (willChainBackgroundContinuation(run)) {
7824
+ // Full handoff discipline lives in
7825
+ // `chainServerDrivenContinuation` (exported + unit-tested):
7826
+ // per-turn SQL run budget, successor row PRE-INSERTED before
7827
+ // the dispatch, fully awaited dispatch with retries, loud
7828
+ // diag/terminal marking on failure. Never throws.
7829
+ await chainServerDrivenContinuation({
7830
+ event,
7831
+ run,
7832
+ effectiveThreadId,
7833
+ effectiveTurnId,
7834
+ requestBody: body as unknown as Record<string, unknown>,
7835
+ backgroundContinuationCount,
7836
+ // Re-evaluate the durable gate rather than keying off
7837
+ // isBackgroundWorker: a successor chunk of a FOREGROUND
7838
+ // self-chain re-enters as a worker too, and must keep
7839
+ // chaining via the regular function the Netlify
7840
+ // `-background` function is only emitted into the deploy
7841
+ // output when the durable flag is on.
7842
+ //
7843
+ // `&& !runsInBackgroundFunction`: a worker PROVEN to already
7844
+ // be executing inside the real `-background` function must
7845
+ // never dispatch back to that same function's own URL — a
7846
+ // background function invoking itself by URL from inside a
7847
+ // live invocation is a documented Netlify platform
7848
+ // limitation (404), unlike the initial foreground→background
7849
+ // dispatch or a worker that landed on the regular function
7850
+ // (a genuinely different function calling the background
7851
+ // one, which works). This only changes the dispatch TARGET
7852
+ // for that one proven-in-bg-function case by this point
7853
+ // (with the in-process resumable-error resume above) it is
7854
+ // reached only when the chunk genuinely exhausted its
7855
+ // budget, so falling back to the regular `_process-run`
7856
+ // function (40s clamp) here is the correct, safe target.
7857
+ chainViaDurableBackground:
7858
+ isAgentChatDurableBackgroundEnabled({
7859
+ appOptIn: options.durableBackgroundRuns,
7860
+ }) && !runsInBackgroundFunction,
7861
+ // Only changes the retry BUDGET, never the dispatch target
7862
+ // above: a worker proven in a real background function has
7863
+ // minutes of remaining wall clock and no connected-client
7864
+ // fallback, so it must not be demoted to the foreground's
7865
+ // 2-attempt budget just because it was forced onto the
7866
+ // regular-function dispatch target. See
7867
+ // `resolveContinuationDispatchBudget`.
7868
+ workerProvenInBackgroundFunction: runsInBackgroundFunction,
7869
+ });
7887
7870
  }
7888
7871
  }
7889
7872
  : undefined;
@@ -7970,7 +7953,7 @@ export function createProductionAgentHandler(
7970
7953
  )
7971
7954
  : null;
7972
7955
 
7973
- startRun(
7956
+ const startedRun = startRun(
7974
7957
  runId,
7975
7958
  effectiveThreadId,
7976
7959
  async (rawSend, signal) => {
@@ -8414,9 +8397,8 @@ export function createProductionAgentHandler(
8414
8397
  // Background worker: await the run to completion so Netlify keeps the
8415
8398
  // background function alive for the whole turn (the client is streaming the
8416
8399
  // same events via the foreground POST's cross-isolate SQL subscription).
8417
- // The onComplete wrapper above resolves `backgroundRunDone`.
8418
8400
  if (isBackgroundWorker) {
8419
- if (backgroundRunDone) await backgroundRunDone;
8401
+ await startedRun.finalized;
8420
8402
  return { ok: true, runId };
8421
8403
  }
8422
8404
 
@@ -44,6 +44,15 @@ export interface ActiveRun {
44
44
  startedAt: number;
45
45
  }
46
46
 
47
+ export interface StartedRun extends ActiveRun {
48
+ /**
49
+ * Resolves after the terminal event and final SQL status have been persisted.
50
+ * Serverless workers must await this before returning or the runtime can
51
+ * freeze the isolate between onComplete and terminalization.
52
+ */
53
+ finalized: Promise<void>;
54
+ }
55
+
47
56
  const activeRuns = new Map<string, ActiveRun>();
48
57
  const threadToRun = new Map<string, string>();
49
58
 
@@ -447,7 +456,7 @@ export function startRun(
447
456
  ) => Promise<void>,
448
457
  onComplete?: (run: ActiveRun) => void | Promise<void>,
449
458
  options?: StartRunOptions,
450
- ): ActiveRun {
459
+ ): StartedRun {
451
460
  // If there's already a run for this thread, abort it
452
461
  const existingRunId = threadToRun.get(threadId);
453
462
  if (existingRunId) {
@@ -456,7 +465,17 @@ export function startRun(
456
465
 
457
466
  const abort = new AbortController();
458
467
  let softTimedOut = false;
459
- const run: ActiveRun = {
468
+ let resolveFinalized: () => void = () => {};
469
+ let rejectFinalized: (reason?: unknown) => void = () => {};
470
+ const finalized = new Promise<void>((resolve, reject) => {
471
+ resolveFinalized = resolve;
472
+ rejectFinalized = reject;
473
+ });
474
+ // Foreground callers do not await this promise, but terminal persistence
475
+ // failures must still be observable to background workers without creating
476
+ // an unhandled rejection in the foreground path.
477
+ void finalized.catch(() => {});
478
+ const run: StartedRun = {
460
479
  runId,
461
480
  threadId,
462
481
  turnId: options?.turnId ?? runId,
@@ -465,6 +484,7 @@ export function startRun(
465
484
  subscribers: new Set(),
466
485
  abort,
467
486
  startedAt: Date.now(),
487
+ finalized,
468
488
  };
469
489
 
470
490
  activeRuns.set(runId, run);
@@ -1039,6 +1059,21 @@ export function startRun(
1039
1059
  "[run-manager] terminal event persistence error:",
1040
1060
  err instanceof Error ? err.message : err,
1041
1061
  );
1062
+ try {
1063
+ await insertRunEvent(
1064
+ runId,
1065
+ terminal.seq,
1066
+ JSON.stringify(terminal.event),
1067
+ );
1068
+ terminalPersistenceError = null;
1069
+ } catch (retryError) {
1070
+ terminalPersistenceError = retryError;
1071
+ captureRunError(retryError, "completion");
1072
+ console.error(
1073
+ "[run-manager] terminal event retry persistence error:",
1074
+ retryError instanceof Error ? retryError.message : retryError,
1075
+ );
1076
+ }
1042
1077
  }
1043
1078
  }
1044
1079
  }
@@ -1112,6 +1147,13 @@ export function startRun(
1112
1147
  await setRunError(runId, errorCode ?? "unknown", errorDetail);
1113
1148
  }
1114
1149
 
1150
+ if (terminalPersistenceError) {
1151
+ const reconciled = await reconcileTerminalRunFromEvents(runId).catch(
1152
+ () => false,
1153
+ );
1154
+ if (!reconciled) throw terminalPersistenceError;
1155
+ }
1156
+
1115
1157
  // 6. Schedule in-memory cleanup + opportunistic old-run pruning.
1116
1158
  setTimeout(() => {
1117
1159
  activeRuns.delete(runId);
@@ -1124,6 +1166,7 @@ export function startRun(
1124
1166
  resolveErroredRunRetentionMs(),
1125
1167
  ).catch(() => {});
1126
1168
  });
1169
+ runPromise.then(resolveFinalized, rejectFinalized);
1127
1170
 
1128
1171
  // On Cloudflare Workers, keep the isolate alive for this run
1129
1172
  try {