@agent-native/core 0.99.3 → 0.100.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 +21 -0
- package/corpus/core/docs/content/agent-surfaces.mdx +43 -0
- package/corpus/core/docs/content/external-agents.mdx +11 -0
- package/corpus/core/docs/content/locales/ar-SA/agent-surfaces.mdx +30 -0
- package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +9 -0
- package/corpus/core/docs/content/locales/de-DE/agent-surfaces.mdx +34 -0
- package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +10 -0
- package/corpus/core/docs/content/locales/es-ES/agent-surfaces.mdx +34 -0
- package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +10 -0
- package/corpus/core/docs/content/locales/fr-FR/agent-surfaces.mdx +34 -0
- package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +11 -0
- package/corpus/core/docs/content/locales/hi-IN/agent-surfaces.mdx +31 -0
- package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +9 -0
- package/corpus/core/docs/content/locales/ja-JP/agent-surfaces.mdx +32 -0
- package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +10 -0
- package/corpus/core/docs/content/locales/ko-KR/agent-surfaces.mdx +32 -0
- package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +9 -0
- package/corpus/core/docs/content/locales/pt-BR/agent-surfaces.mdx +33 -0
- package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +10 -0
- package/corpus/core/docs/content/locales/zh-CN/agent-surfaces.mdx +29 -0
- package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-TW/agent-surfaces.mdx +29 -0
- package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +8 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/context-xray/actions/context-evict.ts +11 -0
- package/corpus/core/src/agent/context-xray/actions/context-pin.ts +11 -0
- package/corpus/core/src/agent/context-xray/actions/context-preview-get.ts +134 -0
- package/corpus/core/src/agent/context-xray/actions/context-restore.ts +11 -0
- package/corpus/core/src/agent/context-xray/actions/errors.ts +20 -0
- package/corpus/core/src/agent/context-xray/manifest.ts +106 -7
- package/corpus/core/src/agent/engine/context-directives-transform.ts +5 -1
- package/corpus/core/src/agent/production-agent.ts +100 -38
- package/corpus/core/src/agent/run-loop-with-resume.ts +7 -2
- package/corpus/core/src/client/AgentPanel.tsx +35 -8
- package/corpus/core/src/client/agent-page/AgentContextTab.tsx +501 -0
- package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +370 -0
- package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +1084 -0
- package/corpus/core/src/client/agent-page/types.ts +18 -0
- package/corpus/core/src/client/agent-page/use-jobs.ts +150 -0
- package/corpus/core/src/client/context-xray/ContextMeter.tsx +22 -5
- package/corpus/core/src/client/context-xray/ContextTreemap.tsx +16 -1
- package/corpus/core/src/client/context-xray/ContextXRayPanel.tsx +168 -3
- package/corpus/core/src/client/context-xray/format.ts +6 -0
- package/corpus/core/src/client/index.ts +6 -0
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +51 -23
- package/corpus/core/src/client/settings/AutomationsSection.tsx +407 -387
- package/corpus/core/src/jobs/actions/list-recurring-jobs.ts +105 -0
- package/corpus/core/src/jobs/actions/manage-recurring-job.ts +85 -0
- package/corpus/core/src/jobs/tools.ts +1 -1
- package/corpus/core/src/localization/default-messages.ts +110 -0
- package/corpus/core/src/mcp/build-server.ts +1 -0
- package/corpus/core/src/mcp/connect-route.ts +62 -65
- package/corpus/core/src/server/action-discovery.ts +23 -0
- package/corpus/core/src/server/agent-chat/prompt-resources.ts +174 -2
- package/corpus/core/src/server/agent-chat-plugin.ts +175 -2
- package/corpus/core/src/server/auth.ts +1 -7
- package/corpus/core/src/server/google-oauth.ts +1 -7
- package/corpus/core/src/server/workspace-oauth.ts +31 -0
- package/corpus/core/src/shared/context-xray.ts +84 -1
- package/corpus/core/src/shared/mcp-connect-content.ts +130 -0
- package/corpus/core/src/templates/default/app/routes/_index.tsx +11 -0
- package/corpus/core/src/templates/default/app/routes/agent.tsx +33 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +59 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/context-xray/SKILL.md +25 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +9 -0
- package/corpus/core/src/triggers/actions/list-automations.ts +99 -0
- package/corpus/core/src/triggers/actions/manage-automation.ts +78 -0
- package/corpus/templates/analytics/actions/data-source-status.ts +33 -15
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +149 -24
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +17 -0
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +8 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/analytics/app/i18n-data.ts +1 -0
- package/corpus/templates/analytics/app/routes/agent.tsx +5 -0
- package/corpus/templates/analytics/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-analytics-answers-can-use-built-in-first-party-event-data.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-chart-legends-can-filter-to-one-series-or-hide-a-series-on-h.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-gpt-5-6-luna-answers-now-recover-normally-instead-of-showing.md +6 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
- package/corpus/templates/assets/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/assets/app/routes/agent.tsx +5 -0
- package/corpus/templates/assets/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +4 -1
- package/corpus/templates/brain/app/lib/brain.ts +11 -0
- package/corpus/templates/brain/app/root.tsx +15 -1
- package/corpus/templates/brain/app/routes/agent.tsx +11 -0
- package/corpus/templates/brain/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/templates/calendar/AGENTS.md +9 -0
- package/corpus/templates/calendar/actions/list-events.ts +659 -44
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/calendar/app/root.tsx +17 -1
- package/corpus/templates/calendar/app/routes/_app.agent.tsx +25 -0
- package/corpus/templates/calendar/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-13-calendar-inventory-reads-report-source-coverage.md +6 -0
- package/corpus/templates/calendar/server/lib/calendar-connector-catalog.ts +8 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +173 -51
- package/corpus/templates/calendar/server/lib/ical-fetcher.ts +13 -2
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +5 -3
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/corpus/templates/chat/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +7 -0
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/chat/app/root.tsx +24 -2
- package/corpus/templates/chat/app/routes/agent.tsx +15 -0
- package/corpus/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +8 -0
- package/corpus/templates/clips/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +2 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/clips/app/root.tsx +7 -1
- package/corpus/templates/clips/app/routes/_app.agent.tsx +15 -0
- package/corpus/templates/clips/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/content/app/i18n-data.ts +44 -6
- package/corpus/templates/content/app/root.tsx +8 -0
- package/corpus/templates/content/app/routes/_app.agent.tsx +15 -0
- package/corpus/templates/content/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/design/app/i18n-data.ts +20 -0
- package/corpus/templates/design/app/root.tsx +7 -1
- package/corpus/templates/design/app/routes/agent.tsx +15 -0
- package/corpus/templates/design/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +10 -1
- package/corpus/templates/dispatch/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
- package/corpus/templates/dispatch/app/root.tsx +7 -2
- package/corpus/templates/dispatch/app/routes/agent.tsx +18 -0
- package/corpus/templates/dispatch/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +35 -0
- package/corpus/templates/forms/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/forms/app/root.tsx +6 -1
- package/corpus/templates/forms/app/routes/_app.agent.tsx +15 -0
- package/corpus/templates/forms/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +14 -2
- package/corpus/templates/macros/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/macros/app/root.tsx +17 -2
- package/corpus/templates/macros/app/routes/agent.tsx +15 -0
- package/corpus/templates/macros/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/mail/AGENTS.md +12 -0
- package/corpus/templates/mail/actions/list-emails.ts +486 -4
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +42 -0
- package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +6 -0
- package/corpus/templates/mail/app/routes/agent.tsx +15 -0
- package/corpus/templates/mail/changelog/2026-07-13-coverage-aware-connected-inbox-inventory.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/mail/server/db/schema.ts +17 -0
- package/corpus/templates/mail/server/lib/google-auth.ts +33 -5
- package/corpus/templates/mail/server/lib/inventory-cursor.ts +406 -0
- package/corpus/templates/mail/server/lib/list-inbox-emails.ts +21 -2
- package/corpus/templates/mail/server/lib/mail-connector-catalog.ts +8 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +4 -9
- package/corpus/templates/mail/server/plugins/db.ts +20 -0
- package/corpus/templates/plan/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/plan/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/plan/app/root.tsx +8 -0
- package/corpus/templates/plan/app/routes/agent.tsx +15 -0
- package/corpus/templates/plan/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/corpus/templates/slides/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/slides/app/root.tsx +10 -1
- package/corpus/templates/slides/app/routes/agent.tsx +15 -0
- package/corpus/templates/slides/changelog/2026-07-13-new-agent-page-see-and-manage-your-agent-s-context-files-con.md +6 -0
- package/dist/agent/context-xray/actions/context-evict.js +5 -2
- package/dist/agent/context-xray/actions/context-evict.js.map +1 -1
- package/dist/agent/context-xray/actions/context-pin.js +5 -2
- package/dist/agent/context-xray/actions/context-pin.js.map +1 -1
- package/dist/agent/context-xray/actions/context-preview-get.d.ts +17 -0
- package/dist/agent/context-xray/actions/context-preview-get.d.ts.map +1 -0
- package/dist/agent/context-xray/actions/context-preview-get.js +94 -0
- package/dist/agent/context-xray/actions/context-preview-get.js.map +1 -0
- package/dist/agent/context-xray/actions/context-restore.js +5 -2
- package/dist/agent/context-xray/actions/context-restore.js.map +1 -1
- package/dist/agent/context-xray/actions/errors.d.ts +3 -0
- package/dist/agent/context-xray/actions/errors.d.ts.map +1 -1
- package/dist/agent/context-xray/actions/errors.js +6 -0
- package/dist/agent/context-xray/actions/errors.js.map +1 -1
- package/dist/agent/context-xray/manifest.d.ts +18 -1
- package/dist/agent/context-xray/manifest.d.ts.map +1 -1
- package/dist/agent/context-xray/manifest.js +68 -6
- package/dist/agent/context-xray/manifest.js.map +1 -1
- package/dist/agent/engine/context-directives-transform.d.ts +2 -0
- package/dist/agent/engine/context-directives-transform.d.ts.map +1 -1
- package/dist/agent/engine/context-directives-transform.js +2 -1
- package/dist/agent/engine/context-directives-transform.js.map +1 -1
- package/dist/agent/production-agent.d.ts +15 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +74 -36
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +6 -3
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +9 -11
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/agent-page/AgentContextTab.d.ts +5 -0
- package/dist/client/agent-page/AgentContextTab.d.ts.map +1 -0
- package/dist/client/agent-page/AgentContextTab.js +202 -0
- package/dist/client/agent-page/AgentContextTab.js.map +1 -0
- package/dist/client/agent-page/AgentJobsTab.d.ts +3 -0
- package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -0
- package/dist/client/agent-page/AgentJobsTab.js +98 -0
- package/dist/client/agent-page/AgentJobsTab.js.map +1 -0
- package/dist/client/agent-page/AgentTabsPage.d.ts +20 -0
- package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -0
- package/dist/client/agent-page/AgentTabsPage.js +420 -0
- package/dist/client/agent-page/AgentTabsPage.js.map +1 -0
- package/dist/client/agent-page/types.d.ts +17 -0
- package/dist/client/agent-page/types.d.ts.map +1 -0
- package/dist/client/agent-page/types.js +9 -0
- package/dist/client/agent-page/types.js.map +1 -0
- package/dist/client/agent-page/use-jobs.d.ts +61 -0
- package/dist/client/agent-page/use-jobs.d.ts.map +1 -0
- package/dist/client/agent-page/use-jobs.js +71 -0
- package/dist/client/agent-page/use-jobs.js.map +1 -0
- package/dist/client/context-xray/ContextMeter.d.ts +3 -1
- package/dist/client/context-xray/ContextMeter.d.ts.map +1 -1
- package/dist/client/context-xray/ContextMeter.js +14 -5
- package/dist/client/context-xray/ContextMeter.js.map +1 -1
- package/dist/client/context-xray/ContextTreemap.d.ts +3 -2
- package/dist/client/context-xray/ContextTreemap.d.ts.map +1 -1
- package/dist/client/context-xray/ContextTreemap.js +9 -1
- package/dist/client/context-xray/ContextTreemap.js.map +1 -1
- package/dist/client/context-xray/ContextXRayPanel.d.ts.map +1 -1
- package/dist/client/context-xray/ContextXRayPanel.js +95 -20
- package/dist/client/context-xray/ContextXRayPanel.js.map +1 -1
- package/dist/client/context-xray/format.d.ts.map +1 -1
- package/dist/client/context-xray/format.js +12 -0
- package/dist/client/context-xray/format.js.map +1 -1
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts +9 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +26 -10
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/settings/AutomationsSection.d.ts +9 -0
- package/dist/client/settings/AutomationsSection.d.ts.map +1 -1
- package/dist/client/settings/AutomationsSection.js +128 -147
- package/dist/client/settings/AutomationsSection.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/jobs/actions/list-recurring-jobs.d.ts +21 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -0
- package/dist/jobs/actions/list-recurring-jobs.js +74 -0
- package/dist/jobs/actions/list-recurring-jobs.js.map +1 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +18 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts.map +1 -0
- package/dist/jobs/actions/manage-recurring-job.js +68 -0
- package/dist/jobs/actions/manage-recurring-job.js.map +1 -0
- package/dist/jobs/tools.d.ts +13 -0
- package/dist/jobs/tools.d.ts.map +1 -1
- package/dist/jobs/tools.js +1 -1
- package/dist/jobs/tools.js.map +1 -1
- package/dist/localization/default-messages.d.ts +96 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +96 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +1 -0
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +44 -59
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +23 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat/prompt-resources.d.ts +14 -0
- package/dist/server/agent-chat/prompt-resources.d.ts.map +1 -1
- package/dist/server/agent-chat/prompt-resources.js +139 -2
- package/dist/server/agent-chat/prompt-resources.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +1 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +136 -3
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +1 -4
- package/dist/server/auth.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +1 -4
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/workspace-oauth.d.ts +11 -0
- package/dist/server/workspace-oauth.d.ts.map +1 -0
- package/dist/server/workspace-oauth.js +25 -0
- package/dist/server/workspace-oauth.js.map +1 -0
- package/dist/shared/context-xray.d.ts +45 -1
- package/dist/shared/context-xray.d.ts.map +1 -1
- package/dist/shared/context-xray.js +12 -0
- package/dist/shared/context-xray.js.map +1 -1
- package/dist/shared/mcp-connect-content.d.ts +37 -0
- package/dist/shared/mcp-connect-content.d.ts.map +1 -0
- package/dist/shared/mcp-connect-content.js +92 -0
- package/dist/shared/mcp-connect-content.js.map +1 -0
- package/dist/templates/default/app/routes/_index.tsx +11 -0
- package/dist/templates/default/app/routes/agent.tsx +33 -0
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/agent-page/SKILL.md +59 -0
- package/dist/templates/workspace-core/.agents/skills/context-xray/SKILL.md +25 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -0
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +9 -0
- package/dist/triggers/actions/list-automations.d.ts +24 -0
- package/dist/triggers/actions/list-automations.d.ts.map +1 -0
- package/dist/triggers/actions/list-automations.js +72 -0
- package/dist/triggers/actions/list-automations.js.map +1 -0
- package/dist/triggers/actions/manage-automation.d.ts +18 -0
- package/dist/triggers/actions/manage-automation.d.ts.map +1 -0
- package/dist/triggers/actions/manage-automation.js +60 -0
- package/dist/triggers/actions/manage-automation.js.map +1 -0
- package/docs/content/agent-surfaces.mdx +43 -0
- package/docs/content/external-agents.mdx +11 -0
- package/docs/content/locales/ar-SA/agent-surfaces.mdx +30 -0
- package/docs/content/locales/ar-SA/external-agents.mdx +9 -0
- package/docs/content/locales/de-DE/agent-surfaces.mdx +34 -0
- package/docs/content/locales/de-DE/external-agents.mdx +10 -0
- package/docs/content/locales/es-ES/agent-surfaces.mdx +34 -0
- package/docs/content/locales/es-ES/external-agents.mdx +10 -0
- package/docs/content/locales/fr-FR/agent-surfaces.mdx +34 -0
- package/docs/content/locales/fr-FR/external-agents.mdx +11 -0
- package/docs/content/locales/hi-IN/agent-surfaces.mdx +31 -0
- package/docs/content/locales/hi-IN/external-agents.mdx +9 -0
- package/docs/content/locales/ja-JP/agent-surfaces.mdx +32 -0
- package/docs/content/locales/ja-JP/external-agents.mdx +10 -0
- package/docs/content/locales/ko-KR/agent-surfaces.mdx +32 -0
- package/docs/content/locales/ko-KR/external-agents.mdx +9 -0
- package/docs/content/locales/pt-BR/agent-surfaces.mdx +33 -0
- package/docs/content/locales/pt-BR/external-agents.mdx +10 -0
- package/docs/content/locales/zh-CN/agent-surfaces.mdx +29 -0
- package/docs/content/locales/zh-CN/external-agents.mdx +8 -0
- package/docs/content/locales/zh-TW/agent-surfaces.mdx +29 -0
- package/docs/content/locales/zh-TW/external-agents.mdx +8 -0
- package/package.json +1 -1
- package/src/templates/default/app/routes/_index.tsx +11 -0
- package/src/templates/default/app/routes/agent.tsx +33 -0
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +59 -0
- package/src/templates/workspace-core/.agents/skills/context-xray/SKILL.md +25 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -0
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +9 -0
|
@@ -6,9 +6,24 @@ import { z } from "zod";
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
getClients,
|
|
9
|
+
getConnectedAccounts,
|
|
9
10
|
fetchGmailLabelMap,
|
|
10
11
|
isConnected,
|
|
11
12
|
} from "../server/lib/google-auth.js";
|
|
13
|
+
import {
|
|
14
|
+
buildMailInventoryPage,
|
|
15
|
+
claimInventoryCursor,
|
|
16
|
+
compareInventoryItems,
|
|
17
|
+
createInventoryCursor,
|
|
18
|
+
inventoryQueryFingerprint,
|
|
19
|
+
releaseInventoryCursorClaim,
|
|
20
|
+
settleInventoryCursorClaim,
|
|
21
|
+
type MailInventoryError,
|
|
22
|
+
type MailInventoryFetchResult,
|
|
23
|
+
type MailInventoryItem,
|
|
24
|
+
type MailInventoryCursorState,
|
|
25
|
+
type MailInventoryCursorClaim,
|
|
26
|
+
} from "../server/lib/inventory-cursor.js";
|
|
12
27
|
import {
|
|
13
28
|
getSnoozedThreadIds,
|
|
14
29
|
getSyntheticEmailsForView,
|
|
@@ -38,6 +53,55 @@ function toCompact(emails: any[]): any[] {
|
|
|
38
53
|
}));
|
|
39
54
|
}
|
|
40
55
|
|
|
56
|
+
function toInventoryItem(
|
|
57
|
+
email: any,
|
|
58
|
+
includeSnippet: boolean,
|
|
59
|
+
): MailInventoryItem {
|
|
60
|
+
return {
|
|
61
|
+
id: String(email.id ?? "").slice(0, 256),
|
|
62
|
+
threadId: String(email.threadId ?? email.id ?? "").slice(0, 256),
|
|
63
|
+
accountEmail: String(email.accountEmail ?? "").slice(0, 320),
|
|
64
|
+
date: String(email.date ?? "").slice(0, 64),
|
|
65
|
+
from: email.from?.email
|
|
66
|
+
? {
|
|
67
|
+
...(email.from.name
|
|
68
|
+
? { name: String(email.from.name).slice(0, 160) }
|
|
69
|
+
: {}),
|
|
70
|
+
email: String(email.from.email).slice(0, 320),
|
|
71
|
+
}
|
|
72
|
+
: { email: String(email.from ?? "").slice(0, 320) },
|
|
73
|
+
subject: String(email.subject ?? "").slice(0, 500),
|
|
74
|
+
isUnread: email.hasUnread ?? !email.isRead,
|
|
75
|
+
...(email.isStarred !== undefined ? { isStarred: email.isStarred } : {}),
|
|
76
|
+
messageCount: email.messageCount ?? 1,
|
|
77
|
+
unreadCount: email.unreadCount ?? (email.isRead ? 0 : 1),
|
|
78
|
+
...(includeSnippet && email.snippet
|
|
79
|
+
? { snippet: String(email.snippet).slice(0, 320) }
|
|
80
|
+
: {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function inventoryError(message: unknown): MailInventoryError {
|
|
85
|
+
const bounded = String(message ?? "Provider request failed")
|
|
86
|
+
.replace(/\bBearer\s+\S+/gi, "Bearer [redacted]")
|
|
87
|
+
.replace(
|
|
88
|
+
/\b(access_token|refresh_token|id_token|token)=([^\s&]+)/gi,
|
|
89
|
+
"$1=[redacted]",
|
|
90
|
+
)
|
|
91
|
+
.slice(0, 240);
|
|
92
|
+
const rateLimited = /\b(?:429|quota|rate.?limit)\b/i.test(bounded);
|
|
93
|
+
const auth = /\b(?:401|403|auth|token|credential|permission)\b/i.test(
|
|
94
|
+
bounded,
|
|
95
|
+
);
|
|
96
|
+
return {
|
|
97
|
+
code: rateLimited ? "rate_limited" : auth ? "authentication" : "provider",
|
|
98
|
+
message: bounded,
|
|
99
|
+
retryable:
|
|
100
|
+
rateLimited ||
|
|
101
|
+
/\b(?:timeout|temporar|unavailable|5\d\d)\b/i.test(bounded),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
41
105
|
function latestPerThread(emails: any[]): any[] {
|
|
42
106
|
const byThread = new Map<
|
|
43
107
|
string,
|
|
@@ -85,6 +149,107 @@ function latestPerThread(emails: any[]): any[] {
|
|
|
85
149
|
);
|
|
86
150
|
}
|
|
87
151
|
|
|
152
|
+
async function localInventoryEnvelope(
|
|
153
|
+
emails: any[],
|
|
154
|
+
requestedAccounts: string[] | undefined,
|
|
155
|
+
resolvedAccounts: string[],
|
|
156
|
+
query: { view: string; q?: string },
|
|
157
|
+
limit: number,
|
|
158
|
+
ownerEmail: string,
|
|
159
|
+
cursor?: string,
|
|
160
|
+
) {
|
|
161
|
+
const normalizedRequested = requestedAccounts
|
|
162
|
+
? [...new Set(requestedAccounts.map((email) => email.toLowerCase()))]
|
|
163
|
+
: null;
|
|
164
|
+
const queryFingerprint = inventoryQueryFingerprint({
|
|
165
|
+
...query,
|
|
166
|
+
requestedAccounts: normalizedRequested,
|
|
167
|
+
limit,
|
|
168
|
+
source: "local",
|
|
169
|
+
});
|
|
170
|
+
let claim: MailInventoryCursorClaim | null = null;
|
|
171
|
+
let state: MailInventoryCursorState;
|
|
172
|
+
if (cursor) {
|
|
173
|
+
claim = await claimInventoryCursor(ownerEmail, cursor, queryFingerprint);
|
|
174
|
+
if (!claim) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"Inventory cursor is invalid, expired, or has already been used.",
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
state = claim.state;
|
|
180
|
+
} else {
|
|
181
|
+
const inventoryItems = latestPerThread(emails)
|
|
182
|
+
.map((email) =>
|
|
183
|
+
toInventoryItem(
|
|
184
|
+
{
|
|
185
|
+
...email,
|
|
186
|
+
accountEmail: email.accountEmail ?? resolvedAccounts[0] ?? "local",
|
|
187
|
+
},
|
|
188
|
+
false,
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
.sort(compareInventoryItems);
|
|
192
|
+
state = {
|
|
193
|
+
queryFingerprint,
|
|
194
|
+
requestedAccounts: normalizedRequested,
|
|
195
|
+
firstPage: true,
|
|
196
|
+
accounts: resolvedAccounts.map((accountEmail) => {
|
|
197
|
+
const pending = inventoryItems.filter(
|
|
198
|
+
(item) =>
|
|
199
|
+
item.accountEmail.toLowerCase() === accountEmail.toLowerCase(),
|
|
200
|
+
);
|
|
201
|
+
return {
|
|
202
|
+
accountEmail,
|
|
203
|
+
status: "ok" as const,
|
|
204
|
+
exhausted: true,
|
|
205
|
+
pending,
|
|
206
|
+
emittedCount: 0,
|
|
207
|
+
knownCount: pending.length,
|
|
208
|
+
emittedThreadIds: [],
|
|
209
|
+
};
|
|
210
|
+
}),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const { items, hasMore } = await buildMailInventoryPage(
|
|
216
|
+
state,
|
|
217
|
+
limit,
|
|
218
|
+
async () => ({ items: [], errors: {}, nextPageTokens: {} }),
|
|
219
|
+
);
|
|
220
|
+
const nextCursor = claim
|
|
221
|
+
? await settleInventoryCursorClaim(claim, state, hasMore)
|
|
222
|
+
: hasMore
|
|
223
|
+
? await createInventoryCursor(ownerEmail, state)
|
|
224
|
+
: undefined;
|
|
225
|
+
return {
|
|
226
|
+
version: 1,
|
|
227
|
+
query,
|
|
228
|
+
requestedAccounts: state.requestedAccounts,
|
|
229
|
+
resolvedAccounts: state.accounts.map((account) => account.accountEmail),
|
|
230
|
+
queriedAccounts: state.accounts.map((account) => account.accountEmail),
|
|
231
|
+
accounts: state.accounts.map((account) => ({
|
|
232
|
+
accountEmail: account.accountEmail,
|
|
233
|
+
status: account.status,
|
|
234
|
+
count: account.knownCount ?? account.emittedCount,
|
|
235
|
+
emittedCount: account.emittedCount,
|
|
236
|
+
exhausted: account.exhausted && account.pending.length === 0,
|
|
237
|
+
})),
|
|
238
|
+
coverageComplete: true,
|
|
239
|
+
complete: !hasMore,
|
|
240
|
+
items,
|
|
241
|
+
page: {
|
|
242
|
+
returned: items.length,
|
|
243
|
+
hasMore,
|
|
244
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (claim) await releaseInventoryCursorClaim(claim);
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
88
253
|
async function readLocalEmails(ownerEmail: string): Promise<any[]> {
|
|
89
254
|
const data = await getUserSetting(ownerEmail, "local-emails");
|
|
90
255
|
if (data && Array.isArray((data as any).emails)) {
|
|
@@ -112,13 +277,27 @@ export default defineAction({
|
|
|
112
277
|
])
|
|
113
278
|
.optional()
|
|
114
279
|
.describe("View to list (default: inbox)"),
|
|
115
|
-
q: z.string().optional().describe("Full-text search query"),
|
|
280
|
+
q: z.string().max(500).optional().describe("Full-text search query"),
|
|
116
281
|
account: z
|
|
117
282
|
.string()
|
|
118
283
|
.optional()
|
|
119
284
|
.describe(
|
|
120
285
|
"Filter to a specific account email address. By default searches all connected accounts.",
|
|
121
286
|
),
|
|
287
|
+
accountEmails: z
|
|
288
|
+
.array(z.string().email())
|
|
289
|
+
.min(1)
|
|
290
|
+
.optional()
|
|
291
|
+
.describe(
|
|
292
|
+
"Inventory only: connected account email addresses to include.",
|
|
293
|
+
),
|
|
294
|
+
format: z
|
|
295
|
+
.enum(["legacy", "inventory"])
|
|
296
|
+
.optional()
|
|
297
|
+
.describe(
|
|
298
|
+
"Use inventory for a coverage-aware compact multi-account read.",
|
|
299
|
+
),
|
|
300
|
+
cursor: z.string().max(256).optional().describe("Inventory page cursor."),
|
|
122
301
|
limit: z.coerce
|
|
123
302
|
.number()
|
|
124
303
|
.optional()
|
|
@@ -146,7 +325,7 @@ export default defineAction({
|
|
|
146
325
|
view,
|
|
147
326
|
};
|
|
148
327
|
},
|
|
149
|
-
run: async (args) => {
|
|
328
|
+
run: async (args, ctx) => {
|
|
150
329
|
const view = args.view ?? "inbox";
|
|
151
330
|
const query = args.q;
|
|
152
331
|
const limit = args.limit ?? 50;
|
|
@@ -155,9 +334,47 @@ export default defineAction({
|
|
|
155
334
|
const accountFilter = args.account?.toLowerCase();
|
|
156
335
|
const ownerEmail = getRequestUserEmail();
|
|
157
336
|
if (!ownerEmail) throw new Error("no authenticated user");
|
|
337
|
+
if (args.account && args.accountEmails) {
|
|
338
|
+
throw new Error("Pass account or accountEmails, not both.");
|
|
339
|
+
}
|
|
340
|
+
const inventory =
|
|
341
|
+
args.format === "inventory" ||
|
|
342
|
+
(ctx?.caller === "mcp" && args.format === undefined);
|
|
343
|
+
if (inventory && (!Number.isInteger(limit) || limit < 1 || limit > 100)) {
|
|
344
|
+
throw new Error("Inventory limit must be an integer from 1 through 100.");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Inventory is deliberately resolved before any refresh/list call. Apart
|
|
348
|
+
// from preventing a cross-account data leak, this keeps a selected read
|
|
349
|
+
// from touching token state for accounts the caller did not choose.
|
|
350
|
+
const requestedAccounts =
|
|
351
|
+
args.accountEmails ?? (args.account ? [args.account] : undefined);
|
|
158
352
|
|
|
159
353
|
if (view === "snoozed" || view === "scheduled") {
|
|
160
354
|
let emails = await getSyntheticEmailsForView(ownerEmail, view);
|
|
355
|
+
const syntheticAccountsByLower = new Map<string, string>();
|
|
356
|
+
for (const email of emails) {
|
|
357
|
+
const accountEmail = String(email.accountEmail ?? ownerEmail);
|
|
358
|
+
syntheticAccountsByLower.set(accountEmail.toLowerCase(), accountEmail);
|
|
359
|
+
}
|
|
360
|
+
const syntheticSelectedAccounts = inventory
|
|
361
|
+
? Array.from(
|
|
362
|
+
new Set(
|
|
363
|
+
(
|
|
364
|
+
requestedAccounts ??
|
|
365
|
+
Array.from(syntheticAccountsByLower.values())
|
|
366
|
+
).map((email) => email.toLowerCase()),
|
|
367
|
+
),
|
|
368
|
+
).map((email) => {
|
|
369
|
+
const available = syntheticAccountsByLower.get(email);
|
|
370
|
+
if (!available) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Account ${email} is not available in ${view} mail for this user.`,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
return available;
|
|
376
|
+
})
|
|
377
|
+
: undefined;
|
|
161
378
|
if (query) {
|
|
162
379
|
emails = emails.filter((e) => emailMessageMatchesSearch(e, query));
|
|
163
380
|
}
|
|
@@ -166,6 +383,35 @@ export default defineAction({
|
|
|
166
383
|
(e) => e.accountEmail?.toLowerCase() === accountFilter,
|
|
167
384
|
);
|
|
168
385
|
}
|
|
386
|
+
if (inventory) {
|
|
387
|
+
const selected = new Set(
|
|
388
|
+
(syntheticSelectedAccounts ?? []).map((email) => email.toLowerCase()),
|
|
389
|
+
);
|
|
390
|
+
if (selected.size > 0) {
|
|
391
|
+
emails = emails.filter((email) =>
|
|
392
|
+
selected.has(String(email.accountEmail ?? "").toLowerCase()),
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
const syntheticResolvedAccounts =
|
|
396
|
+
syntheticSelectedAccounts && syntheticSelectedAccounts.length > 0
|
|
397
|
+
? syntheticSelectedAccounts
|
|
398
|
+
: Array.from(
|
|
399
|
+
new Set(
|
|
400
|
+
emails.map((email) =>
|
|
401
|
+
String(email.accountEmail ?? ownerEmail).toLowerCase(),
|
|
402
|
+
),
|
|
403
|
+
),
|
|
404
|
+
);
|
|
405
|
+
return await localInventoryEnvelope(
|
|
406
|
+
emails,
|
|
407
|
+
requestedAccounts,
|
|
408
|
+
syntheticResolvedAccounts,
|
|
409
|
+
{ view, ...(query ? { q: query } : {}) },
|
|
410
|
+
limit,
|
|
411
|
+
ownerEmail,
|
|
412
|
+
args.cursor,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
169
415
|
return JSON.stringify(
|
|
170
416
|
compact ? toCompact(emails.slice(0, limit)) : emails.slice(0, limit),
|
|
171
417
|
null,
|
|
@@ -173,8 +419,192 @@ export default defineAction({
|
|
|
173
419
|
);
|
|
174
420
|
}
|
|
175
421
|
|
|
176
|
-
|
|
177
|
-
|
|
422
|
+
const connectedAccounts = inventory
|
|
423
|
+
? await getConnectedAccounts(ownerEmail)
|
|
424
|
+
: [];
|
|
425
|
+
const connectedByLower = new Map(
|
|
426
|
+
connectedAccounts.map((email) => [email.toLowerCase(), email]),
|
|
427
|
+
);
|
|
428
|
+
const selectedAccounts =
|
|
429
|
+
inventory && connectedAccounts.length > 0
|
|
430
|
+
? Array.from(
|
|
431
|
+
new Set(
|
|
432
|
+
(requestedAccounts ?? connectedAccounts).map((email) =>
|
|
433
|
+
email.toLowerCase(),
|
|
434
|
+
),
|
|
435
|
+
),
|
|
436
|
+
).map((email) => {
|
|
437
|
+
const owned = connectedByLower.get(email);
|
|
438
|
+
if (!owned)
|
|
439
|
+
throw new Error(
|
|
440
|
+
`Account ${email} is not connected for this user.`,
|
|
441
|
+
);
|
|
442
|
+
return owned;
|
|
443
|
+
})
|
|
444
|
+
: undefined;
|
|
445
|
+
|
|
446
|
+
if (
|
|
447
|
+
(inventory && selectedAccounts && selectedAccounts.length > 0) ||
|
|
448
|
+
(!inventory && (await isConnected(ownerEmail)))
|
|
449
|
+
) {
|
|
450
|
+
const inventoryAccounts = selectedAccounts ?? [];
|
|
451
|
+
const clients = inventory
|
|
452
|
+
? inventoryAccounts.map((email) => ({
|
|
453
|
+
email,
|
|
454
|
+
accessToken: "",
|
|
455
|
+
refreshToken: "",
|
|
456
|
+
}))
|
|
457
|
+
: await getClients(ownerEmail);
|
|
458
|
+
if (inventory) {
|
|
459
|
+
const normalizedRequested = requestedAccounts
|
|
460
|
+
? [...new Set(requestedAccounts.map((email) => email.toLowerCase()))]
|
|
461
|
+
: null;
|
|
462
|
+
const queryFingerprint = inventoryQueryFingerprint({
|
|
463
|
+
view,
|
|
464
|
+
q: query ?? null,
|
|
465
|
+
requestedAccounts: normalizedRequested,
|
|
466
|
+
limit,
|
|
467
|
+
});
|
|
468
|
+
let cursorState: MailInventoryCursorState;
|
|
469
|
+
let cursorClaim: MailInventoryCursorClaim | null = null;
|
|
470
|
+
if (args.cursor) {
|
|
471
|
+
const claimed = await claimInventoryCursor(
|
|
472
|
+
ownerEmail,
|
|
473
|
+
args.cursor,
|
|
474
|
+
queryFingerprint,
|
|
475
|
+
);
|
|
476
|
+
if (!claimed) {
|
|
477
|
+
throw new Error(
|
|
478
|
+
"Inventory cursor is invalid, expired, or has already been used.",
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
cursorClaim = claimed;
|
|
482
|
+
cursorState = claimed.state;
|
|
483
|
+
} else {
|
|
484
|
+
cursorState = {
|
|
485
|
+
queryFingerprint,
|
|
486
|
+
requestedAccounts: normalizedRequested,
|
|
487
|
+
firstPage: true,
|
|
488
|
+
accounts: inventoryAccounts.map((accountEmail) => ({
|
|
489
|
+
accountEmail,
|
|
490
|
+
status: "ok",
|
|
491
|
+
exhausted: false,
|
|
492
|
+
pending: [],
|
|
493
|
+
emittedCount: 0,
|
|
494
|
+
knownCount: 0,
|
|
495
|
+
emittedThreadIds: [],
|
|
496
|
+
})),
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const fetchInventory = async (
|
|
501
|
+
requests: Array<{ accountEmail: string; pageToken?: string }>,
|
|
502
|
+
): Promise<MailInventoryFetchResult> => {
|
|
503
|
+
const accountEmails = requests.map((request) => request.accountEmail);
|
|
504
|
+
const pageTokens = Object.fromEntries(
|
|
505
|
+
requests
|
|
506
|
+
.filter((request) => request.pageToken)
|
|
507
|
+
.map((request) => [request.accountEmail, request.pageToken!]),
|
|
508
|
+
);
|
|
509
|
+
const listResult = await listInboxEmails({
|
|
510
|
+
ownerEmail,
|
|
511
|
+
view,
|
|
512
|
+
q: query,
|
|
513
|
+
limit,
|
|
514
|
+
pageTokens:
|
|
515
|
+
Object.keys(pageTokens).length > 0 ? pageTokens : undefined,
|
|
516
|
+
threadFormat: "metadata",
|
|
517
|
+
includeRecentMessageCandidates: false,
|
|
518
|
+
accountTokens: accountEmails.map((email) => ({
|
|
519
|
+
email,
|
|
520
|
+
accessToken: "",
|
|
521
|
+
})),
|
|
522
|
+
accountEmails,
|
|
523
|
+
labelMap: new Map(),
|
|
524
|
+
});
|
|
525
|
+
if (!listResult.ok) {
|
|
526
|
+
return {
|
|
527
|
+
items: [],
|
|
528
|
+
errors: Object.fromEntries(
|
|
529
|
+
accountEmails.map((email) => [
|
|
530
|
+
email.toLowerCase(),
|
|
531
|
+
inventoryError(listResult.message),
|
|
532
|
+
]),
|
|
533
|
+
),
|
|
534
|
+
nextPageTokens: {},
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
items: latestPerThread(listResult.emails).map((email) =>
|
|
539
|
+
toInventoryItem(email, false),
|
|
540
|
+
),
|
|
541
|
+
errors: Object.fromEntries(
|
|
542
|
+
listResult.errors.map((error) => [
|
|
543
|
+
error.email.toLowerCase(),
|
|
544
|
+
inventoryError(error.error),
|
|
545
|
+
]),
|
|
546
|
+
),
|
|
547
|
+
nextPageTokens: Object.fromEntries(
|
|
548
|
+
Object.entries(listResult.nextPageTokens ?? {}).map(
|
|
549
|
+
([email, token]) => [email.toLowerCase(), token],
|
|
550
|
+
),
|
|
551
|
+
),
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
try {
|
|
556
|
+
const { items, hasMore } = await buildMailInventoryPage(
|
|
557
|
+
cursorState,
|
|
558
|
+
limit,
|
|
559
|
+
fetchInventory,
|
|
560
|
+
);
|
|
561
|
+
const nextCursor = cursorClaim
|
|
562
|
+
? await settleInventoryCursorClaim(
|
|
563
|
+
cursorClaim,
|
|
564
|
+
cursorState,
|
|
565
|
+
hasMore,
|
|
566
|
+
)
|
|
567
|
+
: hasMore
|
|
568
|
+
? await createInventoryCursor(ownerEmail, cursorState)
|
|
569
|
+
: undefined;
|
|
570
|
+
const coverageComplete = cursorState.accounts.every(
|
|
571
|
+
(account) => account.status === "ok",
|
|
572
|
+
);
|
|
573
|
+
return {
|
|
574
|
+
version: 1,
|
|
575
|
+
query: { view, ...(query ? { q: query } : {}) },
|
|
576
|
+
requestedAccounts: cursorState.requestedAccounts,
|
|
577
|
+
resolvedAccounts: cursorState.accounts.map(
|
|
578
|
+
(account) => account.accountEmail,
|
|
579
|
+
),
|
|
580
|
+
queriedAccounts: cursorState.accounts.map(
|
|
581
|
+
(account) => account.accountEmail,
|
|
582
|
+
),
|
|
583
|
+
accounts: cursorState.accounts.map((account) => ({
|
|
584
|
+
accountEmail: account.accountEmail,
|
|
585
|
+
status: account.status,
|
|
586
|
+
count: account.knownCount ?? account.emittedCount,
|
|
587
|
+
emittedCount: account.emittedCount,
|
|
588
|
+
exhausted:
|
|
589
|
+
account.status === "ok" &&
|
|
590
|
+
account.exhausted &&
|
|
591
|
+
account.pending.length === 0,
|
|
592
|
+
...(account.error ? { error: account.error } : {}),
|
|
593
|
+
})),
|
|
594
|
+
coverageComplete,
|
|
595
|
+
complete: coverageComplete && !hasMore,
|
|
596
|
+
items,
|
|
597
|
+
page: {
|
|
598
|
+
returned: items.length,
|
|
599
|
+
hasMore,
|
|
600
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
} catch (error) {
|
|
604
|
+
if (cursorClaim) await releaseInventoryCursorClaim(cursorClaim);
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
178
608
|
const labelMap = new Map<string, string>();
|
|
179
609
|
await Promise.all(
|
|
180
610
|
clients.map(async ({ accessToken }) => {
|
|
@@ -239,6 +669,37 @@ export default defineAction({
|
|
|
239
669
|
|
|
240
670
|
// Fallback: local store
|
|
241
671
|
let emails = await readLocalEmails(ownerEmail);
|
|
672
|
+
const localAccountsByLower = new Map<string, string>();
|
|
673
|
+
for (const email of emails) {
|
|
674
|
+
const accountEmail = String(email.accountEmail ?? "local");
|
|
675
|
+
localAccountsByLower.set(accountEmail.toLowerCase(), accountEmail);
|
|
676
|
+
}
|
|
677
|
+
const localSelectedAccounts = inventory
|
|
678
|
+
? Array.from(
|
|
679
|
+
new Set(
|
|
680
|
+
(
|
|
681
|
+
requestedAccounts ?? Array.from(localAccountsByLower.values())
|
|
682
|
+
).map((email) => email.toLowerCase()),
|
|
683
|
+
),
|
|
684
|
+
).map((email) => {
|
|
685
|
+
const available = localAccountsByLower.get(email);
|
|
686
|
+
if (!available) {
|
|
687
|
+
throw new Error(
|
|
688
|
+
`Account ${email} is not available in local mail for this user.`,
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
return available;
|
|
692
|
+
})
|
|
693
|
+
: undefined;
|
|
694
|
+
|
|
695
|
+
if (localSelectedAccounts && requestedAccounts) {
|
|
696
|
+
const selected = new Set(
|
|
697
|
+
localSelectedAccounts.map((email) => email.toLowerCase()),
|
|
698
|
+
);
|
|
699
|
+
emails = emails.filter((email) =>
|
|
700
|
+
selected.has(String(email.accountEmail ?? "local").toLowerCase()),
|
|
701
|
+
);
|
|
702
|
+
}
|
|
242
703
|
|
|
243
704
|
switch (view) {
|
|
244
705
|
case "inbox":
|
|
@@ -288,6 +749,27 @@ export default defineAction({
|
|
|
288
749
|
}
|
|
289
750
|
}
|
|
290
751
|
|
|
752
|
+
if (inventory) {
|
|
753
|
+
const localResolvedAccounts =
|
|
754
|
+
localSelectedAccounts && localSelectedAccounts.length > 0
|
|
755
|
+
? localSelectedAccounts
|
|
756
|
+
: Array.from(
|
|
757
|
+
new Set(
|
|
758
|
+
emails.map((email) =>
|
|
759
|
+
String(email.accountEmail ?? "local").toLowerCase(),
|
|
760
|
+
),
|
|
761
|
+
),
|
|
762
|
+
);
|
|
763
|
+
return await localInventoryEnvelope(
|
|
764
|
+
emails,
|
|
765
|
+
requestedAccounts,
|
|
766
|
+
localResolvedAccounts,
|
|
767
|
+
{ view, ...(query ? { q: query } : {}) },
|
|
768
|
+
limit,
|
|
769
|
+
ownerEmail,
|
|
770
|
+
args.cursor,
|
|
771
|
+
);
|
|
772
|
+
}
|
|
291
773
|
emails = latestPerThread(emails).slice(0, limit);
|
|
292
774
|
const payload = compact ? toCompact(emails) : emails;
|
|
293
775
|
if (includeCounts) {
|
|
@@ -13,6 +13,7 @@ import { normalizeMailLabel } from "@shared/gmail-labels";
|
|
|
13
13
|
import type { Label } from "@shared/types";
|
|
14
14
|
import {
|
|
15
15
|
IconMenu2,
|
|
16
|
+
IconBrain,
|
|
16
17
|
IconSettings,
|
|
17
18
|
IconSearch,
|
|
18
19
|
IconCheck,
|
|
@@ -144,6 +145,7 @@ function AccountAvatar({
|
|
|
144
145
|
function isStandardLayoutPath(pathname: string): boolean {
|
|
145
146
|
return (
|
|
146
147
|
pathname === "/settings" ||
|
|
148
|
+
pathname === "/agent" ||
|
|
147
149
|
pathname === "/team" ||
|
|
148
150
|
pathname === "/draft-queue" ||
|
|
149
151
|
pathname.startsWith("/draft-queue/") ||
|
|
@@ -195,6 +197,7 @@ export function AppLayout({ children }: AppLayoutProps) {
|
|
|
195
197
|
<AgentSidebar
|
|
196
198
|
position="right"
|
|
197
199
|
defaultOpen={!isMobile}
|
|
200
|
+
agentPageHref="/agent"
|
|
198
201
|
emptyStateText={t("agent.emptyState")}
|
|
199
202
|
suggestions={[
|
|
200
203
|
t("agent.suggestionSummarize"),
|
|
@@ -1748,6 +1751,25 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|
|
1748
1751
|
<div className="flex items-center gap-1 px-2 py-2">
|
|
1749
1752
|
<DevDatabaseLink />
|
|
1750
1753
|
<FeedbackButton className="min-w-0 flex-1" />
|
|
1754
|
+
<Tooltip>
|
|
1755
|
+
<TooltipTrigger asChild>
|
|
1756
|
+
<Link
|
|
1757
|
+
to="/agent"
|
|
1758
|
+
onClick={closeSidebar}
|
|
1759
|
+
aria-label={t("settings.openAgentSettings")}
|
|
1760
|
+
className={cn(
|
|
1761
|
+
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground",
|
|
1762
|
+
location.pathname === "/agent" &&
|
|
1763
|
+
"bg-accent/60 text-foreground",
|
|
1764
|
+
)}
|
|
1765
|
+
>
|
|
1766
|
+
<IconBrain className="h-4 w-4" />
|
|
1767
|
+
</Link>
|
|
1768
|
+
</TooltipTrigger>
|
|
1769
|
+
<TooltipContent>
|
|
1770
|
+
{t("settings.openAgentSettings")}
|
|
1771
|
+
</TooltipContent>
|
|
1772
|
+
</Tooltip>
|
|
1751
1773
|
<Tooltip>
|
|
1752
1774
|
<TooltipTrigger asChild>
|
|
1753
1775
|
<Link
|
|
@@ -1967,6 +1989,7 @@ function StandardLayout({ children }: AppLayoutProps) {
|
|
|
1967
1989
|
|
|
1968
1990
|
const fallbackTitle = (() => {
|
|
1969
1991
|
if (location.pathname === "/settings") return t("settings.title");
|
|
1992
|
+
if (location.pathname === "/agent") return t("settings.agentTitle");
|
|
1970
1993
|
if (location.pathname === "/team") return t("mail.pages.team");
|
|
1971
1994
|
if (location.pathname.startsWith("/draft-queue"))
|
|
1972
1995
|
return t("mail.views.draftQueue");
|
|
@@ -2101,6 +2124,25 @@ function StandardLayout({ children }: AppLayoutProps) {
|
|
|
2101
2124
|
<DevDatabaseLink />
|
|
2102
2125
|
<FeedbackButton className="min-w-0 flex-1" />
|
|
2103
2126
|
<div className="flex shrink-0 items-center gap-0.5">
|
|
2127
|
+
<Tooltip>
|
|
2128
|
+
<TooltipTrigger asChild>
|
|
2129
|
+
<Link
|
|
2130
|
+
to="/agent"
|
|
2131
|
+
onClick={() => setSidebarOpen(false)}
|
|
2132
|
+
aria-label={t("settings.openAgentSettings")}
|
|
2133
|
+
className={cn(
|
|
2134
|
+
"flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground",
|
|
2135
|
+
location.pathname === "/agent" &&
|
|
2136
|
+
"bg-accent/60 text-foreground",
|
|
2137
|
+
)}
|
|
2138
|
+
>
|
|
2139
|
+
<IconBrain className="h-4 w-4" />
|
|
2140
|
+
</Link>
|
|
2141
|
+
</TooltipTrigger>
|
|
2142
|
+
<TooltipContent>
|
|
2143
|
+
{t("settings.openAgentSettings")}
|
|
2144
|
+
</TooltipContent>
|
|
2145
|
+
</Tooltip>
|
|
2104
2146
|
<Tooltip>
|
|
2105
2147
|
<TooltipTrigger asChild>
|
|
2106
2148
|
<Link
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CommandMenu, useT } from "@agent-native/core/client";
|
|
2
2
|
import {
|
|
3
|
+
IconBrain,
|
|
3
4
|
IconInbox,
|
|
4
5
|
IconStar,
|
|
5
6
|
IconSend,
|
|
@@ -74,6 +75,11 @@ const navCommands = [
|
|
|
74
75
|
shortcut: "G A",
|
|
75
76
|
},
|
|
76
77
|
{ labelKey: "commandPalette.goToTrash", icon: IconTrash, route: "/trash" },
|
|
78
|
+
{
|
|
79
|
+
labelKey: "settings.openAgentSettings",
|
|
80
|
+
icon: IconBrain,
|
|
81
|
+
route: "/agent",
|
|
82
|
+
},
|
|
77
83
|
];
|
|
78
84
|
|
|
79
85
|
export function CommandPalette({
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AgentTabsPage, useT } from "@agent-native/core/client";
|
|
2
|
+
import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
|
|
3
|
+
|
|
4
|
+
import messages from "@/i18n/en-US";
|
|
5
|
+
|
|
6
|
+
export function meta() {
|
|
7
|
+
return [{ title: messages.settings.agentTitle }];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function AgentRoute() {
|
|
11
|
+
const t = useT();
|
|
12
|
+
useSetPageTitle(t("settings.agentTitle"));
|
|
13
|
+
|
|
14
|
+
return <AgentTabsPage appName="Mail" />;
|
|
15
|
+
}
|