@agent-native/core 0.145.7 → 0.145.8

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 (92) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +5 -1
  3. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +3 -2
  4. package/corpus/templates/analytics/changelog/2026-08-07-settings-skeleton-placeholders.md +6 -0
  5. package/corpus/templates/analytics/netlify.toml +1 -1
  6. package/corpus/templates/analytics/package.json +1 -0
  7. package/corpus/templates/analytics/scripts/migrate-production.ts +24 -0
  8. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +9 -0
  9. package/corpus/templates/analytics/server/plugins/db.ts +14 -3
  10. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +5 -1
  11. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +5 -1
  12. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +5 -1
  13. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -1
  14. package/corpus/templates/chat/AGENTS.md +3 -0
  15. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +5 -1
  16. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +5 -1
  17. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +21 -3
  18. package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +23 -5
  19. package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +24 -5
  20. package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +23 -3
  21. package/corpus/templates/crm/changelog/2026-08-07-settings-loading-skeletons.md +6 -0
  22. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +5 -1
  23. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +5 -1
  24. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +5 -1
  25. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +5 -1
  26. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +5 -1
  27. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +5 -1
  28. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +5 -1
  29. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +5 -1
  30. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +5 -1
  31. package/dist/client/components/ApiKeySettings.js +2 -5
  32. package/dist/client/org/TeamPage.js +4 -2
  33. package/dist/client/settings/AccountSettingsCard.js +2 -1
  34. package/dist/client/settings/AgentsSection.js +2 -1
  35. package/dist/client/settings/SecretsSection.js +3 -2
  36. package/dist/client/settings/SettingsPanel.js +6 -12
  37. package/dist/client/settings/SettingsSkeleton.d.ts +12 -0
  38. package/dist/client/settings/SettingsSkeleton.js +12 -0
  39. package/dist/client/settings/VoiceTranscriptionSection.js +5 -4
  40. package/dist/client/settings/index.d.ts +1 -0
  41. package/dist/client/settings/index.js +1 -0
  42. package/dist/collab/struct-routes.d.ts +1 -1
  43. package/dist/db/client.d.ts +13 -7
  44. package/dist/db/client.js +124 -7
  45. package/dist/db/ddl-guard.js +3 -1
  46. package/dist/db/ensure-additive-columns.d.ts +2 -0
  47. package/dist/db/ensure-additive-columns.js +5 -2
  48. package/dist/db/index.d.ts +2 -2
  49. package/dist/db/index.js +2 -2
  50. package/dist/db/migrations.d.ts +11 -54
  51. package/dist/db/migrations.js +70 -0
  52. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  53. package/dist/jobs/index.d.ts +1 -0
  54. package/dist/jobs/index.js +1 -0
  55. package/dist/jobs/run-history.d.ts +4 -0
  56. package/dist/jobs/run-history.js +44 -0
  57. package/dist/notifications/routes.d.ts +2 -2
  58. package/dist/observability/routes.d.ts +1 -1
  59. package/dist/progress/routes.d.ts +1 -1
  60. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  61. package/dist/provider-api/actions/provider-api.d.ts +12 -12
  62. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  63. package/dist/resources/handlers.d.ts +1 -1
  64. package/dist/secrets/routes.d.ts +6 -6
  65. package/dist/server/agent-chat-plugin.js +6 -4
  66. package/dist/server/auth-plugin.js +5 -0
  67. package/dist/server/better-auth-instance.js +0 -61
  68. package/dist/server/better-auth-migrations.d.ts +8 -0
  69. package/dist/server/better-auth-migrations.js +179 -0
  70. package/dist/server/edge.d.ts +2 -0
  71. package/dist/server/edge.js +2 -0
  72. package/dist/server/index.d.ts +2 -0
  73. package/dist/server/index.js +2 -0
  74. package/dist/server/release-migrations.d.ts +8 -0
  75. package/dist/server/release-migrations.js +24 -0
  76. package/dist/server/sweep-runtime.d.ts +1 -1
  77. package/dist/server/sweep-runtime.js +15 -0
  78. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -1
  79. package/dist/templates/chat/AGENTS.md +3 -0
  80. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +5 -1
  81. package/dist/templates/default/AGENTS.md +3 -0
  82. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -1
  83. package/dist/templates/workspace-core/AGENTS.md +3 -0
  84. package/dist/templates/workspace-root/AGENTS.md +3 -0
  85. package/package.json +1 -1
  86. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -1
  87. package/src/templates/chat/AGENTS.md +3 -0
  88. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +5 -1
  89. package/src/templates/default/AGENTS.md +3 -0
  90. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -1
  91. package/src/templates/workspace-core/AGENTS.md +3 -0
  92. package/src/templates/workspace-root/AGENTS.md +3 -0
@@ -19,6 +19,13 @@ export declare function isDuplicateColumnError(err: unknown): boolean;
19
19
  */
20
20
  export declare function isPermissionError(err: unknown): boolean;
21
21
  export interface RunMigrationsOptions {
22
+ /**
23
+ * Run migrations even inside a serverless request runtime. Off by default:
24
+ * see the guard in {@link runMigrations} for why request-path DDL is what
25
+ * took analytics down. Only set this when the caller has no scheduled or
26
+ * background runtime that could migrate instead.
27
+ */
28
+ runInServerlessRequest?: boolean;
22
29
  /**
23
30
  * Name of the migrations bookkeeping table. REQUIRED — there is intentionally
24
31
  * no default. Two templates that share a database (e.g. via the same Neon URL)
@@ -85,61 +92,11 @@ export interface MigrationEntry {
85
92
  run?: () => Promise<MigrationRunResult>;
86
93
  }
87
94
  /**
88
- * Runs a list of migrations against the configured database, gated by a
89
- * per-table bookkeeping row (`options.table`) so repeated boots only apply
90
- * new migrations.
91
- *
92
- * ## Name-based tracking (why it exists)
93
- *
94
- * The legacy gate is purely numeric: `SELECT MAX(version)` from the
95
- * bookkeeping table, then apply every entry whose `version` is greater. That
96
- * scheme silently breaks down when **two independent branches each extend the
97
- * same migration list with different DDL under the same version numbers** —
98
- * exactly what happened to the analytics template's v75-v83 range. Branch A
99
- * shipped alert-rule tables as v75-v78; branch B shipped unrelated DDL as its
100
- * own v75-v83. Whichever branch deployed first recorded "v75..v83 applied"
101
- * in the bookkeeping table. When the other branch merged and deployed, its
102
- * migrations at those same version numbers were never applied — `MAX(version)`
103
- * was already ≥ their version, so the gate treated them as done even though
104
- * their actual DDL had never run. The result: `analytics_migrations` showed
105
- * rows for 1..83 with no gaps, yet `analytics_alert_rules`,
106
- * `analytics_alert_incidents`, and `session_recordings.network_error_count`
107
- * did not exist in production. Version numbers are not a stable identity —
108
- * they're just sequence position, and sequence position collides across
109
- * branches.
110
- *
111
- * Name-based tracking fixes this by keying application on a stable, unique
112
- * **string slug** instead of a position in a shared integer sequence:
113
- *
114
- * - Entries with a `name` are recorded in a companion table,
115
- * `${table}_named` (`name TEXT PRIMARY KEY`), and APPLY IFF their name is
116
- * absent from that table — completely independent of version numbers or
117
- * the legacy `MAX(version)` gate. Two branches can both ship a migration
118
- * named `"analytics-alert-rules-table"` at version 75, or one at version 75
119
- * and the other at version 90 after a rebase — either way it applies
120
- * exactly once per database, keyed on the name.
121
- * - Entries WITHOUT a `name` keep the exact legacy behavior
122
- * (`version > MAX(recorded version)`), so nothing changes for existing
123
- * unnamed migrations.
124
- * - Named migrations still execute in list order, interleaved with unnamed
125
- * ones exactly as written.
126
- * - When a named migration's DDL runs, we record BOTH the named row (always)
127
- * and — if its `version` is greater than the current legacy max — the
128
- * legacy version row too, in the SAME atomic batch/transaction as the DDL.
129
- * This keeps the legacy `MAX(version)` gate monotonically advancing for
130
- * any unnamed migrations that come after it in the list, while ensuring a
131
- * named migration is never "double recorded" in a way that would let it
132
- * re-apply.
133
- * - A duplicate `name` across the migration list throws at startup — that's
134
- * a programmer error (copy-paste or merge mistake), not a runtime data
135
- * problem, and failing loud beats silently tracking the wrong row.
95
+ * Run an explicit release-time migration job with migration duty enabled.
136
96
  *
137
- * New migrations should always set a `name`. Legacy unnamed migrations don't
138
- * need to be renamed retroactively — the two gating strategies coexist in the
139
- * same list — but if a table is EVER at risk of being shared across parallel
140
- * branches (any template's own migrations qualify, since branches routinely
141
- * extend the same list concurrently), giving new entries a name is what makes
142
- * them immune to the collision class described above.
97
+ * The flag is process-local and restored even when the job fails, so a build
98
+ * step can opt in without creating a permanent escape hatch for request code.
143
99
  */
100
+ export declare function withMigrationRuntime<T>(run: () => Promise<T>): Promise<T>;
144
101
  export declare function runMigrations(migrations: Array<MigrationEntry>, options: RunMigrationsOptions): NitroPluginDef;
145
102
  export {};
@@ -226,6 +226,56 @@ function resolveMigrationSql(sql, pg) {
226
226
  * extend the same list concurrently), giving new entries a name is what makes
227
227
  * them immune to the collision class described above.
228
228
  */
229
+ /**
230
+ * True in a production serverless runtime that serves user REQUESTS.
231
+ *
232
+ * Deliberately mirrors the analytics template's own predicate rather than
233
+ * importing it: `packages/core/src/db` must not depend on a template, and the
234
+ * agent/background modules that carry the sibling predicates would introduce a
235
+ * cycle here.
236
+ */
237
+ function isServerlessRequestRuntime() {
238
+ if (process.env.NODE_ENV !== "production")
239
+ return false;
240
+ return (process.env.NETLIFY === "true" ||
241
+ Boolean(process.env.NETLIFY_FUNCTION_NAME) ||
242
+ Boolean(process.env.AWS_LAMBDA_FUNCTION_NAME) ||
243
+ Boolean(process.env.LAMBDA_TASK_ROOT) ||
244
+ process.env.AWS_EXECUTION_ENV?.startsWith("AWS_Lambda") === true ||
245
+ process.env.VERCEL === "1");
246
+ }
247
+ /**
248
+ * A runtime that is ALLOWED to migrate: scheduled jobs and durable background
249
+ * workers, which are off the request path and may take as long as they need.
250
+ *
251
+ * Set by whichever runtime claims migration duty. Analytics sets its own
252
+ * equivalent flag before running the scheduled rollup.
253
+ */
254
+ function isMigrationAuthorizedRuntime() {
255
+ return (globalThis.__AGENT_NATIVE_MIGRATION_RUNTIME__ === true);
256
+ }
257
+ /**
258
+ * Run an explicit release-time migration job with migration duty enabled.
259
+ *
260
+ * The flag is process-local and restored even when the job fails, so a build
261
+ * step can opt in without creating a permanent escape hatch for request code.
262
+ */
263
+ export async function withMigrationRuntime(run) {
264
+ const runtime = globalThis;
265
+ const previous = runtime.__AGENT_NATIVE_MIGRATION_RUNTIME__;
266
+ runtime.__AGENT_NATIVE_MIGRATION_RUNTIME__ = true;
267
+ try {
268
+ return await run();
269
+ }
270
+ finally {
271
+ if (previous === undefined) {
272
+ delete runtime.__AGENT_NATIVE_MIGRATION_RUNTIME__;
273
+ }
274
+ else {
275
+ runtime.__AGENT_NATIVE_MIGRATION_RUNTIME__ = previous;
276
+ }
277
+ }
278
+ }
229
279
  export function runMigrations(migrations, options) {
230
280
  const table = options?.table;
231
281
  if (!table ||
@@ -256,6 +306,26 @@ export function runMigrations(migrations, options) {
256
306
  return async () => { };
257
307
  const namedTable = `${table}_named`;
258
308
  return async () => {
309
+ // Migrations are schema DDL plus an introspection pass. On serverless
310
+ // "first touch" is EVERY cold start, so this lands on the critical path of
311
+ // a user request — measured at ~5.5-8.6s for the version check alone on a
312
+ // 180-table database, with 4-6 copies running concurrently under load.
313
+ //
314
+ // Guarded HERE rather than at each call site on purpose. The analytics
315
+ // template guarded its own runner in #2708; three core plugins
316
+ // (org, context-xray, observational-memory) kept calling this unguarded,
317
+ // so the probe storm survived the fix that was supposed to end it. A
318
+ // fourth special case would have shipped the same bug a fourth time.
319
+ //
320
+ // Schema still has to exist: a scheduled/background runtime sets
321
+ // `__AGENT_NATIVE_MIGRATION_RUNTIME__`, and `runInServerlessRequest` is
322
+ // the explicit opt-in for a caller that genuinely cannot defer.
323
+ if (options?.runInServerlessRequest !== true &&
324
+ isServerlessRequestRuntime() &&
325
+ !isMigrationAuthorizedRuntime()) {
326
+ console.info(`[migrations] Skipping "${table}" migrations in a serverless request runtime`);
327
+ return;
328
+ }
259
329
  try {
260
330
  // Check for Cloudflare D1 binding (only if DATABASE_URL not set)
261
331
  const d1 = getDialect() === "d1"
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
+ error?: undefined;
20
21
  configured?: undefined;
21
22
  connectPath?: undefined;
22
23
  url: string;
23
24
  id: string;
24
25
  provider: string;
25
- error?: undefined;
26
26
  }>;
27
27
  export default _default;
@@ -3,3 +3,4 @@ export { processRecurringJobs, parseJobFrontmatter, buildJobContent, type JobFro
3
3
  export { getAutomationSchedulerHealth, recordAutomationSchedulerHealth, type AutomationSchedulerHealth, } from "./scheduler-health.js";
4
4
  export { RECURRING_JOBS_SWEEP_PATH, RECURRING_JOBS_SWEEP_TOKEN_SUBJECT, } from "./scheduler-dispatch.js";
5
5
  export { createJobTools } from "./tools.js";
6
+ export { AUTOMATION_RUN_MIGRATIONS, runAutomationRunMigrations, } from "./run-history.js";
@@ -3,3 +3,4 @@ export { processRecurringJobs, parseJobFrontmatter, buildJobContent, } from "./s
3
3
  export { getAutomationSchedulerHealth, recordAutomationSchedulerHealth, } from "./scheduler-health.js";
4
4
  export { RECURRING_JOBS_SWEEP_PATH, RECURRING_JOBS_SWEEP_TOKEN_SUBJECT, } from "./scheduler-dispatch.js";
5
5
  export { createJobTools } from "./tools.js";
6
+ export { AUTOMATION_RUN_MIGRATIONS, runAutomationRunMigrations, } from "./run-history.js";
@@ -1,3 +1,4 @@
1
+ import { type MigrationEntry } from "../db/migrations.js";
1
2
  /**
2
3
  * "interrupted" is derived at read time, never stored: a process killed
3
4
  * mid-run cannot write its own outcome, so a row left running long past the
@@ -30,6 +31,9 @@ export interface StartAutomationRunInput {
30
31
  /** A pre-created row still waiting for its background worker handoff. */
31
32
  dispatchPending?: boolean;
32
33
  }
34
+ /** Authoritative release-time schema for durable automation history. */
35
+ export declare const AUTOMATION_RUN_MIGRATIONS: MigrationEntry[];
36
+ export declare function runAutomationRunMigrations(nitroApp: unknown): Promise<void>;
33
37
  /**
34
38
  * Record an automation execution. Manual runs create the row before dispatch,
35
39
  * so `dispatchPending` distinguishes that durable handoff from a run that has
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { z } from "zod";
3
3
  import { getDbExec, intType, isPostgres } from "../db/client.js";
4
4
  import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
5
+ import { runMigrations } from "../db/migrations.js";
5
6
  import { emit as emitBusEvent, registerEvent } from "../event-bus/index.js";
6
7
  registerEvent({
7
8
  name: "automation.run.finished",
@@ -32,6 +33,49 @@ const INTERRUPTED_RUN_MESSAGE = "Worker stopped before a terminal result was rec
32
33
  const CLAIM_LEASE_MS = RUN_LIVENESS_CEILING_MS;
33
34
  /** Rows kept per automation, so a per-minute schedule cannot grow forever. */
34
35
  const RUNS_RETAINED_PER_AUTOMATION = 50;
36
+ /** Authoritative release-time schema for durable automation history. */
37
+ export const AUTOMATION_RUN_MIGRATIONS = [
38
+ {
39
+ version: 1,
40
+ name: "automation-runs-table",
41
+ sql: `
42
+ CREATE TABLE IF NOT EXISTS ${TABLE} (
43
+ id TEXT PRIMARY KEY,
44
+ owner TEXT NOT NULL,
45
+ automation TEXT NOT NULL,
46
+ path TEXT NOT NULL,
47
+ scope TEXT,
48
+ org_id TEXT,
49
+ run_id TEXT,
50
+ thread_id TEXT,
51
+ status TEXT NOT NULL DEFAULT 'running',
52
+ started_at INTEGER NOT NULL,
53
+ finished_at INTEGER,
54
+ error TEXT,
55
+ claimed_at INTEGER,
56
+ dispatch_pending INTEGER NOT NULL DEFAULT 0
57
+ );
58
+ CREATE INDEX IF NOT EXISTS idx_${TABLE}_owner_automation
59
+ ON ${TABLE} (owner, automation, started_at)
60
+ `,
61
+ },
62
+ {
63
+ version: 2,
64
+ name: "automation-runs-claimed-at",
65
+ sql: `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS claimed_at INTEGER`,
66
+ },
67
+ {
68
+ version: 3,
69
+ name: "automation-runs-dispatch-pending",
70
+ sql: `ALTER TABLE ${TABLE}
71
+ ADD COLUMN IF NOT EXISTS dispatch_pending INTEGER NOT NULL DEFAULT 0`,
72
+ },
73
+ ];
74
+ export async function runAutomationRunMigrations(nitroApp) {
75
+ await runMigrations(AUTOMATION_RUN_MIGRATIONS, {
76
+ table: "_automation_run_migrations",
77
+ })(nitroApp);
78
+ }
35
79
  let _initPromise;
36
80
  async function ensureTable() {
37
81
  if (!_initPromise) {
@@ -13,13 +13,13 @@
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
14
  count: number;
15
15
  updated?: undefined;
16
- error?: undefined;
17
16
  ok?: undefined;
17
+ error?: undefined;
18
18
  } | {
19
19
  count?: undefined;
20
20
  updated: number;
21
- error?: undefined;
22
21
  ok?: undefined;
22
+ error?: undefined;
23
23
  } | {
24
24
  count?: undefined;
25
25
  updated?: undefined;
@@ -62,6 +62,6 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
62
62
  summary?: undefined;
63
63
  spans?: undefined;
64
64
  id?: undefined;
65
- ok: boolean;
66
65
  error?: undefined;
66
+ ok: boolean;
67
67
  }>>;
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
@@ -75,8 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- id?: undefined;
78
+ message?: undefined;
79
79
  deleted?: undefined;
80
+ found?: undefined;
80
81
  providers: {
81
82
  id: string;
82
83
  label: string;
@@ -88,21 +89,21 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
89
  }[];
89
90
  count: number;
90
91
  provider?: undefined;
91
- found?: undefined;
92
92
  registered?: undefined;
93
+ id?: undefined;
93
94
  label?: undefined;
94
- message?: undefined;
95
95
  } | {
96
- id?: undefined;
96
+ message?: undefined;
97
97
  deleted?: undefined;
98
98
  providers?: undefined;
99
99
  count?: undefined;
100
100
  found: boolean;
101
101
  provider: import("../custom-registry.js").CustomProviderConfig;
102
102
  registered?: undefined;
103
+ id?: undefined;
103
104
  label?: undefined;
104
- message?: undefined;
105
105
  } | {
106
+ message?: undefined;
106
107
  deleted?: undefined;
107
108
  providers?: undefined;
108
109
  count?: undefined;
@@ -111,23 +112,22 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
111
112
  id: string;
112
113
  registered?: undefined;
113
114
  label?: undefined;
114
- message?: undefined;
115
115
  } | {
116
+ message?: undefined;
117
+ found?: undefined;
116
118
  providers?: undefined;
117
119
  count?: undefined;
118
120
  provider?: undefined;
119
- found?: undefined;
120
121
  deleted: boolean;
121
122
  id: string;
122
123
  registered?: undefined;
123
124
  label?: undefined;
124
- message?: undefined;
125
125
  } | {
126
126
  deleted?: undefined;
127
+ found?: undefined;
127
128
  providers?: undefined;
128
129
  count?: undefined;
129
130
  provider?: undefined;
130
- found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -311,9 +311,11 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
+ deleted?: undefined;
314
315
  id?: undefined;
315
316
  provider?: undefined;
316
- deleted?: undefined;
317
+ found?: undefined;
318
+ message?: undefined;
317
319
  providers: {
318
320
  id: string;
319
321
  label: string;
@@ -324,46 +326,44 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
324
326
  updatedAt: number;
325
327
  }[];
326
328
  count: number;
327
- found?: undefined;
328
329
  registered?: undefined;
329
330
  label?: undefined;
330
- message?: undefined;
331
331
  } | {
332
- id?: undefined;
333
- count?: undefined;
334
332
  deleted?: undefined;
333
+ count?: undefined;
334
+ id?: undefined;
335
+ message?: undefined;
335
336
  providers?: undefined;
336
337
  found: boolean;
337
338
  provider: import("../custom-registry.js").CustomProviderConfig;
338
339
  registered?: undefined;
339
340
  label?: undefined;
340
- message?: undefined;
341
341
  } | {
342
+ deleted?: undefined;
342
343
  count?: undefined;
343
344
  provider?: undefined;
344
- deleted?: undefined;
345
+ message?: undefined;
345
346
  providers?: undefined;
346
347
  found: boolean;
347
348
  id: string;
348
349
  registered?: undefined;
349
350
  label?: undefined;
350
- message?: undefined;
351
351
  } | {
352
352
  count?: undefined;
353
353
  provider?: undefined;
354
- providers?: undefined;
355
354
  found?: undefined;
355
+ message?: undefined;
356
+ providers?: undefined;
356
357
  deleted: boolean;
357
358
  id: string;
358
359
  registered?: undefined;
359
360
  label?: undefined;
360
- message?: undefined;
361
361
  } | {
362
+ deleted?: undefined;
362
363
  count?: undefined;
363
364
  provider?: undefined;
364
- deleted?: undefined;
365
- providers?: undefined;
366
365
  found?: undefined;
366
+ providers?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
+ deleted?: undefined;
76
77
  jobs: {
77
78
  id: string;
78
79
  name: string;
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
83
84
  updatedAt: string;
84
85
  }[];
85
86
  total: number;
86
- deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
+ deleted?: undefined;
94
95
  jobs?: undefined;
95
96
  total?: undefined;
96
- deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- error: string;
52
51
  ok?: undefined;
52
+ error: string;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -37,17 +37,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
37
37
  ok?: undefined;
38
38
  status?: undefined;
39
39
  } | {
40
+ error?: undefined;
40
41
  ok: boolean;
41
42
  status: string;
42
- error?: undefined;
43
43
  } | {
44
44
  ok?: undefined;
45
45
  error: string;
46
46
  removed?: undefined;
47
47
  } | {
48
+ error?: undefined;
48
49
  ok: boolean;
49
50
  removed: boolean;
50
- error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
@@ -58,13 +58,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
58
58
  error: string;
59
59
  note?: undefined;
60
60
  } | {
61
+ error?: undefined;
61
62
  ok: boolean;
62
63
  note?: undefined;
63
- error?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  ok: boolean;
66
67
  note: string;
67
- error?: undefined;
68
68
  } | {
69
69
  note?: undefined;
70
70
  ok: boolean;
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
95
95
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
96
96
  error: string;
97
97
  } | {
98
+ error?: undefined;
98
99
  ok: boolean;
99
100
  key: string;
100
- error?: undefined;
101
101
  } | {
102
+ error?: undefined;
102
103
  ok: boolean;
103
104
  removed: boolean;
104
- error?: undefined;
105
105
  }>>;
@@ -4885,10 +4885,12 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4885
4885
  catch (error) {
4886
4886
  console.warn("[recurring-jobs] Scheduler module unavailable:", error instanceof Error ? error.message : error);
4887
4887
  }
4888
- // A non-Netlify self-dispatch only waits for the request to leave the
4889
- // current invocation. Keep the durable history row as a retryable queue
4890
- // so a frozen serverless handoff is recovered on the next sweep.
4891
- if (!isBackgroundRuntime && !sweepsDisabled) {
4888
+ // A synchronous self-dispatch only waits for the request to leave the
4889
+ // current invocation. Long-lived runtimes keep this retryable-queue
4890
+ // backstop; serverless runtimes use their durable scheduler instead.
4891
+ if (!isBackgroundRuntime &&
4892
+ !disableRecurringJobsRuntime &&
4893
+ !sweepsDisabled) {
4892
4894
  (() => {
4893
4895
  let inFlight = false;
4894
4896
  const sweep = async () => {
@@ -1,10 +1,15 @@
1
1
  import { autoMountAuth } from "./auth.js";
2
+ import { runBetterAuthMigrations } from "./better-auth-migrations.js";
2
3
  import { getH3App, awaitBootstrap, markDefaultPluginProvided, trackPluginInit, } from "./framework-request-handler.js";
3
4
  export function createAuthPlugin(options) {
4
5
  return (nitroApp) => {
5
6
  markDefaultPluginProvided(nitroApp, "auth");
6
7
  const initPromise = (async () => {
7
8
  await awaitBootstrap(nitroApp);
9
+ // guard:allow-boot-data-work — local/long-lived runtimes provision auth
10
+ // before mounting routes; production functions are rejected by the
11
+ // migration runner and use the release job instead.
12
+ await runBetterAuthMigrations(nitroApp);
8
13
  await autoMountAuth(getH3App(nitroApp), options);
9
14
  })();
10
15
  trackPluginInit(nitroApp, initPromise, {
@@ -17,7 +17,6 @@ import { sqliteTable, text as sqliteText, integer as sqliteInteger, } from "driz
17
17
  import { TEMPLATES } from "../cli/templates-meta.js";
18
18
  import { getDbExec, isPostgres } from "../db/client.js";
19
19
  import { getDialect, getDatabaseUrl, getDatabaseAuthToken, closePgliteClients, getPgliteClient, isPgliteUrl, loadPgliteDrizzle, pgPoolOptions, neonPoolOptions, attachNeonPoolErrorLogger, sharedDbPool, onSharedDbPoolsClosed, onSharedDbPoolReplaced, } from "../db/client.js";
20
- import { ensureTableExists } from "../db/ddl-guard.js";
21
20
  import { CORE_RESET_PASSWORD_EMAIL_ID, CORE_VERIFY_SIGNUP_EMAIL_ID, } from "../email-catalog/system-emails.js";
22
21
  import { saveOAuthTokens } from "../oauth-tokens/store.js";
23
22
  import { acceptPendingInvitationsForEmail } from "../org/accept-pending.js";
@@ -518,65 +517,6 @@ async function mirrorGoogleAccountToOAuthTokens(account) {
518
517
  tokens.id_token = account.idToken;
519
518
  await saveOAuthTokens("google", email, tokens, email);
520
519
  }
521
- async function ensureBetterAuthTables() {
522
- const db = getDbExec();
523
- // PG guard: probe information_schema first (no lock) for each table; run
524
- // DDL only when missing, bounded by a transaction-scoped lock_timeout.
525
- // Probe names are UNQUOTED (what information_schema.tables.table_name stores);
526
- // createSql keeps the QUOTED "user"/"session"/… form required by Postgres.
527
- if (isPostgres()) {
528
- const pgTables = [
529
- [
530
- "user",
531
- `CREATE TABLE IF NOT EXISTS "user" (id TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, email_verified BOOLEAN NOT NULL DEFAULT FALSE, image TEXT, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL)`,
532
- ],
533
- [
534
- "session",
535
- `CREATE TABLE IF NOT EXISTS "session" (id TEXT PRIMARY KEY, expires_at TIMESTAMPTZ NOT NULL, token TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL, ip_address TEXT, user_agent TEXT, user_id TEXT NOT NULL, active_organization_id TEXT)`,
536
- ],
537
- [
538
- "account",
539
- `CREATE TABLE IF NOT EXISTS "account" (id TEXT PRIMARY KEY, account_id TEXT NOT NULL, provider_id TEXT NOT NULL, user_id TEXT NOT NULL, access_token TEXT, refresh_token TEXT, id_token TEXT, access_token_expires_at TIMESTAMPTZ, refresh_token_expires_at TIMESTAMPTZ, scope TEXT, password TEXT, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL)`,
540
- ],
541
- [
542
- "verification",
543
- `CREATE TABLE IF NOT EXISTS "verification" (id TEXT PRIMARY KEY, identifier TEXT NOT NULL, value TEXT NOT NULL, expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL)`,
544
- ],
545
- [
546
- "organization",
547
- `CREATE TABLE IF NOT EXISTS "organization" (id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, logo TEXT, metadata TEXT, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL)`,
548
- ],
549
- [
550
- "member",
551
- `CREATE TABLE IF NOT EXISTS "member" (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, user_id TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'member', created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL)`,
552
- ],
553
- [
554
- "invitation",
555
- `CREATE TABLE IF NOT EXISTS "invitation" (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, email TEXT NOT NULL, role TEXT, status TEXT NOT NULL DEFAULT 'pending', expires_at TIMESTAMPTZ NOT NULL, inviter_id TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL)`,
556
- ],
557
- [
558
- "jwks",
559
- `CREATE TABLE IF NOT EXISTS "jwks" (id TEXT PRIMARY KEY, public_key TEXT NOT NULL, private_key TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL, expires_at TIMESTAMPTZ)`,
560
- ],
561
- ];
562
- for (const [name, sql] of pgTables)
563
- await ensureTableExists(name, sql);
564
- return;
565
- }
566
- // SQLite (local dev): no lock problem — keep the original behaviour.
567
- const sqliteStatements = [
568
- `CREATE TABLE IF NOT EXISTS user (id TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, email_verified INTEGER NOT NULL DEFAULT 0, image TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
569
- `CREATE TABLE IF NOT EXISTS session (id TEXT PRIMARY KEY, expires_at INTEGER NOT NULL, token TEXT NOT NULL UNIQUE, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, ip_address TEXT, user_agent TEXT, user_id TEXT NOT NULL, active_organization_id TEXT)`,
570
- `CREATE TABLE IF NOT EXISTS account (id TEXT PRIMARY KEY, account_id TEXT NOT NULL, provider_id TEXT NOT NULL, user_id TEXT NOT NULL, access_token TEXT, refresh_token TEXT, id_token TEXT, access_token_expires_at INTEGER, refresh_token_expires_at INTEGER, scope TEXT, password TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
571
- `CREATE TABLE IF NOT EXISTS verification (id TEXT PRIMARY KEY, identifier TEXT NOT NULL, value TEXT NOT NULL, expires_at INTEGER NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
572
- `CREATE TABLE IF NOT EXISTS organization (id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, logo TEXT, metadata TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
573
- `CREATE TABLE IF NOT EXISTS member (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, user_id TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'member', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
574
- `CREATE TABLE IF NOT EXISTS invitation (id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, email TEXT NOT NULL, role TEXT, status TEXT NOT NULL DEFAULT 'pending', expires_at INTEGER NOT NULL, inviter_id TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
575
- `CREATE TABLE IF NOT EXISTS jwks (id TEXT PRIMARY KEY, public_key TEXT NOT NULL, private_key TEXT NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER)`,
576
- ];
577
- for (const sql of sqliteStatements)
578
- await db.execute(sql);
579
- }
580
520
  /**
581
521
  * Get or create the Better Auth instance.
582
522
  * Lazily initialized on first call — the database must be reachable by then.
@@ -860,7 +800,6 @@ function resetAuthOnPoolClose(driver, url) {
860
800
  async function createBetterAuthInstance(config) {
861
801
  const dialect = getDialect();
862
802
  const basePath = config?.basePath ?? "/_agent-native/auth/ba";
863
- await ensureBetterAuthTables();
864
803
  // Build social providers from env vars
865
804
  const socialProviders = {
866
805
  ...config?.socialProviders,
@@ -0,0 +1,8 @@
1
+ import { type MigrationEntry } from "../db/migrations.js";
2
+ /**
3
+ * Better Auth's framework-owned schema. This is deliberately a release
4
+ * migration, not a fallback inside `getBetterAuth()`: request functions must
5
+ * be able to construct the auth adapter without probing or creating tables.
6
+ */
7
+ export declare const BETTER_AUTH_MIGRATIONS: MigrationEntry[];
8
+ export declare function runBetterAuthMigrations(nitroApp: unknown): Promise<void>;