@agent-native/core 0.205.0 → 0.206.0-nightly-20261009011246

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 (115) hide show
  1. package/dist/a2a/client.d.ts +2 -2
  2. package/dist/agent/engine/anthropic-engine.d.ts +1 -1
  3. package/dist/agent/engine/builder-engine.d.ts +1 -1
  4. package/dist/agent/engine/builder-engine.js +23 -8
  5. package/dist/agent/engine/error-telemetry.d.ts +5 -0
  6. package/dist/agent/engine/error-telemetry.js +41 -0
  7. package/dist/agent/engine/registry.d.ts +1 -6
  8. package/dist/agent/engine/registry.js +2 -31
  9. package/dist/agent/model-config.d.ts +26 -14
  10. package/dist/agent/model-config.js +131 -8
  11. package/dist/agent/model-version.d.ts +19 -0
  12. package/dist/agent/model-version.js +153 -0
  13. package/dist/agent/production-agent.d.ts +1 -0
  14. package/dist/agent/production-agent.js +24 -6
  15. package/dist/agent/provider-model-selection.js +6 -4
  16. package/dist/agent/run-manager.js +5 -11
  17. package/dist/agent/thread-data-builder.js +14 -6
  18. package/dist/authorization/check-action.js +8 -4
  19. package/dist/chat-threads/store.js +22 -9
  20. package/dist/client/analytics.d.ts +1 -0
  21. package/dist/client/analytics.js +10 -2
  22. package/dist/client/chat/runtime.js +18 -0
  23. package/dist/client/chat-model-groups.d.ts +3 -0
  24. package/dist/client/error-format.js +10 -7
  25. package/dist/client/onboarding/use-onboarding.d.ts +12 -0
  26. package/dist/client/onboarding/use-onboarding.js +491 -4
  27. package/dist/client/session-replay-privacy.d.ts +8 -0
  28. package/dist/client/session-replay-privacy.js +8 -0
  29. package/dist/client/session-replay.d.ts +1 -0
  30. package/dist/client/session-replay.js +18 -9
  31. package/dist/client/sse-event-processor.js +10 -6
  32. package/dist/client/use-chat-models.d.ts +5 -0
  33. package/dist/client/use-chat-models.js +51 -5
  34. package/dist/client/use-session.d.ts +8 -1
  35. package/dist/client/use-session.js +20 -1
  36. package/dist/collab/struct-routes.d.ts +1 -1
  37. package/dist/deploy/build.js +353 -3
  38. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  39. package/dist/jobs/backfill-run-history-ownership.d.ts +12 -0
  40. package/dist/jobs/backfill-run-history-ownership.js +87 -0
  41. package/dist/jobs/background-automation-runner.js +3 -9
  42. package/dist/jobs/remote-execution.js +3 -9
  43. package/dist/jobs/run-history-ownership.d.ts +5 -0
  44. package/dist/jobs/run-history-ownership.js +16 -0
  45. package/dist/jobs/run-now.js +2 -3
  46. package/dist/localization/agent-resources-messages.d.ts +3 -0
  47. package/dist/localization/agent-resources-messages.js +33 -0
  48. package/dist/mcp/actions/create-org-service-token.js +5 -7
  49. package/dist/mcp/build-server.d.ts +23 -18
  50. package/dist/mcp/build-server.js +220 -212
  51. package/dist/mcp/connect-route.d.ts +11 -2
  52. package/dist/mcp/connect-route.js +34 -24
  53. package/dist/mcp/connect-store.d.ts +9 -13
  54. package/dist/mcp/connect-store.js +11 -24
  55. package/dist/mcp/credential-membership.js +17 -6
  56. package/dist/mcp/oauth-route.d.ts +11 -1
  57. package/dist/mcp/oauth-route.js +28 -6
  58. package/dist/mcp/oauth-token.d.ts +12 -2
  59. package/dist/mcp/oauth-token.js +15 -4
  60. package/dist/mcp/server.js +9 -5
  61. package/dist/notifications/routes.d.ts +2 -2
  62. package/dist/observability/posthog-ai.d.ts +1 -0
  63. package/dist/observability/posthog-ai.js +20 -8
  64. package/dist/observability/routes.d.ts +6 -6
  65. package/dist/observability/traces.js +61 -39
  66. package/dist/org/app-roles.d.ts +8 -1
  67. package/dist/org/app-roles.js +29 -9
  68. package/dist/org/context.js +24 -8
  69. package/dist/org/index.d.ts +2 -2
  70. package/dist/org/workspace-app-identity.d.ts +1 -0
  71. package/dist/org/workspace-app-identity.js +25 -0
  72. package/dist/progress/routes.d.ts +1 -1
  73. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  74. package/dist/provider-api/actions/provider-api.d.ts +3 -3
  75. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  76. package/dist/scripts/agent-engines/list-agent-engines.js +1 -0
  77. package/dist/server/action-routes.js +8 -1
  78. package/dist/server/agent-chat-plugin.js +1 -0
  79. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  80. package/dist/server/auth.js +33 -17
  81. package/dist/server/bearer-credential-refusal.d.ts +23 -0
  82. package/dist/server/bearer-credential-refusal.js +67 -0
  83. package/dist/server/better-auth-instance.js +13 -6
  84. package/dist/server/builder-browser.d.ts +2 -0
  85. package/dist/server/builder-browser.js +16 -3
  86. package/dist/server/capture-error.d.ts +1 -0
  87. package/dist/server/capture-error.js +28 -4
  88. package/dist/server/complete-text.js +16 -3
  89. package/dist/server/core-routes-plugin.js +2 -0
  90. package/dist/server/dev-action-bridge.js +7 -1
  91. package/dist/server/google-oauth.js +6 -3
  92. package/dist/server/index.d.ts +1 -1
  93. package/dist/server/index.js +1 -1
  94. package/dist/server/realtime-token.d.ts +1 -1
  95. package/dist/server/recap-image-route.js +4 -0
  96. package/dist/server/request-context.d.ts +16 -0
  97. package/dist/server/request-context.js +17 -0
  98. package/dist/shared/agent-chat-context.d.ts +1 -0
  99. package/dist/shared/agent-chat-context.js +26 -0
  100. package/dist/shared/index.d.ts +1 -1
  101. package/dist/shared/index.js +1 -1
  102. package/dist/shared/reasoning-effort.js +10 -1
  103. package/dist/shared/ssr-session-bootstrap.d.ts +7 -0
  104. package/dist/shared/ssr-session-bootstrap.js +21 -0
  105. package/dist/sharing/access.js +86 -5
  106. package/dist/templates/builder-code-starter/server/routes/index.get.ts +2 -0
  107. package/dist/triggers/routes.js +2 -1
  108. package/docs/content/automation-connectors.mdx +5 -3
  109. package/docs/content/automations.mdx +78 -49
  110. package/docs/content/observability.mdx +16 -5
  111. package/docs/content/organizations-teams-permissions.mdx +44 -2
  112. package/docs/content/recurring-jobs.mdx +4 -8
  113. package/docs/content/template-forms.mdx +6 -0
  114. package/package.json +5 -4
  115. package/src/templates/builder-code-starter/server/routes/index.get.ts +2 -0
@@ -73,7 +73,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
- deleted?: undefined;
77
76
  jobs: {
78
77
  id: string;
79
78
  name: string;
@@ -84,6 +83,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
84
83
  updatedAt: string;
85
84
  }[];
86
85
  total: number;
86
+ deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
- deleted?: undefined;
95
94
  jobs?: undefined;
96
95
  total?: undefined;
96
+ deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
@@ -201,6 +201,7 @@ export async function run(args = {}) {
201
201
  label: e.label,
202
202
  description: e.description,
203
203
  defaultModel: e.defaultModel,
204
+ runtimeSupportedModels: e.supportedModels,
204
205
  supportedModels: e.name === CHATGPT_SUBSCRIPTION_ENGINE_NAME
205
206
  ? (chatGPTCatalog?.models ?? [])
206
207
  : applyProviderModelSelection(e.supportedModels, modelSelection),
@@ -43,7 +43,7 @@ function currentBuildId() {
43
43
  }
44
44
  import { isLoopbackRequest, registerAuthPublicPaths } from "./auth.js";
45
45
  import { getH3App } from "./framework-request-handler.js";
46
- import { getRequestIdentityAuthenticatedAtMs, getRequestIdentitySessionToken, hasExplicitPersonalOrgScope, markExplicitPersonalOrgScope, runWithRequestContext, } from "./request-context.js";
46
+ import { getVerifiedServiceIdentityFromEvent, getRequestIdentityAuthenticatedAtMs, getRequestIdentitySessionToken, hasExplicitPersonalOrgScope, markExplicitPersonalOrgScope, runWithRequestContext, } from "./request-context.js";
47
47
  const ROUTE_PREFIX = "/_agent-native/actions";
48
48
  const WEBMCP_ACTION_ROUTE_PREFIX = "/_agent-native/webmcp/actions";
49
49
  const MAX_MCP_DIRECTORY_WIDGET_SCHEMA_ARGUMENT_BYTES = 32 * 1024;
@@ -640,8 +640,15 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
640
640
  const identitySessionToken = userEmail
641
641
  ? getRequestIdentitySessionToken(event, userEmail)
642
642
  : undefined;
643
+ const verifiedServiceIdentity = getVerifiedServiceIdentityFromEvent(event);
643
644
  return runWithRequestContext({
644
645
  userEmail,
646
+ ...(verifiedServiceIdentity &&
647
+ verifiedServiceIdentity.userEmail.trim().toLowerCase() ===
648
+ userEmail?.trim().toLowerCase() &&
649
+ verifiedServiceIdentity.orgId === orgId
650
+ ? { verifiedServiceIdentity }
651
+ : {}),
645
652
  ...(identityAuthenticatedAtMs !== undefined
646
653
  ? { identityAuthenticatedAtMs }
647
654
  : {}),
@@ -4063,6 +4063,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4063
4063
  label: entry.label,
4064
4064
  description: entry.description,
4065
4065
  defaultModel: entry.defaultModel,
4066
+ runtimeSupportedModels: entry.supportedModels,
4066
4067
  ...(await modelsFor(entry)),
4067
4068
  requiredEnvVars: entry.requiredEnvVars,
4068
4069
  installPackage: entry.installPackage,
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
8
8
  * resolveEngine's credential detection.
9
9
  */
10
10
  export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
11
- error: string;
12
11
  ok?: undefined;
12
+ error: string;
13
13
  scope?: undefined;
14
14
  } | {
15
15
  ok: boolean;
@@ -4,6 +4,7 @@ import { getAppConfig, resolveAppHomePath } from "../app-config/index.js";
4
4
  import { acceptPendingInvitationsForEmail } from "../org/accept-pending.js";
5
5
  import { newOrgSelection, ORG_SELECTION_COOKIE, } from "../org/request-org-cache.js";
6
6
  import { isWorkspaceAppAccessAllowed, WORKSPACE_APP_ACCESS_UNAVAILABLE, WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE, } from "../org/workspace-app-access.js";
7
+ import { resolveWorkspaceAccessAppId } from "../org/workspace-app-identity.js";
7
8
  import { EMBED_SESSION_COOKIE, EMBED_START_PATH, EMBED_TARGET_HEADER, isMcpDirectoryWidgetReadCapabilityScope, } from "../shared/embed-auth.js";
8
9
  import { FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_MAX_AGE, } from "../shared/first-run-onboarding.js";
9
10
  import { isGoogleProfileImageUrl } from "../shared/google-profile-image.js";
@@ -65,6 +66,7 @@ import { getAppOriginClientConfigScript } from "./app-origin-config.js";
65
66
  import { getAppProductionUrl } from "./app-url.js";
66
67
  import { addSignupAttributionHeader, readAnalyticsAnonymousId, signupAttributionContextFromCookieHeader, signupAttributionFromCookieHeader, } from "./attribution.js";
67
68
  import { getAuthLoginMode } from "./auth-login-mode.js";
69
+ import { markBearerCredentialRefused, respondBearerCredentialRefused, } from "./bearer-credential-refusal.js";
68
70
  import { injectBetaOptOutPersistence } from "./beta-opt-out-html.js";
69
71
  import { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, getBetterAuthInternalAdapter, getBetterAuthUserIdForEmail, getAuthSecret, getBetterAuth, getBetterAuthSync, isDeployPreview, } from "./better-auth-instance.js";
70
72
  import { BUILDER_CONNECT_PARAM, BUILDER_RELAY_PATH, BUILDER_RELAY_STATE_PARAM, verifyBuilderConnectTokenAndGetOwner, verifyBuilderPreviewRelayStateForCallback, } from "./builder-browser.js";
@@ -86,7 +88,7 @@ import { encodeMagicLinkSignupAttribution, MAGIC_LINK_ATTRIBUTION_PARAM, } from
86
88
  import { safeOAuthReturnUrl } from "./oauth-return-url.js";
87
89
  import { getDeploySettingsRequiredHtml, getOnboardingHtml, getResetPasswordHtml, } from "./onboarding-html.js";
88
90
  import { queryEchoSafeRedirect } from "./query-echo-safe-redirect.js";
89
- import { getRequestContext, markRequestIdentityAuthenticatedAtMs, hasContinuationLocalRequestContext, hasExplicitPersonalOrgScope, markExplicitPersonalOrgScope, runWithRequestContext, } from "./request-context.js";
91
+ import { getRequestContext, markRequestIdentityAuthenticatedAtMs, markVerifiedServiceIdentityForEvent, hasContinuationLocalRequestContext, hasExplicitPersonalOrgScope, markExplicitPersonalOrgScope, runWithRequestContext, } from "./request-context.js";
90
92
  import { captureAuthError } from "./sentry.js";
91
93
  import { isTestIdentity } from "./test-identity.js";
92
94
  import { isWorkspaceOAuthCallbackRelayEnabled } from "./workspace-oauth.js";
@@ -773,7 +775,7 @@ export async function getMcpOAuthBearerSession(event) {
773
775
  if (!bearerToken)
774
776
  return null;
775
777
  try {
776
- const [{ getMcpOAuthAudiences }, { verifyAuth, resolveMcpIdentityOrgId }] = await Promise.all([
778
+ const [{ getMcpConnectUrl, getMcpOAuthAudiences }, { verifyAuth, resolveMcpIdentityOrgId },] = await Promise.all([
777
779
  import("../mcp/oauth-route.js"),
778
780
  import("../mcp/build-server.js"),
779
781
  ]);
@@ -786,12 +788,26 @@ export async function getMcpOAuthBearerSession(event) {
786
788
  markCredentialMembershipUnavailable(event);
787
789
  return null;
788
790
  }
789
- const identity = result.authed ? result.identity : undefined;
791
+ if (!result.authed) {
792
+ if (result.refusal) {
793
+ markBearerCredentialRefused(event, result.refusal, getMcpConnectUrl(event));
794
+ }
795
+ return null;
796
+ }
797
+ const identity = result.identity;
790
798
  if (!identity?.userEmail)
791
799
  return null;
792
800
  if (identity.orgId === null)
793
801
  markExplicitPersonalOrgScope(event);
794
802
  const orgId = await resolveMcpIdentityOrgId(identity);
803
+ if (identity.identityAssurance === "service" &&
804
+ identity.orgId &&
805
+ orgId === identity.orgId) {
806
+ markVerifiedServiceIdentityForEvent(event, {
807
+ userEmail: identity.userEmail,
808
+ orgId: identity.orgId,
809
+ });
810
+ }
795
811
  return {
796
812
  email: identity.userEmail,
797
813
  token: bearerToken,
@@ -2682,20 +2698,6 @@ function loginHtmlResponse(config, event, requestPath, options = {}) {
2682
2698
  },
2683
2699
  });
2684
2700
  }
2685
- function resolveWorkspaceAccessAppId() {
2686
- const app = getAppConfig().app;
2687
- const workspaceId = app.workspaceId?.trim();
2688
- if (workspaceId)
2689
- return workspaceId;
2690
- const isDispatch = [
2691
- app.id,
2692
- app.legacyId,
2693
- app.template,
2694
- app.slug,
2695
- app.packageName,
2696
- ].some((value) => value?.trim().toLowerCase() === "dispatch");
2697
- return isDispatch ? "dispatch" : "";
2698
- }
2699
2701
  function isHtmlDocumentRequest(event, pathname) {
2700
2702
  if (!isReadMethod(event))
2701
2703
  return false;
@@ -3049,6 +3051,9 @@ function createAuthGuardFn(app) {
3049
3051
  if (isCredentialMembershipUnavailable(event)) {
3050
3052
  return respondCredentialMembershipUnavailable(event);
3051
3053
  }
3054
+ const refused = respondBearerCredentialRefused(event);
3055
+ if (refused)
3056
+ return refused;
3052
3057
  if (p.startsWith("/api/") || p.startsWith("/_agent-native/")) {
3053
3058
  setResponseStatus(event, 401);
3054
3059
  // Dev-only breadcrumb for the loopback origin-label trap: the session
@@ -3615,6 +3620,17 @@ function normalizePath(path) {
3615
3620
  function matchesPathList(path, paths) {
3616
3621
  return paths.some((candidate) => {
3617
3622
  const normalized = normalizePath(candidate);
3623
+ if (normalized.includes("/:")) {
3624
+ const expectedSegments = normalized.split("/");
3625
+ const actualSegments = path.split("/");
3626
+ return (expectedSegments.length === actualSegments.length &&
3627
+ expectedSegments.every((segment, index) => {
3628
+ if (/^:[A-Za-z][A-Za-z0-9_]*$/.test(segment)) {
3629
+ return Boolean(actualSegments[index]);
3630
+ }
3631
+ return segment === actualSegments[index];
3632
+ }));
3633
+ }
3618
3634
  return path === normalized || path.startsWith(normalized + "/");
3619
3635
  });
3620
3636
  }
@@ -0,0 +1,23 @@
1
+ import { type H3Event } from "h3";
2
+ /**
3
+ * Why a presented bearer token was refused. `invalid` covers every token
4
+ * nothing here could verify. The others describe a credential this app issued
5
+ * and verified, so its holder has proven possession and can act on the reason.
6
+ */
7
+ export type BearerCredentialRefusal = "invalid" | "revoked" | "unknown-connect-token" | "identity-mismatch" | "not-member" | "email-retired" | "service-principal-inactive";
8
+ /**
9
+ * One sentence naming the refusal. It travels in a `WWW-Authenticate`
10
+ * `error_description`, so it must stay printable ASCII without `"` or `\`.
11
+ */
12
+ export declare function describeBearerCredentialRefusal(refusal: BearerCredentialRefusal): string;
13
+ export declare function describeBearerCredentialRefusalWithRecovery(refusal: BearerCredentialRefusal, connectUrl: string | undefined): string;
14
+ /**
15
+ * Records why this request's bearer token was refused, so the auth guard's
16
+ * 401 says why instead of a bare `Unauthorized`.
17
+ */
18
+ export declare function markBearerCredentialRefused(event: H3Event, refusal: BearerCredentialRefusal, connectUrl: string | undefined): void;
19
+ export declare function respondBearerCredentialRefused(event: H3Event): {
20
+ error: string;
21
+ reason: BearerCredentialRefusal;
22
+ message: string;
23
+ } | null;
@@ -0,0 +1,67 @@
1
+ import { setResponseStatus } from "h3";
2
+ const CONTEXT_KEY = "__anBearerCredentialRefusal";
3
+ /**
4
+ * One sentence naming the refusal. It travels in a `WWW-Authenticate`
5
+ * `error_description`, so it must stay printable ASCII without `"` or `\`.
6
+ */
7
+ export function describeBearerCredentialRefusal(refusal) {
8
+ switch (refusal) {
9
+ case "revoked":
10
+ return "This token was revoked.";
11
+ case "unknown-connect-token":
12
+ return "This app has no record of this connect token: another deployment minted it, or its record was deleted.";
13
+ case "identity-mismatch":
14
+ return "This connect token does not match the account it was issued to.";
15
+ case "not-member":
16
+ return "This token's account is no longer a member of the organization it was issued for.";
17
+ case "email-retired":
18
+ return "This token was issued to an email address the account no longer uses.";
19
+ case "service-principal-inactive":
20
+ return "This token belongs to a service principal an organization admin suspended or retired; only an admin can resume a suspended one.";
21
+ case "invalid":
22
+ return "This bearer token could not be verified: it is malformed, expired, issued for another app, or older than a required reconnect.";
23
+ }
24
+ }
25
+ /** RFC 6750 `error_description` characters: printable ASCII except `"` and `\`. */
26
+ const ERROR_DESCRIPTION_CHARACTERS = /^[\x20\x21\x23-\x5B\x5D-\x7E]*$/;
27
+ /**
28
+ * The connect URL serialized as an ASCII URL, or undefined when it still could
29
+ * not travel in an `error_description`. Its host comes from request headers
30
+ * and its base path from configuration, so neither is trusted to be safe.
31
+ */
32
+ function headerSafeUrl(url) {
33
+ if (!url)
34
+ return undefined;
35
+ let href;
36
+ try {
37
+ href = new URL(url).href;
38
+ }
39
+ catch {
40
+ // coercion-ok: an unparseable connect URL is left out of the hint, which then names no URL.
41
+ return undefined;
42
+ }
43
+ return ERROR_DESCRIPTION_CHARACTERS.test(href) ? href : undefined;
44
+ }
45
+ export function describeBearerCredentialRefusalWithRecovery(refusal, connectUrl) {
46
+ const url = headerSafeUrl(connectUrl);
47
+ const recovery = url ? `Reconnect at ${url}.` : "Reconnect this connector.";
48
+ return `${describeBearerCredentialRefusal(refusal)} ${recovery}`;
49
+ }
50
+ /**
51
+ * Records why this request's bearer token was refused, so the auth guard's
52
+ * 401 says why instead of a bare `Unauthorized`.
53
+ */
54
+ export function markBearerCredentialRefused(event, refusal, connectUrl) {
55
+ const holder = event;
56
+ (holder.context ??= {})[CONTEXT_KEY] = {
57
+ reason: refusal,
58
+ message: describeBearerCredentialRefusalWithRecovery(refusal, connectUrl),
59
+ };
60
+ }
61
+ export function respondBearerCredentialRefused(event) {
62
+ const refused = event.context?.[CONTEXT_KEY];
63
+ if (!refused)
64
+ return null;
65
+ setResponseStatus(event, 401);
66
+ return { error: "Unauthorized", ...refused };
67
+ }
@@ -28,6 +28,7 @@ import { getAuthEmailForUserId, getRequiredAuthProviderForEmail, } from "../org/
28
28
  import { autoJoinDomainMatchingOrgs } from "../org/auto-join-domain.js";
29
29
  import { createFrameworkSCIMIdentity, frameworkOrgBridgePlugin, } from "../org/scim-provisioning.js";
30
30
  import { enforceSignupAdmission, isBootstrapAdmin, } from "../org/signup-admission.js";
31
+ import { normalizeAnalyticsSessionId } from "../shared/analytics-session-id.js";
31
32
  import { isGoogleProfileImageUrl } from "../shared/google-profile-image.js";
32
33
  import { loadOptionalPeer } from "../shared/optional-peer.js";
33
34
  import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, } from "../shared/password-policy.js";
@@ -131,6 +132,7 @@ export async function emitSignupEventForCreatedUser(user, context) {
131
132
  const requestHeaders = context?.headers ?? context?.request?.headers ?? null;
132
133
  if (!requestHeaders)
133
134
  return;
135
+ const requestSessionId = normalizeAnalyticsSessionId(requestHeaders.get("x-agent-native-session-id"));
134
136
  const scoped = hasContinuationLocalRequestContext()
135
137
  ? getRequestContext()
136
138
  : undefined;
@@ -146,7 +148,8 @@ export async function emitSignupEventForCreatedUser(user, context) {
146
148
  signupAttributionContextFromCookieHeader(requestHeaders.get("cookie"));
147
149
  attribution = browser?.attribution;
148
150
  anonymousId = browser?.anonymousId;
149
- sessionId = browser?.sessionId;
151
+ sessionId =
152
+ normalizeAnalyticsSessionId(browser?.sessionId) ?? requestSessionId;
150
153
  }
151
154
  catch (err) {
152
155
  console.error("[auth] failed to derive signup attribution", err);
@@ -155,9 +158,13 @@ export async function emitSignupEventForCreatedUser(user, context) {
155
158
  // so an account created by another signed-in user (admin or API creation)
156
159
  // must not inherit it.
157
160
  const actingUserId = context?.context?.session?.user?.id;
158
- if (user.id && attribution && (!actingUserId || actingUserId === user.id)) {
161
+ const ownsSignupAttribution = !actingUserId || actingUserId === user.id;
162
+ const eventAttribution = ownsSignupAttribution ? attribution : undefined;
163
+ const eventAnonymousId = ownsSignupAttribution ? anonymousId : undefined;
164
+ const eventSessionId = ownsSignupAttribution ? sessionId : undefined;
165
+ if (user.id && eventAttribution) {
159
166
  try {
160
- await persistUserFirstTouchAttribution(user.id, attribution);
167
+ await persistUserFirstTouchAttribution(user.id, eventAttribution);
161
168
  }
162
169
  catch (err) {
163
170
  // The signup itself already succeeded; the event below still carries
@@ -177,9 +184,9 @@ export async function emitSignupEventForCreatedUser(user, context) {
177
184
  authUserId: user.id,
178
185
  email,
179
186
  name: user.name,
180
- attribution,
181
- anonymousId,
182
- sessionId,
187
+ attribution: eventAttribution,
188
+ anonymousId: eventAnonymousId,
189
+ sessionId: eventSessionId,
183
190
  });
184
191
  }
185
192
  export async function hasGoogleAuthIdentity(email) {
@@ -302,6 +302,8 @@ export declare function isBuilderAccountProvisioningEnabled(): boolean;
302
302
  export declare function provisionBuilderAccount(input: {
303
303
  email: string;
304
304
  name?: string;
305
+ agentNativeApp?: string;
306
+ agentNativeTemplate?: string;
305
307
  }): Promise<BuilderRelayCredentials>;
306
308
  /** @deprecated Repository-backed Builder projects are retained for compatibility. */
307
309
  export declare function findBuilderProjectForRepo(args: BuilderProjectLookupArgs): Promise<BuilderProjectResult | null>;
@@ -1819,14 +1819,22 @@ export async function provisionBuilderAccount(input) {
1819
1819
  throw new Error("Builder account provisioning requires a valid email.");
1820
1820
  }
1821
1821
  const name = input.name?.trim().slice(0, 120) || email.slice(0, email.indexOf("@"));
1822
+ const agentNativeApp = cleanTrackingParam(input.agentNativeApp);
1823
+ const agentNativeTemplate = cleanTrackingParam(input.agentNativeTemplate);
1824
+ const version = agentNativeApp || agentNativeTemplate
1825
+ ? "agent-native-account-v2"
1826
+ : "agent-native-account-v1";
1822
1827
  const timestamp = String(Date.now());
1823
1828
  const requestId = randomBytes(32).toString("base64url");
1824
1829
  const signaturePayload = [
1825
- "agent-native-account-v1",
1830
+ version,
1826
1831
  timestamp,
1827
1832
  requestId,
1828
1833
  email,
1829
1834
  name,
1835
+ ...(version === "agent-native-account-v2"
1836
+ ? [agentNativeApp ?? "", agentNativeTemplate ?? ""]
1837
+ : []),
1830
1838
  ].join("\n");
1831
1839
  const signature = createHmac("sha256", builderAccountProvisioningSecret())
1832
1840
  .update(signaturePayload)
@@ -1835,12 +1843,17 @@ export async function provisionBuilderAccount(input) {
1835
1843
  method: "POST",
1836
1844
  headers: {
1837
1845
  "Content-Type": "application/json",
1838
- "x-agent-native-account-version": "agent-native-account-v1",
1846
+ "x-agent-native-account-version": version,
1839
1847
  "x-agent-native-account-timestamp": timestamp,
1840
1848
  "x-agent-native-account-request-id": requestId,
1841
1849
  "x-agent-native-account-signature": signature,
1842
1850
  },
1843
- body: JSON.stringify({ email, name }),
1851
+ body: JSON.stringify({
1852
+ email,
1853
+ name,
1854
+ ...(agentNativeApp ? { agentNativeApp } : {}),
1855
+ ...(agentNativeTemplate ? { agentNativeTemplate } : {}),
1856
+ }),
1844
1857
  }, "account provisioning");
1845
1858
  const parsed = await readBuilderApiObject(response, "account provisioning");
1846
1859
  if (!response.ok) {
@@ -1,5 +1,6 @@
1
1
  import type { FailureContext } from "../shared/failure-report.js";
2
2
  export interface CaptureErrorContext {
3
+ errorMessagePolicy?: "omit";
3
4
  route?: string;
4
5
  method?: string;
5
6
  userAgent?: string;
@@ -1,3 +1,4 @@
1
+ import { runErrorTelemetryProperties, runTelemetryException, } from "../agent/engine/error-telemetry.js";
1
2
  import { isTransientDatabaseError } from "../db/client.js";
2
3
  import { withFailureContext } from "../observability/failure-context.js";
3
4
  import { classifyError } from "./error-noise-filter.js";
@@ -106,9 +107,11 @@ function emit(error, context) {
106
107
  return eventId;
107
108
  }
108
109
  function errorLabel(error) {
109
- const { name, message } = (error ?? {});
110
+ const { name, message, errorCode } = (error ?? {});
110
111
  const text = typeof message === "string" ? message : String(error);
111
- return `${typeof name === "string" ? name : "Error"}: ${text}`.slice(0, 120);
112
+ // An omitted run failure's message is fixed text, so its code is what tells it apart.
113
+ const code = typeof errorCode === "string" ? ` [${errorCode}]` : "";
114
+ return `${typeof name === "string" ? name : "Error"}${code}: ${text}`.slice(0, 120);
112
115
  }
113
116
  function recordSuppressed(state, error) {
114
117
  state.suppressed += 1;
@@ -255,6 +258,7 @@ export function captureError(error, callerContext = {}) {
255
258
  }
256
259
  : callerContext;
257
260
  let outgoing = context;
261
+ let reportedError = error;
258
262
  try {
259
263
  const verdict = classifyError(error, { tags: context.tags });
260
264
  if (verdict.drop) {
@@ -266,10 +270,23 @@ export function captureError(error, callerContext = {}) {
266
270
  if (errorCode && context.tags?.errorCode === undefined) {
267
271
  outgoing = { ...context, tags: { ...context.tags, errorCode } };
268
272
  }
273
+ // The failure packet copies the code tag, so it must be sanitized first.
274
+ if (context.errorMessagePolicy === "omit") {
275
+ const properties = runErrorTelemetryProperties(outgoing.tags?.errorCode, error instanceof Error ? error.message : undefined);
276
+ reportedError = runTelemetryException(error, properties.error_code);
277
+ outgoing = {
278
+ ...outgoing,
279
+ tags: {
280
+ ...outgoing.tags,
281
+ errorCode: properties.error_code,
282
+ errorCause: properties.error_cause,
283
+ },
284
+ };
285
+ }
269
286
  const cls = classifyFloodClass(error, outgoing);
270
287
  outgoing = withPacket(outgoing, { errorCode, failureClass: cls });
271
288
  if (cls) {
272
- const admitted = admitFloodEvent(cls, error, outgoing);
289
+ const admitted = admitFloodEvent(cls, reportedError, outgoing);
273
290
  if (!admitted)
274
291
  return undefined;
275
292
  outgoing = admitted;
@@ -278,7 +295,14 @@ export function captureError(error, callerContext = {}) {
278
295
  }
279
296
  catch {
280
297
  outgoing = context;
298
+ if (context.errorMessagePolicy === "omit") {
299
+ reportedError = runTelemetryException(error, "unknown");
300
+ outgoing = {
301
+ ...context,
302
+ tags: { ...context.tags, errorCode: "unknown" },
303
+ };
304
+ }
281
305
  }
282
- return emit(error, outgoing);
306
+ return emit(reportedError, outgoing);
283
307
  }
284
308
  export const captureServerError = captureError;
@@ -32,6 +32,7 @@ function contentText(parts) {
32
32
  function createCompletionAbortSignal(signal, timeoutMs) {
33
33
  const controller = new AbortController();
34
34
  const cleanupFns = [];
35
+ let timeoutError;
35
36
  if (signal) {
36
37
  if (signal.aborted) {
37
38
  controller.abort(signal.reason);
@@ -44,12 +45,14 @@ function createCompletionAbortSignal(signal, timeoutMs) {
44
45
  }
45
46
  if (timeoutMs !== undefined) {
46
47
  const timeout = setTimeout(() => {
47
- controller.abort(new Error(`completeText timed out after ${timeoutMs}ms`));
48
+ timeoutError = new EngineError(`completeText timed out after ${timeoutMs}ms`, { errorCode: "complete_text_timeout" });
49
+ controller.abort(timeoutError);
48
50
  }, timeoutMs);
49
51
  cleanupFns.push(() => clearTimeout(timeout));
50
52
  }
51
53
  return {
52
54
  signal: controller.signal,
55
+ getTimeoutError: () => timeoutError,
53
56
  cleanup: () => {
54
57
  for (const cleanup of cleanupFns)
55
58
  cleanup();
@@ -85,7 +88,8 @@ export async function completeText(options) {
85
88
  (await getStoredModelForEngine(engine, { appId: options.appId })) ??
86
89
  engine.defaultModel;
87
90
  const model = normalizeModelForEngine(engine, modelCandidate);
88
- const { signal, cleanup } = createCompletionAbortSignal(options.signal, options.timeoutMs);
91
+ const completionAbort = createCompletionAbortSignal(options.signal, options.timeoutMs);
92
+ const { signal } = completionAbort;
89
93
  let streamedText = "";
90
94
  let finalContent;
91
95
  let usage;
@@ -131,9 +135,18 @@ export async function completeText(options) {
131
135
  }
132
136
  }
133
137
  }
138
+ catch (error) {
139
+ const timeoutError = completionAbort.getTimeoutError();
140
+ if (timeoutError)
141
+ throw timeoutError;
142
+ throw error;
143
+ }
134
144
  finally {
135
- cleanup();
145
+ completionAbort.cleanup();
136
146
  }
147
+ const timeoutError = completionAbort.getTimeoutError();
148
+ if (timeoutError)
149
+ throw timeoutError;
137
150
  const content = finalContent ?? [{ type: "text", text: streamedText }];
138
151
  return {
139
152
  text: contentText(content) || streamedText,
@@ -1480,6 +1480,8 @@ export async function activateBuilderAccount(event, input) {
1480
1480
  const credentials = await provisionBuilderAccount({
1481
1481
  email: ownerEmail,
1482
1482
  name: session.name,
1483
+ agentNativeApp: input.tracking.agentNativeApp,
1484
+ agentNativeTemplate: input.tracking.agentNativeTemplate,
1483
1485
  });
1484
1486
  const { writeBuilderCredentials } = await import("./credential-provider.js");
1485
1487
  const written = await writeBuilderCredentials(ownerEmail, credentials, activationOrg ?? undefined);
@@ -33,6 +33,7 @@ import { resolveDeployEnvironment } from "./deploy-environment.js";
33
33
  import { DEV_ACTION_DISCOVERY_PATH, readDevActionDiscoveryFile, } from "./dev-action-discovery.js";
34
34
  import { getH3App } from "./framework-request-handler.js";
35
35
  import { getRequestOrgId, getRequestUserEmail, runWithRequestContext, } from "./request-context.js";
36
+ import { getForwardedRequestOrigin } from "./request-origin.js";
36
37
  export const DEV_ACTION_ROUTE = "/_agent-native/dev/action";
37
38
  export const DEV_DB_QUERY_ROUTE = "/_agent-native/dev/db-query";
38
39
  export const DEV_ACTION_TOKEN_HEADER = "x-agent-native-dev-token";
@@ -231,7 +232,12 @@ export function mountDevActionForwardRoute(nitroApp, actions, options) {
231
232
  const userEmail = getHeader(event, DEV_ACTION_USER_HEADER) ||
232
233
  (await resolveDevUserEmail());
233
234
  const orgId = getHeader(event, DEV_ACTION_ORG_HEADER) || undefined;
234
- return runWithRequestContext({ userEmail, orgId }, async () => {
235
+ const context = {
236
+ userEmail,
237
+ orgId,
238
+ requestOrigin: getForwardedRequestOrigin(event),
239
+ };
240
+ return runWithRequestContext(context, async () => {
235
241
  try {
236
242
  const ctx = {
237
243
  userEmail: getRequestUserEmail(),
@@ -4,7 +4,7 @@ import { getAppConfig } from "../app-config/index.js";
4
4
  import { normalizeAnalyticsAnonymousId } from "../shared/analytics-anonymous-id.js";
5
5
  import { normalizeAppPath } from "../shared/sign-in-journey.js";
6
6
  import { getAppBasePathFromViteEnv } from "./app-base-path.js";
7
- import { readAnalyticsAnonymousId, signupAttributionFromCookieHeader, } from "./attribution.js";
7
+ import { readAnalyticsAnonymousId, readAnalyticsSessionId, signupAttributionFromCookieHeader, } from "./attribution.js";
8
8
  import { getBetterAuthUserIdForEmail, hasBetterAuthUserEmail, trackSignupEvent, } from "./better-auth-instance.js";
9
9
  import { getWorkspaceA2ADerivedSecret } from "./derived-secret.js";
10
10
  import { writeDesktopSso } from "./desktop-sso.js";
@@ -575,10 +575,12 @@ export async function createOAuthSession(event, email, opts) {
575
575
  setFirstRunOnboardingCookie(event);
576
576
  }
577
577
  if (shouldTrackSignup && opts.trackSignup) {
578
+ const signupCookieHeader = getHeader(event, "cookie") ?? null;
578
579
  const attribution = opts.trackSignup.attribution ??
579
- signupAttributionFromCookieHeader(getHeader(event, "cookie") ?? null);
580
+ signupAttributionFromCookieHeader(signupCookieHeader);
580
581
  const anonymousId = opts.trackSignup.signupAnonymousId ??
581
- readAnalyticsAnonymousId(getHeader(event, "cookie") ?? null);
582
+ readAnalyticsAnonymousId(signupCookieHeader);
583
+ const sessionId = readAnalyticsSessionId(signupCookieHeader);
582
584
  const authUserId = (await getBetterAuthUserIdForEmail(email)) ??
583
585
  opts.trackSignup.canonicalAuthUserId;
584
586
  await trackSignupEvent({
@@ -590,6 +592,7 @@ export async function createOAuthSession(event, email, opts) {
590
592
  name: opts.trackSignup.name,
591
593
  attribution,
592
594
  anonymousId,
595
+ sessionId,
593
596
  });
594
597
  }
595
598
  // Desktop SSO: record this session in the home-dir broker file so
@@ -79,7 +79,7 @@ export { attachToolSearch, createToolSearchEntry, searchToolRegistry, TOOL_SEARC
79
79
  export { filterFrameworkToolGroups, frameworkGroupEnabled, resolveFrameworkTools, FRAMEWORK_TOOL_GROUPS, type FrameworkToolGroup, type FrameworkToolsConfig, type FrameworkToolsOption, type ResolvedFrameworkTools, } from "../framework-tools.js";
80
80
  export { buildCompactSkillsSummary, registerPromptContextProvider, type PromptContextProvider, type PromptContextProviderContext, type PromptContextProviderContribution, } from "./agent-chat/prompt-resources.js";
81
81
  export { mountActionRoutes, type MountActionRoutesOptions, type ActionRouteAuthAdapter, type ActionRouteResolvedCaller, } from "./action-routes.js";
82
- export { AGENT_RUN_OWNER_CONTEXT_KEY, seedAgentRunOwnerContext, type AgentRunOwnerContext, } from "./agent-run-context.js";
82
+ export { AGENT_RUN_OWNER_CONTEXT_KEY, readBrowserSessionIdHeader, seedAgentRunOwnerContext, type AgentRunOwnerContext, } from "./agent-run-context.js";
83
83
  export { runWithRequestContext, hasRequestContext, hasRequestBoundary, getRequestContext, getRequestUserEmail, getRequestUserName, getRequestOrgId, getAmbientUserEmail, getAmbientOrgId, getRequestTimezone, getRequestRunContext, getCredentialContext, isIntegrationCallerRequest, type RequestContext, type RequestRunContext, } from "./request-context.js";
84
84
  export { formatDateInTimezone, todayInTimezone } from "./date-utils.js";
85
85
  export { createOnboardingPlugin, defaultOnboardingPlugin, } from "../onboarding/plugin.js";
@@ -74,7 +74,7 @@ export { attachToolSearch, createToolSearchEntry, searchToolRegistry, TOOL_SEARC
74
74
  export { filterFrameworkToolGroups, frameworkGroupEnabled, resolveFrameworkTools, FRAMEWORK_TOOL_GROUPS, } from "../framework-tools.js";
75
75
  export { buildCompactSkillsSummary, registerPromptContextProvider, } from "./agent-chat/prompt-resources.js";
76
76
  export { mountActionRoutes, } from "./action-routes.js";
77
- export { AGENT_RUN_OWNER_CONTEXT_KEY, seedAgentRunOwnerContext, } from "./agent-run-context.js";
77
+ export { AGENT_RUN_OWNER_CONTEXT_KEY, readBrowserSessionIdHeader, seedAgentRunOwnerContext, } from "./agent-run-context.js";
78
78
  export { runWithRequestContext, hasRequestContext, hasRequestBoundary, getRequestContext, getRequestUserEmail, getRequestUserName, getRequestOrgId, getAmbientUserEmail, getAmbientOrgId, getRequestTimezone, getRequestRunContext, getCredentialContext, isIntegrationCallerRequest, } from "./request-context.js";
79
79
  export { formatDateInTimezone, todayInTimezone } from "./date-utils.js";
80
80
  export { createOnboardingPlugin, defaultOnboardingPlugin, } from "../onboarding/plugin.js";
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
54
54
  expiresAt?: undefined;
55
55
  ttlSeconds?: undefined;
56
56
  } | {
57
- error?: undefined;
58
57
  token: string;
59
58
  expiresAt: string;
60
59
  ttlSeconds: number;
60
+ error?: undefined;
61
61
  }>>;
@@ -1,5 +1,6 @@
1
1
  import { defineEventHandler, getHeader, getMethod, readRawBody, setResponseHeader, setResponseStatus, } from "h3";
2
2
  import { getMcpOAuthBearerSession, getSession, } from "./auth.js";
3
+ import { respondBearerCredentialRefused } from "./bearer-credential-refusal.js";
3
4
  import { isCredentialMembershipUnavailable, respondCredentialMembershipUnavailable, } from "./credential-membership-unavailable.js";
4
5
  import { getAppUrl } from "./google-oauth.js";
5
6
  import { RECAP_IMAGE_CONTENT_TYPE, RECAP_IMAGE_MAX_BYTES, getRecapImage, isValidRecapImageToken, saveRecapImage, } from "./recap-image-store.js";
@@ -55,6 +56,9 @@ async function handleUpload(event) {
55
56
  return respondCredentialMembershipUnavailable(event);
56
57
  }
57
58
  if (!session?.email) {
59
+ const refused = respondBearerCredentialRefused(event);
60
+ if (refused)
61
+ return refused;
58
62
  setResponseStatus(event, 401);
59
63
  return { error: "Authentication required" };
60
64
  }