@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
@@ -156,7 +156,8 @@ export async function bindBuilderSignupAttempt(event, attemptId, method, email)
156
156
  const createdAtMs = createdAt ? new Date(createdAt).getTime() : Number.NaN;
157
157
  if (!identity ||
158
158
  !Number.isFinite(createdAtMs) ||
159
- createdAtMs < assignment.issuedAt)
159
+ createdAtMs < assignment.issuedAt ||
160
+ (assignment.acceptedAt !== undefined && createdAtMs < assignment.acceptedAt))
160
161
  return false;
161
162
  track("builder_signup_experiment_enrolled", { ...experimentProperties(assignment), method }, { authUserId: identity.user.id, userId: normalizedEmail });
162
163
  if (assignment.variant === "control") {
@@ -186,8 +187,6 @@ export async function bindBuilderSignupAttempt(event, attemptId, method, email)
186
187
  if (assignment.method !== method ||
187
188
  (assignment.email && assignment.email !== normalizedEmail))
188
189
  return false;
189
- if (createdAtMs < assignment.acceptedAt)
190
- return false;
191
190
  const bound = z
192
191
  .object({ id: z.string(), boundUserId: z.string() })
193
192
  .parse(await mutateSetting(key, (current) => current ?? { id: assignment.id, boundUserId: identity.user.id }));
@@ -204,7 +203,7 @@ export async function bindBuilderSignupAttempt(event, attemptId, method, email)
204
203
  maxAge: TTL,
205
204
  secure: getHeader(event, "host")?.includes("localhost") !== true,
206
205
  });
207
- return true;
206
+ return { userId: identity.user.id };
208
207
  }
209
208
  export async function getBuilderSignupConnectError(userId) {
210
209
  if (!userId || !isBuilderSignupExperimentEnabled())
@@ -257,7 +256,22 @@ async function finishSignupActivation(key, attempt, outcome) {
257
256
  changed,
258
257
  };
259
258
  }
260
- export function createBuilderSignupExperimentHandler(dependencies) {
259
+ let verifiedSignupStarter;
260
+ export function setBuilderSignupVerifiedStarter(starter) {
261
+ verifiedSignupStarter = starter;
262
+ }
263
+ /** Called only after the auth provider issued this verified signup's session. */
264
+ export async function startBuilderSignupForVerifiedSession(event, session) {
265
+ if (!isBuilderSignupExperimentEnabled() ||
266
+ !session.userId ||
267
+ !session.token ||
268
+ session.emailVerified !== true)
269
+ return;
270
+ if (!verifiedSignupStarter)
271
+ throw new Error("Builder signup activation handler is unavailable");
272
+ await verifiedSignupStarter(event, session);
273
+ }
274
+ export function createBuilderSignupExperiment(dependencies) {
261
275
  const reconcile = async (event, session, key, existing, assignment) => {
262
276
  const { attempt, startedAt } = existing;
263
277
  if (!session.userId ||
@@ -293,10 +307,11 @@ export function createBuilderSignupExperimentHandler(dependencies) {
293
307
  ...(result.code ? { code: result.code } : {}),
294
308
  };
295
309
  };
296
- return defineEventHandler(async (event) => {
310
+ const handle = async (event, verifiedSignupSession) => {
297
311
  setResponseHeader(event, "Cache-Control", "no-store");
298
312
  const refuse = (status, code) => {
299
- setResponseStatus(event, status);
313
+ if (!verifiedSignupSession)
314
+ setResponseStatus(event, status);
300
315
  return { status: "unavailable", code };
301
316
  };
302
317
  if (!isBuilderSignupExperimentEnabled()) {
@@ -304,12 +319,12 @@ export function createBuilderSignupExperimentHandler(dependencies) {
304
319
  deleteCookie(event, "an_builder_signup_pending", { path: "/" });
305
320
  return { status: "disabled" };
306
321
  }
307
- const method = getMethod(event);
322
+ const method = verifiedSignupSession ? "POST" : getMethod(event);
308
323
  if (method !== "GET" && method !== "POST")
309
324
  return refuse(405, "method_not_allowed");
310
- if (!dependencies.sameOrigin(event))
325
+ if (!verifiedSignupSession && !dependencies.sameOrigin(event))
311
326
  return refuse(403, "cross_origin");
312
- const session = await dependencies.getSession(event);
327
+ const session = verifiedSignupSession ?? (await dependencies.getSession(event));
313
328
  let assignment = decodeBuilderSignupAssignment(getCookie(event, builderSignupAssignmentCookieName()));
314
329
  if (method === "GET") {
315
330
  if (session)
@@ -329,7 +344,8 @@ export function createBuilderSignupExperimentHandler(dependencies) {
329
344
  experimentId: experimentId(),
330
345
  };
331
346
  }
332
- if (!/^application\/json\s*(;|$)/i.test(getHeader(event, "content-type") ?? ""))
347
+ if (!verifiedSignupSession &&
348
+ !/^application\/json\s*(;|$)/i.test(getHeader(event, "content-type") ?? ""))
333
349
  return refuse(415, "json_required");
334
350
  const body = z
335
351
  .object({
@@ -339,7 +355,9 @@ export function createBuilderSignupExperimentHandler(dependencies) {
339
355
  method: z.enum(["password", "magic-link", "google"]).optional(),
340
356
  email: z.string().email().max(320).optional(),
341
357
  })
342
- .safeParse(await readBodyWithSizeLimit(event, 4096));
358
+ .safeParse(verifiedSignupSession
359
+ ? { operation: "start" }
360
+ : await readBodyWithSizeLimit(event, 4096));
343
361
  if (!body.success)
344
362
  return refuse(400, "invalid_request");
345
363
  const waitUntil = body.data.operation === "start"
@@ -385,7 +403,7 @@ export function createBuilderSignupExperimentHandler(dependencies) {
385
403
  }
386
404
  }
387
405
  if (body.data.operation === "status" ||
388
- (body.data.operation === "start" && !waitUntil)) {
406
+ (body.data.operation === "start" && !waitUntil && !verifiedSignupSession)) {
389
407
  if (!session?.emailVerified || !session.userId)
390
408
  return refuse(401, "verified_session_required");
391
409
  if (!consent) {
@@ -586,5 +604,9 @@ export function createBuilderSignupExperimentHandler(dependencies) {
586
604
  }
587
605
  // Hosts without a durable background lifetime must await the write.
588
606
  return completion;
589
- });
607
+ };
608
+ return {
609
+ handler: defineEventHandler((event) => handle(event)),
610
+ startVerifiedSignup: (event, session) => handle(event, session),
611
+ };
590
612
  }
@@ -10,7 +10,7 @@ import { registerBuiltinEngines } from "../agent/engine/builtin.js";
10
10
  import { OPENAI_BASE_URL_ENV_VAR, PROVIDER_ENV_META, } from "../agent/engine/provider-env-vars.js";
11
11
  import { detectEngineFromEnvForRequest, detectEngineFromUserSecrets, isStoredEngineUsableForRequest, } from "../agent/engine/registry.js";
12
12
  import { canUpdateAgentLoopSettings, readAgentLoopSettings, resetAgentLoopSettings, validateMaxIterationsInput, writeAgentLoopSettings, } from "../agent/loop-settings.js";
13
- import { createBuilderSignupExperimentHandler, getBuilderSignupConnectError, getBuilderSignupTrackingIdentity, } from "./builder-signup-experiment.js";
13
+ import { createBuilderSignupExperiment, setBuilderSignupVerifiedStarter, getBuilderSignupConnectError, getBuilderSignupTrackingIdentity, } from "./builder-signup-experiment.js";
14
14
  export { normalizeAgentEngineStatusModel, resolveAgentEngineStatus, } from "../agent/engine-status.js";
15
15
  import { getAppConfig } from "../app-config/index.js";
16
16
  import { getState, putState, compareAndSetState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, saveComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, getStateMany, putStateMany, APP_STATE_ANONYMOUS_OWNER_CONTEXT_KEY, } from "../application-state/handlers.js";
@@ -3478,7 +3478,7 @@ export function createCoreRoutesPlugin(options = {}) {
3478
3478
  setResponseHeader(event, "Location", authorizationUrl);
3479
3479
  return "";
3480
3480
  }));
3481
- getH3App(nitroApp).use(`${P}/auth/builder-signup-experiment`, createBuilderSignupExperimentHandler({
3481
+ const builderSignupExperiment = createBuilderSignupExperiment({
3482
3482
  getSession,
3483
3483
  sameOrigin: isSameOriginConnect,
3484
3484
  activate: async (event, session, owner) => {
@@ -3498,7 +3498,9 @@ export function createCoreRoutesPlugin(options = {}) {
3498
3498
  });
3499
3499
  },
3500
3500
  reconcile: reconcileBuilderSignupActivation,
3501
- }));
3501
+ });
3502
+ setBuilderSignupVerifiedStarter(builderSignupExperiment.startVerifiedSignup);
3503
+ getH3App(nitroApp).use(`${P}/auth/builder-signup-experiment`, builderSignupExperiment.handler);
3502
3504
  getH3App(nitroApp).use(`${P}/builder/provision`, createBuilderProvisionHandler((event) => resolveBuilderOwnerContext(event)));
3503
3505
  // Desktop Code Agents speak Anthropic Messages locally, but Builder
3504
3506
  // Gateway credentials live on the server. This same-origin route keeps
@@ -16,7 +16,7 @@ export { createEmbedSessionTicket, consumeEmbedSessionTicket, normalizeEmbedTarg
16
16
  export { createSSEHandler, type SSEHandlerOptions } from "./sse.js";
17
17
  export { isCredentialMembershipUnavailable } from "./credential-membership-unavailable.js";
18
18
  export { readOrgMemberRole } from "./personal-provider-key-policy.js";
19
- export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, getSession, getBrowserCookieSession, isSessionResolutionUnavailable, getMcpOAuthBearerSession, logout, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, setFirstRunOnboardingCookie, clearFrameworkSessionCookies, runAuthGuard, registerDesktopExchange, prepareDesktopOAuthBrowserBinding, matchesDesktopOAuthBrowserBinding, DESKTOP_OAUTH_BROWSER_BINDING_COOKIE, setDesktopExchange, setDesktopExchangeError, safeReturnPath, type DesktopExchangeErrorPayload, type AuthSession, type AuthOptions, } from "./auth.js";
19
+ export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, recordBuilderSignupBinding, getSession, getBrowserCookieSession, isSessionResolutionUnavailable, getMcpOAuthBearerSession, logout, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, setFirstRunOnboardingCookie, clearFrameworkSessionCookies, runAuthGuard, registerDesktopExchange, prepareDesktopOAuthBrowserBinding, matchesDesktopOAuthBrowserBinding, DESKTOP_OAUTH_BROWSER_BINDING_COOKIE, setDesktopExchange, setDesktopExchangeError, safeReturnPath, type DesktopExchangeErrorPayload, type AuthSession, type AuthOptions, } from "./auth.js";
20
20
  export { hasSameOriginBrowserEvidence } from "./request-origin.js";
21
21
  export { getEmbedSessionOrigin } from "./embed-request-origin.js";
22
22
  export { handleIdentitySso, ensureIdentityUser, getIdentityHubUrl, isIdentitySsoEnabled, isIdentitySsoBypassPath, identitySsoLoginButtonHtml, IDENTITY_SSO_BOOTSTRAP_ACTIVATE_PATH, IDENTITY_SSO_BOOTSTRAP_BINDING_COOKIE, clearIdentitySsoBootstrapBindingCookie, getIdentitySsoBootstrapBindingCookie, setIdentitySsoBootstrapBindingCookie, IDENTITY_SSO_PROVIDER_ID, IDENTITY_SSO_SCOPE, IDENTITY_SSO_DESKTOP_COMPLETE_PATH, } from "./identity-sso.js";
@@ -41,6 +41,7 @@ export { callerHasThreadAccess } from "../agent/run-ownership.js";
41
41
  export { abortTurnByRefDurably, getActiveRunForThreadAsync, } from "../agent/run-manager.js";
42
42
  export { mountRealtimeVoiceRoutes, realtimeVoiceSafetyIdentifier, REALTIME_VOICE_MAX_SDP_BYTES, REALTIME_VOICE_MAX_TOOL_BODY_BYTES, REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS, REALTIME_VOICE_SESSION_PATH, REALTIME_VOICE_TOOL_PATH, type MountRealtimeVoiceRoutesOptions, type RealtimeVoiceIntelligence, type RealtimeVoiceRequestContext, type RealtimeVoiceToolExecutionRequest, type RealtimeVoiceToolExecutionResult, } from "./realtime-voice.js";
43
43
  export { getStoredModelForEngine, resolveEngine, } from "../agent/engine/index.js";
44
+ export { resolveChatModelAvailability, type ChatModelAvailability, } from "../agent/engine/chat-model-availability.js";
44
45
  export { completeText, type CompleteTextMessage, type CompleteTextOptions, type CompleteTextResult, type CompleteTextUsage, } from "./complete-text.js";
45
46
  export { createDevScriptRegistry } from "../scripts/dev/index.js";
46
47
  export { createPollHandler, recordChange, prepareTransactionalChange, getVersion, getChangesSince, getCurrentPollBaseline, getPollEmitter, canSeeChangeForUser, POLL_CHANGE_EVENT, type TransactionalChange, } from "./poll.js";
@@ -16,7 +16,7 @@ export { createEmbedSessionTicket, consumeEmbedSessionTicket, normalizeEmbedTarg
16
16
  export { createSSEHandler } from "./sse.js";
17
17
  export { isCredentialMembershipUnavailable } from "./credential-membership-unavailable.js";
18
18
  export { readOrgMemberRole } from "./personal-provider-key-policy.js";
19
- export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, getSession, getBrowserCookieSession, isSessionResolutionUnavailable, getMcpOAuthBearerSession, logout, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, setFirstRunOnboardingCookie, clearFrameworkSessionCookies, runAuthGuard, registerDesktopExchange, prepareDesktopOAuthBrowserBinding, matchesDesktopOAuthBrowserBinding, DESKTOP_OAUTH_BROWSER_BINDING_COOKIE, setDesktopExchange, setDesktopExchangeError, safeReturnPath, } from "./auth.js";
19
+ export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, recordBuilderSignupBinding, getSession, getBrowserCookieSession, isSessionResolutionUnavailable, getMcpOAuthBearerSession, logout, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, setFirstRunOnboardingCookie, clearFrameworkSessionCookies, runAuthGuard, registerDesktopExchange, prepareDesktopOAuthBrowserBinding, matchesDesktopOAuthBrowserBinding, DESKTOP_OAUTH_BROWSER_BINDING_COOKIE, setDesktopExchange, setDesktopExchangeError, safeReturnPath, } from "./auth.js";
20
20
  export { hasSameOriginBrowserEvidence } from "./request-origin.js";
21
21
  export { getEmbedSessionOrigin } from "./embed-request-origin.js";
22
22
  export { handleIdentitySso, ensureIdentityUser, getIdentityHubUrl, isIdentitySsoEnabled, isIdentitySsoBypassPath, identitySsoLoginButtonHtml, IDENTITY_SSO_BOOTSTRAP_ACTIVATE_PATH, IDENTITY_SSO_BOOTSTRAP_BINDING_COOKIE, clearIdentitySsoBootstrapBindingCookie, getIdentitySsoBootstrapBindingCookie, setIdentitySsoBootstrapBindingCookie, IDENTITY_SSO_PROVIDER_ID, IDENTITY_SSO_SCOPE, IDENTITY_SSO_DESKTOP_COMPLETE_PATH, } from "./identity-sso.js";
@@ -39,6 +39,7 @@ export { callerHasThreadAccess } from "../agent/run-ownership.js";
39
39
  export { abortTurnByRefDurably, getActiveRunForThreadAsync, } from "../agent/run-manager.js";
40
40
  export { mountRealtimeVoiceRoutes, realtimeVoiceSafetyIdentifier, REALTIME_VOICE_MAX_SDP_BYTES, REALTIME_VOICE_MAX_TOOL_BODY_BYTES, REALTIME_VOICE_MAX_TOOL_OUTPUT_CHARS, REALTIME_VOICE_SESSION_PATH, REALTIME_VOICE_TOOL_PATH, } from "./realtime-voice.js";
41
41
  export { getStoredModelForEngine, resolveEngine, } from "../agent/engine/index.js";
42
+ export { resolveChatModelAvailability, } from "../agent/engine/chat-model-availability.js";
42
43
  export { completeText, } from "./complete-text.js";
43
44
  export { createDevScriptRegistry } from "../scripts/dev/index.js";
44
45
  export { createPollHandler, recordChange, prepareTransactionalChange, getVersion, getChangesSince, getCurrentPollBaseline, getPollEmitter, canSeeChangeForUser, POLL_CHANGE_EVENT, } from "./poll.js";
@@ -33,6 +33,8 @@ export interface OnboardingHtmlOptions {
33
33
  signupLegalNotice?: SignupLegalNoticeOptions | false;
34
34
  googleAuthMode?: GoogleAuthMode;
35
35
  renderSignInPage?: (props: AuthPageProps) => string | undefined;
36
+ /** Trusted HTML appended to the sign-in document head. */
37
+ authHeadHtml?: string;
36
38
  }
37
39
  export declare function getOnboardingHtml(opts?: OnboardingHtmlOptions): string;
38
40
  export declare function getResetPasswordHtml(requestPath?: string, renderResetPasswordPage?: (props: ResetPasswordPageProps) => string | undefined): string;
@@ -2129,6 +2129,10 @@ export function getOnboardingHtml(opts = {}) {
2129
2129
  bottom: max(1.25rem, env(safe-area-inset-bottom));
2130
2130
  inset-inline-end: max(1.25rem, env(safe-area-inset-right));
2131
2131
  }
2132
+ body.has-marketing .locale-menu {
2133
+ top: auto;
2134
+ bottom: calc(100% + 0.375rem);
2135
+ }
2132
2136
  @media (prefers-color-scheme: light) {
2133
2137
  body.has-marketing {
2134
2138
  --auth-marketing-border: color-mix(in srgb, var(--auth-marketing-foreground) 18%, transparent);
@@ -2185,7 +2189,7 @@ export function getOnboardingHtml(opts = {}) {
2185
2189
  padding: max(1.5rem, env(safe-area-inset-top)) 1.25rem max(1.5rem, env(safe-area-inset-bottom));
2186
2190
  border-inline-start: 0;
2187
2191
  }
2188
- .auth-marketing-home .marketing-panel { display: none; }
2192
+ .auth-marketing-home .marketing-panel { order: 2; min-height: 0; }
2189
2193
  .auth-marketing-home .card h1 {
2190
2194
  font-size: clamp(1.625rem, 6vw, 2rem);
2191
2195
  line-height: 1.15;
@@ -2211,9 +2215,9 @@ export function getOnboardingHtml(opts = {}) {
2211
2215
  }
2212
2216
  `;
2213
2217
  const authClientScriptPath = authClientAssetPath(appBasePath);
2214
- const title = hasMarketing
2215
- ? `${marketing.appName} — ${t("pageTitleSignIn")}`
2216
- : t("pageTitleWelcome");
2218
+ const title = marketing && !simplifiedAuth
2219
+ ? `${marketing.appName} — ${EN_AUTH_COPY.pageTitleSignIn}`
2220
+ : EN_AUTH_COPY.pageTitleWelcome;
2217
2221
  const authPageMarkup = opts.renderSignInPage?.(authPageProps);
2218
2222
  const fallbackTitle = googleOnly
2219
2223
  ? t("pageTitleSignIn")
@@ -2254,7 +2258,7 @@ export function getOnboardingHtml(opts = {}) {
2254
2258
  : hasMarketing
2255
2259
  ? "has-marketing"
2256
2260
  : "";
2257
- return `<!DOCTYPE html><html lang="${DEFAULT_LOCALE}" dir="ltr"><head><meta charset="UTF-8"/><script data-agent-native-locale-init="">${localeInitScript}</script><script data-agent-native-embedded-init="">${embeddedAuthInitScript}</script><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>${fontLinks}<title>${escapeHtml(title)}</title><link rel="icon" type="image/svg+xml" href="${escapeHtml(withAppBasePath("/favicon.svg", appBasePath))}"/><link rel="apple-touch-icon" href="${escapeHtml(withAppBasePath("/icon-180.svg", appBasePath))}"/>${marketingMeta}<style>${authDocumentStyles + authPageLayoutStyles}</style><script type="module" src="${escapeHtml(authClientScriptPath)}"></script></head><body${bodyClass ? ` class="${bodyClass}"` : ""}><div id="agent-native-auth-root" class="auth-root"${authPageMarkup === undefined ? ' data-agent-native-auth-fallback="true"' : ""}>${pageMarkup}</div><script type="application/json" id="agent-native-auth-data">${authPageData}</script></body></html>`;
2261
+ return `<!DOCTYPE html><html lang="${DEFAULT_LOCALE}" dir="ltr"><head><meta charset="UTF-8"/><script data-agent-native-locale-init="">${localeInitScript}</script><script data-agent-native-embedded-init="">${embeddedAuthInitScript}</script><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>${fontLinks}<title>${escapeHtml(title)}</title><link rel="icon" type="image/svg+xml" href="${escapeHtml(withAppBasePath("/favicon.svg", appBasePath))}"/><link rel="apple-touch-icon" href="${escapeHtml(withAppBasePath("/icon-180.svg", appBasePath))}"/>${marketingMeta}<style>${authDocumentStyles + authPageLayoutStyles}</style>${opts.authHeadHtml ?? ""}<script type="module" src="${escapeHtml(authClientScriptPath)}"></script></head><body${bodyClass ? ` class="${bodyClass}"` : ""}><div id="agent-native-auth-root" class="auth-root"${authPageMarkup === undefined ? ' data-agent-native-auth-fallback="true"' : ""}>${pageMarkup}</div><script type="application/json" id="agent-native-auth-data">${authPageData}</script></body></html>`;
2258
2262
  }
2259
2263
  const RESET_PASSWORD_STYLES = `
2260
2264
  /* guard:allow-raw-color - standalone reset page has no app theme token layer */
@@ -1 +1,3 @@
1
- export declare function runFrameworkReleaseMigrations(_nitroApp?: unknown): Promise<void>;
1
+ export declare function runFrameworkReleaseMigrations(_nitroApp?: unknown, options?: {
2
+ requirePostgres?: boolean;
3
+ }): Promise<void>;
@@ -11,21 +11,23 @@ import { runFrameworkSchemaEnsures } from "./release-schema.js";
11
11
  * still reports success — `Applied migration ...` lines, a zero exit, a
12
12
  * published deploy — so this has to fail here or it fails silently forever.
13
13
  *
14
- * Scoped to `CONTEXT=production` on purpose. The beta lane deliberately builds
14
+ * Explicit production commands always require a PostgreSQL target. Other callers
15
+ * are scoped to `CONTEXT=production`: the beta lane deliberately builds
15
16
  * with `AGENT_NATIVE_RUN_RELEASE_MIGRATIONS=1` under a branch-deploy context
16
17
  * against masked site secrets, and its database is migrated by the beta
17
18
  * publish step — so keying off that flag would fail every beta deploy while
18
19
  * never guarding the production one this exists for.
19
20
  */
20
- function assertReleaseMigrationTargetsRemoteDatabase() {
21
- if (getAppConfig().migration.deployContext !== "production")
21
+ function assertReleaseMigrationTargetsRemoteDatabase(requirePostgres) {
22
+ if (!requirePostgres &&
23
+ getAppConfig().migration.deployContext !== "production")
22
24
  return;
23
25
  const url = getDatabaseUrl();
24
26
  // `isLocalDatabase()` alone is not enough. Netlify hands the CLI a MASKED
25
27
  // secret ("****************uire") outside its own build infra, and that is
26
28
  // neither empty nor a local URL — so it reads as "not local" while being
27
29
  // unconnectable. Require a real remote scheme too.
28
- if (url.includes("://"))
30
+ if (/^postgres(?:ql)?:\/\//i.test(url))
29
31
  return;
30
32
  throw new Error(`Release migrations resolved to an unusable database (${describeReleaseMigrationUrl(url)}). ` +
31
33
  "In a production deploy the schema must be applied to the same remote " +
@@ -50,7 +52,7 @@ function shouldLogReleaseTiming() {
50
52
  process.env.VITEST !== "true" &&
51
53
  process.env.VITEST !== "1");
52
54
  }
53
- export async function runFrameworkReleaseMigrations(_nitroApp) {
55
+ export async function runFrameworkReleaseMigrations(_nitroApp, options = {}) {
54
56
  const logTiming = shouldLogReleaseTiming();
55
57
  const startedAt = Date.now();
56
58
  if (logTiming) {
@@ -58,7 +60,7 @@ export async function runFrameworkReleaseMigrations(_nitroApp) {
58
60
  }
59
61
  let lockWaitMs;
60
62
  try {
61
- assertReleaseMigrationTargetsRemoteDatabase();
63
+ assertReleaseMigrationTargetsRemoteDatabase(options.requirePostgres === true);
62
64
  // One lease for the whole release, so two deploys against one database
63
65
  // never interleave.
64
66
  await withStoreMigrationLock(async () => {
@@ -164,8 +164,14 @@ const STATIC_ASSET_PATTERNS = [
164
164
  "/favicon.ico",
165
165
  "/favicon.svg",
166
166
  "/manifest.json",
167
- "/icon-*.svg",
168
- "/agent-native-*.svg",
167
+ "/icon-180.svg",
168
+ "/icon-192.svg",
169
+ "/icon-512.svg",
170
+ "/agent-native-icon-dark.svg",
171
+ "/agent-native-icon-light.svg",
172
+ "/agent-native-icon-light-512.svg",
173
+ "/agent-native-logo-dark.svg",
174
+ "/agent-native-logo-light.svg",
169
175
  "/library-presets/**",
170
176
  ];
171
177
  function normalizeBasePath(basePath) {
@@ -41,7 +41,7 @@
41
41
  -Choose the primary workflow and route before editing. Add a domain route under `app/routes/` and set `app.homePath` in `server/plugins/agent-native-email-branding.ts` with `defineAppConfig`. The default Get started page lives at `/home`, and Chat stays at `/chat`; edit `app/routes/home.tsx` when replacing that page with a domain-specific home. Add a static link in `app/components/layout/Sidebar.tsx` before `ChatThreadsSection`; `Header.tsx` uses `APP_TITLE` elsewhere. Shared sidebar and AgentInspector remain.
42
42
  +Choose the primary workflow and route before editing. Build on the blank canvas in `app/routes/_index.tsx` or add domain routes under `app/routes/`. This starter has no Chat route, sidebar, header, or agent rail; add only the navigation the product needs. Set `app.homePath` in the existing `defineAppConfig` in `server/plugins/config.ts` as `DEVELOPING.md` describes.
43
43
 
44
- -Use `adding-a-feature` for functional parity and `frontend-design` for a user-facing screen. Keep feature guidance here; do not rewrite `README.md` or `DESIGN.md`. After all edits, run one typecheck, one doctor check, and one browser smoke of the primary workflow, including overlap rejection, cancellation freeing the slot, and authenticated landing. Skip production build and extra test suites.
44
+ -Use `adding-a-feature` for functional parity and `frontend-design` for a user-facing screen. Keep feature guidance here; do not rewrite `README.md` or `DESIGN.md`. After all edits, run one typecheck, one doctor check, and one browser smoke of the primary workflow and its authenticated landing. Skip production build and extra test suites.
45
45
  +Use `adding-a-feature` for functional parity and `frontend-design` for a user-facing screen. Keep feature guidance here and update `README.md` as `DEVELOPING.md` describes. After all edits, run one typecheck, one doctor check, and one browser smoke of the primary workflow and its authenticated landing. Skip production build and extra test suites.
46
46
 
47
47
  Before building common workspace or agent UI, read `agent-native-toolkit`; for supported customization, read `customizing-agent-native`.
@@ -1,13 +1,14 @@
1
1
  ===================================================================
2
2
  --- a/server/plugins/auth.ts
3
3
  +++ b/server/plugins/auth.ts
4
- @@ -1,7 +1,8 @@
4
+ @@ -1,8 +1,8 @@
5
5
  -import { createToolkitAuthPlugin } from "@agent-native/toolkit/app/auth/server";
6
6
  +import { createAuthPlugin } from "@agent-native/core/server";
7
7
 
8
8
  -export default createToolkitAuthPlugin({
9
9
  - workspaceAppPublicPaths: ["/"],
10
10
  - rootAuth: true,
11
+ - // Remove this option to use built-in marketing, or supply an object for custom copy.
11
12
  - marketing: false,
12
13
  +// Email/password auth with optional Google OAuth (enabled automatically when
13
14
  +// GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set). No marketing copy — a
@@ -26,6 +26,6 @@ For custom branding, keep `server/plugins/agent-native-email-branding.ts` aligne
26
26
 
27
27
  Choose the primary workflow and route before editing. Add a domain route under `app/routes/` and set `app.homePath` in `server/plugins/agent-native-email-branding.ts` with `defineAppConfig`. The default Get started page lives at `/home`, and Chat stays at `/chat`; edit `app/routes/home.tsx` when replacing that page with a domain-specific home. Add a static link in `app/components/layout/Sidebar.tsx` before `ChatThreadsSection`; `Header.tsx` uses `APP_TITLE` elsewhere. Shared sidebar and AgentInspector remain.
28
28
 
29
- Use `adding-a-feature` for functional parity and `frontend-design` for a user-facing screen. Keep feature guidance here; do not rewrite `README.md` or `DESIGN.md`. After all edits, run one typecheck, one doctor check, and one browser smoke of the primary workflow, including overlap rejection, cancellation freeing the slot, and authenticated landing. Skip production build and extra test suites.
29
+ Use `adding-a-feature` for functional parity and `frontend-design` for a user-facing screen. Keep feature guidance here; do not rewrite `README.md` or `DESIGN.md`. After all edits, run one typecheck, one doctor check, and one browser smoke of the primary workflow and its authenticated landing. Skip production build and extra test suites.
30
30
 
31
31
  Before building common workspace or agent UI, read `agent-native-toolkit`; for supported customization, read `customizing-agent-native`.
@@ -403,7 +403,7 @@ export function Sidebar({
403
403
  <nav
404
404
  aria-label={t("navigation.navigation")}
405
405
  className={cn(
406
- "flex min-h-0 flex-1 flex-col",
406
+ "flex min-h-0 flex-1 flex-col overflow-y-auto",
407
407
  collapsed ? "items-center gap-1 px-1 py-2" : "pt-1",
408
408
  )}
409
409
  >
@@ -9,7 +9,7 @@ loadEnv();
9
9
 
10
10
  async function main(): Promise<void> {
11
11
  await withMigrationRuntime(async () => {
12
- await runFrameworkReleaseMigrations(null);
12
+ await runFrameworkReleaseMigrations(null, { requirePostgres: true });
13
13
  await runAppReleaseMigrations();
14
14
  });
15
15
  }
@@ -3,5 +3,6 @@ import { createToolkitAuthPlugin } from "@agent-native/toolkit/app/auth/server";
3
3
  export default createToolkitAuthPlugin({
4
4
  workspaceAppPublicPaths: ["/"],
5
5
  rootAuth: true,
6
+ // Remove this option to use built-in marketing, or supply an object for custom copy.
6
7
  marketing: false,
7
8
  });
@@ -2,47 +2,6 @@ overrides:
2
2
  "@assistant-ui/store": ">=0.2.9 <0.2.14"
3
3
  "@assistant-ui/tap": "^0.5.14"
4
4
  nf3: "0.3.17"
5
- "@tiptap/core": "3.28.0"
6
- "@tiptap/extension-blockquote": "3.28.0"
7
- "@tiptap/extension-bold": "3.28.0"
8
- "@tiptap/extension-bubble-menu": "3.28.0"
9
- "@tiptap/extension-bullet-list": "3.28.0"
10
- "@tiptap/extension-code": "3.28.0"
11
- "@tiptap/extension-code-block": "3.28.0"
12
- "@tiptap/extension-code-block-lowlight": "3.28.0"
13
- "@tiptap/extension-collaboration": "3.28.0"
14
- "@tiptap/extension-collaboration-caret": "3.28.0"
15
- "@tiptap/extension-color": "3.28.0"
16
- "@tiptap/extension-document": "3.28.0"
17
- "@tiptap/extension-dropcursor": "3.28.0"
18
- "@tiptap/extension-floating-menu": "3.28.0"
19
- "@tiptap/extension-gapcursor": "3.28.0"
20
- "@tiptap/extension-hard-break": "3.28.0"
21
- "@tiptap/extension-heading": "3.28.0"
22
- "@tiptap/extension-horizontal-rule": "3.28.0"
23
- "@tiptap/extension-image": "3.28.0"
24
- "@tiptap/extension-italic": "3.28.0"
25
- "@tiptap/extension-link": "3.28.0"
26
- "@tiptap/extension-list": "3.28.0"
27
- "@tiptap/extension-list-item": "3.28.0"
28
- "@tiptap/extension-list-keymap": "3.28.0"
29
- "@tiptap/extension-ordered-list": "3.28.0"
30
- "@tiptap/extension-paragraph": "3.28.0"
31
- "@tiptap/extension-placeholder": "3.28.0"
32
- "@tiptap/extension-strike": "3.28.0"
33
- "@tiptap/extension-table": "3.28.0"
34
- "@tiptap/extension-table-cell": "3.28.0"
35
- "@tiptap/extension-table-header": "3.28.0"
36
- "@tiptap/extension-table-row": "3.28.0"
37
- "@tiptap/extension-task-item": "3.28.0"
38
- "@tiptap/extension-task-list": "3.28.0"
39
- "@tiptap/extension-text": "3.28.0"
40
- "@tiptap/extension-text-style": "3.28.0"
41
- "@tiptap/extension-underline": "3.28.0"
42
- "@tiptap/extensions": "3.28.0"
43
- "@tiptap/pm": "3.28.0"
44
- "@tiptap/react": "3.28.0"
45
- "@tiptap/starter-kit": "3.28.0"
46
5
 
47
6
  allowBuilds:
48
7
  esbuild: true
@@ -9,7 +9,7 @@ loadEnv();
9
9
 
10
10
  async function main(): Promise<void> {
11
11
  await withMigrationRuntime(async () => {
12
- await runFrameworkReleaseMigrations(null);
12
+ await runFrameworkReleaseMigrations(null, { requirePostgres: true });
13
13
  await runAppReleaseMigrations();
14
14
  });
15
15
  }
@@ -27,47 +27,6 @@ overrides:
27
27
  esbuild: "^0.28.1"
28
28
  srvx: "^0.12.4"
29
29
  zod: "^4.4.3"
30
- "@tiptap/core": "3.28.0"
31
- "@tiptap/extension-blockquote": "3.28.0"
32
- "@tiptap/extension-bold": "3.28.0"
33
- "@tiptap/extension-bubble-menu": "3.28.0"
34
- "@tiptap/extension-bullet-list": "3.28.0"
35
- "@tiptap/extension-code": "3.28.0"
36
- "@tiptap/extension-code-block": "3.28.0"
37
- "@tiptap/extension-code-block-lowlight": "3.28.0"
38
- "@tiptap/extension-collaboration": "3.28.0"
39
- "@tiptap/extension-collaboration-caret": "3.28.0"
40
- "@tiptap/extension-color": "3.28.0"
41
- "@tiptap/extension-document": "3.28.0"
42
- "@tiptap/extension-dropcursor": "3.28.0"
43
- "@tiptap/extension-floating-menu": "3.28.0"
44
- "@tiptap/extension-gapcursor": "3.28.0"
45
- "@tiptap/extension-hard-break": "3.28.0"
46
- "@tiptap/extension-heading": "3.28.0"
47
- "@tiptap/extension-horizontal-rule": "3.28.0"
48
- "@tiptap/extension-image": "3.28.0"
49
- "@tiptap/extension-italic": "3.28.0"
50
- "@tiptap/extension-link": "3.28.0"
51
- "@tiptap/extension-list": "3.28.0"
52
- "@tiptap/extension-list-item": "3.28.0"
53
- "@tiptap/extension-list-keymap": "3.28.0"
54
- "@tiptap/extension-ordered-list": "3.28.0"
55
- "@tiptap/extension-paragraph": "3.28.0"
56
- "@tiptap/extension-placeholder": "3.28.0"
57
- "@tiptap/extension-strike": "3.28.0"
58
- "@tiptap/extension-table": "3.28.0"
59
- "@tiptap/extension-table-cell": "3.28.0"
60
- "@tiptap/extension-table-header": "3.28.0"
61
- "@tiptap/extension-table-row": "3.28.0"
62
- "@tiptap/extension-task-item": "3.28.0"
63
- "@tiptap/extension-task-list": "3.28.0"
64
- "@tiptap/extension-text": "3.28.0"
65
- "@tiptap/extension-text-style": "3.28.0"
66
- "@tiptap/extension-underline": "3.28.0"
67
- "@tiptap/extensions": "3.28.0"
68
- "@tiptap/pm": "3.28.0"
69
- "@tiptap/react": "3.28.0"
70
- "@tiptap/starter-kit": "3.28.0"
71
30
 
72
31
  packageExtensions:
73
32
  "node-pty@*":
@@ -14,12 +14,17 @@ const MAX_BATCH_SIZE = 50;
14
14
  const SEND_TIMEOUT_MS = 10_000;
15
15
  const QUEUE_KEY = Symbol.for("@agent-native/core/tracking.queue");
16
16
  const TIMER_KEY = Symbol.for("@agent-native/core/tracking.timer");
17
+ const IN_FLIGHT_KEY = Symbol.for("@agent-native/core/tracking.in-flight");
17
18
  function getQueue() {
18
19
  const g = globalThis;
19
20
  if (!g[QUEUE_KEY])
20
21
  g[QUEUE_KEY] = [];
21
22
  return g[QUEUE_KEY];
22
23
  }
24
+ function getInFlight() {
25
+ const global = globalThis;
26
+ return (global[IN_FLIGHT_KEY] ??= new Set());
27
+ }
23
28
  function getTimer() {
24
29
  const g = globalThis;
25
30
  return g[TIMER_KEY] ?? null;
@@ -51,14 +56,21 @@ function drainQueue() {
51
56
  }
52
57
  const queue = getQueue();
53
58
  const batch = queue.splice(0, queue.length);
54
- return Promise.all(batch.map((item) =>
55
- // Whichever call's frame drains the queue, the events are not its writes.
56
- withoutWriteObservation(() => fetch(item.url, {
57
- method: "POST",
58
- headers: { "Content-Type": "application/json", ...item.headers },
59
- body: item.body,
60
- signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
61
- })).then(() => undefined, () => undefined)));
59
+ const inFlight = getInFlight();
60
+ for (const item of batch) {
61
+ // Whichever call's frame drains the queue, the events are not its writes.
62
+ const send = withoutWriteObservation(() => fetch(item.url, {
63
+ method: "POST",
64
+ headers: { "Content-Type": "application/json", ...item.headers },
65
+ body: item.body,
66
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
67
+ })).then(() => undefined, () => undefined);
68
+ inFlight.add(send);
69
+ void send.finally(() => inFlight.delete(send));
70
+ }
71
+ // Serverless sends leave the queue immediately. A later flush must still
72
+ // hold the request's background lifetime until those sends settle.
73
+ return Promise.all([...inFlight]);
62
74
  }
63
75
  function isLocalhostUrl(value) {
64
76
  if (!value || !value.trim())
@@ -3,6 +3,7 @@ import { defineAction } from "../../action.js";
3
3
  import { listAccessibleAutomationDefinitions, } from "../../automations/service.js";
4
4
  import { hasStalePause, isPausedByFramework, } from "../../jobs/automation-outcome.js";
5
5
  import { describeCron, effectiveTimezone, isValidCron, nextOccurrence, } from "../../jobs/cron.js";
6
+ import { isRecoveredFactoryJob, jobBelongsToApp, } from "../../jobs/frontmatter.js";
6
7
  import { listLatestAutomationRuns } from "../../jobs/run-history.js";
7
8
  function nextRun(meta) {
8
9
  if (!meta.enabled)
@@ -111,7 +112,12 @@ export default defineAction({
111
112
  deliveryTenantId: meta.deliveryTenantId ?? null,
112
113
  canUpdate,
113
114
  effectiveRole,
114
- capabilities,
115
+ capabilities: {
116
+ ...capabilities,
117
+ canOperate: capabilities.canOperate &&
118
+ (jobBelongsToApp(meta, ctx.appId) ||
119
+ isRecoveredFactoryJob(meta, resource.path, ctx.appId, resource.owner)),
120
+ },
115
121
  sharing,
116
122
  creator,
117
123
  };