@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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +48 -0
- package/corpus/core/docs/AGENTS.md +1 -0
- package/corpus/core/docs/content/client.md +15 -0
- package/corpus/core/docs/content/internationalization.md +145 -0
- package/corpus/core/docs/content/locales/ar-SA/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/de-DE/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/es-ES/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/fr-FR/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/hi-IN/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/ja-JP/internationalization.md +114 -0
- package/corpus/core/docs/content/locales/ko-KR/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/pt-BR/internationalization.md +102 -0
- package/corpus/core/docs/content/locales/zh-CN/internationalization.md +102 -0
- package/corpus/core/docs/content/skills-guide.md +19 -18
- package/corpus/core/package.json +7 -1
- package/corpus/core/src/agent/durable-background.ts +38 -2
- package/corpus/core/src/agent/production-agent.ts +54 -0
- package/corpus/core/src/agent/run-manager.ts +27 -0
- package/corpus/core/src/agent/run-store.ts +185 -4
- package/corpus/core/src/client/CommandMenu.tsx +16 -0
- package/corpus/core/src/client/app-providers.tsx +26 -1
- package/corpus/core/src/client/i18n.tsx +605 -0
- package/corpus/core/src/client/index.ts +28 -0
- package/corpus/core/src/index.browser.ts +25 -0
- package/corpus/core/src/localization/actions/get-localization-preference.ts +23 -0
- package/corpus/core/src/localization/actions/set-localization-preference.ts +30 -0
- package/corpus/core/src/localization/index.ts +2 -0
- package/corpus/core/src/localization/server.ts +131 -0
- package/corpus/core/src/localization/shared.ts +163 -0
- package/corpus/core/src/server/action-discovery.ts +8 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +71 -0
- package/corpus/core/src/server/auth.ts +13 -0
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/corpus/core/src/templates/default/.agents/skills/internationalization/SKILL.md +77 -0
- package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/core/src/templates/default/AGENTS.md +16 -15
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +29 -0
- package/corpus/core/src/templates/default/app/i18n/index.ts +13 -0
- package/corpus/core/src/templates/default/app/root.tsx +20 -1
- package/corpus/core/src/templates/default/app/routes/_index.tsx +24 -9
- package/corpus/core/src/templates/default/app/routes/settings.tsx +67 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/internationalization/SKILL.md +77 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/core/src/vite/action-types-plugin.ts +10 -0
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +12 -12
- package/corpus/templates/analytics/app/components/layout/Header.tsx +18 -15
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
- package/corpus/templates/analytics/app/components/layout/MobileNav.tsx +9 -3
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +28 -26
- package/corpus/templates/analytics/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/analytics/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/analytics/app/i18n/en-US.ts +3 -0
- package/corpus/templates/analytics/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/analytics/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/analytics/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/analytics/app/i18n/index.ts +31 -0
- package/corpus/templates/analytics/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/analytics/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/analytics/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/analytics/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/analytics/app/i18n-data.ts +433 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +50 -16
- package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +2 -2
- package/corpus/templates/analytics/app/root.tsx +14 -1
- package/corpus/templates/analytics/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/assets/app/components/layout/Header.tsx +17 -8
- package/corpus/templates/assets/app/components/layout/Layout.tsx +10 -6
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +40 -23
- package/corpus/templates/assets/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/assets/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/assets/app/i18n/en-US.ts +3 -0
- package/corpus/templates/assets/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/assets/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/assets/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/assets/app/i18n/index.ts +31 -0
- package/corpus/templates/assets/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/assets/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/assets/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/assets/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/assets/app/i18n-data.ts +668 -0
- package/corpus/templates/assets/app/root.tsx +40 -15
- package/corpus/templates/assets/app/routes/_index.tsx +9 -10
- package/corpus/templates/assets/app/routes/settings.tsx +37 -6
- package/corpus/templates/assets/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +12 -8
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +23 -12
- package/corpus/templates/brain/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/brain/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/brain/app/i18n/en-US.ts +3 -0
- package/corpus/templates/brain/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/brain/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/brain/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/brain/app/i18n/index.ts +31 -0
- package/corpus/templates/brain/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/brain/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/brain/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/brain/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/brain/app/i18n-data.ts +624 -0
- package/corpus/templates/brain/app/root.tsx +27 -12
- package/corpus/templates/brain/app/routes/knowledge.tsx +6 -6
- package/corpus/templates/brain/app/routes/ops.tsx +4 -4
- package/corpus/templates/brain/app/routes/search.tsx +4 -4
- package/corpus/templates/brain/app/routes/settings.tsx +48 -11
- package/corpus/templates/brain/app/routes/sources.tsx +3 -3
- package/corpus/templates/brain/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/brain/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +2 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +16 -9
- package/corpus/templates/calendar/app/i18n/ar-SA.ts +57 -0
- package/corpus/templates/calendar/app/i18n/de-DE.ts +58 -0
- package/corpus/templates/calendar/app/i18n/en-US.ts +57 -0
- package/corpus/templates/calendar/app/i18n/es-ES.ts +58 -0
- package/corpus/templates/calendar/app/i18n/fr-FR.ts +58 -0
- package/corpus/templates/calendar/app/i18n/hi-IN.ts +57 -0
- package/corpus/templates/calendar/app/i18n/index.ts +31 -0
- package/corpus/templates/calendar/app/i18n/ja-JP.ts +58 -0
- package/corpus/templates/calendar/app/i18n/ko-KR.ts +57 -0
- package/corpus/templates/calendar/app/i18n/pt-BR.ts +57 -0
- package/corpus/templates/calendar/app/i18n/zh-CN.ts +57 -0
- package/corpus/templates/calendar/app/i18n-data.ts +516 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +2 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +63 -28
- package/corpus/templates/calendar/app/root.tsx +78 -7
- package/corpus/templates/calendar/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/calendar/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/chat/app/components/layout/Header.tsx +17 -11
- package/corpus/templates/chat/app/components/layout/Layout.tsx +12 -8
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +47 -18
- package/corpus/templates/chat/app/i18n/ar-SA.ts +62 -0
- package/corpus/templates/chat/app/i18n/de-DE.ts +63 -0
- package/corpus/templates/chat/app/i18n/en-US.ts +63 -0
- package/corpus/templates/chat/app/i18n/es-ES.ts +63 -0
- package/corpus/templates/chat/app/i18n/fr-FR.ts +63 -0
- package/corpus/templates/chat/app/i18n/hi-IN.ts +63 -0
- package/corpus/templates/chat/app/i18n/index.ts +31 -0
- package/corpus/templates/chat/app/i18n/ja-JP.ts +63 -0
- package/corpus/templates/chat/app/i18n/ko-KR.ts +63 -0
- package/corpus/templates/chat/app/i18n/pt-BR.ts +63 -0
- package/corpus/templates/chat/app/i18n/zh-CN.ts +61 -0
- package/corpus/templates/chat/app/i18n-data.ts +475 -0
- package/corpus/templates/chat/app/root.tsx +18 -5
- package/corpus/templates/chat/app/routes/_index.tsx +9 -8
- package/corpus/templates/chat/app/routes/settings.tsx +86 -0
- package/corpus/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/clips/app/components/layout/Header.tsx +23 -17
- package/corpus/templates/clips/app/components/library/empty-state.tsx +10 -34
- package/corpus/templates/clips/app/components/library/library-layout.tsx +45 -34
- package/corpus/templates/clips/app/i18n/ar-SA.ts +182 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +184 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +179 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +183 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +184 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +180 -0
- package/corpus/templates/clips/app/i18n/index.ts +41 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +180 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +178 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +183 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +173 -0
- package/corpus/templates/clips/app/root.tsx +104 -10
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +154 -95
- package/corpus/templates/clips/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-clips-now-includes-a-language-picker-and-simplified-chinese-ui.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/content/app/components/EmptyState.tsx +7 -5
- package/corpus/templates/content/app/components/ThemeToggle.tsx +4 -2
- package/corpus/templates/content/app/components/layout/Header.tsx +2 -0
- package/corpus/templates/content/app/components/layout/Layout.tsx +8 -7
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +41 -1
- package/corpus/templates/content/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/content/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/content/app/i18n/en-US.ts +3 -0
- package/corpus/templates/content/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/content/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/content/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/content/app/i18n/index.ts +31 -0
- package/corpus/templates/content/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/content/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/content/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/content/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/content/app/i18n-data.ts +490 -0
- package/corpus/templates/content/app/root.tsx +110 -23
- package/corpus/templates/content/app/routes/_app.settings.tsx +84 -0
- package/corpus/templates/content/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/content/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/design/app/components/layout/Header.tsx +16 -8
- package/corpus/templates/design/app/components/layout/Layout.tsx +8 -6
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +25 -9
- package/corpus/templates/design/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/design/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/design/app/i18n/en-US.ts +3 -0
- package/corpus/templates/design/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/design/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/design/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/design/app/i18n/index.ts +31 -0
- package/corpus/templates/design/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/design/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/design/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/design/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +350 -0
- package/corpus/templates/design/app/root.tsx +40 -15
- package/corpus/templates/design/app/routes/settings.tsx +43 -0
- package/corpus/templates/design/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/design/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/dispatch/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/en-US.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/index.ts +31 -0
- package/corpus/templates/dispatch/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/dispatch/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +267 -0
- package/corpus/templates/dispatch/app/root.tsx +18 -4
- package/corpus/templates/dispatch/app/routes/settings.tsx +86 -0
- package/corpus/templates/dispatch/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/forms/app/components/layout/Header.tsx +16 -9
- package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -4
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +34 -17
- package/corpus/templates/forms/app/i18n/ar-SA.ts +58 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +59 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +59 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +59 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +59 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +59 -0
- package/corpus/templates/forms/app/i18n/index.ts +31 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +59 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +59 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +59 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +56 -0
- package/corpus/templates/forms/app/root.tsx +65 -16
- package/corpus/templates/forms/app/routes/_app.settings.tsx +85 -0
- package/corpus/templates/forms/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +29 -17
- package/corpus/templates/macros/app/components/layout/Header.tsx +16 -10
- package/corpus/templates/macros/app/i18n/ar-SA.ts +54 -0
- package/corpus/templates/macros/app/i18n/de-DE.ts +55 -0
- package/corpus/templates/macros/app/i18n/en-US.ts +55 -0
- package/corpus/templates/macros/app/i18n/es-ES.ts +55 -0
- package/corpus/templates/macros/app/i18n/fr-FR.ts +55 -0
- package/corpus/templates/macros/app/i18n/hi-IN.ts +55 -0
- package/corpus/templates/macros/app/i18n/index.ts +31 -0
- package/corpus/templates/macros/app/i18n/ja-JP.ts +55 -0
- package/corpus/templates/macros/app/i18n/ko-KR.ts +55 -0
- package/corpus/templates/macros/app/i18n/pt-BR.ts +55 -0
- package/corpus/templates/macros/app/i18n/zh-CN.ts +53 -0
- package/corpus/templates/macros/app/root.tsx +65 -15
- package/corpus/templates/macros/app/routes/settings.tsx +85 -0
- package/corpus/templates/macros/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/macros/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +9 -4
- package/corpus/templates/mail/app/i18n/ar-SA.ts +33 -0
- package/corpus/templates/mail/app/i18n/de-DE.ts +33 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +32 -0
- package/corpus/templates/mail/app/i18n/es-ES.ts +33 -0
- package/corpus/templates/mail/app/i18n/fr-FR.ts +33 -0
- package/corpus/templates/mail/app/i18n/hi-IN.ts +32 -0
- package/corpus/templates/mail/app/i18n/index.ts +31 -0
- package/corpus/templates/mail/app/i18n/ja-JP.ts +32 -0
- package/corpus/templates/mail/app/i18n/ko-KR.ts +33 -0
- package/corpus/templates/mail/app/i18n/pt-BR.ts +34 -0
- package/corpus/templates/mail/app/i18n/zh-CN.ts +32 -0
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +67 -13
- package/corpus/templates/mail/app/root.tsx +20 -0
- package/corpus/templates/mail/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/mail/changelog/2026-06-24-settings-now-link-directly-to-agent-settings-for-model-api-k.md +6 -0
- package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/plan/app/components/layout/Header.tsx +19 -12
- package/corpus/templates/plan/app/components/layout/Layout.tsx +11 -7
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +41 -28
- package/corpus/templates/plan/app/i18n/ar-SA.ts +77 -0
- package/corpus/templates/plan/app/i18n/de-DE.ts +78 -0
- package/corpus/templates/plan/app/i18n/en-US.ts +78 -0
- package/corpus/templates/plan/app/i18n/es-ES.ts +78 -0
- package/corpus/templates/plan/app/i18n/fr-FR.ts +78 -0
- package/corpus/templates/plan/app/i18n/hi-IN.ts +78 -0
- package/corpus/templates/plan/app/i18n/index.ts +31 -0
- package/corpus/templates/plan/app/i18n/ja-JP.ts +79 -0
- package/corpus/templates/plan/app/i18n/ko-KR.ts +77 -0
- package/corpus/templates/plan/app/i18n/pt-BR.ts +78 -0
- package/corpus/templates/plan/app/i18n/zh-CN.ts +74 -0
- package/corpus/templates/plan/app/root.tsx +45 -8
- package/corpus/templates/plan/app/routes/settings.tsx +86 -0
- package/corpus/templates/plan/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/slides/app/components/layout/Header.tsx +23 -11
- package/corpus/templates/slides/app/components/layout/Layout.tsx +7 -6
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +23 -11
- package/corpus/templates/slides/app/i18n/ar-SA.ts +54 -0
- package/corpus/templates/slides/app/i18n/de-DE.ts +56 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +55 -0
- package/corpus/templates/slides/app/i18n/es-ES.ts +55 -0
- package/corpus/templates/slides/app/i18n/fr-FR.ts +55 -0
- package/corpus/templates/slides/app/i18n/hi-IN.ts +55 -0
- package/corpus/templates/slides/app/i18n/index.ts +31 -0
- package/corpus/templates/slides/app/i18n/ja-JP.ts +55 -0
- package/corpus/templates/slides/app/i18n/ko-KR.ts +55 -0
- package/corpus/templates/slides/app/i18n/pt-BR.ts +55 -0
- package/corpus/templates/slides/app/i18n/zh-CN.ts +53 -0
- package/corpus/templates/slides/app/root.tsx +50 -8
- package/corpus/templates/slides/app/routes/settings.tsx +85 -0
- package/corpus/templates/slides/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/corpus/templates/videos/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +14 -9
- package/corpus/templates/videos/app/components/Sidebar.tsx +16 -9
- package/corpus/templates/videos/app/components/StudioHeader.tsx +12 -8
- package/corpus/templates/videos/app/components/layout/Header.tsx +21 -15
- package/corpus/templates/videos/app/components/layout/Layout.tsx +10 -7
- package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +21 -6
- package/corpus/templates/videos/app/i18n/ar-SA.ts +73 -0
- package/corpus/templates/videos/app/i18n/de-DE.ts +75 -0
- package/corpus/templates/videos/app/i18n/en-US.ts +74 -0
- package/corpus/templates/videos/app/i18n/es-ES.ts +75 -0
- package/corpus/templates/videos/app/i18n/fr-FR.ts +75 -0
- package/corpus/templates/videos/app/i18n/hi-IN.ts +74 -0
- package/corpus/templates/videos/app/i18n/index.ts +31 -0
- package/corpus/templates/videos/app/i18n/ja-JP.ts +74 -0
- package/corpus/templates/videos/app/i18n/ko-KR.ts +74 -0
- package/corpus/templates/videos/app/i18n/pt-BR.ts +75 -0
- package/corpus/templates/videos/app/i18n/zh-CN.ts +72 -0
- package/corpus/templates/videos/app/pages/Index.tsx +11 -9
- package/corpus/templates/videos/app/root.tsx +45 -7
- package/corpus/templates/videos/app/routes/settings.tsx +85 -0
- package/corpus/templates/videos/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
- package/corpus/templates/videos/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
- package/dist/agent/durable-background.d.ts +15 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +28 -2
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +34 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +8 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +18 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +99 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +155 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +3 -0
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +9 -0
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/app-providers.d.ts +8 -1
- package/dist/client/app-providers.d.ts.map +1 -1
- package/dist/client/app-providers.js +7 -5
- package/dist/client/app-providers.js.map +1 -1
- package/dist/client/i18n.d.ts +54 -0
- package/dist/client/i18n.d.ts.map +1 -0
- package/dist/client/i18n.js +354 -0
- package/dist/client/i18n.js.map +1 -0
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/localization/actions/get-localization-preference.d.ts +4 -0
- package/dist/localization/actions/get-localization-preference.d.ts.map +1 -0
- package/dist/localization/actions/get-localization-preference.js +16 -0
- package/dist/localization/actions/get-localization-preference.js.map +1 -0
- package/dist/localization/actions/set-localization-preference.d.ts +6 -0
- package/dist/localization/actions/set-localization-preference.d.ts.map +1 -0
- package/dist/localization/actions/set-localization-preference.js +24 -0
- package/dist/localization/actions/set-localization-preference.js.map +1 -0
- package/dist/localization/index.d.ts +3 -0
- package/dist/localization/index.d.ts.map +1 -0
- package/dist/localization/index.js +3 -0
- package/dist/localization/index.js.map +1 -0
- package/dist/localization/server.d.ts +25 -0
- package/dist/localization/server.d.ts.map +1 -0
- package/dist/localization/server.js +69 -0
- package/dist/localization/server.js.map +1 -0
- package/dist/localization/shared.d.ts +25 -0
- package/dist/localization/shared.d.ts.map +1 -0
- package/dist/localization/shared.js +134 -0
- package/dist/localization/shared.js.map +1 -0
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +8 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +58 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +12 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/dist/templates/default/.agents/skills/internationalization/SKILL.md +77 -0
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/dist/templates/default/AGENTS.md +16 -15
- package/dist/templates/default/app/i18n/en-US.ts +29 -0
- package/dist/templates/default/app/i18n/index.ts +13 -0
- package/dist/templates/default/app/root.tsx +20 -1
- package/dist/templates/default/app/routes/_index.tsx +24 -9
- package/dist/templates/default/app/routes/settings.tsx +67 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/internationalization/SKILL.md +77 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +8 -0
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/docs/AGENTS.md +1 -0
- package/docs/content/client.md +15 -0
- package/docs/content/internationalization.md +145 -0
- package/docs/content/locales/ar-SA/internationalization.md +102 -0
- package/docs/content/locales/de-DE/internationalization.md +102 -0
- package/docs/content/locales/es-ES/internationalization.md +102 -0
- package/docs/content/locales/fr-FR/internationalization.md +102 -0
- package/docs/content/locales/hi-IN/internationalization.md +102 -0
- package/docs/content/locales/ja-JP/internationalization.md +114 -0
- package/docs/content/locales/ko-KR/internationalization.md +102 -0
- package/docs/content/locales/pt-BR/internationalization.md +102 -0
- package/docs/content/locales/zh-CN/internationalization.md +102 -0
- package/docs/content/skills-guide.md +19 -18
- package/package.json +7 -1
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/src/templates/default/.agents/skills/internationalization/SKILL.md +77 -0
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +6 -0
- package/src/templates/default/AGENTS.md +16 -15
- package/src/templates/default/app/i18n/en-US.ts +29 -0
- package/src/templates/default/app/i18n/index.ts +13 -0
- package/src/templates/default/app/root.tsx +20 -1
- package/src/templates/default/app/routes/_index.tsx +24 -9
- package/src/templates/default/app/routes/settings.tsx +67 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/internationalization/SKILL.md +77 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +6 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineAction } from "../../action.js";
|
|
3
|
+
import { putUserSetting } from "../../settings/user-settings.js";
|
|
4
|
+
import {
|
|
5
|
+
LOCALIZATION_SETTING_KEY,
|
|
6
|
+
normalizeLocalePreference,
|
|
7
|
+
type LocalizationPreference,
|
|
8
|
+
} from "../shared.js";
|
|
9
|
+
|
|
10
|
+
export default defineAction({
|
|
11
|
+
description:
|
|
12
|
+
"Set the current user's interface language preference to 'system' or a supported BCP-47 locale.",
|
|
13
|
+
schema: z.object({
|
|
14
|
+
locale: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe("Language preference: 'system' or a supported BCP-47 locale."),
|
|
17
|
+
}),
|
|
18
|
+
run: async (args, ctx): Promise<LocalizationPreference> => {
|
|
19
|
+
if (!ctx?.userEmail) throw new Error("Not authenticated.");
|
|
20
|
+
const locale = normalizeLocalePreference(args.locale);
|
|
21
|
+
if (!locale) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"Unsupported locale. Use system, en-US, zh-CN, es-ES, fr-FR, de-DE, ja-JP, ko-KR, pt-BR, hi-IN, or ar-SA.",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const value: LocalizationPreference & Record<string, unknown> = { locale };
|
|
27
|
+
await putUserSetting(ctx.userEmail, LOCALIZATION_SETTING_KEY, value);
|
|
28
|
+
return value;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_LOCALE,
|
|
3
|
+
LOCALE_HYDRATION_GLOBAL,
|
|
4
|
+
LOCALE_STORAGE_KEY,
|
|
5
|
+
SUPPORTED_LOCALES,
|
|
6
|
+
localeDirection,
|
|
7
|
+
normalizeLocaleCode,
|
|
8
|
+
normalizeLocalizationPreference,
|
|
9
|
+
resolveLocaleFromPreference,
|
|
10
|
+
type LocaleCode,
|
|
11
|
+
type LocalePreference,
|
|
12
|
+
type LocalizationPreference,
|
|
13
|
+
} from "./shared.js";
|
|
14
|
+
|
|
15
|
+
interface RequestLike {
|
|
16
|
+
headers?: Headers | Record<string, string | string[] | undefined> | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type HeaderMap = Headers | Record<string, string | string[] | undefined>;
|
|
20
|
+
|
|
21
|
+
export interface ResolvedRequestLocale {
|
|
22
|
+
locale: LocaleCode;
|
|
23
|
+
preference: LocalizationPreference;
|
|
24
|
+
dir: "ltr" | "rtl";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ResolveLocaleFromRequestOptions {
|
|
28
|
+
request?: Request | RequestLike | null;
|
|
29
|
+
acceptLanguage?: string | null;
|
|
30
|
+
preference?: LocalizationPreference | LocalePreference | null;
|
|
31
|
+
fallback?: LocaleCode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface LocaleInitScriptOptions {
|
|
35
|
+
locale?: LocaleCode;
|
|
36
|
+
preference?: LocalizationPreference | LocalePreference;
|
|
37
|
+
messages?: Record<string, unknown> | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readHeader(
|
|
41
|
+
headers: HeaderMap | undefined | null,
|
|
42
|
+
key: string,
|
|
43
|
+
): string | null {
|
|
44
|
+
if (!headers) return null;
|
|
45
|
+
if (typeof (headers as Headers).get === "function") {
|
|
46
|
+
return (headers as Headers).get(key);
|
|
47
|
+
}
|
|
48
|
+
const lowerKey = key.toLowerCase();
|
|
49
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
50
|
+
if (name.toLowerCase() !== lowerKey) continue;
|
|
51
|
+
if (Array.isArray(value)) return value.join(",");
|
|
52
|
+
return value ?? null;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function parseAcceptLanguage(header: string | null | undefined) {
|
|
58
|
+
if (!header) return [];
|
|
59
|
+
return header
|
|
60
|
+
.split(",")
|
|
61
|
+
.map((part) => {
|
|
62
|
+
const [tag, ...params] = part.trim().split(";");
|
|
63
|
+
const q = params
|
|
64
|
+
.map((param) => param.trim())
|
|
65
|
+
.find((param) => param.startsWith("q="));
|
|
66
|
+
const weight = q ? Number.parseFloat(q.slice(2)) : 1;
|
|
67
|
+
return {
|
|
68
|
+
tag,
|
|
69
|
+
weight: Number.isFinite(weight) ? weight : 0,
|
|
70
|
+
};
|
|
71
|
+
})
|
|
72
|
+
.filter((entry) => entry.tag && entry.weight > 0)
|
|
73
|
+
.sort((a, b) => b.weight - a.weight)
|
|
74
|
+
.map((entry) => entry.tag);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function resolveLocaleFromRequest(
|
|
78
|
+
options: ResolveLocaleFromRequestOptions = {},
|
|
79
|
+
): ResolvedRequestLocale {
|
|
80
|
+
const header =
|
|
81
|
+
options.acceptLanguage ??
|
|
82
|
+
readHeader(options.request?.headers, "accept-language");
|
|
83
|
+
const candidates = parseAcceptLanguage(header);
|
|
84
|
+
const preference = normalizeLocalizationPreference(
|
|
85
|
+
options.preference ?? { locale: "system" },
|
|
86
|
+
);
|
|
87
|
+
const locale =
|
|
88
|
+
preference.locale === "system"
|
|
89
|
+
? resolveLocaleFromPreference(preference, [
|
|
90
|
+
...candidates,
|
|
91
|
+
options.fallback ?? DEFAULT_LOCALE,
|
|
92
|
+
])
|
|
93
|
+
: (normalizeLocaleCode(preference.locale) ??
|
|
94
|
+
options.fallback ??
|
|
95
|
+
DEFAULT_LOCALE);
|
|
96
|
+
return {
|
|
97
|
+
locale,
|
|
98
|
+
preference,
|
|
99
|
+
dir: localeDirection(locale),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getLocaleInitScript(options: LocaleInitScriptOptions = {}) {
|
|
104
|
+
const safeLocale = normalizeLocaleCode(options.locale) ?? DEFAULT_LOCALE;
|
|
105
|
+
const shouldStorePreference = options.preference !== undefined;
|
|
106
|
+
const safePreference = normalizeLocalizationPreference(
|
|
107
|
+
options.preference ?? { locale: "system" },
|
|
108
|
+
);
|
|
109
|
+
const payload = {
|
|
110
|
+
locale: safeLocale,
|
|
111
|
+
preference: safePreference,
|
|
112
|
+
dir: localeDirection(safeLocale),
|
|
113
|
+
messages: options.messages ?? undefined,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return `(function(){try{var supported=${JSON.stringify(
|
|
117
|
+
SUPPORTED_LOCALES,
|
|
118
|
+
)};var payload=${JSON.stringify(
|
|
119
|
+
payload,
|
|
120
|
+
)};var shouldStorePreference=${JSON.stringify(
|
|
121
|
+
shouldStorePreference,
|
|
122
|
+
)};function valid(x){return supported.indexOf(x)>=0}function canon(x){if(typeof x!=='string'||!x)return null;try{var c=Intl.getCanonicalLocales(x)[0];if(valid(c))return c;var lang=c&&c.split('-')[0].toLowerCase();for(var i=0;i<supported.length;i++){if(supported[i].split('-')[0].toLowerCase()===lang)return supported[i]}}catch(e){}return null}function storageGet(k){try{return window.localStorage.getItem(k)}catch(e){return null}}function storageSet(k,v){try{window.localStorage.setItem(k,v)}catch(e){}}var stored=storageGet(${JSON.stringify(
|
|
123
|
+
LOCALE_STORAGE_KEY,
|
|
124
|
+
)});var pref=payload.preference&&payload.preference.locale;var locale=payload.locale;if(!valid(locale)){locale=null}if(!locale&&stored&&stored!=='system'){locale=canon(stored)}if(!locale&&pref&&pref!=='system'){locale=canon(pref)}if(!locale){var langs=navigator.languages&&navigator.languages.length?navigator.languages:[navigator.language];for(var j=0;j<langs.length&&!locale;j++){locale=canon(langs[j])}}if(!locale)locale=${JSON.stringify(
|
|
125
|
+
DEFAULT_LOCALE,
|
|
126
|
+
)};var root=document.documentElement;root.setAttribute('lang',locale);root.setAttribute('dir',locale==='ar-SA'?'rtl':'ltr');root.setAttribute('data-locale',locale);payload.locale=locale;payload.dir=locale==='ar-SA'?'rtl':'ltr';window[${JSON.stringify(
|
|
127
|
+
LOCALE_HYDRATION_GLOBAL,
|
|
128
|
+
)}]=payload;if(shouldStorePreference&&pref){storageSet(${JSON.stringify(
|
|
129
|
+
LOCALE_STORAGE_KEY,
|
|
130
|
+
)},pref)}}catch(e){}})();`;
|
|
131
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export const SUPPORTED_LOCALES = [
|
|
2
|
+
"en-US",
|
|
3
|
+
"zh-CN",
|
|
4
|
+
"es-ES",
|
|
5
|
+
"fr-FR",
|
|
6
|
+
"de-DE",
|
|
7
|
+
"ja-JP",
|
|
8
|
+
"ko-KR",
|
|
9
|
+
"pt-BR",
|
|
10
|
+
"hi-IN",
|
|
11
|
+
"ar-SA",
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
export type LocaleCode = (typeof SUPPORTED_LOCALES)[number];
|
|
15
|
+
export type LocalePreference = "system" | LocaleCode;
|
|
16
|
+
|
|
17
|
+
export interface LocaleMetadata {
|
|
18
|
+
code: LocaleCode;
|
|
19
|
+
englishName: string;
|
|
20
|
+
nativeName: string;
|
|
21
|
+
dir: "ltr" | "rtl";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface LocalizationPreference {
|
|
25
|
+
locale: LocalePreference;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_LOCALE: LocaleCode = "en-US";
|
|
29
|
+
export const LOCALIZATION_SETTING_KEY = "localization";
|
|
30
|
+
export const LOCALE_STORAGE_KEY = "agent-native:locale-preference";
|
|
31
|
+
export const LOCALE_HYDRATION_GLOBAL = "__AGENT_NATIVE_LOCALE__";
|
|
32
|
+
|
|
33
|
+
export const LOCALE_METADATA: Record<LocaleCode, LocaleMetadata> = {
|
|
34
|
+
"en-US": {
|
|
35
|
+
code: "en-US",
|
|
36
|
+
englishName: "English",
|
|
37
|
+
nativeName: "English",
|
|
38
|
+
dir: "ltr",
|
|
39
|
+
},
|
|
40
|
+
"zh-CN": {
|
|
41
|
+
code: "zh-CN",
|
|
42
|
+
englishName: "Chinese (Simplified)",
|
|
43
|
+
nativeName: "简体中文",
|
|
44
|
+
dir: "ltr",
|
|
45
|
+
},
|
|
46
|
+
"es-ES": {
|
|
47
|
+
code: "es-ES",
|
|
48
|
+
englishName: "Spanish",
|
|
49
|
+
nativeName: "Español",
|
|
50
|
+
dir: "ltr",
|
|
51
|
+
},
|
|
52
|
+
"fr-FR": {
|
|
53
|
+
code: "fr-FR",
|
|
54
|
+
englishName: "French",
|
|
55
|
+
nativeName: "Français",
|
|
56
|
+
dir: "ltr",
|
|
57
|
+
},
|
|
58
|
+
"de-DE": {
|
|
59
|
+
code: "de-DE",
|
|
60
|
+
englishName: "German",
|
|
61
|
+
nativeName: "Deutsch",
|
|
62
|
+
dir: "ltr",
|
|
63
|
+
},
|
|
64
|
+
"ja-JP": {
|
|
65
|
+
code: "ja-JP",
|
|
66
|
+
englishName: "Japanese",
|
|
67
|
+
nativeName: "日本語",
|
|
68
|
+
dir: "ltr",
|
|
69
|
+
},
|
|
70
|
+
"ko-KR": {
|
|
71
|
+
code: "ko-KR",
|
|
72
|
+
englishName: "Korean",
|
|
73
|
+
nativeName: "한국어",
|
|
74
|
+
dir: "ltr",
|
|
75
|
+
},
|
|
76
|
+
"pt-BR": {
|
|
77
|
+
code: "pt-BR",
|
|
78
|
+
englishName: "Portuguese (Brazil)",
|
|
79
|
+
nativeName: "Português (Brasil)",
|
|
80
|
+
dir: "ltr",
|
|
81
|
+
},
|
|
82
|
+
"hi-IN": {
|
|
83
|
+
code: "hi-IN",
|
|
84
|
+
englishName: "Hindi",
|
|
85
|
+
nativeName: "हिन्दी",
|
|
86
|
+
dir: "ltr",
|
|
87
|
+
},
|
|
88
|
+
"ar-SA": {
|
|
89
|
+
code: "ar-SA",
|
|
90
|
+
englishName: "Arabic",
|
|
91
|
+
nativeName: "العربية",
|
|
92
|
+
dir: "rtl",
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const SUPPORTED_LOCALE_SET = new Set<string>(SUPPORTED_LOCALES);
|
|
97
|
+
|
|
98
|
+
export function isLocaleCode(value: unknown): value is LocaleCode {
|
|
99
|
+
return typeof value === "string" && SUPPORTED_LOCALE_SET.has(value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function normalizeLocaleCode(value: unknown): LocaleCode | null {
|
|
103
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
104
|
+
try {
|
|
105
|
+
for (const canonical of Intl.getCanonicalLocales(value.trim())) {
|
|
106
|
+
if (isLocaleCode(canonical)) return canonical;
|
|
107
|
+
const language = canonical.split("-")[0]?.toLowerCase();
|
|
108
|
+
const match = SUPPORTED_LOCALES.find(
|
|
109
|
+
(locale) => locale.split("-")[0]?.toLowerCase() === language,
|
|
110
|
+
);
|
|
111
|
+
if (match) return match;
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function normalizeLocalePreference(
|
|
120
|
+
value: unknown,
|
|
121
|
+
): LocalePreference | null {
|
|
122
|
+
if (value === "system") return "system";
|
|
123
|
+
return normalizeLocaleCode(value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function normalizeLocalizationPreference(
|
|
127
|
+
value: unknown,
|
|
128
|
+
): LocalizationPreference {
|
|
129
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
130
|
+
const locale = normalizeLocalePreference(
|
|
131
|
+
(value as { locale?: unknown }).locale,
|
|
132
|
+
);
|
|
133
|
+
if (locale) return { locale };
|
|
134
|
+
}
|
|
135
|
+
const locale = normalizeLocalePreference(value);
|
|
136
|
+
return { locale: locale ?? "system" };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function localeDirection(locale: LocaleCode): "ltr" | "rtl" {
|
|
140
|
+
return LOCALE_METADATA[locale]?.dir ?? "ltr";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function resolveLocaleFromCandidates(
|
|
144
|
+
candidates: Iterable<unknown>,
|
|
145
|
+
): LocaleCode {
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
const normalized = normalizeLocaleCode(candidate);
|
|
148
|
+
if (normalized) return normalized;
|
|
149
|
+
}
|
|
150
|
+
return DEFAULT_LOCALE;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function resolveLocaleFromPreference(
|
|
154
|
+
preference: LocalizationPreference | LocalePreference | unknown,
|
|
155
|
+
systemCandidates: Iterable<unknown> = [],
|
|
156
|
+
): LocaleCode {
|
|
157
|
+
const normalized =
|
|
158
|
+
typeof preference === "string"
|
|
159
|
+
? normalizeLocalePreference(preference)
|
|
160
|
+
: normalizeLocalizationPreference(preference).locale;
|
|
161
|
+
if (normalized && normalized !== "system") return normalized;
|
|
162
|
+
return resolveLocaleFromCandidates(systemCandidates);
|
|
163
|
+
}
|
|
@@ -575,6 +575,14 @@ export async function mergeCoreSharingActions(
|
|
|
575
575
|
"context-report",
|
|
576
576
|
() => import("../agent/context-xray/actions/context-report.js"),
|
|
577
577
|
],
|
|
578
|
+
[
|
|
579
|
+
"get-localization-preference",
|
|
580
|
+
() => import("../localization/actions/get-localization-preference.js"),
|
|
581
|
+
],
|
|
582
|
+
[
|
|
583
|
+
"set-localization-preference",
|
|
584
|
+
() => import("../localization/actions/set-localization-preference.js"),
|
|
585
|
+
],
|
|
578
586
|
[
|
|
579
587
|
"change-appearance",
|
|
580
588
|
() => import("../appearance/actions/change-appearance.js"),
|
|
@@ -152,6 +152,7 @@ import {
|
|
|
152
152
|
} from "../a2a/auth-policy.js";
|
|
153
153
|
import {
|
|
154
154
|
AGENT_CHAT_PROCESS_RUN_PATH,
|
|
155
|
+
extractProcessRunId,
|
|
155
156
|
prepareProcessRunRequest,
|
|
156
157
|
} from "../agent/durable-background.js";
|
|
157
158
|
import {
|
|
@@ -7174,6 +7175,14 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7174
7175
|
status: run.status,
|
|
7175
7176
|
heartbeatAt: run.heartbeatAt,
|
|
7176
7177
|
lastProgressAt: run.lastProgressAt,
|
|
7178
|
+
// Durable-background diagnostics: how the run was dispatched and
|
|
7179
|
+
// the last reached `_process-run` worker stage (JSON
|
|
7180
|
+
// `{stage,detail?,at}`). Surfaced here so a silent background
|
|
7181
|
+
// worker death is diagnosable from the client WITHOUT the
|
|
7182
|
+
// unreadable Netlify background-function logs — read
|
|
7183
|
+
// `/runs/active?threadId=...` and inspect `diagStage`.
|
|
7184
|
+
dispatchMode: run.dispatchMode ?? null,
|
|
7185
|
+
diagStage: run.diagStage ?? null,
|
|
7177
7186
|
// Server clock so the client computes "stuck" elapsed time
|
|
7178
7187
|
// server-relative, immune to client clock skew.
|
|
7179
7188
|
serverNow: Date.now(),
|
|
@@ -7791,6 +7800,19 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7791
7800
|
setResponseStatus(event, 405);
|
|
7792
7801
|
return { error: "Method not allowed" };
|
|
7793
7802
|
}
|
|
7803
|
+
// DIAGNOSTIC: load the run-store diagnostic recorder. Each stage we
|
|
7804
|
+
// reach is written onto the run row (diag_stage) so a silent failure
|
|
7805
|
+
// INSIDE the Netlify background function — whose logs we cannot read —
|
|
7806
|
+
// is still diagnosable from the client via /runs/active. Best-effort:
|
|
7807
|
+
// the import + every record call is wrapped so diagnostics can never
|
|
7808
|
+
// break the worker path.
|
|
7809
|
+
const diag = await import("../agent/run-store.js")
|
|
7810
|
+
.then((m) => ({
|
|
7811
|
+
record: m.recordRunDiagnostic,
|
|
7812
|
+
stages: m.RUN_DIAG_STAGE,
|
|
7813
|
+
}))
|
|
7814
|
+
.catch(() => null);
|
|
7815
|
+
|
|
7794
7816
|
// Consume the body ONCE (h3 v2's web Request stream is single-use).
|
|
7795
7817
|
let processBody: any;
|
|
7796
7818
|
try {
|
|
@@ -7800,6 +7822,18 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7800
7822
|
return { error: "Invalid request body" };
|
|
7801
7823
|
}
|
|
7802
7824
|
|
|
7825
|
+
// Record "the route handler was entered" against the run BEFORE auth
|
|
7826
|
+
// runs. This is the proof the bg-fn invocation actually reached Nitro
|
|
7827
|
+
// (vs. dying at the function entry / never being invoked). The runId
|
|
7828
|
+
// is parsed without authenticating so we can attach it even on a
|
|
7829
|
+
// subsequent auth failure.
|
|
7830
|
+
const diagRunId = extractProcessRunId(processBody);
|
|
7831
|
+
if (diag && diagRunId) {
|
|
7832
|
+
await diag
|
|
7833
|
+
.record(diagRunId, diag.stages.routeEntered)
|
|
7834
|
+
.catch(() => {});
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7803
7837
|
// Validate + HMAC-authenticate the self-dispatch and prepare the
|
|
7804
7838
|
// background-worker body. Pure decision (unit-tested in
|
|
7805
7839
|
// durable-background.spec.ts); the route only wires it to h3.
|
|
@@ -7808,10 +7842,36 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7808
7842
|
getHeader(event, "authorization"),
|
|
7809
7843
|
);
|
|
7810
7844
|
if (!prepared.ok) {
|
|
7845
|
+
// DIAGNOSTIC: record the auth/validation failure ONTO the run
|
|
7846
|
+
// before returning the error status. Without this, a 401 (e.g.
|
|
7847
|
+
// A2A_SECRET missing/mismatched in the bg-fn env, or the path not
|
|
7848
|
+
// bypassing session auth) inside the unreadable bg function would
|
|
7849
|
+
// leave the run to time out with NO clue. The detail carries the
|
|
7850
|
+
// status + whether A2A_SECRET is even present in this isolate.
|
|
7851
|
+
if (diag && prepared.runId) {
|
|
7852
|
+
const a2aPresent = Boolean(
|
|
7853
|
+
process.env.A2A_SECRET && process.env.A2A_SECRET.length > 0,
|
|
7854
|
+
);
|
|
7855
|
+
await diag
|
|
7856
|
+
.record(
|
|
7857
|
+
prepared.runId,
|
|
7858
|
+
diag.stages.authFailed,
|
|
7859
|
+
`status=${prepared.status} error=${prepared.error} a2aSecretPresent=${a2aPresent}`,
|
|
7860
|
+
)
|
|
7861
|
+
.catch(() => {});
|
|
7862
|
+
}
|
|
7811
7863
|
setResponseStatus(event, prepared.status);
|
|
7812
7864
|
return { error: prepared.error };
|
|
7813
7865
|
}
|
|
7814
7866
|
|
|
7867
|
+
// DIAGNOSTIC: auth + body validation passed. Reaching here proves the
|
|
7868
|
+
// request was authenticated and we are about to invoke the worker.
|
|
7869
|
+
if (diag) {
|
|
7870
|
+
await diag
|
|
7871
|
+
.record(prepared.runId, diag.stages.authPassed)
|
|
7872
|
+
.catch(() => {});
|
|
7873
|
+
}
|
|
7874
|
+
|
|
7815
7875
|
// Stash the verified+augmented body for the handler — the body stream
|
|
7816
7876
|
// is already consumed, so the handler reads this instead.
|
|
7817
7877
|
(event as any).context = (event as any).context ?? {};
|
|
@@ -7821,6 +7881,17 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7821
7881
|
return await invokeAgentChatHandler(event);
|
|
7822
7882
|
} catch (err: any) {
|
|
7823
7883
|
console.error("[agent-chat] _process-run failed:", err);
|
|
7884
|
+
// DIAGNOSTIC: the worker invocation threw at the route boundary —
|
|
7885
|
+
// record the message so the failure cause is readable client-side.
|
|
7886
|
+
if (diag) {
|
|
7887
|
+
await diag
|
|
7888
|
+
.record(
|
|
7889
|
+
prepared.runId,
|
|
7890
|
+
diag.stages.routeThrew,
|
|
7891
|
+
err instanceof Error ? err.message : String(err),
|
|
7892
|
+
)
|
|
7893
|
+
.catch(() => {});
|
|
7894
|
+
}
|
|
7824
7895
|
setResponseStatus(event, 500);
|
|
7825
7896
|
return { error: "process-run failed" };
|
|
7826
7897
|
}
|
|
@@ -1627,6 +1627,19 @@ function createAuthGuardFn(): (
|
|
|
1627
1627
|
return;
|
|
1628
1628
|
}
|
|
1629
1629
|
|
|
1630
|
+
// Durable-background AGENT-CHAT processor. The foreground POST self-dispatches
|
|
1631
|
+
// a long chat turn here (through the Netlify `-background` function, which
|
|
1632
|
+
// rewrites its default url to this path); the route HMAC-verifies the
|
|
1633
|
+
// dispatch (same internal-token scheme as agent-teams above) plus an atomic
|
|
1634
|
+
// SQL claim. The self-dispatch carries ONLY a Bearer HMAC token and NO
|
|
1635
|
+
// session cookie, so without this bypass the blanket 401-for-/_agent-native/*
|
|
1636
|
+
// gate below blocks the worker before `prepareProcessRunRequest` ever runs —
|
|
1637
|
+
// the run is never claimed, its heartbeat never starts, and it times out with
|
|
1638
|
+
// no visible progress. Exact path only (mirrors agent-teams).
|
|
1639
|
+
if (p === "/_agent-native/agent-chat/_process-run") {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1630
1643
|
// Read-only agent chat share links. The random token is the bearer secret;
|
|
1631
1644
|
// the route returns a sanitized transcript plus bounded run summaries and
|
|
1632
1645
|
// exposes no write surface, live event stream, tool payloads, or owner APIs.
|
|
@@ -78,6 +78,14 @@ export {
|
|
|
78
78
|
} from "./identity-sso.js";
|
|
79
79
|
export { requireEnvKey, type MissingKeyResponse } from "./missing-key.js";
|
|
80
80
|
export { verifyCaptcha, type CaptchaVerifyResult } from "./captcha.js";
|
|
81
|
+
export {
|
|
82
|
+
getLocaleInitScript,
|
|
83
|
+
parseAcceptLanguage,
|
|
84
|
+
resolveLocaleFromRequest,
|
|
85
|
+
type LocaleInitScriptOptions,
|
|
86
|
+
type ResolveLocaleFromRequestOptions,
|
|
87
|
+
type ResolvedRequestLocale,
|
|
88
|
+
} from "../localization/server.js";
|
|
81
89
|
export {
|
|
82
90
|
createProductionAgentHandler,
|
|
83
91
|
type ActionEntry,
|
|
@@ -86,6 +86,10 @@ Instruction examples may name secret keys like `SLACK_WEBHOOK`, but must use
|
|
|
86
86
|
placeholders such as `${keys.SLACK_WEBHOOK}` or `<SLACK_WEBHOOK>`. Do not paste
|
|
87
87
|
real keys, internal data, or customer data into instructions as examples.
|
|
88
88
|
|
|
89
|
+
If the feature adds or changes visible UI copy, prompts, toasts, labels, empty
|
|
90
|
+
states, or formatting, read `internationalization` and update the app's i18n
|
|
91
|
+
catalogs in the same change.
|
|
92
|
+
|
|
89
93
|
For app-backed skills, declare skill visibility in the app-skill manifest:
|
|
90
94
|
|
|
91
95
|
- `internal` — only the app's own agents should use it.
|
|
@@ -177,3 +181,4 @@ TL;DR: spread `ownableColumns()` into the resource table, pair it with `createSh
|
|
|
177
181
|
- **create-skill** — How to create skills for new patterns (area 3 in detail)
|
|
178
182
|
- **storing-data** — Where to store the feature's data
|
|
179
183
|
- **real-time-sync** — How the UI stays in sync when the agent writes data
|
|
184
|
+
- **internationalization** — How to update localized UI copy and catalogs
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: internationalization
|
|
3
|
+
description: >-
|
|
4
|
+
How to add or edit localized UI copy in agent-native apps. Use when adding,
|
|
5
|
+
removing, or changing user-visible interface text, prompts, toasts, labels,
|
|
6
|
+
empty states, or date/number/list formatting.
|
|
7
|
+
scope: dev
|
|
8
|
+
metadata:
|
|
9
|
+
internal: true
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Internationalization
|
|
13
|
+
|
|
14
|
+
## Rule
|
|
15
|
+
|
|
16
|
+
Visible framework/template UI copy belongs in the app's i18n catalog, not
|
|
17
|
+
inline in components. When you add or edit UI text, update the English source
|
|
18
|
+
catalog first, update existing locale catalogs, and run the i18n guard.
|
|
19
|
+
|
|
20
|
+
## Catalogs
|
|
21
|
+
|
|
22
|
+
Templates use `app/i18n/`:
|
|
23
|
+
|
|
24
|
+
- `en-US.ts` is the canonical source tree and fallback.
|
|
25
|
+
- Other locale files keep the same non-plural keys and the same placeholders.
|
|
26
|
+
- `index.ts` exports an `AgentNativeI18nCatalog` with English bundled and
|
|
27
|
+
non-English catalogs loaded by dynamic import.
|
|
28
|
+
|
|
29
|
+
Use BCP-47 filenames from the supported set: `en-US`, `zh-CN`, `es-ES`,
|
|
30
|
+
`fr-FR`, `de-DE`, `ja-JP`, `ko-KR`, `pt-BR`, `hi-IN`, `ar-SA`.
|
|
31
|
+
|
|
32
|
+
## UI Pattern
|
|
33
|
+
|
|
34
|
+
- Wrap apps with `AppProviders i18n={{ catalog: i18nCatalog }}`.
|
|
35
|
+
- Read strings with `useT()` and keep keys stable.
|
|
36
|
+
- Sidebar apps should expose a `/settings` route in the app sidebar. Put
|
|
37
|
+
`<LanguagePicker />` in that settings page, usually in a Language or General
|
|
38
|
+
section, and keep the header language icon only as a quick-access shortcut.
|
|
39
|
+
- Settings pages should include an "Agent settings" row/card that calls
|
|
40
|
+
`openAgentSettings()` from `@agent-native/core/client` to open the right
|
|
41
|
+
agent sidebar's Settings tab. Localize the title, description, and button.
|
|
42
|
+
- Use `useFormatters()` for dates, numbers, relative time, and lists instead
|
|
43
|
+
of embedding formatted values in translation strings.
|
|
44
|
+
- Do not translate stable identifiers: action names, route names, enum values,
|
|
45
|
+
app-state keys, database values, protocol fields, env var names, or provider
|
|
46
|
+
names.
|
|
47
|
+
|
|
48
|
+
## Plurals And Placeholders
|
|
49
|
+
|
|
50
|
+
Plural strings use i18next/CLDR suffixes. Do not force every locale to copy
|
|
51
|
+
English plural categories:
|
|
52
|
+
|
|
53
|
+
- English uses `_one` and `_other`.
|
|
54
|
+
- Chinese and Japanese usually use `_other`.
|
|
55
|
+
- Arabic uses `_zero`, `_one`, `_two`, `_few`, `_many`, and `_other`.
|
|
56
|
+
|
|
57
|
+
Keep interpolation placeholders identical across locales, such as
|
|
58
|
+
`{{count}}`, `{{name}}`, or ICU-style `{count}` arguments.
|
|
59
|
+
|
|
60
|
+
## RTL
|
|
61
|
+
|
|
62
|
+
New UI should be RTL-safe. Prefer logical CSS utilities/properties such as
|
|
63
|
+
`ms-*`, `me-*`, `ps-*`, `pe-*`, `start-*`, `end-*`, `text-start`, and
|
|
64
|
+
`text-end` when direction matters. Avoid hardcoded left/right positioning for
|
|
65
|
+
new localized UI unless it is genuinely physical.
|
|
66
|
+
|
|
67
|
+
## Verification
|
|
68
|
+
|
|
69
|
+
Run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm guard:i18n-catalogs
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For broader changes, also run the affected template tests and `pnpm typecheck`.
|
|
76
|
+
Machine translation is only a starting point; high-visibility strings need
|
|
77
|
+
human review.
|
|
@@ -66,6 +66,11 @@ el.dataset.selectedId = selectedItem?.id || "";
|
|
|
66
66
|
|
|
67
67
|
**Use configuration-driven rendering** — Extract visual decisions (colors, layouts, sizes) into JSON config files in `data/`. The agent can modify the config (Tier 1) instead of the component source (Tier 2).
|
|
68
68
|
|
|
69
|
+
**Keep localized copy in catalogs** — When editing visible UI copy, labels,
|
|
70
|
+
toasts, empty states, prompts, or formatting, read `internationalization` and
|
|
71
|
+
update `app/i18n/en-US.ts` plus existing locale catalogs instead of leaving new
|
|
72
|
+
inline strings in components.
|
|
73
|
+
|
|
69
74
|
## Don't
|
|
70
75
|
|
|
71
76
|
- Don't modify `.env` files or files containing secrets
|
|
@@ -80,3 +85,4 @@ el.dataset.selectedId = selectedItem?.id || "";
|
|
|
80
85
|
- **actions** — The agent can create or modify actions to add new capabilities
|
|
81
86
|
- **delegate-to-agent** — Self-modification requests come through the agent chat
|
|
82
87
|
- **real-time-sync** — Database writes trigger change events to update the UI
|
|
88
|
+
- **internationalization** — UI copy, language catalogs, locale switching, and RTL-safe edits
|
|
@@ -143,21 +143,22 @@ repackage, or proxy an action.
|
|
|
143
143
|
|
|
144
144
|
Skills in `.agents/skills/` provide detailed guidance for each architectural rule. Read them before making changes.
|
|
145
145
|
|
|
146
|
-
| Skill
|
|
147
|
-
|
|
|
148
|
-
| `agent-native-docs`
|
|
149
|
-
| `adding-a-feature`
|
|
150
|
-
| `real-time-sync`
|
|
151
|
-
| `storing-data`
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
146
|
+
| Skill | When to read |
|
|
147
|
+
| ---------------------- | --------------------------------------------------------------------------------- |
|
|
148
|
+
| `agent-native-docs` | Before using advanced Agent Native framework APIs or generated-app features |
|
|
149
|
+
| `adding-a-feature` | **Read first when adding ANY new feature** — the four-area parity checklist |
|
|
150
|
+
| `real-time-sync` | Before wiring data fetching for anything the agent can mutate (must auto-refresh) |
|
|
151
|
+
| `storing-data` | Before storing or reading any app state |
|
|
152
|
+
| `internationalization` | Before adding or editing visible UI copy, prompts, toasts, labels, or formatting |
|
|
153
|
+
| `delegate-to-agent` | Before adding LLM calls or AI delegation |
|
|
154
|
+
| `actions` | Before creating or modifying actions |
|
|
155
|
+
| `self-modifying-code` | Before editing source, components, or styles |
|
|
156
|
+
| `capture-learnings` | Before recording user preferences or corrections |
|
|
157
|
+
| `frontend-design` | Before building or restyling any UI component, page, or layout |
|
|
158
|
+
| `shadcn-ui` | Before adding, updating, or debugging shadcn/ui components |
|
|
159
|
+
| `agent-engines` | Before switching LLM providers or registering a custom engine |
|
|
160
|
+
| `notifications` | Before surfacing alerts/progress to the user or adding channels |
|
|
161
|
+
| `progress` | Before running any task that takes more than a few seconds |
|
|
161
162
|
|
|
162
163
|
## When Adding Features
|
|
163
164
|
|