@agent-native/core 0.201.1-nightly-20261005213104 → 0.201.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/a2a/handlers.js +96 -44
  2. package/dist/a2a/organization-identity.d.ts +9 -0
  3. package/dist/a2a/organization-identity.js +34 -0
  4. package/dist/a2a/server.d.ts +13 -7
  5. package/dist/a2a/server.js +187 -88
  6. package/dist/a2a/task-store.d.ts +7 -2
  7. package/dist/a2a/task-store.js +23 -8
  8. package/dist/a2a-claims.js +12 -2
  9. package/dist/app-config/mcp.d.ts +4 -0
  10. package/dist/app-config/mcp.js +7 -0
  11. package/dist/app-config/schema.d.ts +3 -0
  12. package/dist/app-config/schema.js +2 -0
  13. package/dist/automation/index.d.ts +1 -1
  14. package/dist/client/route-chunk-recovery.js +75 -11
  15. package/dist/client/route-warmup.d.ts +2 -0
  16. package/dist/client/route-warmup.js +3 -0
  17. package/dist/client/vite-dev-recovery-script.js +85 -17
  18. package/dist/collab/awareness.d.ts +2 -2
  19. package/dist/collab/struct-routes.d.ts +1 -1
  20. package/dist/deploy/build.js +3 -1
  21. package/dist/feature-flags/a2a-action-route.js +3 -4
  22. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  23. package/dist/mcp/build-server.d.ts +4 -0
  24. package/dist/mcp/build-server.js +133 -39
  25. package/dist/mcp/server.js +21 -8
  26. package/dist/notifications/routes.d.ts +3 -3
  27. package/dist/observability/routes.d.ts +9 -9
  28. package/dist/onboarding/app-profile.js +2 -1
  29. package/dist/onboarding/workspace-services.js +3 -0
  30. package/dist/org/context.d.ts +13 -0
  31. package/dist/org/context.js +60 -0
  32. package/dist/org/index.d.ts +1 -1
  33. package/dist/org/membership.d.ts +6 -0
  34. package/dist/org/membership.js +23 -5
  35. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  36. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  37. package/dist/resources/handlers.d.ts +1 -1
  38. package/dist/secrets/routes.d.ts +11 -11
  39. package/dist/server/action-routes.js +12 -5
  40. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  41. package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
  42. package/dist/server/builder-browser.d.ts +4 -2
  43. package/dist/server/builder-browser.js +147 -9
  44. package/dist/server/core-routes-plugin.js +13 -6
  45. package/dist/server/entry-server.js +43 -2
  46. package/dist/server/sentry.js +9 -1
  47. package/dist/server/ssr-handler.d.ts +1 -1
  48. package/dist/server/ssr-handler.js +3 -7
  49. package/dist/shared/cache-control.d.ts +4 -1
  50. package/dist/shared/cache-control.js +11 -6
  51. package/dist/shared/route-chunk-recovery-bootstrap.d.ts +7 -0
  52. package/dist/shared/route-chunk-recovery-bootstrap.js +38 -0
  53. package/dist/triggers/actions/manage-automation.d.ts +5 -5
  54. package/package.json +6 -4
@@ -311,10 +311,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
- provider?: undefined;
314
+ id?: undefined;
315
315
  deleted?: undefined;
316
316
  message?: undefined;
317
- id?: undefined;
318
317
  providers: {
319
318
  id: string;
320
319
  label: string;
@@ -325,44 +324,45 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
325
324
  updatedAt: number;
326
325
  }[];
327
326
  count: number;
327
+ provider?: undefined;
328
328
  found?: undefined;
329
329
  registered?: undefined;
330
330
  label?: undefined;
331
331
  } | {
332
+ id?: undefined;
332
333
  count?: undefined;
333
334
  deleted?: undefined;
334
335
  message?: undefined;
335
- id?: undefined;
336
336
  providers?: undefined;
337
337
  found: boolean;
338
338
  provider: import("../custom-registry.js").CustomProviderConfig;
339
339
  registered?: undefined;
340
340
  label?: undefined;
341
341
  } | {
342
- provider?: undefined;
343
342
  count?: undefined;
344
343
  deleted?: undefined;
345
344
  message?: undefined;
346
345
  providers?: undefined;
346
+ provider?: undefined;
347
347
  found: boolean;
348
348
  id: string;
349
349
  registered?: undefined;
350
350
  label?: undefined;
351
351
  } | {
352
- provider?: undefined;
353
352
  count?: undefined;
354
353
  message?: undefined;
355
354
  providers?: undefined;
355
+ provider?: undefined;
356
356
  found?: undefined;
357
357
  deleted: boolean;
358
358
  id: string;
359
359
  registered?: undefined;
360
360
  label?: undefined;
361
361
  } | {
362
- provider?: undefined;
363
362
  count?: undefined;
364
363
  deleted?: undefined;
365
364
  providers?: undefined;
365
+ provider?: undefined;
366
366
  found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
@@ -39,8 +39,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
39
39
  error: string;
40
40
  }>;
41
41
  export declare function handleDeleteResource(event: any): Promise<{
42
- ok?: undefined;
43
42
  error: string;
43
+ ok?: undefined;
44
44
  } | {
45
45
  error?: undefined;
46
46
  ok: boolean;
@@ -58,47 +58,47 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
58
58
  error: string;
59
59
  errorCode?: undefined;
60
60
  retryable?: undefined;
61
- ok?: undefined;
62
61
  status?: undefined;
62
+ ok?: undefined;
63
63
  } | {
64
64
  error: string;
65
65
  errorCode: string;
66
66
  retryable?: undefined;
67
- ok?: undefined;
68
67
  status?: undefined;
68
+ ok?: undefined;
69
69
  } | {
70
70
  error: string;
71
71
  errorCode: string;
72
72
  retryable: boolean;
73
- ok?: undefined;
74
73
  status?: undefined;
74
+ ok?: undefined;
75
75
  } | {
76
- error?: undefined;
77
76
  errorCode?: undefined;
78
77
  retryable?: undefined;
79
78
  ok: boolean;
80
79
  status: string;
80
+ error?: undefined;
81
81
  } | {
82
- ok?: undefined;
83
82
  error: string;
84
83
  removed?: undefined;
84
+ ok?: undefined;
85
85
  } | {
86
- error?: undefined;
87
86
  ok: boolean;
88
87
  removed: boolean;
88
+ error?: undefined;
89
89
  }>>;
90
90
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
91
- ok?: undefined;
92
91
  error: string;
93
92
  note?: undefined;
93
+ ok?: undefined;
94
94
  } | {
95
- error?: undefined;
96
95
  ok: boolean;
97
96
  note?: undefined;
98
- } | {
99
97
  error?: undefined;
98
+ } | {
100
99
  ok: boolean;
101
100
  note: string;
101
+ error?: undefined;
102
102
  } | {
103
103
  note?: undefined;
104
104
  ok: boolean;
@@ -126,11 +126,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
126
126
  } | {
127
127
  error: string;
128
128
  } | {
129
- error?: undefined;
130
129
  ok: boolean;
131
130
  key: string;
132
- } | {
133
131
  error?: undefined;
132
+ } | {
134
133
  ok: boolean;
135
134
  removed: boolean;
135
+ error?: undefined;
136
136
  }>>;
@@ -1,13 +1,14 @@
1
1
  import { createError, defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getQuery, getHeader, readBody as readH3Body, } from "h3";
2
2
  import "../authorization/check-action.js";
3
3
  import { verifyA2ATokenWithClaims } from "../a2a-claims.js";
4
+ import { isA2AIdentityVerificationUnavailableError } from "../a2a/server.js";
4
5
  import { actionCallEmitsChange, actionChangeResource, } from "../action-call-classification.js";
5
6
  import { ActionContractError, isActionContractError, isActionExposedToExternalAgents, isAgentActionStopError, validateActionArgs, } from "../action.js";
6
7
  import { LLM_CREDENTIAL_KEYS, LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "../agent/engine/credential-errors.js";
7
8
  import { isTransientDatabaseError } from "../db/client.js";
8
9
  import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
9
10
  import { isFeatureFlagAdminEmail } from "../feature-flags/permissions.js";
10
- import { isFederationMembershipValidatedForEvent, resolveOrgByDomain, resolveOrgIdForEmail, } from "../org/context.js";
11
+ import { isFederationMembershipValidatedForEvent, resolveOrgIdForEmail, } from "../org/context.js";
11
12
  import { LLM_PROVIDER_MISSING_ERROR_CODE, LLM_PROVIDER_MISSING_STATUS, } from "../shared/action-error-codes.js";
12
13
  import { agentNativeMcpInstructions, agentNativeToolTitle, } from "../shared/agent-mcp-metadata.js";
13
14
  import { EMBED_TARGET_HEADER, EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, } from "../shared/embed-auth.js";
@@ -66,8 +67,8 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
66
67
  const claims = await verifyA2ATokenWithClaims(token, event);
67
68
  if (!claims || !claims.scope.includes(required))
68
69
  throw new Error("Invalid feature flag delegation");
69
- const localOrg = await resolveOrgByDomain(claims.orgDomain);
70
- if (!localOrg && !isFeatureFlagAdminEmail(claims.email))
70
+ const orgId = claims.orgId?.trim() || null;
71
+ if (!orgId && !isFeatureFlagAdminEmail(claims.email))
71
72
  throw new Error("Invalid feature flag delegation");
72
73
  if (actionName === "set-feature-flag" &&
73
74
  (await consumeOneTimeJti(claims.jti))) {
@@ -75,7 +76,7 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
75
76
  }
76
77
  return {
77
78
  owner: claims.email,
78
- orgId: localOrg?.orgId ?? null,
79
+ orgId,
79
80
  anonymous: false,
80
81
  delegationJti: claims.jti,
81
82
  delegationIssuer: claims.issuer,
@@ -386,7 +387,13 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
386
387
  if (!caller)
387
388
  caller = await resolveFeatureFlagA2ACaller(event, name);
388
389
  }
389
- catch {
390
+ catch (error) {
391
+ if (isA2AIdentityVerificationUnavailableError(error)) {
392
+ throw createError({
393
+ statusCode: 503,
394
+ statusMessage: "Identity verification temporarily unavailable",
395
+ });
396
+ }
390
397
  throw createError({
391
398
  statusCode: 401,
392
399
  statusMessage: "Unauthorized",
@@ -16,9 +16,9 @@ export declare function createAgentEngineDisconnectHandler(): import("h3").Event
16
16
  error: any;
17
17
  scope?: undefined;
18
18
  } | {
19
- error?: undefined;
20
19
  ok: boolean;
21
20
  scope: "org" | "user";
21
+ error?: undefined;
22
22
  }>>;
23
23
  export interface DefaultModelSelectionRequest {
24
24
  engine: string;
@@ -1,9 +1,9 @@
1
1
  export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
2
  error: string;
3
- ok?: undefined;
4
3
  models?: undefined;
4
+ ok?: undefined;
5
5
  } | {
6
- error?: undefined;
7
6
  ok: boolean;
8
7
  models: string[];
8
+ error?: undefined;
9
9
  }>>;
@@ -43,8 +43,8 @@ export declare class BuilderAccountProvisioningError extends Error {
43
43
  export declare function isBuilderAccountAlreadyExistsError(error: unknown): boolean;
44
44
  export declare function createBuilderConnectState(): string;
45
45
  export declare function isSignedBuilderConnectState(value: string | null | undefined): boolean;
46
- export declare function isBuilderConnectCallbackUrlAllowed(candidate: string, event: H3Event): boolean;
47
- export declare function resolveBuilderConnectCallbackUrl(event: H3Event, state?: string): string | null;
46
+ export declare function isBuilderConnectCallbackUrlAllowed(candidate: string, event: H3Event, callbackOriginOverride?: string): boolean;
47
+ export declare function resolveBuilderConnectCallbackUrl(event: H3Event, state?: string, callbackOriginOverride?: string): string | null;
48
48
  export declare function isSafeBuilderRelayTargetOrigin(value: string): boolean;
49
49
  export declare function isTrustedBuilderRelayTargetOrigin(value: string): boolean;
50
50
  export declare function resolveBuilderPreviewRelayTargetOrigin(previewOrigin: string): string;
@@ -94,6 +94,7 @@ export declare const BUILDER_AGENT_NATIVE_CONNECT_SOURCE_PARAM = "agentNativeCon
94
94
  export declare const BUILDER_AGENT_NATIVE_APP_PARAM = "agentNativeApp";
95
95
  export declare const BUILDER_AGENT_NATIVE_TEMPLATE_PARAM = "agentNativeTemplate";
96
96
  export declare const BUILDER_CONNECT_MODE_PARAM = "_an_mode";
97
+ export declare const BUILDER_PREVIEW_ORIGIN_HEADER = "x-agent-native-preview-origin";
97
98
  export declare const BUILDER_AGENT_NATIVE_PROVISION_MODE = "agent-native";
98
99
  export declare const BUILDER_PROVISIONING_TOKEN_PARAM = "_an_provision";
99
100
  export declare const BUILDER_CONNECT_ATTEMPT_PARAM = "_an_connect_attempt";
@@ -207,6 +208,7 @@ export declare function buildBuilderCliAuthUrl(callbackOrigin: string, state?: s
207
208
  }): string;
208
209
  export declare function getBuilderBrowserConnectUrl(origin: string): string;
209
210
  export declare function getBuilderBrowserConnectUrlForOwner(origin: string, ownerEmail: string | null | undefined): string;
211
+ export declare function getBuilderConnectCallbackOriginFromUrl(callbackUrl: string): string | null;
210
212
  export declare function getBuilderBrowserOriginForEvent(event: H3Event): string;
211
213
  export declare function getBuilderCliAuthCallbackOriginForEvent(event: H3Event): string;
212
214
  export declare function getBuilderBrowserStatus(origin: string): BuilderBrowserStatus;
@@ -1,5 +1,5 @@
1
1
  import { createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
2
- import { getHeader, getRequestIP, setResponseHeader, setResponseStatus, } from "h3";
2
+ import { getHeader, getQuery, getRequestIP, setResponseHeader, setResponseStatus, } from "h3";
3
3
  import { ActionContractError } from "../action.js";
4
4
  import { getSetting } from "../settings/store.js";
5
5
  import { applyBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
@@ -112,8 +112,8 @@ export function isSignedBuilderConnectState(value) {
112
112
  return safeEqualText(expected, signature);
113
113
  });
114
114
  }
115
- export function isBuilderConnectCallbackUrlAllowed(candidate, event) {
116
- const callbackOrigin = getBuilderConnectCallbackOrigin(event);
115
+ export function isBuilderConnectCallbackUrlAllowed(candidate, event, callbackOriginOverride) {
116
+ const callbackOrigin = getBuilderConnectCallbackOrigin(event, callbackOriginOverride);
117
117
  if (!callbackOrigin ||
118
118
  !isAllowedOAuthRedirectUri(candidate, event, callbackOrigin))
119
119
  return false;
@@ -137,16 +137,18 @@ export function isBuilderConnectCallbackUrlAllowed(candidate, event) {
137
137
  }
138
138
  return url.protocol === "https:";
139
139
  }
140
- export function resolveBuilderConnectCallbackUrl(event, state) {
140
+ export function resolveBuilderConnectCallbackUrl(event, state, callbackOriginOverride) {
141
141
  const stateSuffix = state ? `?state=${encodeURIComponent(state)}` : "";
142
- const callbackOrigin = getBuilderConnectCallbackOrigin(event);
142
+ const callbackOrigin = getBuilderConnectCallbackOrigin(event, callbackOriginOverride);
143
143
  if (!callbackOrigin)
144
144
  return null;
145
145
  const withBase = `${callbackOrigin}${getAppBasePath()}${BUILDER_CALLBACK_PATH}${stateSuffix}`;
146
- if (isBuilderConnectCallbackUrlAllowed(withBase, event))
146
+ if (isBuilderConnectCallbackUrlAllowed(withBase, event, callbackOriginOverride)) {
147
147
  return withBase;
148
+ }
148
149
  const root = `${callbackOrigin}${BUILDER_CALLBACK_PATH}${stateSuffix}`;
149
- if (root !== withBase && isBuilderConnectCallbackUrlAllowed(root, event)) {
150
+ if (root !== withBase &&
151
+ isBuilderConnectCallbackUrlAllowed(root, event, callbackOriginOverride)) {
150
152
  return root;
151
153
  }
152
154
  return null;
@@ -429,6 +431,9 @@ export const BUILDER_AGENT_NATIVE_CONNECT_SOURCE_PARAM = "agentNativeConnectSour
429
431
  export const BUILDER_AGENT_NATIVE_APP_PARAM = "agentNativeApp";
430
432
  export const BUILDER_AGENT_NATIVE_TEMPLATE_PARAM = "agentNativeTemplate";
431
433
  export const BUILDER_CONNECT_MODE_PARAM = "_an_mode";
434
+ export const BUILDER_PREVIEW_ORIGIN_HEADER = "x-agent-native-preview-origin";
435
+ const BUILDER_PREVIEW_ORIGIN_PARAM = "_an_preview_origin";
436
+ const BUILDER_PREVIEW_ORIGIN_SIGNATURE_PARAM = "_an_preview_origin_signature";
432
437
  export const BUILDER_AGENT_NATIVE_PROVISION_MODE = "agent-native";
433
438
  export const BUILDER_PROVISIONING_TOKEN_PARAM = "_an_provision";
434
439
  export const BUILDER_CONNECT_ATTEMPT_PARAM = "_an_connect_attempt";
@@ -958,7 +963,15 @@ function isConfiguredBuilderRequestHost(event, host) {
958
963
  (process.env.NODE_ENV === "production" ? "https" : "http");
959
964
  return isConfiguredAppOrigin(`${proto}://${host}`);
960
965
  }
961
- function getBuilderConnectCallbackOrigin(event) {
966
+ function getBuilderConnectCallbackOrigin(event, originOverride) {
967
+ if (originOverride) {
968
+ return isAllowedBuilderConnectOrigin(originOverride)
969
+ ? originOverride
970
+ : null;
971
+ }
972
+ const signedPreviewOrigin = getSignedBuilderPreviewOriginFromEvent(event);
973
+ if (signedPreviewOrigin)
974
+ return signedPreviewOrigin;
962
975
  const requestHost = firstHeaderValue(readEventHeader(event, "host"));
963
976
  const headerHost = getBuilderRequestHost(event);
964
977
  if (isRejectedDirectBuilderCloudHost(requestHost, headerHost)) {
@@ -978,6 +991,123 @@ function getBuilderConnectCallbackOrigin(event) {
978
991
  }
979
992
  return configuredOrigin;
980
993
  }
994
+ function isAllowedBuilderConnectOrigin(origin) {
995
+ let url;
996
+ try {
997
+ url = new URL(origin);
998
+ }
999
+ catch {
1000
+ // coercion-ok: a malformed callback origin is explicitly rejected.
1001
+ return false;
1002
+ }
1003
+ if (url.origin !== origin ||
1004
+ url.username ||
1005
+ url.password ||
1006
+ url.pathname !== "/" ||
1007
+ url.search ||
1008
+ url.hash) {
1009
+ return false;
1010
+ }
1011
+ if (isTrustedBuilderRequestHost(url.host)) {
1012
+ return (url.protocol === "https:" ||
1013
+ (url.protocol === "http:" && process.env.NODE_ENV !== "production"));
1014
+ }
1015
+ return isConfiguredAppOrigin(origin);
1016
+ }
1017
+ function normalizeBuilderPreviewOrigin(value) {
1018
+ try {
1019
+ const url = new URL(value);
1020
+ if (url.origin !== value ||
1021
+ url.username ||
1022
+ url.password ||
1023
+ url.pathname !== "/" ||
1024
+ url.search ||
1025
+ url.hash ||
1026
+ (url.protocol !== "https:" &&
1027
+ !(url.protocol === "http:" && process.env.NODE_ENV !== "production"))) {
1028
+ return null;
1029
+ }
1030
+ return url.origin;
1031
+ }
1032
+ catch {
1033
+ // coercion-ok: a malformed preview origin is not an allowed origin.
1034
+ return null;
1035
+ }
1036
+ }
1037
+ function getSameOriginBuilderPreviewOrigin(event) {
1038
+ const rawOrigin = firstHeaderValue(readEventHeader(event, BUILDER_PREVIEW_ORIGIN_HEADER));
1039
+ if (!rawOrigin || readEventHeader(event, "sec-fetch-site") === "cross-site") {
1040
+ return null;
1041
+ }
1042
+ const origin = normalizeBuilderPreviewOrigin(rawOrigin);
1043
+ if (!origin || !isTrustedBuilderRequestHost(new URL(origin).host)) {
1044
+ return null;
1045
+ }
1046
+ const requestHost = firstHeaderValue(readEventHeader(event, "host"));
1047
+ if (!requestHost ||
1048
+ (!isLoopbackBuilderRequestHost(requestHost) &&
1049
+ requestHost.toLowerCase() !== new URL(origin).host.toLowerCase())) {
1050
+ return null;
1051
+ }
1052
+ const fetchSite = readEventHeader(event, "sec-fetch-site");
1053
+ const requestOrigin = firstHeaderValue(readEventHeader(event, "origin"));
1054
+ if (fetchSite === "same-origin" || requestOrigin === origin)
1055
+ return origin;
1056
+ const referer = firstHeaderValue(readEventHeader(event, "referer"));
1057
+ if (!referer)
1058
+ return null;
1059
+ try {
1060
+ return new URL(referer).origin === origin ? origin : null;
1061
+ }
1062
+ catch {
1063
+ // coercion-ok: an unreadable Referer cannot authorize a preview origin.
1064
+ return null;
1065
+ }
1066
+ }
1067
+ function signBuilderPreviewOrigin(origin) {
1068
+ return createHmac("sha256", builderConnectStateSigningKeys()[0])
1069
+ .update(`builder-connect-preview-origin\0${origin}`)
1070
+ .digest("base64url");
1071
+ }
1072
+ function getSignedBuilderPreviewOriginFromEvent(event) {
1073
+ const query = getQuery(event);
1074
+ const rawOrigin = query[BUILDER_PREVIEW_ORIGIN_PARAM];
1075
+ const signature = query[BUILDER_PREVIEW_ORIGIN_SIGNATURE_PARAM];
1076
+ if (typeof rawOrigin !== "string" || typeof signature !== "string") {
1077
+ return null;
1078
+ }
1079
+ const origin = normalizeBuilderPreviewOrigin(rawOrigin);
1080
+ if (!origin || !isTrustedBuilderRequestHost(new URL(origin).host)) {
1081
+ return null;
1082
+ }
1083
+ const requestHost = firstHeaderValue(readEventHeader(event, "host"));
1084
+ if (!requestHost ||
1085
+ (!isLoopbackBuilderRequestHost(requestHost) &&
1086
+ requestHost.toLowerCase() !== new URL(origin).host.toLowerCase())) {
1087
+ return null;
1088
+ }
1089
+ return builderConnectStateSigningKeys().some((key) => safeEqualText(createHmac("sha256", key)
1090
+ .update(`builder-connect-preview-origin\0${origin}`)
1091
+ .digest("base64url"), signature))
1092
+ ? origin
1093
+ : null;
1094
+ }
1095
+ function addSignedBuilderPreviewOrigin(url, origin) {
1096
+ const parsed = new URL(url);
1097
+ parsed.searchParams.set(BUILDER_PREVIEW_ORIGIN_PARAM, origin);
1098
+ parsed.searchParams.set(BUILDER_PREVIEW_ORIGIN_SIGNATURE_PARAM, signBuilderPreviewOrigin(origin));
1099
+ return parsed.toString();
1100
+ }
1101
+ export function getBuilderConnectCallbackOriginFromUrl(callbackUrl) {
1102
+ try {
1103
+ const origin = new URL(callbackUrl).origin;
1104
+ return isAllowedBuilderConnectOrigin(origin) ? origin : null;
1105
+ }
1106
+ catch {
1107
+ // coercion-ok: a malformed stored callback URL must fail verification.
1108
+ return null;
1109
+ }
1110
+ }
981
1111
  function isRejectedDirectBuilderCloudHost(requestHost, resolvedHost) {
982
1112
  return isBuilderCloudRequestHost(requestHost) && requestHost !== resolvedHost;
983
1113
  }
@@ -986,6 +1116,9 @@ function getConfiguredBuilderFallbackOrigin(event) {
986
1116
  return isConfiguredAppOrigin(origin) ? origin : null;
987
1117
  }
988
1118
  export function getBuilderBrowserOriginForEvent(event) {
1119
+ const signedPreviewOrigin = getSignedBuilderPreviewOriginFromEvent(event);
1120
+ if (signedPreviewOrigin)
1121
+ return signedPreviewOrigin;
989
1122
  const requestHost = firstHeaderValue(readEventHeader(event, "host"));
990
1123
  const headerHost = getBuilderRequestHost(event);
991
1124
  if (isRejectedDirectBuilderCloudHost(requestHost, headerHost)) {
@@ -1057,7 +1190,12 @@ function parseOptionalEnvBoolean(value) {
1057
1190
  return /^(1|true)$/i.test(value);
1058
1191
  }
1059
1192
  export function getBuilderBrowserStatusForEvent(event) {
1060
- return getBuilderBrowserStatus(getBuilderBrowserOriginForEvent(event));
1193
+ const previewOrigin = getSameOriginBuilderPreviewOrigin(event);
1194
+ const status = getBuilderBrowserStatus(previewOrigin ?? getBuilderBrowserOriginForEvent(event));
1195
+ if (previewOrigin && status.connectUrl) {
1196
+ status.connectUrl = addSignedBuilderPreviewOrigin(status.connectUrl, previewOrigin);
1197
+ }
1198
+ return status;
1061
1199
  }
1062
1200
  export const BUILDER_ENV_KEYS = [
1063
1201
  "BUILDER_PRIVATE_KEY",
@@ -59,7 +59,7 @@ import { getSession } from "./auth.js";
59
59
  import { createAutomationFailureUnsubscribeHandler } from "./automation-failure-notifications.js";
60
60
  import { getBetterAuthInternalAdapter, getBetterAuthSync, } from "./better-auth-instance.js";
61
61
  import { resolveBuilderRequestAuthorization } from "./builder-api-auth.js";
62
- import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_MODE_PARAM, BUILDER_AGENT_NATIVE_PROVISION_MODE, BUILDER_CONNECT_ATTEMPT_PARAM, BUILDER_CONNECT_STATE_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_RELAY_FLOW_HEADER, BUILDER_RELAY_SIGNATURE_HEADER, BUILDER_RELAY_STATE_PARAM, BUILDER_RELAY_TIMESTAMP_HEADER, appendBuilderConnectToken, appendBuilderConnectStateCookie, builderConnectTrackingProperties, BUILDER_UPSTREAM_FAILURE_STATUS, createBuilderConnectState, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, createBuilderRelayRequest, getBuilderConnectTrackingParams, getBuilderBrowserOriginForEvent, getBuilderBrowserStatusForEvent, isBuilderAccountAlreadyExistsError, isBuilderAccountProvisioningEnabled, isBuilderConnectCallbackUrlAllowed, isSignedBuilderConnectState, normalizeBuilderAgentContext, parseBuilderConnectStateCookie, provisionBuilderAccount, resolveBuilderBranchProjectId, resolveBuilderConnectCallbackUrl, resolveBuilderConnectCallbackState, resolveBuilderPreviewRelayParentOrigin, removeBuilderConnectStateCookie, runBuilderAgent, sendBuilderPopupErrorPage, verifyBuilderRelayRequest, verifyBuilderPreviewRelayStateForCallback, verifyBuilderConnectTokenAndGetOwner, signBuilderProvisioningToken, verifyBuilderProvisioningToken, withBuilderConnectTrackingParams, } from "./builder-browser.js";
62
+ import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_MODE_PARAM, BUILDER_AGENT_NATIVE_PROVISION_MODE, BUILDER_CONNECT_ATTEMPT_PARAM, BUILDER_CONNECT_STATE_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_RELAY_FLOW_HEADER, BUILDER_RELAY_SIGNATURE_HEADER, BUILDER_RELAY_STATE_PARAM, BUILDER_RELAY_TIMESTAMP_HEADER, appendBuilderConnectToken, appendBuilderConnectStateCookie, builderConnectTrackingProperties, BUILDER_UPSTREAM_FAILURE_STATUS, createBuilderConnectState, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, createBuilderRelayRequest, getBuilderConnectCallbackOriginFromUrl, getBuilderConnectTrackingParams, getBuilderBrowserOriginForEvent, getBuilderBrowserStatusForEvent, isBuilderAccountAlreadyExistsError, isBuilderAccountProvisioningEnabled, isBuilderConnectCallbackUrlAllowed, isSignedBuilderConnectState, normalizeBuilderAgentContext, parseBuilderConnectStateCookie, provisionBuilderAccount, resolveBuilderBranchProjectId, resolveBuilderConnectCallbackUrl, resolveBuilderConnectCallbackState, resolveBuilderPreviewRelayParentOrigin, removeBuilderConnectStateCookie, runBuilderAgent, sendBuilderPopupErrorPage, verifyBuilderRelayRequest, verifyBuilderPreviewRelayStateForCallback, verifyBuilderConnectTokenAndGetOwner, signBuilderProvisioningToken, verifyBuilderProvisioningToken, withBuilderConnectTrackingParams, } from "./builder-browser.js";
63
63
  import { BUILDER_ASSETS_WRITE_SCOPE, BUILDER_OAUTH_SCOPE, builderOAuthScopeFor, canRoleConnectPersonalBuilder, deleteBuilderOAuthSession, exchangeBuilderOAuthAuthorization, getBuilderOAuthGrants, getBuilderOAuthStoredScope, hasStoredBuilderOAuthGrant, isBuilderOrgManagerRole, isPersonalBuilderGrantAllowed, saveBuilderOAuthCredentials, startBuilderOAuthAuthorization, } from "./builder-oauth.js";
64
64
  import { captureError, registerErrorCaptureProvider } from "./capture-error.js";
65
65
  import { createChatGPTSubscriptionOAuthCallbackHandler, createChatGPTSubscriptionOAuthStartHandler, } from "./chatgpt-subscription-oauth.js";
@@ -3472,7 +3472,8 @@ export function createCoreRoutesPlugin(options = {}) {
3472
3472
  ? await selectLiveBuilderConnectStates(cookieStates)
3473
3473
  : cookieStates;
3474
3474
  const { state, resetStateCookie } = resolveBuilderConnectCallbackState(queryState, liveStates ? liveStates.join(",") : rawStateCookie);
3475
- const parentOrigin = getBuilderBrowserOriginForEvent(event);
3475
+ let parentOrigin = getBuilderBrowserOriginForEvent(event);
3476
+ let callbackOriginOverride;
3476
3477
  let callbackAttemptId = requestConnectAttemptId;
3477
3478
  // A finished attempt — succeeded or failed — must not leave its
3478
3479
  // state in the cookie, or the next restart resolves against two
@@ -3492,8 +3493,7 @@ export function createCoreRoutesPlugin(options = {}) {
3492
3493
  }
3493
3494
  setCookie(event, BUILDER_CONNECT_STATE_COOKIE, remaining, {
3494
3495
  httpOnly: true,
3495
- secure: (resolveBuilderConnectCallbackUrl(event, finishedState) ??
3496
- parentOrigin).startsWith("https://"),
3496
+ secure: (resolveBuilderConnectCallbackUrl(event, finishedState, callbackOriginOverride) ?? parentOrigin).startsWith("https://"),
3497
3497
  sameSite: "lax",
3498
3498
  path: "/",
3499
3499
  maxAge: Math.ceil(BUILDER_CONNECT_PENDING_TTL_MS / 1_000),
@@ -3553,7 +3553,14 @@ export function createCoreRoutesPlugin(options = {}) {
3553
3553
  const redirectUri = typeof pending.redirectUri === "string"
3554
3554
  ? pending.redirectUri
3555
3555
  : null;
3556
- const expectedRedirectUri = resolveBuilderConnectCallbackUrl(event, state);
3556
+ const storedCallbackOrigin = redirectUri
3557
+ ? getBuilderConnectCallbackOriginFromUrl(redirectUri)
3558
+ : null;
3559
+ if (storedCallbackOrigin) {
3560
+ callbackOriginOverride = storedCallbackOrigin;
3561
+ parentOrigin = storedCallbackOrigin;
3562
+ }
3563
+ const expectedRedirectUri = resolveBuilderConnectCallbackUrl(event, state, callbackOriginOverride);
3557
3564
  if (!ownerEmail ||
3558
3565
  !isBuilderConnectCallbackOwner(ownerEmail, session?.email) ||
3559
3566
  Date.now() >= expiresAt ||
@@ -3561,7 +3568,7 @@ export function createCoreRoutesPlugin(options = {}) {
3561
3568
  !redirectUri ||
3562
3569
  !expectedRedirectUri ||
3563
3570
  redirectUri !== expectedRedirectUri ||
3564
- !isBuilderConnectCallbackUrlAllowed(redirectUri, event)) {
3571
+ !isBuilderConnectCallbackUrlAllowed(redirectUri, event, callbackOriginOverride)) {
3565
3572
  if (Date.now() >= expiresAt) {
3566
3573
  await deleteSetting(`builder-connect-pending:${state}`).catch(() => false); // coercion-ok: expired pending cleanup is best-effort; callback already failed verification
3567
3574
  }
@@ -2,8 +2,49 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import ReactDOMServer from "react-dom/server.browser";
3
3
  const { renderToReadableStream } = ReactDOMServer;
4
4
  import { isbot } from "isbot";
5
+ import { ROUTE_CHUNK_RECOVERY_BOOTSTRAP_SCRIPT } from "../shared/route-chunk-recovery-bootstrap.js";
5
6
  import { wrapWithAnalytics } from "./analytics.js";
6
7
  export const streamTimeout = 5_000;
8
+ const HEAD_OPEN_PATTERN = /<head\b[^>]*>/i;
9
+ const CHUNK_RECOVERY_BOOTSTRAP_TAG = `<script data-agent-native-chunk-recovery-bootstrap>${ROUTE_CHUNK_RECOVERY_BOOTSTRAP_SCRIPT}</script>`;
10
+ function installEarlyChunkRecoveryBootstrap(body) {
11
+ const decoder = new TextDecoder();
12
+ const encoder = new TextEncoder();
13
+ let pending = "";
14
+ let injected = false;
15
+ return body.pipeThrough(new TransformStream({
16
+ transform(chunk, controller) {
17
+ pending += decoder.decode(chunk, { stream: true });
18
+ if (!injected) {
19
+ const headOpenMatch = HEAD_OPEN_PATTERN.exec(pending);
20
+ if (!headOpenMatch || headOpenMatch.index === undefined)
21
+ return;
22
+ const headEnd = headOpenMatch.index + headOpenMatch[0].length;
23
+ controller.enqueue(encoder.encode(pending.slice(0, headEnd) + CHUNK_RECOVERY_BOOTSTRAP_TAG));
24
+ pending = pending.slice(headEnd);
25
+ injected = true;
26
+ }
27
+ if (pending) {
28
+ controller.enqueue(encoder.encode(pending));
29
+ pending = "";
30
+ }
31
+ },
32
+ flush(controller) {
33
+ pending += decoder.decode();
34
+ if (!injected) {
35
+ const headOpenMatch = HEAD_OPEN_PATTERN.exec(pending);
36
+ if (headOpenMatch && headOpenMatch.index !== undefined) {
37
+ const headEnd = headOpenMatch.index + headOpenMatch[0].length;
38
+ controller.enqueue(encoder.encode(pending.slice(0, headEnd) + CHUNK_RECOVERY_BOOTSTRAP_TAG));
39
+ pending = pending.slice(headEnd);
40
+ injected = true;
41
+ }
42
+ }
43
+ if (pending)
44
+ controller.enqueue(encoder.encode(pending));
45
+ },
46
+ }));
47
+ }
7
48
  export function createDocumentRequestHandler(ServerRouter) {
8
49
  return async function handleDocumentRequest(request, responseStatusCode, responseHeaders, routerContext, _loadContext) {
9
50
  if (request.method.toUpperCase() === "HEAD") {
@@ -33,8 +74,8 @@ export function createDocumentRequestHandler(ServerRouter) {
33
74
  if (waitForAll) {
34
75
  await body.allReady;
35
76
  }
36
- responseHeaders.set("Content-Type", "text/html");
37
- return new Response(wrapWithAnalytics(body), {
77
+ responseHeaders.set("Content-Type", "text/html; charset=utf-8");
78
+ return new Response(wrapWithAnalytics(installEarlyChunkRecoveryBootstrap(body)), {
38
79
  headers: responseHeaders,
39
80
  status: responseStatusCode,
40
81
  });
@@ -258,8 +258,10 @@ export function setSentryRequestContext(ctx) {
258
258
  export function captureAuthError(error, context) {
259
259
  if (getRequestContext()?.isSyntheticTraffic)
260
260
  return undefined;
261
- if (!_initSucceeded || !Sentry)
261
+ if (!_initSucceeded || !Sentry) {
262
+ logSignupErrorWhenSentryUnavailable(context.route);
262
263
  return undefined;
264
+ }
263
265
  const sentry = Sentry;
264
266
  try {
265
267
  return sentry.withScope((scope) => {
@@ -274,9 +276,15 @@ export function captureAuthError(error, context) {
274
276
  });
275
277
  }
276
278
  catch {
279
+ logSignupErrorWhenSentryUnavailable(context.route);
277
280
  return undefined;
278
281
  }
279
282
  }
283
+ function logSignupErrorWhenSentryUnavailable(route) {
284
+ if (route !== "signup")
285
+ return;
286
+ console.error("[agent-native][auth] signup error (Sentry unavailable)");
287
+ }
280
288
  export function captureRouteError(error, context = {}) {
281
289
  if (getRequestContext()?.isSyntheticTraffic)
282
290
  return undefined;
@@ -1,2 +1,2 @@
1
- export { DEFAULT_SSR_CACHE_HEADERS, DEFAULT_SPECULATION_RULES_HEADER, DEFAULT_SSR_CACHE_CONTROL, DISABLED_SSR_CACHE_HEADERS, isSsrCacheEnabled, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, SSR_CACHE_ENV_VAR, } from "../shared/cache-control.js";
1
+ export { DEFAULT_SSR_CACHE_HEADERS, DEFAULT_SPECULATION_RULES_HEADER, DEFAULT_SSR_CACHE_CONTROL, DISABLED_SSR_CACHE_HEADERS, isSsrCacheEnabled, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, resolveSsrNetlifyQueryVary, SSR_CACHE_ENV_VAR, } from "../shared/cache-control.js";
2
2
  export declare function createH3SSRHandler(getBuild: () => unknown): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<Response>>;
@@ -13,7 +13,7 @@ import { getFrameworkRoutePrefix } from "./framework-route-prefix.js";
13
13
  import { getPostHogClientConfigScript } from "./posthog-config.js";
14
14
  import { runWithRequestContext } from "./request-context.js";
15
15
  import { getRealtimeClientConfigScript, getSentryClientConfigScript, } from "./sentry-config.js";
16
- export { DEFAULT_SSR_CACHE_HEADERS, DEFAULT_SPECULATION_RULES_HEADER, DEFAULT_SSR_CACHE_CONTROL, DISABLED_SSR_CACHE_HEADERS, isSsrCacheEnabled, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, SSR_CACHE_ENV_VAR, } from "../shared/cache-control.js";
16
+ export { DEFAULT_SSR_CACHE_HEADERS, DEFAULT_SPECULATION_RULES_HEADER, DEFAULT_SSR_CACHE_CONTROL, DISABLED_SSR_CACHE_HEADERS, isSsrCacheEnabled, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, resolveSsrNetlifyQueryVary, SSR_CACHE_ENV_VAR, } from "../shared/cache-control.js";
17
17
  function getAppBasePath() {
18
18
  return getAppBasePathFromViteEnv();
19
19
  }
@@ -215,12 +215,8 @@ function applyDefaultSsrCacheHeader(headers, status, pathname) {
215
215
  for (const [name, value] of Object.entries(resolveSsrCacheHeaders())) {
216
216
  headers.set(name, value);
217
217
  }
218
- const cacheKeyHeaders = resolveSsrCacheKeyHeaders();
219
- const netlifyVary = varyByQuery
220
- ? cacheKeyHeaders["netlify-vary"]
221
- ? "query"
222
- : undefined
223
- : cacheKeyHeaders["netlify-vary"];
218
+ const cacheKeyHeaders = resolveSsrCacheKeyHeaders(undefined, { varyByQuery });
219
+ const netlifyVary = cacheKeyHeaders["netlify-vary"];
224
220
  if (netlifyVary)
225
221
  headers.set("netlify-vary", netlifyVary);
226
222
  else
@@ -32,7 +32,10 @@ export type SsrCacheHeaders = Record<"cache-control" | "cdn-cache-control" | "ne
32
32
  export declare function parseSsrCacheSetting(raw: string | undefined | null): SsrCachePolicy;
33
33
  export declare function ssrCacheHeadersForPolicy(policy: SsrCachePolicy): SsrCacheHeaders;
34
34
  export declare function resolveSsrCacheHeaders(env?: Record<string, string | undefined>): Readonly<SsrCacheHeaders>;
35
- export declare function resolveSsrCacheKeyHeaders(env?: Record<string, string | undefined>): Readonly<Record<string, string>>;
35
+ export declare function resolveSsrNetlifyQueryVary(varyByQuery?: boolean): string;
36
+ export declare function resolveSsrCacheKeyHeaders(env?: Record<string, string | undefined>, options?: {
37
+ varyByQuery?: boolean;
38
+ }): Readonly<Record<string, string>>;
36
39
  export declare function isSsrCacheEnabled(env?: Record<string, string | undefined>): boolean;
37
40
  export declare const DEFAULT_SPECULATION_RULES_PATH = "/_agent-native/speculation-rules.json";
38
41
  export declare const DEFAULT_SPECULATION_RULES_HEADER = "\"/_agent-native/speculation-rules.json\"";