@agent-native/core 0.7.81 → 0.7.83

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 (257) hide show
  1. package/dist/action.d.ts +8 -0
  2. package/dist/action.d.ts.map +1 -1
  3. package/dist/action.js +4 -0
  4. package/dist/action.js.map +1 -1
  5. package/dist/agent/production-agent.d.ts +12 -2
  6. package/dist/agent/production-agent.d.ts.map +1 -1
  7. package/dist/agent/production-agent.js +58 -20
  8. package/dist/agent/production-agent.js.map +1 -1
  9. package/dist/agent/run-manager.d.ts +8 -1
  10. package/dist/agent/run-manager.d.ts.map +1 -1
  11. package/dist/agent/run-manager.js +11 -12
  12. package/dist/agent/run-manager.js.map +1 -1
  13. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  14. package/dist/agent/thread-data-builder.js +13 -17
  15. package/dist/agent/thread-data-builder.js.map +1 -1
  16. package/dist/agent/types.d.ts +4 -0
  17. package/dist/agent/types.d.ts.map +1 -1
  18. package/dist/agent/types.js.map +1 -1
  19. package/dist/application-state/handlers.d.ts.map +1 -1
  20. package/dist/application-state/handlers.js +3 -8
  21. package/dist/application-state/handlers.js.map +1 -1
  22. package/dist/application-state/script-helpers.d.ts +2 -4
  23. package/dist/application-state/script-helpers.d.ts.map +1 -1
  24. package/dist/application-state/script-helpers.js +10 -47
  25. package/dist/application-state/script-helpers.js.map +1 -1
  26. package/dist/cli/create.d.ts +1 -1
  27. package/dist/cli/create.d.ts.map +1 -1
  28. package/dist/cli/create.js +35 -10
  29. package/dist/cli/create.js.map +1 -1
  30. package/dist/cli/workspace-dev.js +78 -15
  31. package/dist/cli/workspace-dev.js.map +1 -1
  32. package/dist/client/AgentPanel.d.ts.map +1 -1
  33. package/dist/client/AgentPanel.js +6 -2
  34. package/dist/client/AgentPanel.js.map +1 -1
  35. package/dist/client/AssistantChat.d.ts +0 -15
  36. package/dist/client/AssistantChat.d.ts.map +1 -1
  37. package/dist/client/AssistantChat.js +69 -57
  38. package/dist/client/AssistantChat.js.map +1 -1
  39. package/dist/client/ConnectBuilderCard.d.ts +7 -1
  40. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  41. package/dist/client/ConnectBuilderCard.js +46 -5
  42. package/dist/client/ConnectBuilderCard.js.map +1 -1
  43. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  44. package/dist/client/ErrorBoundary.js +20 -5
  45. package/dist/client/ErrorBoundary.js.map +1 -1
  46. package/dist/client/FeedbackButton.d.ts +3 -2
  47. package/dist/client/FeedbackButton.d.ts.map +1 -1
  48. package/dist/client/FeedbackButton.js +23 -15
  49. package/dist/client/FeedbackButton.js.map +1 -1
  50. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  51. package/dist/client/agent-chat-adapter.js +303 -169
  52. package/dist/client/agent-chat-adapter.js.map +1 -1
  53. package/dist/client/builder-frame.d.ts +36 -0
  54. package/dist/client/builder-frame.d.ts.map +1 -1
  55. package/dist/client/builder-frame.js +80 -9
  56. package/dist/client/builder-frame.js.map +1 -1
  57. package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
  58. package/dist/client/composer/ComposerPlusMenu.js +7 -2
  59. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  60. package/dist/client/composer/PastedTextChip.d.ts +9 -0
  61. package/dist/client/composer/PastedTextChip.d.ts.map +1 -0
  62. package/dist/client/composer/PastedTextChip.js +47 -0
  63. package/dist/client/composer/PastedTextChip.js.map +1 -0
  64. package/dist/client/composer/PromptComposer.d.ts +4 -2
  65. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  66. package/dist/client/composer/PromptComposer.js +33 -5
  67. package/dist/client/composer/PromptComposer.js.map +1 -1
  68. package/dist/client/composer/TiptapComposer.d.ts +13 -1
  69. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  70. package/dist/client/composer/TiptapComposer.js +61 -16
  71. package/dist/client/composer/TiptapComposer.js.map +1 -1
  72. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  73. package/dist/client/composer/VoiceButton.js +5 -1
  74. package/dist/client/composer/VoiceButton.js.map +1 -1
  75. package/dist/client/composer/pasted-text.d.ts +6 -0
  76. package/dist/client/composer/pasted-text.d.ts.map +1 -0
  77. package/dist/client/composer/pasted-text.js +49 -0
  78. package/dist/client/composer/pasted-text.js.map +1 -0
  79. package/dist/client/composer/useVoiceDictation.d.ts +1 -0
  80. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  81. package/dist/client/composer/useVoiceDictation.js +18 -0
  82. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  83. package/dist/client/index.d.ts +0 -1
  84. package/dist/client/index.d.ts.map +1 -1
  85. package/dist/client/index.js +0 -1
  86. package/dist/client/index.js.map +1 -1
  87. package/dist/client/integrations/IntegrationCard.d.ts.map +1 -1
  88. package/dist/client/integrations/IntegrationCard.js +14 -2
  89. package/dist/client/integrations/IntegrationCard.js.map +1 -1
  90. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  91. package/dist/client/integrations/IntegrationsPanel.js +23 -4
  92. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  93. package/dist/client/notifications/NotificationsBell.d.ts.map +1 -1
  94. package/dist/client/notifications/NotificationsBell.js +4 -42
  95. package/dist/client/notifications/NotificationsBell.js.map +1 -1
  96. package/dist/client/org/OrgSwitcher.d.ts +4 -6
  97. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  98. package/dist/client/org/OrgSwitcher.js +84 -74
  99. package/dist/client/org/OrgSwitcher.js.map +1 -1
  100. package/dist/client/org/TeamPage.d.ts.map +1 -1
  101. package/dist/client/org/TeamPage.js +3 -154
  102. package/dist/client/org/TeamPage.js.map +1 -1
  103. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  104. package/dist/client/resources/ResourcesPanel.js +13 -35
  105. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  106. package/dist/client/settings/SettingsPanel.js +1 -1
  107. package/dist/client/settings/SettingsPanel.js.map +1 -1
  108. package/dist/client/settings/useBuilderStatus.d.ts +6 -0
  109. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  110. package/dist/client/settings/useBuilderStatus.js +3 -0
  111. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  112. package/dist/client/sse-event-processor.d.ts +15 -1
  113. package/dist/client/sse-event-processor.d.ts.map +1 -1
  114. package/dist/client/sse-event-processor.js +58 -54
  115. package/dist/client/sse-event-processor.js.map +1 -1
  116. package/dist/client/tools/ToolEditor.d.ts.map +1 -1
  117. package/dist/client/tools/ToolEditor.js +34 -4
  118. package/dist/client/tools/ToolEditor.js.map +1 -1
  119. package/dist/client/tools/ToolViewer.d.ts.map +1 -1
  120. package/dist/client/tools/ToolViewer.js +20 -1
  121. package/dist/client/tools/ToolViewer.js.map +1 -1
  122. package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
  123. package/dist/client/tools/ToolsListPage.js +2 -1
  124. package/dist/client/tools/ToolsListPage.js.map +1 -1
  125. package/dist/client/tools/ToolsSidebarSection.js +1 -1
  126. package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
  127. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  128. package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
  129. package/dist/client/use-chat-threads.d.ts.map +1 -1
  130. package/dist/client/use-chat-threads.js +7 -2
  131. package/dist/client/use-chat-threads.js.map +1 -1
  132. package/dist/collab/client.d.ts.map +1 -1
  133. package/dist/collab/client.js +26 -7
  134. package/dist/collab/client.js.map +1 -1
  135. package/dist/jobs/scheduler.js +0 -4
  136. package/dist/jobs/scheduler.js.map +1 -1
  137. package/dist/oauth-tokens/store.d.ts +0 -4
  138. package/dist/oauth-tokens/store.d.ts.map +1 -1
  139. package/dist/oauth-tokens/store.js +3 -24
  140. package/dist/oauth-tokens/store.js.map +1 -1
  141. package/dist/observability/routes.d.ts.map +1 -1
  142. package/dist/observability/routes.js +1 -9
  143. package/dist/observability/routes.js.map +1 -1
  144. package/dist/onboarding/default-steps.js +1 -1
  145. package/dist/onboarding/default-steps.js.map +1 -1
  146. package/dist/onboarding/plugin.d.ts.map +1 -1
  147. package/dist/onboarding/plugin.js +1 -8
  148. package/dist/onboarding/plugin.js.map +1 -1
  149. package/dist/org/accept-pending.d.ts.map +1 -1
  150. package/dist/org/accept-pending.js +1 -2
  151. package/dist/org/accept-pending.js.map +1 -1
  152. package/dist/org/context.d.ts +0 -2
  153. package/dist/org/context.d.ts.map +1 -1
  154. package/dist/org/context.js +0 -5
  155. package/dist/org/context.js.map +1 -1
  156. package/dist/resources/script-helpers.d.ts +3 -4
  157. package/dist/resources/script-helpers.d.ts.map +1 -1
  158. package/dist/resources/script-helpers.js +8 -15
  159. package/dist/resources/script-helpers.js.map +1 -1
  160. package/dist/scripts/chat/search-chats.d.ts.map +1 -1
  161. package/dist/scripts/chat/search-chats.js +4 -4
  162. package/dist/scripts/chat/search-chats.js.map +1 -1
  163. package/dist/scripts/manage-agent-loop-settings.js +2 -2
  164. package/dist/scripts/manage-agent-loop-settings.js.map +1 -1
  165. package/dist/scripts/resources/delete-memory.d.ts.map +1 -1
  166. package/dist/scripts/resources/delete-memory.js +4 -2
  167. package/dist/scripts/resources/delete-memory.js.map +1 -1
  168. package/dist/scripts/resources/delete.d.ts.map +1 -1
  169. package/dist/scripts/resources/delete.js +11 -4
  170. package/dist/scripts/resources/delete.js.map +1 -1
  171. package/dist/scripts/resources/list.d.ts.map +1 -1
  172. package/dist/scripts/resources/list.js +5 -3
  173. package/dist/scripts/resources/list.js.map +1 -1
  174. package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -1
  175. package/dist/scripts/resources/migrate-learnings.js +5 -2
  176. package/dist/scripts/resources/migrate-learnings.js.map +1 -1
  177. package/dist/scripts/resources/read.d.ts.map +1 -1
  178. package/dist/scripts/resources/read.js +4 -2
  179. package/dist/scripts/resources/read.js.map +1 -1
  180. package/dist/scripts/resources/save-memory.d.ts.map +1 -1
  181. package/dist/scripts/resources/save-memory.js +4 -2
  182. package/dist/scripts/resources/save-memory.js.map +1 -1
  183. package/dist/scripts/resources/write.d.ts.map +1 -1
  184. package/dist/scripts/resources/write.js +11 -4
  185. package/dist/scripts/resources/write.js.map +1 -1
  186. package/dist/secrets/onboarding.d.ts.map +1 -1
  187. package/dist/secrets/onboarding.js +1 -9
  188. package/dist/secrets/onboarding.js.map +1 -1
  189. package/dist/secrets/routes.d.ts.map +1 -1
  190. package/dist/secrets/routes.js +2 -7
  191. package/dist/secrets/routes.js.map +1 -1
  192. package/dist/server/action-discovery.d.ts +15 -0
  193. package/dist/server/action-discovery.d.ts.map +1 -1
  194. package/dist/server/action-discovery.js +49 -0
  195. package/dist/server/action-discovery.js.map +1 -1
  196. package/dist/server/agent-chat-plugin.d.ts +5 -0
  197. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  198. package/dist/server/agent-chat-plugin.js +81 -20
  199. package/dist/server/agent-chat-plugin.js.map +1 -1
  200. package/dist/server/agent-discovery.d.ts.map +1 -1
  201. package/dist/server/agent-discovery.js +5 -7
  202. package/dist/server/agent-discovery.js.map +1 -1
  203. package/dist/server/auth.d.ts +16 -20
  204. package/dist/server/auth.d.ts.map +1 -1
  205. package/dist/server/auth.js +115 -333
  206. package/dist/server/auth.js.map +1 -1
  207. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  208. package/dist/server/core-routes-plugin.js +23 -16
  209. package/dist/server/core-routes-plugin.js.map +1 -1
  210. package/dist/server/credential-provider.d.ts.map +1 -1
  211. package/dist/server/credential-provider.js +1 -2
  212. package/dist/server/credential-provider.js.map +1 -1
  213. package/dist/server/google-oauth.d.ts +14 -2
  214. package/dist/server/google-oauth.d.ts.map +1 -1
  215. package/dist/server/google-oauth.js +32 -10
  216. package/dist/server/google-oauth.js.map +1 -1
  217. package/dist/server/index.d.ts +3 -3
  218. package/dist/server/index.d.ts.map +1 -1
  219. package/dist/server/index.js +2 -2
  220. package/dist/server/index.js.map +1 -1
  221. package/dist/server/oauth-helpers.d.ts +2 -4
  222. package/dist/server/oauth-helpers.d.ts.map +1 -1
  223. package/dist/server/oauth-helpers.js +2 -4
  224. package/dist/server/oauth-helpers.js.map +1 -1
  225. package/dist/server/transcribe-voice.d.ts.map +1 -1
  226. package/dist/server/transcribe-voice.js +2 -4
  227. package/dist/server/transcribe-voice.js.map +1 -1
  228. package/dist/triggers/dispatcher.d.ts.map +1 -1
  229. package/dist/triggers/dispatcher.js +0 -3
  230. package/dist/triggers/dispatcher.js.map +1 -1
  231. package/dist/vite/client.d.ts.map +1 -1
  232. package/dist/vite/client.js +13 -0
  233. package/dist/vite/client.js.map +1 -1
  234. package/docs/content/actions.md +1 -0
  235. package/docs/content/authentication.md +3 -20
  236. package/docs/content/creating-templates.md +1 -1
  237. package/docs/content/deployment.md +0 -1
  238. package/docs/content/security.md +0 -1
  239. package/docs/content/template-analytics.md +10 -0
  240. package/docs/content/template-calendar.md +10 -0
  241. package/docs/content/template-clips.md +10 -0
  242. package/docs/content/template-content.md +11 -1
  243. package/docs/content/template-dispatch.md +10 -0
  244. package/docs/content/template-forms.md +10 -0
  245. package/docs/content/template-mail.md +10 -0
  246. package/docs/content/template-slides.md +11 -1
  247. package/docs/content/template-starter.md +11 -1
  248. package/docs/content/template-video.md +10 -0
  249. package/package.json +1 -1
  250. package/dist/client/dev-mode.d.ts +0 -14
  251. package/dist/client/dev-mode.d.ts.map +0 -1
  252. package/dist/client/dev-mode.js +0 -14
  253. package/dist/client/dev-mode.js.map +0 -1
  254. package/dist/server/local-migration.d.ts +0 -41
  255. package/dist/server/local-migration.d.ts.map +0 -1
  256. package/dist/server/local-migration.js +0 -235
  257. package/dist/server/local-migration.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { defineEventHandler, getMethod, getQuery, getRequestIP, sendRedirect, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, } from "h3";
2
+ import { defineEventHandler, getMethod, getQuery, getRequestIP, sendRedirect, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, getHeader, } from "h3";
3
3
  // In h3 v2, `event.req` IS the web Request — but in Nitro's dev server (srvx
4
4
  // runtime), event.url and event.req share the same underlying URL object.
5
5
  // When registerMiddleware strips the mount prefix from event.url.pathname, it
@@ -34,11 +34,10 @@ function toWebRequest(event) {
34
34
  }
35
35
  return req;
36
36
  }
37
- import { getDbExec, isPostgres, intType, isLocalDatabase, retryOnDdlRace, } from "../db/client.js";
37
+ import { getDbExec, isPostgres, intType, retryOnDdlRace, } from "../db/client.js";
38
38
  import { getBetterAuth, getBetterAuthSync } from "./better-auth-instance.js";
39
39
  import { getAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
40
40
  import { getOnboardingHtml, getResetPasswordHtml } from "./onboarding-html.js";
41
- import { migrateLocalUserData } from "./local-migration.js";
42
41
  import { readBody } from "../server/h3-helpers.js";
43
42
  import { readDesktopSso, writeDesktopSso, clearDesktopSso, } from "./desktop-sso.js";
44
43
  import { isElectron as isElectronRequest, getAppBasePath, getAppUrl, encodeOAuthState, decodeOAuthState, createOAuthSession, oauthCallbackResponse, oauthErrorPage, resolveOAuthRedirectUri, isAllowedOAuthRedirectUri, } from "./google-oauth.js";
@@ -82,36 +81,8 @@ function getOAuthStateAppId() {
82
81
  }
83
82
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 30; // 30 days
84
83
  // ---------------------------------------------------------------------------
85
- // AUTH_MODE detection
84
+ // Environment helpers
86
85
  // ---------------------------------------------------------------------------
87
- let _warnedRemoteLocalMode = false;
88
- /**
89
- * Check if the app is in local-only mode (no auth).
90
- *
91
- * Returns true when AUTH_MODE=local is explicitly set.
92
- *
93
- * Local mode is an explicit escape hatch for when you want to guarantee
94
- * no auth is used. In development, getSession() also falls back to
95
- * local@localhost automatically if no other auth method succeeds, so
96
- * apps are always usable without configuration in dev.
97
- *
98
- * Refuses to enable on any non-local database (Postgres, Turso, D1): local
99
- * mode uses a single shared virtual user with no per-machine scoping, so on
100
- * a shared DB every developer would land on the same account and collide.
101
- */
102
- async function isLocalModeEnabled() {
103
- if (process.env.AUTH_MODE !== "local")
104
- return false;
105
- if (!isLocalDatabase()) {
106
- if (!_warnedRemoteLocalMode) {
107
- _warnedRemoteLocalMode = true;
108
- console.warn("[agent-native] AUTH_MODE=local ignored: database is not local SQLite. " +
109
- "local@localhost has no per-user scoping and would collide across developers on a shared DB.");
110
- }
111
- return false;
112
- }
113
- return true;
114
- }
115
86
  /**
116
87
  * Check if we're in a development/test environment.
117
88
  * Used for cookie security settings, not for auth bypass.
@@ -245,6 +216,36 @@ function safeTokenMatch(input, tokens) {
245
216
  }
246
217
  return false;
247
218
  }
219
+ function getBearerSessionToken(event) {
220
+ const auth = getHeader(event, "authorization");
221
+ if (!auth)
222
+ return undefined;
223
+ const match = /^Bearer\s+(.+)$/i.exec(auth.trim());
224
+ return match?.[1]?.trim() || undefined;
225
+ }
226
+ async function getBearerLegacySession(event) {
227
+ const bearerToken = getBearerSessionToken(event);
228
+ if (!bearerToken)
229
+ return null;
230
+ const email = await getSessionEmail(bearerToken);
231
+ return email ? { email, token: bearerToken } : null;
232
+ }
233
+ function shouldExposeSessionTokenInBody(event) {
234
+ const origin = getHeader(event, "origin");
235
+ if (origin && DESKTOP_AUTH_TOKEN_BODY_ORIGINS.has(origin))
236
+ return true;
237
+ // Some native WebViews do not consistently emit an Origin header for
238
+ // programmatic fetches. The desktop app marks same-server requests with
239
+ // X-Request-Source; browsers can only use that cross-origin after our CORS
240
+ // allowlist has approved the origin, and same-origin pages already receive
241
+ // an equivalent httpOnly session cookie on successful login.
242
+ return !origin && getHeader(event, "x-request-source") === "clips-desktop";
243
+ }
244
+ function authLoginResponse(event, token, email) {
245
+ if (!shouldExposeSessionTokenInBody(event))
246
+ return { ok: true };
247
+ return email ? { ok: true, token, email } : { ok: true, token };
248
+ }
248
249
  // ---------------------------------------------------------------------------
249
250
  // Legacy session store — kept for backward compat (addSession/getSessionEmail)
250
251
  // Used by google-oauth.ts for mobile deep linking session creation.
@@ -348,7 +349,6 @@ export async function getSessionEmail(token) {
348
349
  // getSession — the auth contract
349
350
  // ---------------------------------------------------------------------------
350
351
  let customGetSession = null;
351
- let authDisabledMode = false;
352
352
  let _authGuardConfig = null;
353
353
  const _genericGoogleOAuthRoutesEnabled = new WeakMap();
354
354
  function setGenericGoogleOAuthRoutesEnabled(app, enabled) {
@@ -361,6 +361,10 @@ function areGenericGoogleOAuthRoutesEnabled(app) {
361
361
  }
362
362
  const _desktopExchanges = new Map();
363
363
  const DESKTOP_EXCHANGE_ERROR_PREFIX = "__error__::";
364
+ const DESKTOP_AUTH_TOKEN_BODY_ORIGINS = new Set([
365
+ "tauri://localhost",
366
+ "http://localhost:1420",
367
+ ]);
364
368
  // 5-minute TTL for exchange entries (short — single-use tokens).
365
369
  const DESKTOP_EXCHANGE_TTL_MS = 5 * 60 * 1000;
366
370
  export function setDesktopExchange(flowId, token, email) {
@@ -477,17 +481,6 @@ export async function runAuthGuard(event) {
477
481
  return; // Auth not mounted (local mode, etc.)
478
482
  return _authGuardFn(event);
479
483
  }
480
- /**
481
- * The framework's dev-mode bypass identity. When `AUTH_MODE=local` (or
482
- * dev-mode falls back), `getSession()` returns `{ email: DEV_MODE_USER_EMAIL }`.
483
- * Production code that needs to check whether the current request is the
484
- * dev-mode user (or filter it out of mailers, dashboards, etc.) should
485
- * compare against this constant instead of inlining the literal —
486
- * `guard-no-localhost-fallback.mjs` blocks the literal everywhere except
487
- * `auth.ts` and a handful of dev-mode helpers.
488
- */
489
- export const DEV_MODE_USER_EMAIL = "local@localhost";
490
- const LOCAL_SESSION = { email: DEV_MODE_USER_EMAIL };
491
484
  // ---------------------------------------------------------------------------
492
485
  // Auth guard factory
493
486
  // ---------------------------------------------------------------------------
@@ -506,9 +499,7 @@ function applyCorsHeaders(event) {
506
499
  // response would be missing the Allow-Origin header and the browser
507
500
  // blocks the response body (making it look like a network error
508
501
  // rather than "unauthenticated").
509
- const reqHeaders = (event.node?.req?.headers ?? {});
510
- const originRaw = reqHeaders["origin"];
511
- const origin = Array.isArray(originRaw) ? originRaw[0] : originRaw;
502
+ const origin = getHeader(event, "origin");
512
503
  if (!origin)
513
504
  return { hasOrigin: false, allowed: true };
514
505
  const allowedOrigin = getAllowedCorsOrigin(origin, {
@@ -524,6 +515,24 @@ function applyCorsHeaders(event) {
524
515
  setResponseHeader(event, "Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF");
525
516
  return { hasOrigin: true, allowed: true };
526
517
  }
518
+ function createAuthCorsHandler() {
519
+ return defineEventHandler((event) => {
520
+ const cors = applyCorsHeaders(event);
521
+ if (getMethod(event) !== "OPTIONS")
522
+ return;
523
+ if (cors.hasOrigin && !cors.allowed) {
524
+ setResponseStatus(event, 403);
525
+ return "";
526
+ }
527
+ setResponseStatus(event, 204);
528
+ return "";
529
+ });
530
+ }
531
+ function mountAuthCorsMiddleware(app) {
532
+ const handler = createAuthCorsHandler();
533
+ app.use("/_agent-native/auth", handler);
534
+ app.use("/_agent-native/google", handler);
535
+ }
527
536
  function createAuthGuardFn() {
528
537
  return async (event) => {
529
538
  const config = _authGuardConfig;
@@ -628,8 +637,8 @@ function createAuthGuardFn() {
628
637
  });
629
638
  }
630
639
  // Auth entry pages are framework-owned pages, not app routes. When a user
631
- // already has a session (including AUTH_MODE=local), redirect them back to
632
- // the mounted app instead of letting React Router try to render /login.
640
+ // already has a session, redirect them back to the mounted app instead of
641
+ // letting React Router try to render /login.
633
642
  if (p === "/login" || p === "/signup") {
634
643
  const session = await getSession(event);
635
644
  if (session) {
@@ -656,6 +665,15 @@ function createAuthGuardFn() {
656
665
  p.endsWith(".woff")) {
657
666
  return;
658
667
  }
668
+ // React Router 7's lazy route discovery fetches `/__manifest?p=...` to
669
+ // resolve manifest patches for `<Link>`s the user might click. The
670
+ // auth fallback returning loginHtml here makes RR fail to parse the
671
+ // body as RSC, surfacing as a console error and (when the visitor
672
+ // already errored elsewhere) blocking the app from rendering. Let it
673
+ // through — it returns a tiny RSC-encoded manifest of the public
674
+ // route tree, no per-user data.
675
+ if (p === "/__manifest")
676
+ return;
659
677
  if (isPublicPath(normalizedUrl, publicPaths))
660
678
  return;
661
679
  const session = await getSession(event);
@@ -687,51 +705,21 @@ function mapBetterAuthSession(baSession) {
687
705
  * Get the current auth session for a request.
688
706
  *
689
707
  * Resolution chain:
690
- * 1. AUTH_MODE=locallocal@localhost (explicit escape hatch)
691
- * 2. AUTH_DISABLED=truelocal@localhost (infrastructure auth)
692
- * 3. ACCESS_TOKEN → check legacy cookie-based token sessions
693
- * 4. BYOA custom getSession delegate to template callback
694
- * 5. Better Auth → check session via Better Auth API (cookie or Bearer)
695
- * 6. Legacy cookie check an_session cookie in legacy sessions table
708
+ * 1. ACCESS_TOKENcheck legacy cookie-based token sessions
709
+ * 2. BYOA custom getSession delegate to template callback
710
+ * 3. Bearer legacy session → check Authorization: Bearer against sessions
711
+ * 4. Better Authcheck session via Better Auth API (cookie or Bearer)
712
+ * 5. Legacy cookie → check an_session cookie in legacy sessions table
713
+ * 6. Desktop SSO broker (Electron loopback only)
696
714
  * 7. Mobile _session query param → promote to cookie
697
- * 8. Dev-mode fallback → local@localhost (never block in development)
715
+ *
716
+ * Returns `null` for unauthenticated requests. There is no dev-mode bypass:
717
+ * local development uses the same Better Auth signup flow as production. The
718
+ * onboarding/sign-in page is served by `runAuthGuard` for any unauthenticated
719
+ * page load.
698
720
  */
699
721
  export async function getSession(event) {
700
- // 1. AUTH_MODE=local explicit local-only mode
701
- if ((await isLocalModeEnabled()) || authDisabledMode) {
702
- // Check for a real session cookie first (e.g. from Google OAuth)
703
- try {
704
- const cookie = getCookie(event, COOKIE_NAME);
705
- if (cookie) {
706
- const email = await getSessionEmail(cookie);
707
- if (email)
708
- return { email, token: cookie };
709
- }
710
- }
711
- catch {
712
- // DB not ready yet
713
- }
714
- // Also try Better Auth session (for users who created an account then went local)
715
- try {
716
- const ba = getBetterAuthSync();
717
- if (ba) {
718
- const baSession = await ba.api.getSession({
719
- headers: event.headers,
720
- });
721
- if (baSession?.user?.email) {
722
- return mapBetterAuthSession(baSession);
723
- }
724
- }
725
- }
726
- catch {
727
- // Better Auth not initialized yet
728
- }
729
- const querySession = await promoteQuerySession(event);
730
- if (querySession)
731
- return querySession;
732
- return LOCAL_SESSION;
733
- }
734
- // 2. ACCESS_TOKEN check (programmatic/agent access)
722
+ // 1. ACCESS_TOKEN check (programmatic/agent access)
735
723
  const accessTokens = getAccessTokens();
736
724
  if (accessTokens.length > 0) {
737
725
  const cookie = getCookie(event, COOKIE_NAME);
@@ -741,11 +729,14 @@ export async function getSession(event) {
741
729
  return { email, token: cookie };
742
730
  }
743
731
  }
744
- // 3. BYOA custom getSession
732
+ // 2. BYOA custom getSession
745
733
  if (customGetSession) {
746
734
  const session = await customGetSession(event);
747
735
  if (session)
748
736
  return session;
737
+ const bearerSession = await getBearerLegacySession(event);
738
+ if (bearerSession)
739
+ return bearerSession;
749
740
  // Desktop SSO broker: even with BYOA auth, fall back to the broker
750
741
  // for Electron requests so cross-template SSO works for custom-auth
751
742
  // templates too. Gated on `readDesktopSsoSafely` so a non-loopback
@@ -757,6 +748,11 @@ export async function getSession(event) {
757
748
  // Fall through to mobile _session check
758
749
  }
759
750
  else {
751
+ // 3. Bearer legacy session. Desktop/native clients can persist a session
752
+ // token outside the WebView cookie jar and attach it to all app requests.
753
+ const bearerSession = await getBearerLegacySession(event);
754
+ if (bearerSession)
755
+ return bearerSession;
760
756
  // 4. Better Auth session (cookie or Bearer token)
761
757
  try {
762
758
  const ba = getBetterAuthSync();
@@ -765,9 +761,6 @@ export async function getSession(event) {
765
761
  headers: event.headers,
766
762
  });
767
763
  if (baSession?.user?.email) {
768
- // Successful real sign-in — clear the upgrade-pending marker so
769
- // the dev fallback becomes reachable again for future local work.
770
- clearUpgradePendingCookie(event);
771
764
  return mapBetterAuthSession(baSession);
772
765
  }
773
766
  }
@@ -780,11 +773,10 @@ export async function getSession(event) {
780
773
  if (cookie) {
781
774
  const email = await getSessionEmail(cookie);
782
775
  if (email) {
783
- clearUpgradePendingCookie(event);
784
776
  return { email, token: cookie };
785
777
  }
786
778
  }
787
- // 5b. Desktop SSO broker fallback.
779
+ // 6. Desktop SSO broker fallback.
788
780
  // Each template in the Electron desktop app has its own database, so
789
781
  // a session token created by one template doesn't resolve in another.
790
782
  // When an Electron request has no resolvable session, trust the
@@ -795,40 +787,13 @@ export async function getSession(event) {
795
787
  // impersonate whichever email last signed into the desktop app.
796
788
  const sso = await readDesktopSsoSafely(event);
797
789
  if (sso?.email) {
798
- clearUpgradePendingCookie(event);
799
790
  return { email: sso.email, token: sso.token };
800
791
  }
801
792
  }
802
- // 6. Mobile WebView bridge — _session query param
793
+ // 7. Mobile WebView bridge — _session query param
803
794
  const querySession = await promoteQuerySession(event);
804
795
  if (querySession)
805
796
  return querySession;
806
- // 7. Dev-mode safety net — in development on a local SQLite database, fall
807
- // back to local@localhost so the app is usable without any auth configuration.
808
- // This prevents 401 errors when Better Auth isn't configured or the user
809
- // simply wants to play around locally.
810
- //
811
- // Gated on isLocalDatabase() because local@localhost has no per-user scoping:
812
- // on a shared DB (Postgres, Turso, D1) this fallback would land every
813
- // developer on the same account and expose each other's data.
814
- //
815
- // STRICT NODE_ENV check: this used to read `isDevEnvironment()` which
816
- // also accepted `NODE_ENV=test`, meaning a misconfigured prod deploy
817
- // started with `NODE_ENV=test` (or undefined NODE_ENV in some CI/build
818
- // contexts) would silently bypass auth entirely. Limiting to the literal
819
- // string "development" closes that footgun. Tests that need this branch
820
- // to fire stub NODE_ENV explicitly to "development".
821
- //
822
- // EXCEPTION: if the user has explicitly exited local mode (clicked "Upgrade
823
- // to real account"), they've signaled they want real auth. The upgrade
824
- // cookie suppresses this fallback so the onboarding/sign-in page is served
825
- // instead of silently re-authenticating them as local@localhost.
826
- if (process.env.NODE_ENV === "development" &&
827
- isLocalDatabase() &&
828
- !isUpgradePending(event) &&
829
- !hasSignInFlag(event)) {
830
- return LOCAL_SESSION;
831
- }
832
797
  return null;
833
798
  }
834
799
  async function promoteQuerySession(event) {
@@ -842,44 +807,6 @@ async function promoteQuerySession(event) {
842
807
  setResponseHeader(event, "Referrer-Policy", "no-referrer");
843
808
  return { email, token: qToken };
844
809
  }
845
- /**
846
- * Cookie set by POST /_agent-native/auth/exit-local-mode so we know the user
847
- * is in the middle of upgrading from local@localhost to a real account.
848
- * While this cookie is present we skip the dev-mode "auto local session"
849
- * fallback so the onboarding/sign-in page can actually render.
850
- * Cleared on successful sign-in/sign-up.
851
- */
852
- const UPGRADE_COOKIE = "an_upgrade_pending";
853
- function isUpgradePending(event) {
854
- try {
855
- return getCookie(event, UPGRADE_COOKIE) === "1";
856
- }
857
- catch {
858
- return false;
859
- }
860
- }
861
- function setUpgradePendingCookie(event) {
862
- setCookie(event, UPGRADE_COOKIE, "1", {
863
- httpOnly: true,
864
- ...crossSiteCookieAttrs(event),
865
- path: "/",
866
- maxAge: 60 * 60, // 1 hour — enough to complete sign-in
867
- });
868
- }
869
- /**
870
- * URL-flag fallback for third-party iframe contexts (e.g. the Builder.io
871
- * editor) where SameSite=Lax cookies from an exit-local-mode POST are not
872
- * delivered on the subsequent reload. TeamPage reloads with ?signin=1 so
873
- * we can reliably suppress the dev-mode local fallback without a cookie.
874
- */
875
- function hasSignInFlag(event) {
876
- try {
877
- return getQuery(event)?.signin === "1";
878
- }
879
- catch {
880
- return false;
881
- }
882
- }
883
810
  function isReadMethod(event) {
884
811
  const method = getMethod(event);
885
812
  return method === "GET" || method === "HEAD";
@@ -905,21 +832,11 @@ function setFrameworkSessionCookie(event, token) {
905
832
  }
906
833
  function isHttpsRequest(event) {
907
834
  try {
908
- const req = event.req ?? event.node?.req;
909
- const headers = req?.headers;
910
- const get = (k) => {
911
- if (!headers)
912
- return undefined;
913
- if (typeof headers.get === "function") {
914
- return headers.get(k) ?? undefined;
915
- }
916
- const v = headers[k];
917
- return Array.isArray(v) ? v[0] : v;
918
- };
919
- const xfProto = get("x-forwarded-proto");
835
+ const xfProto = getHeader(event, "x-forwarded-proto");
920
836
  if (xfProto && String(xfProto).split(",")[0].trim() === "https") {
921
837
  return true;
922
838
  }
839
+ const req = event.req ?? event.node?.req;
923
840
  const url = req?.url;
924
841
  if (typeof url === "string" && url.startsWith("https://"))
925
842
  return true;
@@ -932,14 +849,6 @@ function isHttpsRequest(event) {
932
849
  }
933
850
  return false;
934
851
  }
935
- function clearUpgradePendingCookie(event) {
936
- try {
937
- deleteCookie(event, UPGRADE_COOKIE, { path: "/" });
938
- }
939
- catch {
940
- // ignore
941
- }
942
- }
943
852
  // ---------------------------------------------------------------------------
944
853
  // Public path matching
945
854
  // ---------------------------------------------------------------------------
@@ -1053,57 +962,6 @@ const TOKEN_LOGIN_HTML = `<!DOCTYPE html>
1053
962
  </body>
1054
963
  </html>`;
1055
964
  // ---------------------------------------------------------------------------
1056
- // Local mode route helpers
1057
- // ---------------------------------------------------------------------------
1058
- function localModeMarkerDisabled(event) {
1059
- setResponseStatus(event, 410);
1060
- return {
1061
- error: "The local-mode marker file has been removed. Set AUTH_MODE=local in your local shell or .env only for one-off local development.",
1062
- };
1063
- }
1064
- function exitLocalMode(event) {
1065
- // Mark the browser so getSession's dev-mode fallback won't silently
1066
- // re-authenticate the user as local@localhost on the next request.
1067
- setUpgradePendingCookie(event);
1068
- return { ok: true };
1069
- }
1070
- /**
1071
- * POST /_agent-native/auth/migrate-local-data handler. Exposed here (not
1072
- * inlined in a single mount function) because it must be registered from
1073
- * every auth-mount path — including local-mode and fallback — so the
1074
- * upgrade-from-local flow never 500s when BetterAuth init is skipped or
1075
- * failed. Previously this was only mounted inside mountBetterAuthRoutes()
1076
- * which meant that in local mode (or when BetterAuth failed to init) the
1077
- * request fell through to the Nitro SSR renderer and produced a 500.
1078
- */
1079
- const migrateLocalDataHandler = defineEventHandler(async (event) => {
1080
- if (getMethod(event) !== "POST") {
1081
- setResponseStatus(event, 405);
1082
- return { error: "Method not allowed" };
1083
- }
1084
- const session = await getSession(event);
1085
- if (!session?.email || session.email === "local@localhost") {
1086
- setResponseStatus(event, 401);
1087
- return { error: "Not authenticated as a real account" };
1088
- }
1089
- try {
1090
- const result = await migrateLocalUserData(session.email);
1091
- return { ok: true, ...result };
1092
- }
1093
- catch (e) {
1094
- console.error("[migrate-local-data] Migration threw for", session.email, e);
1095
- setResponseStatus(event, 500);
1096
- return {
1097
- error: e?.message || "Migration failed",
1098
- // Only surface the stack when explicitly enabled. `isDevEnvironment()`
1099
- // returns true on preview deploys and Lambda contexts that forget
1100
- // NODE_ENV=production, which leaked stack traces to clients. Use
1101
- // AGENT_NATIVE_DEBUG_ERRORS=1 for opt-in debug visibility.
1102
- stack: process.env.AGENT_NATIVE_DEBUG_ERRORS === "1" ? e?.stack : undefined,
1103
- };
1104
- }
1105
- });
1106
- // ---------------------------------------------------------------------------
1107
965
  // mountBetterAuthRoutes — Better Auth powered auth with backward-compat routes
1108
966
  // ---------------------------------------------------------------------------
1109
967
  async function mountBetterAuthRoutes(app, options) {
@@ -1470,23 +1328,6 @@ async function mountBetterAuthRoutes(app, options) {
1470
1328
  }
1471
1329
  return response;
1472
1330
  }));
1473
- // POST /_agent-native/auth/local-mode — legacy endpoint kept so old clients
1474
- // receive an explicit error instead of creating a persistent local marker.
1475
- app.use("/_agent-native/auth/local-mode", defineEventHandler(async (event) => {
1476
- if (getMethod(event) !== "POST") {
1477
- setResponseStatus(event, 405);
1478
- return { error: "Method not allowed" };
1479
- }
1480
- return localModeMarkerDisabled(event);
1481
- }));
1482
- // POST /_agent-native/auth/exit-local-mode — switch back to real auth
1483
- app.use("/_agent-native/auth/exit-local-mode", defineEventHandler(async (event) => {
1484
- if (getMethod(event) !== "POST") {
1485
- setResponseStatus(event, 405);
1486
- return { error: "Method not allowed" };
1487
- }
1488
- return exitLocalMode(event);
1489
- }));
1490
1331
  // Backward-compat: POST /_agent-native/auth/login
1491
1332
  app.use("/_agent-native/auth/login", defineEventHandler(async (event) => {
1492
1333
  if (getMethod(event) !== "POST") {
@@ -1510,7 +1351,7 @@ async function mountBetterAuthRoutes(app, options) {
1510
1351
  path: "/",
1511
1352
  maxAge: sessionMaxAge,
1512
1353
  });
1513
- return { ok: true };
1354
+ return authLoginResponse(event, sessionToken, "user");
1514
1355
  }
1515
1356
  // Email/password login via Better Auth
1516
1357
  const email = body?.email?.trim?.()?.toLowerCase?.();
@@ -1538,7 +1379,7 @@ async function mountBetterAuthRoutes(app, options) {
1538
1379
  expiresAt: Date.now() + sessionMaxAge * 1000,
1539
1380
  });
1540
1381
  }
1541
- return { ok: true };
1382
+ return authLoginResponse(event, result.token, email);
1542
1383
  }
1543
1384
  // signInEmail succeeded but returned no token — typically means the
1544
1385
  // email isn't verified yet. Don't return { ok: true } without a
@@ -1589,6 +1430,9 @@ async function mountBetterAuthRoutes(app, options) {
1589
1430
  const cookie = getCookie(event, COOKIE_NAME);
1590
1431
  if (cookie)
1591
1432
  await removeSession(cookie);
1433
+ const bearerToken = getBearerSessionToken(event);
1434
+ if (bearerToken)
1435
+ await removeSession(bearerToken);
1592
1436
  deleteCookie(event, COOKIE_NAME, { path: "/" });
1593
1437
  try {
1594
1438
  await auth.api.signOut({ headers: event.headers });
@@ -1677,10 +1521,6 @@ async function mountBetterAuthRoutes(app, options) {
1677
1521
  const session = await getSession(event);
1678
1522
  return session ?? { error: "Not authenticated" };
1679
1523
  }));
1680
- // POST /_agent-native/auth/migrate-local-data — move local-mode data to
1681
- // the currently signed-in account. Called by the UI after a user upgrades
1682
- // from local mode to a real account so they don't lose their data.
1683
- app.use("/_agent-native/auth/migrate-local-data", migrateLocalDataHandler);
1684
1524
  // GET /_agent-native/auth/reset — HTML page shown when a user clicks the
1685
1525
  // reset link in their email. Reads ?token=... and POSTs to Better Auth's
1686
1526
  // /reset-password endpoint on submit.
@@ -1729,12 +1569,15 @@ function mountTokenOnlyRoutes(app, accessTokens, publicPaths = []) {
1729
1569
  path: "/",
1730
1570
  maxAge: sessionMaxAge,
1731
1571
  });
1732
- return { ok: true };
1572
+ return authLoginResponse(event, sessionToken, "user");
1733
1573
  }));
1734
1574
  app.use("/_agent-native/auth/logout", defineEventHandler(async (event) => {
1735
1575
  const cookie = getCookie(event, COOKIE_NAME);
1736
1576
  if (cookie)
1737
1577
  await removeSession(cookie);
1578
+ const bearerToken = getBearerSessionToken(event);
1579
+ if (bearerToken)
1580
+ await removeSession(bearerToken);
1738
1581
  deleteCookie(event, COOKIE_NAME, { path: "/" });
1739
1582
  if (isElectronRequest(event))
1740
1583
  await clearDesktopSso();
@@ -1748,38 +1591,12 @@ function mountTokenOnlyRoutes(app, accessTokens, publicPaths = []) {
1748
1591
  const session = await getSession(event);
1749
1592
  return session ?? { error: "Not authenticated" };
1750
1593
  }));
1751
- app.use("/_agent-native/auth/migrate-local-data", migrateLocalDataHandler);
1752
1594
  _authGuardConfig = { loginHtml: TOKEN_LOGIN_HTML, publicPaths };
1753
1595
  const guardFn = createAuthGuardFn();
1754
1596
  _authGuardFn = guardFn;
1755
1597
  app.use(defineEventHandler(guardFn));
1756
1598
  }
1757
1599
  // ---------------------------------------------------------------------------
1758
- // mountLocalModeRoutes — stub routes for AUTH_MODE=local
1759
- // ---------------------------------------------------------------------------
1760
- function mountLocalModeRoutes(app) {
1761
- app.use("/_agent-native/auth/session", defineEventHandler(async (event) => {
1762
- if (!isReadMethod(event)) {
1763
- setResponseStatus(event, 405);
1764
- return { error: "Method not allowed" };
1765
- }
1766
- return await getSession(event);
1767
- }));
1768
- app.use("/_agent-native/auth/login", defineEventHandler(() => ({ ok: true })));
1769
- app.use("/_agent-native/auth/logout", defineEventHandler(() => ({ ok: true })));
1770
- // Allow exiting local mode to switch to real auth
1771
- app.use("/_agent-native/auth/exit-local-mode", defineEventHandler(async (event) => {
1772
- if (getMethod(event) !== "POST") {
1773
- setResponseStatus(event, 405);
1774
- return { error: "Method not allowed" };
1775
- }
1776
- return exitLocalMode(event);
1777
- }));
1778
- // Upgrade path: migrate-local-data must be reachable from local mode
1779
- // because the user is still in local mode when they trigger the upgrade.
1780
- app.use("/_agent-native/auth/migrate-local-data", migrateLocalDataHandler);
1781
- }
1782
- // ---------------------------------------------------------------------------
1783
1600
  // mountAuthFallbackRoutes — minimal auth endpoints when Better Auth init fails
1784
1601
  // ---------------------------------------------------------------------------
1785
1602
  function mountAuthFallbackRoutes(app) {
@@ -1815,7 +1632,7 @@ function mountAuthFallbackRoutes(app) {
1815
1632
  expiresAt: Date.now() + sessionMaxAge * 1000,
1816
1633
  });
1817
1634
  }
1818
- return { ok: true };
1635
+ return authLoginResponse(event, result.token, email);
1819
1636
  }
1820
1637
  setResponseStatus(event, 403);
1821
1638
  return {
@@ -1859,6 +1676,9 @@ function mountAuthFallbackRoutes(app) {
1859
1676
  const cookie = getCookie(event, COOKIE_NAME);
1860
1677
  if (cookie)
1861
1678
  await removeSession(cookie);
1679
+ const bearerToken = getBearerSessionToken(event);
1680
+ if (bearerToken)
1681
+ await removeSession(bearerToken);
1862
1682
  deleteCookie(event, COOKIE_NAME, { path: "/" });
1863
1683
  try {
1864
1684
  const auth = await getBetterAuth();
@@ -1871,20 +1691,6 @@ function mountAuthFallbackRoutes(app) {
1871
1691
  await clearDesktopSso();
1872
1692
  return { ok: true };
1873
1693
  }));
1874
- app.use("/_agent-native/auth/local-mode", defineEventHandler(async (event) => {
1875
- if (getMethod(event) !== "POST") {
1876
- setResponseStatus(event, 405);
1877
- return { error: "Method not allowed" };
1878
- }
1879
- return localModeMarkerDisabled(event);
1880
- }));
1881
- app.use("/_agent-native/auth/exit-local-mode", defineEventHandler(async (event) => {
1882
- if (getMethod(event) !== "POST") {
1883
- setResponseStatus(event, 405);
1884
- return { error: "Method not allowed" };
1885
- }
1886
- return exitLocalMode(event);
1887
- }));
1888
1694
  app.use("/_agent-native/auth/session", defineEventHandler(async (event) => {
1889
1695
  if (!isReadMethod(event)) {
1890
1696
  setResponseStatus(event, 405);
@@ -1893,10 +1699,6 @@ function mountAuthFallbackRoutes(app) {
1893
1699
  const session = await getSession(event);
1894
1700
  return session ?? { error: "Not authenticated" };
1895
1701
  }));
1896
- // Must be reachable from fallback mode too — otherwise a user who
1897
- // upgrades-from-local on a server that couldn't init Better Auth gets a
1898
- // 500 instead of a clear 401.
1899
- app.use("/_agent-native/auth/migrate-local-data", migrateLocalDataHandler);
1900
1702
  }
1901
1703
  // ---------------------------------------------------------------------------
1902
1704
  // autoMountAuth — the recommended entry point
@@ -1904,14 +1706,16 @@ function mountAuthFallbackRoutes(app) {
1904
1706
  /**
1905
1707
  * Automatically configure auth based on environment and configuration:
1906
1708
  *
1907
- * - **AUTH_MODE=local**: Auth bypassed. `getSession()` returns `{ email: "local@localhost" }`.
1908
- * This is the explicit escape hatch for solo local development.
1909
1709
  * - **BYOA (custom getSession)**: Template-provided auth callback handles everything.
1910
- * - **AUTH_DISABLED=true**: Auth bypassed (for infrastructure-level auth like Cloudflare Access).
1911
1710
  * - **ACCESS_TOKEN/ACCESS_TOKENS**: Simple token-based auth.
1912
1711
  * - **Default**: Better Auth with email/password, social providers, organizations, and JWT.
1913
1712
  * Users see an onboarding page to create an account on first visit.
1914
1713
  *
1714
+ * Local development uses the same Better Auth flow as production. Email
1715
+ * verification is automatically skipped in dev/test environments and when
1716
+ * no email provider is configured (see `shouldSkipEmailVerification`), so a
1717
+ * fresh local clone only needs an email + password to get started.
1718
+ *
1915
1719
  * Returns true if auth was mounted, false if skipped.
1916
1720
  */
1917
1721
  export async function autoMountAuth(app, options = {}) {
@@ -1959,8 +1763,7 @@ export async function autoMountAuth(app, options = {}) {
1959
1763
  _authGuardConfig = null;
1960
1764
  _mountedApp = app;
1961
1765
  if (!app) {
1962
- if ((await isLocalModeEnabled()) || isDevEnvironment()) {
1963
- authDisabledMode = false;
1766
+ if (isDevEnvironment()) {
1964
1767
  customGetSession = null;
1965
1768
  return false;
1966
1769
  }
@@ -1968,27 +1771,12 @@ export async function autoMountAuth(app, options = {}) {
1968
1771
  }
1969
1772
  // Reset globals
1970
1773
  customGetSession = null;
1971
- authDisabledMode = false;
1972
1774
  sessionMaxAge = options.maxAge ?? DEFAULT_MAX_AGE;
1973
1775
  const publicPaths = options.publicPaths ?? [];
1776
+ mountAuthCorsMiddleware(app);
1974
1777
  if (options.getSession) {
1975
1778
  customGetSession = options.getSession;
1976
1779
  }
1977
- // AUTH_MODE=local — explicit local-only mode (escape hatch)
1978
- if (await isLocalModeEnabled()) {
1979
- try {
1980
- // Mount the standard auth endpoints and guard even in local mode so the
1981
- // app can switch back to real auth immediately after AUTH_MODE is
1982
- // cleared, without waiting for a server restart/remount.
1983
- await mountBetterAuthRoutes(app, options);
1984
- }
1985
- catch (err) {
1986
- console.error("[agent-native] Failed to initialize Better Auth in local mode:", err);
1987
- mountLocalModeRoutes(app);
1988
- }
1989
- console.log("[agent-native] Auth mode: local (upgrade path enabled).");
1990
- return false;
1991
- }
1992
1780
  // BYOA — custom getSession provider
1993
1781
  if (customGetSession) {
1994
1782
  app.use("/_agent-native/auth/session", defineEventHandler(async (event) => {
@@ -2004,12 +1792,14 @@ export async function autoMountAuth(app, options = {}) {
2004
1792
  const cookie = getCookie(event, COOKIE_NAME);
2005
1793
  if (cookie)
2006
1794
  await removeSession(cookie);
1795
+ const bearerToken = getBearerSessionToken(event);
1796
+ if (bearerToken)
1797
+ await removeSession(bearerToken);
2007
1798
  deleteCookie(event, COOKIE_NAME, { path: "/" });
2008
1799
  if (isElectronRequest(event))
2009
1800
  await clearDesktopSso();
2010
1801
  return { ok: true };
2011
1802
  }));
2012
- app.use("/_agent-native/auth/migrate-local-data", migrateLocalDataHandler);
2013
1803
  const byoaLoginHtml = options.loginHtml ?? TOKEN_LOGIN_HTML;
2014
1804
  _authGuardConfig = { loginHtml: byoaLoginHtml, publicPaths };
2015
1805
  const guardFn = createAuthGuardFn();
@@ -2019,14 +1809,6 @@ export async function autoMountAuth(app, options = {}) {
2019
1809
  console.log("[agent-native] Auth enabled — custom getSession provider.");
2020
1810
  return true;
2021
1811
  }
2022
- // AUTH_DISABLED — skip auth (infrastructure-level auth)
2023
- if (process.env.AUTH_DISABLED === "true") {
2024
- authDisabledMode = true;
2025
- console.warn("[agent-native] AUTH_DISABLED=true — running without auth. " +
2026
- "Ensure this app is behind infrastructure-level auth (Cloudflare Access, VPN, etc.).");
2027
- mountLocalModeRoutes(app);
2028
- return false;
2029
- }
2030
1812
  // ACCESS_TOKEN-only mode
2031
1813
  const tokens = getAccessTokens();
2032
1814
  if (tokens.length > 0) {