@agent-native/core 0.98.0 → 0.98.2
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 +32 -0
- package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
- package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +1 -1
- package/corpus/core/docs/content/pr-visual-recap.mdx +50 -1
- package/corpus/core/docs/content/template-plan.mdx +1 -1
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/action-change-marker.ts +8 -1
- package/corpus/core/src/changelog/parse.ts +8 -3
- package/corpus/core/src/cli/plan-local.ts +43 -11
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -9
- package/corpus/core/src/cli/recap.ts +356 -4
- package/corpus/core/src/cli/skills-content/local-files.ts +17 -12
- package/corpus/core/src/client/AgentPanel.tsx +2 -3
- package/corpus/core/src/client/AssistantChat.tsx +83 -39
- package/corpus/core/src/client/analytics.ts +125 -14
- package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
- package/corpus/core/src/client/chat-model-groups.ts +28 -0
- package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
- package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
- package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
- package/corpus/core/src/client/composer/index.ts +5 -0
- package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
- package/corpus/core/src/client/index.ts +10 -0
- package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
- package/corpus/core/src/client/session-replay.ts +10 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +4 -28
- package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
- package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
- package/corpus/core/src/client/url-scrub.ts +13 -0
- package/corpus/core/src/client/use-action.ts +12 -0
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
- package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
- package/corpus/core/src/client/use-db-sync.ts +84 -34
- package/corpus/core/src/client/use-demo-mode-status.ts +44 -0
- package/corpus/core/src/client/use-pausing-interval.ts +14 -4
- package/corpus/core/src/integrations/scope-store.ts +1 -1
- package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
- package/corpus/core/src/localization/default-messages.ts +44 -1
- package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
- package/corpus/core/src/secrets/crypto.ts +28 -8
- package/corpus/core/src/server/action-change.ts +5 -1
- package/corpus/core/src/server/action-routes.ts +8 -0
- package/corpus/core/src/server/poll.ts +1 -0
- package/corpus/core/src/server/realtime-voice.ts +299 -13
- package/corpus/core/src/styles/agent-native.css +2 -2
- package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
- package/corpus/core/src/vite/client.ts +9 -1
- package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
- package/corpus/templates/analytics/AGENTS.md +17 -13
- package/corpus/templates/analytics/actions/navigate.ts +59 -5
- package/corpus/templates/analytics/actions/view-screen.ts +3 -6
- package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +20 -5
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
- package/corpus/templates/analytics/app/global.css +19 -20
- package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +156 -0
- package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
- package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
- package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
- package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
- package/corpus/templates/analytics/app/root.tsx +28 -8
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
- package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
- package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-daily-dashboard-emails-reliably-include-the-complete-dashboa.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-charts-trend-upward.md +6 -0
- package/corpus/templates/analytics/package.json +1 -1
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
- package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +17 -33
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/assets/app/global.css +4 -4
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
- package/corpus/templates/assets/app/routes/library.tsx +0 -1
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/brain/app/global.css +5 -5
- package/corpus/templates/brain/app/root.tsx +0 -3
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/calendar/app/root.tsx +1 -0
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
- package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
- package/corpus/templates/clips/changelog/2026-07-11-clips-no-longer-stay-stuck-on-preparing-clip-when-they-are-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +2 -2
- package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
- package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
- package/corpus/templates/content/changelog/2026-07-12-the-left-sidebar-stays-fixed-horizontally-while-scrolling-on.md +6 -0
- package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
- package/corpus/templates/macros/app/root.tsx +0 -3
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
- package/corpus/templates/mail/app/root.tsx +1 -12
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/plan/.agents/skills/visual-plan/references/local-files.md +17 -12
- package/corpus/templates/plan/.agents/skills/visual-recap/references/local-files.md +17 -12
- package/corpus/templates/plan/actions/validate-local-plan-source.ts +4 -4
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -1
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
- package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +41 -0
- package/corpus/templates/plan/app/lib/plan-tracking.ts +3 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +14 -36
- package/corpus/templates/plan/app/root.tsx +13 -3
- package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-12-local-plan-source-now-stays-on-device.md +6 -0
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
- package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
- package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
- package/dist/action-change-marker.d.ts +1 -0
- package/dist/action-change-marker.d.ts.map +1 -1
- package/dist/action-change-marker.js +7 -1
- package/dist/action-change-marker.js.map +1 -1
- package/dist/changelog/parse.d.ts +4 -2
- package/dist/changelog/parse.d.ts.map +1 -1
- package/dist/changelog/parse.js +5 -3
- package/dist/changelog/parse.js.map +1 -1
- package/dist/cli/plan-local.d.ts +6 -6
- package/dist/cli/plan-local.d.ts.map +1 -1
- package/dist/cli/plan-local.js +36 -7
- package/dist/cli/plan-local.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +2 -8
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +2 -8
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +22 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +286 -2
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills-content/local-files.d.ts +1 -1
- package/dist/cli/skills-content/local-files.d.ts.map +1 -1
- package/dist/cli/skills-content/local-files.js +17 -12
- package/dist/cli/skills-content/local-files.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +7 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +62 -40
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +103 -13
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.js +9 -2
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +24 -0
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
- package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.js +172 -42
- package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +4 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +47 -29
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +2 -2
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/index.d.ts +2 -2
- package/dist/client/composer/index.d.ts.map +1 -1
- package/dist/client/composer/index.js +1 -1
- package/dist/client/composer/index.js.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
- package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
- package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/progress/RunsTray.d.ts.map +1 -1
- package/dist/client/progress/RunsTray.js +10 -1
- package/dist/client/progress/RunsTray.js.map +1 -1
- package/dist/client/session-replay.d.ts +2 -0
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +7 -0
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +4 -23
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/settings/SecretsSection.d.ts +2 -3
- package/dist/client/settings/SecretsSection.d.ts.map +1 -1
- package/dist/client/settings/SecretsSection.js +46 -22
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +9 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +46 -12
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/url-scrub.d.ts.map +1 -1
- package/dist/client/url-scrub.js +14 -0
- package/dist/client/url-scrub.js.map +1 -1
- package/dist/client/use-action.d.ts +2 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +10 -0
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +7 -1
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +17 -3
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +8 -2
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +57 -32
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-demo-mode-status.d.ts +12 -0
- package/dist/client/use-demo-mode-status.d.ts.map +1 -0
- package/dist/client/use-demo-mode-status.js +30 -0
- package/dist/client/use-demo-mode-status.js.map +1 -0
- package/dist/client/use-pausing-interval.d.ts.map +1 -1
- package/dist/client/use-pausing-interval.js +16 -4
- package/dist/client/use-pausing-interval.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/integrations/scope-store.js +1 -1
- package/dist/integrations/scope-store.js.map +1 -1
- package/dist/integrations/usage-budget-store.js +1 -1
- package/dist/integrations/usage-budget-store.js.map +1 -1
- package/dist/localization/default-messages.d.ts +41 -1
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +41 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp-client/workspace-servers.js +4 -4
- package/dist/mcp-client/workspace-servers.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/crypto.d.ts +7 -6
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +30 -11
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/server/action-change.d.ts +1 -0
- package/dist/server/action-change.d.ts.map +1 -1
- package/dist/server/action-change.js +4 -1
- package/dist/server/action-change.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +5 -0
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +1 -0
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-voice.d.ts +7 -0
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +222 -12
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/styles/agent-native.css +2 -2
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +6 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/ar-SA/template-plan.mdx +1 -1
- package/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/de-DE/template-plan.mdx +1 -1
- package/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/es-ES/template-plan.mdx +1 -1
- package/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/fr-FR/template-plan.mdx +1 -1
- package/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/hi-IN/template-plan.mdx +1 -1
- package/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/ja-JP/template-plan.mdx +1 -1
- package/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/ko-KR/template-plan.mdx +1 -1
- package/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/pt-BR/template-plan.mdx +1 -1
- package/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/zh-CN/template-plan.mdx +1 -1
- package/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
- package/docs/content/locales/zh-TW/template-plan.mdx +1 -1
- package/docs/content/pr-visual-recap.mdx +50 -1
- package/docs/content/template-plan.mdx +1 -1
- package/package.json +2 -2
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
|
@@ -2,6 +2,8 @@ import { defineEventHandler, getHeader, getMethod, readRawBody, setResponseHeade
|
|
|
2
2
|
import { getBuilderGatewayRequestHeaders } from "../agent/engine/builder-gateway-headers.js";
|
|
3
3
|
import { actionsToEngineTools, } from "../agent/production-agent.js";
|
|
4
4
|
import { redactSensitiveFields, sanitizeToolErrorText, sanitizeToolErrorValue, } from "../agent/tool-error-redaction.js";
|
|
5
|
+
import { TOOL_SEARCH_ACTION_NAME } from "../agent/tool-search.js";
|
|
6
|
+
import { parseAcceptLanguage } from "../localization/server.js";
|
|
5
7
|
import { getSession } from "./auth.js";
|
|
6
8
|
import { getBuilderGatewayBaseUrl, resolveBuilderCredentials, resolveSecret, } from "./credential-provider.js";
|
|
7
9
|
import { getH3App } from "./framework-request-handler.js";
|
|
@@ -15,6 +17,9 @@ export const REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS = 16_000;
|
|
|
15
17
|
export const REALTIME_VOICE_MAX_TOOLS = 32;
|
|
16
18
|
export const REALTIME_VOICE_MAX_TOOL_SCHEMA_BYTES = 32_000;
|
|
17
19
|
export const REALTIME_VOICE_MAX_SESSION_BYTES = 64_000;
|
|
20
|
+
export const REALTIME_VOICE_TOOL_GRANT_TTL_MS = 10 * 60 * 1_000;
|
|
21
|
+
export const REALTIME_VOICE_MAX_TOOL_GRANT_SESSIONS = 256;
|
|
22
|
+
export const REALTIME_VOICE_CAPABILITY_HEADER = "X-Agent-Native-Realtime-Capability";
|
|
18
23
|
const OPENAI_REALTIME_CALLS_URL = "https://api.openai.com/v1/realtime/calls";
|
|
19
24
|
const DEFAULT_MODEL = "gpt-realtime-2.1";
|
|
20
25
|
const DEFAULT_VOICE = "marin";
|
|
@@ -26,6 +31,36 @@ const REALTIME_TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
26
31
|
const CALL_ID = /^[A-Za-z0-9_-]{1,256}$/;
|
|
27
32
|
const SESSION_ID = /^[A-Za-z0-9_.:-]{1,256}$/;
|
|
28
33
|
const BROWSER_TAB_ID = /^[A-Za-z0-9_-]{1,96}$/;
|
|
34
|
+
const ISO_639_1_LANGUAGE = /^[A-Za-z]{2}$/;
|
|
35
|
+
const REALTIME_VOICE_BUILT_INS = new Set([
|
|
36
|
+
"alloy",
|
|
37
|
+
"ash",
|
|
38
|
+
"ballad",
|
|
39
|
+
"coral",
|
|
40
|
+
"echo",
|
|
41
|
+
"sage",
|
|
42
|
+
"shimmer",
|
|
43
|
+
"verse",
|
|
44
|
+
"marin",
|
|
45
|
+
"cedar",
|
|
46
|
+
]);
|
|
47
|
+
const REALTIME_VOICE_REASONING_EFFORT = {
|
|
48
|
+
instant: "minimal",
|
|
49
|
+
balanced: "low",
|
|
50
|
+
deep: "medium",
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Realtime sessions have a deliberately bounded tool manifest. Keep the
|
|
54
|
+
* context/navigation tools ahead of large template registries so voice can
|
|
55
|
+
* always see and operate the same UI navigation surface as text chat.
|
|
56
|
+
*/
|
|
57
|
+
const REALTIME_VOICE_PRIORITY_TOOLS = [
|
|
58
|
+
"navigate",
|
|
59
|
+
"set-url-path",
|
|
60
|
+
"set-search-params",
|
|
61
|
+
"view-screen",
|
|
62
|
+
TOOL_SEARCH_ACTION_NAME,
|
|
63
|
+
];
|
|
29
64
|
function readSafeHeader(event, name) {
|
|
30
65
|
const value = getHeader(event, name);
|
|
31
66
|
if (typeof value !== "string")
|
|
@@ -39,6 +74,34 @@ function configuredIdentifier(value, fallback) {
|
|
|
39
74
|
? trimmed
|
|
40
75
|
: fallback;
|
|
41
76
|
}
|
|
77
|
+
export function resolveRealtimeVoiceTranscriptionLanguage(acceptLanguage) {
|
|
78
|
+
for (const locale of parseAcceptLanguage(acceptLanguage)) {
|
|
79
|
+
const primaryLanguage = locale.split("-")[0];
|
|
80
|
+
if (ISO_639_1_LANGUAGE.test(primaryLanguage)) {
|
|
81
|
+
return primaryLanguage.toLowerCase();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return "en";
|
|
85
|
+
}
|
|
86
|
+
export function resolveRealtimeVoiceLanguagePreference(value, acceptLanguage) {
|
|
87
|
+
const normalized = value?.trim().toLowerCase();
|
|
88
|
+
return normalized && ISO_639_1_LANGUAGE.test(normalized)
|
|
89
|
+
? normalized
|
|
90
|
+
: resolveRealtimeVoiceTranscriptionLanguage(acceptLanguage);
|
|
91
|
+
}
|
|
92
|
+
export function resolveRealtimeVoiceReasoningEffort(value) {
|
|
93
|
+
const normalized = value?.trim().toLowerCase();
|
|
94
|
+
return normalized &&
|
|
95
|
+
Object.hasOwn(REALTIME_VOICE_REASONING_EFFORT, normalized)
|
|
96
|
+
? REALTIME_VOICE_REASONING_EFFORT[normalized]
|
|
97
|
+
: "low";
|
|
98
|
+
}
|
|
99
|
+
export function resolveRealtimeVoicePreference(value, fallback) {
|
|
100
|
+
const normalized = value?.trim().toLowerCase();
|
|
101
|
+
return normalized && REALTIME_VOICE_BUILT_INS.has(normalized)
|
|
102
|
+
? normalized
|
|
103
|
+
: fallback;
|
|
104
|
+
}
|
|
42
105
|
function truncate(value, maxChars) {
|
|
43
106
|
if (value.length <= maxChars)
|
|
44
107
|
return value;
|
|
@@ -111,7 +174,22 @@ function buildRealtimeTools(actions) {
|
|
|
111
174
|
parameters,
|
|
112
175
|
});
|
|
113
176
|
}
|
|
114
|
-
|
|
177
|
+
const priority = new Map(REALTIME_VOICE_PRIORITY_TOOLS.map((name, index) => [name, index]));
|
|
178
|
+
return tools
|
|
179
|
+
.map((tool, index) => ({ tool, index }))
|
|
180
|
+
.sort((left, right) => {
|
|
181
|
+
const leftPriority = priority.get(left.tool.name);
|
|
182
|
+
const rightPriority = priority.get(right.tool.name);
|
|
183
|
+
if (leftPriority !== undefined || rightPriority !== undefined) {
|
|
184
|
+
if (leftPriority === undefined)
|
|
185
|
+
return 1;
|
|
186
|
+
if (rightPriority === undefined)
|
|
187
|
+
return -1;
|
|
188
|
+
return leftPriority - rightPriority;
|
|
189
|
+
}
|
|
190
|
+
return left.index - right.index;
|
|
191
|
+
})
|
|
192
|
+
.map(({ tool }) => tool);
|
|
115
193
|
}
|
|
116
194
|
function packRealtimeTools(session, eligibleTools) {
|
|
117
195
|
const packed = [];
|
|
@@ -129,6 +207,104 @@ function packRealtimeTools(session, eligibleTools) {
|
|
|
129
207
|
}
|
|
130
208
|
return packed;
|
|
131
209
|
}
|
|
210
|
+
function mintRealtimeToolCapability() {
|
|
211
|
+
return globalThis.crypto.randomUUID().replaceAll("-", "");
|
|
212
|
+
}
|
|
213
|
+
function cleanRealtimeToolCapabilities(capabilities, now = Date.now()) {
|
|
214
|
+
for (const [key, capability] of capabilities) {
|
|
215
|
+
if (capability.expiresAt <= now)
|
|
216
|
+
capabilities.delete(key);
|
|
217
|
+
}
|
|
218
|
+
while (capabilities.size > REALTIME_VOICE_MAX_TOOL_GRANT_SESSIONS) {
|
|
219
|
+
let oldestKey;
|
|
220
|
+
let oldestExpiry = Number.POSITIVE_INFINITY;
|
|
221
|
+
for (const [key, capability] of capabilities) {
|
|
222
|
+
if (capability.expiresAt < oldestExpiry) {
|
|
223
|
+
oldestKey = key;
|
|
224
|
+
oldestExpiry = capability.expiresAt;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (!oldestKey)
|
|
228
|
+
break;
|
|
229
|
+
capabilities.delete(oldestKey);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function registerRealtimeToolCapability(capabilities, auth, initialNames) {
|
|
233
|
+
cleanRealtimeToolCapabilities(capabilities);
|
|
234
|
+
const token = mintRealtimeToolCapability();
|
|
235
|
+
capabilities.set(token, {
|
|
236
|
+
userEmail: auth.userEmail.trim().toLowerCase(),
|
|
237
|
+
...(auth.orgId ? { orgId: auth.orgId } : {}),
|
|
238
|
+
...(auth.browserTabId ? { browserTabId: auth.browserTabId } : {}),
|
|
239
|
+
expiresAt: Date.now() + REALTIME_VOICE_TOOL_GRANT_TTL_MS,
|
|
240
|
+
initialNames: new Set(initialNames),
|
|
241
|
+
names: new Set(),
|
|
242
|
+
});
|
|
243
|
+
cleanRealtimeToolCapabilities(capabilities);
|
|
244
|
+
return token;
|
|
245
|
+
}
|
|
246
|
+
function resolveRealtimeToolCapability(capabilities, token, auth) {
|
|
247
|
+
cleanRealtimeToolCapabilities(capabilities);
|
|
248
|
+
if (!token)
|
|
249
|
+
return null;
|
|
250
|
+
const capability = capabilities.get(token);
|
|
251
|
+
if (!capability)
|
|
252
|
+
return null;
|
|
253
|
+
if (capability.userEmail !== auth.userEmail.trim().toLowerCase() ||
|
|
254
|
+
capability.orgId !== auth.orgId ||
|
|
255
|
+
capability.browserTabId !== auth.browserTabId) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
capability.expiresAt = Date.now() + REALTIME_VOICE_TOOL_GRANT_TTL_MS;
|
|
259
|
+
return capability;
|
|
260
|
+
}
|
|
261
|
+
function parseSuccessfulToolSearchNames(output) {
|
|
262
|
+
try {
|
|
263
|
+
const parsed = JSON.parse(output);
|
|
264
|
+
if (!Array.isArray(parsed.results))
|
|
265
|
+
return [];
|
|
266
|
+
const names = [];
|
|
267
|
+
for (const result of parsed.results) {
|
|
268
|
+
if (!isRecord(result) || typeof result.name !== "string")
|
|
269
|
+
continue;
|
|
270
|
+
if (!REALTIME_TOOL_NAME.test(result.name))
|
|
271
|
+
continue;
|
|
272
|
+
if (!names.includes(result.name))
|
|
273
|
+
names.push(result.name);
|
|
274
|
+
if (names.length >= REALTIME_VOICE_MAX_TOOLS)
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
return names;
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function grantDiscoveredRealtimeTools(input) {
|
|
284
|
+
const query = input.request.args.query;
|
|
285
|
+
if (input.request.name !== TOOL_SEARCH_ACTION_NAME ||
|
|
286
|
+
input.result.status !== "completed" ||
|
|
287
|
+
typeof query !== "string" ||
|
|
288
|
+
!query.trim()) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
const candidates = parseSuccessfulToolSearchNames(input.result.output)
|
|
292
|
+
.filter((name) => !input.initialAllowedNames.has(name))
|
|
293
|
+
.map((name) => input.toolsByName.get(name))
|
|
294
|
+
.filter((tool) => Boolean(tool));
|
|
295
|
+
const boundedTools = packRealtimeTools({}, candidates);
|
|
296
|
+
const expandedTools = [];
|
|
297
|
+
for (const tool of boundedTools) {
|
|
298
|
+
if (!input.capability.names.has(tool.name) &&
|
|
299
|
+
input.capability.names.size >= REALTIME_VOICE_MAX_TOOLS) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
input.capability.names.add(tool.name);
|
|
303
|
+
expandedTools.push(tool);
|
|
304
|
+
}
|
|
305
|
+
input.capability.expiresAt = Date.now() + REALTIME_VOICE_TOOL_GRANT_TTL_MS;
|
|
306
|
+
return expandedTools;
|
|
307
|
+
}
|
|
132
308
|
function declaredBodyBytes(event) {
|
|
133
309
|
const raw = readSafeHeader(event, "content-length");
|
|
134
310
|
if (!raw)
|
|
@@ -188,7 +364,7 @@ function invalidMethod(event) {
|
|
|
188
364
|
setResponseStatus(event, 405);
|
|
189
365
|
return { error: "Method not allowed" };
|
|
190
366
|
}
|
|
191
|
-
function createSessionHandler(tools, options) {
|
|
367
|
+
function createSessionHandler(tools, capabilities, options) {
|
|
192
368
|
return defineEventHandler(async (event) => {
|
|
193
369
|
if (getMethod(event) !== "POST")
|
|
194
370
|
return invalidMethod(event);
|
|
@@ -241,14 +417,22 @@ function createSessionHandler(tools, options) {
|
|
|
241
417
|
};
|
|
242
418
|
}
|
|
243
419
|
const instructions = await buildInstructions(auth, options);
|
|
420
|
+
const transcriptionLanguage = resolveRealtimeVoiceLanguagePreference(readSafeHeader(event, "x-agent-native-realtime-language"), readSafeHeader(event, "accept-language"));
|
|
421
|
+
const reasoningEffort = resolveRealtimeVoiceReasoningEffort(readSafeHeader(event, "x-agent-native-realtime-intelligence"));
|
|
422
|
+
const voice = resolveRealtimeVoicePreference(readSafeHeader(event, "x-agent-native-realtime-voice"), configuredIdentifier(options.voice, DEFAULT_VOICE));
|
|
244
423
|
const sessionBase = {
|
|
245
424
|
type: "realtime",
|
|
246
425
|
model: configuredIdentifier(options.model, DEFAULT_MODEL),
|
|
247
426
|
instructions,
|
|
427
|
+
parallel_tool_calls: false,
|
|
428
|
+
reasoning: { effort: reasoningEffort },
|
|
248
429
|
output_modalities: ["audio"],
|
|
249
430
|
audio: {
|
|
250
431
|
input: {
|
|
251
|
-
transcription: {
|
|
432
|
+
transcription: {
|
|
433
|
+
model: "gpt-4o-mini-transcribe",
|
|
434
|
+
language: transcriptionLanguage,
|
|
435
|
+
},
|
|
252
436
|
turn_detection: {
|
|
253
437
|
type: "semantic_vad",
|
|
254
438
|
create_response: true,
|
|
@@ -257,13 +441,14 @@ function createSessionHandler(tools, options) {
|
|
|
257
441
|
},
|
|
258
442
|
},
|
|
259
443
|
output: {
|
|
260
|
-
voice
|
|
444
|
+
voice,
|
|
261
445
|
},
|
|
262
446
|
},
|
|
263
447
|
};
|
|
448
|
+
const packedTools = packRealtimeTools(sessionBase, tools);
|
|
264
449
|
const session = {
|
|
265
450
|
...sessionBase,
|
|
266
|
-
tools:
|
|
451
|
+
tools: packedTools,
|
|
267
452
|
tool_choice: "auto",
|
|
268
453
|
};
|
|
269
454
|
let upstream;
|
|
@@ -325,6 +510,7 @@ function createSessionHandler(tools, options) {
|
|
|
325
510
|
}
|
|
326
511
|
setResponseStatus(event, upstream.status);
|
|
327
512
|
setResponseHeader(event, "Content-Type", "application/sdp");
|
|
513
|
+
setResponseHeader(event, REALTIME_VOICE_CAPABILITY_HEADER, registerRealtimeToolCapability(capabilities, auth, packedTools.map((tool) => tool.name)));
|
|
328
514
|
return answerSdp;
|
|
329
515
|
});
|
|
330
516
|
});
|
|
@@ -377,7 +563,7 @@ function normalizeExecutionResult(result) {
|
|
|
377
563
|
: {}),
|
|
378
564
|
};
|
|
379
565
|
}
|
|
380
|
-
function createToolHandler(
|
|
566
|
+
function createToolHandler(toolsByName, capabilities, options) {
|
|
381
567
|
return defineEventHandler(async (event) => {
|
|
382
568
|
if (getMethod(event) !== "POST")
|
|
383
569
|
return invalidMethod(event);
|
|
@@ -391,6 +577,11 @@ function createToolHandler(allowedToolNames, options) {
|
|
|
391
577
|
setResponseStatus(event, 401);
|
|
392
578
|
return { error: "Authentication required" };
|
|
393
579
|
}
|
|
580
|
+
const capability = resolveRealtimeToolCapability(capabilities, readSafeHeader(event, REALTIME_VOICE_CAPABILITY_HEADER), auth);
|
|
581
|
+
if (!capability) {
|
|
582
|
+
setResponseStatus(event, 403);
|
|
583
|
+
return { error: "Invalid or expired realtime voice capability" };
|
|
584
|
+
}
|
|
394
585
|
const contentType = readSafeHeader(event, "content-type")?.toLowerCase();
|
|
395
586
|
if (!contentType?.includes("application/json")) {
|
|
396
587
|
setResponseStatus(event, 415);
|
|
@@ -416,11 +607,17 @@ function createToolHandler(allowedToolNames, options) {
|
|
|
416
607
|
setResponseStatus(event, 400);
|
|
417
608
|
return { error: "Invalid realtime tool request" };
|
|
418
609
|
}
|
|
419
|
-
if (
|
|
610
|
+
if (request.browserTabId !== undefined &&
|
|
611
|
+
request.browserTabId !== capability.browserTabId) {
|
|
612
|
+
setResponseStatus(event, 403);
|
|
613
|
+
return { error: "Realtime voice browser tab mismatch" };
|
|
614
|
+
}
|
|
615
|
+
if (!capability.initialNames.has(request.name) &&
|
|
616
|
+
!capability.names.has(request.name)) {
|
|
420
617
|
setResponseStatus(event, 404);
|
|
421
618
|
return { error: "Unknown realtime voice tool" };
|
|
422
619
|
}
|
|
423
|
-
const browserTabId =
|
|
620
|
+
const browserTabId = capability.browserTabId;
|
|
424
621
|
const threadId = request.sessionId
|
|
425
622
|
? `realtime:${request.sessionId}`
|
|
426
623
|
: `realtime:${request.callId}`;
|
|
@@ -439,8 +636,20 @@ function createToolHandler(allowedToolNames, options) {
|
|
|
439
636
|
userEmail: auth.userEmail,
|
|
440
637
|
orgId: auth.orgId,
|
|
441
638
|
...request,
|
|
639
|
+
...(browserTabId ? { browserTabId } : {}),
|
|
442
640
|
}));
|
|
443
|
-
|
|
641
|
+
const expandedTools = grantDiscoveredRealtimeTools({
|
|
642
|
+
request,
|
|
643
|
+
result,
|
|
644
|
+
toolsByName,
|
|
645
|
+
initialAllowedNames: capability.initialNames,
|
|
646
|
+
capability,
|
|
647
|
+
});
|
|
648
|
+
return {
|
|
649
|
+
callId: request.callId,
|
|
650
|
+
...result,
|
|
651
|
+
...(expandedTools.length > 0 ? { expandedTools } : {}),
|
|
652
|
+
};
|
|
444
653
|
}
|
|
445
654
|
catch (error) {
|
|
446
655
|
setResponseStatus(event, 500);
|
|
@@ -459,10 +668,11 @@ export function mountRealtimeVoiceRoutes(nitroApp, actions, options) {
|
|
|
459
668
|
throw new Error("mountRealtimeVoiceRoutes requires executeTool");
|
|
460
669
|
}
|
|
461
670
|
const tools = buildRealtimeTools(actions);
|
|
462
|
-
const
|
|
671
|
+
const toolsByName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
672
|
+
const capabilities = new Map();
|
|
463
673
|
const app = getH3App(nitroApp);
|
|
464
|
-
app.use(REALTIME_VOICE_SESSION_PATH, createSessionHandler(tools, options));
|
|
465
|
-
app.use(REALTIME_VOICE_TOOL_PATH, createToolHandler(
|
|
674
|
+
app.use(REALTIME_VOICE_SESSION_PATH, createSessionHandler(tools, capabilities, options));
|
|
675
|
+
app.use(REALTIME_VOICE_TOOL_PATH, createToolHandler(toolsByName, capabilities, options));
|
|
466
676
|
return {
|
|
467
677
|
sessionPath: REALTIME_VOICE_SESSION_PATH,
|
|
468
678
|
toolPath: REALTIME_VOICE_TOOL_PATH,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-voice.js","sourceRoot":"","sources":["../../src/server/realtime-voice.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getHeader,\n getMethod,\n readRawBody,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { getBuilderGatewayRequestHeaders } from \"../agent/engine/builder-gateway-headers.js\";\nimport {\n actionsToEngineTools,\n type ActionEntry,\n} from \"../agent/production-agent.js\";\nimport {\n redactSensitiveFields,\n sanitizeToolErrorText,\n sanitizeToolErrorValue,\n} from \"../agent/tool-error-redaction.js\";\nimport { getSession } from \"./auth.js\";\nimport {\n getBuilderGatewayBaseUrl,\n resolveBuilderCredentials,\n resolveSecret,\n} from \"./credential-provider.js\";\nimport { getH3App } from \"./framework-request-handler.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { isSameOriginRequest } from \"./request-origin.js\";\n\nexport const REALTIME_VOICE_SESSION_PATH =\n \"/_agent-native/realtime-voice/session\";\nexport const REALTIME_VOICE_TOOL_PATH = \"/_agent-native/realtime-voice/tool\";\nexport const REALTIME_VOICE_MAX_SDP_BYTES = 64 * 1024;\nexport const REALTIME_VOICE_MAX_TOOL_BODY_BYTES = 64 * 1024;\nexport const REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS = 16_000;\nexport const REALTIME_VOICE_MAX_TOOLS = 32;\nexport const REALTIME_VOICE_MAX_TOOL_SCHEMA_BYTES = 32_000;\nexport const REALTIME_VOICE_MAX_SESSION_BYTES = 64_000;\n\nconst OPENAI_REALTIME_CALLS_URL = \"https://api.openai.com/v1/realtime/calls\";\nconst DEFAULT_MODEL = \"gpt-realtime-2.1\";\nconst DEFAULT_VOICE = \"marin\";\nconst DEFAULT_INSTRUCTIONS =\n \"You are the live voice interface for this Agent Native app. Speak naturally, briefly, and conversationally. Use the available function tools when the user asks you to navigate or take an action. Never claim an action succeeded until its tool result confirms success. If a tool requires approval, explain that the user must approve it in chat.\";\nconst MAX_INSTRUCTIONS_CHARS = 16_000;\nconst MAX_TOOL_DESCRIPTION_CHARS = 2_000;\nconst MAX_APPROVAL_KEY_CHARS = 1_024;\nconst REALTIME_TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/;\nconst CALL_ID = /^[A-Za-z0-9_-]{1,256}$/;\nconst SESSION_ID = /^[A-Za-z0-9_.:-]{1,256}$/;\nconst BROWSER_TAB_ID = /^[A-Za-z0-9_-]{1,96}$/;\n\nexport interface RealtimeVoiceRequestContext {\n event: H3Event;\n userEmail: string;\n orgId?: string;\n browserTabId?: string;\n}\n\nexport interface RealtimeVoiceToolExecutionRequest extends RealtimeVoiceRequestContext {\n name: string;\n args: Record<string, unknown>;\n callId: string;\n sessionId?: string;\n}\n\nexport interface RealtimeVoiceToolExecutionResult {\n status: \"completed\" | \"failed\" | \"approval_required\";\n output: string;\n approvalKey?: string;\n}\n\nexport interface MountRealtimeVoiceRoutesOptions {\n /** Server-controlled model. Defaults to gpt-realtime-2.1. */\n model?: string;\n /** Server-controlled output voice. Defaults to marin. */\n voice?: string;\n /** Static app guidance appended to the safe default voice instructions. */\n instructions?: string;\n /** Per-request app/navigation guidance. It is sent only to OpenAI. */\n getInstructions?: (\n context: RealtimeVoiceRequestContext,\n ) => string | null | undefined | Promise<string | null | undefined>;\n /** Optional app-specific active-organization resolver. */\n resolveOrgId?: (\n event: H3Event,\n ) => string | null | undefined | Promise<string | null | undefined>;\n /**\n * Central agent tool executor supplied by the agent-chat plugin. The executor\n * owns validation, approval, journaling, timeout, mutation notification, and\n * action-result normalization; this transport must not call ActionEntry.run.\n */\n executeTool: (\n request: RealtimeVoiceToolExecutionRequest,\n ) =>\n | RealtimeVoiceToolExecutionResult\n | Promise<RealtimeVoiceToolExecutionResult>;\n}\n\ninterface RealtimeFunctionTool {\n type: \"function\";\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}\n\ninterface AuthenticatedVoiceContext extends RealtimeVoiceRequestContext {\n timezone?: string;\n}\n\nfunction readSafeHeader(event: H3Event, name: string): string | undefined {\n const value = getHeader(event, name);\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction configuredIdentifier(\n value: string | undefined,\n fallback: string,\n): string {\n const trimmed = value?.trim();\n return trimmed && /^[A-Za-z0-9_.:-]{1,128}$/.test(trimmed)\n ? trimmed\n : fallback;\n}\n\nfunction truncate(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n return `${value.slice(0, maxChars)}\\n\\n...[truncated]`;\n}\n\nfunction sanitizeOutput(value: unknown, maxChars: number): string {\n let serialized: string;\n try {\n const redacted = redactSensitiveFields(value);\n serialized =\n typeof redacted === \"string\"\n ? redacted\n : (JSON.stringify(redacted, (_key, entry) =>\n typeof entry === \"bigint\" ? entry.toString() : entry,\n ) ?? \"null\");\n } catch {\n serialized = \"[Unserializable tool result]\";\n }\n return truncate(sanitizeToolErrorText(serialized), maxChars);\n}\n\nasync function safeOpenAiErrorDetail(\n response: Response,\n apiKey: string,\n): Promise<string | null> {\n const raw = await response.text().catch(() => \"\");\n if (!raw) return null;\n let detail = raw;\n try {\n const parsed = JSON.parse(raw) as {\n error?: { message?: unknown; code?: unknown; type?: unknown } | unknown;\n };\n if (parsed.error && typeof parsed.error === \"object\") {\n const error = parsed.error as {\n message?: unknown;\n code?: unknown;\n type?: unknown;\n };\n detail = [error.message, error.code, error.type]\n .filter((value) => typeof value === \"string\" && value.trim())\n .join(\" · \");\n }\n } catch {\n // Plain-text upstream errors are sanitized below.\n }\n const redacted = sanitizeToolErrorText(detail).replaceAll(\n apiKey,\n \"[REDACTED]\",\n );\n return truncate(redacted, 500) || null;\n}\n\nfunction normalizeToolSchema(\n inputSchema: unknown,\n): Record<string, unknown> | null {\n try {\n const serialized = JSON.stringify(inputSchema);\n if (\n !serialized ||\n Buffer.byteLength(serialized, \"utf8\") >\n REALTIME_VOICE_MAX_TOOL_SCHEMA_BYTES\n ) {\n return null;\n }\n const parsed = JSON.parse(serialized) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction buildRealtimeTools(\n actions: Record<string, ActionEntry>,\n): RealtimeFunctionTool[] {\n const tools: RealtimeFunctionTool[] = [];\n for (const tool of actionsToEngineTools(actions)) {\n if (!REALTIME_TOOL_NAME.test(tool.name)) continue;\n const parameters = normalizeToolSchema(tool.inputSchema);\n if (!parameters) continue;\n tools.push({\n type: \"function\",\n name: tool.name,\n description: truncate(\n sanitizeToolErrorText(tool.description || \"\"),\n MAX_TOOL_DESCRIPTION_CHARS,\n ),\n parameters,\n });\n }\n return tools;\n}\n\nfunction packRealtimeTools(\n session: Record<string, unknown>,\n eligibleTools: RealtimeFunctionTool[],\n): RealtimeFunctionTool[] {\n const packed: RealtimeFunctionTool[] = [];\n for (const tool of eligibleTools.slice(0, REALTIME_VOICE_MAX_TOOLS)) {\n const candidate = [...packed, tool];\n const candidateSession = {\n ...session,\n tools: candidate,\n tool_choice: \"auto\",\n };\n if (\n Buffer.byteLength(JSON.stringify(candidateSession), \"utf8\") <=\n REALTIME_VOICE_MAX_SESSION_BYTES\n ) {\n packed.push(tool);\n }\n }\n return packed;\n}\n\nfunction declaredBodyBytes(event: H3Event): number | undefined {\n const raw = readSafeHeader(event, \"content-length\");\n if (!raw) return undefined;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;\n}\n\nasync function readLimitedRawBody(\n event: H3Event,\n maxBytes: number,\n): Promise<string | null | \"oversize\"> {\n const declared = declaredBodyBytes(event);\n if (declared !== undefined && declared > maxBytes) return \"oversize\";\n\n const raw = await readRawBody(event, \"utf8\").catch(() => undefined);\n if (raw == null) return null;\n const text =\n typeof raw === \"string\" ? raw : new TextDecoder().decode(raw as Uint8Array);\n return new TextEncoder().encode(text).byteLength > maxBytes\n ? \"oversize\"\n : text;\n}\n\nasync function authenticateVoiceRequest(\n event: H3Event,\n options: MountRealtimeVoiceRoutesOptions,\n): Promise<AuthenticatedVoiceContext | null> {\n const session = await getSession(event).catch(() => null);\n if (!session?.email) return null;\n const resolvedOrgId = options.resolveOrgId\n ? await options.resolveOrgId(event)\n : session.orgId;\n const timezone = readSafeHeader(event, \"x-user-timezone\");\n const rawBrowserTabId = readSafeHeader(event, \"x-agent-native-browser-tab\");\n const browserTabId =\n rawBrowserTabId && BROWSER_TAB_ID.test(rawBrowserTabId)\n ? rawBrowserTabId\n : undefined;\n return {\n event,\n userEmail: session.email,\n ...(resolvedOrgId ? { orgId: resolvedOrgId } : {}),\n ...(timezone && timezone.length < 64 ? { timezone } : {}),\n ...(browserTabId ? { browserTabId } : {}),\n };\n}\n\nasync function buildInstructions(\n context: RealtimeVoiceRequestContext,\n options: MountRealtimeVoiceRoutesOptions,\n): Promise<string> {\n const dynamic = await options.getInstructions?.(context);\n const appInstructions = dynamic ?? options.instructions;\n if (!appInstructions?.trim()) return DEFAULT_INSTRUCTIONS;\n return truncate(\n sanitizeToolErrorText(\n `${DEFAULT_INSTRUCTIONS}\\n\\n${appInstructions.trim()}`,\n ),\n MAX_INSTRUCTIONS_CHARS,\n );\n}\n\n/**\n * Hash the authenticated identity before sending it to OpenAI. The stable\n * digest is useful for abuse detection without disclosing the user's email.\n */\nexport async function realtimeVoiceSafetyIdentifier(\n userEmail: string,\n): Promise<string> {\n const input = new TextEncoder().encode(\n `agent-native:${userEmail.trim().toLowerCase()}`,\n );\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", input);\n return Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n\nfunction invalidMethod(event: H3Event): { error: string } {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n}\n\nfunction createSessionHandler(\n tools: RealtimeFunctionTool[],\n options: MountRealtimeVoiceRoutesOptions,\n) {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"POST\") return invalidMethod(event);\n if (!isSameOriginRequest(event)) {\n setResponseStatus(event, 403);\n return { error: \"Cross-origin request rejected\" };\n }\n setResponseHeader(event, \"Cache-Control\", \"no-store\");\n\n const auth = await authenticateVoiceRequest(event, options);\n if (!auth) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const contentType = readSafeHeader(event, \"content-type\")?.toLowerCase();\n if (!contentType?.includes(\"application/sdp\")) {\n setResponseStatus(event, 415);\n return { error: \"Expected Content-Type: application/sdp\" };\n }\n\n const rawSdp = await readLimitedRawBody(\n event,\n REALTIME_VOICE_MAX_SDP_BYTES,\n );\n if (rawSdp === \"oversize\") {\n setResponseStatus(event, 413);\n return {\n error: `SDP offer is too large (max ${REALTIME_VOICE_MAX_SDP_BYTES} bytes)`,\n };\n }\n const sdp = rawSdp ?? \"\";\n if (!sdp.trim()) {\n setResponseStatus(event, 400);\n return { error: \"SDP offer is required\" };\n }\n\n return runWithRequestContext(\n {\n userEmail: auth.userEmail,\n orgId: auth.orgId,\n timezone: auth.timezone,\n run: auth.browserTabId\n ? { browserTabId: auth.browserTabId }\n : undefined,\n },\n async () => {\n const builderCredentials = await resolveBuilderCredentials();\n const builderConfigured = Boolean(\n builderCredentials.privateKey?.trim() &&\n builderCredentials.publicKey?.trim(),\n );\n const apiKey = builderConfigured\n ? null\n : (await resolveSecret(\"OPENAI_API_KEY\"))?.trim();\n if (!builderConfigured && !apiKey) {\n setResponseStatus(event, 409);\n return {\n error:\n \"Connect Builder or configure an OpenAI API key to use realtime voice.\",\n code: \"realtime_voice_setup_required\",\n };\n }\n\n const instructions = await buildInstructions(auth, options);\n const sessionBase = {\n type: \"realtime\",\n model: configuredIdentifier(options.model, DEFAULT_MODEL),\n instructions,\n output_modalities: [\"audio\"],\n audio: {\n input: {\n transcription: { model: \"gpt-4o-mini-transcribe\" },\n turn_detection: {\n type: \"semantic_vad\",\n create_response: true,\n interrupt_response: true,\n eagerness: \"auto\",\n },\n },\n output: {\n voice: configuredIdentifier(options.voice, DEFAULT_VOICE),\n },\n },\n };\n const session = {\n ...sessionBase,\n tools: packRealtimeTools(sessionBase, tools),\n tool_choice: \"auto\",\n };\n\n let upstream: Response;\n try {\n if (builderConfigured) {\n const gatewayUrl = new URL(\n \"realtime/calls\",\n getBuilderGatewayBaseUrl().endsWith(\"/\")\n ? getBuilderGatewayBaseUrl()\n : `${getBuilderGatewayBaseUrl()}/`,\n );\n gatewayUrl.searchParams.set(\n \"apiKey\",\n builderCredentials.publicKey!.trim(),\n );\n upstream = await fetch(gatewayUrl.toString(), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${builderCredentials.privateKey!.trim()}`,\n \"x-builder-api-key\": builderCredentials.publicKey!.trim(),\n ...getBuilderGatewayRequestHeaders(),\n ...(builderCredentials.userId\n ? { \"x-builder-user-id\": builderCredentials.userId }\n : {}),\n },\n body: JSON.stringify({ sdp, session }),\n });\n } else {\n const form = new FormData();\n form.set(\"sdp\", sdp);\n form.set(\"session\", JSON.stringify(session));\n upstream = await fetch(OPENAI_REALTIME_CALLS_URL, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"OpenAI-Safety-Identifier\": await realtimeVoiceSafetyIdentifier(\n auth.userEmail,\n ),\n },\n body: form,\n });\n }\n } catch {\n setResponseStatus(event, 502);\n return {\n error: builderConfigured\n ? \"Could not reach the Builder realtime voice gateway\"\n : \"Could not reach the OpenAI Realtime API\",\n };\n }\n\n if (!upstream.ok) {\n const detail = await safeOpenAiErrorDetail(\n upstream,\n builderConfigured ? builderCredentials.privateKey! : apiKey!,\n );\n setResponseStatus(event, builderConfigured ? upstream.status : 502);\n return {\n error: `${builderConfigured ? \"Builder\" : \"OpenAI\"} rejected the realtime session (${upstream.status})${detail ? `: ${detail}` : \"\"}`,\n };\n }\n\n const answerSdp = await upstream.text().catch(() => \"\");\n if (!answerSdp.trim()) {\n setResponseStatus(event, 502);\n return {\n error: `${builderConfigured ? \"Builder\" : \"OpenAI\"} returned an empty realtime session answer`,\n };\n }\n\n setResponseStatus(event, upstream.status);\n setResponseHeader(event, \"Content-Type\", \"application/sdp\");\n return answerSdp;\n },\n );\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parseToolRequest(value: unknown): {\n name: string;\n args: Record<string, unknown>;\n callId: string;\n sessionId?: string;\n browserTabId?: string;\n} | null {\n if (!isRecord(value)) return null;\n const { name, args, callId, sessionId, browserTabId } = value;\n if (typeof name !== \"string\" || !REALTIME_TOOL_NAME.test(name)) return null;\n if (typeof callId !== \"string\" || !CALL_ID.test(callId)) return null;\n if (!isRecord(args)) return null;\n if (\n sessionId !== undefined &&\n (typeof sessionId !== \"string\" || !SESSION_ID.test(sessionId))\n ) {\n return null;\n }\n if (\n browserTabId !== undefined &&\n (typeof browserTabId !== \"string\" || !BROWSER_TAB_ID.test(browserTabId))\n ) {\n return null;\n }\n return {\n name,\n args,\n callId,\n ...(sessionId ? { sessionId } : {}),\n ...(browserTabId ? { browserTabId } : {}),\n };\n}\n\nfunction normalizeExecutionResult(\n result: RealtimeVoiceToolExecutionResult,\n): RealtimeVoiceToolExecutionResult {\n const status = result?.status;\n if (\n status !== \"completed\" &&\n status !== \"failed\" &&\n status !== \"approval_required\"\n ) {\n throw new Error(\"Invalid realtime tool execution status\");\n }\n const output = sanitizeOutput(\n result.output,\n REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS,\n );\n return {\n status,\n output,\n ...(status === \"approval_required\" &&\n typeof result.approvalKey === \"string\" &&\n result.approvalKey.length > 0 &&\n result.approvalKey.length <= MAX_APPROVAL_KEY_CHARS\n ? { approvalKey: result.approvalKey }\n : {}),\n };\n}\n\nfunction createToolHandler(\n allowedToolNames: ReadonlySet<string>,\n options: MountRealtimeVoiceRoutesOptions,\n) {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"POST\") return invalidMethod(event);\n if (!isSameOriginRequest(event)) {\n setResponseStatus(event, 403);\n return { error: \"Cross-origin request rejected\" };\n }\n setResponseHeader(event, \"Cache-Control\", \"no-store\");\n\n const auth = await authenticateVoiceRequest(event, options);\n if (!auth) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const contentType = readSafeHeader(event, \"content-type\")?.toLowerCase();\n if (!contentType?.includes(\"application/json\")) {\n setResponseStatus(event, 415);\n return { error: \"Expected Content-Type: application/json\" };\n }\n\n const raw = await readLimitedRawBody(\n event,\n REALTIME_VOICE_MAX_TOOL_BODY_BYTES,\n );\n if (raw === \"oversize\") {\n setResponseStatus(event, 413);\n return {\n error: `Tool request is too large (max ${REALTIME_VOICE_MAX_TOOL_BODY_BYTES} bytes)`,\n };\n }\n\n let parsedBody: unknown;\n try {\n parsedBody = JSON.parse(raw ?? \"\");\n } catch {\n setResponseStatus(event, 400);\n return { error: \"Invalid realtime tool request\" };\n }\n const request = parseToolRequest(parsedBody);\n if (!request) {\n setResponseStatus(event, 400);\n return { error: \"Invalid realtime tool request\" };\n }\n if (!allowedToolNames.has(request.name)) {\n setResponseStatus(event, 404);\n return { error: \"Unknown realtime voice tool\" };\n }\n\n const browserTabId = request.browserTabId ?? auth.browserTabId;\n const threadId = request.sessionId\n ? `realtime:${request.sessionId}`\n : `realtime:${request.callId}`;\n return runWithRequestContext(\n {\n userEmail: auth.userEmail,\n orgId: auth.orgId,\n timezone: auth.timezone,\n run: {\n threadId,\n ...(browserTabId ? { browserTabId } : {}),\n },\n },\n async () => {\n try {\n const result = normalizeExecutionResult(\n await options.executeTool({\n event,\n userEmail: auth.userEmail,\n orgId: auth.orgId,\n ...request,\n }),\n );\n return { callId: request.callId, ...result };\n } catch (error) {\n setResponseStatus(event, 500);\n return {\n callId: request.callId,\n status: \"failed\" as const,\n output: truncate(\n sanitizeToolErrorValue(error) || \"Tool execution failed\",\n REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS,\n ),\n };\n }\n },\n );\n });\n}\n\n/** Mount the authenticated OpenAI Realtime WebRTC and tool bridge routes. */\nexport function mountRealtimeVoiceRoutes(\n nitroApp: any,\n actions: Record<string, ActionEntry>,\n options: MountRealtimeVoiceRoutesOptions,\n): { sessionPath: string; toolPath: string } {\n if (typeof options?.executeTool !== \"function\") {\n throw new Error(\"mountRealtimeVoiceRoutes requires executeTool\");\n }\n\n const tools = buildRealtimeTools(actions);\n const allowedToolNames = new Set(\n tools.slice(0, REALTIME_VOICE_MAX_TOOLS).map((tool) => tool.name),\n );\n const app = getH3App(nitroApp);\n app.use(REALTIME_VOICE_SESSION_PATH, createSessionHandler(tools, options));\n app.use(\n REALTIME_VOICE_TOOL_PATH,\n createToolHandler(allowedToolNames, options),\n );\n return {\n sessionPath: REALTIME_VOICE_SESSION_PATH,\n toolPath: REALTIME_VOICE_TOOL_PATH,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"realtime-voice.js","sourceRoot":"","sources":["../../src/server/realtime-voice.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getHeader,\n getMethod,\n readRawBody,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { getBuilderGatewayRequestHeaders } from \"../agent/engine/builder-gateway-headers.js\";\nimport {\n actionsToEngineTools,\n type ActionEntry,\n} from \"../agent/production-agent.js\";\nimport {\n redactSensitiveFields,\n sanitizeToolErrorText,\n sanitizeToolErrorValue,\n} from \"../agent/tool-error-redaction.js\";\nimport { TOOL_SEARCH_ACTION_NAME } from \"../agent/tool-search.js\";\nimport { parseAcceptLanguage } from \"../localization/server.js\";\nimport { getSession } from \"./auth.js\";\nimport {\n getBuilderGatewayBaseUrl,\n resolveBuilderCredentials,\n resolveSecret,\n} from \"./credential-provider.js\";\nimport { getH3App } from \"./framework-request-handler.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { isSameOriginRequest } from \"./request-origin.js\";\n\nexport const REALTIME_VOICE_SESSION_PATH =\n \"/_agent-native/realtime-voice/session\";\nexport const REALTIME_VOICE_TOOL_PATH = \"/_agent-native/realtime-voice/tool\";\nexport const REALTIME_VOICE_MAX_SDP_BYTES = 64 * 1024;\nexport const REALTIME_VOICE_MAX_TOOL_BODY_BYTES = 64 * 1024;\nexport const REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS = 16_000;\nexport const REALTIME_VOICE_MAX_TOOLS = 32;\nexport const REALTIME_VOICE_MAX_TOOL_SCHEMA_BYTES = 32_000;\nexport const REALTIME_VOICE_MAX_SESSION_BYTES = 64_000;\nexport const REALTIME_VOICE_TOOL_GRANT_TTL_MS = 10 * 60 * 1_000;\nexport const REALTIME_VOICE_MAX_TOOL_GRANT_SESSIONS = 256;\nexport const REALTIME_VOICE_CAPABILITY_HEADER =\n \"X-Agent-Native-Realtime-Capability\";\n\nconst OPENAI_REALTIME_CALLS_URL = \"https://api.openai.com/v1/realtime/calls\";\nconst DEFAULT_MODEL = \"gpt-realtime-2.1\";\nconst DEFAULT_VOICE = \"marin\";\nconst DEFAULT_INSTRUCTIONS =\n \"You are the live voice interface for this Agent Native app. Speak naturally, briefly, and conversationally. Use the available function tools when the user asks you to navigate or take an action. Never claim an action succeeded until its tool result confirms success. If a tool requires approval, explain that the user must approve it in chat.\";\nconst MAX_INSTRUCTIONS_CHARS = 16_000;\nconst MAX_TOOL_DESCRIPTION_CHARS = 2_000;\nconst MAX_APPROVAL_KEY_CHARS = 1_024;\nconst REALTIME_TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/;\nconst CALL_ID = /^[A-Za-z0-9_-]{1,256}$/;\nconst SESSION_ID = /^[A-Za-z0-9_.:-]{1,256}$/;\nconst BROWSER_TAB_ID = /^[A-Za-z0-9_-]{1,96}$/;\nconst ISO_639_1_LANGUAGE = /^[A-Za-z]{2}$/;\nconst REALTIME_VOICE_BUILT_INS = new Set([\n \"alloy\",\n \"ash\",\n \"ballad\",\n \"coral\",\n \"echo\",\n \"sage\",\n \"shimmer\",\n \"verse\",\n \"marin\",\n \"cedar\",\n]);\nconst REALTIME_VOICE_REASONING_EFFORT = {\n instant: \"minimal\",\n balanced: \"low\",\n deep: \"medium\",\n} as const;\n\n/**\n * Realtime sessions have a deliberately bounded tool manifest. Keep the\n * context/navigation tools ahead of large template registries so voice can\n * always see and operate the same UI navigation surface as text chat.\n */\nconst REALTIME_VOICE_PRIORITY_TOOLS = [\n \"navigate\",\n \"set-url-path\",\n \"set-search-params\",\n \"view-screen\",\n TOOL_SEARCH_ACTION_NAME,\n] as const;\n\nexport interface RealtimeVoiceRequestContext {\n event: H3Event;\n userEmail: string;\n orgId?: string;\n browserTabId?: string;\n}\n\nexport interface RealtimeVoiceToolExecutionRequest extends RealtimeVoiceRequestContext {\n name: string;\n args: Record<string, unknown>;\n callId: string;\n sessionId?: string;\n}\n\nexport interface RealtimeVoiceToolExecutionResult {\n status: \"completed\" | \"failed\" | \"approval_required\";\n output: string;\n approvalKey?: string;\n}\n\nexport interface MountRealtimeVoiceRoutesOptions {\n /** Server-controlled model. Defaults to gpt-realtime-2.1. */\n model?: string;\n /** Server-controlled output voice. Defaults to marin. */\n voice?: string;\n /** Static app guidance appended to the safe default voice instructions. */\n instructions?: string;\n /** Per-request app/navigation guidance. It is sent only to OpenAI. */\n getInstructions?: (\n context: RealtimeVoiceRequestContext,\n ) => string | null | undefined | Promise<string | null | undefined>;\n /** Optional app-specific active-organization resolver. */\n resolveOrgId?: (\n event: H3Event,\n ) => string | null | undefined | Promise<string | null | undefined>;\n /**\n * Central agent tool executor supplied by the agent-chat plugin. The executor\n * owns validation, approval, journaling, timeout, mutation notification, and\n * action-result normalization; this transport must not call ActionEntry.run.\n */\n executeTool: (\n request: RealtimeVoiceToolExecutionRequest,\n ) =>\n | RealtimeVoiceToolExecutionResult\n | Promise<RealtimeVoiceToolExecutionResult>;\n}\n\ninterface RealtimeFunctionTool {\n type: \"function\";\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}\n\ninterface RealtimeToolCapability {\n userEmail: string;\n orgId?: string;\n browserTabId?: string;\n expiresAt: number;\n initialNames: Set<string>;\n names: Set<string>;\n}\n\ntype RealtimeToolCapabilityStore = Map<string, RealtimeToolCapability>;\n\ninterface AuthenticatedVoiceContext extends RealtimeVoiceRequestContext {\n timezone?: string;\n}\n\nfunction readSafeHeader(event: H3Event, name: string): string | undefined {\n const value = getHeader(event, name);\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction configuredIdentifier(\n value: string | undefined,\n fallback: string,\n): string {\n const trimmed = value?.trim();\n return trimmed && /^[A-Za-z0-9_.:-]{1,128}$/.test(trimmed)\n ? trimmed\n : fallback;\n}\n\nexport function resolveRealtimeVoiceTranscriptionLanguage(\n acceptLanguage: string | null | undefined,\n): string {\n for (const locale of parseAcceptLanguage(acceptLanguage)) {\n const primaryLanguage = locale.split(\"-\")[0];\n if (ISO_639_1_LANGUAGE.test(primaryLanguage)) {\n return primaryLanguage.toLowerCase();\n }\n }\n return \"en\";\n}\n\nexport function resolveRealtimeVoiceLanguagePreference(\n value: string | null | undefined,\n acceptLanguage: string | null | undefined,\n): string {\n const normalized = value?.trim().toLowerCase();\n return normalized && ISO_639_1_LANGUAGE.test(normalized)\n ? normalized\n : resolveRealtimeVoiceTranscriptionLanguage(acceptLanguage);\n}\n\nexport function resolveRealtimeVoiceReasoningEffort(\n value: string | null | undefined,\n): \"minimal\" | \"low\" | \"medium\" {\n const normalized = value?.trim().toLowerCase();\n return normalized &&\n Object.hasOwn(REALTIME_VOICE_REASONING_EFFORT, normalized)\n ? REALTIME_VOICE_REASONING_EFFORT[\n normalized as keyof typeof REALTIME_VOICE_REASONING_EFFORT\n ]\n : \"low\";\n}\n\nexport function resolveRealtimeVoicePreference(\n value: string | null | undefined,\n fallback: string,\n): string {\n const normalized = value?.trim().toLowerCase();\n return normalized && REALTIME_VOICE_BUILT_INS.has(normalized)\n ? normalized\n : fallback;\n}\n\nfunction truncate(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n return `${value.slice(0, maxChars)}\\n\\n...[truncated]`;\n}\n\nfunction sanitizeOutput(value: unknown, maxChars: number): string {\n let serialized: string;\n try {\n const redacted = redactSensitiveFields(value);\n serialized =\n typeof redacted === \"string\"\n ? redacted\n : (JSON.stringify(redacted, (_key, entry) =>\n typeof entry === \"bigint\" ? entry.toString() : entry,\n ) ?? \"null\");\n } catch {\n serialized = \"[Unserializable tool result]\";\n }\n return truncate(sanitizeToolErrorText(serialized), maxChars);\n}\n\nasync function safeOpenAiErrorDetail(\n response: Response,\n apiKey: string,\n): Promise<string | null> {\n const raw = await response.text().catch(() => \"\");\n if (!raw) return null;\n let detail = raw;\n try {\n const parsed = JSON.parse(raw) as {\n error?: { message?: unknown; code?: unknown; type?: unknown } | unknown;\n };\n if (parsed.error && typeof parsed.error === \"object\") {\n const error = parsed.error as {\n message?: unknown;\n code?: unknown;\n type?: unknown;\n };\n detail = [error.message, error.code, error.type]\n .filter((value) => typeof value === \"string\" && value.trim())\n .join(\" · \");\n }\n } catch {\n // Plain-text upstream errors are sanitized below.\n }\n const redacted = sanitizeToolErrorText(detail).replaceAll(\n apiKey,\n \"[REDACTED]\",\n );\n return truncate(redacted, 500) || null;\n}\n\nfunction normalizeToolSchema(\n inputSchema: unknown,\n): Record<string, unknown> | null {\n try {\n const serialized = JSON.stringify(inputSchema);\n if (\n !serialized ||\n Buffer.byteLength(serialized, \"utf8\") >\n REALTIME_VOICE_MAX_TOOL_SCHEMA_BYTES\n ) {\n return null;\n }\n const parsed = JSON.parse(serialized) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction buildRealtimeTools(\n actions: Record<string, ActionEntry>,\n): RealtimeFunctionTool[] {\n const tools: RealtimeFunctionTool[] = [];\n for (const tool of actionsToEngineTools(actions)) {\n if (!REALTIME_TOOL_NAME.test(tool.name)) continue;\n const parameters = normalizeToolSchema(tool.inputSchema);\n if (!parameters) continue;\n tools.push({\n type: \"function\",\n name: tool.name,\n description: truncate(\n sanitizeToolErrorText(tool.description || \"\"),\n MAX_TOOL_DESCRIPTION_CHARS,\n ),\n parameters,\n });\n }\n const priority = new Map<string, number>(\n REALTIME_VOICE_PRIORITY_TOOLS.map((name, index) => [name, index]),\n );\n return tools\n .map((tool, index) => ({ tool, index }))\n .sort((left, right) => {\n const leftPriority = priority.get(left.tool.name);\n const rightPriority = priority.get(right.tool.name);\n if (leftPriority !== undefined || rightPriority !== undefined) {\n if (leftPriority === undefined) return 1;\n if (rightPriority === undefined) return -1;\n return leftPriority - rightPriority;\n }\n return left.index - right.index;\n })\n .map(({ tool }) => tool);\n}\n\nfunction packRealtimeTools(\n session: Record<string, unknown>,\n eligibleTools: RealtimeFunctionTool[],\n): RealtimeFunctionTool[] {\n const packed: RealtimeFunctionTool[] = [];\n for (const tool of eligibleTools.slice(0, REALTIME_VOICE_MAX_TOOLS)) {\n const candidate = [...packed, tool];\n const candidateSession = {\n ...session,\n tools: candidate,\n tool_choice: \"auto\",\n };\n if (\n Buffer.byteLength(JSON.stringify(candidateSession), \"utf8\") <=\n REALTIME_VOICE_MAX_SESSION_BYTES\n ) {\n packed.push(tool);\n }\n }\n return packed;\n}\n\nfunction mintRealtimeToolCapability(): string {\n return globalThis.crypto.randomUUID().replaceAll(\"-\", \"\");\n}\n\nfunction cleanRealtimeToolCapabilities(\n capabilities: RealtimeToolCapabilityStore,\n now = Date.now(),\n): void {\n for (const [key, capability] of capabilities) {\n if (capability.expiresAt <= now) capabilities.delete(key);\n }\n while (capabilities.size > REALTIME_VOICE_MAX_TOOL_GRANT_SESSIONS) {\n let oldestKey: string | undefined;\n let oldestExpiry = Number.POSITIVE_INFINITY;\n for (const [key, capability] of capabilities) {\n if (capability.expiresAt < oldestExpiry) {\n oldestKey = key;\n oldestExpiry = capability.expiresAt;\n }\n }\n if (!oldestKey) break;\n capabilities.delete(oldestKey);\n }\n}\n\nfunction registerRealtimeToolCapability(\n capabilities: RealtimeToolCapabilityStore,\n auth: AuthenticatedVoiceContext,\n initialNames: Iterable<string>,\n): string {\n cleanRealtimeToolCapabilities(capabilities);\n const token = mintRealtimeToolCapability();\n capabilities.set(token, {\n userEmail: auth.userEmail.trim().toLowerCase(),\n ...(auth.orgId ? { orgId: auth.orgId } : {}),\n ...(auth.browserTabId ? { browserTabId: auth.browserTabId } : {}),\n expiresAt: Date.now() + REALTIME_VOICE_TOOL_GRANT_TTL_MS,\n initialNames: new Set(initialNames),\n names: new Set(),\n });\n cleanRealtimeToolCapabilities(capabilities);\n return token;\n}\n\nfunction resolveRealtimeToolCapability(\n capabilities: RealtimeToolCapabilityStore,\n token: string | undefined,\n auth: AuthenticatedVoiceContext,\n): RealtimeToolCapability | null {\n cleanRealtimeToolCapabilities(capabilities);\n if (!token) return null;\n const capability = capabilities.get(token);\n if (!capability) return null;\n if (\n capability.userEmail !== auth.userEmail.trim().toLowerCase() ||\n capability.orgId !== auth.orgId ||\n capability.browserTabId !== auth.browserTabId\n ) {\n return null;\n }\n capability.expiresAt = Date.now() + REALTIME_VOICE_TOOL_GRANT_TTL_MS;\n return capability;\n}\n\nfunction parseSuccessfulToolSearchNames(output: string): string[] {\n try {\n const parsed = JSON.parse(output) as { results?: unknown };\n if (!Array.isArray(parsed.results)) return [];\n const names: string[] = [];\n for (const result of parsed.results) {\n if (!isRecord(result) || typeof result.name !== \"string\") continue;\n if (!REALTIME_TOOL_NAME.test(result.name)) continue;\n if (!names.includes(result.name)) names.push(result.name);\n if (names.length >= REALTIME_VOICE_MAX_TOOLS) break;\n }\n return names;\n } catch {\n return [];\n }\n}\n\nfunction grantDiscoveredRealtimeTools(input: {\n request: NonNullable<ReturnType<typeof parseToolRequest>>;\n result: RealtimeVoiceToolExecutionResult;\n toolsByName: ReadonlyMap<string, RealtimeFunctionTool>;\n initialAllowedNames: ReadonlySet<string>;\n capability: RealtimeToolCapability;\n}): RealtimeFunctionTool[] {\n const query = input.request.args.query;\n if (\n input.request.name !== TOOL_SEARCH_ACTION_NAME ||\n input.result.status !== \"completed\" ||\n typeof query !== \"string\" ||\n !query.trim()\n ) {\n return [];\n }\n\n const candidates = parseSuccessfulToolSearchNames(input.result.output)\n .filter((name) => !input.initialAllowedNames.has(name))\n .map((name) => input.toolsByName.get(name))\n .filter((tool): tool is RealtimeFunctionTool => Boolean(tool));\n const boundedTools = packRealtimeTools({}, candidates);\n const expandedTools: RealtimeFunctionTool[] = [];\n for (const tool of boundedTools) {\n if (\n !input.capability.names.has(tool.name) &&\n input.capability.names.size >= REALTIME_VOICE_MAX_TOOLS\n ) {\n continue;\n }\n input.capability.names.add(tool.name);\n expandedTools.push(tool);\n }\n input.capability.expiresAt = Date.now() + REALTIME_VOICE_TOOL_GRANT_TTL_MS;\n return expandedTools;\n}\n\nfunction declaredBodyBytes(event: H3Event): number | undefined {\n const raw = readSafeHeader(event, \"content-length\");\n if (!raw) return undefined;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;\n}\n\nasync function readLimitedRawBody(\n event: H3Event,\n maxBytes: number,\n): Promise<string | null | \"oversize\"> {\n const declared = declaredBodyBytes(event);\n if (declared !== undefined && declared > maxBytes) return \"oversize\";\n\n const raw = await readRawBody(event, \"utf8\").catch(() => undefined);\n if (raw == null) return null;\n const text =\n typeof raw === \"string\" ? raw : new TextDecoder().decode(raw as Uint8Array);\n return new TextEncoder().encode(text).byteLength > maxBytes\n ? \"oversize\"\n : text;\n}\n\nasync function authenticateVoiceRequest(\n event: H3Event,\n options: MountRealtimeVoiceRoutesOptions,\n): Promise<AuthenticatedVoiceContext | null> {\n const session = await getSession(event).catch(() => null);\n if (!session?.email) return null;\n const resolvedOrgId = options.resolveOrgId\n ? await options.resolveOrgId(event)\n : session.orgId;\n const timezone = readSafeHeader(event, \"x-user-timezone\");\n const rawBrowserTabId = readSafeHeader(event, \"x-agent-native-browser-tab\");\n const browserTabId =\n rawBrowserTabId && BROWSER_TAB_ID.test(rawBrowserTabId)\n ? rawBrowserTabId\n : undefined;\n return {\n event,\n userEmail: session.email,\n ...(resolvedOrgId ? { orgId: resolvedOrgId } : {}),\n ...(timezone && timezone.length < 64 ? { timezone } : {}),\n ...(browserTabId ? { browserTabId } : {}),\n };\n}\n\nasync function buildInstructions(\n context: RealtimeVoiceRequestContext,\n options: MountRealtimeVoiceRoutesOptions,\n): Promise<string> {\n const dynamic = await options.getInstructions?.(context);\n const appInstructions = dynamic ?? options.instructions;\n if (!appInstructions?.trim()) return DEFAULT_INSTRUCTIONS;\n return truncate(\n sanitizeToolErrorText(\n `${DEFAULT_INSTRUCTIONS}\\n\\n${appInstructions.trim()}`,\n ),\n MAX_INSTRUCTIONS_CHARS,\n );\n}\n\n/**\n * Hash the authenticated identity before sending it to OpenAI. The stable\n * digest is useful for abuse detection without disclosing the user's email.\n */\nexport async function realtimeVoiceSafetyIdentifier(\n userEmail: string,\n): Promise<string> {\n const input = new TextEncoder().encode(\n `agent-native:${userEmail.trim().toLowerCase()}`,\n );\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", input);\n return Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n\nfunction invalidMethod(event: H3Event): { error: string } {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n}\n\nfunction createSessionHandler(\n tools: RealtimeFunctionTool[],\n capabilities: RealtimeToolCapabilityStore,\n options: MountRealtimeVoiceRoutesOptions,\n) {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"POST\") return invalidMethod(event);\n if (!isSameOriginRequest(event)) {\n setResponseStatus(event, 403);\n return { error: \"Cross-origin request rejected\" };\n }\n setResponseHeader(event, \"Cache-Control\", \"no-store\");\n\n const auth = await authenticateVoiceRequest(event, options);\n if (!auth) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const contentType = readSafeHeader(event, \"content-type\")?.toLowerCase();\n if (!contentType?.includes(\"application/sdp\")) {\n setResponseStatus(event, 415);\n return { error: \"Expected Content-Type: application/sdp\" };\n }\n\n const rawSdp = await readLimitedRawBody(\n event,\n REALTIME_VOICE_MAX_SDP_BYTES,\n );\n if (rawSdp === \"oversize\") {\n setResponseStatus(event, 413);\n return {\n error: `SDP offer is too large (max ${REALTIME_VOICE_MAX_SDP_BYTES} bytes)`,\n };\n }\n const sdp = rawSdp ?? \"\";\n if (!sdp.trim()) {\n setResponseStatus(event, 400);\n return { error: \"SDP offer is required\" };\n }\n\n return runWithRequestContext(\n {\n userEmail: auth.userEmail,\n orgId: auth.orgId,\n timezone: auth.timezone,\n run: auth.browserTabId\n ? { browserTabId: auth.browserTabId }\n : undefined,\n },\n async () => {\n const builderCredentials = await resolveBuilderCredentials();\n const builderConfigured = Boolean(\n builderCredentials.privateKey?.trim() &&\n builderCredentials.publicKey?.trim(),\n );\n const apiKey = builderConfigured\n ? null\n : (await resolveSecret(\"OPENAI_API_KEY\"))?.trim();\n if (!builderConfigured && !apiKey) {\n setResponseStatus(event, 409);\n return {\n error:\n \"Connect Builder or configure an OpenAI API key to use realtime voice.\",\n code: \"realtime_voice_setup_required\",\n };\n }\n\n const instructions = await buildInstructions(auth, options);\n const transcriptionLanguage = resolveRealtimeVoiceLanguagePreference(\n readSafeHeader(event, \"x-agent-native-realtime-language\"),\n readSafeHeader(event, \"accept-language\"),\n );\n const reasoningEffort = resolveRealtimeVoiceReasoningEffort(\n readSafeHeader(event, \"x-agent-native-realtime-intelligence\"),\n );\n const voice = resolveRealtimeVoicePreference(\n readSafeHeader(event, \"x-agent-native-realtime-voice\"),\n configuredIdentifier(options.voice, DEFAULT_VOICE),\n );\n const sessionBase = {\n type: \"realtime\",\n model: configuredIdentifier(options.model, DEFAULT_MODEL),\n instructions,\n parallel_tool_calls: false,\n reasoning: { effort: reasoningEffort },\n output_modalities: [\"audio\"],\n audio: {\n input: {\n transcription: {\n model: \"gpt-4o-mini-transcribe\",\n language: transcriptionLanguage,\n },\n turn_detection: {\n type: \"semantic_vad\",\n create_response: true,\n interrupt_response: true,\n eagerness: \"auto\",\n },\n },\n output: {\n voice,\n },\n },\n };\n const packedTools = packRealtimeTools(sessionBase, tools);\n const session = {\n ...sessionBase,\n tools: packedTools,\n tool_choice: \"auto\",\n };\n\n let upstream: Response;\n try {\n if (builderConfigured) {\n const gatewayUrl = new URL(\n \"realtime/calls\",\n getBuilderGatewayBaseUrl().endsWith(\"/\")\n ? getBuilderGatewayBaseUrl()\n : `${getBuilderGatewayBaseUrl()}/`,\n );\n gatewayUrl.searchParams.set(\n \"apiKey\",\n builderCredentials.publicKey!.trim(),\n );\n upstream = await fetch(gatewayUrl.toString(), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${builderCredentials.privateKey!.trim()}`,\n \"x-builder-api-key\": builderCredentials.publicKey!.trim(),\n ...getBuilderGatewayRequestHeaders(),\n ...(builderCredentials.userId\n ? { \"x-builder-user-id\": builderCredentials.userId }\n : {}),\n },\n body: JSON.stringify({ sdp, session }),\n });\n } else {\n const form = new FormData();\n form.set(\"sdp\", sdp);\n form.set(\"session\", JSON.stringify(session));\n upstream = await fetch(OPENAI_REALTIME_CALLS_URL, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"OpenAI-Safety-Identifier\": await realtimeVoiceSafetyIdentifier(\n auth.userEmail,\n ),\n },\n body: form,\n });\n }\n } catch {\n setResponseStatus(event, 502);\n return {\n error: builderConfigured\n ? \"Could not reach the Builder realtime voice gateway\"\n : \"Could not reach the OpenAI Realtime API\",\n };\n }\n\n if (!upstream.ok) {\n const detail = await safeOpenAiErrorDetail(\n upstream,\n builderConfigured ? builderCredentials.privateKey! : apiKey!,\n );\n setResponseStatus(event, builderConfigured ? upstream.status : 502);\n return {\n error: `${builderConfigured ? \"Builder\" : \"OpenAI\"} rejected the realtime session (${upstream.status})${detail ? `: ${detail}` : \"\"}`,\n };\n }\n\n const answerSdp = await upstream.text().catch(() => \"\");\n if (!answerSdp.trim()) {\n setResponseStatus(event, 502);\n return {\n error: `${builderConfigured ? \"Builder\" : \"OpenAI\"} returned an empty realtime session answer`,\n };\n }\n\n setResponseStatus(event, upstream.status);\n setResponseHeader(event, \"Content-Type\", \"application/sdp\");\n setResponseHeader(\n event,\n REALTIME_VOICE_CAPABILITY_HEADER,\n registerRealtimeToolCapability(\n capabilities,\n auth,\n packedTools.map((tool) => tool.name),\n ),\n );\n return answerSdp;\n },\n );\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parseToolRequest(value: unknown): {\n name: string;\n args: Record<string, unknown>;\n callId: string;\n sessionId?: string;\n browserTabId?: string;\n} | null {\n if (!isRecord(value)) return null;\n const { name, args, callId, sessionId, browserTabId } = value;\n if (typeof name !== \"string\" || !REALTIME_TOOL_NAME.test(name)) return null;\n if (typeof callId !== \"string\" || !CALL_ID.test(callId)) return null;\n if (!isRecord(args)) return null;\n if (\n sessionId !== undefined &&\n (typeof sessionId !== \"string\" || !SESSION_ID.test(sessionId))\n ) {\n return null;\n }\n if (\n browserTabId !== undefined &&\n (typeof browserTabId !== \"string\" || !BROWSER_TAB_ID.test(browserTabId))\n ) {\n return null;\n }\n return {\n name,\n args,\n callId,\n ...(sessionId ? { sessionId } : {}),\n ...(browserTabId ? { browserTabId } : {}),\n };\n}\n\nfunction normalizeExecutionResult(\n result: RealtimeVoiceToolExecutionResult,\n): RealtimeVoiceToolExecutionResult {\n const status = result?.status;\n if (\n status !== \"completed\" &&\n status !== \"failed\" &&\n status !== \"approval_required\"\n ) {\n throw new Error(\"Invalid realtime tool execution status\");\n }\n const output = sanitizeOutput(\n result.output,\n REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS,\n );\n return {\n status,\n output,\n ...(status === \"approval_required\" &&\n typeof result.approvalKey === \"string\" &&\n result.approvalKey.length > 0 &&\n result.approvalKey.length <= MAX_APPROVAL_KEY_CHARS\n ? { approvalKey: result.approvalKey }\n : {}),\n };\n}\n\nfunction createToolHandler(\n toolsByName: ReadonlyMap<string, RealtimeFunctionTool>,\n capabilities: RealtimeToolCapabilityStore,\n options: MountRealtimeVoiceRoutesOptions,\n) {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"POST\") return invalidMethod(event);\n if (!isSameOriginRequest(event)) {\n setResponseStatus(event, 403);\n return { error: \"Cross-origin request rejected\" };\n }\n setResponseHeader(event, \"Cache-Control\", \"no-store\");\n\n const auth = await authenticateVoiceRequest(event, options);\n if (!auth) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n const capability = resolveRealtimeToolCapability(\n capabilities,\n readSafeHeader(event, REALTIME_VOICE_CAPABILITY_HEADER),\n auth,\n );\n if (!capability) {\n setResponseStatus(event, 403);\n return { error: \"Invalid or expired realtime voice capability\" };\n }\n\n const contentType = readSafeHeader(event, \"content-type\")?.toLowerCase();\n if (!contentType?.includes(\"application/json\")) {\n setResponseStatus(event, 415);\n return { error: \"Expected Content-Type: application/json\" };\n }\n\n const raw = await readLimitedRawBody(\n event,\n REALTIME_VOICE_MAX_TOOL_BODY_BYTES,\n );\n if (raw === \"oversize\") {\n setResponseStatus(event, 413);\n return {\n error: `Tool request is too large (max ${REALTIME_VOICE_MAX_TOOL_BODY_BYTES} bytes)`,\n };\n }\n\n let parsedBody: unknown;\n try {\n parsedBody = JSON.parse(raw ?? \"\");\n } catch {\n setResponseStatus(event, 400);\n return { error: \"Invalid realtime tool request\" };\n }\n const request = parseToolRequest(parsedBody);\n if (!request) {\n setResponseStatus(event, 400);\n return { error: \"Invalid realtime tool request\" };\n }\n if (\n request.browserTabId !== undefined &&\n request.browserTabId !== capability.browserTabId\n ) {\n setResponseStatus(event, 403);\n return { error: \"Realtime voice browser tab mismatch\" };\n }\n if (\n !capability.initialNames.has(request.name) &&\n !capability.names.has(request.name)\n ) {\n setResponseStatus(event, 404);\n return { error: \"Unknown realtime voice tool\" };\n }\n\n const browserTabId = capability.browserTabId;\n const threadId = request.sessionId\n ? `realtime:${request.sessionId}`\n : `realtime:${request.callId}`;\n return runWithRequestContext(\n {\n userEmail: auth.userEmail,\n orgId: auth.orgId,\n timezone: auth.timezone,\n run: {\n threadId,\n ...(browserTabId ? { browserTabId } : {}),\n },\n },\n async () => {\n try {\n const result = normalizeExecutionResult(\n await options.executeTool({\n event,\n userEmail: auth.userEmail,\n orgId: auth.orgId,\n ...request,\n ...(browserTabId ? { browserTabId } : {}),\n }),\n );\n const expandedTools = grantDiscoveredRealtimeTools({\n request,\n result,\n toolsByName,\n initialAllowedNames: capability.initialNames,\n capability,\n });\n return {\n callId: request.callId,\n ...result,\n ...(expandedTools.length > 0 ? { expandedTools } : {}),\n };\n } catch (error) {\n setResponseStatus(event, 500);\n return {\n callId: request.callId,\n status: \"failed\" as const,\n output: truncate(\n sanitizeToolErrorValue(error) || \"Tool execution failed\",\n REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS,\n ),\n };\n }\n },\n );\n });\n}\n\n/** Mount the authenticated OpenAI Realtime WebRTC and tool bridge routes. */\nexport function mountRealtimeVoiceRoutes(\n nitroApp: any,\n actions: Record<string, ActionEntry>,\n options: MountRealtimeVoiceRoutesOptions,\n): { sessionPath: string; toolPath: string } {\n if (typeof options?.executeTool !== \"function\") {\n throw new Error(\"mountRealtimeVoiceRoutes requires executeTool\");\n }\n\n const tools = buildRealtimeTools(actions);\n const toolsByName = new Map(tools.map((tool) => [tool.name, tool]));\n const capabilities: RealtimeToolCapabilityStore = new Map();\n const app = getH3App(nitroApp);\n app.use(\n REALTIME_VOICE_SESSION_PATH,\n createSessionHandler(tools, capabilities, options),\n );\n app.use(\n REALTIME_VOICE_TOOL_PATH,\n createToolHandler(toolsByName, capabilities, options),\n );\n return {\n sessionPath: REALTIME_VOICE_SESSION_PATH,\n toolPath: REALTIME_VOICE_TOOL_PATH,\n };\n}\n"]}
|
|
@@ -733,7 +733,7 @@
|
|
|
733
733
|
}
|
|
734
734
|
|
|
735
735
|
.agent-composer-root--hero {
|
|
736
|
-
min-height:
|
|
736
|
+
min-height: 5.625rem;
|
|
737
737
|
border-radius: 1rem;
|
|
738
738
|
border-color: hsl(var(--border) / 0.75);
|
|
739
739
|
background: hsl(var(--muted) / 0.62);
|
|
@@ -751,7 +751,7 @@
|
|
|
751
751
|
}
|
|
752
752
|
|
|
753
753
|
.agent-composer-root--hero [data-agent-composer-slot="editor-input"] {
|
|
754
|
-
min-height:
|
|
754
|
+
min-height: 3rem;
|
|
755
755
|
font-size: 1rem;
|
|
756
756
|
line-height: 1.75rem;
|
|
757
757
|
}
|
|
@@ -30,7 +30,7 @@ The agent modifies data in SQL, but the UI runs in the browser. SSE bridges same
|
|
|
30
30
|
useDbSync({ queryClient });
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
For each non-own event, `useDbSync` bumps a per-source counter (e.g. `dashboards`, `analyses`, `settings`, `action`) and invalidates a small fixed list of framework-internal prefixes (`["action"]`, `["app-state"]`, `["__set_url__"]`, etc.). It does **not** blanket-invalidate templates' own data queries
|
|
33
|
+
For each non-own event, `useDbSync` bumps a per-source counter (e.g. `dashboards`, `analyses`, `settings`, `action`) and invalidates a small fixed list of framework-internal prefixes (`["action"]`, `["app-state"]`, `["__set_url__"]`, etc.). It does **not** blanket-invalidate templates' own data queries — that caused request storms in production. A successful mutating action refreshes active `useActionQuery` observers under the `["action"]` prefix. Browser actions carry a tab id so their originating tab ignores the sync echo while other tabs refresh. Legacy apps can opt into broader compatibility with `actionInvalidatePredicate`, but first-party apps should use action-backed or source-versioned query keys. Idle fallback polling runs once per minute; active agent work temporarily uses the faster cadence.
|
|
34
34
|
|
|
35
35
|
3. **Templates fold per-source counters into their query keys.** This is the pattern that makes "agent writes show up without a manual refresh" reliable:
|
|
36
36
|
|
|
@@ -117,7 +117,7 @@ useQuery({
|
|
|
117
117
|
| UI not updating after agent writes | Is `useDbSync` called with the correct `queryClient`? Does the affected query have an active observer? |
|
|
118
118
|
| Poll endpoint not responding | Is `/_agent-native/poll` accessible? Is the server running? |
|
|
119
119
|
| SSE not connecting | Is `/_agent-native/events` accessible and authenticated? Polling should still keep the UI fresh as fallback. |
|
|
120
|
-
| High CPU / event storms |
|
|
120
|
+
| High CPU / event storms | Use targeted source keys, settle bursty list counters, and avoid broad action invalidation. |
|
|
121
121
|
|
|
122
122
|
## Jitter Prevention
|
|
123
123
|
|