@agent-native/core 0.75.5 → 0.76.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (460) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +48 -0
  3. package/corpus/core/docs/AGENTS.md +1 -0
  4. package/corpus/core/docs/content/client.md +15 -0
  5. package/corpus/core/docs/content/internationalization.md +145 -0
  6. package/corpus/core/docs/content/locales/ar-SA/internationalization.md +102 -0
  7. package/corpus/core/docs/content/locales/de-DE/internationalization.md +102 -0
  8. package/corpus/core/docs/content/locales/es-ES/internationalization.md +102 -0
  9. package/corpus/core/docs/content/locales/fr-FR/internationalization.md +102 -0
  10. package/corpus/core/docs/content/locales/hi-IN/internationalization.md +102 -0
  11. package/corpus/core/docs/content/locales/ja-JP/internationalization.md +114 -0
  12. package/corpus/core/docs/content/locales/ko-KR/internationalization.md +102 -0
  13. package/corpus/core/docs/content/locales/pt-BR/internationalization.md +102 -0
  14. package/corpus/core/docs/content/locales/zh-CN/internationalization.md +102 -0
  15. package/corpus/core/docs/content/skills-guide.md +19 -18
  16. package/corpus/core/package.json +7 -1
  17. package/corpus/core/src/agent/durable-background.ts +38 -2
  18. package/corpus/core/src/agent/production-agent.ts +54 -0
  19. package/corpus/core/src/agent/run-manager.ts +27 -0
  20. package/corpus/core/src/agent/run-store.ts +185 -4
  21. package/corpus/core/src/client/CommandMenu.tsx +16 -0
  22. package/corpus/core/src/client/app-providers.tsx +26 -1
  23. package/corpus/core/src/client/i18n.tsx +605 -0
  24. package/corpus/core/src/client/index.ts +28 -0
  25. package/corpus/core/src/index.browser.ts +25 -0
  26. package/corpus/core/src/localization/actions/get-localization-preference.ts +23 -0
  27. package/corpus/core/src/localization/actions/set-localization-preference.ts +30 -0
  28. package/corpus/core/src/localization/index.ts +2 -0
  29. package/corpus/core/src/localization/server.ts +131 -0
  30. package/corpus/core/src/localization/shared.ts +163 -0
  31. package/corpus/core/src/server/action-discovery.ts +8 -0
  32. package/corpus/core/src/server/agent-chat-plugin.ts +71 -0
  33. package/corpus/core/src/server/auth.ts +13 -0
  34. package/corpus/core/src/server/index.ts +8 -0
  35. package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  36. package/corpus/core/src/templates/default/.agents/skills/internationalization/SKILL.md +77 -0
  37. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +6 -0
  38. package/corpus/core/src/templates/default/AGENTS.md +16 -15
  39. package/corpus/core/src/templates/default/app/i18n/en-US.ts +29 -0
  40. package/corpus/core/src/templates/default/app/i18n/index.ts +13 -0
  41. package/corpus/core/src/templates/default/app/root.tsx +20 -1
  42. package/corpus/core/src/templates/default/app/routes/_index.tsx +24 -9
  43. package/corpus/core/src/templates/default/app/routes/settings.tsx +67 -0
  44. package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  45. package/corpus/core/src/templates/workspace-core/.agents/skills/internationalization/SKILL.md +77 -0
  46. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +6 -0
  47. package/corpus/core/src/vite/action-types-plugin.ts +10 -0
  48. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +6 -0
  49. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +12 -12
  50. package/corpus/templates/analytics/app/components/layout/Header.tsx +18 -15
  51. package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
  52. package/corpus/templates/analytics/app/components/layout/MobileNav.tsx +9 -3
  53. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +28 -26
  54. package/corpus/templates/analytics/app/i18n/ar-SA.ts +3 -0
  55. package/corpus/templates/analytics/app/i18n/de-DE.ts +3 -0
  56. package/corpus/templates/analytics/app/i18n/en-US.ts +3 -0
  57. package/corpus/templates/analytics/app/i18n/es-ES.ts +3 -0
  58. package/corpus/templates/analytics/app/i18n/fr-FR.ts +3 -0
  59. package/corpus/templates/analytics/app/i18n/hi-IN.ts +3 -0
  60. package/corpus/templates/analytics/app/i18n/index.ts +31 -0
  61. package/corpus/templates/analytics/app/i18n/ja-JP.ts +3 -0
  62. package/corpus/templates/analytics/app/i18n/ko-KR.ts +3 -0
  63. package/corpus/templates/analytics/app/i18n/pt-BR.ts +3 -0
  64. package/corpus/templates/analytics/app/i18n/zh-CN.ts +3 -0
  65. package/corpus/templates/analytics/app/i18n-data.ts +433 -0
  66. package/corpus/templates/analytics/app/pages/Settings.tsx +50 -16
  67. package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +2 -2
  68. package/corpus/templates/analytics/app/root.tsx +14 -1
  69. package/corpus/templates/analytics/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  71. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +6 -0
  72. package/corpus/templates/assets/app/components/layout/Header.tsx +17 -8
  73. package/corpus/templates/assets/app/components/layout/Layout.tsx +10 -6
  74. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +40 -23
  75. package/corpus/templates/assets/app/i18n/ar-SA.ts +3 -0
  76. package/corpus/templates/assets/app/i18n/de-DE.ts +3 -0
  77. package/corpus/templates/assets/app/i18n/en-US.ts +3 -0
  78. package/corpus/templates/assets/app/i18n/es-ES.ts +3 -0
  79. package/corpus/templates/assets/app/i18n/fr-FR.ts +3 -0
  80. package/corpus/templates/assets/app/i18n/hi-IN.ts +3 -0
  81. package/corpus/templates/assets/app/i18n/index.ts +31 -0
  82. package/corpus/templates/assets/app/i18n/ja-JP.ts +3 -0
  83. package/corpus/templates/assets/app/i18n/ko-KR.ts +3 -0
  84. package/corpus/templates/assets/app/i18n/pt-BR.ts +3 -0
  85. package/corpus/templates/assets/app/i18n/zh-CN.ts +3 -0
  86. package/corpus/templates/assets/app/i18n-data.ts +668 -0
  87. package/corpus/templates/assets/app/root.tsx +40 -15
  88. package/corpus/templates/assets/app/routes/_index.tsx +9 -10
  89. package/corpus/templates/assets/app/routes/settings.tsx +37 -6
  90. package/corpus/templates/assets/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  91. package/corpus/templates/assets/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  92. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +5 -0
  93. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +6 -0
  94. package/corpus/templates/brain/app/components/layout/Layout.tsx +12 -8
  95. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +23 -12
  96. package/corpus/templates/brain/app/i18n/ar-SA.ts +3 -0
  97. package/corpus/templates/brain/app/i18n/de-DE.ts +3 -0
  98. package/corpus/templates/brain/app/i18n/en-US.ts +3 -0
  99. package/corpus/templates/brain/app/i18n/es-ES.ts +3 -0
  100. package/corpus/templates/brain/app/i18n/fr-FR.ts +3 -0
  101. package/corpus/templates/brain/app/i18n/hi-IN.ts +3 -0
  102. package/corpus/templates/brain/app/i18n/index.ts +31 -0
  103. package/corpus/templates/brain/app/i18n/ja-JP.ts +3 -0
  104. package/corpus/templates/brain/app/i18n/ko-KR.ts +3 -0
  105. package/corpus/templates/brain/app/i18n/pt-BR.ts +3 -0
  106. package/corpus/templates/brain/app/i18n/zh-CN.ts +3 -0
  107. package/corpus/templates/brain/app/i18n-data.ts +624 -0
  108. package/corpus/templates/brain/app/root.tsx +27 -12
  109. package/corpus/templates/brain/app/routes/knowledge.tsx +6 -6
  110. package/corpus/templates/brain/app/routes/ops.tsx +4 -4
  111. package/corpus/templates/brain/app/routes/search.tsx +4 -4
  112. package/corpus/templates/brain/app/routes/settings.tsx +48 -11
  113. package/corpus/templates/brain/app/routes/sources.tsx +3 -3
  114. package/corpus/templates/brain/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  115. package/corpus/templates/brain/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  116. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +6 -0
  117. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +2 -0
  118. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +16 -9
  119. package/corpus/templates/calendar/app/i18n/ar-SA.ts +57 -0
  120. package/corpus/templates/calendar/app/i18n/de-DE.ts +58 -0
  121. package/corpus/templates/calendar/app/i18n/en-US.ts +57 -0
  122. package/corpus/templates/calendar/app/i18n/es-ES.ts +58 -0
  123. package/corpus/templates/calendar/app/i18n/fr-FR.ts +58 -0
  124. package/corpus/templates/calendar/app/i18n/hi-IN.ts +57 -0
  125. package/corpus/templates/calendar/app/i18n/index.ts +31 -0
  126. package/corpus/templates/calendar/app/i18n/ja-JP.ts +58 -0
  127. package/corpus/templates/calendar/app/i18n/ko-KR.ts +57 -0
  128. package/corpus/templates/calendar/app/i18n/pt-BR.ts +57 -0
  129. package/corpus/templates/calendar/app/i18n/zh-CN.ts +57 -0
  130. package/corpus/templates/calendar/app/i18n-data.ts +516 -0
  131. package/corpus/templates/calendar/app/pages/CalendarView.tsx +2 -0
  132. package/corpus/templates/calendar/app/pages/Settings.tsx +63 -28
  133. package/corpus/templates/calendar/app/root.tsx +78 -7
  134. package/corpus/templates/calendar/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  135. package/corpus/templates/calendar/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  136. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +5 -0
  137. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +6 -0
  138. package/corpus/templates/chat/app/components/layout/Header.tsx +17 -11
  139. package/corpus/templates/chat/app/components/layout/Layout.tsx +12 -8
  140. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +47 -18
  141. package/corpus/templates/chat/app/i18n/ar-SA.ts +62 -0
  142. package/corpus/templates/chat/app/i18n/de-DE.ts +63 -0
  143. package/corpus/templates/chat/app/i18n/en-US.ts +63 -0
  144. package/corpus/templates/chat/app/i18n/es-ES.ts +63 -0
  145. package/corpus/templates/chat/app/i18n/fr-FR.ts +63 -0
  146. package/corpus/templates/chat/app/i18n/hi-IN.ts +63 -0
  147. package/corpus/templates/chat/app/i18n/index.ts +31 -0
  148. package/corpus/templates/chat/app/i18n/ja-JP.ts +63 -0
  149. package/corpus/templates/chat/app/i18n/ko-KR.ts +63 -0
  150. package/corpus/templates/chat/app/i18n/pt-BR.ts +63 -0
  151. package/corpus/templates/chat/app/i18n/zh-CN.ts +61 -0
  152. package/corpus/templates/chat/app/i18n-data.ts +475 -0
  153. package/corpus/templates/chat/app/root.tsx +18 -5
  154. package/corpus/templates/chat/app/routes/_index.tsx +9 -8
  155. package/corpus/templates/chat/app/routes/settings.tsx +86 -0
  156. package/corpus/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  157. package/corpus/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  158. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +6 -0
  159. package/corpus/templates/clips/app/components/layout/Header.tsx +23 -17
  160. package/corpus/templates/clips/app/components/library/empty-state.tsx +10 -34
  161. package/corpus/templates/clips/app/components/library/library-layout.tsx +45 -34
  162. package/corpus/templates/clips/app/i18n/ar-SA.ts +182 -0
  163. package/corpus/templates/clips/app/i18n/de-DE.ts +184 -0
  164. package/corpus/templates/clips/app/i18n/en-US.ts +179 -0
  165. package/corpus/templates/clips/app/i18n/es-ES.ts +183 -0
  166. package/corpus/templates/clips/app/i18n/fr-FR.ts +184 -0
  167. package/corpus/templates/clips/app/i18n/hi-IN.ts +180 -0
  168. package/corpus/templates/clips/app/i18n/index.ts +41 -0
  169. package/corpus/templates/clips/app/i18n/ja-JP.ts +180 -0
  170. package/corpus/templates/clips/app/i18n/ko-KR.ts +178 -0
  171. package/corpus/templates/clips/app/i18n/pt-BR.ts +183 -0
  172. package/corpus/templates/clips/app/i18n/zh-CN.ts +173 -0
  173. package/corpus/templates/clips/app/root.tsx +104 -10
  174. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +154 -95
  175. package/corpus/templates/clips/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  176. package/corpus/templates/clips/changelog/2026-06-24-clips-now-includes-a-language-picker-and-simplified-chinese-ui.md +6 -0
  177. package/corpus/templates/clips/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  178. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +6 -0
  179. package/corpus/templates/content/app/components/EmptyState.tsx +7 -5
  180. package/corpus/templates/content/app/components/ThemeToggle.tsx +4 -2
  181. package/corpus/templates/content/app/components/layout/Header.tsx +2 -0
  182. package/corpus/templates/content/app/components/layout/Layout.tsx +8 -7
  183. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +41 -1
  184. package/corpus/templates/content/app/i18n/ar-SA.ts +3 -0
  185. package/corpus/templates/content/app/i18n/de-DE.ts +3 -0
  186. package/corpus/templates/content/app/i18n/en-US.ts +3 -0
  187. package/corpus/templates/content/app/i18n/es-ES.ts +3 -0
  188. package/corpus/templates/content/app/i18n/fr-FR.ts +3 -0
  189. package/corpus/templates/content/app/i18n/hi-IN.ts +3 -0
  190. package/corpus/templates/content/app/i18n/index.ts +31 -0
  191. package/corpus/templates/content/app/i18n/ja-JP.ts +3 -0
  192. package/corpus/templates/content/app/i18n/ko-KR.ts +3 -0
  193. package/corpus/templates/content/app/i18n/pt-BR.ts +3 -0
  194. package/corpus/templates/content/app/i18n/zh-CN.ts +3 -0
  195. package/corpus/templates/content/app/i18n-data.ts +490 -0
  196. package/corpus/templates/content/app/root.tsx +110 -23
  197. package/corpus/templates/content/app/routes/_app.settings.tsx +84 -0
  198. package/corpus/templates/content/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  199. package/corpus/templates/content/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  200. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +6 -0
  201. package/corpus/templates/design/app/components/layout/Header.tsx +16 -8
  202. package/corpus/templates/design/app/components/layout/Layout.tsx +8 -6
  203. package/corpus/templates/design/app/components/layout/Sidebar.tsx +25 -9
  204. package/corpus/templates/design/app/i18n/ar-SA.ts +3 -0
  205. package/corpus/templates/design/app/i18n/de-DE.ts +3 -0
  206. package/corpus/templates/design/app/i18n/en-US.ts +3 -0
  207. package/corpus/templates/design/app/i18n/es-ES.ts +3 -0
  208. package/corpus/templates/design/app/i18n/fr-FR.ts +3 -0
  209. package/corpus/templates/design/app/i18n/hi-IN.ts +3 -0
  210. package/corpus/templates/design/app/i18n/index.ts +31 -0
  211. package/corpus/templates/design/app/i18n/ja-JP.ts +3 -0
  212. package/corpus/templates/design/app/i18n/ko-KR.ts +3 -0
  213. package/corpus/templates/design/app/i18n/pt-BR.ts +3 -0
  214. package/corpus/templates/design/app/i18n/zh-CN.ts +3 -0
  215. package/corpus/templates/design/app/i18n-data.ts +350 -0
  216. package/corpus/templates/design/app/root.tsx +40 -15
  217. package/corpus/templates/design/app/routes/settings.tsx +43 -0
  218. package/corpus/templates/design/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  219. package/corpus/templates/design/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  220. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +6 -0
  221. package/corpus/templates/dispatch/app/i18n/ar-SA.ts +3 -0
  222. package/corpus/templates/dispatch/app/i18n/de-DE.ts +3 -0
  223. package/corpus/templates/dispatch/app/i18n/en-US.ts +3 -0
  224. package/corpus/templates/dispatch/app/i18n/es-ES.ts +3 -0
  225. package/corpus/templates/dispatch/app/i18n/fr-FR.ts +3 -0
  226. package/corpus/templates/dispatch/app/i18n/hi-IN.ts +3 -0
  227. package/corpus/templates/dispatch/app/i18n/index.ts +31 -0
  228. package/corpus/templates/dispatch/app/i18n/ja-JP.ts +3 -0
  229. package/corpus/templates/dispatch/app/i18n/ko-KR.ts +3 -0
  230. package/corpus/templates/dispatch/app/i18n/pt-BR.ts +3 -0
  231. package/corpus/templates/dispatch/app/i18n/zh-CN.ts +3 -0
  232. package/corpus/templates/dispatch/app/i18n-data.ts +267 -0
  233. package/corpus/templates/dispatch/app/root.tsx +18 -4
  234. package/corpus/templates/dispatch/app/routes/settings.tsx +86 -0
  235. package/corpus/templates/dispatch/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  236. package/corpus/templates/dispatch/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  237. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +6 -0
  238. package/corpus/templates/forms/app/components/layout/Header.tsx +16 -9
  239. package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -4
  240. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +34 -17
  241. package/corpus/templates/forms/app/i18n/ar-SA.ts +58 -0
  242. package/corpus/templates/forms/app/i18n/de-DE.ts +59 -0
  243. package/corpus/templates/forms/app/i18n/en-US.ts +59 -0
  244. package/corpus/templates/forms/app/i18n/es-ES.ts +59 -0
  245. package/corpus/templates/forms/app/i18n/fr-FR.ts +59 -0
  246. package/corpus/templates/forms/app/i18n/hi-IN.ts +59 -0
  247. package/corpus/templates/forms/app/i18n/index.ts +31 -0
  248. package/corpus/templates/forms/app/i18n/ja-JP.ts +59 -0
  249. package/corpus/templates/forms/app/i18n/ko-KR.ts +59 -0
  250. package/corpus/templates/forms/app/i18n/pt-BR.ts +59 -0
  251. package/corpus/templates/forms/app/i18n/zh-CN.ts +56 -0
  252. package/corpus/templates/forms/app/root.tsx +65 -16
  253. package/corpus/templates/forms/app/routes/_app.settings.tsx +85 -0
  254. package/corpus/templates/forms/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  255. package/corpus/templates/forms/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  256. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +29 -17
  257. package/corpus/templates/macros/app/components/layout/Header.tsx +16 -10
  258. package/corpus/templates/macros/app/i18n/ar-SA.ts +54 -0
  259. package/corpus/templates/macros/app/i18n/de-DE.ts +55 -0
  260. package/corpus/templates/macros/app/i18n/en-US.ts +55 -0
  261. package/corpus/templates/macros/app/i18n/es-ES.ts +55 -0
  262. package/corpus/templates/macros/app/i18n/fr-FR.ts +55 -0
  263. package/corpus/templates/macros/app/i18n/hi-IN.ts +55 -0
  264. package/corpus/templates/macros/app/i18n/index.ts +31 -0
  265. package/corpus/templates/macros/app/i18n/ja-JP.ts +55 -0
  266. package/corpus/templates/macros/app/i18n/ko-KR.ts +55 -0
  267. package/corpus/templates/macros/app/i18n/pt-BR.ts +55 -0
  268. package/corpus/templates/macros/app/i18n/zh-CN.ts +53 -0
  269. package/corpus/templates/macros/app/root.tsx +65 -15
  270. package/corpus/templates/macros/app/routes/settings.tsx +85 -0
  271. package/corpus/templates/macros/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  272. package/corpus/templates/macros/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  273. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +6 -0
  274. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +9 -4
  275. package/corpus/templates/mail/app/i18n/ar-SA.ts +33 -0
  276. package/corpus/templates/mail/app/i18n/de-DE.ts +33 -0
  277. package/corpus/templates/mail/app/i18n/en-US.ts +32 -0
  278. package/corpus/templates/mail/app/i18n/es-ES.ts +33 -0
  279. package/corpus/templates/mail/app/i18n/fr-FR.ts +33 -0
  280. package/corpus/templates/mail/app/i18n/hi-IN.ts +32 -0
  281. package/corpus/templates/mail/app/i18n/index.ts +31 -0
  282. package/corpus/templates/mail/app/i18n/ja-JP.ts +32 -0
  283. package/corpus/templates/mail/app/i18n/ko-KR.ts +33 -0
  284. package/corpus/templates/mail/app/i18n/pt-BR.ts +34 -0
  285. package/corpus/templates/mail/app/i18n/zh-CN.ts +32 -0
  286. package/corpus/templates/mail/app/pages/SettingsPage.tsx +67 -13
  287. package/corpus/templates/mail/app/root.tsx +20 -0
  288. package/corpus/templates/mail/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  289. package/corpus/templates/mail/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
  290. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +5 -0
  291. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +6 -0
  292. package/corpus/templates/plan/app/components/layout/Header.tsx +19 -12
  293. package/corpus/templates/plan/app/components/layout/Layout.tsx +11 -7
  294. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +41 -28
  295. package/corpus/templates/plan/app/i18n/ar-SA.ts +77 -0
  296. package/corpus/templates/plan/app/i18n/de-DE.ts +78 -0
  297. package/corpus/templates/plan/app/i18n/en-US.ts +78 -0
  298. package/corpus/templates/plan/app/i18n/es-ES.ts +78 -0
  299. package/corpus/templates/plan/app/i18n/fr-FR.ts +78 -0
  300. package/corpus/templates/plan/app/i18n/hi-IN.ts +78 -0
  301. package/corpus/templates/plan/app/i18n/index.ts +31 -0
  302. package/corpus/templates/plan/app/i18n/ja-JP.ts +79 -0
  303. package/corpus/templates/plan/app/i18n/ko-KR.ts +77 -0
  304. package/corpus/templates/plan/app/i18n/pt-BR.ts +78 -0
  305. package/corpus/templates/plan/app/i18n/zh-CN.ts +74 -0
  306. package/corpus/templates/plan/app/root.tsx +45 -8
  307. package/corpus/templates/plan/app/routes/settings.tsx +86 -0
  308. package/corpus/templates/plan/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  309. package/corpus/templates/plan/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  310. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +6 -0
  311. package/corpus/templates/slides/app/components/layout/Header.tsx +23 -11
  312. package/corpus/templates/slides/app/components/layout/Layout.tsx +7 -6
  313. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +23 -11
  314. package/corpus/templates/slides/app/i18n/ar-SA.ts +54 -0
  315. package/corpus/templates/slides/app/i18n/de-DE.ts +56 -0
  316. package/corpus/templates/slides/app/i18n/en-US.ts +55 -0
  317. package/corpus/templates/slides/app/i18n/es-ES.ts +55 -0
  318. package/corpus/templates/slides/app/i18n/fr-FR.ts +55 -0
  319. package/corpus/templates/slides/app/i18n/hi-IN.ts +55 -0
  320. package/corpus/templates/slides/app/i18n/index.ts +31 -0
  321. package/corpus/templates/slides/app/i18n/ja-JP.ts +55 -0
  322. package/corpus/templates/slides/app/i18n/ko-KR.ts +55 -0
  323. package/corpus/templates/slides/app/i18n/pt-BR.ts +55 -0
  324. package/corpus/templates/slides/app/i18n/zh-CN.ts +53 -0
  325. package/corpus/templates/slides/app/root.tsx +50 -8
  326. package/corpus/templates/slides/app/routes/settings.tsx +85 -0
  327. package/corpus/templates/slides/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  328. package/corpus/templates/slides/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  329. package/corpus/templates/videos/.agents/skills/self-modifying-code/SKILL.md +6 -0
  330. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +14 -9
  331. package/corpus/templates/videos/app/components/Sidebar.tsx +16 -9
  332. package/corpus/templates/videos/app/components/StudioHeader.tsx +12 -8
  333. package/corpus/templates/videos/app/components/layout/Header.tsx +21 -15
  334. package/corpus/templates/videos/app/components/layout/Layout.tsx +10 -7
  335. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +21 -6
  336. package/corpus/templates/videos/app/i18n/ar-SA.ts +73 -0
  337. package/corpus/templates/videos/app/i18n/de-DE.ts +75 -0
  338. package/corpus/templates/videos/app/i18n/en-US.ts +74 -0
  339. package/corpus/templates/videos/app/i18n/es-ES.ts +75 -0
  340. package/corpus/templates/videos/app/i18n/fr-FR.ts +75 -0
  341. package/corpus/templates/videos/app/i18n/hi-IN.ts +74 -0
  342. package/corpus/templates/videos/app/i18n/index.ts +31 -0
  343. package/corpus/templates/videos/app/i18n/ja-JP.ts +74 -0
  344. package/corpus/templates/videos/app/i18n/ko-KR.ts +74 -0
  345. package/corpus/templates/videos/app/i18n/pt-BR.ts +75 -0
  346. package/corpus/templates/videos/app/i18n/zh-CN.ts +72 -0
  347. package/corpus/templates/videos/app/pages/Index.tsx +11 -9
  348. package/corpus/templates/videos/app/root.tsx +45 -7
  349. package/corpus/templates/videos/app/routes/settings.tsx +85 -0
  350. package/corpus/templates/videos/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  351. package/corpus/templates/videos/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  352. package/dist/agent/durable-background.d.ts +15 -0
  353. package/dist/agent/durable-background.d.ts.map +1 -1
  354. package/dist/agent/durable-background.js +28 -2
  355. package/dist/agent/durable-background.js.map +1 -1
  356. package/dist/agent/production-agent.d.ts.map +1 -1
  357. package/dist/agent/production-agent.js +34 -1
  358. package/dist/agent/production-agent.js.map +1 -1
  359. package/dist/agent/run-manager.d.ts +8 -0
  360. package/dist/agent/run-manager.d.ts.map +1 -1
  361. package/dist/agent/run-manager.js +18 -1
  362. package/dist/agent/run-manager.js.map +1 -1
  363. package/dist/agent/run-store.d.ts +99 -0
  364. package/dist/agent/run-store.d.ts.map +1 -1
  365. package/dist/agent/run-store.js +155 -4
  366. package/dist/agent/run-store.js.map +1 -1
  367. package/dist/client/CommandMenu.d.ts +3 -0
  368. package/dist/client/CommandMenu.d.ts.map +1 -1
  369. package/dist/client/CommandMenu.js +9 -0
  370. package/dist/client/CommandMenu.js.map +1 -1
  371. package/dist/client/app-providers.d.ts +8 -1
  372. package/dist/client/app-providers.d.ts.map +1 -1
  373. package/dist/client/app-providers.js +7 -5
  374. package/dist/client/app-providers.js.map +1 -1
  375. package/dist/client/i18n.d.ts +54 -0
  376. package/dist/client/i18n.d.ts.map +1 -0
  377. package/dist/client/i18n.js +354 -0
  378. package/dist/client/i18n.js.map +1 -0
  379. package/dist/client/index.d.ts +2 -1
  380. package/dist/client/index.d.ts.map +1 -1
  381. package/dist/client/index.js +2 -1
  382. package/dist/client/index.js.map +1 -1
  383. package/dist/index.browser.d.ts +1 -1
  384. package/dist/index.browser.d.ts.map +1 -1
  385. package/dist/index.browser.js +1 -1
  386. package/dist/index.browser.js.map +1 -1
  387. package/dist/localization/actions/get-localization-preference.d.ts +4 -0
  388. package/dist/localization/actions/get-localization-preference.d.ts.map +1 -0
  389. package/dist/localization/actions/get-localization-preference.js +16 -0
  390. package/dist/localization/actions/get-localization-preference.js.map +1 -0
  391. package/dist/localization/actions/set-localization-preference.d.ts +6 -0
  392. package/dist/localization/actions/set-localization-preference.d.ts.map +1 -0
  393. package/dist/localization/actions/set-localization-preference.js +24 -0
  394. package/dist/localization/actions/set-localization-preference.js.map +1 -0
  395. package/dist/localization/index.d.ts +3 -0
  396. package/dist/localization/index.d.ts.map +1 -0
  397. package/dist/localization/index.js +3 -0
  398. package/dist/localization/index.js.map +1 -0
  399. package/dist/localization/server.d.ts +25 -0
  400. package/dist/localization/server.d.ts.map +1 -0
  401. package/dist/localization/server.js +69 -0
  402. package/dist/localization/server.js.map +1 -0
  403. package/dist/localization/shared.d.ts +25 -0
  404. package/dist/localization/shared.d.ts.map +1 -0
  405. package/dist/localization/shared.js +134 -0
  406. package/dist/localization/shared.js.map +1 -0
  407. package/dist/server/action-discovery.d.ts.map +1 -1
  408. package/dist/server/action-discovery.js +8 -0
  409. package/dist/server/action-discovery.js.map +1 -1
  410. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  411. package/dist/server/agent-chat-plugin.js +58 -1
  412. package/dist/server/agent-chat-plugin.js.map +1 -1
  413. package/dist/server/auth.d.ts.map +1 -1
  414. package/dist/server/auth.js +12 -0
  415. package/dist/server/auth.js.map +1 -1
  416. package/dist/server/index.d.ts +1 -0
  417. package/dist/server/index.d.ts.map +1 -1
  418. package/dist/server/index.js +1 -0
  419. package/dist/server/index.js.map +1 -1
  420. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  421. package/dist/templates/default/.agents/skills/internationalization/SKILL.md +77 -0
  422. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +6 -0
  423. package/dist/templates/default/AGENTS.md +16 -15
  424. package/dist/templates/default/app/i18n/en-US.ts +29 -0
  425. package/dist/templates/default/app/i18n/index.ts +13 -0
  426. package/dist/templates/default/app/root.tsx +20 -1
  427. package/dist/templates/default/app/routes/_index.tsx +24 -9
  428. package/dist/templates/default/app/routes/settings.tsx +67 -0
  429. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  430. package/dist/templates/workspace-core/.agents/skills/internationalization/SKILL.md +77 -0
  431. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +6 -0
  432. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  433. package/dist/vite/action-types-plugin.js +8 -0
  434. package/dist/vite/action-types-plugin.js.map +1 -1
  435. package/docs/AGENTS.md +1 -0
  436. package/docs/content/client.md +15 -0
  437. package/docs/content/internationalization.md +145 -0
  438. package/docs/content/locales/ar-SA/internationalization.md +102 -0
  439. package/docs/content/locales/de-DE/internationalization.md +102 -0
  440. package/docs/content/locales/es-ES/internationalization.md +102 -0
  441. package/docs/content/locales/fr-FR/internationalization.md +102 -0
  442. package/docs/content/locales/hi-IN/internationalization.md +102 -0
  443. package/docs/content/locales/ja-JP/internationalization.md +114 -0
  444. package/docs/content/locales/ko-KR/internationalization.md +102 -0
  445. package/docs/content/locales/pt-BR/internationalization.md +102 -0
  446. package/docs/content/locales/zh-CN/internationalization.md +102 -0
  447. package/docs/content/skills-guide.md +19 -18
  448. package/package.json +7 -1
  449. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  450. package/src/templates/default/.agents/skills/internationalization/SKILL.md +77 -0
  451. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +6 -0
  452. package/src/templates/default/AGENTS.md +16 -15
  453. package/src/templates/default/app/i18n/en-US.ts +29 -0
  454. package/src/templates/default/app/i18n/index.ts +13 -0
  455. package/src/templates/default/app/root.tsx +20 -1
  456. package/src/templates/default/app/routes/_index.tsx +24 -9
  457. package/src/templates/default/app/routes/settings.tsx +67 -0
  458. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  459. package/src/templates/workspace-core/.agents/skills/internationalization/SKILL.md +77 -0
  460. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +6 -0
@@ -285,8 +285,37 @@ export type ProcessRunPreparation =
285
285
  status: number;
286
286
  /** Error payload. */
287
287
  error: string;
288
+ /**
289
+ * The run id parsed from the body, when present. Carried even on failure
290
+ * so the route can RECORD the auth/validation failure ONTO the run
291
+ * (diag_stage) before returning the error status — otherwise a 401/503 in
292
+ * the unreadable Netlify background function would leave the run to time
293
+ * out with no clue why. Null when no run id could be parsed.
294
+ */
295
+ runId: string | null;
288
296
  };
289
297
 
298
+ /**
299
+ * Parse the run id from a `_process-run` request body without authenticating.
300
+ * Mirrors the precedence in `prepareProcessRunRequest` (marker.runId, then
301
+ * top-level taskId). Returns null when neither is a usable string. Used so the
302
+ * route can attach a diagnostic to the run even on an auth/validation failure.
303
+ */
304
+ export function extractProcessRunId(body: unknown): string | null {
305
+ if (!body || typeof body !== "object") return null;
306
+ const record = body as Record<string, unknown>;
307
+ const marker = record[AGENT_CHAT_BACKGROUND_RUN_FIELD] as
308
+ | { runId?: unknown }
309
+ | undefined;
310
+ if (marker && typeof marker.runId === "string" && marker.runId) {
311
+ return marker.runId;
312
+ }
313
+ if (typeof record.taskId === "string" && record.taskId) {
314
+ return record.taskId;
315
+ }
316
+ return null;
317
+ }
318
+
290
319
  /**
291
320
  * Pure, transport-agnostic core of the `_process-run` route: validate the body,
292
321
  * authenticate the HMAC self-dispatch, and produce the body the re-entered
@@ -308,7 +337,12 @@ export function prepareProcessRunRequest(
308
337
  authHeader: string | undefined,
309
338
  ): ProcessRunPreparation {
310
339
  if (!body || typeof body !== "object") {
311
- return { ok: false, status: 400, error: "Invalid request body" };
340
+ return {
341
+ ok: false,
342
+ status: 400,
343
+ error: "Invalid request body",
344
+ runId: null,
345
+ };
312
346
  }
313
347
  const record = body as Record<string, unknown>;
314
348
  const marker = record[AGENT_CHAT_BACKGROUND_RUN_FIELD] as
@@ -321,7 +355,7 @@ export function prepareProcessRunRequest(
321
355
  ? (record.taskId as string)
322
356
  : "";
323
357
  if (!runId) {
324
- return { ok: false, status: 400, error: "runId required" };
358
+ return { ok: false, status: 400, error: "runId required", runId: null };
325
359
  }
326
360
 
327
361
  if (hasConfiguredA2ASecret()) {
@@ -331,6 +365,7 @@ export function prepareProcessRunRequest(
331
365
  ok: false,
332
366
  status: 401,
333
367
  error: "Invalid or expired processor token",
368
+ runId,
334
369
  };
335
370
  }
336
371
  } else if (isA2AProductionRuntime()) {
@@ -339,6 +374,7 @@ export function prepareProcessRunRequest(
339
374
  status: 503,
340
375
  error:
341
376
  "Agent chat background processor not configured — set A2A_SECRET on this deployment.",
377
+ runId,
342
378
  };
343
379
  }
344
380
 
@@ -109,6 +109,8 @@ import {
109
109
  updateRunHeartbeat,
110
110
  updateRunStatusIfRunning,
111
111
  claimBackgroundRun,
112
+ recordRunDiagnostic,
113
+ RUN_DIAG_STAGE,
112
114
  } from "./run-store.js";
113
115
  import {
114
116
  classifyToolCallJournal,
@@ -4599,6 +4601,30 @@ export function createProductionAgentHandler(
4599
4601
  isBackgroundWorker || baseHandleRunComplete
4600
4602
  ? async (run: ActiveRun) => {
4601
4603
  try {
4604
+ // DIAGNOSTIC: a background worker that completed in an errored
4605
+ // state threw inside the loop. Record it (with the last error
4606
+ // event's message when available) so the failure cause is
4607
+ // readable from the client. Skipped for clean completions and for
4608
+ // recoverable soft-timeout boundaries (those chain a continuation
4609
+ // below, they did not "throw").
4610
+ if (
4611
+ isBackgroundWorker &&
4612
+ run.status === "errored" &&
4613
+ !endsAtInternalContinuationBoundary(run)
4614
+ ) {
4615
+ const errEvent = [...run.events]
4616
+ .reverse()
4617
+ .find((e) => e.event.type === "error")?.event as
4618
+ | { error?: string; errorCode?: string }
4619
+ | undefined;
4620
+ await recordRunDiagnostic(
4621
+ run.runId,
4622
+ RUN_DIAG_STAGE.workerThrew,
4623
+ errEvent?.errorCode || errEvent?.error
4624
+ ? `${errEvent.errorCode ?? ""} ${errEvent.error ?? ""}`.trim()
4625
+ : "run ended in errored state",
4626
+ ).catch(() => {});
4627
+ }
4602
4628
  // Persist the (partial) assistant turn to thread_data FIRST — the
4603
4629
  // server-driven continuation below rebuilds from it, so it must be
4604
4630
  // committed before we re-fire.
@@ -4689,6 +4715,17 @@ export function createProductionAgentHandler(
4689
4715
  // on entry so a slow cold-start doesn't leave the row looking stale to the
4690
4716
  // reaper before startRun's 1.5s heartbeat timer takes over.
4691
4717
  if (isBackgroundWorker) {
4718
+ // DIAGNOSTIC: the re-entered handler recognized itself as the background
4719
+ // worker. Record the runtime regime too — `isInBackgroundFunctionRuntime()`
4720
+ // reads a globalThis marker set by the bg-fn entry, which may NOT be set in
4721
+ // this isolate; recording the ACTUAL resolved value reveals whether the
4722
+ // worker is on the 13-min `-background` budget or the 40s clamp. This is
4723
+ // the proof the worker reached its own code (vs. dying at auth before it).
4724
+ await recordRunDiagnostic(
4725
+ runId,
4726
+ RUN_DIAG_STAGE.workerEntered,
4727
+ `runsInBackgroundFunction=${runsInBackgroundFunction} continuationCount=${backgroundContinuationCount}`,
4728
+ ).catch(() => {});
4692
4729
  // A chained continuation chunk's runId was minted by the prior chunk and
4693
4730
  // never inserted, so insert its background row now (idempotently — a
4694
4731
  // duplicate Netlify delivery that already inserted it just PK-collides and
@@ -4703,8 +4740,16 @@ export function createProductionAgentHandler(
4703
4740
  if (!won) {
4704
4741
  // Already claimed by an earlier delivery — return a benign ack so
4705
4742
  // Netlify doesn't retry a successful handoff.
4743
+ await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerClaimLost).catch(
4744
+ () => {},
4745
+ );
4706
4746
  return { ok: true, skipped: "already-claimed" };
4707
4747
  }
4748
+ // DIAGNOSTIC: this worker won the claim and now OWNS the run. If a run
4749
+ // ever stalls at this stage it means the loop below failed to start.
4750
+ await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerClaimed).catch(
4751
+ () => {},
4752
+ );
4708
4753
  await updateRunHeartbeat(runId).catch(() => {});
4709
4754
  }
4710
4755
 
@@ -4719,6 +4764,15 @@ export function createProductionAgentHandler(
4719
4764
 
4720
4765
  send({ type: "activity", label: "Starting agent" });
4721
4766
 
4767
+ // DIAGNOSTIC: the agent loop body actually started running. For a
4768
+ // background worker, a run that is claimed but never reaches this stage
4769
+ // died between claiming and loop start. Best-effort, background only.
4770
+ if (isBackgroundWorker) {
4771
+ await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerStarted).catch(
4772
+ () => {},
4773
+ );
4774
+ }
4775
+
4722
4776
  // Notify listeners that a run has started (used by agent teams)
4723
4777
  if (options.onRunStart) {
4724
4778
  await options.onRunStart(send, threadId ?? runId);
@@ -15,6 +15,7 @@ import {
15
15
  updateRunHeartbeat,
16
16
  bumpRunProgress,
17
17
  reapIfStale,
18
+ reapUnclaimedBackgroundRun,
18
19
  ensureTerminalRunEvent,
19
20
  setRunError,
20
21
  STALE_RUN_ERROR_EVENT,
@@ -1022,6 +1023,14 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
1022
1023
  status: string;
1023
1024
  heartbeatAt: number;
1024
1025
  lastProgressAt: number | null;
1026
+ /** How the run was dispatched (NULL/foreground, background, background-processing). */
1027
+ dispatchMode?: string | null;
1028
+ /**
1029
+ * Last reached `_process-run` worker stage as a JSON string
1030
+ * `{stage,detail?,at}`. Surfaced so a silent background-worker death is
1031
+ * diagnosable from the client WITHOUT the unreadable bg-fn logs.
1032
+ */
1033
+ diagStage?: string | null;
1025
1034
  } | null> {
1026
1035
  // Check memory first — return both running AND recently-completed runs
1027
1036
  // that still have events in memory. This allows sub-agent tabs to replay
@@ -1055,6 +1064,20 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
1055
1064
  const sqlRun = await getRunByThread(threadId, { includeTerminal: true });
1056
1065
  if (!sqlRun) return null;
1057
1066
  if (sqlRun.status === "running") {
1067
+ // FALLBACK HARDENING: a background-dispatched run that is still UNCLAIMED
1068
+ // (dispatch_mode === 'background', never flipped to 'background-processing')
1069
+ // past the tight grace means the bg-fn worker never started — a silent
1070
+ // async-worker death that the 202-ack inline fallback can't catch. Reap it
1071
+ // early and recoverably (background_worker_never_started) so the run no
1072
+ // longer hangs for the full 90s window and the client's recoverable-error
1073
+ // path can re-drive the turn. Only fires when there is provably no live
1074
+ // worker; a claimed/heartbeating run is left alone by the conditional SQL.
1075
+ if (sqlRun.dispatchMode === "background") {
1076
+ const recovered = await reapUnclaimedBackgroundRun(sqlRun.id).catch(
1077
+ () => false,
1078
+ );
1079
+ if (recovered) return null;
1080
+ }
1058
1081
  // If the producer is dead (no recent heartbeat), reap before the
1059
1082
  // client can see a stale "running" status and enter a reconnect
1060
1083
  // loop it can never exit.
@@ -1067,6 +1090,8 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
1067
1090
  status: sqlRun.status,
1068
1091
  heartbeatAt: sqlRun.heartbeatAt ?? sqlRun.startedAt,
1069
1092
  lastProgressAt: sqlRun.lastProgressAt,
1093
+ dispatchMode: sqlRun.dispatchMode,
1094
+ diagStage: sqlRun.diagStage,
1070
1095
  };
1071
1096
  }
1072
1097
  if (sqlRun.status === "completed" || sqlRun.status === "errored") {
@@ -1092,6 +1117,8 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
1092
1117
  status: sqlRun.status,
1093
1118
  heartbeatAt: sqlRun.heartbeatAt ?? sqlRun.startedAt,
1094
1119
  lastProgressAt: sqlRun.lastProgressAt,
1120
+ dispatchMode: sqlRun.dispatchMode,
1121
+ diagStage: sqlRun.diagStage,
1095
1122
  };
1096
1123
  }
1097
1124
  } catch {
@@ -44,6 +44,40 @@ export const STALE_RUN_ERROR_EVENT = {
44
44
  "The run heartbeat stopped while the run was still marked running. Partial output and tool calls were preserved when available.",
45
45
  } as const;
46
46
 
47
+ /**
48
+ * Terminal error for a background-dispatched run whose worker NEVER claimed it
49
+ * (the foreground fired the self-dispatch, Netlify acked it async with a 202,
50
+ * but the `_process-run` worker never ran far enough to flip
51
+ * `dispatch_mode background → background-processing`). Distinct errorCode so the
52
+ * client (and prod triage) can tell "the worker died silently" apart from "a
53
+ * claimed worker's heartbeat went stale". Recoverable so the client surfaces a
54
+ * retry affordance and re-drives the turn. See `reapUnclaimedBackgroundRun`.
55
+ */
56
+ export const UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT = {
57
+ type: "error",
58
+ error:
59
+ "The agent run was handed off to a background worker that never started. It was recovered so you can try again.",
60
+ errorCode: "background_worker_never_started",
61
+ recoverable: true,
62
+ details:
63
+ "A background-dispatched run was acknowledged (HTTP 202) but its worker never claimed the run, so no progress was produced. The run was reaped early (it had no live worker to protect) so the turn can be retried.",
64
+ } as const;
65
+
66
+ /**
67
+ * Grace period before a never-claimed background run (dispatch_mode still
68
+ * 'background', no worker claim) is treated as a dead handoff and reaped.
69
+ *
70
+ * This is intentionally MUCH tighter than `BACKGROUND_RUN_STALE_MS` (90s). The
71
+ * wide 90s window exists ONLY to protect a CLAIMED worker whose heartbeat lags
72
+ * during a slow cold start. A run that is still `dispatch_mode = 'background'`
73
+ * has, by definition, NO worker — nothing to protect — so once a Netlify
74
+ * background function has had a reasonable cold-start window to claim it and
75
+ * hasn't, the handoff is dead and should surface promptly instead of leaving
76
+ * the user staring at a spinner for 90s. 25s comfortably exceeds a normal
77
+ * Netlify Lambda cold start while still failing fast on a silent worker death.
78
+ */
79
+ export const UNCLAIMED_BACKGROUND_RUN_GRACE_MS = 25_000;
80
+
47
81
  async function ensureRunTables(): Promise<void> {
48
82
  if (!_initPromise) {
49
83
  _initPromise = (async () => {
@@ -61,7 +95,8 @@ async function ensureRunTables(): Promise<void> {
61
95
  turn_id TEXT,
62
96
  error_code TEXT,
63
97
  error_detail TEXT,
64
- dispatch_mode TEXT
98
+ dispatch_mode TEXT,
99
+ diag_stage TEXT
65
100
  )
66
101
  `);
67
102
  // Backfill heartbeat_at on older deployments.
@@ -118,11 +153,16 @@ async function ensureRunTables(): Promise<void> {
118
153
  // normal synchronous path, "background" for a run dispatched into a
119
154
  // Netlify background function. The reaper/claim widen the stale window
120
155
  // for background rows so a slow cold-start isn't falsely reaped.
156
+ // diag_stage records the last reached pipeline stage (+ any error) for a
157
+ // background-dispatched run so a silent worker death is DIAGNOSABLE from
158
+ // the client (/runs/active surfaces it) without reading the unreadable
159
+ // Netlify background-function logs. See recordRunDiagnostic.
121
160
  for (const col of [
122
161
  "turn_id",
123
162
  "error_code",
124
163
  "error_detail",
125
164
  "dispatch_mode",
165
+ "diag_stage",
126
166
  ] as const) {
127
167
  try {
128
168
  if (isPostgres()) {
@@ -416,6 +456,72 @@ export async function setRunError(
416
456
  }
417
457
  }
418
458
 
459
+ /**
460
+ * Diagnostic stage names recorded onto a background run as it moves through the
461
+ * `_process-run` worker pipeline. Each value is the LAST stage successfully
462
+ * reached, so a stuck run's `diag_stage` reveals exactly where it died. Ordered
463
+ * roughly by execution; the literal strings are the client-readable contract.
464
+ */
465
+ export const RUN_DIAG_STAGE = {
466
+ /** The `_process-run` route handler was entered (the request reached Nitro). */
467
+ routeEntered: "route_entered",
468
+ /** HMAC auth + body validation in prepareProcessRunRequest FAILED. */
469
+ authFailed: "auth_failed",
470
+ /** HMAC auth + body validation PASSED; about to invoke the worker handler. */
471
+ authPassed: "auth_passed",
472
+ /** The re-entered agent-chat handler recognized itself as the bg worker. */
473
+ workerEntered: "worker_entered",
474
+ /** The worker won the atomic claim (it owns the run). */
475
+ workerClaimed: "worker_claimed",
476
+ /** The worker LOST the claim (a duplicate delivery already owns the run). */
477
+ workerClaimLost: "worker_claim_lost",
478
+ /** The agent loop started (startRun fired). */
479
+ workerStarted: "worker_started",
480
+ /** The worker threw before/while running the loop (message carried in detail). */
481
+ workerThrew: "worker_threw",
482
+ /** The route handler caught an error from the worker invocation. */
483
+ routeThrew: "route_threw",
484
+ } as const;
485
+
486
+ export type RunDiagStage = (typeof RUN_DIAG_STAGE)[keyof typeof RUN_DIAG_STAGE];
487
+
488
+ /**
489
+ * Record the last reached pipeline stage (+ optional short detail) for a run.
490
+ *
491
+ * PURPOSE: a Netlify background function's logs are not readable from the build
492
+ * tooling, so when its worker dies silently the run just times out with no clue
493
+ * WHY. This writes the failure stage straight onto the `agent_runs` row, which
494
+ * `/runs/active` and `listRunsForThread` surface to the client — so the next
495
+ * prod run's death cause is readable WITHOUT bg-fn logs. Cheap, additive, and
496
+ * best-effort: it must never throw or perturb the run (it is called on the auth
497
+ * path BEFORE a 401 is returned, and around the worker body).
498
+ *
499
+ * The stored value is a compact JSON `{ stage, detail?, at }` capped to 2 KB so
500
+ * a long stack can't bloat the row.
501
+ */
502
+ export async function recordRunDiagnostic(
503
+ runId: string,
504
+ stage: RunDiagStage,
505
+ detail?: string,
506
+ ): Promise<void> {
507
+ if (!runId) return;
508
+ try {
509
+ await ensureRunTables();
510
+ const client = getDbExec();
511
+ const payload = JSON.stringify({
512
+ stage,
513
+ ...(detail ? { detail: detail.slice(0, 1500) } : {}),
514
+ at: Date.now(),
515
+ }).slice(0, 2000);
516
+ await client.execute({
517
+ sql: `UPDATE agent_runs SET diag_stage = ? WHERE id = ?`,
518
+ args: [payload, runId],
519
+ });
520
+ } catch {
521
+ // Diagnostics are best-effort; never let them break the run or the route.
522
+ }
523
+ }
524
+
419
525
  /** Update the run's liveness heartbeat. Called periodically by run-manager. */
420
526
  export async function updateRunHeartbeat(runId: string): Promise<void> {
421
527
  await ensureRunTables();
@@ -490,6 +596,68 @@ export async function reapIfStale(
490
596
  return reaped;
491
597
  }
492
598
 
599
+ /**
600
+ * FALLBACK HARDENING for the "dispatched with 202 but the worker never started"
601
+ * case. A background-dispatched run sits in `dispatch_mode = 'background'` until
602
+ * the worker wins `claimBackgroundRun` (which flips it to
603
+ * `background-processing`). If the worker silently dies (e.g. the bg-fn 401s
604
+ * before it can claim), the row stays `background`, never heartbeats again, and
605
+ * — because dispatch returned 202 — the foreground already returned the SSE
606
+ * stream, so the existing fast-fail inline fallback never engaged. The run would
607
+ * otherwise hang for the full 90s background window and then error opaquely.
608
+ *
609
+ * This reaps such a run EARLY and DISTINCTLY: a row that is still unclaimed
610
+ * (`dispatch_mode = 'background'`) past the tight `UNCLAIMED_BACKGROUND_RUN_GRACE_MS`
611
+ * grace is a dead handoff — there is no live worker to protect with the wide
612
+ * window — so we flip it to `errored` with the recoverable
613
+ * `background_worker_never_started` code. The client's existing recoverable-error
614
+ * path then lets the user (or auto-recovery) re-drive the turn. Idempotent and
615
+ * conditional: only an unclaimed, still-running, grace-exceeded row matches, so a
616
+ * claimed worker, a fresh dispatch, or a terminal row is never touched.
617
+ *
618
+ * Returns true when this call reaped the run.
619
+ */
620
+ export async function reapUnclaimedBackgroundRun(
621
+ runId: string,
622
+ ): Promise<boolean> {
623
+ await ensureRunTables();
624
+ const client = getDbExec();
625
+ const completedAt = Date.now();
626
+ const cutoff = completedAt - UNCLAIMED_BACKGROUND_RUN_GRACE_MS;
627
+ const { rowsAffected } = await client.execute({
628
+ sql: `UPDATE agent_runs
629
+ SET status = 'errored',
630
+ completed_at = ?,
631
+ error_code = ?,
632
+ error_detail = ?
633
+ WHERE id = ?
634
+ AND status = 'running'
635
+ AND dispatch_mode = 'background'
636
+ AND COALESCE(heartbeat_at, started_at) < ?`,
637
+ args: [
638
+ completedAt,
639
+ UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT.errorCode,
640
+ UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT.details,
641
+ runId,
642
+ cutoff,
643
+ ],
644
+ });
645
+ const reaped = (rowsAffected ?? 0) > 0;
646
+ if (reaped) {
647
+ await recordRunDiagnostic(
648
+ runId,
649
+ RUN_DIAG_STAGE.workerThrew,
650
+ "unclaimed background dispatch reaped (worker never claimed the run)",
651
+ );
652
+ await safeAppendTerminalRunEvent(
653
+ runId,
654
+ UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT,
655
+ "reap-unclaimed-background",
656
+ );
657
+ }
658
+ return reaped;
659
+ }
660
+
493
661
  export async function updateRunStatus(
494
662
  runId: string,
495
663
  status: "completed" | "errored" | "aborted",
@@ -644,12 +812,14 @@ export async function getRunByThread(
644
812
  heartbeatAt: number | null;
645
813
  completedAt: number | null;
646
814
  lastProgressAt: number | null;
815
+ dispatchMode: string | null;
816
+ diagStage: string | null;
647
817
  } | null> {
648
818
  await ensureRunTables();
649
819
  const client = getDbExec();
650
820
  const sql = options?.includeTerminal
651
- ? `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at FROM agent_runs WHERE thread_id = ? ORDER BY started_at DESC LIMIT 1`
652
- : `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at FROM agent_runs WHERE thread_id = ? AND status = 'running' ORDER BY started_at DESC LIMIT 1`;
821
+ ? `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, dispatch_mode, diag_stage FROM agent_runs WHERE thread_id = ? ORDER BY started_at DESC LIMIT 1`
822
+ : `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, dispatch_mode, diag_stage FROM agent_runs WHERE thread_id = ? AND status = 'running' ORDER BY started_at DESC LIMIT 1`;
653
823
  const { rows } = await client.execute({ sql, args: [threadId] });
654
824
  if (rows.length === 0) return null;
655
825
  const r = rows[0] as {
@@ -661,6 +831,8 @@ export async function getRunByThread(
661
831
  heartbeat_at: number | string | null;
662
832
  completed_at: number | string | null;
663
833
  last_progress_at: number | string | null;
834
+ dispatch_mode?: string | null;
835
+ diag_stage?: string | null;
664
836
  };
665
837
  return {
666
838
  id: r.id,
@@ -672,6 +844,8 @@ export async function getRunByThread(
672
844
  completedAt: r.completed_at == null ? null : Number(r.completed_at),
673
845
  lastProgressAt:
674
846
  r.last_progress_at == null ? null : Number(r.last_progress_at),
847
+ dispatchMode: r.dispatch_mode ?? null,
848
+ diagStage: r.diag_stage ?? null,
675
849
  };
676
850
  }
677
851
 
@@ -686,6 +860,9 @@ export interface AgentRunSummary {
686
860
  lastProgressAt: number | null;
687
861
  errorCode: string | null;
688
862
  abortReason: string | null;
863
+ dispatchMode: string | null;
864
+ /** Last reached `_process-run` worker stage (JSON `{stage,detail?,at}`). */
865
+ diagStage: string | null;
689
866
  }
690
867
 
691
868
  export async function listRunsForThread(
@@ -696,7 +873,7 @@ export async function listRunsForThread(
696
873
  const limit = Math.min(Math.max(options.limit ?? 10, 1), 50);
697
874
  const client = getDbExec();
698
875
  const { rows } = await client.execute({
699
- sql: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, error_code, abort_reason
876
+ sql: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, error_code, abort_reason, dispatch_mode, diag_stage
700
877
  FROM agent_runs
701
878
  WHERE thread_id = ?
702
879
  ORDER BY started_at DESC
@@ -715,6 +892,8 @@ export async function listRunsForThread(
715
892
  last_progress_at?: number | string | null;
716
893
  error_code?: string | null;
717
894
  abort_reason?: string | null;
895
+ dispatch_mode?: string | null;
896
+ diag_stage?: string | null;
718
897
  };
719
898
  return {
720
899
  id: row.id,
@@ -728,6 +907,8 @@ export async function listRunsForThread(
728
907
  row.last_progress_at == null ? null : Number(row.last_progress_at),
729
908
  errorCode: row.error_code ?? null,
730
909
  abortReason: row.abort_reason ?? null,
910
+ dispatchMode: row.dispatch_mode ?? null,
911
+ diagStage: row.diag_stage ?? null,
731
912
  };
732
913
  });
733
914
  }
@@ -63,6 +63,22 @@ export function openAgentSidebar() {
63
63
  window.dispatchEvent(new Event("agent-panel:open"));
64
64
  }
65
65
 
66
+ export function openAgentSettings(
67
+ section?: string | { section?: string | null },
68
+ ) {
69
+ if (typeof window === "undefined") return;
70
+
71
+ const normalizedSection =
72
+ typeof section === "string" ? section : section?.section;
73
+
74
+ openAgentSidebar();
75
+ window.dispatchEvent(
76
+ new CustomEvent("agent-panel:open-settings", {
77
+ detail: normalizedSection ? { section: normalizedSection } : undefined,
78
+ }),
79
+ );
80
+ }
81
+
66
82
  export function focusAgentChat() {
67
83
  window.dispatchEvent(
68
84
  new CustomEvent("agent-panel:set-mode", {
@@ -53,6 +53,10 @@ import { TooltipProvider } from "@radix-ui/react-tooltip";
53
53
  import { Toaster } from "sonner";
54
54
  import { ClientOnly } from "./ClientOnly.js";
55
55
  import { DefaultSpinner } from "./DefaultSpinner.js";
56
+ import {
57
+ AgentNativeI18nProvider,
58
+ type AgentNativeI18nProviderProps,
59
+ } from "./i18n.js";
56
60
 
57
61
  export interface AppProvidersProps {
58
62
  /** QueryClient instance — create with `createAgentNativeQueryClient()`. */
@@ -94,6 +98,13 @@ export interface AppProvidersProps {
94
98
  */
95
99
  disableThemeTransitions?: boolean;
96
100
 
101
+ /**
102
+ * Optional localization runtime configuration. When omitted, AppProviders
103
+ * still mounts the i18n provider with an English fallback so templates can
104
+ * call useT/useLocale before they add catalogs. Pass false to opt out.
105
+ */
106
+ i18n?: Omit<AgentNativeI18nProviderProps, "children"> | false;
107
+
97
108
  /**
98
109
  * When true the providers render without a `<ClientOnly>` gate so SSR
99
110
  * streams real markup for public/unauthenticated paths.
@@ -119,6 +130,7 @@ function ProvidersInner({
119
130
  tooltipDelayDuration,
120
131
  toaster = DEFAULT_TOASTER,
121
132
  disableThemeTransitions = true,
133
+ i18n,
122
134
  children,
123
135
  }: {
124
136
  queryClient: QueryClient;
@@ -127,8 +139,18 @@ function ProvidersInner({
127
139
  tooltipDelayDuration?: number;
128
140
  toaster?: React.ReactNode | null;
129
141
  disableThemeTransitions?: boolean;
142
+ i18n?: Omit<AgentNativeI18nProviderProps, "children"> | false;
130
143
  children: React.ReactNode;
131
144
  }) {
145
+ const localizedChildren =
146
+ i18n === false ? (
147
+ children
148
+ ) : (
149
+ <AgentNativeI18nProvider {...(i18n ?? {})}>
150
+ {children}
151
+ </AgentNativeI18nProvider>
152
+ );
153
+
132
154
  return (
133
155
  <QueryClientProvider client={queryClient}>
134
156
  <ThemeProvider
@@ -138,7 +160,7 @@ function ProvidersInner({
138
160
  disableTransitionOnChange={disableThemeTransitions}
139
161
  >
140
162
  <TooltipProvider delayDuration={tooltipDelayDuration}>
141
- {children}
163
+ {localizedChildren}
142
164
  {toaster}
143
165
  </TooltipProvider>
144
166
  </ThemeProvider>
@@ -155,6 +177,7 @@ export function AppProviders({
155
177
  tooltipDelayDuration,
156
178
  toaster,
157
179
  disableThemeTransitions,
180
+ i18n,
158
181
  children,
159
182
  }: AppProvidersProps) {
160
183
  const fallback = clientOnlyFallback ?? <DefaultSpinner />;
@@ -168,6 +191,7 @@ export function AppProviders({
168
191
  tooltipDelayDuration={tooltipDelayDuration}
169
192
  toaster={toaster}
170
193
  disableThemeTransitions={disableThemeTransitions}
194
+ i18n={i18n}
171
195
  >
172
196
  {children}
173
197
  </ProvidersInner>
@@ -183,6 +207,7 @@ export function AppProviders({
183
207
  tooltipDelayDuration={tooltipDelayDuration}
184
208
  toaster={toaster}
185
209
  disableThemeTransitions={disableThemeTransitions}
210
+ i18n={i18n}
186
211
  >
187
212
  {children}
188
213
  </ProvidersInner>