@agent-native/core 0.98.0 → 0.98.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +24 -0
  3. package/corpus/core/package.json +2 -2
  4. package/corpus/core/src/action-change-marker.ts +8 -1
  5. package/corpus/core/src/changelog/parse.ts +8 -3
  6. package/corpus/core/src/client/AgentPanel.tsx +2 -3
  7. package/corpus/core/src/client/AssistantChat.tsx +83 -39
  8. package/corpus/core/src/client/analytics.ts +29 -0
  9. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
  10. package/corpus/core/src/client/chat-model-groups.ts +28 -0
  11. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
  12. package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
  13. package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
  14. package/corpus/core/src/client/composer/index.ts +5 -0
  15. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
  16. package/corpus/core/src/client/index.ts +5 -0
  17. package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
  18. package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
  19. package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
  20. package/corpus/core/src/client/use-action.ts +12 -0
  21. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
  22. package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
  23. package/corpus/core/src/client/use-db-sync.ts +84 -34
  24. package/corpus/core/src/client/use-pausing-interval.ts +14 -4
  25. package/corpus/core/src/integrations/scope-store.ts +1 -1
  26. package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
  27. package/corpus/core/src/localization/default-messages.ts +44 -1
  28. package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
  29. package/corpus/core/src/secrets/crypto.ts +28 -8
  30. package/corpus/core/src/server/action-change.ts +5 -1
  31. package/corpus/core/src/server/action-routes.ts +8 -0
  32. package/corpus/core/src/server/poll.ts +1 -0
  33. package/corpus/core/src/server/realtime-voice.ts +299 -13
  34. package/corpus/core/src/styles/agent-native.css +2 -2
  35. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  36. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  37. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  38. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  39. package/corpus/core/src/vite/client.ts +9 -1
  40. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
  41. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
  42. package/corpus/templates/analytics/AGENTS.md +9 -13
  43. package/corpus/templates/analytics/actions/navigate.ts +59 -5
  44. package/corpus/templates/analytics/actions/view-screen.ts +3 -6
  45. package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
  46. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +2 -1
  47. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  48. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
  49. package/corpus/templates/analytics/app/global.css +19 -20
  50. package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
  51. package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
  52. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
  53. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
  54. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
  55. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
  56. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
  57. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
  58. package/corpus/templates/analytics/app/root.tsx +28 -8
  59. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
  60. package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
  61. package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
  62. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
  63. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
  64. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
  65. package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
  66. package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
  67. package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
  71. package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
  72. package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
  73. package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
  74. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
  75. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
  76. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
  77. package/corpus/templates/analytics/package.json +1 -1
  78. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
  79. package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
  80. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
  81. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
  82. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
  83. package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
  84. package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
  85. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
  86. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
  87. package/corpus/templates/assets/app/global.css +4 -4
  88. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
  89. package/corpus/templates/assets/app/routes/library.tsx +0 -1
  90. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
  91. package/corpus/templates/brain/app/global.css +5 -5
  92. package/corpus/templates/brain/app/root.tsx +0 -3
  93. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
  94. package/corpus/templates/calendar/app/root.tsx +1 -0
  95. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
  96. package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
  97. package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
  98. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
  99. package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
  100. package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
  101. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
  102. package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
  103. package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  104. package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
  105. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
  106. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
  107. package/corpus/templates/macros/app/root.tsx +0 -3
  108. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
  109. package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
  110. package/corpus/templates/mail/app/root.tsx +1 -12
  111. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
  112. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
  113. package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
  114. package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -27
  115. package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
  116. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
  117. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
  118. package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
  119. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
  120. package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
  121. package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  122. package/dist/action-change-marker.d.ts +1 -0
  123. package/dist/action-change-marker.d.ts.map +1 -1
  124. package/dist/action-change-marker.js +7 -1
  125. package/dist/action-change-marker.js.map +1 -1
  126. package/dist/changelog/parse.d.ts +4 -2
  127. package/dist/changelog/parse.d.ts.map +1 -1
  128. package/dist/changelog/parse.js +5 -3
  129. package/dist/changelog/parse.js.map +1 -1
  130. package/dist/client/AgentPanel.d.ts.map +1 -1
  131. package/dist/client/AgentPanel.js +2 -3
  132. package/dist/client/AgentPanel.js.map +1 -1
  133. package/dist/client/AssistantChat.d.ts +7 -0
  134. package/dist/client/AssistantChat.d.ts.map +1 -1
  135. package/dist/client/AssistantChat.js +62 -40
  136. package/dist/client/AssistantChat.js.map +1 -1
  137. package/dist/client/analytics.d.ts.map +1 -1
  138. package/dist/client/analytics.js +26 -0
  139. package/dist/client/analytics.js.map +1 -1
  140. package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
  141. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  142. package/dist/client/blocks/library/wireframe-kit.js +9 -2
  143. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  144. package/dist/client/chat-model-groups.d.ts.map +1 -1
  145. package/dist/client/chat-model-groups.js +24 -0
  146. package/dist/client/chat-model-groups.js.map +1 -1
  147. package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
  148. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  149. package/dist/client/composer/RealtimeVoiceMode.js +172 -42
  150. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  151. package/dist/client/composer/TiptapComposer.d.ts +4 -0
  152. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  153. package/dist/client/composer/TiptapComposer.js +47 -29
  154. package/dist/client/composer/TiptapComposer.js.map +1 -1
  155. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  156. package/dist/client/composer/VoiceButton.js +2 -2
  157. package/dist/client/composer/VoiceButton.js.map +1 -1
  158. package/dist/client/composer/index.d.ts +2 -2
  159. package/dist/client/composer/index.d.ts.map +1 -1
  160. package/dist/client/composer/index.js +1 -1
  161. package/dist/client/composer/index.js.map +1 -1
  162. package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
  163. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  164. package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
  165. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  166. package/dist/client/index.d.ts +1 -1
  167. package/dist/client/index.d.ts.map +1 -1
  168. package/dist/client/index.js +1 -1
  169. package/dist/client/index.js.map +1 -1
  170. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  171. package/dist/client/progress/RunsTray.js +10 -1
  172. package/dist/client/progress/RunsTray.js.map +1 -1
  173. package/dist/client/settings/SecretsSection.d.ts +2 -3
  174. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  175. package/dist/client/settings/SecretsSection.js +46 -22
  176. package/dist/client/settings/SecretsSection.js.map +1 -1
  177. package/dist/client/settings/SettingsPanel.d.ts +9 -0
  178. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  179. package/dist/client/settings/SettingsPanel.js +46 -12
  180. package/dist/client/settings/SettingsPanel.js.map +1 -1
  181. package/dist/client/use-action.d.ts +2 -0
  182. package/dist/client/use-action.d.ts.map +1 -1
  183. package/dist/client/use-action.js +10 -0
  184. package/dist/client/use-action.js.map +1 -1
  185. package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
  186. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  187. package/dist/client/use-agent-chat-home-handoff.js +7 -1
  188. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  189. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  190. package/dist/client/use-agent-engine-configured.js +17 -3
  191. package/dist/client/use-agent-engine-configured.js.map +1 -1
  192. package/dist/client/use-db-sync.d.ts +8 -2
  193. package/dist/client/use-db-sync.d.ts.map +1 -1
  194. package/dist/client/use-db-sync.js +57 -32
  195. package/dist/client/use-db-sync.js.map +1 -1
  196. package/dist/client/use-pausing-interval.d.ts.map +1 -1
  197. package/dist/client/use-pausing-interval.js +16 -4
  198. package/dist/client/use-pausing-interval.js.map +1 -1
  199. package/dist/integrations/scope-store.js +1 -1
  200. package/dist/integrations/scope-store.js.map +1 -1
  201. package/dist/integrations/usage-budget-store.js +1 -1
  202. package/dist/integrations/usage-budget-store.js.map +1 -1
  203. package/dist/localization/default-messages.d.ts +41 -1
  204. package/dist/localization/default-messages.d.ts.map +1 -1
  205. package/dist/localization/default-messages.js +41 -1
  206. package/dist/localization/default-messages.js.map +1 -1
  207. package/dist/mcp-client/workspace-servers.js +4 -4
  208. package/dist/mcp-client/workspace-servers.js.map +1 -1
  209. package/dist/observability/routes.d.ts +5 -5
  210. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  211. package/dist/secrets/crypto.d.ts +7 -6
  212. package/dist/secrets/crypto.d.ts.map +1 -1
  213. package/dist/secrets/crypto.js +30 -11
  214. package/dist/secrets/crypto.js.map +1 -1
  215. package/dist/secrets/routes.d.ts +9 -9
  216. package/dist/server/action-change.d.ts +1 -0
  217. package/dist/server/action-change.d.ts.map +1 -1
  218. package/dist/server/action-change.js +4 -1
  219. package/dist/server/action-change.js.map +1 -1
  220. package/dist/server/action-routes.d.ts.map +1 -1
  221. package/dist/server/action-routes.js +5 -0
  222. package/dist/server/action-routes.js.map +1 -1
  223. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  224. package/dist/server/poll.d.ts.map +1 -1
  225. package/dist/server/poll.js +1 -0
  226. package/dist/server/poll.js.map +1 -1
  227. package/dist/server/realtime-voice.d.ts +7 -0
  228. package/dist/server/realtime-voice.d.ts.map +1 -1
  229. package/dist/server/realtime-voice.js +222 -12
  230. package/dist/server/realtime-voice.js.map +1 -1
  231. package/dist/styles/agent-native.css +2 -2
  232. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  233. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  234. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  235. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  236. package/dist/vite/client.d.ts.map +1 -1
  237. package/dist/vite/client.js +6 -1
  238. package/dist/vite/client.js.map +1 -1
  239. package/package.json +2 -2
  240. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  241. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  242. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  243. 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
- return tools;
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: { model: "gpt-4o-mini-transcribe" },
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: configuredIdentifier(options.voice, DEFAULT_VOICE),
444
+ voice,
261
445
  },
262
446
  },
263
447
  };
448
+ const packedTools = packRealtimeTools(sessionBase, tools);
264
449
  const session = {
265
450
  ...sessionBase,
266
- tools: packRealtimeTools(sessionBase, 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(allowedToolNames, options) {
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 (!allowedToolNames.has(request.name)) {
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 = request.browserTabId ?? auth.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
- return { callId: request.callId, ...result };
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 allowedToolNames = new Set(tools.slice(0, REALTIME_VOICE_MAX_TOOLS).map((tool) => tool.name));
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(allowedToolNames, options));
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: 7.5rem;
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: 4rem;
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 for ordinary domain events — that caused a request storm in production. The exception is `source: "action"`: a successful mutating action is the framework-wide "agent changed app data" signal, so `useDbSync` also refreshes active React Query observers as a compatibility safety net for custom apps that have not yet moved every read to `useActionQuery` or source-versioned query keys.
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 | The agent is writing rapidly. Add `staleTime` to queries to debounce refetches. |
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