@agent-native/core 0.209.2 → 0.209.3-nightly-20261016212700

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 (79) hide show
  1. package/dist/a2a-claims.js +5 -5
  2. package/dist/agent/context-xray/plugin.js +2 -6
  3. package/dist/agent/context-xray/schema-store.d.ts +1 -0
  4. package/dist/agent/context-xray/schema-store.js +7 -0
  5. package/dist/agent/harness/store.js +3 -0
  6. package/dist/agent/observational-memory/plugin.js +2 -6
  7. package/dist/agent/observational-memory/store.js +3 -0
  8. package/dist/agent/run-store.js +3 -0
  9. package/dist/agent/tool-approval-store.js +4 -0
  10. package/dist/automation/index.d.ts +1 -1
  11. package/dist/chat-threads/store.js +3 -0
  12. package/dist/client/action-query-cache-context.d.ts +1 -0
  13. package/dist/client/action-query-cache-context.js +2 -0
  14. package/dist/client/action-query-cache.d.ts +1 -1
  15. package/dist/client/action-query-cache.js +34 -15
  16. package/dist/client/motion-text.css +8 -0
  17. package/dist/client/motion-text.d.ts +6 -0
  18. package/dist/client/motion-text.js +154 -29
  19. package/dist/client/use-action.js +18 -1
  20. package/dist/collab/routes.d.ts +1 -1
  21. package/dist/db/migrations.d.ts +1 -0
  22. package/dist/db/migrations.js +1 -1
  23. package/dist/db/runtime-facts.d.ts +7 -0
  24. package/dist/db/runtime-facts.js +18 -0
  25. package/dist/db/schema-catalog.js +4 -1
  26. package/dist/db/store-ledger.test-helpers.d.ts +5 -0
  27. package/dist/db/store-ledger.test-helpers.js +19 -0
  28. package/dist/db/store-registry.d.ts +34 -0
  29. package/dist/db/store-registry.generated.js +13 -0
  30. package/dist/db/store-registry.js +288 -46
  31. package/dist/db/versioned-store-migrations.d.ts +14 -0
  32. package/dist/db/versioned-store-migrations.js +37 -0
  33. package/dist/db/widen-columns.js +5 -2
  34. package/dist/extensions/html-shell.js +67 -0
  35. package/dist/integrations/remote-devices-store.js +3 -0
  36. package/dist/jobs/run-history.d.ts +3 -2
  37. package/dist/jobs/run-history.js +5 -5
  38. package/dist/jobs/scheduler-health.d.ts +3 -2
  39. package/dist/jobs/scheduler-health.js +5 -5
  40. package/dist/mcp/build-server.d.ts +3 -0
  41. package/dist/mcp/build-server.js +24 -7
  42. package/dist/mcp/server.js +40 -9
  43. package/dist/notifications/routes.d.ts +1 -1
  44. package/dist/oauth-tokens/store.js +3 -0
  45. package/dist/observability/routes.d.ts +4 -4
  46. package/dist/observability/store.js +4 -0
  47. package/dist/org/auto-join-domain.js +23 -15
  48. package/dist/org/context.d.ts +12 -0
  49. package/dist/org/context.js +27 -19
  50. package/dist/org/handlers.js +31 -13
  51. package/dist/org/index.d.ts +2 -1
  52. package/dist/org/index.js +1 -0
  53. package/dist/org/plugin.js +2 -4
  54. package/dist/org/schema-store.d.ts +1 -0
  55. package/dist/org/schema-store.js +13 -0
  56. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  57. package/dist/provider-api/actions/provider-api.d.ts +9 -9
  58. package/dist/resources/store.js +5 -2
  59. package/dist/server/agent-access.js +2 -6
  60. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  61. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  62. package/dist/server/auth-plugin.js +2 -6
  63. package/dist/server/auth.js +22 -2
  64. package/dist/server/better-auth-migrations.d.ts +4 -2
  65. package/dist/server/better-auth-migrations.js +18 -6
  66. package/dist/server/identity-sso-store.js +3 -0
  67. package/dist/server/realtime-token.d.ts +1 -1
  68. package/dist/server/release-migrations.d.ts +1 -1
  69. package/dist/server/release-migrations.js +3 -65
  70. package/dist/server/release-schema.js +10 -2
  71. package/dist/server/short-lived-token.d.ts +3 -2
  72. package/dist/server/short-lived-token.js +3 -2
  73. package/dist/triggers/actions/manage-automation.d.ts +3 -3
  74. package/dist/triggers/event-queue.d.ts +4 -2
  75. package/dist/triggers/event-queue.js +38 -38
  76. package/dist/usage/alerts-store.js +3 -0
  77. package/dist/workspace-connections/groups.js +4 -0
  78. package/dist/workspace-connections/store.js +3 -0
  79. package/package.json +3 -3
@@ -316,6 +316,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
316
316
  notes?: string | undefined;
317
317
  scope?: "org" | "user" | undefined;
318
318
  }, {
319
+ id?: undefined;
320
+ deleted?: undefined;
319
321
  message?: undefined;
320
322
  providers: {
321
323
  id: string;
@@ -329,33 +331,31 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
329
331
  count: number;
330
332
  provider?: undefined;
331
333
  found?: undefined;
332
- deleted?: undefined;
333
334
  registered?: undefined;
334
- id?: undefined;
335
335
  label?: undefined;
336
336
  } | {
337
- message?: undefined;
337
+ id?: undefined;
338
+ deleted?: undefined;
338
339
  count?: undefined;
340
+ message?: undefined;
339
341
  providers?: undefined;
340
342
  found: boolean;
341
343
  provider: import("../custom-registry.js").CustomProviderConfig;
342
- deleted?: undefined;
343
344
  registered?: undefined;
344
- id?: undefined;
345
345
  label?: undefined;
346
346
  } | {
347
- message?: undefined;
347
+ deleted?: undefined;
348
348
  count?: undefined;
349
+ message?: undefined;
349
350
  providers?: undefined;
350
351
  provider?: undefined;
351
352
  found: boolean;
352
353
  id: string;
353
- deleted?: undefined;
354
354
  registered?: undefined;
355
355
  label?: undefined;
356
356
  } | {
357
- message?: undefined;
358
357
  count?: undefined;
358
+ message?: undefined;
359
359
  providers?: undefined;
360
360
  provider?: undefined;
361
361
  found?: undefined;
@@ -364,11 +364,11 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
364
364
  registered?: undefined;
365
365
  label?: undefined;
366
366
  } | {
367
+ deleted?: undefined;
367
368
  count?: undefined;
368
369
  providers?: undefined;
369
370
  provider?: undefined;
370
371
  found?: undefined;
371
- deleted?: undefined;
372
372
  registered: boolean;
373
373
  id: string;
374
374
  label: string;
@@ -3,6 +3,7 @@ import { getDbExec } from "../db/client.js";
3
3
  import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
4
4
  import { isMigrationAuthorizedRuntime } from "../db/migration-runtime.js";
5
5
  import { retainRequestDbPoolScope } from "../db/request-pool-context.js";
6
+ import { isRecordingStoreMigration } from "../db/runtime-facts.js";
6
7
  import { defineStore } from "../db/store-registry.js";
7
8
  import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
8
9
  import { noteJobFrontmatterWrite } from "../jobs/frontmatter-loss.js";
@@ -866,8 +867,9 @@ export const resourcesStore = defineStore({
866
867
  run: async () => {
867
868
  await ensureIndexExists("resources_visibility_expires_idx", `CREATE INDEX IF NOT EXISTS resources_visibility_expires_idx ON resources (visibility, expires_at)`).catch((err) => {
868
869
  // At release, fail so the ledger never records an index that was not built.
869
- if (isMigrationAuthorizedRuntime())
870
+ if (isMigrationAuthorizedRuntime() || isRecordingStoreMigration()) {
870
871
  throw err;
872
+ }
871
873
  // An index is an optimization, not a correctness requirement: a
872
874
  // concurrent creator or a permissions edge must not fail table init and
873
875
  // take the app down with it. The scan it avoids is slow, not wrong — but
@@ -889,8 +891,9 @@ export const resourcesStore = defineStore({
889
891
  // `CREATE INDEX CONCURRENTLY` without creating anything (see the matching
890
892
  // note in chat-threads/store.ts). A plain build is the form that lands.
891
893
  await ensureIndexExists("resources_path_pattern_idx", `CREATE INDEX IF NOT EXISTS resources_path_pattern_idx ON resources (path text_pattern_ops)`).catch((err) => {
892
- if (isMigrationAuthorizedRuntime())
894
+ if (isMigrationAuthorizedRuntime() || isRecordingStoreMigration()) {
893
895
  throw err;
896
+ }
894
897
  // coercion-ok: absence of an index degrades latency, never correctness
895
898
  console.warn("[resources] could not ensure resources_path_pattern_idx; prefix reads such as the jobs/ fingerprint will full-scan:", err?.message ?? err);
896
899
  });
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_AGENT_ACCESS_TTL_SECONDS, scopedAgentAccessResourceId, } from "../shared/agent-access.js";
2
- import { signCompactShortLivedToken, verifyCompactShortLivedToken, verifyShortLivedToken, } from "./short-lived-token.js";
2
+ import { signCompactShortLivedToken, verifyCompactShortLivedToken, } from "./short-lived-token.js";
3
3
  export { AGENT_ACCESS_PARAM, DEFAULT_AGENT_ACCESS_TTL_SECONDS, appendAgentAccessParam, buildAgentAccessApiUrl, buildAgentAccessUrl, normalizeAgentAccessBasePath, normalizeAgentAccessOrigin, scopedAgentAccessResourceId, toAgentAccessUrl, } from "../shared/agent-access.js";
4
4
  export function signScopedAgentAccessToken({ resourceKind, resourceId, viewerEmail, agentLabel, ttlSeconds = DEFAULT_AGENT_ACCESS_TTL_SECONDS, }) {
5
5
  return signCompactShortLivedToken({
@@ -12,11 +12,7 @@ export function signScopedAgentAccessToken({ resourceKind, resourceId, viewerEma
12
12
  export function verifyScopedAgentAccessToken(token, scope) {
13
13
  if (!token)
14
14
  return { ok: false, reason: "missing" };
15
- const resourceId = scopedAgentAccessResourceId(scope.resourceKind, scope.resourceId);
16
- const compact = verifyCompactShortLivedToken(token, resourceId);
17
- if (compact.ok || compact.reason !== "bad_signature")
18
- return compact;
19
- return verifyShortLivedToken(token, resourceId);
15
+ return verifyCompactShortLivedToken(token, scopedAgentAccessResourceId(scope.resourceKind, scope.resourceId));
20
16
  }
21
17
  export function createScopedAgentAccessGrant(options) {
22
18
  const ttlSeconds = options.ttlSeconds ?? DEFAULT_AGENT_ACCESS_TTL_SECONDS;
@@ -67,12 +67,12 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
67
67
  code: any;
68
68
  } | {
69
69
  errorCode?: undefined;
70
- error?: undefined;
71
70
  code?: undefined;
72
71
  ok: boolean;
73
72
  key: string;
74
73
  baseUrlKey?: string;
75
74
  scope: AgentEngineApiKeyScope;
76
75
  defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
76
+ error?: undefined;
77
77
  }>>;
78
78
  export {};
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
8
8
  * resolveEngine's credential detection.
9
9
  */
10
10
  export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
11
- ok?: undefined;
12
11
  error: string;
12
+ ok?: undefined;
13
13
  scope?: undefined;
14
14
  } | {
15
15
  ok: boolean;
@@ -1,12 +1,8 @@
1
- import { runMigrations } from "../db/migrations.js";
2
- import { ORG_MIGRATIONS } from "../org/migrations.js";
1
+ import { orgStore } from "../org/schema-store.js";
3
2
  import { authSessionHandler, autoMountAuth } from "./auth.js";
4
3
  import { runBetterAuthMigrations } from "./better-auth-migrations.js";
5
4
  import { FRAMEWORK_AUTH_EARLY_PATHS, getH3App, markDefaultPluginProvided, markFrameworkRoutesReadyBeforeBootstrap, trackPluginInit, } from "./framework-request-handler.js";
6
5
  export function createAuthPlugin(options) {
7
- const migrateOrgSchema = runMigrations(ORG_MIGRATIONS, {
8
- table: "_org_migrations",
9
- });
10
6
  return (nitroApp) => {
11
7
  markDefaultPluginProvided(nitroApp, "auth");
12
8
  const isByoa = Boolean(options?.getSession);
@@ -38,7 +34,7 @@ export function createAuthPlugin(options) {
38
34
  // transaction, where a missing table aborts the sign-up. The org
39
35
  // plugin migrates only after the whole bootstrap, too late for that.
40
36
  const mountPromise = runBetterAuthMigrations(nitroApp)
41
- .then(() => migrateOrgSchema(nitroApp))
37
+ .then(() => orgStore.ready())
42
38
  .then(() => autoMountAuth(app, options));
43
39
  markFrameworkRoutesReadyBeforeBootstrap(nitroApp, FRAMEWORK_AUTH_EARLY_PATHS);
44
40
  await mountPromise;
@@ -802,11 +802,21 @@ export async function getMcpOAuthBearerSession(event) {
802
802
  const bearerToken = getBearerSessionToken(event);
803
803
  if (!bearerToken)
804
804
  return null;
805
+ let identityVerificationUnavailableErrorClass;
806
+ let identityVerificationRefusedErrorClass;
807
+ let connectUrl;
805
808
  try {
806
- const [{ getMcpConnectUrl, getMcpOAuthAudiences }, { verifyAuth, resolveMcpIdentityOrgId },] = await Promise.all([
809
+ const [oauthRoute, mcp] = await Promise.all([
807
810
  import("../mcp/oauth-route.js"),
808
811
  import("../mcp/build-server.js"),
809
812
  ]);
813
+ connectUrl = oauthRoute.getMcpConnectUrl(event);
814
+ identityVerificationUnavailableErrorClass =
815
+ mcp.McpIdentityVerificationUnavailableError;
816
+ identityVerificationRefusedErrorClass =
817
+ mcp.McpIdentityVerificationRefusedError;
818
+ const { getMcpOAuthAudiences } = oauthRoute;
819
+ const { verifyAuth, resolveMcpIdentityOrgId } = mcp;
810
820
  const result = await verifyAuth(authHeader, undefined, {
811
821
  resourceUrl: getMcpOAuthAudiences(event),
812
822
  allowDevOpen: false,
@@ -818,7 +828,7 @@ export async function getMcpOAuthBearerSession(event) {
818
828
  }
819
829
  if (!result.authed) {
820
830
  if (result.refusal) {
821
- markBearerCredentialRefused(event, result.refusal, getMcpConnectUrl(event));
831
+ markBearerCredentialRefused(event, result.refusal, connectUrl);
822
832
  }
823
833
  return null;
824
834
  }
@@ -843,6 +853,16 @@ export async function getMcpOAuthBearerSession(event) {
843
853
  };
844
854
  }
845
855
  catch (e) {
856
+ if (identityVerificationRefusedErrorClass &&
857
+ e instanceof identityVerificationRefusedErrorClass) {
858
+ markBearerCredentialRefused(event, "invalid", connectUrl);
859
+ return null;
860
+ }
861
+ if (identityVerificationUnavailableErrorClass &&
862
+ e instanceof identityVerificationUnavailableErrorClass) {
863
+ markCredentialMembershipUnavailable(event);
864
+ return null;
865
+ }
846
866
  console.error("[auth] MCP OAuth bearer verification error:", e);
847
867
  return null;
848
868
  }
@@ -1,4 +1,4 @@
1
- import { type MigrationEntry } from "../db/migrations.js";
1
+ import type { MigrationEntry } from "../db/migrations.js";
2
2
  /**
3
3
  * Better Auth encrypts persisted JWT private keys with the current auth
4
4
  * secret. If that secret is rotated, the old row remains the newest key and
@@ -19,4 +19,6 @@ export declare function expireJwksKeysAfterAuthSecretRotation(): Promise<void>;
19
19
  * be able to construct the auth adapter without probing or creating tables.
20
20
  */
21
21
  export declare const BETTER_AUTH_MIGRATIONS: MigrationEntry[];
22
- export declare function runBetterAuthMigrations(nitroApp: unknown): Promise<void>;
22
+ export declare const betterAuthStore: import("../db/store-registry.js").Store;
23
+ /** Kept for callers of the public export; the store owns these migrations. */
24
+ export declare function runBetterAuthMigrations(_nitroApp?: unknown): Promise<void>;
@@ -1,4 +1,5 @@
1
- import { runMigrations } from "../db/migrations.js";
1
+ import { defineStore } from "../db/store-registry.js";
2
+ import { fromMigrationEntries } from "../db/versioned-store-migrations.js";
2
3
  async function assertBetterAuthUserIdentityColumns() {
3
4
  const { getDbExec } = await import("../db/client.js");
4
5
  const { rows } = await getDbExec().execute(`SELECT column_name FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'user' AND column_name IN ('id', 'email')`);
@@ -170,7 +171,9 @@ export const BETTER_AUTH_MIGRATIONS = [
170
171
  },
171
172
  },
172
173
  {
173
- version: 5,
174
+ // Shipped as a second v5. Named entries are gated by name, so the new
175
+ // number changes nothing for databases that already applied it.
176
+ version: 11,
174
177
  name: "better-auth-user-lower-email-index",
175
178
  sql: {
176
179
  postgres: 'CREATE INDEX IF NOT EXISTS better_auth_user_lower_email_idx ON "user" (LOWER(email))',
@@ -447,8 +450,17 @@ export const BETTER_AUTH_MIGRATIONS = [
447
450
  },
448
451
  },
449
452
  ];
450
- export async function runBetterAuthMigrations(nitroApp) {
451
- await runMigrations(BETTER_AUTH_MIGRATIONS, {
452
- table: "_better_auth_migrations",
453
- })(nitroApp);
453
+ export const betterAuthStore = defineStore({
454
+ id: "better_auth",
455
+ // v3 creates the legacy `sessions` table the auth-sessions store owns; on
456
+ // main every store ran before this list.
457
+ after: ["auth_sessions"],
458
+ migrations: fromMigrationEntries("_better_auth_migrations", BETTER_AUTH_MIGRATIONS,
459
+ // v4 expires every active signing key: it must run at most once per
460
+ // database, which the ledger (or the imported old bookkeeping) guarantees.
461
+ { ranAtBoot: true, data: [4] }),
462
+ });
463
+ /** Kept for callers of the public export; the store owns these migrations. */
464
+ export async function runBetterAuthMigrations(_nitroApp) {
465
+ await betterAuthStore.ready();
454
466
  }
@@ -2,6 +2,8 @@ import { randomBytes } from "node:crypto";
2
2
  import { getDbExec, isConnectionError } from "../db/client.js";
3
3
  import { ensureTableExists } from "../db/ddl-guard.js";
4
4
  import { defineStore } from "../db/store-registry.js";
5
+ import { fromMigrationEntries } from "../db/versioned-store-migrations.js";
6
+ import { IDENTITY_SSO_MIGRATIONS } from "./identity-sso-migrations.js";
5
7
  const DESKTOP_SSO_USER_AGENT = /AgentNativeDesktop(?:SsoCanary)?\//i;
6
8
  const DESKTOP_SSO_CANARY_USER_AGENT = /AgentNativeDesktopSsoCanary\//i;
7
9
  export const CANONICAL_IDENTITY_SSO_HUB_URL = "https://dispatch.agent-native.com";
@@ -223,6 +225,7 @@ export const identitySsoStore = defineStore({
223
225
  await ensureTableExists("identity_sso_jti", buildIdentitySsoJtiCreateSql());
224
226
  },
225
227
  },
228
+ ...fromMigrationEntries("_identity_sso_migrations", IDENTITY_SSO_MIGRATIONS),
226
229
  ],
227
230
  });
228
231
  export function ensureTable() {
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
54
54
  expiresAt?: undefined;
55
55
  ttlSeconds?: undefined;
56
56
  } | {
57
- error?: undefined;
58
57
  token: string;
59
58
  expiresAt: string;
60
59
  ttlSeconds: number;
60
+ error?: undefined;
61
61
  }>>;
@@ -1 +1 @@
1
- export declare function runFrameworkReleaseMigrations(nitroApp: unknown): Promise<void>;
1
+ export declare function runFrameworkReleaseMigrations(_nitroApp?: unknown): Promise<void>;
@@ -1,26 +1,8 @@
1
- import { CONTEXT_XRAY_MIGRATIONS } from "../agent/context-xray/migrations.js";
2
- import { AGENT_HARNESS_SESSION_MIGRATIONS, AGENT_HARNESS_SESSION_MIGRATIONS_TABLE, } from "../agent/harness/migrations.js";
3
- import { OBSERVATIONAL_MEMORY_MIGRATIONS } from "../agent/observational-memory/migrations.js";
4
- import { AGENT_RUN_MIGRATIONS, AGENT_RUN_MIGRATIONS_TABLE, } from "../agent/run-migrations.js";
5
- import { AGENT_TOOL_APPROVAL_MIGRATIONS, AGENT_TOOL_APPROVAL_MIGRATIONS_TABLE, } from "../agent/tool-approval-migrations.js";
6
1
  import { getAppConfig } from "../app-config/index.js";
7
- import { CHAT_THREAD_SCHEMA_MIGRATIONS, CHAT_THREAD_SCHEMA_MIGRATIONS_TABLE, } from "../chat-threads/schema-migrations.js";
8
2
  import { getDatabaseUrl } from "../db/client.js";
9
- import { runMigrations } from "../db/migrations.js";
10
3
  import { withStoreMigrationLock } from "../db/store-registry.js";
11
- import { REMOTE_DEVICE_MIGRATIONS, REMOTE_DEVICE_MIGRATIONS_TABLE, } from "../integrations/remote-device-migrations.js";
12
- import { runAutomationRunMigrations } from "../jobs/run-history.js";
13
- import { runAutomationSchedulerHealthMigrations } from "../jobs/scheduler-health.js";
14
- import { OAUTH_TOKEN_MIGRATIONS, OAUTH_TOKEN_MIGRATIONS_TABLE, } from "../oauth-tokens/migrations.js";
15
- import { OBSERVABILITY_MIGRATIONS, OBSERVABILITY_MIGRATIONS_TABLE, } from "../observability/migrations.js";
16
- import { ORG_MIGRATIONS } from "../org/migrations.js";
17
4
  import { ensureSharingAccessIndexes } from "../sharing/indexes.js";
18
- import { runAutomationTriggerEventMigrations } from "../triggers/event-queue.js";
19
- import { USAGE_ALERT_MIGRATIONS, USAGE_ALERT_MIGRATIONS_TABLE, } from "../usage/migrations.js";
20
- import { WORKSPACE_CONNECTIONS_MIGRATIONS, WORKSPACE_CONNECTIONS_MIGRATIONS_TABLE, } from "../workspace-connections/migrations.js";
21
- import { runBetterAuthMigrations } from "./better-auth-migrations.js";
22
5
  import { recordDatabaseIdentity } from "./database-identity.js";
23
- import { IDENTITY_SSO_MIGRATIONS } from "./identity-sso-migrations.js";
24
6
  import { runFrameworkSchemaEnsures } from "./release-schema.js";
25
7
  /**
26
8
  * A release deploy whose `DATABASE_URL` resolves to a local development
@@ -62,57 +44,13 @@ function describeReleaseMigrationUrl(url) {
62
44
  const scheme = url.includes("://") ? url.split("://")[0] : undefined;
63
45
  return scheme ? `${scheme} url` : "no scheme — likely a masked secret";
64
46
  }
65
- export async function runFrameworkReleaseMigrations(nitroApp) {
47
+ export async function runFrameworkReleaseMigrations(_nitroApp) {
66
48
  assertReleaseMigrationTargetsRemoteDatabase();
67
- // One lease for the whole release: the store pass, Better Auth and every
68
- // versioned list, so two deploys against one database never interleave.
49
+ // One lease for the whole release, so two deploys against one database
50
+ // never interleave.
69
51
  await withStoreMigrationLock(async () => {
70
- // First: the versioned migration lists below only cover the tables that have
71
- // one. Most framework tables are defined by their store's `ensureTable()`,
72
- // which production serverless can never run — see `./release-schema.ts`.
73
52
  await runFrameworkSchemaEnsures();
74
53
  await recordDatabaseIdentity();
75
- await runBetterAuthMigrations(nitroApp);
76
- await runMigrations(AGENT_TOOL_APPROVAL_MIGRATIONS, {
77
- table: AGENT_TOOL_APPROVAL_MIGRATIONS_TABLE,
78
- })(nitroApp);
79
- await runMigrations(OAUTH_TOKEN_MIGRATIONS, {
80
- table: OAUTH_TOKEN_MIGRATIONS_TABLE,
81
- })(nitroApp);
82
- await runMigrations(CHAT_THREAD_SCHEMA_MIGRATIONS, {
83
- table: CHAT_THREAD_SCHEMA_MIGRATIONS_TABLE,
84
- })(nitroApp);
85
- await runMigrations(OBSERVABILITY_MIGRATIONS, {
86
- table: OBSERVABILITY_MIGRATIONS_TABLE,
87
- })(nitroApp);
88
- await runMigrations(AGENT_RUN_MIGRATIONS, {
89
- table: AGENT_RUN_MIGRATIONS_TABLE,
90
- })(nitroApp);
91
- await runMigrations(AGENT_HARNESS_SESSION_MIGRATIONS, {
92
- table: AGENT_HARNESS_SESSION_MIGRATIONS_TABLE,
93
- })(nitroApp);
94
- await runMigrations(USAGE_ALERT_MIGRATIONS, {
95
- table: USAGE_ALERT_MIGRATIONS_TABLE,
96
- })(nitroApp);
97
- await runMigrations(ORG_MIGRATIONS, { table: "_org_migrations" })(nitroApp);
98
- await runMigrations(REMOTE_DEVICE_MIGRATIONS, {
99
- table: REMOTE_DEVICE_MIGRATIONS_TABLE,
100
- })(nitroApp);
101
- await runMigrations(IDENTITY_SSO_MIGRATIONS, {
102
- table: "_identity_sso_migrations",
103
- })(nitroApp);
104
- await runMigrations(CONTEXT_XRAY_MIGRATIONS, {
105
- table: "_context_xray_migrations",
106
- })(nitroApp);
107
- await runMigrations(OBSERVATIONAL_MEMORY_MIGRATIONS, {
108
- table: "_observational_memory_migrations",
109
- })(nitroApp);
110
- await runMigrations(WORKSPACE_CONNECTIONS_MIGRATIONS, {
111
- table: WORKSPACE_CONNECTIONS_MIGRATIONS_TABLE,
112
- })(nitroApp);
113
- await runAutomationRunMigrations(nitroApp);
114
- await runAutomationTriggerEventMigrations(nitroApp);
115
- await runAutomationSchedulerHealthMigrations(nitroApp);
116
54
  // Last, so the framework's own share and owner tables exist. An app's tables
117
55
  // are indexed by the release after the one that creates them.
118
56
  await ensureSharingAccessIndexes();
@@ -9,11 +9,16 @@
9
9
  */
10
10
  import { isMigrationAuthorizedRuntime } from "../db/migration-runtime.js";
11
11
  import { FRAMEWORK_STORES } from "../db/store-registry.generated.js";
12
- import { SchemaNotMigratedError, findUnappliedStoreMigrations, withStoreMigrationLock, } from "../db/store-registry.js";
12
+ import { SchemaNotMigratedError, findUnappliedStoreMigrations, orderStoresForRelease, withStoreMigrationLock, } from "../db/store-registry.js";
13
13
  const FRAMEWORK_SCHEMA_ENSURES = FRAMEWORK_STORES.map(([id, load]) => [id, () => load().then((store) => store.ready())]);
14
14
  export function frameworkSchemaEnsureNames() {
15
15
  return FRAMEWORK_SCHEMA_ENSURES.map(([name]) => name);
16
16
  }
17
+ /** Every registered store, loaded and ordered by its `after` dependencies. */
18
+ async function orderedFrameworkEnsures() {
19
+ const stores = await Promise.all(FRAMEWORK_STORES.map(([, load]) => load()));
20
+ return orderStoresForRelease(stores).map((store) => [store.id, () => store.ready()]);
21
+ }
17
22
  /**
18
23
  * Create every framework-owned table, in one pass, before the app serves
19
24
  * traffic. Callers must hold migration duty via `withMigrationRuntime`; without
@@ -31,8 +36,11 @@ export async function runFrameworkSchemaEnsures(ensures = FRAMEWORK_SCHEMA_ENSUR
31
36
  if (!isMigrationAuthorizedRuntime()) {
32
37
  throw new Error("runFrameworkSchemaEnsures must run inside withMigrationRuntime(); outside it no store is migrated.");
33
38
  }
39
+ const pass = ensures === FRAMEWORK_SCHEMA_ENSURES
40
+ ? await orderedFrameworkEnsures()
41
+ : ensures;
34
42
  await withStoreMigrationLock(async () => {
35
- for (const [name, run] of ensures) {
43
+ for (const [name, run] of pass) {
36
44
  try {
37
45
  await run();
38
46
  }
@@ -34,8 +34,9 @@ export declare function verifyShortLivedToken(token: string, expectedResourceId:
34
34
  export declare function signCompactShortLivedToken(claims: ShortLivedTokenClaims): string;
35
35
  /**
36
36
  * Verify a token produced by {@link signCompactShortLivedToken}. A token for a
37
- * different resource, and any legacy token, fails as `bad_signature` — the id
38
- * is not in the payload, so a mismatch is indistinguishable from tampering.
37
+ * different resource, and any `signShortLivedToken` token, fails as
38
+ * `bad_signature` — the id is not in the payload, so a mismatch is
39
+ * indistinguishable from tampering.
39
40
  */
40
41
  export declare function verifyCompactShortLivedToken(token: string, expectedResourceId: string): VerifyResult;
41
42
  /** Payload `typ` discriminator for realtime subscribe tokens. */
@@ -123,8 +123,9 @@ export function signCompactShortLivedToken(claims) {
123
123
  }
124
124
  /**
125
125
  * Verify a token produced by {@link signCompactShortLivedToken}. A token for a
126
- * different resource, and any legacy token, fails as `bad_signature` — the id
127
- * is not in the payload, so a mismatch is indistinguishable from tampering.
126
+ * different resource, and any `signShortLivedToken` token, fails as
127
+ * `bad_signature` — the id is not in the payload, so a mismatch is
128
+ * indistinguishable from tampering.
128
129
  */
129
130
  export function verifyCompactShortLivedToken(token, expectedResourceId) {
130
131
  if (typeof token !== "string" || !token.includes(".")) {
@@ -14,6 +14,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
14
14
  executionCwd?: string;
15
15
  }, {
16
16
  deleted?: undefined;
17
+ enabled?: undefined;
17
18
  created: boolean;
18
19
  name: string;
19
20
  scope: "organization" | "personal";
@@ -24,25 +25,24 @@ declare const _default: import("../../action.js").ActionDefinition<{
24
25
  reasoningEffort: "auto" | "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh";
25
26
  webhookPath: string;
26
27
  nextRun: string;
27
- enabled?: undefined;
28
28
  executionHostId?: undefined;
29
29
  executionEngine?: undefined;
30
30
  executionCwd?: undefined;
31
31
  } | {
32
+ enabled?: undefined;
33
+ nextRun?: undefined;
32
34
  created?: undefined;
33
35
  scope?: undefined;
34
36
  triggerType?: undefined;
35
37
  event?: undefined;
36
38
  deleted: boolean;
37
39
  name: string;
38
- enabled?: undefined;
39
40
  schedule?: undefined;
40
41
  timezone?: undefined;
41
42
  reasoningEffort?: undefined;
42
43
  executionHostId?: undefined;
43
44
  executionEngine?: undefined;
44
45
  executionCwd?: undefined;
45
- nextRun?: undefined;
46
46
  webhookPath?: undefined;
47
47
  } | {
48
48
  deleted?: undefined;
@@ -1,11 +1,13 @@
1
- import { type MigrationEntry } from "../db/migrations.js";
1
+ import type { MigrationEntry } from "../db/migrations.js";
2
2
  export declare const AUTOMATION_TRIGGER_EVENT_DEDUPE_RETENTION_MS: number;
3
3
  export declare const AUTOMATION_TRIGGER_EVENT_PURGE_BATCH_SIZE = 1000;
4
4
  export declare const AUTOMATION_TRIGGER_EVENT_EXPIRY_BATCH_SIZE = 1000;
5
5
  export declare const MAX_AUTOMATION_TRIGGER_EVENT_FAILURES = 8;
6
6
  declare const CLAIM_LEASE_MS: () => number;
7
7
  export declare const AUTOMATION_TRIGGER_EVENT_MIGRATIONS: MigrationEntry[];
8
- export declare function runAutomationTriggerEventMigrations(nitroApp: unknown): Promise<void>;
8
+ export declare const automationTriggerEventsStore: import("../db/store-registry.js").Store;
9
+ /** Kept for callers of the export; the store owns these migrations. */
10
+ export declare function runAutomationTriggerEventMigrations(_nitroApp?: unknown): Promise<void>;
9
11
  export declare function ensureAutomationTriggerEventQueue(): Promise<void>;
10
12
  export interface EnqueueAutomationTriggerEventInput {
11
13
  triggerId: string;
@@ -1,10 +1,9 @@
1
- // guard:allow-unreleased-schema — owned by the versioned AUTOMATION_TRIGGER_EVENT_MIGRATIONS list in release-migrations.ts until Phase 2 folds versioned lists into stores
2
1
  import { randomUUID } from "node:crypto";
3
2
  import { resolveBackgroundRunHardTimeoutMs } from "../agent/run-manager.js";
4
3
  import { getDbExec } from "../db/client.js";
5
4
  import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
6
- import { runMigrations } from "../db/migrations.js";
7
- import { retainRequestDbPoolScope } from "../db/request-pool-context.js";
5
+ import { defineStore } from "../db/store-registry.js";
6
+ import { fromMigrationEntries } from "../db/versioned-store-migrations.js";
8
7
  const TABLE = "automation_trigger_event_queue";
9
8
  const SWEEP_STATE_TABLE = "automation_trigger_event_queue_sweep_state";
10
9
  const COMPLETED_PAYLOAD = '{"kind":"completed"}';
@@ -91,42 +90,43 @@ export const AUTOMATION_TRIGGER_EVENT_MIGRATIONS = [
91
90
  WHERE status IN ('pending', 'processing')`,
92
91
  },
93
92
  ];
94
- export async function runAutomationTriggerEventMigrations(nitroApp) {
95
- await runMigrations(AUTOMATION_TRIGGER_EVENT_MIGRATIONS, {
96
- table: "_automation_trigger_event_migrations",
97
- })(nitroApp);
93
+ export const automationTriggerEventsStore = defineStore({
94
+ id: "automation_trigger_events",
95
+ migrations: [
96
+ {
97
+ name: "baseline",
98
+ run: async () => {
99
+ await ensureTableExists(TABLE, CREATE_TABLE_SQL);
100
+ await ensureTableExists(SWEEP_STATE_TABLE, CREATE_SWEEP_STATE_TABLE_SQL);
101
+ await ensureColumnExists(TABLE, "failure_attempts", `ALTER TABLE ${TABLE}
102
+ ADD COLUMN IF NOT EXISTS failure_attempts BIGINT NOT NULL DEFAULT 0`);
103
+ await ensureIndexExists(UNIQUE_INDEX, `CREATE UNIQUE INDEX IF NOT EXISTS ${UNIQUE_INDEX}
104
+ ON ${TABLE} (trigger_id, event_id)`);
105
+ await ensureIndexExists(ORDER_INDEX, `CREATE INDEX IF NOT EXISTS ${ORDER_INDEX}
106
+ ON ${TABLE} (trigger_id, sequence_id, status)`);
107
+ await ensureIndexExists(READY_INDEX, `CREATE INDEX IF NOT EXISTS ${READY_INDEX}
108
+ ON ${TABLE} (app_id, status, available_at, claimed_at)`);
109
+ await ensureIndexExists(ACTIVE_HEAD_INDEX, `CREATE INDEX IF NOT EXISTS ${ACTIVE_HEAD_INDEX}
110
+ ON ${TABLE} (app_id, trigger_id, sequence_id)
111
+ WHERE status IN ('pending', 'processing')`);
112
+ await ensureIndexExists(FAILED_INDEX, `CREATE INDEX IF NOT EXISTS ${FAILED_INDEX}
113
+ ON ${TABLE} (completed_at) WHERE status = 'failed'`);
114
+ await ensureIndexExists(STALE_EVENT_INDEX, `CREATE INDEX IF NOT EXISTS ${STALE_EVENT_INDEX}
115
+ ON ${TABLE} (app_id, event_name, emitted_at)
116
+ WHERE status = 'pending'`);
117
+ await ensureIndexExists(COMPLETED_INDEX, `CREATE INDEX IF NOT EXISTS ${COMPLETED_INDEX}
118
+ ON ${TABLE} (completed_at) WHERE status = 'completed'`);
119
+ },
120
+ },
121
+ ...fromMigrationEntries("_automation_trigger_event_migrations", AUTOMATION_TRIGGER_EVENT_MIGRATIONS),
122
+ ],
123
+ });
124
+ /** Kept for callers of the export; the store owns these migrations. */
125
+ export async function runAutomationTriggerEventMigrations(_nitroApp) {
126
+ await automationTriggerEventsStore.ready();
98
127
  }
99
- let _initPromise;
100
- export async function ensureAutomationTriggerEventQueue() {
101
- if (!_initPromise) {
102
- const _initPromiseWithRequestDbPoolScope = (async () => {
103
- await ensureTableExists(TABLE, CREATE_TABLE_SQL);
104
- await ensureTableExists(SWEEP_STATE_TABLE, CREATE_SWEEP_STATE_TABLE_SQL);
105
- await ensureColumnExists(TABLE, "failure_attempts", `ALTER TABLE ${TABLE}
106
- ADD COLUMN IF NOT EXISTS failure_attempts BIGINT NOT NULL DEFAULT 0`);
107
- await ensureIndexExists(UNIQUE_INDEX, `CREATE UNIQUE INDEX IF NOT EXISTS ${UNIQUE_INDEX}
108
- ON ${TABLE} (trigger_id, event_id)`);
109
- await ensureIndexExists(ORDER_INDEX, `CREATE INDEX IF NOT EXISTS ${ORDER_INDEX}
110
- ON ${TABLE} (trigger_id, sequence_id, status)`);
111
- await ensureIndexExists(READY_INDEX, `CREATE INDEX IF NOT EXISTS ${READY_INDEX}
112
- ON ${TABLE} (app_id, status, available_at, claimed_at)`);
113
- await ensureIndexExists(ACTIVE_HEAD_INDEX, `CREATE INDEX IF NOT EXISTS ${ACTIVE_HEAD_INDEX}
114
- ON ${TABLE} (app_id, trigger_id, sequence_id)
115
- WHERE status IN ('pending', 'processing')`);
116
- await ensureIndexExists(FAILED_INDEX, `CREATE INDEX IF NOT EXISTS ${FAILED_INDEX}
117
- ON ${TABLE} (completed_at) WHERE status = 'failed'`);
118
- await ensureIndexExists(STALE_EVENT_INDEX, `CREATE INDEX IF NOT EXISTS ${STALE_EVENT_INDEX}
119
- ON ${TABLE} (app_id, event_name, emitted_at)
120
- WHERE status = 'pending'`);
121
- await ensureIndexExists(COMPLETED_INDEX, `CREATE INDEX IF NOT EXISTS ${COMPLETED_INDEX}
122
- ON ${TABLE} (completed_at) WHERE status = 'completed'`);
123
- })().catch((error) => {
124
- _initPromise = undefined;
125
- throw error;
126
- });
127
- _initPromise = retainRequestDbPoolScope(_initPromiseWithRequestDbPoolScope);
128
- }
129
- return _initPromise;
128
+ export function ensureAutomationTriggerEventQueue() {
129
+ return automationTriggerEventsStore.ready();
130
130
  }
131
131
  function rowToEvent(row) {
132
132
  if (row.claimed_at == null || !Number.isFinite(Number(row.claimed_at))) {
@@ -1,8 +1,10 @@
1
1
  import { getDbExec } from "../db/client.js";
2
2
  import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
3
3
  import { defineStore } from "../db/store-registry.js";
4
+ import { fromMigrationEntries } from "../db/versioned-store-migrations.js";
4
5
  import { deleteNotification, notifyWithDelivery, } from "../notifications/index.js";
5
6
  import { ForbiddenError } from "../sharing/access.js";
7
+ import { USAGE_ALERT_MIGRATIONS, USAGE_ALERT_MIGRATIONS_TABLE, } from "./migrations.js";
6
8
  import { builderCreditsFromCostCents } from "./store.js";
7
9
  const DAY_MS = 86_400_000;
8
10
  const DEFAULT_DAILY_USD = 100;
@@ -128,6 +130,7 @@ export const usageAlertsStore = defineStore({
128
130
  }
129
131
  },
130
132
  },
133
+ ...fromMigrationEntries(USAGE_ALERT_MIGRATIONS_TABLE, USAGE_ALERT_MIGRATIONS),
131
134
  ],
132
135
  });
133
136
  export function ensureTables() {
@@ -2,8 +2,10 @@ import { randomUUID } from "node:crypto";
2
2
  import { getDbExec, isUniqueViolation, retryOnDdlRace, safeJsonParse, } from "../db/client.js";
3
3
  import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
4
4
  import { defineStore } from "../db/store-registry.js";
5
+ import { fromMigrationEntries } from "../db/versioned-store-migrations.js";
5
6
  import { isOrgMember } from "../org/membership.js";
6
7
  import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
8
+ import { WORKSPACE_CONNECTIONS_MIGRATIONS, WORKSPACE_CONNECTIONS_MIGRATIONS_TABLE, } from "./migrations.js";
7
9
  export function workspaceUserGroupsTable() {
8
10
  return "public.workspace_user_groups";
9
11
  }
@@ -124,6 +126,7 @@ async function ensureWorkspaceUserGroupNameTrigger(client) {
124
126
  }
125
127
  export const workspaceUserGroupsStore = defineStore({
126
128
  id: "workspace_user_groups",
129
+ after: ["workspace_connections"],
127
130
  // Hosted requests never created these tables on main, release-owned or not.
128
131
  releaseOnly: true,
129
132
  migrations: [
@@ -153,6 +156,7 @@ export const workspaceUserGroupsStore = defineStore({
153
156
  WHERE normalized_name IS NOT NULL`);
154
157
  },
155
158
  },
159
+ ...fromMigrationEntries(WORKSPACE_CONNECTIONS_MIGRATIONS_TABLE, WORKSPACE_CONNECTIONS_MIGRATIONS.filter((entry) => entry.version >= 8)),
156
160
  ],
157
161
  });
158
162
  export function ensureWorkspaceUserGroupsTable() {