@agent-native/core 0.9.1 → 0.11.0
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/README.md +4 -4
- package/dist/a2a/caller-auth.d.ts +12 -0
- package/dist/a2a/caller-auth.d.ts.map +1 -0
- package/dist/a2a/caller-auth.js +54 -0
- package/dist/a2a/caller-auth.js.map +1 -0
- package/dist/action.d.ts +17 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +22 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +5 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +6 -3
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +8 -17
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +60 -30
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +12 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +12 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +96 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +16 -10
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +8 -22
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +130 -34
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +21 -7
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-sidebar-state.d.ts +3 -0
- package/dist/client/agent-sidebar-state.d.ts.map +1 -0
- package/dist/client/agent-sidebar-state.js +24 -0
- package/dist/client/agent-sidebar-state.js.map +1 -0
- package/dist/client/analytics.d.ts +25 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +40 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/client/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/client/components/ui/dropdown-menu.js +34 -0
- package/dist/client/components/ui/dropdown-menu.js.map +1 -0
- package/dist/client/components/ui/tooltip.d.ts +2 -1
- package/dist/client/components/ui/tooltip.d.ts.map +1 -1
- package/dist/client/components/ui/tooltip.js +9 -2
- package/dist/client/components/ui/tooltip.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +41 -8
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +30 -0
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +27 -2
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.d.ts.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.js +4 -4
- package/dist/client/dev-overlay/DevOverlay.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +6 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +16 -4
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionEditor.js +6 -6
- package/dist/client/extensions/ExtensionEditor.js.map +1 -1
- package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionSlot.js +15 -2
- package/dist/client/extensions/ExtensionSlot.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +41 -19
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +2 -2
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +52 -63
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/extensions/iframe-bridge.d.ts.map +1 -1
- package/dist/client/extensions/iframe-bridge.js +5 -8
- package/dist/client/extensions/iframe-bridge.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/notifications/NotificationsBell.d.ts.map +1 -1
- package/dist/client/notifications/NotificationsBell.js +42 -6
- package/dist/client/notifications/NotificationsBell.js.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.js +5 -5
- package/dist/client/org/InvitationBanner.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +7 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +8 -3
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +156 -22
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/org/hooks.d.ts +29 -1
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +39 -2
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/index.d.ts +2 -1
- package/dist/client/org/index.d.ts.map +1 -1
- package/dist/client/org/index.js +1 -1
- package/dist/client/org/index.js.map +1 -1
- package/dist/client/resources/ResourceTree.d.ts.map +1 -1
- package/dist/client/resources/ResourceTree.js +11 -3
- package/dist/client/resources/ResourceTree.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +62 -12
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +2 -0
- package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
- package/dist/client/resources/use-mcp-servers.js +59 -3
- package/dist/client/resources/use-mcp-servers.js.map +1 -1
- package/dist/client/settings/SecretsSection.d.ts.map +1 -1
- package/dist/client/settings/SecretsSection.js +9 -0
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +50 -12
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +13 -30
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +27 -1
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +4 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +5 -1
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +14 -7
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +100 -19
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +25 -49
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/route-discovery.d.ts.map +1 -1
- package/dist/deploy/route-discovery.js +1 -0
- package/dist/deploy/route-discovery.js.map +1 -1
- package/dist/deploy/workspace-core.d.ts +1 -1
- package/dist/deploy/workspace-core.d.ts.map +1 -1
- package/dist/deploy/workspace-core.js +1 -0
- package/dist/deploy/workspace-core.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +17 -3
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/routes.js +1 -1
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/schema.d.ts +14 -14
- package/dist/extensions/schema.d.ts.map +1 -1
- package/dist/extensions/schema.js +4 -4
- package/dist/extensions/schema.js.map +1 -1
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +23 -0
- package/dist/extensions/store.js.map +1 -1
- package/dist/extensions/theme.d.ts +8 -1
- package/dist/extensions/theme.d.ts.map +1 -1
- package/dist/extensions/theme.js +43 -34
- package/dist/extensions/theme.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +28 -1
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/org/accept-pending.d.ts.map +1 -1
- package/dist/org/accept-pending.js +5 -3
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/free-email-providers.d.ts +18 -0
- package/dist/org/free-email-providers.d.ts.map +1 -0
- package/dist/org/free-email-providers.js +124 -0
- package/dist/org/free-email-providers.js.map +1 -0
- package/dist/org/handlers.d.ts +29 -5
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +178 -37
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/index.d.ts +2 -1
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +2 -1
- package/dist/org/index.js.map +1 -1
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +4 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/org/plugin.d.ts.map +1 -1
- package/dist/org/plugin.js +13 -4
- package/dist/org/plugin.js.map +1 -1
- package/dist/org/schema.d.ts +19 -0
- package/dist/org/schema.d.ts.map +1 -1
- package/dist/org/schema.js +1 -0
- package/dist/org/schema.js.map +1 -1
- package/dist/org/types.d.ts +1 -0
- package/dist/org/types.d.ts.map +1 -1
- package/dist/org/types.js.map +1 -1
- package/dist/resources/metadata.d.ts +1 -0
- package/dist/resources/metadata.d.ts.map +1 -1
- package/dist/resources/metadata.js +13 -3
- package/dist/resources/metadata.js.map +1 -1
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +44 -6
- package/dist/resources/store.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +115 -113
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +33 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +169 -68
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/credential-provider.d.ts +2 -2
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +31 -12
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +31 -0
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +10 -3
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +19 -6
- package/dist/server/google-realtime-session.js.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +45 -6
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/request-context.d.ts +17 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +40 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/sentry-plugin.d.ts +11 -0
- package/dist/server/sentry-plugin.d.ts.map +1 -0
- package/dist/server/sentry-plugin.js +116 -0
- package/dist/server/sentry-plugin.js.map +1 -0
- package/dist/server/sentry.d.ts +92 -0
- package/dist/server/sentry.d.ts.map +1 -0
- package/dist/server/sentry.js +287 -0
- package/dist/server/sentry.js.map +1 -0
- package/dist/server/transcribe-voice.d.ts +2 -4
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +4 -16
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +19 -35
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/styles/agent-native.css +15 -0
- package/docs/content/cloneable-saas.md +7 -9
- package/docs/content/deployment.md +6 -2
- package/docs/content/dispatch.md +1 -1
- package/docs/content/extensions.md +177 -142
- package/docs/content/faq.md +2 -2
- package/docs/content/getting-started.md +13 -11
- package/docs/content/multi-app-workspace.md +2 -2
- package/docs/content/observability.md +47 -0
- package/docs/content/pure-agent-apps.md +1 -1
- package/docs/content/template-clips.md +3 -3
- package/docs/content/template-design.md +3 -3
- package/docs/content/template-dispatch.md +1 -1
- package/docs/content/template-forms.md +1 -1
- package/docs/content/template-mail.md +1 -1
- package/docs/content/what-is-agent-native.md +4 -4
- package/docs/content/workspace.md +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry-plugin.js","sourceRoot":"","sources":["../../src/server/sentry-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Nitro plugin that initializes server-side Sentry and attaches per-request\n * user context.\n *\n * Wires three pieces:\n * 1. On startup, `initServerSentry()` reads `SENTRY_SERVER_DSN` and arms\n * the SDK (no-op when the env var is unset).\n * 2. On every request, hook into Nitro's `request` event: resolve the\n * session via `getSession(event)` and tag the per-request isolation\n * scope with the user's id/email/orgId. Wrapped in try/catch so a\n * session-resolution failure can never 500 the request.\n * 3. On every Nitro `error` event, capture the exception with the route,\n * method, and user-agent attached as searchable tags.\n *\n * Mounted as a default plugin from `framework-request-handler.ts` —\n * templates that don't define `server/plugins/sentry.ts` get this for\n * free. Templates that need to customize (e.g. add custom tags / skip\n * Sentry) can override by exporting their own `sentry.ts` plugin.\n */\nimport {\n awaitBootstrap,\n markDefaultPluginProvided,\n} from \"./framework-request-handler.js\";\nimport { getSession } from \"./auth.js\";\nimport {\n captureRouteError,\n initServerSentry,\n isServerSentryEnabled,\n setSentryRequestContext,\n setSentryUserForRequest,\n} from \"./sentry.js\";\nimport { addRequestContextObserver } from \"./request-context.js\";\nimport { getHeader, getMethod, type H3Event } from \"h3\";\n\ntype NitroPluginDef = (nitroApp: any) => void | Promise<void>;\n\nfunction readRoute(event: H3Event): string | undefined {\n try {\n return event.url?.pathname;\n } catch {\n return undefined;\n }\n}\n\nfunction readUserAgent(event: H3Event): string | undefined {\n try {\n return getHeader(event, \"user-agent\");\n } catch {\n return undefined;\n }\n}\n\n/**\n * Skip session resolution for paths that obviously don't need one. Avoids\n * a DB round-trip on every static-asset / favicon / public-share request\n * while keeping API + framework routes covered.\n */\nfunction shouldResolveSession(path: string | undefined): boolean {\n if (!path) return false;\n // Vite / React Router static assets and similar.\n if (\n path.startsWith(\"/assets/\") ||\n path.startsWith(\"/_build/\") ||\n path === \"/favicon.ico\" ||\n path.startsWith(\"/static/\")\n ) {\n return false;\n }\n return true;\n}\n\nexport function createSentryPlugin(): NitroPluginDef {\n return async (nitroApp: any) => {\n markDefaultPluginProvided(nitroApp, \"sentry\");\n await awaitBootstrap(nitroApp);\n\n initServerSentry();\n if (!isServerSentryEnabled()) {\n // No DSN — skip wiring per-request hooks. We'd just be paying the\n // call-site overhead for every request to no effect.\n return;\n }\n\n // Per-request: resolve session and attach to Sentry isolation scope so\n // any exception captured later in the request carries the user. Wrapped\n // in try/catch so a session-DB hiccup or auth-broken state never turns\n // into a 500 — the worst case is we lose user context on the event.\n nitroApp.hooks?.hook?.(\"request\", async (event: H3Event) => {\n if (!shouldResolveSession(readRoute(event))) return;\n try {\n const session = await getSession(event);\n setSentryUserForRequest(session);\n } catch {\n // best-effort — don't break the request\n }\n });\n\n // Wrap-time: every `runWithRequestContext({ userEmail, orgId, ... })`\n // call also pins user/org onto Sentry's per-async-context isolation\n // scope. Covers paths the cookie-based `request` hook can't see —\n // integration webhook processors, A2A calls, agent-chat tool\n // re-entries, and any internal call chain that opens a request scope\n // without an HTTP cookie.\n addRequestContextObserver((ctx) => {\n setSentryRequestContext({ userEmail: ctx.userEmail, orgId: ctx.orgId });\n });\n\n // Per-error: capture with route/method/UA tags. Nitro's `error` hook\n // signature is (error, { event, tags }) — we forward what we can.\n nitroApp.hooks?.hook?.(\n \"error\",\n (error: unknown, ctx?: { event?: H3Event }) => {\n try {\n const event = ctx?.event;\n captureRouteError(error, {\n route: event ? readRoute(event) : undefined,\n method: event ? getMethod(event) : undefined,\n userAgent: event ? readUserAgent(event) : undefined,\n });\n } catch {\n // Sentry capture must never escape into Nitro's error path.\n }\n },\n );\n };\n}\n\n/**\n * Default Sentry plugin — auto-mounts when a template doesn't define its\n * own `server/plugins/sentry.ts`. Reads `SENTRY_SERVER_DSN` from env and\n * silently no-ops when it's unset, so this is safe to default-mount in\n * every template (including local dev with no DSN configured).\n */\nexport const defaultSentryPlugin: NitroPluginDef = createSentryPlugin();\n"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { AuthSession } from "./auth.js";
|
|
2
|
+
/**
|
|
3
|
+
* Initialize server-side Sentry. Idempotent — safe to call from multiple
|
|
4
|
+
* plugin entrypoints. Returns `true` if initialization actually happened
|
|
5
|
+
* (DSN was set), `false` if Sentry is disabled (no DSN).
|
|
6
|
+
*
|
|
7
|
+
* No DSN is hardcoded: unlike the CLI (a published binary that always
|
|
8
|
+
* wants to phone home crashes), the server runs in customer environments.
|
|
9
|
+
* Operators set `SENTRY_SERVER_DSN` when they want their own Sentry
|
|
10
|
+
* project to receive these events; without it the module no-ops.
|
|
11
|
+
*/
|
|
12
|
+
export declare function initServerSentry(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* `true` once `initServerSentry()` has succeeded with a DSN. Plugins that
|
|
15
|
+
* want to skip work when Sentry is disabled can check this before calling
|
|
16
|
+
* the helpers below.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isServerSentryEnabled(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Attach the current request's user to Sentry's isolation scope so any
|
|
21
|
+
* `captureException` triggered later in the request carries the right
|
|
22
|
+
* `user.id` / `user.email` / `user.username` and `orgId` tag.
|
|
23
|
+
*
|
|
24
|
+
* Sentry node 10 uses Node's AsyncLocalStorage to give each async context
|
|
25
|
+
* its own isolation scope, so setting on `getIsolationScope()` here only
|
|
26
|
+
* affects events emitted while this request's async context is active.
|
|
27
|
+
*
|
|
28
|
+
* No-ops gracefully when Sentry isn't initialized or no session exists —
|
|
29
|
+
* never throws into the request path.
|
|
30
|
+
*/
|
|
31
|
+
export declare function setSentryUserForRequest(session: AuthSession | null): void;
|
|
32
|
+
/**
|
|
33
|
+
* Pin a user/org onto the current isolation scope from a lighter
|
|
34
|
+
* `RequestContext`-shaped payload. Used by the request-context observer so
|
|
35
|
+
* action handlers, agent-chat runs, and integration webhook processors —
|
|
36
|
+
* all of which already wrap their work in `runWithRequestContext({ userEmail,
|
|
37
|
+
* orgId, ... })` — automatically tag Sentry events with the right user even
|
|
38
|
+
* when the Nitro `request` hook didn't see a cookie (e.g. webhook delivery,
|
|
39
|
+
* A2A calls, internal background runs).
|
|
40
|
+
*
|
|
41
|
+
* Skips overwriting a richer user identity already set by
|
|
42
|
+
* `setSentryUserForRequest` — the cookie-resolved session has
|
|
43
|
+
* userId/username on top of email, which we shouldn't clobber.
|
|
44
|
+
*/
|
|
45
|
+
export declare function setSentryRequestContext(ctx: {
|
|
46
|
+
userEmail?: string;
|
|
47
|
+
orgId?: string;
|
|
48
|
+
}): void;
|
|
49
|
+
/**
|
|
50
|
+
* Capture an error from one of the auth attempt routes (login / signup)
|
|
51
|
+
* with the email pinned to the event so support can filter by user. Sets
|
|
52
|
+
* Sentry level to `warning` (not `error`) — bad-password attempts aren't
|
|
53
|
+
* actionable, but a sustained spike of warnings on a route IS the signal
|
|
54
|
+
* we care about.
|
|
55
|
+
*
|
|
56
|
+
* Caller should still return their normal HTTP response (401/409/etc.);
|
|
57
|
+
* this just records the error for observability.
|
|
58
|
+
*/
|
|
59
|
+
export declare function captureAuthError(error: unknown, context: {
|
|
60
|
+
route: "login" | "signup" | "logout";
|
|
61
|
+
email?: string;
|
|
62
|
+
}): string | undefined;
|
|
63
|
+
export interface RouteErrorContext {
|
|
64
|
+
/** The full request path (e.g. `/_agent-native/agent-chat`). */
|
|
65
|
+
route?: string;
|
|
66
|
+
/** HTTP method (e.g. `GET`, `POST`). */
|
|
67
|
+
method?: string;
|
|
68
|
+
/** Caller's `User-Agent` header. */
|
|
69
|
+
userAgent?: string;
|
|
70
|
+
/** Free-form extra tags to add to the event (low-cardinality). */
|
|
71
|
+
tags?: Record<string, string | undefined>;
|
|
72
|
+
/**
|
|
73
|
+
* High-cardinality / structured payload — not searchable but visible in
|
|
74
|
+
* the Sentry event detail (recording IDs, byte counts, compression
|
|
75
|
+
* metadata, response body tails, etc.).
|
|
76
|
+
*/
|
|
77
|
+
extra?: Record<string, unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* Grouped contexts shown as separate cards in the Sentry event UI.
|
|
80
|
+
*/
|
|
81
|
+
contexts?: Record<string, Record<string, unknown>>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Capture an exception that surfaced in a Nitro route handler with the
|
|
85
|
+
* request's route/method/userAgent attached as searchable Sentry tags.
|
|
86
|
+
*
|
|
87
|
+
* Non-throwing: if Sentry isn't initialized or the underlying capture
|
|
88
|
+
* fails, this is a no-op. Returns the Sentry event ID when capture
|
|
89
|
+
* succeeded, otherwise `undefined`.
|
|
90
|
+
*/
|
|
91
|
+
export declare function captureRouteError(error: unknown, context?: RouteErrorContext): string | undefined;
|
|
92
|
+
//# sourceMappingURL=sentry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/server/sentry.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAoC7C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAoF1C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAsBzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,IAAI,CAgBP;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,MAAM,GAAG,SAAS,CAcpB;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,iBAAsB,GAC9B,MAAM,GAAG,SAAS,CA2BpB"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side Sentry initialization for Nitro.
|
|
3
|
+
*
|
|
4
|
+
* Errors thrown inside Nitro routes (the framework's own /_agent-native/*
|
|
5
|
+
* handlers, the template's API routes, action handlers, agent-chat streams)
|
|
6
|
+
* never reach the CLI's Sentry init — that only covers the developer's
|
|
7
|
+
* machine. Without server-side Sentry the only signal a 500 ever produces
|
|
8
|
+
* is a server-side console.error that lives and dies with the request.
|
|
9
|
+
*
|
|
10
|
+
* This module is the third Sentry init point in the framework:
|
|
11
|
+
* - cli/index.ts → @sentry/node, hardcoded DSN, "agent-native-cli"
|
|
12
|
+
* - client/analytics.ts → @sentry/browser, VITE_SENTRY_CLIENT_DSN
|
|
13
|
+
* - server/sentry.ts → @sentry/node, SENTRY_SERVER_DSN
|
|
14
|
+
*
|
|
15
|
+
* Each maps to a different Sentry project so we can route errors to the
|
|
16
|
+
* right team without one project drowning out the others. Don't wire the
|
|
17
|
+
* three together — they share the SDK package but live in different
|
|
18
|
+
* processes / runtimes / call sites.
|
|
19
|
+
*/
|
|
20
|
+
import * as Sentry from "@sentry/node";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import fs from "node:fs";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
let _initStarted = false;
|
|
25
|
+
let _initSucceeded = false;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the agent-native version baked into core's package.json so Sentry
|
|
28
|
+
* "release" reflects the running framework version. Mirrors how the CLI
|
|
29
|
+
* computes `_version` — same dist layout, same fallback string. Guarded so
|
|
30
|
+
* a missing/unreadable package.json never crashes server boot.
|
|
31
|
+
*/
|
|
32
|
+
function resolveServerRelease() {
|
|
33
|
+
const explicit = process.env.AGENT_NATIVE_RELEASE;
|
|
34
|
+
if (explicit)
|
|
35
|
+
return explicit;
|
|
36
|
+
try {
|
|
37
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
// dist/server/sentry.js → ../../package.json
|
|
39
|
+
const pkgPath = path.resolve(here, "../../package.json");
|
|
40
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
41
|
+
if (pkg?.version)
|
|
42
|
+
return `agent-native-server@${pkg.version}`;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// ignore — fall through to "unknown"
|
|
46
|
+
}
|
|
47
|
+
return "agent-native-server@unknown";
|
|
48
|
+
}
|
|
49
|
+
function parseTracesSampleRate() {
|
|
50
|
+
const raw = process.env.SENTRY_SERVER_TRACES_SAMPLE_RATE;
|
|
51
|
+
if (!raw)
|
|
52
|
+
return 0;
|
|
53
|
+
const n = Number(raw);
|
|
54
|
+
if (!Number.isFinite(n) || n < 0 || n > 1)
|
|
55
|
+
return 0;
|
|
56
|
+
return n;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Initialize server-side Sentry. Idempotent — safe to call from multiple
|
|
60
|
+
* plugin entrypoints. Returns `true` if initialization actually happened
|
|
61
|
+
* (DSN was set), `false` if Sentry is disabled (no DSN).
|
|
62
|
+
*
|
|
63
|
+
* No DSN is hardcoded: unlike the CLI (a published binary that always
|
|
64
|
+
* wants to phone home crashes), the server runs in customer environments.
|
|
65
|
+
* Operators set `SENTRY_SERVER_DSN` when they want their own Sentry
|
|
66
|
+
* project to receive these events; without it the module no-ops.
|
|
67
|
+
*/
|
|
68
|
+
export function initServerSentry() {
|
|
69
|
+
if (_initStarted)
|
|
70
|
+
return _initSucceeded;
|
|
71
|
+
_initStarted = true;
|
|
72
|
+
const dsn = process.env.SENTRY_SERVER_DSN;
|
|
73
|
+
if (!dsn) {
|
|
74
|
+
if (process.env.DEBUG) {
|
|
75
|
+
console.log("[agent-native] SENTRY_SERVER_DSN not set — server Sentry disabled.");
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
Sentry.init({
|
|
80
|
+
dsn,
|
|
81
|
+
environment: process.env.NODE_ENV || "production",
|
|
82
|
+
release: resolveServerRelease(),
|
|
83
|
+
tracesSampleRate: parseTracesSampleRate(),
|
|
84
|
+
// sendDefaultPii MUST stay false — the framework runs inside customer
|
|
85
|
+
// environments and we never want to silently ship request headers,
|
|
86
|
+
// cookies, or process.env contents to Sentry without explicit consent.
|
|
87
|
+
sendDefaultPii: false,
|
|
88
|
+
beforeSend(event) {
|
|
89
|
+
// Drop expected user-input rejections so they don't pollute Sentry
|
|
90
|
+
// with non-bug noise. Mirrors the CLI's drop list — the framework
|
|
91
|
+
// and CLI both throw `ValidationError` for the same class of input
|
|
92
|
+
// failures, and exception type comes through as the class name.
|
|
93
|
+
const exceptionType = event.exception?.values?.[0]?.type;
|
|
94
|
+
if (exceptionType === "ValidationError" ||
|
|
95
|
+
event.tags?.handled === "validation") {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
// Defense in depth: scrub PII even if some integration auto-attached
|
|
99
|
+
// request metadata despite sendDefaultPii: false.
|
|
100
|
+
if (event.request) {
|
|
101
|
+
if (event.request.headers) {
|
|
102
|
+
const headers = event.request.headers;
|
|
103
|
+
for (const k of Object.keys(headers)) {
|
|
104
|
+
const lk = k.toLowerCase();
|
|
105
|
+
if (lk === "cookie" ||
|
|
106
|
+
lk === "authorization" ||
|
|
107
|
+
lk === "set-cookie" ||
|
|
108
|
+
lk === "proxy-authorization") {
|
|
109
|
+
delete headers[k];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Cookies live in their own field too.
|
|
114
|
+
delete event.request.cookies;
|
|
115
|
+
}
|
|
116
|
+
// Keep user info that was explicitly set via Sentry.setUser
|
|
117
|
+
// (id/email/username) so we can attribute crashes back to a real
|
|
118
|
+
// operator. Always strip ip_address — auto-collected, no consent.
|
|
119
|
+
if (event.user) {
|
|
120
|
+
const user = event.user;
|
|
121
|
+
delete user.ip_address;
|
|
122
|
+
const hasIdentity = typeof user.id === "string" ||
|
|
123
|
+
typeof user.email === "string" ||
|
|
124
|
+
typeof user.username === "string";
|
|
125
|
+
if (!hasIdentity) {
|
|
126
|
+
delete event.user;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Sentry's contexts can carry process.env snapshots — strip env-shaped
|
|
130
|
+
// contexts so we don't leak deployment secrets.
|
|
131
|
+
if (event.contexts && typeof event.contexts === "object") {
|
|
132
|
+
delete event.contexts.runtime_env;
|
|
133
|
+
}
|
|
134
|
+
return event;
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
_initSucceeded = true;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* `true` once `initServerSentry()` has succeeded with a DSN. Plugins that
|
|
142
|
+
* want to skip work when Sentry is disabled can check this before calling
|
|
143
|
+
* the helpers below.
|
|
144
|
+
*/
|
|
145
|
+
export function isServerSentryEnabled() {
|
|
146
|
+
return _initSucceeded;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Attach the current request's user to Sentry's isolation scope so any
|
|
150
|
+
* `captureException` triggered later in the request carries the right
|
|
151
|
+
* `user.id` / `user.email` / `user.username` and `orgId` tag.
|
|
152
|
+
*
|
|
153
|
+
* Sentry node 10 uses Node's AsyncLocalStorage to give each async context
|
|
154
|
+
* its own isolation scope, so setting on `getIsolationScope()` here only
|
|
155
|
+
* affects events emitted while this request's async context is active.
|
|
156
|
+
*
|
|
157
|
+
* No-ops gracefully when Sentry isn't initialized or no session exists —
|
|
158
|
+
* never throws into the request path.
|
|
159
|
+
*/
|
|
160
|
+
export function setSentryUserForRequest(session) {
|
|
161
|
+
if (!_initSucceeded)
|
|
162
|
+
return;
|
|
163
|
+
try {
|
|
164
|
+
const scope = Sentry.getIsolationScope();
|
|
165
|
+
if (!session) {
|
|
166
|
+
scope.setUser(null);
|
|
167
|
+
scope.setTag("orgId", null);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
scope.setUser({
|
|
171
|
+
id: session.userId ?? session.email,
|
|
172
|
+
email: session.email,
|
|
173
|
+
username: session.name,
|
|
174
|
+
});
|
|
175
|
+
scope.setTag("orgId", session.orgId ?? null);
|
|
176
|
+
if (session.orgRole) {
|
|
177
|
+
scope.setTag("orgRole", session.orgRole);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Sentry scope APIs should never throw, but if they do we'd rather
|
|
182
|
+
// continue serving the request than crash on observability.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Pin a user/org onto the current isolation scope from a lighter
|
|
187
|
+
* `RequestContext`-shaped payload. Used by the request-context observer so
|
|
188
|
+
* action handlers, agent-chat runs, and integration webhook processors —
|
|
189
|
+
* all of which already wrap their work in `runWithRequestContext({ userEmail,
|
|
190
|
+
* orgId, ... })` — automatically tag Sentry events with the right user even
|
|
191
|
+
* when the Nitro `request` hook didn't see a cookie (e.g. webhook delivery,
|
|
192
|
+
* A2A calls, internal background runs).
|
|
193
|
+
*
|
|
194
|
+
* Skips overwriting a richer user identity already set by
|
|
195
|
+
* `setSentryUserForRequest` — the cookie-resolved session has
|
|
196
|
+
* userId/username on top of email, which we shouldn't clobber.
|
|
197
|
+
*/
|
|
198
|
+
export function setSentryRequestContext(ctx) {
|
|
199
|
+
if (!_initSucceeded)
|
|
200
|
+
return;
|
|
201
|
+
try {
|
|
202
|
+
const scope = Sentry.getIsolationScope();
|
|
203
|
+
if (ctx.userEmail) {
|
|
204
|
+
const existing = scope.getScopeData().user;
|
|
205
|
+
if (!existing?.id && !existing?.email) {
|
|
206
|
+
scope.setUser({ id: ctx.userEmail, email: ctx.userEmail });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (ctx.orgId) {
|
|
210
|
+
scope.setTag("orgId", ctx.orgId);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// never throw
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Capture an error from one of the auth attempt routes (login / signup)
|
|
219
|
+
* with the email pinned to the event so support can filter by user. Sets
|
|
220
|
+
* Sentry level to `warning` (not `error`) — bad-password attempts aren't
|
|
221
|
+
* actionable, but a sustained spike of warnings on a route IS the signal
|
|
222
|
+
* we care about.
|
|
223
|
+
*
|
|
224
|
+
* Caller should still return their normal HTTP response (401/409/etc.);
|
|
225
|
+
* this just records the error for observability.
|
|
226
|
+
*/
|
|
227
|
+
export function captureAuthError(error, context) {
|
|
228
|
+
if (!_initSucceeded)
|
|
229
|
+
return undefined;
|
|
230
|
+
try {
|
|
231
|
+
return Sentry.withScope((scope) => {
|
|
232
|
+
scope.setLevel("warning");
|
|
233
|
+
scope.setTag("auth", context.route);
|
|
234
|
+
if (context.email) {
|
|
235
|
+
scope.setUser({ id: context.email, email: context.email });
|
|
236
|
+
}
|
|
237
|
+
return Sentry.captureException(error);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Capture an exception that surfaced in a Nitro route handler with the
|
|
246
|
+
* request's route/method/userAgent attached as searchable Sentry tags.
|
|
247
|
+
*
|
|
248
|
+
* Non-throwing: if Sentry isn't initialized or the underlying capture
|
|
249
|
+
* fails, this is a no-op. Returns the Sentry event ID when capture
|
|
250
|
+
* succeeded, otherwise `undefined`.
|
|
251
|
+
*/
|
|
252
|
+
export function captureRouteError(error, context = {}) {
|
|
253
|
+
if (!_initSucceeded)
|
|
254
|
+
return undefined;
|
|
255
|
+
try {
|
|
256
|
+
return Sentry.withScope((scope) => {
|
|
257
|
+
if (context.route)
|
|
258
|
+
scope.setTag("route", context.route);
|
|
259
|
+
if (context.method)
|
|
260
|
+
scope.setTag("method", context.method);
|
|
261
|
+
if (context.userAgent)
|
|
262
|
+
scope.setTag("userAgent", context.userAgent);
|
|
263
|
+
if (context.tags) {
|
|
264
|
+
for (const [k, v] of Object.entries(context.tags)) {
|
|
265
|
+
if (typeof v === "string")
|
|
266
|
+
scope.setTag(k, v);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (context.extra) {
|
|
270
|
+
for (const [k, v] of Object.entries(context.extra)) {
|
|
271
|
+
if (v !== undefined)
|
|
272
|
+
scope.setExtra(k, v);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (context.contexts) {
|
|
276
|
+
for (const [k, v] of Object.entries(context.contexts)) {
|
|
277
|
+
scope.setContext(k, v);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return Sentry.captureException(error);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=sentry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../src/server/sentry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-side Sentry initialization for Nitro.\n *\n * Errors thrown inside Nitro routes (the framework's own /_agent-native/*\n * handlers, the template's API routes, action handlers, agent-chat streams)\n * never reach the CLI's Sentry init — that only covers the developer's\n * machine. Without server-side Sentry the only signal a 500 ever produces\n * is a server-side console.error that lives and dies with the request.\n *\n * This module is the third Sentry init point in the framework:\n * - cli/index.ts → @sentry/node, hardcoded DSN, \"agent-native-cli\"\n * - client/analytics.ts → @sentry/browser, VITE_SENTRY_CLIENT_DSN\n * - server/sentry.ts → @sentry/node, SENTRY_SERVER_DSN\n *\n * Each maps to a different Sentry project so we can route errors to the\n * right team without one project drowning out the others. Don't wire the\n * three together — they share the SDK package but live in different\n * processes / runtimes / call sites.\n */\nimport * as Sentry from \"@sentry/node\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AuthSession } from \"./auth.js\";\n\nlet _initStarted = false;\nlet _initSucceeded = false;\n\n/**\n * Resolve the agent-native version baked into core's package.json so Sentry\n * \"release\" reflects the running framework version. Mirrors how the CLI\n * computes `_version` — same dist layout, same fallback string. Guarded so\n * a missing/unreadable package.json never crashes server boot.\n */\nfunction resolveServerRelease(): string {\n const explicit = process.env.AGENT_NATIVE_RELEASE;\n if (explicit) return explicit;\n try {\n const here = path.dirname(fileURLToPath(import.meta.url));\n // dist/server/sentry.js → ../../package.json\n const pkgPath = path.resolve(here, \"../../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\")) as {\n version?: string;\n };\n if (pkg?.version) return `agent-native-server@${pkg.version}`;\n } catch {\n // ignore — fall through to \"unknown\"\n }\n return \"agent-native-server@unknown\";\n}\n\nfunction parseTracesSampleRate(): number {\n const raw = process.env.SENTRY_SERVER_TRACES_SAMPLE_RATE;\n if (!raw) return 0;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0 || n > 1) return 0;\n return n;\n}\n\n/**\n * Initialize server-side Sentry. Idempotent — safe to call from multiple\n * plugin entrypoints. Returns `true` if initialization actually happened\n * (DSN was set), `false` if Sentry is disabled (no DSN).\n *\n * No DSN is hardcoded: unlike the CLI (a published binary that always\n * wants to phone home crashes), the server runs in customer environments.\n * Operators set `SENTRY_SERVER_DSN` when they want their own Sentry\n * project to receive these events; without it the module no-ops.\n */\nexport function initServerSentry(): boolean {\n if (_initStarted) return _initSucceeded;\n _initStarted = true;\n\n const dsn = process.env.SENTRY_SERVER_DSN;\n if (!dsn) {\n if (process.env.DEBUG) {\n console.log(\n \"[agent-native] SENTRY_SERVER_DSN not set — server Sentry disabled.\",\n );\n }\n return false;\n }\n\n Sentry.init({\n dsn,\n environment: process.env.NODE_ENV || \"production\",\n release: resolveServerRelease(),\n tracesSampleRate: parseTracesSampleRate(),\n // sendDefaultPii MUST stay false — the framework runs inside customer\n // environments and we never want to silently ship request headers,\n // cookies, or process.env contents to Sentry without explicit consent.\n sendDefaultPii: false,\n beforeSend(event) {\n // Drop expected user-input rejections so they don't pollute Sentry\n // with non-bug noise. Mirrors the CLI's drop list — the framework\n // and CLI both throw `ValidationError` for the same class of input\n // failures, and exception type comes through as the class name.\n const exceptionType = event.exception?.values?.[0]?.type;\n if (\n exceptionType === \"ValidationError\" ||\n event.tags?.handled === \"validation\"\n ) {\n return null;\n }\n\n // Defense in depth: scrub PII even if some integration auto-attached\n // request metadata despite sendDefaultPii: false.\n if (event.request) {\n if (event.request.headers) {\n const headers = event.request.headers as Record<string, string>;\n for (const k of Object.keys(headers)) {\n const lk = k.toLowerCase();\n if (\n lk === \"cookie\" ||\n lk === \"authorization\" ||\n lk === \"set-cookie\" ||\n lk === \"proxy-authorization\"\n ) {\n delete headers[k];\n }\n }\n }\n // Cookies live in their own field too.\n delete (event.request as Record<string, unknown>).cookies;\n }\n\n // Keep user info that was explicitly set via Sentry.setUser\n // (id/email/username) so we can attribute crashes back to a real\n // operator. Always strip ip_address — auto-collected, no consent.\n if (event.user) {\n const user = event.user as Record<string, unknown>;\n delete user.ip_address;\n const hasIdentity =\n typeof user.id === \"string\" ||\n typeof user.email === \"string\" ||\n typeof user.username === \"string\";\n if (!hasIdentity) {\n delete event.user;\n }\n }\n\n // Sentry's contexts can carry process.env snapshots — strip env-shaped\n // contexts so we don't leak deployment secrets.\n if (event.contexts && typeof event.contexts === \"object\") {\n delete (event.contexts as Record<string, unknown>).runtime_env;\n }\n\n return event;\n },\n });\n\n _initSucceeded = true;\n return true;\n}\n\n/**\n * `true` once `initServerSentry()` has succeeded with a DSN. Plugins that\n * want to skip work when Sentry is disabled can check this before calling\n * the helpers below.\n */\nexport function isServerSentryEnabled(): boolean {\n return _initSucceeded;\n}\n\n/**\n * Attach the current request's user to Sentry's isolation scope so any\n * `captureException` triggered later in the request carries the right\n * `user.id` / `user.email` / `user.username` and `orgId` tag.\n *\n * Sentry node 10 uses Node's AsyncLocalStorage to give each async context\n * its own isolation scope, so setting on `getIsolationScope()` here only\n * affects events emitted while this request's async context is active.\n *\n * No-ops gracefully when Sentry isn't initialized or no session exists —\n * never throws into the request path.\n */\nexport function setSentryUserForRequest(session: AuthSession | null): void {\n if (!_initSucceeded) return;\n try {\n const scope = Sentry.getIsolationScope();\n if (!session) {\n scope.setUser(null);\n scope.setTag(\"orgId\", null);\n return;\n }\n scope.setUser({\n id: session.userId ?? session.email,\n email: session.email,\n username: session.name,\n });\n scope.setTag(\"orgId\", session.orgId ?? null);\n if (session.orgRole) {\n scope.setTag(\"orgRole\", session.orgRole);\n }\n } catch {\n // Sentry scope APIs should never throw, but if they do we'd rather\n // continue serving the request than crash on observability.\n }\n}\n\n/**\n * Pin a user/org onto the current isolation scope from a lighter\n * `RequestContext`-shaped payload. Used by the request-context observer so\n * action handlers, agent-chat runs, and integration webhook processors —\n * all of which already wrap their work in `runWithRequestContext({ userEmail,\n * orgId, ... })` — automatically tag Sentry events with the right user even\n * when the Nitro `request` hook didn't see a cookie (e.g. webhook delivery,\n * A2A calls, internal background runs).\n *\n * Skips overwriting a richer user identity already set by\n * `setSentryUserForRequest` — the cookie-resolved session has\n * userId/username on top of email, which we shouldn't clobber.\n */\nexport function setSentryRequestContext(ctx: {\n userEmail?: string;\n orgId?: string;\n}): void {\n if (!_initSucceeded) return;\n try {\n const scope = Sentry.getIsolationScope();\n if (ctx.userEmail) {\n const existing = scope.getScopeData().user;\n if (!existing?.id && !existing?.email) {\n scope.setUser({ id: ctx.userEmail, email: ctx.userEmail });\n }\n }\n if (ctx.orgId) {\n scope.setTag(\"orgId\", ctx.orgId);\n }\n } catch {\n // never throw\n }\n}\n\n/**\n * Capture an error from one of the auth attempt routes (login / signup)\n * with the email pinned to the event so support can filter by user. Sets\n * Sentry level to `warning` (not `error`) — bad-password attempts aren't\n * actionable, but a sustained spike of warnings on a route IS the signal\n * we care about.\n *\n * Caller should still return their normal HTTP response (401/409/etc.);\n * this just records the error for observability.\n */\nexport function captureAuthError(\n error: unknown,\n context: { route: \"login\" | \"signup\" | \"logout\"; email?: string },\n): string | undefined {\n if (!_initSucceeded) return undefined;\n try {\n return Sentry.withScope((scope) => {\n scope.setLevel(\"warning\");\n scope.setTag(\"auth\", context.route);\n if (context.email) {\n scope.setUser({ id: context.email, email: context.email });\n }\n return Sentry.captureException(error);\n });\n } catch {\n return undefined;\n }\n}\n\nexport interface RouteErrorContext {\n /** The full request path (e.g. `/_agent-native/agent-chat`). */\n route?: string;\n /** HTTP method (e.g. `GET`, `POST`). */\n method?: string;\n /** Caller's `User-Agent` header. */\n userAgent?: string;\n /** Free-form extra tags to add to the event (low-cardinality). */\n tags?: Record<string, string | undefined>;\n /**\n * High-cardinality / structured payload — not searchable but visible in\n * the Sentry event detail (recording IDs, byte counts, compression\n * metadata, response body tails, etc.).\n */\n extra?: Record<string, unknown>;\n /**\n * Grouped contexts shown as separate cards in the Sentry event UI.\n */\n contexts?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Capture an exception that surfaced in a Nitro route handler with the\n * request's route/method/userAgent attached as searchable Sentry tags.\n *\n * Non-throwing: if Sentry isn't initialized or the underlying capture\n * fails, this is a no-op. Returns the Sentry event ID when capture\n * succeeded, otherwise `undefined`.\n */\nexport function captureRouteError(\n error: unknown,\n context: RouteErrorContext = {},\n): string | undefined {\n if (!_initSucceeded) return undefined;\n try {\n return Sentry.withScope((scope) => {\n if (context.route) scope.setTag(\"route\", context.route);\n if (context.method) scope.setTag(\"method\", context.method);\n if (context.userAgent) scope.setTag(\"userAgent\", context.userAgent);\n if (context.tags) {\n for (const [k, v] of Object.entries(context.tags)) {\n if (typeof v === \"string\") scope.setTag(k, v);\n }\n }\n if (context.extra) {\n for (const [k, v] of Object.entries(context.extra)) {\n if (v !== undefined) scope.setExtra(k, v);\n }\n }\n if (context.contexts) {\n for (const [k, v] of Object.entries(context.contexts)) {\n scope.setContext(k, v);\n }\n }\n return Sentry.captureException(error);\n });\n } catch {\n return undefined;\n }\n}\n"]}
|
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
* `{ error }` on failure.
|
|
7
7
|
*
|
|
8
8
|
* Key resolution order for BYOK providers:
|
|
9
|
-
* 1.
|
|
10
|
-
*
|
|
11
|
-
* 2. `resolveCredential("<PROVIDER>_API_KEY")` — env var + SQL settings
|
|
12
|
-
* store.
|
|
9
|
+
* 1. Request-scoped encrypted secret (`app_secrets`: user, org, workspace).
|
|
10
|
+
* 2. Env var fallback only outside authenticated request contexts.
|
|
13
11
|
*
|
|
14
12
|
* If no server provider is configured, returns 400 with an error the
|
|
15
13
|
* composer UI can surface (the client falls back to Web Speech when possible).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcribe-voice.d.ts","sourceRoot":"","sources":["../../src/server/transcribe-voice.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"transcribe-voice.d.ts","sourceRoot":"","sources":["../../src/server/transcribe-voice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA8FH,wBAAgB,4BAA4B;UA8bxB,MAAM;;;IA7FzB"}
|
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
* `{ error }` on failure.
|
|
7
7
|
*
|
|
8
8
|
* Key resolution order for BYOK providers:
|
|
9
|
-
* 1.
|
|
10
|
-
*
|
|
11
|
-
* 2. `resolveCredential("<PROVIDER>_API_KEY")` — env var + SQL settings
|
|
12
|
-
* store.
|
|
9
|
+
* 1. Request-scoped encrypted secret (`app_secrets`: user, org, workspace).
|
|
10
|
+
* 2. Env var fallback only outside authenticated request contexts.
|
|
13
11
|
*
|
|
14
12
|
* If no server provider is configured, returns 400 with an error the
|
|
15
13
|
* composer UI can surface (the client falls back to Web Speech when possible).
|
|
@@ -19,11 +17,9 @@
|
|
|
19
17
|
* typed JSON-in / JSON-out).
|
|
20
18
|
*/
|
|
21
19
|
import { defineEventHandler, getMethod, getRequestHeader, readMultipartFormData, setResponseStatus, } from "h3";
|
|
22
|
-
import { readAppSecret } from "../secrets/storage.js";
|
|
23
|
-
import { resolveCredential } from "../credentials/index.js";
|
|
24
20
|
import { getSession } from "./auth.js";
|
|
25
21
|
import { appStateGet } from "../application-state/store.js";
|
|
26
|
-
import { resolveHasBuilderPrivateKey } from "./credential-provider.js";
|
|
22
|
+
import { resolveHasBuilderPrivateKey, resolveSecret, } from "./credential-provider.js";
|
|
27
23
|
import { transcribeWithBuilder } from "../transcription/builder-transcription.js";
|
|
28
24
|
import { runWithRequestContext } from "./request-context.js";
|
|
29
25
|
import { getOrgContext } from "../org/context.js";
|
|
@@ -200,15 +196,7 @@ export function createTranscribeVoiceHandler() {
|
|
|
200
196
|
// Per-user-or-fallback API key resolution. Hoisted up so the Gemini
|
|
201
197
|
// path below can use it without duplicating logic.
|
|
202
198
|
async function resolveApiKey(key) {
|
|
203
|
-
|
|
204
|
-
if (!session?.email)
|
|
205
|
-
return (await resolveCredential(key, ctx)) ?? undefined;
|
|
206
|
-
const userSecret = await readAppSecret({
|
|
207
|
-
key,
|
|
208
|
-
scope: "user",
|
|
209
|
-
scopeId: session.email,
|
|
210
|
-
}).catch(() => null);
|
|
211
|
-
return (userSecret?.value || (await resolveCredential(key, ctx)) || undefined);
|
|
199
|
+
return (await withRequestContext(() => resolveSecret(key))) ?? undefined;
|
|
212
200
|
}
|
|
213
201
|
if (transcriptText) {
|
|
214
202
|
return await cleanupTranscriptText({
|