@agent-native/core 0.227.0-nightly-20261110032437 → 0.227.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.
Files changed (68) hide show
  1. package/dist/agent/default-agent-engine.d.ts +1 -0
  2. package/dist/agent/default-agent-engine.js +6 -3
  3. package/dist/agent/engine/builder-engine.js +10 -6
  4. package/dist/agent/engine/chat-model-availability.d.ts +9 -0
  5. package/dist/agent/engine/chat-model-availability.js +39 -0
  6. package/dist/agent/optional-placeholders.d.ts +1 -0
  7. package/dist/agent/optional-placeholders.js +3 -0
  8. package/dist/cli/create.js +0 -49
  9. package/dist/cli/index.js +1 -1
  10. package/dist/cli/react-router-command.js +13 -7
  11. package/dist/client/chat/agentkit-protocol.js +259 -200
  12. package/dist/client/chat/run-outcome.d.ts +48 -0
  13. package/dist/client/chat/run-outcome.js +50 -0
  14. package/dist/collab/routes.d.ts +1 -1
  15. package/dist/collab/struct-routes.d.ts +1 -1
  16. package/dist/deploy/build.d.ts +1 -0
  17. package/dist/deploy/build.js +30 -1
  18. package/dist/framework-package-versions.json +11 -11
  19. package/dist/jobs/run-now.js +9 -1
  20. package/dist/notifications/routes.d.ts +3 -3
  21. package/dist/org/index.d.ts +1 -1
  22. package/dist/org/migrations.d.ts +1 -1
  23. package/dist/provider-api/actions/provider-api.d.ts +1 -1
  24. package/dist/resources/handlers.d.ts +1 -1
  25. package/dist/scripts/agent-engines/list-agent-engines.js +9 -5
  26. package/dist/server/auth.d.ts +3 -1
  27. package/dist/server/auth.js +29 -9
  28. package/dist/server/builder-signup-experiment.d.ts +89 -42
  29. package/dist/server/builder-signup-experiment.js +36 -14
  30. package/dist/server/core-routes-plugin.js +5 -3
  31. package/dist/server/index.d.ts +2 -1
  32. package/dist/server/index.js +2 -1
  33. package/dist/server/onboarding-html.d.ts +2 -0
  34. package/dist/server/onboarding-html.js +9 -5
  35. package/dist/server/release-migrations.d.ts +3 -1
  36. package/dist/server/release-migrations.js +8 -6
  37. package/dist/shared/mcp-embed-headers.js +8 -2
  38. package/dist/templates/builder-code-starter/AGENTS.md.patch +1 -1
  39. package/dist/templates/builder-code-starter/server/plugins/auth.ts.patch +2 -1
  40. package/dist/templates/chat/AGENTS.md +1 -1
  41. package/dist/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  42. package/dist/templates/chat/scripts/migrate-production.ts +1 -1
  43. package/dist/templates/chat/server/plugins/auth.ts +1 -0
  44. package/dist/templates/default/pnpm-workspace.yaml +0 -41
  45. package/dist/templates/default/scripts/migrate-production.ts +1 -1
  46. package/dist/templates/workspace-root/pnpm-workspace.yaml +0 -41
  47. package/dist/tracking/providers.js +20 -8
  48. package/dist/triggers/actions/list-automations.js +7 -1
  49. package/docs/content/agent-web-surfaces.mdx +54 -42
  50. package/docs/content/authentication.mdx +14 -0
  51. package/docs/content/client-agent-chat.mdx +2 -0
  52. package/docs/content/client-routing.mdx +2 -0
  53. package/docs/content/create-with-an-agent.mdx +1 -1
  54. package/docs/content/environment-variables.mdx +2 -0
  55. package/docs/content/extensions.mdx +8 -9
  56. package/docs/content/external-agents.mdx +8 -0
  57. package/docs/content/upgrading-core-ui.mdx +2 -0
  58. package/docs/content/vercel.mdx +2 -0
  59. package/package.json +4 -4
  60. package/src/templates/builder-code-starter/AGENTS.md.patch +1 -1
  61. package/src/templates/builder-code-starter/server/plugins/auth.ts.patch +2 -1
  62. package/src/templates/chat/AGENTS.md +1 -1
  63. package/src/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  64. package/src/templates/chat/scripts/migrate-production.ts +1 -1
  65. package/src/templates/chat/server/plugins/auth.ts +1 -0
  66. package/src/templates/default/pnpm-workspace.yaml +0 -41
  67. package/src/templates/default/scripts/migrate-production.ts +1 -1
  68. package/src/templates/workspace-root/pnpm-workspace.yaml +0 -41
@@ -15,6 +15,54 @@ import { type AutoContinueRefusalCode } from "../../agent/auto-continue.js";
15
15
  */
16
16
  export declare const RUN_OUTCOMES: readonly ["running", "succeeded", "stopped", "interrupted", "failed", "unverified"];
17
17
  export type RunOutcome = (typeof RUN_OUTCOMES)[number];
18
+ export type ActiveRunPhase = {
19
+ readonly type: "streaming";
20
+ } | {
21
+ readonly type: "holdingInterruption";
22
+ readonly error: AgentError;
23
+ } | {
24
+ readonly type: "followingSuccessor";
25
+ } | {
26
+ readonly type: "awaitingContinuation";
27
+ readonly streamClosed: boolean;
28
+ };
29
+ export type RunPhase = ActiveRunPhase | {
30
+ readonly type: "cancelling";
31
+ readonly previous: ActiveRunPhase;
32
+ } | {
33
+ readonly type: "terminal";
34
+ } | {
35
+ readonly type: "disposed";
36
+ };
37
+ export type RunPhaseEvent = {
38
+ readonly type: "streamEnded";
39
+ } | {
40
+ readonly type: "streamFailed";
41
+ } | {
42
+ readonly type: "interrupted";
43
+ readonly error: AgentError;
44
+ } | {
45
+ readonly type: "followSuccessor";
46
+ } | {
47
+ readonly type: "streamOpened";
48
+ } | {
49
+ readonly type: "continuationResumed";
50
+ } | {
51
+ readonly type: "awaitContinuation";
52
+ readonly streamClosed: boolean;
53
+ } | {
54
+ readonly type: "terminal";
55
+ } | {
56
+ readonly type: "cancelStarted";
57
+ } | {
58
+ readonly type: "cancelSucceeded";
59
+ } | {
60
+ readonly type: "cancelFailed";
61
+ } | {
62
+ readonly type: "dispose";
63
+ };
64
+ /** Late stream and authority results cannot revive a terminal or disposed run. */
65
+ export declare function transition(phase: RunPhase, event: RunPhaseEvent): RunPhase;
18
66
  /**
19
67
  * Codes that described "the agent stopped / is stuck" one incident at a time.
20
68
  * They survive only as telemetry detail; the user sees their outcome. A new
@@ -24,6 +24,56 @@ export const RUN_OUTCOMES = [
24
24
  "failed",
25
25
  "unverified",
26
26
  ];
27
+ /** Late stream and authority results cannot revive a terminal or disposed run. */
28
+ export function transition(phase, event) {
29
+ if (event.type === "dispose")
30
+ return { type: "disposed" };
31
+ if (phase.type === "disposed")
32
+ return phase;
33
+ if (phase.type === "terminal")
34
+ return phase;
35
+ if (phase.type === "cancelling") {
36
+ if (event.type === "cancelFailed")
37
+ return phase.previous;
38
+ if (event.type === "cancelSucceeded")
39
+ return { type: "terminal" };
40
+ return phase;
41
+ }
42
+ switch (event.type) {
43
+ case "streamEnded":
44
+ case "streamFailed":
45
+ return phase.type === "streaming"
46
+ ? { type: "followingSuccessor" }
47
+ : phase;
48
+ case "interrupted":
49
+ return phase.type === "streaming"
50
+ ? { type: "holdingInterruption", error: event.error }
51
+ : phase;
52
+ case "followSuccessor":
53
+ return phase.type === "holdingInterruption"
54
+ ? { type: "followingSuccessor" }
55
+ : phase;
56
+ case "streamOpened":
57
+ return phase.type === "followingSuccessor"
58
+ ? { type: "streaming" }
59
+ : phase;
60
+ case "continuationResumed":
61
+ return phase.type === "awaitingContinuation"
62
+ ? { type: "streaming" }
63
+ : phase;
64
+ case "awaitContinuation":
65
+ return phase.type === "streaming"
66
+ ? { type: "awaitingContinuation", streamClosed: event.streamClosed }
67
+ : phase;
68
+ case "terminal":
69
+ return { type: "terminal" };
70
+ case "cancelStarted":
71
+ return { type: "cancelling", previous: phase };
72
+ case "cancelSucceeded":
73
+ case "cancelFailed":
74
+ return phase;
75
+ }
76
+ }
27
77
  /**
28
78
  * Codes that described "the agent stopped / is stuck" one incident at a time.
29
79
  * They survive only as telemetry detail; the user sees their outcome. A new
@@ -26,8 +26,8 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
26
26
  ok: boolean;
27
27
  }>>;
28
28
  export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- ok?: undefined;
30
29
  text?: undefined;
30
+ ok?: undefined;
31
31
  error: string;
32
32
  } | {
33
33
  error?: undefined;
@@ -13,9 +13,9 @@ export declare const postCollabPatch: import("h3").EventHandlerWithFetch<import(
13
13
  ok: boolean;
14
14
  }>>;
15
15
  export declare const getCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- data?: undefined;
17
16
  error: string;
18
17
  docId?: undefined;
18
+ data?: undefined;
19
19
  } | {
20
20
  error?: undefined;
21
21
  docId: string;
@@ -186,6 +186,7 @@ export declare function cloudflareExternalBundleEntry(resolvedMod: string, neede
186
186
  */
187
187
  export declare function patchCloudflareModuleServerOutput(serverDir: string): void;
188
188
  export declare function bundleYjsRuntimeForServerlessOutput(serverDir: string, projectCwd: string): string[];
189
+ export declare function prepareVercelFunctionOutputs(outputDir: string, projectCwd: string): void;
189
190
  export declare function shouldBundleYjsRuntimeForPreset(targetPreset: string): boolean;
190
191
  export declare function nitroServerCodeSplittingGroupsForPreset(targetPreset: string): {
191
192
  test: RegExp;
@@ -3864,6 +3864,32 @@ export function bundleYjsRuntimeForServerlessOutput(serverDir, projectCwd) {
3864
3864
  });
3865
3865
  return bareImports;
3866
3866
  }
3867
+ export function prepareVercelFunctionOutputs(outputDir, projectCwd) {
3868
+ const functionsDir = path.join(outputDir, "functions");
3869
+ if (!fs.existsSync(functionsDir))
3870
+ return;
3871
+ const visit = (dir) => {
3872
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
3873
+ const entryPath = path.join(dir, entry.name);
3874
+ if (entry.name.endsWith(".func")) {
3875
+ if (entry.isSymbolicLink()) {
3876
+ const target = fs.realpathSync(entryPath);
3877
+ const relative = path.relative(fs.realpathSync(functionsDir), target);
3878
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
3879
+ throw new Error(`[deploy] Vercel function link leaves output: ${entryPath}`);
3880
+ }
3881
+ fs.unlinkSync(entryPath);
3882
+ fs.cpSync(target, entryPath, { recursive: true });
3883
+ }
3884
+ bundleYjsRuntimeForServerlessOutput(entryPath, projectCwd);
3885
+ }
3886
+ else if (entry.isDirectory()) {
3887
+ visit(entryPath);
3888
+ }
3889
+ }
3890
+ };
3891
+ visit(functionsDir);
3892
+ }
3867
3893
  export function shouldBundleYjsRuntimeForPreset(targetPreset) {
3868
3894
  return (targetPreset === "netlify" ||
3869
3895
  targetPreset === "vercel" ||
@@ -5050,7 +5076,10 @@ export default bundle;
5050
5076
  sanitizeServerlessFunctionPackageManifest(nitro.options.output.serverDir);
5051
5077
  pruneServerlessFunctionDeadWeight(nitro.options.output.serverDir);
5052
5078
  }
5053
- if (shouldBundleYjsRuntimeForPreset(preset)) {
5079
+ if (preset === "vercel") {
5080
+ prepareVercelFunctionOutputs(nitro.options.output.dir, cwd);
5081
+ }
5082
+ else if (shouldBundleYjsRuntimeForPreset(preset)) {
5054
5083
  bundleYjsRuntimeForServerlessOutput(nitro.options.output.serverDir, cwd);
5055
5084
  }
5056
5085
  if (isCloudflareModulePreset(preset)) {
@@ -1,17 +1,17 @@
1
1
  {
2
- "@agent-native/agentkit": "0.227.0-nightly-20261110032437",
2
+ "@agent-native/agentkit": "0.227.0",
3
3
  "@agent-native/code-agents-ui": "0.1.75",
4
- "@agent-native/core": "0.227.0-nightly-20261110032437",
5
- "@agent-native/core-corpus": "0.227.0-nightly-20261110032437",
6
- "@agent-native/creative-context": "0.8.68",
7
- "@agent-native/dispatch": "0.42.4-nightly-20261110032437",
4
+ "@agent-native/core": "0.227.0",
5
+ "@agent-native/core-corpus": "0.227.0",
6
+ "@agent-native/creative-context": "0.8.69",
7
+ "@agent-native/dispatch": "0.42.4",
8
8
  "@agent-native/embedding": "0.2.0",
9
9
  "@agent-native/migrate": "0.0.7",
10
- "@agent-native/otel": "0.1.35",
11
- "@agent-native/pinpoint": "0.1.111",
12
- "@agent-native/recap-cli": "0.5.99",
13
- "@agent-native/scheduling": "0.2.56-nightly-20261110032437",
10
+ "@agent-native/otel": "0.1.36",
11
+ "@agent-native/pinpoint": "0.1.112",
12
+ "@agent-native/recap-cli": "0.5.100",
13
+ "@agent-native/scheduling": "0.2.56",
14
14
  "@agent-native/shared-app-config": "0.1.2",
15
- "@agent-native/skills": "0.4.4-nightly-20261110032437",
16
- "@agent-native/toolkit": "0.227.0-nightly-20261110032437"
15
+ "@agent-native/skills": "0.4.4",
16
+ "@agent-native/toolkit": "0.227.0"
17
17
  }
@@ -4,7 +4,7 @@ import { canQueueAutomationRunNow, } from "../automations/service.js";
4
4
  import { isLocalDatabase } from "../db/client.js";
5
5
  import { organizationResourceOwner, resourceGetByPath, } from "../resources/store.js";
6
6
  import { fireInternalDispatch } from "../server/self-dispatch.js";
7
- import { parseJobResource } from "./frontmatter.js";
7
+ import { isRecoveredFactoryJob, jobBelongsToApp, parseJobResource, } from "./frontmatter.js";
8
8
  import { automationRunOwnership } from "./run-history-ownership.js";
9
9
  import { listUnclaimedAutomationRuns, startAutomationRun, } from "./run-history.js";
10
10
  async function dispatchAutomationRun(historyId, requestHeaders) {
@@ -113,6 +113,14 @@ export async function authorizeAutomationRunNow(input) {
113
113
  });
114
114
  }
115
115
  if (!(await canQueueAutomationRunNow(input, access.resource, access.scope, input.additionalJobAppIds))) {
116
+ const { meta } = parseJobResource(access.resource.content);
117
+ if (!jobBelongsToApp(meta, input.appId, input.additionalJobAppIds) &&
118
+ !isRecoveredFactoryJob(meta, access.resource.path, input.appId, access.resource.owner)) {
119
+ const ownerAppId = meta.appId?.trim();
120
+ throw Object.assign(new Error(ownerAppId
121
+ ? `This automation belongs to the "${ownerAppId}" app. Run it from that app.`
122
+ : "This automation has no app owner and cannot be run."), { statusCode: 403 });
123
+ }
116
124
  throw Object.assign(new Error("Collaborate access is required to run this automation."), { statusCode: 403 });
117
125
  }
118
126
  return access;
@@ -1,12 +1,12 @@
1
1
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
2
- error?: undefined;
3
2
  count: number;
4
3
  updated?: undefined;
4
+ error?: undefined;
5
5
  ok?: undefined;
6
6
  } | {
7
- error?: undefined;
8
7
  count?: undefined;
9
8
  updated: number;
9
+ error?: undefined;
10
10
  ok?: undefined;
11
11
  } | {
12
12
  count?: undefined;
@@ -14,8 +14,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
14
14
  error: string;
15
15
  ok?: undefined;
16
16
  } | {
17
- error?: undefined;
18
17
  count?: undefined;
19
18
  updated?: undefined;
19
+ error?: undefined;
20
20
  ok: boolean;
21
21
  }>>;
@@ -180,9 +180,9 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
180
180
  soloDeploymentAdmin: boolean | undefined;
181
181
  }>>;
182
182
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
183
- id?: undefined;
184
183
  name?: undefined;
185
184
  success: boolean;
185
+ id?: undefined;
186
186
  role?: undefined;
187
187
  } | {
188
188
  success?: undefined;
@@ -1,7 +1,7 @@
1
1
  export declare const ORG_MIGRATIONS: ({
2
- name?: undefined;
3
2
  version: number;
4
3
  sql: string;
4
+ name?: undefined;
5
5
  } | {
6
6
  version: number;
7
7
  name: string;
@@ -316,8 +316,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
316
316
  notes?: string | undefined;
317
317
  scope?: "org" | "user" | undefined;
318
318
  }, {
319
- provider?: undefined;
320
319
  id?: undefined;
320
+ provider?: undefined;
321
321
  message?: undefined;
322
322
  providers: {
323
323
  id: string;
@@ -49,7 +49,6 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
49
49
  error: string;
50
50
  storageSetupRequired?: undefined;
51
51
  } | {
52
- error?: undefined;
53
52
  id: string;
54
53
  path: string;
55
54
  owner: string;
@@ -64,6 +63,7 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
64
63
  runId: string | null;
65
64
  expiresAt: number | null;
66
65
  metadata: string | null;
66
+ error?: undefined;
67
67
  url: string;
68
68
  provider: string;
69
69
  storageSetupRequired?: undefined;
@@ -1,6 +1,6 @@
1
1
  import { getAgentAppModelDefaultForCurrentRequest } from "../../agent/app-model-defaults.js";
2
2
  import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../../agent/chatgpt-subscription-contract.js";
3
- import { readDefaultAgentEngineSettingDetailed, resolveDefaultAgentEngineAuthority, } from "../../agent/default-agent-engine.js";
3
+ import { isDefaultAgentEngineScopeResolved, readDefaultAgentEngineSettingDetailed, resolveDefaultAgentEngineAuthority, } from "../../agent/default-agent-engine.js";
4
4
  import { listChatGPTSubscriptionModels } from "../../agent/engine/chatgpt-subscription-engine.js";
5
5
  import { listAgentEngines, registerBuiltinEngines, detectEngineFromEnv, detectEngineFromUserSecrets, getAgentEngineEntry, isAgentEnginePackageInstalled, isStoredEngineUsableForRequest, normalizeModelForEngine, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, } from "../../agent/engine/index.js";
6
6
  import { applyProviderModelSelection, providerForEngineName, resolveEffectiveProviderModelSelection, } from "../../agent/provider-model-selection.js";
@@ -101,10 +101,14 @@ export async function run(args = {}) {
101
101
  }
102
102
  return pending;
103
103
  };
104
- const [defaultSetting, defaultAuthority] = await Promise.all([
105
- readDefaultAgentEngineSettingDetailed(),
106
- resolveDefaultAgentEngineAuthority(),
107
- ]);
104
+ // An unresolved scope cannot say which default applies. Listing engines
105
+ // must still work, so the read is skipped and reported as "unresolved"
106
+ // instead of "none", which would claim no default is saved.
107
+ const defaultAuthority = await resolveDefaultAgentEngineAuthority();
108
+ const scopeUnresolved = !isDefaultAgentEngineScopeResolved();
109
+ const defaultSetting = scopeUnresolved
110
+ ? { value: null, source: "unresolved" }
111
+ : await readDefaultAgentEngineSettingDetailed();
108
112
  const current = defaultSetting.value
109
113
  ? defaultSetting.value
110
114
  : null;
@@ -9,7 +9,7 @@ import type { GoogleAuthMode } from "./google-auth-mode.js";
9
9
  import { type OnboardingHtmlOptions } from "./onboarding-html.js";
10
10
  import { isSessionResolutionUnavailable } from "./session-resolution-unavailable.js";
11
11
  export declare function getSessionMaxAge(): number;
12
- export declare function recordBuilderSignupBinding(event: H3Event, attemptId: unknown, method: "password" | "magic-link" | "google", email: string): Promise<void>;
12
+ export declare function recordBuilderSignupBinding(event: H3Event, attemptId: unknown, method: "password" | "magic-link" | "google", email: string, verifiedSession?: AuthSession): Promise<void>;
13
13
  export interface AuthSession {
14
14
  email: string;
15
15
  userId?: string;
@@ -94,6 +94,8 @@ export interface AuthOptions {
94
94
  * fallback markup, which the auth client script replaces in the browser.
95
95
  */
96
96
  renderSignInPage?: (props: AuthPageProps) => string | undefined;
97
+ /** Trusted HTML appended to the sign-in document head. */
98
+ authHeadHtml?: string;
97
99
  renderResetPasswordPage?: (props: ResetPasswordPageProps) => string | undefined;
98
100
  rootAuth?: boolean;
99
101
  googleOnly?: boolean;
@@ -12,7 +12,7 @@ import { FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_MAX_AGE, } from "../s
12
12
  import { FIRST_TOUCH_SERVER_PATH } from "../shared/first-touch-persistence.js";
13
13
  import { isGoogleProfileImageUrl } from "../shared/google-profile-image.js";
14
14
  import { corsAllowHeadersFor, EMBED_TRANSPLANT_HEADER, isExplicitCorsAllowedOrigin, isMcpEmbedCorsOrigin, MCP_EMBED_CORS_ALLOW_HEADERS, shouldAllowMcpEmbedCredentials, } from "../shared/mcp-embed-headers.js";
15
- import { bindBuilderSignupAttempt, builderSignupAssignmentCookieName, } from "./builder-signup-experiment.js";
15
+ import { bindBuilderSignupAttempt, startBuilderSignupForVerifiedSession, builderSignupAssignmentCookieName, } from "./builder-signup-experiment.js";
16
16
  import { createConsentHandler, createConsentRegionHandler, requestAllowsConsentCategory, } from "./consent-route.js";
17
17
  import { readDevActionDiscoveryFile } from "./dev-action-discovery.js";
18
18
  import { devLoopbackAuthHint } from "./dev-origin-hint.js";
@@ -155,12 +155,24 @@ function requestWithSignupAttribution(request, signupAttribution) {
155
155
  function headersWithSignupAttribution(headers, cookieHeader) {
156
156
  return addSignupAttributionHeader(headers, signupAttributionContextFromCookieHeader(cookieHeader));
157
157
  }
158
- export async function recordBuilderSignupBinding(event, attemptId, method, email) {
158
+ export async function recordBuilderSignupBinding(event, attemptId, method, email, verifiedSession) {
159
159
  if (typeof attemptId !== "string" &&
160
160
  !getCookie(event, builderSignupAssignmentCookieName()))
161
161
  return;
162
162
  try {
163
- await bindBuilderSignupAttempt(event, attemptId, method, email);
163
+ const bound = await bindBuilderSignupAttempt(event, attemptId, method, email);
164
+ if (bound &&
165
+ verifiedSession?.emailVerified === true &&
166
+ verifiedSession.token &&
167
+ verifiedSession.email.trim().toLowerCase() === email.trim().toLowerCase()) {
168
+ // The callback just issued this session; its cookie is in the response,
169
+ // not the incoming request. Org/credential checks must see that identity.
170
+ const signupSession = { ...verifiedSession, userId: bound.userId };
171
+ event.context.__anSessionCache = Promise.resolve(signupSession);
172
+ delete event.context.__anOrgContextCache;
173
+ markRequestIdentityAuthenticatedAtMs(event, verifiedSession.email, Date.now(), verifiedSession.token);
174
+ await startBuilderSignupForVerifiedSession(event, signupSession);
175
+ }
164
176
  }
165
177
  catch (error) {
166
178
  captureAuthError(error, { route: "signup", email });
@@ -1862,6 +1874,7 @@ function getOnboardingHtmlOptions(options, event, rawPath, authMode) {
1862
1874
  signupLegalNotice: options.signupLegalNotice,
1863
1875
  googleAuthMode: options.googleAuthMode,
1864
1876
  renderSignInPage: options.renderSignInPage,
1877
+ authHeadHtml: options.authHeadHtml,
1865
1878
  requestHost: event ? getRequestHost(event) : undefined,
1866
1879
  requestPath: rawPath,
1867
1880
  requestOrigin: event ? getOrigin(event) : undefined,
@@ -4420,15 +4433,16 @@ async function mountBetterAuthRoutes(app, options) {
4420
4433
  console.warn("[auth] failed to store Google profile image:", error);
4421
4434
  });
4422
4435
  }
4436
+ const signupUserId = isNewGoogleUser
4437
+ ? await getBetterAuthUserIdForEmail(email)
4438
+ : undefined;
4423
4439
  const { sessionToken } = await createOAuthSession(event, email, {
4424
4440
  hasProductionSession: false,
4425
4441
  desktop,
4426
4442
  mobile,
4427
4443
  trackSignup: {
4428
4444
  authProvider: "google",
4429
- canonicalAuthUserId: isNewGoogleUser
4430
- ? await getBetterAuthUserIdForEmail(email)
4431
- : undefined,
4445
+ ...(signupUserId ? { canonicalAuthUserId: signupUserId } : {}),
4432
4446
  name: typeof user.name === "string" ? user.name : undefined,
4433
4447
  attribution: signupAttribution,
4434
4448
  signupAnonymousId,
@@ -4437,8 +4451,14 @@ async function mountBetterAuthRoutes(app, options) {
4437
4451
  isNewUser: isNewGoogleUser,
4438
4452
  },
4439
4453
  });
4440
- if (isNewGoogleUser)
4441
- await recordBuilderSignupBinding(event, builderSignupAttemptId, "google", email);
4454
+ if (isNewGoogleUser) {
4455
+ await recordBuilderSignupBinding(event, builderSignupAttemptId, "google", email, {
4456
+ email,
4457
+ ...(sessionToken ? { token: sessionToken } : {}),
4458
+ ...(typeof user.name === "string" ? { name: user.name } : {}),
4459
+ emailVerified: true,
4460
+ });
4461
+ }
4442
4462
  logGoogleOAuthDebug(event, "callback-session-created", {
4443
4463
  flowId,
4444
4464
  desktop,
@@ -5418,7 +5438,7 @@ async function mountBetterAuthRoutes(app, options) {
5418
5438
  : query.return;
5419
5439
  const signupSession = await getSession(event);
5420
5440
  if (signupSession?.emailVerified)
5421
- await recordBuilderSignupBinding(event, query.builder_signup_attempt, "magic-link", signupSession.email);
5441
+ await recordBuilderSignupBinding(event, query.builder_signup_attempt, "magic-link", signupSession.email, signupSession);
5422
5442
  setFirstRunOnboardingCookie(event);
5423
5443
  return queryEchoSafeRedirect(event, redirectWithStagedCookies(event, safeReturnPath(rawReturn), 302));
5424
5444
  }));
@@ -31,13 +31,18 @@ export declare function builderSignupAssignmentCookieName(): string;
31
31
  export declare function encodeBuilderSignupAssignment(assignment: Assignment): string;
32
32
  export declare function decodeBuilderSignupAssignment(value: string | undefined): Assignment | null;
33
33
  export declare function isBuilderSignupExperimentEnabled(): boolean;
34
- export declare function bindBuilderSignupAttempt(event: H3Event, attemptId: unknown, method: "password" | "magic-link" | "google", email: string): Promise<boolean>;
34
+ export declare function bindBuilderSignupAttempt(event: H3Event, attemptId: unknown, method: "password" | "magic-link" | "google", email: string): Promise<{
35
+ userId: string;
36
+ } | false>;
35
37
  export declare function getBuilderSignupConnectError(userId: string | undefined): Promise<{
36
38
  code: "account_exists";
37
39
  message: string;
38
40
  at: number;
39
41
  } | undefined>;
40
- export declare function createBuilderSignupExperimentHandler(dependencies: {
42
+ export declare function setBuilderSignupVerifiedStarter(starter: (event: H3Event, session: AuthSession) => Promise<unknown>): void;
43
+ /** Called only after the auth provider issued this verified signup's session. */
44
+ export declare function startBuilderSignupForVerifiedSession(event: H3Event, session: AuthSession): Promise<void>;
45
+ export declare function createBuilderSignupExperiment(dependencies: {
41
46
  getSession: (event: H3Event) => Promise<AuthSession | null>;
42
47
  sameOrigin: (event: H3Event) => boolean;
43
48
  activate: (event: H3Event, session: AuthSession, owner: {
@@ -53,44 +58,86 @@ export declare function createBuilderSignupExperimentHandler(dependencies: {
53
58
  attempt: string;
54
59
  startedAt: number;
55
60
  }) => Promise<boolean>;
56
- }): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
- status: "connected" | "fallback";
58
- code?: string | undefined;
59
- } | {
60
- status: "unavailable";
61
- code: string;
62
- } | {
63
- status: "disabled";
64
- code?: undefined;
65
- } | {
66
- id: string;
67
- variant: "automatic" | "control";
68
- issuedAt: number;
69
- acceptedAt?: number | undefined;
70
- attemptId?: string | undefined;
71
- method?: "google" | "magic-link" | "password" | undefined;
72
- email?: string | undefined;
73
- policy: "builder-signup-2026-11-06";
74
- status: "assigned";
75
- experimentId: string;
76
- code?: undefined;
77
- } | {
78
- status: "disabled";
79
- code: string;
80
- } | {
81
- code?: undefined;
82
- status: "pending";
83
- } | {
84
- code?: undefined;
85
- status: "eligible";
86
- } | {
87
- code?: undefined;
88
- status: "not_required";
89
- } | {
90
- code?: undefined;
91
- status: "accepted";
92
- } | {
93
- status: "pending";
94
- code: string;
95
- }>>;
61
+ }): {
62
+ handler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
63
+ status: "connected" | "fallback";
64
+ code?: string | undefined;
65
+ } | {
66
+ status: "unavailable";
67
+ code: string;
68
+ } | {
69
+ status: "disabled";
70
+ code?: undefined;
71
+ } | {
72
+ id: string;
73
+ variant: "automatic" | "control";
74
+ issuedAt: number;
75
+ acceptedAt?: number | undefined;
76
+ attemptId?: string | undefined;
77
+ method?: "google" | "magic-link" | "password" | undefined;
78
+ email?: string | undefined;
79
+ policy: "builder-signup-2026-11-06";
80
+ status: "assigned";
81
+ experimentId: string;
82
+ code?: undefined;
83
+ } | {
84
+ status: "disabled";
85
+ code: string;
86
+ } | {
87
+ code?: undefined;
88
+ status: "pending";
89
+ } | {
90
+ code?: undefined;
91
+ status: "eligible";
92
+ } | {
93
+ code?: undefined;
94
+ status: "not_required";
95
+ } | {
96
+ code?: undefined;
97
+ status: "accepted";
98
+ } | {
99
+ status: "pending";
100
+ code: string;
101
+ }>>;
102
+ startVerifiedSignup: (event: H3Event, session: AuthSession) => Promise<{
103
+ status: "connected" | "fallback";
104
+ code?: string | undefined;
105
+ } | {
106
+ status: "unavailable";
107
+ code: string;
108
+ } | {
109
+ status: "disabled";
110
+ code?: undefined;
111
+ } | {
112
+ id: string;
113
+ variant: "automatic" | "control";
114
+ issuedAt: number;
115
+ acceptedAt?: number | undefined;
116
+ attemptId?: string | undefined;
117
+ method?: "google" | "magic-link" | "password" | undefined;
118
+ email?: string | undefined;
119
+ policy: "builder-signup-2026-11-06";
120
+ status: "assigned";
121
+ experimentId: string;
122
+ code?: undefined;
123
+ } | {
124
+ status: "disabled";
125
+ code: string;
126
+ } | {
127
+ code?: undefined;
128
+ status: "pending";
129
+ } | {
130
+ code?: undefined;
131
+ status: "eligible";
132
+ } | {
133
+ code?: undefined;
134
+ status: "not_required";
135
+ } | {
136
+ code?: undefined;
137
+ status: "accepted";
138
+ } | {
139
+ status: "pending";
140
+ code: string;
141
+ }>;
142
+ };
96
143
  export {};