@axiom-lattice/pg-stores 5.0.3 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +95 -0
  3. package/dist/index.d.mts +178 -2
  4. package/dist/index.d.ts +178 -2
  5. package/dist/index.js +1243 -197
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +1167 -124
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +3 -3
  10. package/src/__tests__/PostgreSQLInviteLinkStore.test.ts +63 -0
  11. package/src/__tests__/PostgreSQLOidcIdentityClaimStore.test.ts +123 -0
  12. package/src/__tests__/PostgreSQLOidcProviderConfigStore.test.ts +233 -0
  13. package/src/__tests__/PostgreSQLPlatformInvitationStore.test.ts +222 -0
  14. package/src/__tests__/PostgreSQLPlatformMemberStore.test.ts +146 -0
  15. package/src/__tests__/PostgreSQLPlatformSettingsStore.test.ts +115 -0
  16. package/src/__tests__/PostgreSQLUserExternalIdentityStore.test.ts +125 -0
  17. package/src/__tests__/PostgreSQLUserStore.test.ts +75 -0
  18. package/src/createPgStoreConfig.ts +34 -0
  19. package/src/index.ts +16 -0
  20. package/src/migrations/invite_link_migration.ts +35 -0
  21. package/src/migrations/oidc_identity_claim_invite_migration.ts +29 -0
  22. package/src/migrations/oidc_identity_claim_migration.ts +39 -0
  23. package/src/migrations/oidc_provider_allowed_hosts_migration.ts +19 -0
  24. package/src/migrations/oidc_provider_config_migration.ts +27 -0
  25. package/src/migrations/platform_invitation_migration.ts +22 -0
  26. package/src/migrations/platform_member_migration.ts +21 -0
  27. package/src/migrations/platform_settings_migration.ts +21 -0
  28. package/src/migrations/user_email_lower_unique_migration.ts +22 -0
  29. package/src/migrations/user_external_identity_migration.ts +27 -0
  30. package/src/stores/PostgreSQLInviteLinkStore.ts +134 -0
  31. package/src/stores/PostgreSQLOidcIdentityClaimStore.ts +251 -0
  32. package/src/stores/PostgreSQLOidcProviderConfigStore.ts +222 -0
  33. package/src/stores/PostgreSQLPlatformInvitationStore.ts +133 -0
  34. package/src/stores/PostgreSQLPlatformMemberStore.ts +105 -0
  35. package/src/stores/PostgreSQLPlatformSettingsStore.ts +98 -0
  36. package/src/stores/PostgreSQLUserExternalIdentityStore.ts +134 -0
  37. package/src/stores/PostgreSQLUserStore.ts +3 -2
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  InvalidFileShareAddressError: () => InvalidFileShareAddressError,
40
40
  InvalidMcpAppShareAddressError: () => InvalidMcpAppShareAddressError,
41
41
  InvalidMcpAppShareVisibilityError: () => InvalidMcpAppShareVisibilityError,
42
+ InvalidUserExternalIdentityRowError: () => InvalidUserExternalIdentityRowError,
42
43
  MenuStore: () => MenuStore,
43
44
  MigrationManager: () => MigrationManager,
44
45
  PGVectorStoreProvider: () => PGVectorStoreProvider,
@@ -55,10 +56,16 @@ __export(index_exports, {
55
56
  PostgreSQLConnectionStore: () => PostgreSQLConnectionStore,
56
57
  PostgreSQLDatabaseConfigStore: () => PostgreSQLDatabaseConfigStore,
57
58
  PostgreSQLEvalStore: () => PostgreSQLEvalStore,
59
+ PostgreSQLInviteLinkStore: () => PostgreSQLInviteLinkStore,
58
60
  PostgreSQLMcpServerConfigStore: () => PostgreSQLMcpServerConfigStore,
59
61
  PostgreSQLMetricsServerConfigStore: () => PostgreSQLMetricsServerConfigStore,
60
62
  PostgreSQLModelProviderStore: () => PostgreSQLModelProviderStore,
63
+ PostgreSQLOidcIdentityClaimStore: () => PostgreSQLOidcIdentityClaimStore,
64
+ PostgreSQLOidcProviderConfigStore: () => PostgreSQLOidcProviderConfigStore,
61
65
  PostgreSQLOpenAuditStore: () => PostgreSQLOpenAuditStore,
66
+ PostgreSQLPlatformInvitationStore: () => PostgreSQLPlatformInvitationStore,
67
+ PostgreSQLPlatformMemberStore: () => PostgreSQLPlatformMemberStore,
68
+ PostgreSQLPlatformSettingsStore: () => PostgreSQLPlatformSettingsStore,
62
69
  PostgreSQLPluginDeploymentStore: () => PostgreSQLPluginDeploymentStore,
63
70
  PostgreSQLProjectBotMembershipStore: () => PostgreSQLProjectBotMembershipStore,
64
71
  PostgreSQLProjectMembershipStore: () => PostgreSQLProjectMembershipStore,
@@ -72,6 +79,7 @@ __export(index_exports, {
72
79
  PostgreSQLTaskWorkItemStore: () => PostgreSQLTaskWorkItemStore,
73
80
  PostgreSQLTenantStore: () => PostgreSQLTenantStore,
74
81
  PostgreSQLThreadStore: () => PostgreSQLThreadStore,
82
+ PostgreSQLUserExternalIdentityStore: () => PostgreSQLUserExternalIdentityStore,
75
83
  PostgreSQLUserStore: () => PostgreSQLUserStore,
76
84
  PostgreSQLUserTenantLinkStore: () => PostgreSQLUserTenantLinkStore,
77
85
  PostgreSQLWorkflowTrackingStore: () => PostgreSQLWorkflowTrackingStore,
@@ -89,6 +97,7 @@ __export(index_exports, {
89
97
  addInterruptColumnsToEval: () => addInterruptColumnsToEval,
90
98
  addInterruptPolicyToEvalCases: () => addInterruptPolicyToEvalCases,
91
99
  addMcpAppContentType: () => addMcpAppContentType,
100
+ addOidcIdentityClaimInviteFields: () => addOidcIdentityClaimInviteFields,
92
101
  addProjectConfigColumn: () => addProjectConfigColumn,
93
102
  addScheduleTenantId: () => addScheduleTenantId,
94
103
  addShareKind: () => addShareKind,
@@ -118,11 +127,17 @@ __export(index_exports, {
118
127
  createEvalRunResultsTable: () => createEvalRunResultsTable,
119
128
  createEvalRunsTable: () => createEvalRunsTable,
120
129
  createEvalSuitesTable: () => createEvalSuitesTable,
130
+ createInviteLinksTable: () => createInviteLinksTable,
121
131
  createMcpServerConfigsTable: () => createMcpServerConfigsTable,
122
132
  createMenuItemsTable: () => createMenuItemsTable,
123
133
  createMetricsConfigsTable: () => createMetricsConfigsTable,
134
+ createOidcIdentityClaimsTable: () => createOidcIdentityClaimsTable,
135
+ createOidcProviderConfigsTable: () => createOidcProviderConfigsTable,
124
136
  createPGVectorStoreProvider: () => createPGVectorStoreProvider,
125
137
  createPgStoreConfig: () => createPgStoreConfig,
138
+ createPlatformInvitationsTable: () => createPlatformInvitationsTable,
139
+ createPlatformMembersTable: () => createPlatformMembersTable,
140
+ createPlatformSettingsTable: () => createPlatformSettingsTable,
126
141
  createPluginDeployments: () => createPluginDeployments,
127
142
  createProjectRoomReadStates: () => createProjectRoomReadStates,
128
143
  createProjectRoomTables: () => createProjectRoomTables,
@@ -134,6 +149,8 @@ __export(index_exports, {
134
149
  createTenantsTable: () => createTenantsTable,
135
150
  createThreadMessageQueueTable: () => createThreadMessageQueueTable,
136
151
  createThreadsTable: () => createThreadsTable,
152
+ createUserEmailLowerUniqueIndex: () => createUserEmailLowerUniqueIndex,
153
+ createUserExternalIdentityTable: () => createUserExternalIdentityTable,
137
154
  createUsersTable: () => createUsersTable,
138
155
  createWorkflowTrackingTables: () => createWorkflowTrackingTables,
139
156
  createWorkspacesTable: () => createWorkspacesTable,
@@ -3094,9 +3111,10 @@ var PostgreSQLUserStore = class {
3094
3111
  `
3095
3112
  SELECT id, email, name, status, metadata, created_at, updated_at
3096
3113
  FROM lattice_users
3097
- WHERE email = $1
3114
+ WHERE lower(email) = lower($1)
3115
+ LIMIT 1
3098
3116
  `,
3099
- [email]
3117
+ [email.trim()]
3100
3118
  );
3101
3119
  if (result.rows.length === 0) return null;
3102
3120
  return this.mapRowToUser(result.rows[0]);
@@ -3610,30 +3628,705 @@ var PostgreSQLUserTenantLinkStore = class {
3610
3628
  `,
3611
3629
  updateValues
3612
3630
  );
3613
- return this.getLink(userId, tenantId);
3631
+ return this.getLink(userId, tenantId);
3632
+ }
3633
+ async deleteLink(userId, tenantId) {
3634
+ await this.ensureInitialized();
3635
+ const result = await this.pool.query(
3636
+ `
3637
+ DELETE FROM lattice_user_tenant_links
3638
+ WHERE user_id = $1 AND tenant_id = $2
3639
+ `,
3640
+ [userId, tenantId]
3641
+ );
3642
+ return result.rowCount !== null && result.rowCount > 0;
3643
+ }
3644
+ async hasLink(userId, tenantId) {
3645
+ await this.ensureInitialized();
3646
+ const result = await this.pool.query(
3647
+ `
3648
+ SELECT 1 FROM lattice_user_tenant_links
3649
+ WHERE user_id = $1 AND tenant_id = $2
3650
+ LIMIT 1
3651
+ `,
3652
+ [userId, tenantId]
3653
+ );
3654
+ return result.rows.length > 0;
3655
+ }
3656
+ };
3657
+
3658
+ // src/stores/PostgreSQLUserExternalIdentityStore.ts
3659
+ var import_protocols2 = require("@axiom-lattice/protocols");
3660
+ var InvalidUserExternalIdentityRowError = class extends Error {
3661
+ constructor() {
3662
+ super("Invalid user external identity row");
3663
+ this.code = "INVALID_USER_EXTERNAL_IDENTITY_ROW";
3664
+ this.name = "InvalidUserExternalIdentityRowError";
3665
+ }
3666
+ };
3667
+ var columns = "user_id, provider_id, issuer, subject, email, linked_at";
3668
+ function mapRow(row) {
3669
+ if (typeof row.user_id !== "string" || typeof row.provider_id !== "string" || typeof row.issuer !== "string" || typeof row.subject !== "string" || !(row.linked_at instanceof Date) || Number.isNaN(row.linked_at.getTime())) {
3670
+ throw new InvalidUserExternalIdentityRowError();
3671
+ }
3672
+ return {
3673
+ userId: row.user_id,
3674
+ providerId: row.provider_id,
3675
+ issuer: row.issuer,
3676
+ subject: row.subject,
3677
+ ...typeof row.email === "string" ? { email: row.email } : {},
3678
+ linkedAt: row.linked_at
3679
+ };
3680
+ }
3681
+ var PostgreSQLUserExternalIdentityStore = class {
3682
+ constructor(options) {
3683
+ this.pool = options.pool;
3684
+ }
3685
+ async getByProviderSubject(providerId, issuer, subject) {
3686
+ const result = await this.pool.query(
3687
+ `SELECT ${columns} FROM lattice_user_identities
3688
+ WHERE provider_id = $1 AND issuer = $2 AND subject = $3`,
3689
+ [providerId, issuer, subject]
3690
+ );
3691
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
3692
+ }
3693
+ async listByUser(userId) {
3694
+ const result = await this.pool.query(
3695
+ `SELECT ${columns} FROM lattice_user_identities WHERE user_id = $1 ORDER BY linked_at ASC`,
3696
+ [userId]
3697
+ );
3698
+ return result.rows.map(mapRow);
3699
+ }
3700
+ async link(identity) {
3701
+ const inserted = await this.pool.query(
3702
+ `INSERT INTO lattice_user_identities (${columns})
3703
+ VALUES ($1, $2, $3, $4, $5, $6)
3704
+ ON CONFLICT (provider_id, issuer, subject) DO NOTHING
3705
+ RETURNING ${columns}`,
3706
+ [
3707
+ identity.userId,
3708
+ identity.providerId,
3709
+ identity.issuer,
3710
+ identity.subject,
3711
+ identity.email ?? null,
3712
+ identity.linkedAt
3713
+ ]
3714
+ );
3715
+ if (inserted.rows[0]) return;
3716
+ const existing = await this.getByProviderSubject(
3717
+ identity.providerId,
3718
+ identity.issuer,
3719
+ identity.subject
3720
+ );
3721
+ if (!existing) {
3722
+ throw new import_protocols2.ExternalIdentityPersistError(
3723
+ identity.providerId,
3724
+ identity.issuer,
3725
+ identity.subject
3726
+ );
3727
+ }
3728
+ if (existing.userId !== identity.userId) {
3729
+ throw new import_protocols2.ExternalIdentityConflictError(
3730
+ identity.providerId,
3731
+ identity.issuer,
3732
+ identity.subject
3733
+ );
3734
+ }
3735
+ if (identity.email !== void 0 && identity.email !== existing.email) {
3736
+ await this.pool.query(
3737
+ `UPDATE lattice_user_identities SET email = $1
3738
+ WHERE provider_id = $2 AND issuer = $3 AND subject = $4`,
3739
+ [identity.email, identity.providerId, identity.issuer, identity.subject]
3740
+ );
3741
+ }
3742
+ }
3743
+ async unlink(providerId, issuer, subject) {
3744
+ await this.pool.query(
3745
+ `DELETE FROM lattice_user_identities
3746
+ WHERE provider_id = $1 AND issuer = $2 AND subject = $3`,
3747
+ [providerId, issuer, subject]
3748
+ );
3749
+ }
3750
+ };
3751
+
3752
+ // src/stores/PostgreSQLOidcProviderConfigStore.ts
3753
+ var import_core5 = require("@axiom-lattice/core");
3754
+ var import_protocols3 = require("@axiom-lattice/protocols");
3755
+ var TABLE2 = "lattice_oidc_provider_configs";
3756
+ function parseAllowedPrivateHosts(value) {
3757
+ if (!Array.isArray(value)) return void 0;
3758
+ return value.filter((entry) => typeof entry === "string");
3759
+ }
3760
+ function serializeAllowedPrivateHosts(value) {
3761
+ return value === void 0 ? null : JSON.stringify(value);
3762
+ }
3763
+ function normalizeTenantId(value) {
3764
+ if (typeof value !== "string") return void 0;
3765
+ const trimmed = value.trim();
3766
+ return trimmed.length > 0 ? trimmed : void 0;
3767
+ }
3768
+ function mapRow2(row) {
3769
+ if (typeof row.id !== "string" || typeof row.display_name !== "string" || typeof row.issuer !== "string" || typeof row.client_id !== "string" || typeof row.client_secret !== "string" || typeof row.scopes !== "string" || typeof row.enabled !== "boolean" || typeof row.jit_provisioning !== "boolean" || typeof row.auto_link_email !== "boolean" || !(row.created_at instanceof Date) || !(row.updated_at instanceof Date)) {
3770
+ throw new Error("Invalid OIDC provider config row");
3771
+ }
3772
+ const allowedPrivateHosts = parseAllowedPrivateHosts(
3773
+ row.allowed_private_hosts
3774
+ );
3775
+ return {
3776
+ id: row.id,
3777
+ displayName: row.display_name,
3778
+ issuer: row.issuer,
3779
+ clientId: row.client_id,
3780
+ clientSecret: (0, import_core5.safeDecrypt)(
3781
+ row.client_secret,
3782
+ `OIDC provider config "${row.id}" client secret`
3783
+ ),
3784
+ scopes: row.scopes,
3785
+ enabled: row.enabled,
3786
+ jitProvisioning: row.jit_provisioning,
3787
+ autoLinkEmail: row.auto_link_email,
3788
+ ...typeof row.default_tenant_id === "string" ? { defaultTenantId: row.default_tenant_id } : {},
3789
+ ...allowedPrivateHosts !== void 0 ? { allowedPrivateHosts } : {},
3790
+ createdAt: row.created_at,
3791
+ updatedAt: row.updated_at
3792
+ };
3793
+ }
3794
+ function isUniqueViolation(error) {
3795
+ return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
3796
+ }
3797
+ var PostgreSQLOidcProviderConfigStore = class {
3798
+ constructor(options) {
3799
+ this.pool = options.pool;
3800
+ }
3801
+ async list() {
3802
+ const result = await this.pool.query(
3803
+ `SELECT * FROM ${TABLE2} ORDER BY created_at ASC, id ASC`
3804
+ );
3805
+ return result.rows.map(mapRow2);
3806
+ }
3807
+ async get(id) {
3808
+ const result = await this.pool.query(
3809
+ `SELECT * FROM ${TABLE2} WHERE id = $1`,
3810
+ [id]
3811
+ );
3812
+ return result.rows[0] ? mapRow2(result.rows[0]) : null;
3813
+ }
3814
+ async create(id, input) {
3815
+ const tenantId = normalizeTenantId(input.defaultTenantId);
3816
+ try {
3817
+ await this.pool.query(
3818
+ `INSERT INTO ${TABLE2}
3819
+ (id, display_name, issuer, client_id, client_secret, scopes,
3820
+ enabled, jit_provisioning, auto_link_email, default_tenant_id,
3821
+ allowed_private_hosts)
3822
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
3823
+ [
3824
+ id,
3825
+ input.displayName,
3826
+ input.issuer,
3827
+ input.clientId,
3828
+ (0, import_core5.encrypt)(input.clientSecret),
3829
+ input.scopes,
3830
+ input.enabled,
3831
+ input.jitProvisioning,
3832
+ input.autoLinkEmail,
3833
+ tenantId ?? null,
3834
+ serializeAllowedPrivateHosts(input.allowedPrivateHosts)
3835
+ ]
3836
+ );
3837
+ } catch (error) {
3838
+ if (isUniqueViolation(error)) {
3839
+ throw new import_protocols3.OidcProviderConfigConflictError(id);
3840
+ }
3841
+ throw error;
3842
+ }
3843
+ const created = await this.get(id);
3844
+ if (!created) {
3845
+ throw new Error(`OIDC provider config "${id}" was not persisted`);
3846
+ }
3847
+ return created;
3848
+ }
3849
+ async update(id, updates) {
3850
+ const existing = await this.get(id);
3851
+ if (!existing) return null;
3852
+ const setClauses = [];
3853
+ const values = [];
3854
+ let paramIndex = 1;
3855
+ const set = (column, value) => {
3856
+ setClauses.push(`${column} = $${paramIndex++}`);
3857
+ values.push(value);
3858
+ };
3859
+ if (updates.displayName !== void 0) set("display_name", updates.displayName);
3860
+ if (updates.issuer !== void 0) set("issuer", updates.issuer);
3861
+ if (updates.clientId !== void 0) set("client_id", updates.clientId);
3862
+ if (updates.clientSecret !== void 0 && updates.clientSecret !== "") {
3863
+ set("client_secret", (0, import_core5.encrypt)(updates.clientSecret));
3864
+ }
3865
+ if (updates.scopes !== void 0) set("scopes", updates.scopes);
3866
+ if (updates.enabled !== void 0) set("enabled", updates.enabled);
3867
+ if (updates.jitProvisioning !== void 0) {
3868
+ set("jit_provisioning", updates.jitProvisioning);
3869
+ }
3870
+ if (updates.autoLinkEmail !== void 0) {
3871
+ set("auto_link_email", updates.autoLinkEmail);
3872
+ }
3873
+ if (updates.defaultTenantId !== void 0) {
3874
+ set("default_tenant_id", normalizeTenantId(updates.defaultTenantId) ?? null);
3875
+ }
3876
+ if (updates.allowedPrivateHosts !== void 0) {
3877
+ set(
3878
+ "allowed_private_hosts",
3879
+ serializeAllowedPrivateHosts(updates.allowedPrivateHosts)
3880
+ );
3881
+ }
3882
+ if (setClauses.length === 0) return existing;
3883
+ setClauses.push("updated_at = NOW()");
3884
+ values.push(id);
3885
+ await this.pool.query(
3886
+ `UPDATE ${TABLE2} SET ${setClauses.join(", ")} WHERE id = $${paramIndex}`,
3887
+ values
3888
+ );
3889
+ return this.get(id);
3890
+ }
3891
+ async delete(id) {
3892
+ const result = await this.pool.query(`DELETE FROM ${TABLE2} WHERE id = $1`, [id]);
3893
+ return (result.rowCount ?? 0) > 0;
3894
+ }
3895
+ };
3896
+
3897
+ // src/stores/PostgreSQLPlatformMemberStore.ts
3898
+ var TABLE3 = "lattice_platform_members";
3899
+ var ROLES = ["owner", "admin", "auditor"];
3900
+ function isPlatformRole(value) {
3901
+ return ROLES.includes(value);
3902
+ }
3903
+ function mapRow3(row) {
3904
+ if (typeof row.user_id !== "string" || typeof row.role !== "string" || !isPlatformRole(row.role) || !(row.granted_at instanceof Date) || !(row.updated_at instanceof Date)) {
3905
+ throw new Error("Invalid platform member row");
3906
+ }
3907
+ return {
3908
+ userId: row.user_id,
3909
+ role: row.role,
3910
+ ...typeof row.granted_by === "string" ? { grantedBy: row.granted_by } : {},
3911
+ grantedAt: row.granted_at,
3912
+ updatedAt: row.updated_at
3913
+ };
3914
+ }
3915
+ var PostgreSQLPlatformMemberStore = class {
3916
+ constructor(options) {
3917
+ this.pool = options.pool;
3918
+ }
3919
+ /** Lists every platform member ordered by user id. */
3920
+ async list() {
3921
+ const result = await this.pool.query(
3922
+ `SELECT * FROM ${TABLE3} ORDER BY user_id ASC`
3923
+ );
3924
+ return result.rows.map(mapRow3);
3925
+ }
3926
+ /** Returns the platform member for a user, or null. */
3927
+ async get(userId) {
3928
+ const result = await this.pool.query(
3929
+ `SELECT * FROM ${TABLE3} WHERE user_id = $1`,
3930
+ [userId]
3931
+ );
3932
+ return result.rows[0] ? mapRow3(result.rows[0]) : null;
3933
+ }
3934
+ /**
3935
+ * Inserts or replaces a member keyed by user id.
3936
+ *
3937
+ * The supplied grant replaces any existing row in full (last write wins),
3938
+ * including `grantedAt` and `updatedAt`.
3939
+ */
3940
+ async upsert(member) {
3941
+ const result = await this.pool.query(
3942
+ `INSERT INTO ${TABLE3} (user_id, role, granted_by, granted_at, updated_at)
3943
+ VALUES ($1, $2, $3, $4, $5)
3944
+ ON CONFLICT (user_id) DO UPDATE SET
3945
+ role = EXCLUDED.role,
3946
+ granted_by = EXCLUDED.granted_by,
3947
+ granted_at = EXCLUDED.granted_at,
3948
+ updated_at = EXCLUDED.updated_at
3949
+ RETURNING *`,
3950
+ [
3951
+ member.userId,
3952
+ member.role,
3953
+ member.grantedBy ?? null,
3954
+ member.grantedAt,
3955
+ member.updatedAt
3956
+ ]
3957
+ );
3958
+ return mapRow3(result.rows[0]);
3959
+ }
3960
+ /** Deletes a member. Returns whether a row was removed. */
3961
+ async delete(userId) {
3962
+ const result = await this.pool.query(`DELETE FROM ${TABLE3} WHERE user_id = $1`, [
3963
+ userId
3964
+ ]);
3965
+ return (result.rowCount ?? 0) > 0;
3966
+ }
3967
+ };
3968
+
3969
+ // src/stores/PostgreSQLPlatformInvitationStore.ts
3970
+ var import_protocols4 = require("@axiom-lattice/protocols");
3971
+ var TABLE4 = "lattice_platform_invitations";
3972
+ var ROLES2 = ["owner", "admin", "auditor"];
3973
+ function isPlatformRole2(value) {
3974
+ return ROLES2.includes(value);
3975
+ }
3976
+ function mapRow4(row) {
3977
+ if (typeof row.email !== "string" || typeof row.role !== "string" || !isPlatformRole2(row.role) || !(row.created_at instanceof Date)) {
3978
+ throw new Error("Invalid platform invitation row");
3979
+ }
3980
+ return {
3981
+ email: row.email,
3982
+ role: row.role,
3983
+ ...typeof row.invited_by === "string" ? { invitedBy: row.invited_by } : {},
3984
+ createdAt: row.created_at,
3985
+ ...row.expires_at instanceof Date ? { expiresAt: row.expires_at } : {},
3986
+ ...row.consumed_at instanceof Date ? { consumedAt: row.consumed_at } : {}
3987
+ };
3988
+ }
3989
+ var PostgreSQLPlatformInvitationStore = class {
3990
+ constructor(options) {
3991
+ this.pool = options.pool;
3992
+ }
3993
+ /** Lists every invitation ordered by normalized email. */
3994
+ async list() {
3995
+ const result = await this.pool.query(
3996
+ `SELECT * FROM ${TABLE4} ORDER BY email ASC`
3997
+ );
3998
+ return result.rows.map(mapRow4);
3999
+ }
4000
+ /** Returns the invitation for an email, or null. */
4001
+ async get(email) {
4002
+ const result = await this.pool.query(
4003
+ `SELECT * FROM ${TABLE4} WHERE email = $1`,
4004
+ [(0, import_protocols4.normalizePlatformInvitationEmail)(email)]
4005
+ );
4006
+ return result.rows[0] ? mapRow4(result.rows[0]) : null;
4007
+ }
4008
+ /**
4009
+ * Inserts or replaces an invitation keyed by normalized email.
4010
+ *
4011
+ * The supplied invitation replaces any existing row in full (last write wins),
4012
+ * including `expiresAt` and `consumedAt`.
4013
+ */
4014
+ async upsert(invitation) {
4015
+ const result = await this.pool.query(
4016
+ `INSERT INTO ${TABLE4}
4017
+ (email, role, invited_by, created_at, expires_at, consumed_at)
4018
+ VALUES ($1, $2, $3, $4, $5, $6)
4019
+ ON CONFLICT (email) DO UPDATE SET
4020
+ role = EXCLUDED.role,
4021
+ invited_by = EXCLUDED.invited_by,
4022
+ created_at = EXCLUDED.created_at,
4023
+ expires_at = EXCLUDED.expires_at,
4024
+ consumed_at = EXCLUDED.consumed_at
4025
+ RETURNING *`,
4026
+ [
4027
+ (0, import_protocols4.normalizePlatformInvitationEmail)(invitation.email),
4028
+ invitation.role,
4029
+ invitation.invitedBy ?? null,
4030
+ invitation.createdAt,
4031
+ invitation.expiresAt ?? null,
4032
+ invitation.consumedAt ?? null
4033
+ ]
4034
+ );
4035
+ return mapRow4(result.rows[0]);
4036
+ }
4037
+ /** Deletes an invitation. Returns whether a row was removed. */
4038
+ async delete(email) {
4039
+ const result = await this.pool.query(
4040
+ `DELETE FROM ${TABLE4} WHERE email = $1`,
4041
+ [(0, import_protocols4.normalizePlatformInvitationEmail)(email)]
4042
+ );
4043
+ return (result.rowCount ?? 0) > 0;
4044
+ }
4045
+ /**
4046
+ * Atomically consumes a pending, unexpired invitation.
4047
+ *
4048
+ * Returns the consumed invitation, or null when the row is missing, already
4049
+ * consumed, or expired.
4050
+ */
4051
+ async consume(email, at) {
4052
+ const result = await this.pool.query(
4053
+ `UPDATE ${TABLE4}
4054
+ SET consumed_at = $1
4055
+ WHERE email = $2
4056
+ AND consumed_at IS NULL
4057
+ AND (expires_at IS NULL OR expires_at > $1)
4058
+ RETURNING *`,
4059
+ [at, (0, import_protocols4.normalizePlatformInvitationEmail)(email)]
4060
+ );
4061
+ return result.rows[0] ? mapRow4(result.rows[0]) : null;
4062
+ }
4063
+ };
4064
+
4065
+ // src/stores/PostgreSQLOidcIdentityClaimStore.ts
4066
+ var import_protocols5 = require("@axiom-lattice/protocols");
4067
+ var TABLE5 = "lattice_oidc_identity_claims";
4068
+ function mapRow5(row) {
4069
+ return {
4070
+ id: row.id,
4071
+ providerId: row.provider_id,
4072
+ issuer: row.issuer,
4073
+ subject: row.subject,
4074
+ email: row.email,
4075
+ ...row.name !== null ? { name: row.name } : {},
4076
+ ...row.picture !== null ? { picture: row.picture } : {},
4077
+ status: row.status,
4078
+ createdAt: row.created_at,
4079
+ updatedAt: row.updated_at,
4080
+ expiresAt: row.expires_at,
4081
+ ...row.reviewed_by !== null ? { reviewedBy: row.reviewed_by } : {},
4082
+ ...row.reviewed_at !== null ? { reviewedAt: row.reviewed_at } : {},
4083
+ ...row.linked_user_id !== null ? { linkedUserId: row.linked_user_id } : {},
4084
+ ...row.source !== null ? { source: row.source } : {},
4085
+ ...row.invite_link_id !== null ? { inviteLinkId: row.invite_link_id } : {},
4086
+ ...row.requested_user_id !== null ? { requestedUserId: row.requested_user_id } : {},
4087
+ ...row.requested_platform_role !== null ? { requestedPlatformRole: row.requested_platform_role } : {},
4088
+ ...row.requested_tenant_id !== null ? { requestedTenantId: row.requested_tenant_id } : {},
4089
+ ...row.requested_tenant_role !== null ? { requestedTenantRole: row.requested_tenant_role } : {}
4090
+ };
4091
+ }
4092
+ function isUniqueViolation2(error) {
4093
+ return typeof error === "object" && error !== null && error.code === "23505";
4094
+ }
4095
+ var PostgreSQLOidcIdentityClaimStore = class {
4096
+ constructor(options) {
4097
+ this.pool = options.pool;
4098
+ }
4099
+ async list(status) {
4100
+ const result = status ? await this.pool.query(
4101
+ `SELECT * FROM ${TABLE5} WHERE status = $1
4102
+ ORDER BY created_at ASC, id ASC`,
4103
+ [status]
4104
+ ) : await this.pool.query(
4105
+ `SELECT * FROM ${TABLE5} ORDER BY created_at ASC, id ASC`
4106
+ );
4107
+ return result.rows.map(mapRow5);
4108
+ }
4109
+ async get(id) {
4110
+ const result = await this.pool.query(
4111
+ `SELECT * FROM ${TABLE5} WHERE id = $1`,
4112
+ [id]
4113
+ );
4114
+ return result.rows[0] ? mapRow5(result.rows[0]) : null;
4115
+ }
4116
+ async getPendingBySubject(providerId, issuer, subject, now) {
4117
+ const result = await this.pool.query(
4118
+ `SELECT * FROM ${TABLE5}
4119
+ WHERE provider_id = $1 AND issuer = $2 AND subject = $3
4120
+ AND status = 'pending' AND expires_at > $4`,
4121
+ [providerId, issuer, subject, now]
4122
+ );
4123
+ return result.rows[0] ? mapRow5(result.rows[0]) : null;
4124
+ }
4125
+ async create(input) {
4126
+ const now = /* @__PURE__ */ new Date();
4127
+ await this.pool.query(
4128
+ `UPDATE ${TABLE5}
4129
+ SET status = 'expired', updated_at = $1
4130
+ WHERE provider_id = $2 AND issuer = $3 AND subject = $4
4131
+ AND status = 'pending' AND expires_at <= $1`,
4132
+ [now, input.providerId, input.issuer, input.subject]
4133
+ );
4134
+ try {
4135
+ const result = await this.pool.query(
4136
+ `INSERT INTO ${TABLE5}
4137
+ (id, provider_id, issuer, subject, email, name, picture, status,
4138
+ created_at, updated_at, expires_at, source, invite_link_id,
4139
+ requested_user_id, requested_platform_role, requested_tenant_id,
4140
+ requested_tenant_role)
4141
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending', $8, $8, $9,
4142
+ $10, $11, $12, $13, $14, $15)
4143
+ RETURNING *`,
4144
+ [
4145
+ input.id,
4146
+ input.providerId,
4147
+ input.issuer,
4148
+ input.subject,
4149
+ (0, import_protocols5.normalizeOidcIdentityEmail)(input.email),
4150
+ input.name ?? null,
4151
+ input.picture ?? null,
4152
+ now,
4153
+ input.expiresAt,
4154
+ input.source ?? null,
4155
+ input.inviteLinkId ?? null,
4156
+ input.requestedUserId ?? null,
4157
+ input.requestedPlatformRole ?? null,
4158
+ input.requestedTenantId ?? null,
4159
+ input.requestedTenantRole ?? null
4160
+ ]
4161
+ );
4162
+ return mapRow5(result.rows[0]);
4163
+ } catch (error) {
4164
+ if (isUniqueViolation2(error)) {
4165
+ throw new import_protocols5.OidcIdentityClaimConflictError(
4166
+ input.providerId,
4167
+ input.issuer,
4168
+ input.subject
4169
+ );
4170
+ }
4171
+ throw error;
4172
+ }
4173
+ }
4174
+ async updatePending(id, updates, now) {
4175
+ const sets = [];
4176
+ const params = [];
4177
+ if (updates.email !== void 0) {
4178
+ params.push((0, import_protocols5.normalizeOidcIdentityEmail)(updates.email));
4179
+ sets.push(`email = $${params.length}`);
4180
+ }
4181
+ if (updates.name !== void 0) {
4182
+ params.push(updates.name);
4183
+ sets.push(`name = $${params.length}`);
4184
+ }
4185
+ if (updates.picture !== void 0) {
4186
+ params.push(updates.picture);
4187
+ sets.push(`picture = $${params.length}`);
4188
+ }
4189
+ if (sets.length === 0) return this.get(id);
4190
+ params.push(now);
4191
+ sets.push(`updated_at = $${params.length}`);
4192
+ params.push(id);
4193
+ const result = await this.pool.query(
4194
+ `UPDATE ${TABLE5} SET ${sets.join(", ")}
4195
+ WHERE id = $${params.length} AND status = 'pending'
4196
+ RETURNING *`,
4197
+ params
4198
+ );
4199
+ return result.rows[0] ? mapRow5(result.rows[0]) : null;
4200
+ }
4201
+ async approve(id, approval, now) {
4202
+ const result = await this.pool.query(
4203
+ `UPDATE ${TABLE5}
4204
+ SET status = 'approved', reviewed_by = $1, reviewed_at = $2,
4205
+ linked_user_id = $3, updated_at = $2
4206
+ WHERE id = $4 AND status = 'pending'
4207
+ RETURNING *`,
4208
+ [approval.reviewedBy, now, approval.linkedUserId, id]
4209
+ );
4210
+ return result.rows[0] ? mapRow5(result.rows[0]) : null;
4211
+ }
4212
+ async reject(id, reviewedBy, now) {
4213
+ const result = await this.pool.query(
4214
+ `UPDATE ${TABLE5}
4215
+ SET status = 'rejected', reviewed_by = $1, reviewed_at = $2,
4216
+ updated_at = $2
4217
+ WHERE id = $3 AND status = 'pending'
4218
+ RETURNING *`,
4219
+ [reviewedBy, now, id]
4220
+ );
4221
+ return result.rows[0] ? mapRow5(result.rows[0]) : null;
4222
+ }
4223
+ async delete(id) {
4224
+ const result = await this.pool.query(
4225
+ `DELETE FROM ${TABLE5} WHERE id = $1`,
4226
+ [id]
4227
+ );
4228
+ return (result.rowCount ?? 0) > 0;
4229
+ }
4230
+ };
4231
+
4232
+ // src/stores/PostgreSQLInviteLinkStore.ts
4233
+ var import_core6 = require("@axiom-lattice/core");
4234
+ var import_protocols6 = require("@axiom-lattice/protocols");
4235
+ var TABLE6 = "lattice_invite_links";
4236
+ function mapRow6(row) {
4237
+ return {
4238
+ id: row.id,
4239
+ scope: row.scope,
4240
+ token: (0, import_core6.safeDecrypt)(row.token_encrypted, `Invite link "${row.id}" token`),
4241
+ tokenHash: row.token_hash,
4242
+ ...row.email !== null ? { email: row.email } : {},
4243
+ ...row.platform_role !== null ? { platformRole: row.platform_role } : {},
4244
+ ...row.tenant_id !== null ? { tenantId: row.tenant_id } : {},
4245
+ ...row.tenant_role !== null ? { tenantRole: row.tenant_role } : {},
4246
+ ...row.created_by !== null ? { createdBy: row.created_by } : {},
4247
+ createdAt: row.created_at,
4248
+ updatedAt: row.updated_at,
4249
+ expiresAt: row.expires_at,
4250
+ maxUses: Number(row.max_uses),
4251
+ usedCount: Number(row.used_count),
4252
+ ...row.revoked_at !== null ? { revokedAt: row.revoked_at } : {}
4253
+ };
4254
+ }
4255
+ var PostgreSQLInviteLinkStore = class {
4256
+ constructor(options) {
4257
+ this.options = options;
4258
+ }
4259
+ get pool() {
4260
+ return this.options.pool;
4261
+ }
4262
+ async list() {
4263
+ const result = await this.pool.query(
4264
+ `SELECT * FROM ${TABLE6} ORDER BY created_at DESC, id DESC`
4265
+ );
4266
+ return result.rows.map(mapRow6);
3614
4267
  }
3615
- async deleteLink(userId, tenantId) {
3616
- await this.ensureInitialized();
4268
+ async get(id) {
3617
4269
  const result = await this.pool.query(
3618
- `
3619
- DELETE FROM lattice_user_tenant_links
3620
- WHERE user_id = $1 AND tenant_id = $2
3621
- `,
3622
- [userId, tenantId]
4270
+ `SELECT * FROM ${TABLE6} WHERE id = $1`,
4271
+ [id]
3623
4272
  );
3624
- return result.rowCount !== null && result.rowCount > 0;
4273
+ return result.rows[0] ? mapRow6(result.rows[0]) : null;
3625
4274
  }
3626
- async hasLink(userId, tenantId) {
3627
- await this.ensureInitialized();
4275
+ async getByTokenHash(tokenHash) {
3628
4276
  const result = await this.pool.query(
3629
- `
3630
- SELECT 1 FROM lattice_user_tenant_links
3631
- WHERE user_id = $1 AND tenant_id = $2
3632
- LIMIT 1
3633
- `,
3634
- [userId, tenantId]
4277
+ `SELECT * FROM ${TABLE6} WHERE token_hash = $1`,
4278
+ [tokenHash]
3635
4279
  );
3636
- return result.rows.length > 0;
4280
+ return result.rows[0] ? mapRow6(result.rows[0]) : null;
4281
+ }
4282
+ async create(input) {
4283
+ const now = /* @__PURE__ */ new Date();
4284
+ const result = await this.pool.query(
4285
+ `INSERT INTO ${TABLE6}
4286
+ (id, scope, token_hash, token_encrypted, email, platform_role,
4287
+ tenant_id, tenant_role, created_by, created_at, updated_at,
4288
+ expires_at, max_uses, used_count)
4289
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $10, $11, $12, 0)
4290
+ RETURNING *`,
4291
+ [
4292
+ input.id,
4293
+ input.scope,
4294
+ input.tokenHash,
4295
+ (0, import_core6.encrypt)(input.token),
4296
+ input.email ? (0, import_protocols6.normalizeInviteLinkEmail)(input.email) : null,
4297
+ input.platformRole ?? null,
4298
+ input.tenantId ?? null,
4299
+ input.tenantRole ?? null,
4300
+ input.createdBy ?? null,
4301
+ now,
4302
+ input.expiresAt,
4303
+ input.maxUses
4304
+ ]
4305
+ );
4306
+ return mapRow6(result.rows[0]);
4307
+ }
4308
+ async consume(tokenHash, now) {
4309
+ const result = await this.pool.query(
4310
+ `UPDATE ${TABLE6}
4311
+ SET used_count = used_count + 1, updated_at = $1
4312
+ WHERE token_hash = $2
4313
+ AND revoked_at IS NULL
4314
+ AND expires_at > $1
4315
+ AND used_count < max_uses
4316
+ RETURNING *`,
4317
+ [now, tokenHash]
4318
+ );
4319
+ return result.rows[0] ? mapRow6(result.rows[0]) : null;
4320
+ }
4321
+ async revoke(id, now) {
4322
+ const result = await this.pool.query(
4323
+ `UPDATE ${TABLE6}
4324
+ SET revoked_at = COALESCE(revoked_at, $1), updated_at = $1
4325
+ WHERE id = $2
4326
+ RETURNING *`,
4327
+ [now, id]
4328
+ );
4329
+ return result.rows[0] ? mapRow6(result.rows[0]) : null;
3637
4330
  }
3638
4331
  };
3639
4332
 
@@ -5253,7 +5946,7 @@ var PostgreSQLEvalStore = class {
5253
5946
  // src/stores/ThreadMessageQueueStore.ts
5254
5947
  var import_pg13 = require("pg");
5255
5948
  var import_crypto = __toESM(require("crypto"));
5256
- var import_protocols2 = require("@axiom-lattice/protocols");
5949
+ var import_protocols7 = require("@axiom-lattice/protocols");
5257
5950
 
5258
5951
  // src/migrations/thread_message_queue_migrations.ts
5259
5952
  var createThreadMessageQueueTable = {
@@ -5740,15 +6433,15 @@ var ThreadMessageQueueStore = class {
5740
6433
  priority: row.priority || 0,
5741
6434
  command: row.command ? typeof row.command === "string" ? JSON.parse(row.command) : row.command : void 0,
5742
6435
  custom_run_config: row.custom_run_config ? typeof row.custom_run_config === "string" ? JSON.parse(row.custom_run_config) : row.custom_run_config : void 0,
5743
- trusted_run_context: row.trusted_run_context ? (0, import_protocols2.parseTrustedRunContext)(typeof row.trusted_run_context === "string" ? JSON.parse(row.trusted_run_context) : row.trusted_run_context) : void 0,
5744
- execution_mode: row.execution_mode == null ? void 0 : (0, import_protocols2.parseQueuedExecutionMode)(row.execution_mode)
6436
+ trusted_run_context: row.trusted_run_context ? (0, import_protocols7.parseTrustedRunContext)(typeof row.trusted_run_context === "string" ? JSON.parse(row.trusted_run_context) : row.trusted_run_context) : void 0,
6437
+ execution_mode: row.execution_mode == null ? void 0 : (0, import_protocols7.parseQueuedExecutionMode)(row.execution_mode)
5745
6438
  };
5746
6439
  }
5747
6440
  };
5748
6441
  function validateQueueTrust(params) {
5749
6442
  return {
5750
- trustedRunContext: params.trusted_run_context === void 0 ? void 0 : (0, import_protocols2.parseTrustedRunContext)(params.trusted_run_context),
5751
- executionMode: params.execution_mode === void 0 ? void 0 : (0, import_protocols2.parseQueuedExecutionMode)(params.execution_mode)
6443
+ trustedRunContext: params.trusted_run_context === void 0 ? void 0 : (0, import_protocols7.parseTrustedRunContext)(params.trusted_run_context),
6444
+ executionMode: params.execution_mode === void 0 ? void 0 : (0, import_protocols7.parseQueuedExecutionMode)(params.execution_mode)
5752
6445
  };
5753
6446
  }
5754
6447
  function scopeClause(scope, start) {
@@ -5766,7 +6459,7 @@ function scopeClause(scope, start) {
5766
6459
 
5767
6460
  // src/stores/ChannelBindingStore.ts
5768
6461
  var import_pg14 = require("pg");
5769
- var import_protocols3 = require("@axiom-lattice/protocols");
6462
+ var import_protocols8 = require("@axiom-lattice/protocols");
5770
6463
 
5771
6464
  // src/migrations/channel_bindings_migration.ts
5772
6465
  var createChannelBindingsTable = {
@@ -5915,7 +6608,7 @@ var ChannelBindingStore = class {
5915
6608
  return this.mapRowToBinding(result.rows[0]);
5916
6609
  } catch (error) {
5917
6610
  if (isBindingSubjectConflict(error)) {
5918
- throw new import_protocols3.DuplicateChannelBindingSubjectError();
6611
+ throw new import_protocols8.DuplicateChannelBindingSubjectError();
5919
6612
  }
5920
6613
  throw error;
5921
6614
  }
@@ -6117,7 +6810,7 @@ var alterChannelInstallationsTable = {
6117
6810
  };
6118
6811
 
6119
6812
  // src/stores/PostgreSQLChannelInstallationStore.ts
6120
- var import_core5 = require("@axiom-lattice/core");
6813
+ var import_core7 = require("@axiom-lattice/core");
6121
6814
  var PostgreSQLChannelInstallationStore = class {
6122
6815
  constructor(options) {
6123
6816
  this.initialized = false;
@@ -6339,17 +7032,17 @@ var PostgreSQLChannelInstallationStore = class {
6339
7032
  encryptSecrets(config) {
6340
7033
  return {
6341
7034
  ...config,
6342
- ...typeof config.appSecret === "string" ? { appSecret: (0, import_core5.encrypt)(config.appSecret) } : {},
6343
- ...typeof config.verificationToken === "string" ? { verificationToken: (0, import_core5.encrypt)(config.verificationToken) } : {},
6344
- ...typeof config.encryptKey === "string" ? { encryptKey: (0, import_core5.encrypt)(config.encryptKey) } : {}
7035
+ ...typeof config.appSecret === "string" ? { appSecret: (0, import_core7.encrypt)(config.appSecret) } : {},
7036
+ ...typeof config.verificationToken === "string" ? { verificationToken: (0, import_core7.encrypt)(config.verificationToken) } : {},
7037
+ ...typeof config.encryptKey === "string" ? { encryptKey: (0, import_core7.encrypt)(config.encryptKey) } : {}
6345
7038
  };
6346
7039
  }
6347
7040
  decryptSecrets(config) {
6348
7041
  return {
6349
7042
  ...config,
6350
- ...typeof config.appSecret === "string" ? { appSecret: (0, import_core5.safeDecrypt)(config.appSecret) } : {},
6351
- ...typeof config.verificationToken === "string" ? { verificationToken: (0, import_core5.safeDecrypt)(config.verificationToken) } : {},
6352
- ...typeof config.encryptKey === "string" ? { encryptKey: (0, import_core5.safeDecrypt)(config.encryptKey) } : {}
7043
+ ...typeof config.appSecret === "string" ? { appSecret: (0, import_core7.safeDecrypt)(config.appSecret) } : {},
7044
+ ...typeof config.verificationToken === "string" ? { verificationToken: (0, import_core7.safeDecrypt)(config.verificationToken) } : {},
7045
+ ...typeof config.encryptKey === "string" ? { encryptKey: (0, import_core7.safeDecrypt)(config.encryptKey) } : {}
6353
7046
  };
6354
7047
  }
6355
7048
  };
@@ -6427,7 +7120,7 @@ var addApiKeyGrants = {
6427
7120
  };
6428
7121
 
6429
7122
  // src/stores/PostgreSQLA2AApiKeyStore.ts
6430
- var import_core6 = require("@axiom-lattice/core");
7123
+ var import_core8 = require("@axiom-lattice/core");
6431
7124
  var import_crypto2 = require("crypto");
6432
7125
  function generateApiKey() {
6433
7126
  return `a2a_${(0, import_crypto2.randomUUID)().replace(/-/g, "")}`;
@@ -6435,7 +7128,7 @@ function generateApiKey() {
6435
7128
  function mapRowToRecord(row) {
6436
7129
  return {
6437
7130
  id: row.id,
6438
- key: (0, import_core6.safeDecrypt)(row.key_value),
7131
+ key: (0, import_core8.safeDecrypt)(row.key_value),
6439
7132
  tenantId: row.tenant_id,
6440
7133
  projectId: row.project_id,
6441
7134
  assistantIds: row.assistant_ids ?? void 0,
@@ -6499,7 +7192,7 @@ var PostgreSQLA2AApiKeyStore = class {
6499
7192
  );
6500
7193
  for (const row of all.rows) {
6501
7194
  try {
6502
- if ((0, import_core6.decrypt)(row.key_value) === key) return mapRowToRecord(row);
7195
+ if ((0, import_core8.decrypt)(row.key_value) === key) return mapRowToRecord(row);
6503
7196
  } catch {
6504
7197
  continue;
6505
7198
  }
@@ -6543,7 +7236,7 @@ var PostgreSQLA2AApiKeyStore = class {
6543
7236
  `INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, assistant_ids, grants, label)
6544
7237
  VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
6545
7238
  [
6546
- (0, import_core6.encrypt)(key),
7239
+ (0, import_core8.encrypt)(key),
6547
7240
  input.tenantId,
6548
7241
  input.projectId,
6549
7242
  input.assistantIds ? JSON.stringify(input.assistantIds) : null,
@@ -6612,7 +7305,7 @@ var PostgreSQLA2AApiKeyStore = class {
6612
7305
  const key = generateApiKey();
6613
7306
  const result = await this.pool.query(
6614
7307
  `UPDATE lattice_a2a_api_keys SET key_value = $1, updated_at = NOW() WHERE id = $2 RETURNING *`,
6615
- [(0, import_core6.encrypt)(key), id]
7308
+ [(0, import_core8.encrypt)(key), id]
6616
7309
  );
6617
7310
  if (result.rows.length === 0) throw new Error(`A2A API key not found: ${id}`);
6618
7311
  const record = mapRowToRecord(result.rows[0]);
@@ -6631,7 +7324,7 @@ var PostgreSQLA2AApiKeyStore = class {
6631
7324
  const map2 = /* @__PURE__ */ new Map();
6632
7325
  for (const row of result.rows) {
6633
7326
  try {
6634
- const key = (0, import_core6.decrypt)(row.key_value);
7327
+ const key = (0, import_core8.decrypt)(row.key_value);
6635
7328
  map2.set(key, {
6636
7329
  key,
6637
7330
  tenantId: row.tenant_id,
@@ -6881,7 +7574,7 @@ function isIdentity(value) {
6881
7574
  function isStatus(value) {
6882
7575
  return value === "draft" || value === "active" || value === "disabled";
6883
7576
  }
6884
- function mapRow(row) {
7577
+ function mapRow7(row) {
6885
7578
  if (!isStatus(row.status) || !isIntegration(row.integration) || !isScope(row.scope) || !isFeatures(row.features) || !isAppearance(row.appearance) || !isIdentity(row.identity)) {
6886
7579
  throw new Error(`Invalid agent web app row: ${row.id}`);
6887
7580
  }
@@ -6953,7 +7646,7 @@ var PostgreSQLAgentWebAppStore = class {
6953
7646
  ORDER BY created_at DESC, id DESC`,
6954
7647
  [tenantId, assistantId]
6955
7648
  );
6956
- return result.rows.map(mapRow);
7649
+ return result.rows.map(mapRow7);
6957
7650
  }
6958
7651
  async getById(tenantId, webAppId) {
6959
7652
  await this.ensureInitialized();
@@ -6962,7 +7655,7 @@ var PostgreSQLAgentWebAppStore = class {
6962
7655
  WHERE tenant_id = $1 AND id = $2`,
6963
7656
  [tenantId, webAppId]
6964
7657
  );
6965
- return result.rows[0] ? mapRow(result.rows[0]) : null;
7658
+ return result.rows[0] ? mapRow7(result.rows[0]) : null;
6966
7659
  }
6967
7660
  async findById(webAppId) {
6968
7661
  await this.ensureInitialized();
@@ -6971,7 +7664,7 @@ var PostgreSQLAgentWebAppStore = class {
6971
7664
  WHERE id = $1`,
6972
7665
  [webAppId]
6973
7666
  );
6974
- return result.rows[0] ? mapRow(result.rows[0]) : null;
7667
+ return result.rows[0] ? mapRow7(result.rows[0]) : null;
6975
7668
  }
6976
7669
  async create(tenantId, input) {
6977
7670
  await this.ensureInitialized();
@@ -6995,7 +7688,7 @@ var PostgreSQLAgentWebAppStore = class {
6995
7688
  input.identity ? JSON.stringify(input.identity) : null
6996
7689
  ]
6997
7690
  );
6998
- return mapRow(result.rows[0]);
7691
+ return mapRow7(result.rows[0]);
6999
7692
  }
7000
7693
  async update(tenantId, webAppId, patch, options) {
7001
7694
  await this.ensureInitialized();
@@ -7037,7 +7730,7 @@ var PostgreSQLAgentWebAppStore = class {
7037
7730
  RETURNING *`,
7038
7731
  values
7039
7732
  );
7040
- return result.rows[0] ? mapRow(result.rows[0]) : null;
7733
+ return result.rows[0] ? mapRow7(result.rows[0]) : null;
7041
7734
  }
7042
7735
  async delete(tenantId, webAppId) {
7043
7736
  await this.ensureInitialized();
@@ -7052,7 +7745,7 @@ var PostgreSQLAgentWebAppStore = class {
7052
7745
 
7053
7746
  // src/stores/PostgreSQLScheduleStorage.ts
7054
7747
  var import_pg19 = require("pg");
7055
- var import_protocols4 = require("@axiom-lattice/protocols");
7748
+ var import_protocols9 = require("@axiom-lattice/protocols");
7056
7749
 
7057
7750
  // src/migrations/schedule_migrations.ts
7058
7751
  var createScheduledTasksTable = {
@@ -7467,10 +8160,10 @@ var PostgreSQLScheduleStorage = class {
7467
8160
  WHERE task_id = $3 AND status = $4
7468
8161
  RETURNING task_id`,
7469
8162
  [
7470
- import_protocols4.ScheduledTaskStatus.RUNNING,
8163
+ import_protocols9.ScheduledTaskStatus.RUNNING,
7471
8164
  /* @__PURE__ */ new Date(),
7472
8165
  taskId,
7473
- import_protocols4.ScheduledTaskStatus.PENDING
8166
+ import_protocols9.ScheduledTaskStatus.PENDING
7474
8167
  ]
7475
8168
  );
7476
8169
  return (result.rowCount ?? 0) > 0;
@@ -7490,8 +8183,8 @@ var PostgreSQLScheduleStorage = class {
7490
8183
  WHERE status = $2 AND updated_at < now() - ($3::bigint || ' milliseconds')::interval
7491
8184
  RETURNING task_id`,
7492
8185
  [
7493
- import_protocols4.ScheduledTaskStatus.PENDING,
7494
- import_protocols4.ScheduledTaskStatus.RUNNING,
8186
+ import_protocols9.ScheduledTaskStatus.PENDING,
8187
+ import_protocols9.ScheduledTaskStatus.RUNNING,
7495
8188
  olderThanMs
7496
8189
  ]
7497
8190
  );
@@ -7514,7 +8207,7 @@ var PostgreSQLScheduleStorage = class {
7514
8207
  await this.ensureInitialized();
7515
8208
  const result = await this.pool.query(
7516
8209
  `SELECT * FROM lattice_scheduled_tasks WHERE status IN ($1, $2) ORDER BY created_at ASC`,
7517
- [import_protocols4.ScheduledTaskStatus.PENDING, import_protocols4.ScheduledTaskStatus.PAUSED]
8210
+ [import_protocols9.ScheduledTaskStatus.PENDING, import_protocols9.ScheduledTaskStatus.PAUSED]
7518
8211
  );
7519
8212
  return result.rows.map((row) => this.mapRowToTask(row));
7520
8213
  }
@@ -7673,9 +8366,9 @@ var PostgreSQLScheduleStorage = class {
7673
8366
  AND updated_at < $4
7674
8367
  `,
7675
8368
  [
7676
- import_protocols4.ScheduledTaskStatus.COMPLETED,
7677
- import_protocols4.ScheduledTaskStatus.CANCELLED,
7678
- import_protocols4.ScheduledTaskStatus.FAILED,
8369
+ import_protocols9.ScheduledTaskStatus.COMPLETED,
8370
+ import_protocols9.ScheduledTaskStatus.CANCELLED,
8371
+ import_protocols9.ScheduledTaskStatus.FAILED,
7679
8372
  cutoff
7680
8373
  ]
7681
8374
  );
@@ -8691,7 +9384,7 @@ var PostgreSQLTaskStore = class {
8691
9384
 
8692
9385
  // src/stores/PostgreSQLTaskWorkItemStore.ts
8693
9386
  var import_pg21 = require("pg");
8694
- var import_protocols5 = require("@axiom-lattice/protocols");
9387
+ var import_protocols10 = require("@axiom-lattice/protocols");
8695
9388
  var import_uuid4 = require("uuid");
8696
9389
 
8697
9390
  // src/migrations/task_work_items_migration.ts
@@ -8981,8 +9674,8 @@ var PostgreSQLTaskWorkItemStore = class {
8981
9674
  }
8982
9675
  /** List pending execution results using one bounded PostgreSQL anti-join query. */
8983
9676
  async listPendingExecutionResults(params) {
8984
- if (!Number.isSafeInteger(params.limit) || params.limit < 0 || params.limit > import_protocols5.MAX_PENDING_EXECUTION_RESULTS_LIMIT) {
8985
- const error = new RangeError(`limit must be a safe integer between 0 and ${import_protocols5.MAX_PENDING_EXECUTION_RESULTS_LIMIT}`);
9677
+ if (!Number.isSafeInteger(params.limit) || params.limit < 0 || params.limit > import_protocols10.MAX_PENDING_EXECUTION_RESULTS_LIMIT) {
9678
+ const error = new RangeError(`limit must be a safe integer between 0 and ${import_protocols10.MAX_PENDING_EXECUTION_RESULTS_LIMIT}`);
8986
9679
  error.code = "INVALID_LIMIT";
8987
9680
  throw error;
8988
9681
  }
@@ -9835,8 +10528,8 @@ var import_langgraph_checkpoint_postgres = require("@langchain/langgraph-checkpo
9835
10528
  var import_pg25 = require("pg");
9836
10529
  var import_pgvector = require("@langchain/community/vectorstores/pgvector");
9837
10530
  var import_documents = require("@langchain/core/documents");
9838
- var import_core7 = require("@axiom-lattice/core");
9839
- var import_protocols6 = require("@axiom-lattice/protocols");
10531
+ var import_core9 = require("@axiom-lattice/core");
10532
+ var import_protocols11 = require("@axiom-lattice/protocols");
9840
10533
  function safeTableName(name) {
9841
10534
  if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
9842
10535
  throw new Error(`Invalid table name: ${name}`);
@@ -9851,14 +10544,14 @@ var PGVectorStoreProvider = class {
9851
10544
  async create(params) {
9852
10545
  const { tableName, embeddingKey } = params;
9853
10546
  const tenantId = params.tenantId ?? "default";
9854
- const requestedKey = embeddingKey || import_protocols6.EMBEDDING_DEFAULT_KEY;
9855
- const embeddings = await import_core7.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
10547
+ const requestedKey = embeddingKey || import_protocols11.EMBEDDING_DEFAULT_KEY;
10548
+ const embeddings = await import_core9.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
9856
10549
  tenantId,
9857
10550
  requestedKey
9858
- ) ?? (requestedKey === import_protocols6.EMBEDDING_DEFAULT_KEY ? void 0 : await import_core7.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
10551
+ ) ?? (requestedKey === import_protocols11.EMBEDDING_DEFAULT_KEY ? void 0 : await import_core9.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
9859
10552
  tenantId,
9860
- import_protocols6.EMBEDDING_DEFAULT_KEY
9861
- )) ?? await import_core7.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
10553
+ import_protocols11.EMBEDDING_DEFAULT_KEY
10554
+ )) ?? await import_core9.embeddingsLatticeManager.getEmbeddingsClientWithTenant(
9862
10555
  tenantId,
9863
10556
  "default"
9864
10557
  );
@@ -10126,7 +10819,7 @@ var createChannelIdentityMappingTables = {
10126
10819
  };
10127
10820
 
10128
10821
  // src/migrations/connection_config_migrations.ts
10129
- var import_core8 = require("@axiom-lattice/core");
10822
+ var import_core10 = require("@axiom-lattice/core");
10130
10823
  var createConnectionConfigsTable = {
10131
10824
  version: 160,
10132
10825
  name: "create_connection_configs_table",
@@ -10197,8 +10890,8 @@ var reencryptLegacyConnectionSecrets = {
10197
10890
  let changed = false;
10198
10891
  for (const field of secretFields) {
10199
10892
  const value = config[field];
10200
- if (typeof value === "string" && value.length > 0 && !(0, import_core8.isEncryptedValue)(value)) {
10201
- config[field] = (0, import_core8.encrypt)(value);
10893
+ if (typeof value === "string" && value.length > 0 && !(0, import_core10.isEncryptedValue)(value)) {
10894
+ config[field] = (0, import_core10.encrypt)(value);
10202
10895
  changed = true;
10203
10896
  }
10204
10897
  }
@@ -10214,6 +10907,254 @@ var reencryptLegacyConnectionSecrets = {
10214
10907
  }
10215
10908
  };
10216
10909
 
10910
+ // src/migrations/user_external_identity_migration.ts
10911
+ var createUserExternalIdentityTable = {
10912
+ version: 209,
10913
+ name: "create_user_external_identity",
10914
+ up: async (client) => {
10915
+ await client.query(`
10916
+ CREATE TABLE IF NOT EXISTS lattice_user_identities (
10917
+ user_id VARCHAR(255) NOT NULL,
10918
+ provider_id VARCHAR(255) NOT NULL,
10919
+ issuer TEXT NOT NULL,
10920
+ subject TEXT NOT NULL,
10921
+ email TEXT,
10922
+ linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
10923
+ PRIMARY KEY (provider_id, issuer, subject)
10924
+ )
10925
+ `);
10926
+ await client.query(`
10927
+ CREATE INDEX IF NOT EXISTS idx_lattice_user_identities_user
10928
+ ON lattice_user_identities (user_id)
10929
+ `);
10930
+ },
10931
+ down: async (client) => {
10932
+ await client.query("DROP INDEX IF EXISTS idx_lattice_user_identities_user");
10933
+ await client.query("DROP TABLE IF EXISTS lattice_user_identities");
10934
+ }
10935
+ };
10936
+
10937
+ // src/migrations/user_email_lower_unique_migration.ts
10938
+ var createUserEmailLowerUniqueIndex = {
10939
+ version: 210,
10940
+ name: "create_user_email_lower_unique_index",
10941
+ up: async (client) => {
10942
+ await client.query(`
10943
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_lattice_users_email_lower
10944
+ ON lattice_users (lower(email))
10945
+ `);
10946
+ },
10947
+ down: async (client) => {
10948
+ await client.query("DROP INDEX IF EXISTS idx_lattice_users_email_lower");
10949
+ }
10950
+ };
10951
+
10952
+ // src/migrations/oidc_provider_config_migration.ts
10953
+ var createOidcProviderConfigsTable = {
10954
+ version: 211,
10955
+ name: "create_oidc_provider_configs",
10956
+ up: async (client) => {
10957
+ await client.query(`
10958
+ CREATE TABLE IF NOT EXISTS lattice_oidc_provider_configs (
10959
+ id VARCHAR(64) PRIMARY KEY,
10960
+ display_name TEXT NOT NULL,
10961
+ issuer TEXT NOT NULL,
10962
+ client_id TEXT NOT NULL,
10963
+ client_secret TEXT NOT NULL,
10964
+ scopes TEXT NOT NULL,
10965
+ enabled BOOLEAN NOT NULL DEFAULT TRUE,
10966
+ jit_provisioning BOOLEAN NOT NULL DEFAULT FALSE,
10967
+ auto_link_email BOOLEAN NOT NULL DEFAULT FALSE,
10968
+ default_tenant_id TEXT,
10969
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
10970
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
10971
+ )
10972
+ `);
10973
+ },
10974
+ down: async (client) => {
10975
+ await client.query("DROP TABLE IF EXISTS lattice_oidc_provider_configs");
10976
+ }
10977
+ };
10978
+
10979
+ // src/migrations/platform_member_migration.ts
10980
+ var createPlatformMembersTable = {
10981
+ version: 212,
10982
+ name: "create_platform_members",
10983
+ up: async (client) => {
10984
+ await client.query(`
10985
+ CREATE TABLE IF NOT EXISTS lattice_platform_members (
10986
+ user_id VARCHAR(255) PRIMARY KEY,
10987
+ role VARCHAR(16) NOT NULL,
10988
+ granted_by VARCHAR(255),
10989
+ granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
10990
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
10991
+ )
10992
+ `);
10993
+ },
10994
+ down: async (client) => {
10995
+ await client.query("DROP TABLE IF EXISTS lattice_platform_members");
10996
+ }
10997
+ };
10998
+
10999
+ // src/migrations/platform_invitation_migration.ts
11000
+ var createPlatformInvitationsTable = {
11001
+ version: 213,
11002
+ name: "create_platform_invitations",
11003
+ up: async (client) => {
11004
+ await client.query(`
11005
+ CREATE TABLE IF NOT EXISTS lattice_platform_invitations (
11006
+ email VARCHAR(320) PRIMARY KEY,
11007
+ role VARCHAR(16) NOT NULL,
11008
+ invited_by VARCHAR(255),
11009
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11010
+ expires_at TIMESTAMPTZ,
11011
+ consumed_at TIMESTAMPTZ
11012
+ )
11013
+ `);
11014
+ },
11015
+ down: async (client) => {
11016
+ await client.query("DROP TABLE IF EXISTS lattice_platform_invitations");
11017
+ }
11018
+ };
11019
+
11020
+ // src/migrations/oidc_identity_claim_migration.ts
11021
+ var createOidcIdentityClaimsTable = {
11022
+ version: 214,
11023
+ name: "create_oidc_identity_claims",
11024
+ up: async (client) => {
11025
+ await client.query(`
11026
+ CREATE TABLE IF NOT EXISTS lattice_oidc_identity_claims (
11027
+ id UUID PRIMARY KEY,
11028
+ provider_id VARCHAR(64) NOT NULL,
11029
+ issuer TEXT NOT NULL,
11030
+ subject TEXT NOT NULL,
11031
+ email VARCHAR(320) NOT NULL,
11032
+ name TEXT,
11033
+ picture TEXT,
11034
+ status VARCHAR(16) NOT NULL,
11035
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11036
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11037
+ expires_at TIMESTAMPTZ NOT NULL,
11038
+ reviewed_by VARCHAR(255),
11039
+ reviewed_at TIMESTAMPTZ,
11040
+ linked_user_id VARCHAR(255)
11041
+ )
11042
+ `);
11043
+ await client.query(`
11044
+ CREATE UNIQUE INDEX IF NOT EXISTS lattice_oidc_identity_claims_pending_subject
11045
+ ON lattice_oidc_identity_claims (provider_id, issuer, subject)
11046
+ WHERE status = 'pending'
11047
+ `);
11048
+ await client.query(`
11049
+ CREATE INDEX IF NOT EXISTS lattice_oidc_identity_claims_status_created
11050
+ ON lattice_oidc_identity_claims (status, created_at)
11051
+ `);
11052
+ },
11053
+ down: async (client) => {
11054
+ await client.query("DROP TABLE IF EXISTS lattice_oidc_identity_claims");
11055
+ }
11056
+ };
11057
+
11058
+ // src/migrations/invite_link_migration.ts
11059
+ var createInviteLinksTable = {
11060
+ version: 215,
11061
+ name: "create_invite_links",
11062
+ up: async (client) => {
11063
+ await client.query(`
11064
+ CREATE TABLE IF NOT EXISTS lattice_invite_links (
11065
+ id UUID PRIMARY KEY,
11066
+ scope VARCHAR(16) NOT NULL,
11067
+ token_hash VARCHAR(64) NOT NULL UNIQUE,
11068
+ token_encrypted TEXT NOT NULL,
11069
+ email VARCHAR(320),
11070
+ platform_role VARCHAR(16),
11071
+ tenant_id VARCHAR(255),
11072
+ tenant_role VARCHAR(16),
11073
+ created_by VARCHAR(255),
11074
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11075
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11076
+ expires_at TIMESTAMPTZ NOT NULL,
11077
+ max_uses INTEGER NOT NULL,
11078
+ used_count INTEGER NOT NULL DEFAULT 0,
11079
+ revoked_at TIMESTAMPTZ
11080
+ )
11081
+ `);
11082
+ await client.query(`
11083
+ CREATE INDEX IF NOT EXISTS lattice_invite_links_created
11084
+ ON lattice_invite_links (created_at DESC)
11085
+ `);
11086
+ },
11087
+ down: async (client) => {
11088
+ await client.query("DROP TABLE IF EXISTS lattice_invite_links");
11089
+ }
11090
+ };
11091
+
11092
+ // src/migrations/oidc_identity_claim_invite_migration.ts
11093
+ var addOidcIdentityClaimInviteFields = {
11094
+ version: 216,
11095
+ name: "add_oidc_identity_claim_invite_fields",
11096
+ up: async (client) => {
11097
+ await client.query(`
11098
+ ALTER TABLE lattice_oidc_identity_claims
11099
+ ADD COLUMN IF NOT EXISTS source VARCHAR(32),
11100
+ ADD COLUMN IF NOT EXISTS invite_link_id UUID,
11101
+ ADD COLUMN IF NOT EXISTS requested_user_id VARCHAR(255),
11102
+ ADD COLUMN IF NOT EXISTS requested_platform_role VARCHAR(16),
11103
+ ADD COLUMN IF NOT EXISTS requested_tenant_id VARCHAR(255),
11104
+ ADD COLUMN IF NOT EXISTS requested_tenant_role VARCHAR(16)
11105
+ `);
11106
+ },
11107
+ down: async (client) => {
11108
+ await client.query(`
11109
+ ALTER TABLE lattice_oidc_identity_claims
11110
+ DROP COLUMN IF EXISTS source,
11111
+ DROP COLUMN IF EXISTS invite_link_id,
11112
+ DROP COLUMN IF EXISTS requested_user_id,
11113
+ DROP COLUMN IF EXISTS requested_platform_role,
11114
+ DROP COLUMN IF EXISTS requested_tenant_id,
11115
+ DROP COLUMN IF EXISTS requested_tenant_role
11116
+ `);
11117
+ }
11118
+ };
11119
+
11120
+ // src/migrations/platform_settings_migration.ts
11121
+ var createPlatformSettingsTable = {
11122
+ version: 217,
11123
+ name: "create_platform_settings",
11124
+ up: async (client) => {
11125
+ await client.query(`
11126
+ CREATE TABLE IF NOT EXISTS lattice_platform_settings (
11127
+ id VARCHAR(32) PRIMARY KEY,
11128
+ document JSONB NOT NULL,
11129
+ version INTEGER NOT NULL,
11130
+ updated_by VARCHAR(255),
11131
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
11132
+ )
11133
+ `);
11134
+ },
11135
+ down: async (client) => {
11136
+ await client.query("DROP TABLE IF EXISTS lattice_platform_settings");
11137
+ }
11138
+ };
11139
+
11140
+ // src/migrations/oidc_provider_allowed_hosts_migration.ts
11141
+ var addOidcProviderAllowedHosts = {
11142
+ version: 218,
11143
+ name: "add_oidc_provider_allowed_hosts",
11144
+ up: async (client) => {
11145
+ await client.query(`
11146
+ ALTER TABLE lattice_oidc_provider_configs
11147
+ ADD COLUMN IF NOT EXISTS allowed_private_hosts JSONB
11148
+ `);
11149
+ },
11150
+ down: async (client) => {
11151
+ await client.query(`
11152
+ ALTER TABLE lattice_oidc_provider_configs
11153
+ DROP COLUMN IF EXISTS allowed_private_hosts
11154
+ `);
11155
+ }
11156
+ };
11157
+
10217
11158
  // src/migrations/collection_migrations.ts
10218
11159
  var createCollectionsTable = {
10219
11160
  version: 137,
@@ -11279,7 +12220,7 @@ var PostgreSQLCapabilityBundleStore = class {
11279
12220
  function isValidDate(value) {
11280
12221
  return value instanceof Date && !Number.isNaN(value.getTime());
11281
12222
  }
11282
- function mapRow2(row) {
12223
+ function mapRow8(row) {
11283
12224
  if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || row.type !== "main" || typeof row.name !== "string" || !isValidDate(row.created_at) || !isValidDate(row.updated_at)) {
11284
12225
  throw new Error("Invalid project room row");
11285
12226
  }
@@ -11310,7 +12251,7 @@ var PostgreSQLProjectRoomStore = class {
11310
12251
  RETURNING id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at`,
11311
12252
  [input.id, input.tenantId, input.workspaceId, input.projectId, "main", input.name]
11312
12253
  );
11313
- return mapRow2(result.rows[0]);
12254
+ return mapRow8(result.rows[0]);
11314
12255
  }
11315
12256
  /** Finds a tenant-scoped project's main room, or returns null. */
11316
12257
  async getMainRoom(tenantId, projectId) {
@@ -11318,7 +12259,7 @@ var PostgreSQLProjectRoomStore = class {
11318
12259
  "SELECT id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at FROM lattice_project_rooms WHERE tenant_id = $1 AND project_id = $2 AND type = 'main'",
11319
12260
  [tenantId, projectId]
11320
12261
  );
11321
- return result.rows[0] ? mapRow2(result.rows[0]) : null;
12262
+ return result.rows[0] ? mapRow8(result.rows[0]) : null;
11322
12263
  }
11323
12264
  };
11324
12265
 
@@ -11344,7 +12285,7 @@ function isRole(value) {
11344
12285
  function isStatus2(value) {
11345
12286
  return value === "active" || value === "removed";
11346
12287
  }
11347
- function mapRow3(row) {
12288
+ function mapRow9(row) {
11348
12289
  if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.project_id !== "string" || typeof row.user_id !== "string" || !isRole(row.role) || !isStatus2(row.status) || !isDate(row.joined_at) || !isDate(row.updated_at)) throw new Error("Invalid project membership row");
11349
12290
  return {
11350
12291
  id: row.id,
@@ -11365,7 +12306,7 @@ function mapDuplicate(error) {
11365
12306
  if (isDuplicate2(error)) throw new ProjectMembershipIdConflictError();
11366
12307
  throw error;
11367
12308
  }
11368
- var columns = "id, tenant_id, project_id, user_id, role, status, joined_at, updated_at";
12309
+ var columns2 = "id, tenant_id, project_id, user_id, role, status, joined_at, updated_at";
11369
12310
  var PostgreSQLProjectMembershipStore = class {
11370
12311
  /** Creates a store using an externally managed pool; the pool is not migrated or closed. */
11371
12312
  constructor(options) {
@@ -11373,13 +12314,13 @@ var PostgreSQLProjectMembershipStore = class {
11373
12314
  }
11374
12315
  /** Lists memberships in stable joined-time and ID order. */
11375
12316
  async list(tenantId, projectId) {
11376
- const result = await this.pool.query(`SELECT ${columns} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`, [tenantId, projectId]);
11377
- return result.rows.map(mapRow3);
12317
+ const result = await this.pool.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`, [tenantId, projectId]);
12318
+ return result.rows.map(mapRow9);
11378
12319
  }
11379
12320
  /** Finds a membership by exact tenant, project, and user identity. */
11380
12321
  async findByUser(tenantId, projectId, userId) {
11381
- const result = await this.pool.query(`SELECT ${columns} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 AND user_id = $3`, [tenantId, projectId, userId]);
11382
- return result.rows[0] ? mapRow3(result.rows[0]) : null;
12322
+ const result = await this.pool.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 AND user_id = $3`, [tenantId, projectId, userId]);
12323
+ return result.rows[0] ? mapRow9(result.rows[0]) : null;
11383
12324
  }
11384
12325
  /** Inserts a membership and maps database uniqueness errors to stable typed errors. */
11385
12326
  async create(input) {
@@ -11387,8 +12328,8 @@ var PostgreSQLProjectMembershipStore = class {
11387
12328
  try {
11388
12329
  await client.query("BEGIN");
11389
12330
  await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
11390
- const result = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${columns}`, [input.id, input.tenantId, input.projectId, input.userId, input.role, input.status]);
11391
- const membership = mapRow3(result.rows[0]);
12331
+ const result = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${columns2}`, [input.id, input.tenantId, input.projectId, input.userId, input.role, input.status]);
12332
+ const membership = mapRow9(result.rows[0]);
11392
12333
  await client.query("COMMIT");
11393
12334
  return membership;
11394
12335
  } catch (error) {
@@ -11407,16 +12348,16 @@ var PostgreSQLProjectMembershipStore = class {
11407
12348
  try {
11408
12349
  await client.query("BEGIN");
11409
12350
  await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
11410
- const existingResult = await client.query(`SELECT ${columns} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 FOR UPDATE`, [input.tenantId, input.projectId]);
12351
+ const existingResult = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 FOR UPDATE`, [input.tenantId, input.projectId]);
11411
12352
  if (existingResult.rows.length > 0) {
11412
- const existing = existingResult.rows.map(mapRow3).find((item) => item.userId === input.userId && item.role === "owner" && item.status === "active");
12353
+ const existing = existingResult.rows.map(mapRow9).find((item) => item.userId === input.userId && item.role === "owner" && item.status === "active");
11413
12354
  await client.query("COMMIT");
11414
12355
  return existing ? { kind: "existing", membership: existing } : { kind: "already_initialized" };
11415
12356
  }
11416
12357
  const idResult = await client.query("SELECT id FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2", [input.tenantId, input.id]);
11417
12358
  if (idResult.rows.length > 0) throw new ProjectMembershipIdConflictError();
11418
- const inserted = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, 'owner', 'active', date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${columns}`, [input.id, input.tenantId, input.projectId, input.userId]);
11419
- const membership = mapRow3(inserted.rows[0]);
12359
+ const inserted = await client.query(`INSERT INTO lattice_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES ($1, $2, $3, $4, 'owner', 'active', date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp())) RETURNING ${columns2}`, [input.id, input.tenantId, input.projectId, input.userId]);
12360
+ const membership = mapRow9(inserted.rows[0]);
11420
12361
  await client.query("COMMIT");
11421
12362
  return { kind: "created", membership };
11422
12363
  } catch (error) {
@@ -11448,16 +12389,16 @@ var PostgreSQLProjectMembershipStore = class {
11448
12389
  return { kind: "not_found" };
11449
12390
  }
11450
12391
  await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${tenantId}:${identity.rows[0].project_id}`]);
11451
- const activeRows = await client.query(`SELECT ${columns} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 AND status = 'active' FOR UPDATE`, [tenantId, identity.rows[0].project_id]);
11452
- const lockedMemberships = activeRows.rows.map(mapRow3);
12392
+ const activeRows = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND project_id = $2 AND status = 'active' FOR UPDATE`, [tenantId, identity.rows[0].project_id]);
12393
+ const lockedMemberships = activeRows.rows.map(mapRow9);
11453
12394
  let current = lockedMemberships.find((membership) => membership.id === id);
11454
12395
  if (!current) {
11455
- const target = await client.query(`SELECT ${columns} FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`, [tenantId, id]);
12396
+ const target = await client.query(`SELECT ${columns2} FROM lattice_project_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`, [tenantId, id]);
11456
12397
  if (!target.rows[0]) {
11457
12398
  await client.query("COMMIT");
11458
12399
  return { kind: "not_found" };
11459
12400
  }
11460
- current = mapRow3(target.rows[0]);
12401
+ current = mapRow9(target.rows[0]);
11461
12402
  }
11462
12403
  if (current.updatedAt.getTime() !== expected.getTime()) {
11463
12404
  await client.query("COMMIT");
@@ -11470,13 +12411,13 @@ var PostgreSQLProjectMembershipStore = class {
11470
12411
  return { kind: "last_owner" };
11471
12412
  }
11472
12413
  }
11473
- const result = await client.query(remove ? `UPDATE lattice_project_memberships SET status = 'removed', updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${columns}` : `UPDATE lattice_project_memberships SET role = $4, updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${columns}`, remove ? [tenantId, id, expected] : [tenantId, id, expected, role]);
12414
+ const result = await client.query(remove ? `UPDATE lattice_project_memberships SET status = 'removed', updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${columns2}` : `UPDATE lattice_project_memberships SET role = $4, updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp())) WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3 RETURNING ${columns2}`, remove ? [tenantId, id, expected] : [tenantId, id, expected, role]);
11474
12415
  if (!result.rows[0]) {
11475
12416
  await client.query("COMMIT");
11476
12417
  return { kind: "conflict" };
11477
12418
  }
11478
12419
  await client.query("COMMIT");
11479
- return { kind: remove ? "removed" : "updated", membership: mapRow3(result.rows[0]) };
12420
+ return { kind: remove ? "removed" : "updated", membership: mapRow9(result.rows[0]) };
11480
12421
  } catch (error) {
11481
12422
  try {
11482
12423
  await client.query("ROLLBACK");
@@ -11490,7 +12431,7 @@ var PostgreSQLProjectMembershipStore = class {
11490
12431
  };
11491
12432
 
11492
12433
  // src/stores/PostgreSQLProjectBotMembershipStore.ts
11493
- var columns2 = "id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at";
12434
+ var columns3 = "id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at";
11494
12435
  var ProjectBotMembershipIdConflictError = class extends Error {
11495
12436
  /** Creates an accurate tenant-scoped membership ID collision error. */
11496
12437
  constructor(tenantId, id) {
@@ -11507,7 +12448,7 @@ function isRole2(value) {
11507
12448
  function isStatus3(value) {
11508
12449
  return value === "active" || value === "paused" || value === "removed";
11509
12450
  }
11510
- function mapRow4(row) {
12451
+ function mapRow10(row) {
11511
12452
  if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || typeof row.room_id !== "string" || typeof row.assistant_id !== "string" || !isRole2(row.role) || typeof row.title !== "string" || row.responsibility !== null && typeof row.responsibility !== "string" || typeof row.mention_name !== "string" || !isStatus3(row.status) || typeof row.room_thread_id !== "string" || !isDate2(row.joined_at) || !isDate2(row.updated_at)) {
11512
12453
  throw new Error("Invalid project bot membership row");
11513
12454
  }
@@ -11528,11 +12469,11 @@ function mapRow4(row) {
11528
12469
  updatedAt: row.updated_at
11529
12470
  };
11530
12471
  }
11531
- function isUniqueViolation(error) {
12472
+ function isUniqueViolation3(error) {
11532
12473
  return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
11533
12474
  }
11534
12475
  function constraintConflict(error) {
11535
- if (!isUniqueViolation(error) || typeof error.constraint !== "string") return void 0;
12476
+ if (!isUniqueViolation3(error) || typeof error.constraint !== "string") return void 0;
11536
12477
  if (error.constraint === "uq_lattice_project_bot_memberships_coordinator") return "coordinator_conflict";
11537
12478
  if (error.constraint === "uq_lattice_project_bot_memberships_mention") return "mention_conflict";
11538
12479
  return void 0;
@@ -11551,26 +12492,26 @@ var PostgreSQLProjectBotMembershipStore = class {
11551
12492
  /** Lists retained memberships in stable join order. */
11552
12493
  async list(tenantId, projectId) {
11553
12494
  const result = await this.pool.query(
11554
- `SELECT ${columns2} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`,
12495
+ `SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 ORDER BY joined_at ASC, id ASC`,
11555
12496
  [tenantId, projectId]
11556
12497
  );
11557
- return result.rows.map(mapRow4);
12498
+ return result.rows.map(mapRow10);
11558
12499
  }
11559
12500
  /** Finds a membership by tenant-scoped ID. */
11560
12501
  async findById(tenantId, id) {
11561
12502
  const result = await this.pool.query(
11562
- `SELECT ${columns2} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2`,
12503
+ `SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2`,
11563
12504
  [tenantId, id]
11564
12505
  );
11565
- return result.rows[0] ? mapRow4(result.rows[0]) : null;
12506
+ return result.rows[0] ? mapRow10(result.rows[0]) : null;
11566
12507
  }
11567
12508
  /** Finds an assistant's durable membership in a tenant-scoped project. */
11568
12509
  async findByAssistant(tenantId, projectId, assistantId) {
11569
12510
  const result = await this.pool.query(
11570
- `SELECT ${columns2} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3`,
12511
+ `SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3`,
11571
12512
  [tenantId, projectId, assistantId]
11572
12513
  );
11573
- return result.rows[0] ? mapRow4(result.rows[0]) : null;
12514
+ return result.rows[0] ? mapRow10(result.rows[0]) : null;
11574
12515
  }
11575
12516
  /** Inserts a new membership or reactivates the assistant's durable membership atomically. */
11576
12517
  async save(input) {
@@ -11579,17 +12520,17 @@ var PostgreSQLProjectBotMembershipStore = class {
11579
12520
  await client.query("BEGIN");
11580
12521
  await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${input.projectId}`]);
11581
12522
  const existingResult = await client.query(
11582
- `SELECT ${columns2} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3 FOR UPDATE`,
12523
+ `SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND project_id = $2 AND assistant_id = $3 FOR UPDATE`,
11583
12524
  [input.tenantId, input.projectId, input.assistantId]
11584
12525
  );
11585
- const existing = existingResult.rows[0] ? mapRow4(existingResult.rows[0]) : void 0;
12526
+ const existing = existingResult.rows[0] ? mapRow10(existingResult.rows[0]) : void 0;
11586
12527
  let result;
11587
12528
  if (existing) {
11588
12529
  result = await client.query(
11589
12530
  `UPDATE lattice_project_bot_memberships
11590
12531
  SET role = $3, title = $4, responsibility = $5, mention_name = $6, status = 'active',
11591
12532
  updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp()))
11592
- WHERE tenant_id = $1 AND id = $2 RETURNING ${columns2}`,
12533
+ WHERE tenant_id = $1 AND id = $2 RETURNING ${columns3}`,
11593
12534
  [input.tenantId, existing.id, input.role, input.title, input.responsibility ?? null, input.mentionName]
11594
12535
  );
11595
12536
  } else {
@@ -11602,7 +12543,7 @@ var PostgreSQLProjectBotMembershipStore = class {
11602
12543
  `INSERT INTO lattice_project_bot_memberships
11603
12544
  (id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at)
11604
12545
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'active', $11, date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', clock_timestamp()))
11605
- RETURNING ${columns2}`,
12546
+ RETURNING ${columns3}`,
11606
12547
  [
11607
12548
  input.id,
11608
12549
  input.tenantId,
@@ -11618,14 +12559,14 @@ var PostgreSQLProjectBotMembershipStore = class {
11618
12559
  ]
11619
12560
  );
11620
12561
  }
11621
- const membership = mapRow4(result.rows[0]);
12562
+ const membership = mapRow10(result.rows[0]);
11622
12563
  await client.query("COMMIT");
11623
12564
  return { kind: existing === void 0 ? "created" : existing.status === "removed" ? "reactivated" : "updated", membership };
11624
12565
  } catch (error) {
11625
12566
  await rollback(client);
11626
12567
  const conflict = constraintConflict(error);
11627
12568
  if (conflict) return { kind: conflict };
11628
- if (isUniqueViolation(error) && error.constraint === "lattice_project_bot_memberships_pkey") {
12569
+ if (isUniqueViolation3(error) && error.constraint === "lattice_project_bot_memberships_pkey") {
11629
12570
  throw new ProjectBotMembershipIdConflictError(input.tenantId, input.id);
11630
12571
  }
11631
12572
  throw error;
@@ -11649,14 +12590,14 @@ var PostgreSQLProjectBotMembershipStore = class {
11649
12590
  if (typeof identity.rows[0].project_id !== "string") throw new Error("Invalid project bot membership row");
11650
12591
  await client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${input.tenantId}:${identity.rows[0].project_id}`]);
11651
12592
  const locked = await client.query(
11652
- `SELECT ${columns2} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`,
12593
+ `SELECT ${columns3} FROM lattice_project_bot_memberships WHERE tenant_id = $1 AND id = $2 FOR UPDATE`,
11653
12594
  [input.tenantId, input.id]
11654
12595
  );
11655
12596
  if (!locked.rows[0]) {
11656
12597
  await client.query("COMMIT");
11657
12598
  return { kind: "not_found" };
11658
12599
  }
11659
- const existing = mapRow4(locked.rows[0]);
12600
+ const existing = mapRow10(locked.rows[0]);
11660
12601
  if (existing.updatedAt.getTime() !== input.expectedUpdatedAt.getTime()) {
11661
12602
  await client.query("COMMIT");
11662
12603
  return { kind: "conflict" };
@@ -11667,7 +12608,7 @@ var PostgreSQLProjectBotMembershipStore = class {
11667
12608
  SET role = $4, title = $5, responsibility = $6, mention_name = $7, status = $8,
11668
12609
  updated_at = GREATEST(date_trunc('milliseconds', updated_at) + interval '1 millisecond', date_trunc('milliseconds', clock_timestamp()))
11669
12610
  WHERE tenant_id = $1 AND id = $2 AND date_trunc('milliseconds', updated_at) = $3
11670
- RETURNING ${columns2}`,
12611
+ RETURNING ${columns3}`,
11671
12612
  [
11672
12613
  input.tenantId,
11673
12614
  input.id,
@@ -11683,7 +12624,7 @@ var PostgreSQLProjectBotMembershipStore = class {
11683
12624
  await client.query("COMMIT");
11684
12625
  return { kind: "conflict" };
11685
12626
  }
11686
- const membership = mapRow4(updated.rows[0]);
12627
+ const membership = mapRow10(updated.rows[0]);
11687
12628
  await client.query("COMMIT");
11688
12629
  return { kind: "updated", membership };
11689
12630
  } catch (error) {
@@ -11698,7 +12639,7 @@ var PostgreSQLProjectBotMembershipStore = class {
11698
12639
  };
11699
12640
 
11700
12641
  // src/stores/PostgreSQLProjectRoomMessageStore.ts
11701
- var columns3 = "id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at";
12642
+ var columns4 = "id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at";
11702
12643
  var ProjectRoomMessageIdConflictError = class extends Error {
11703
12644
  /** Creates an accurate tenant-scoped message ID collision error. */
11704
12645
  constructor(tenantId, id) {
@@ -11739,7 +12680,7 @@ function mapMention(value) {
11739
12680
  function isSource(value) {
11740
12681
  return value === "user" || value === "agent" || value === "task" || value === "routine" || value === "system";
11741
12682
  }
11742
- function mapRow5(row) {
12683
+ function mapRow11(row) {
11743
12684
  const author = mapAuthor(row.author);
11744
12685
  const content = isRecord4(row.content) && row.content.type === "text" && typeof row.content.text === "string" && hasOnlyKeys(row.content, ["type", "text"]) ? { type: "text", text: row.content.text } : void 0;
11745
12686
  const mentions = Array.isArray(row.mentions) ? row.mentions.map(mapMention) : void 0;
@@ -11766,7 +12707,7 @@ function mapRowsTolerant(rows, scope) {
11766
12707
  const messages = [];
11767
12708
  for (const row of rows) {
11768
12709
  try {
11769
- messages.push(mapRow5(row));
12710
+ messages.push(mapRow11(row));
11770
12711
  } catch (error) {
11771
12712
  console.warn("[ProjectRoomMessageStore] Skipping invalid message row", {
11772
12713
  tenantId: scope.tenantId,
@@ -11778,7 +12719,7 @@ function mapRowsTolerant(rows, scope) {
11778
12719
  }
11779
12720
  return messages;
11780
12721
  }
11781
- function isUniqueViolation2(error) {
12722
+ function isUniqueViolation4(error) {
11782
12723
  return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
11783
12724
  }
11784
12725
  var PostgreSQLProjectRoomMessageStore = class {
@@ -11793,15 +12734,15 @@ var PostgreSQLProjectRoomMessageStore = class {
11793
12734
  `INSERT INTO lattice_project_room_messages
11794
12735
  (id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at)
11795
12736
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, date_trunc('milliseconds', clock_timestamp()))
11796
- RETURNING ${columns3}`,
12737
+ RETURNING ${columns4}`,
11797
12738
  this.parameters(input)
11798
12739
  );
11799
- return mapRow5(result.rows[0]);
12740
+ return mapRow11(result.rows[0]);
11800
12741
  } catch (error) {
11801
- if (isUniqueViolation2(error) && error.constraint === "lattice_project_room_messages_pkey") {
12742
+ if (isUniqueViolation4(error) && error.constraint === "lattice_project_room_messages_pkey") {
11802
12743
  throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
11803
12744
  }
11804
- if (isUniqueViolation2(error) && error.constraint === "uq_lattice_project_room_messages_idempotency") {
12745
+ if (isUniqueViolation4(error) && error.constraint === "uq_lattice_project_room_messages_idempotency") {
11805
12746
  throw new DuplicateProjectRoomMessageIdempotencyKeyError(input.tenantId, input.roomId, input.idempotencyKey ?? "");
11806
12747
  }
11807
12748
  throw error;
@@ -11816,12 +12757,12 @@ var PostgreSQLProjectRoomMessageStore = class {
11816
12757
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, date_trunc('milliseconds', clock_timestamp()))
11817
12758
  ON CONFLICT (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL
11818
12759
  DO UPDATE SET idempotency_key = lattice_project_room_messages.idempotency_key
11819
- RETURNING ${columns3}`,
12760
+ RETURNING ${columns4}`,
11820
12761
  this.parameters(input)
11821
12762
  );
11822
- return mapRow5(result.rows[0]);
12763
+ return mapRow11(result.rows[0]);
11823
12764
  } catch (error) {
11824
- if (isUniqueViolation2(error) && error.constraint === "lattice_project_room_messages_pkey") {
12765
+ if (isUniqueViolation4(error) && error.constraint === "lattice_project_room_messages_pkey") {
11825
12766
  throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
11826
12767
  }
11827
12768
  throw error;
@@ -11834,12 +12775,12 @@ var PostgreSQLProjectRoomMessageStore = class {
11834
12775
  }
11835
12776
  const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
11836
12777
  const result = input.before ? await this.pool.query(
11837
- `SELECT ${columns3} FROM lattice_project_room_messages
12778
+ `SELECT ${columns4} FROM lattice_project_room_messages
11838
12779
  WHERE tenant_id = $1 AND room_id = $2 AND (created_at < $3 OR (created_at = $3 AND id < $4))
11839
12780
  ORDER BY created_at DESC, id DESC LIMIT $5`,
11840
12781
  [input.tenantId, input.roomId, input.before.createdAt, input.before.id, limit]
11841
12782
  ) : await this.pool.query(
11842
- `SELECT ${columns3} FROM lattice_project_room_messages
12783
+ `SELECT ${columns4} FROM lattice_project_room_messages
11843
12784
  WHERE tenant_id = $1 AND room_id = $2 ORDER BY created_at DESC, id DESC LIMIT $3`,
11844
12785
  [input.tenantId, input.roomId, limit]
11845
12786
  );
@@ -11848,10 +12789,10 @@ var PostgreSQLProjectRoomMessageStore = class {
11848
12789
  /** Finds a message by tenant-scoped ID. */
11849
12790
  async findById(tenantId, id) {
11850
12791
  const result = await this.pool.query(
11851
- `SELECT ${columns3} FROM lattice_project_room_messages WHERE tenant_id = $1 AND id = $2`,
12792
+ `SELECT ${columns4} FROM lattice_project_room_messages WHERE tenant_id = $1 AND id = $2`,
11852
12793
  [tenantId, id]
11853
12794
  );
11854
- return result.rows[0] ? mapRow5(result.rows[0]) : null;
12795
+ return result.rows[0] ? mapRow11(result.rows[0]) : null;
11855
12796
  }
11856
12797
  /** Counts messages created strictly after a horizon, optionally excluding one human author. */
11857
12798
  async countAfter(input) {
@@ -11909,7 +12850,7 @@ var PostgreSQLProjectRoomMessageStore = class {
11909
12850
  };
11910
12851
 
11911
12852
  // src/stores/PostgreSQLProjectRoomReadStateStore.ts
11912
- function mapRow6(row) {
12853
+ function mapRow12(row) {
11913
12854
  if (typeof row.tenant_id !== "string" || typeof row.room_id !== "string" || typeof row.user_id !== "string" || !(row.last_read_at instanceof Date) || Number.isNaN(row.last_read_at.getTime()) || !(row.updated_at instanceof Date) || Number.isNaN(row.updated_at.getTime())) {
11914
12855
  throw new Error("Invalid project room read state row");
11915
12856
  }
@@ -11927,7 +12868,7 @@ var PostgreSQLProjectRoomReadStateStore = class {
11927
12868
  FROM lattice_project_room_read_states WHERE tenant_id = $1 AND room_id = $2 AND user_id = $3`,
11928
12869
  [tenantId, roomId, userId]
11929
12870
  );
11930
- return result.rows[0] ? mapRow6(result.rows[0]) : null;
12871
+ return result.rows[0] ? mapRow12(result.rows[0]) : null;
11931
12872
  }
11932
12873
  /** Upserts the marker monotonically using GREATEST so older reports never regress it. */
11933
12874
  async markRead(input) {
@@ -11943,14 +12884,14 @@ var PostgreSQLProjectRoomReadStateStore = class {
11943
12884
  RETURNING tenant_id, room_id, user_id, last_read_at, updated_at`,
11944
12885
  [input.tenantId, input.roomId, input.userId, input.lastReadAt]
11945
12886
  );
11946
- return mapRow6(result.rows[0]);
12887
+ return mapRow12(result.rows[0]);
11947
12888
  }
11948
12889
  };
11949
12890
 
11950
12891
  // src/stores/PostgreSQLUserPushSubscriptionStore.ts
11951
12892
  var import_node_crypto2 = require("crypto");
11952
- var columns4 = "id, tenant_id, user_id, endpoint, p256dh, auth, user_agent, created_at";
11953
- function mapRow7(row) {
12893
+ var columns5 = "id, tenant_id, user_id, endpoint, p256dh, auth, user_agent, created_at";
12894
+ function mapRow13(row) {
11954
12895
  if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.user_id !== "string" || typeof row.endpoint !== "string" || typeof row.p256dh !== "string" || typeof row.auth !== "string" || !(row.created_at instanceof Date) || Number.isNaN(row.created_at.getTime())) {
11955
12896
  throw new Error("Invalid user push subscription row");
11956
12897
  }
@@ -11972,22 +12913,22 @@ var PostgreSQLUserPushSubscriptionStore = class {
11972
12913
  /** Lists every subscription for a user. */
11973
12914
  async list(tenantId, userId) {
11974
12915
  const result = await this.pool.query(
11975
- `SELECT ${columns4} FROM lattice_user_push_subscriptions WHERE tenant_id = $1 AND user_id = $2`,
12916
+ `SELECT ${columns5} FROM lattice_user_push_subscriptions WHERE tenant_id = $1 AND user_id = $2`,
11976
12917
  [tenantId, userId]
11977
12918
  );
11978
- return result.rows.map(mapRow7);
12919
+ return result.rows.map(mapRow13);
11979
12920
  }
11980
12921
  /** Upserts a subscription keyed by tenant + user + endpoint. */
11981
12922
  async save(input) {
11982
12923
  const result = await this.pool.query(
11983
- `INSERT INTO lattice_user_push_subscriptions (${columns4})
12924
+ `INSERT INTO lattice_user_push_subscriptions (${columns5})
11984
12925
  VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
11985
12926
  ON CONFLICT (tenant_id, user_id, endpoint) DO UPDATE
11986
12927
  SET p256dh = EXCLUDED.p256dh, auth = EXCLUDED.auth, user_agent = EXCLUDED.user_agent
11987
- RETURNING ${columns4}`,
12928
+ RETURNING ${columns5}`,
11988
12929
  [(0, import_node_crypto2.randomUUID)(), input.tenantId, input.userId, input.endpoint, input.keys.p256dh, input.keys.auth, input.userAgent ?? null]
11989
12930
  );
11990
- return mapRow7(result.rows[0]);
12931
+ return mapRow13(result.rows[0]);
11991
12932
  }
11992
12933
  /** Deletes one subscription by endpoint. */
11993
12934
  async delete(tenantId, userId, endpoint) {
@@ -12001,7 +12942,7 @@ var PostgreSQLUserPushSubscriptionStore = class {
12001
12942
 
12002
12943
  // src/stores/PostgreSQLModelProviderStore.ts
12003
12944
  var import_pg27 = require("pg");
12004
- var import_core9 = require("@axiom-lattice/core");
12945
+ var import_core11 = require("@axiom-lattice/core");
12005
12946
  function extraOrUndefined(value) {
12006
12947
  return value && Object.keys(value).length > 0 ? value : void 0;
12007
12948
  }
@@ -12125,7 +13066,7 @@ var PostgreSQLModelProviderStore = class {
12125
13066
  await this.ensureInitialized();
12126
13067
  const now = /* @__PURE__ */ new Date();
12127
13068
  const nowString = now.toISOString();
12128
- const apiKeyEnc = data.apiKey ? (0, import_core9.encrypt)(data.apiKey) : null;
13069
+ const apiKeyEnc = data.apiKey ? (0, import_core11.encrypt)(data.apiKey) : null;
12129
13070
  const apiKeyHint = data.apiKey ? data.apiKey.slice(-4) : null;
12130
13071
  await this.pool.query(
12131
13072
  `
@@ -12201,7 +13142,7 @@ var PostgreSQLModelProviderStore = class {
12201
13142
  updateData.api_key_enc = null;
12202
13143
  updateData.api_key_hint = null;
12203
13144
  } else if (updates.apiKey) {
12204
- updateData.api_key_enc = (0, import_core9.encrypt)(updates.apiKey);
13145
+ updateData.api_key_enc = (0, import_core11.encrypt)(updates.apiKey);
12205
13146
  updateData.api_key_hint = updates.apiKey.slice(-4);
12206
13147
  }
12207
13148
  if (updates.enabled !== void 0) {
@@ -12521,7 +13462,7 @@ var PostgreSQLModelProviderStore = class {
12521
13462
  mapRowToEntry(row) {
12522
13463
  let apiKey;
12523
13464
  if (row.api_key_enc) {
12524
- apiKey = (0, import_core9.safeDecrypt)(row.api_key_enc);
13465
+ apiKey = (0, import_core11.safeDecrypt)(row.api_key_enc);
12525
13466
  }
12526
13467
  return {
12527
13468
  id: row.id,
@@ -12774,7 +13715,7 @@ var PostgreSQLOutboxStore = class {
12774
13715
  };
12775
13716
 
12776
13717
  // src/stores/PostgreSQLPluginDeploymentStore.ts
12777
- var columns5 = "id, tenant_id, plugin_type, plugin_version, mode, status, provider, sandbox_name, port, endpoint, image_ref, idle_timeout_ms, last_health_at, last_error, created_at, updated_at";
13718
+ var columns6 = "id, tenant_id, plugin_type, plugin_version, mode, status, provider, sandbox_name, port, endpoint, image_ref, idle_timeout_ms, last_health_at, last_error, created_at, updated_at";
12778
13719
  var statuses = [
12779
13720
  "requested",
12780
13721
  "provisioning",
@@ -12787,7 +13728,7 @@ var statuses = [
12787
13728
  function isStatus4(value) {
12788
13729
  return typeof value === "string" && statuses.includes(value);
12789
13730
  }
12790
- function mapRow8(row) {
13731
+ function mapRow14(row) {
12791
13732
  if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.plugin_type !== "string" || typeof row.plugin_version !== "string" || row.mode !== "onDemand" && row.mode !== "alwaysOn" || !isStatus4(row.status) || typeof row.provider !== "string" || typeof row.sandbox_name !== "string" || typeof row.port !== "number" || typeof row.idle_timeout_ms !== "number" || !(row.created_at instanceof Date) || Number.isNaN(row.created_at.getTime()) || !(row.updated_at instanceof Date) || Number.isNaN(row.updated_at.getTime())) {
12792
13733
  throw new Error("Invalid plugin deployment row");
12793
13734
  }
@@ -12818,23 +13759,23 @@ var PostgreSQLPluginDeploymentStore = class {
12818
13759
  /** Lists every deployment for a tenant, ordered by plugin type. */
12819
13760
  async listByTenant(tenantId) {
12820
13761
  const result = await this.pool.query(
12821
- `SELECT ${columns5} FROM lattice_plugin_deployments WHERE tenant_id = $1 ORDER BY plugin_type`,
13762
+ `SELECT ${columns6} FROM lattice_plugin_deployments WHERE tenant_id = $1 ORDER BY plugin_type`,
12822
13763
  [tenantId]
12823
13764
  );
12824
- return result.rows.map(mapRow8);
13765
+ return result.rows.map(mapRow14);
12825
13766
  }
12826
13767
  /** Returns the deployment for a tenant + plugin type, or undefined. */
12827
13768
  async get(tenantId, pluginType) {
12828
13769
  const result = await this.pool.query(
12829
- `SELECT ${columns5} FROM lattice_plugin_deployments WHERE tenant_id = $1 AND plugin_type = $2`,
13770
+ `SELECT ${columns6} FROM lattice_plugin_deployments WHERE tenant_id = $1 AND plugin_type = $2`,
12830
13771
  [tenantId, pluginType]
12831
13772
  );
12832
- return result.rows[0] ? mapRow8(result.rows[0]) : void 0;
13773
+ return result.rows[0] ? mapRow14(result.rows[0]) : void 0;
12833
13774
  }
12834
13775
  /** Upserts a deployment keyed by tenant + plugin type, preserving the original id and createdAt. */
12835
13776
  async upsert(deployment) {
12836
13777
  const result = await this.pool.query(
12837
- `INSERT INTO lattice_plugin_deployments (${columns5})
13778
+ `INSERT INTO lattice_plugin_deployments (${columns6})
12838
13779
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
12839
13780
  ON CONFLICT (tenant_id, plugin_type) DO UPDATE SET
12840
13781
  plugin_version = EXCLUDED.plugin_version,
@@ -12849,7 +13790,7 @@ var PostgreSQLPluginDeploymentStore = class {
12849
13790
  last_health_at = EXCLUDED.last_health_at,
12850
13791
  last_error = EXCLUDED.last_error,
12851
13792
  updated_at = NOW()
12852
- RETURNING ${columns5}`,
13793
+ RETURNING ${columns6}`,
12853
13794
  [
12854
13795
  deployment.id,
12855
13796
  deployment.tenantId,
@@ -12869,7 +13810,7 @@ var PostgreSQLPluginDeploymentStore = class {
12869
13810
  new Date(deployment.updatedAt)
12870
13811
  ]
12871
13812
  );
12872
- return mapRow8(result.rows[0]);
13813
+ return mapRow14(result.rows[0]);
12873
13814
  }
12874
13815
  /** Applies a partial update to mutable columns; returns undefined when no row exists. */
12875
13816
  async updateStatus(tenantId, pluginType, patch) {
@@ -12897,10 +13838,10 @@ var PostgreSQLPluginDeploymentStore = class {
12897
13838
  const result = await this.pool.query(
12898
13839
  `UPDATE lattice_plugin_deployments SET ${assignments.join(", ")}
12899
13840
  WHERE tenant_id = $${tenantParam} AND plugin_type = $${pluginParam}
12900
- RETURNING ${columns5}`,
13841
+ RETURNING ${columns6}`,
12901
13842
  params
12902
13843
  );
12903
- return result.rows[0] ? mapRow8(result.rows[0]) : void 0;
13844
+ return result.rows[0] ? mapRow14(result.rows[0]) : void 0;
12904
13845
  }
12905
13846
  /** Deletes a deployment by tenant + plugin type; returns whether a row was removed. */
12906
13847
  async delete(tenantId, pluginType) {
@@ -12914,7 +13855,7 @@ var PostgreSQLPluginDeploymentStore = class {
12914
13855
 
12915
13856
  // src/stores/PostgreSQLAgentRunLedger.ts
12916
13857
  var import_pg30 = require("pg");
12917
- var import_core10 = require("@axiom-lattice/core");
13858
+ var import_core12 = require("@axiom-lattice/core");
12918
13859
  var DEFAULT_LIST_LIMIT = 50;
12919
13860
  var MAX_LIST_LIMIT = 200;
12920
13861
  var CLAIM_RUN_SQL = `UPDATE lattice_agent_runs AS r
@@ -13048,7 +13989,7 @@ var PostgreSQLAgentRunLedger = class {
13048
13989
  } catch (error) {
13049
13990
  await client.query("ROLLBACK");
13050
13991
  if (isActiveRunViolation(error)) {
13051
- throw new import_core10.AgentRunAlreadyActiveError();
13992
+ throw new import_core12.AgentRunAlreadyActiveError();
13052
13993
  }
13053
13994
  throw error;
13054
13995
  } finally {
@@ -13060,19 +14001,19 @@ var PostgreSQLAgentRunLedger = class {
13060
14001
  const source = input.source ?? "run_api";
13061
14002
  if (source === "project_task") {
13062
14003
  if (input.taskId === void 0 || input.taskId.trim().length === 0) {
13063
- throw new import_core10.AgentRunInvalidTaskIdError(
14004
+ throw new import_core12.AgentRunInvalidTaskIdError(
13064
14005
  "A project_task run requires a non-empty taskId"
13065
14006
  );
13066
14007
  }
13067
14008
  } else if (input.taskId !== void 0) {
13068
- throw new import_core10.AgentRunInvalidTaskIdError(
14009
+ throw new import_core12.AgentRunInvalidTaskIdError(
13069
14010
  `A ${source} run cannot carry a taskId`
13070
14011
  );
13071
14012
  }
13072
14013
  if (input.input != null && input.command != null) {
13073
- throw new import_core10.AgentRunInputCommandConflictError();
14014
+ throw new import_core12.AgentRunInputCommandConflictError();
13074
14015
  }
13075
- const inputMessageIds = input.inputMessageIds ? (0, import_core10.normalizeAgentRunMessageIds)(input.inputMessageIds) : (0, import_core10.extractAgentRunInputMessageIds)(input.input);
14016
+ const inputMessageIds = input.inputMessageIds ? (0, import_core12.normalizeAgentRunMessageIds)(input.inputMessageIds) : (0, import_core12.extractAgentRunInputMessageIds)(input.input);
13076
14017
  const availableAt = input.availableAt ?? null;
13077
14018
  const limit = input.maxActiveRunsForTenant;
13078
14019
  const client = await this.pool.connect();
@@ -13099,7 +14040,7 @@ var PostgreSQLAgentRunLedger = class {
13099
14040
  if ((duplicate.rowCount ?? 0) > 0) {
13100
14041
  const existing = duplicate.rows[0].input_message_ids;
13101
14042
  const messageId = existing.find((id) => inputMessageIds.includes(id)) ?? inputMessageIds[0];
13102
- throw new import_core10.AgentRunDuplicateMessageIdError(messageId);
14043
+ throw new import_core12.AgentRunDuplicateMessageIdError(messageId);
13103
14044
  }
13104
14045
  }
13105
14046
  if (limit !== void 0 && limit > 0) {
@@ -13112,7 +14053,7 @@ var PostgreSQLAgentRunLedger = class {
13112
14053
  [input.tenantId]
13113
14054
  );
13114
14055
  if (Number(active.rows[0].count) >= limit) {
13115
- throw new import_core10.AgentRunTenantQuotaExceededError(input.tenantId, limit);
14056
+ throw new import_core12.AgentRunTenantQuotaExceededError(input.tenantId, limit);
13116
14057
  }
13117
14058
  }
13118
14059
  const result = await client.query(
@@ -13169,7 +14110,7 @@ var PostgreSQLAgentRunLedger = class {
13169
14110
  }
13170
14111
  const maxRunning = positiveLimit(options?.maxRunningPerTenant);
13171
14112
  const sources = options?.sources === void 0 ? ["run_api"] : [...options.sources];
13172
- (0, import_core10.assertQueueSources)(sources);
14113
+ (0, import_core12.assertQueueSources)(sources);
13173
14114
  const params = [limit];
13174
14115
  let cursorClause = "";
13175
14116
  if (after) {
@@ -13241,7 +14182,7 @@ var PostgreSQLAgentRunLedger = class {
13241
14182
  async claimRun(input) {
13242
14183
  const maxRunning = positiveLimit(input.maxRunningPerTenant);
13243
14184
  const sources = input.sources === void 0 ? ["run_api"] : [...input.sources];
13244
- (0, import_core10.assertQueueSources)(sources);
14185
+ (0, import_core12.assertQueueSources)(sources);
13245
14186
  if (maxRunning !== void 0) {
13246
14187
  return this.claimRunWithTenantCap(input, maxRunning, sources);
13247
14188
  }
@@ -13710,7 +14651,7 @@ var PostgreSQLAgentRunLedger = class {
13710
14651
  }
13711
14652
  /** Reports expired run-queue rows for the requested sources. */
13712
14653
  async reapExpired(sources = ["run_api"]) {
13713
- (0, import_core10.assertQueueSources)(sources);
14654
+ (0, import_core12.assertQueueSources)(sources);
13714
14655
  const result = await this.pool.query(
13715
14656
  `SELECT * FROM lattice_agent_runs
13716
14657
  WHERE source = ANY($1::text[]) AND status = 'running'
@@ -13856,11 +14797,82 @@ function isActiveRunViolation(error) {
13856
14797
  return code === "23505" && constraint === "uq_lattice_agent_runs_one_running";
13857
14798
  }
13858
14799
 
14800
+ // src/stores/PostgreSQLPlatformSettingsStore.ts
14801
+ var import_protocols12 = require("@axiom-lattice/protocols");
14802
+ var TABLE7 = "lattice_platform_settings";
14803
+ var SETTINGS_ID = "default";
14804
+ var COLUMNS = "id, document, version, updated_by, updated_at";
14805
+ function normalizeUpdatedBy(updatedBy) {
14806
+ const trimmed = updatedBy?.trim();
14807
+ return trimmed ? trimmed : null;
14808
+ }
14809
+ function mapRow15(row) {
14810
+ return {
14811
+ ...row.document,
14812
+ version: Number(row.version),
14813
+ ...row.updated_by !== null ? { updatedBy: row.updated_by } : {},
14814
+ updatedAt: row.updated_at
14815
+ };
14816
+ }
14817
+ var PostgreSQLPlatformSettingsStore = class {
14818
+ constructor(options) {
14819
+ this.options = options;
14820
+ }
14821
+ get pool() {
14822
+ return this.options.pool;
14823
+ }
14824
+ async get() {
14825
+ const result = await this.pool.query(
14826
+ `SELECT ${COLUMNS} FROM ${TABLE7} WHERE id = $1`,
14827
+ [SETTINGS_ID]
14828
+ );
14829
+ return result.rows[0] ? mapRow15(result.rows[0]) : null;
14830
+ }
14831
+ async save(document, expectedVersion, updatedBy) {
14832
+ const version = expectedVersion + 1;
14833
+ const result = expectedVersion === 0 ? await this.pool.query(
14834
+ `INSERT INTO ${TABLE7} (id, document, version, updated_by, updated_at)
14835
+ VALUES ($1, $2::jsonb, $3, $4, NOW())
14836
+ ON CONFLICT (id) DO NOTHING
14837
+ RETURNING ${COLUMNS}`,
14838
+ [
14839
+ SETTINGS_ID,
14840
+ JSON.stringify(document),
14841
+ version,
14842
+ normalizeUpdatedBy(updatedBy)
14843
+ ]
14844
+ ) : await this.pool.query(
14845
+ `UPDATE ${TABLE7}
14846
+ SET document = $2::jsonb, version = $3, updated_by = $4, updated_at = NOW()
14847
+ WHERE id = $1 AND version = $5
14848
+ RETURNING ${COLUMNS}`,
14849
+ [
14850
+ SETTINGS_ID,
14851
+ JSON.stringify(document),
14852
+ version,
14853
+ normalizeUpdatedBy(updatedBy),
14854
+ expectedVersion
14855
+ ]
14856
+ );
14857
+ if (result.rows[0]) {
14858
+ return mapRow15(result.rows[0]);
14859
+ }
14860
+ const actual = await this.pool.query(
14861
+ `SELECT version FROM ${TABLE7} WHERE id = $1`,
14862
+ [SETTINGS_ID]
14863
+ );
14864
+ throw new import_protocols12.PlatformSettingsVersionConflictError(
14865
+ expectedVersion,
14866
+ actual.rows[0] ? Number(actual.rows[0].version) : 0
14867
+ );
14868
+ }
14869
+ };
14870
+
13859
14871
  // src/stores/PostgreSQLChannelReplyJobStore.ts
13860
14872
  var import_pg31 = require("pg");
13861
- var import_protocols7 = require("@axiom-lattice/protocols");
14873
+ var import_protocols13 = require("@axiom-lattice/protocols");
13862
14874
  var DB_CLOCK_MS2 = "FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint";
13863
- var COLUMNS = `run_id, tenant_id, assistant_id, thread_id, adapter_channel,
14875
+ var COLUMNS2 = `run_id, tenant_id, assistant_id, thread_id, adapter_channel,
13864
14876
  channel_installation_id, reply_target, input_message_id, stream_cursor, status,
13865
14877
  attempts, available_at, lease_owner, lease_expires_at, last_error,
13866
14878
  output_message_id, created_at, updated_at, delivered_at`;
@@ -13878,7 +14890,7 @@ function toStatus(value) {
13878
14890
  }
13879
14891
  throw new Error(`Invalid channel reply job status: ${value}`);
13880
14892
  }
13881
- function mapRow9(row) {
14893
+ function mapRow16(row) {
13882
14894
  return {
13883
14895
  runId: row.run_id,
13884
14896
  tenantId: row.tenant_id,
@@ -13949,7 +14961,7 @@ var PostgreSQLChannelReplyJobStore = class {
13949
14961
  }
13950
14962
  /** Creates a pending job with database-clock created/updated timestamps. */
13951
14963
  async create(input) {
13952
- (0, import_protocols7.assertValidCreateChannelReplyJobInput)(input);
14964
+ (0, import_protocols13.assertValidCreateChannelReplyJobInput)(input);
13953
14965
  const result = await this.pool.query(
13954
14966
  `WITH clock AS (SELECT ${DB_CLOCK_MS2} AS now_ms)
13955
14967
  INSERT INTO lattice_channel_reply_jobs (
@@ -13964,7 +14976,7 @@ var PostgreSQLChannelReplyJobStore = class {
13964
14976
  'pending', 0, $10, NULL, NULL,
13965
14977
  NULL, NULL, clock.now_ms, clock.now_ms, NULL
13966
14978
  FROM clock
13967
- RETURNING ${COLUMNS}`,
14979
+ RETURNING ${COLUMNS2}`,
13968
14980
  [
13969
14981
  input.runId,
13970
14982
  input.tenantId,
@@ -13978,31 +14990,31 @@ var PostgreSQLChannelReplyJobStore = class {
13978
14990
  input.availableAt
13979
14991
  ]
13980
14992
  );
13981
- return mapRow9(result.rows[0]);
14993
+ return mapRow16(result.rows[0]);
13982
14994
  }
13983
14995
  /** Reads one job within a tenant scope. */
13984
14996
  async get(tenantId, runId) {
13985
14997
  const result = await this.pool.query(
13986
- `SELECT ${COLUMNS}
14998
+ `SELECT ${COLUMNS2}
13987
14999
  FROM lattice_channel_reply_jobs
13988
15000
  WHERE tenant_id = $1 AND run_id = $2`,
13989
15001
  [tenantId, runId]
13990
15002
  );
13991
- return result.rowCount === 0 ? null : mapRow9(result.rows[0]);
15003
+ return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
13992
15004
  }
13993
15005
  /** Lists claimable pending jobs that are due at `now`, oldest first. */
13994
15006
  async listPending(now, limit) {
13995
15007
  const result = await this.pool.query(
13996
- `SELECT ${COLUMNS}
15008
+ `SELECT ${COLUMNS2}
13997
15009
  FROM lattice_channel_reply_jobs
13998
15010
  WHERE status = 'pending'
13999
15011
  AND available_at <= $1
14000
15012
  AND (lease_owner IS NULL OR lease_expires_at IS NULL OR lease_expires_at <= $1)
14001
15013
  ORDER BY available_at ASC, created_at ASC, run_id ASC
14002
15014
  LIMIT $2`,
14003
- [now, (0, import_protocols7.normalizeChannelReplyJobListLimit)(limit)]
15015
+ [now, (0, import_protocols13.normalizeChannelReplyJobListLimit)(limit)]
14004
15016
  );
14005
- return result.rows.map(mapRow9);
15017
+ return result.rows.map(mapRow16);
14006
15018
  }
14007
15019
  /**
14008
15020
  * Atomically leases a claimable job.
@@ -14023,10 +15035,10 @@ var PostgreSQLChannelReplyJobStore = class {
14023
15035
  AND j.status = 'pending'
14024
15036
  AND j.available_at <= clock.now_ms
14025
15037
  AND (j.lease_owner IS NULL OR j.lease_expires_at IS NULL OR j.lease_expires_at <= clock.now_ms)
14026
- RETURNING ${COLUMNS}`,
15038
+ RETURNING ${COLUMNS2}`,
14027
15039
  [tenantId, runId, owner, leaseMs]
14028
15040
  );
14029
- return result.rowCount === 0 ? null : mapRow9(result.rows[0]);
15041
+ return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
14030
15042
  }
14031
15043
  /**
14032
15044
  * Marks a job delivered when `owner` still holds the lease.
@@ -14049,10 +15061,10 @@ var PostgreSQLChannelReplyJobStore = class {
14049
15061
  AND j.run_id = $2
14050
15062
  AND j.lease_owner = $3
14051
15063
  AND j.status = 'pending'
14052
- RETURNING ${COLUMNS}`,
15064
+ RETURNING ${COLUMNS2}`,
14053
15065
  [tenantId, runId, owner, outputMessageId]
14054
15066
  );
14055
- return result.rowCount === 0 ? null : mapRow9(result.rows[0]);
15067
+ return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
14056
15068
  }
14057
15069
  /**
14058
15070
  * Returns a job to pending with an incremented attempt count.
@@ -14076,10 +15088,10 @@ var PostgreSQLChannelReplyJobStore = class {
14076
15088
  AND j.run_id = $2
14077
15089
  AND j.lease_owner = $3
14078
15090
  AND j.status = 'pending'
14079
- RETURNING ${COLUMNS}`,
15091
+ RETURNING ${COLUMNS2}`,
14080
15092
  [tenantId, runId, owner, error, availableAt]
14081
15093
  );
14082
- return result.rowCount === 0 ? null : mapRow9(result.rows[0]);
15094
+ return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
14083
15095
  }
14084
15096
  /**
14085
15097
  * Marks a job terminally failed when `owner` still holds the lease.
@@ -14101,10 +15113,10 @@ var PostgreSQLChannelReplyJobStore = class {
14101
15113
  AND j.run_id = $2
14102
15114
  AND j.lease_owner = $3
14103
15115
  AND j.status = 'pending'
14104
- RETURNING ${COLUMNS}`,
15116
+ RETURNING ${COLUMNS2}`,
14105
15117
  [tenantId, runId, owner, error]
14106
15118
  );
14107
- return result.rowCount === 0 ? null : mapRow9(result.rows[0]);
15119
+ return result.rowCount === 0 ? null : mapRow16(result.rows[0]);
14108
15120
  }
14109
15121
  /** Deletes one job. Returns whether a row was removed. */
14110
15122
  async delete(tenantId, runId) {
@@ -14126,7 +15138,7 @@ var PostgreSQLChannelReplyJobStore = class {
14126
15138
  ORDER BY updated_at ASC, run_id ASC
14127
15139
  LIMIT $2
14128
15140
  )`,
14129
- [before, (0, import_protocols7.normalizeChannelReplyJobListLimit)(limit)]
15141
+ [before, (0, import_protocols13.normalizeChannelReplyJobListLimit)(limit)]
14130
15142
  );
14131
15143
  return result.rowCount ?? 0;
14132
15144
  }
@@ -14164,7 +15176,7 @@ async function rollbackQuietly(client) {
14164
15176
  } catch {
14165
15177
  }
14166
15178
  }
14167
- function isUniqueViolation3(error) {
15179
+ function isUniqueViolation5(error) {
14168
15180
  return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
14169
15181
  }
14170
15182
  function isRecord5(value) {
@@ -14292,7 +15304,7 @@ var PostgreSQLCheckpointStorage = class {
14292
15304
  await client.query("COMMIT");
14293
15305
  } catch (error) {
14294
15306
  await rollbackQuietly(client);
14295
- if (isUniqueViolation3(error)) {
15307
+ if (isUniqueViolation5(error)) {
14296
15308
  throw new CheckpointTargetNotEmptyError(targetThreadId);
14297
15309
  }
14298
15310
  throw error;
@@ -14580,8 +15592,8 @@ var PostgreSQLCheckpointStorage = class {
14580
15592
  };
14581
15593
 
14582
15594
  // src/stores/PostgreSQLStore.ts
14583
- var import_protocols8 = require("@axiom-lattice/protocols");
14584
- function mapRow10(row) {
15595
+ var import_protocols14 = require("@axiom-lattice/protocols");
15596
+ function mapRow17(row) {
14585
15597
  return {
14586
15598
  namespace: row.namespace,
14587
15599
  key: row.key,
@@ -14599,9 +15611,9 @@ var PostgreSQLStore = class {
14599
15611
  this.pool = pool;
14600
15612
  }
14601
15613
  async put(tenantId, input) {
14602
- (0, import_protocols8.assertStoreIdentity)(input.namespace, input.key);
15614
+ (0, import_protocols14.assertStoreIdentity)(input.namespace, input.key);
14603
15615
  const now = /* @__PURE__ */ new Date();
14604
- const expiresAt = (0, import_protocols8.storeExpiresAt)(input.ttlMinutes, now) ?? null;
15616
+ const expiresAt = (0, import_protocols14.storeExpiresAt)(input.ttlMinutes, now) ?? null;
14605
15617
  const result = await this.pool.query(
14606
15618
  `INSERT INTO lattice_store_items
14607
15619
  (tenant_id, namespace, key, value, created_at, updated_at, expires_at)
@@ -14613,10 +15625,10 @@ var PostgreSQLStore = class {
14613
15625
  RETURNING tenant_id, namespace, key, value, created_at, updated_at, expires_at`,
14614
15626
  [tenantId, input.namespace, input.key, JSON.stringify(input.value), now, expiresAt]
14615
15627
  );
14616
- return mapRow10(result.rows[0]);
15628
+ return mapRow17(result.rows[0]);
14617
15629
  }
14618
15630
  async get(tenantId, namespace, key) {
14619
- (0, import_protocols8.assertStoreIdentity)(namespace, key);
15631
+ (0, import_protocols14.assertStoreIdentity)(namespace, key);
14620
15632
  const result = await this.pool.query(
14621
15633
  `SELECT tenant_id, namespace, key, value, created_at, updated_at, expires_at
14622
15634
  FROM lattice_store_items
@@ -14624,10 +15636,10 @@ var PostgreSQLStore = class {
14624
15636
  AND (expires_at IS NULL OR expires_at > $4)`,
14625
15637
  [tenantId, namespace, key, /* @__PURE__ */ new Date()]
14626
15638
  );
14627
- return result.rows[0] ? mapRow10(result.rows[0]) : null;
15639
+ return result.rows[0] ? mapRow17(result.rows[0]) : null;
14628
15640
  }
14629
15641
  async delete(tenantId, namespace, key) {
14630
- (0, import_protocols8.assertStoreIdentity)(namespace, key);
15642
+ (0, import_protocols14.assertStoreIdentity)(namespace, key);
14631
15643
  const result = await this.pool.query(
14632
15644
  `DELETE FROM lattice_store_items
14633
15645
  WHERE tenant_id = $1 AND namespace = $2 AND key = $3`,
@@ -14642,7 +15654,7 @@ var PostgreSQLStore = class {
14642
15654
  ];
14643
15655
  const params = [tenantId, /* @__PURE__ */ new Date()];
14644
15656
  if (input.namespacePrefix) {
14645
- (0, import_protocols8.assertStoreNamespacePrefix)(input.namespacePrefix);
15657
+ (0, import_protocols14.assertStoreNamespacePrefix)(input.namespacePrefix);
14646
15658
  }
14647
15659
  if (input.namespacePrefix && input.namespacePrefix.length > 0) {
14648
15660
  params.push(input.namespacePrefix);
@@ -14656,9 +15668,9 @@ var PostgreSQLStore = class {
14656
15668
  params.push(input.query.replace(/[\\%_]/g, "\\$&"));
14657
15669
  conditions.push(`value::text ILIKE '%' || $${params.length} || '%' ESCAPE '\\'`);
14658
15670
  }
14659
- params.push((0, import_protocols8.storePaginationValue)(input.limit, 10));
15671
+ params.push((0, import_protocols14.storePaginationValue)(input.limit, 10));
14660
15672
  const limitParam = params.length;
14661
- params.push((0, import_protocols8.storePaginationValue)(input.offset, 0));
15673
+ params.push((0, import_protocols14.storePaginationValue)(input.offset, 0));
14662
15674
  const offsetParam = params.length;
14663
15675
  const result = await this.pool.query(
14664
15676
  `SELECT tenant_id, namespace, key, value, created_at, updated_at, expires_at
@@ -14668,10 +15680,10 @@ var PostgreSQLStore = class {
14668
15680
  LIMIT $${limitParam} OFFSET $${offsetParam}`,
14669
15681
  params
14670
15682
  );
14671
- return result.rows.map(mapRow10);
15683
+ return result.rows.map(mapRow17);
14672
15684
  }
14673
15685
  async listNamespaces(tenantId, prefix = [], limit = 100) {
14674
- (0, import_protocols8.assertStoreNamespacePrefix)(prefix);
15686
+ (0, import_protocols14.assertStoreNamespacePrefix)(prefix);
14675
15687
  const conditions = [
14676
15688
  "tenant_id = $1",
14677
15689
  "(expires_at IS NULL OR expires_at > $2)"
@@ -14681,7 +15693,7 @@ var PostgreSQLStore = class {
14681
15693
  params.push(prefix);
14682
15694
  conditions.push(`namespace[1:array_length($${params.length}::text[], 1)] = $${params.length}`);
14683
15695
  }
14684
- params.push((0, import_protocols8.storePaginationValue)(limit, 100));
15696
+ params.push((0, import_protocols14.storePaginationValue)(limit, 100));
14685
15697
  const result = await this.pool.query(
14686
15698
  `SELECT DISTINCT namespace
14687
15699
  FROM lattice_store_items
@@ -14693,7 +15705,7 @@ var PostgreSQLStore = class {
14693
15705
  return result.rows.map((row) => row.namespace);
14694
15706
  }
14695
15707
  async deleteExpired(now, limit) {
14696
- const bounded = (0, import_protocols8.storePaginationValue)(limit, 0);
15708
+ const bounded = (0, import_protocols14.storePaginationValue)(limit, 0);
14697
15709
  const result = await this.pool.query(
14698
15710
  `WITH expired AS (
14699
15711
  SELECT ctid FROM lattice_store_items
@@ -14839,6 +15851,16 @@ async function createPgStoreConfig(connectionString) {
14839
15851
  mm.register(agentRunProjectTaskRunningIndexMigration);
14840
15852
  mm.register(addModelProviderKindDefault);
14841
15853
  mm.register(addShareKind);
15854
+ mm.register(createUserExternalIdentityTable);
15855
+ mm.register(createUserEmailLowerUniqueIndex);
15856
+ mm.register(createOidcProviderConfigsTable);
15857
+ mm.register(createPlatformMembersTable);
15858
+ mm.register(createPlatformInvitationsTable);
15859
+ mm.register(createOidcIdentityClaimsTable);
15860
+ mm.register(createInviteLinksTable);
15861
+ mm.register(addOidcIdentityClaimInviteFields);
15862
+ mm.register(createPlatformSettingsTable);
15863
+ mm.register(addOidcProviderAllowedHosts);
14842
15864
  await mm.migrate();
14843
15865
  const checkpoint = import_langgraph_checkpoint_postgres.PostgresSaver.fromConnString(connectionString);
14844
15866
  try {
@@ -14855,6 +15877,13 @@ async function createPgStoreConfig(connectionString) {
14855
15877
  user: new PostgreSQLUserStore(opts),
14856
15878
  tenant: new PostgreSQLTenantStore(opts),
14857
15879
  userTenantLink: new PostgreSQLUserTenantLinkStore(opts),
15880
+ userExternalIdentity: new PostgreSQLUserExternalIdentityStore(opts),
15881
+ oidcProviderConfig: new PostgreSQLOidcProviderConfigStore(opts),
15882
+ platformMember: new PostgreSQLPlatformMemberStore(opts),
15883
+ platformInvitation: new PostgreSQLPlatformInvitationStore(opts),
15884
+ oidcIdentityClaim: new PostgreSQLOidcIdentityClaimStore(opts),
15885
+ inviteLink: new PostgreSQLInviteLinkStore(opts),
15886
+ platformSettings: new PostgreSQLPlatformSettingsStore(opts),
14858
15887
  channelBinding: new ChannelBindingStore(opts),
14859
15888
  channelInstallation: new PostgreSQLChannelInstallationStore(opts),
14860
15889
  thread: new PostgreSQLThreadStore(opts),
@@ -15452,6 +16481,7 @@ function mapRowToChannelIdentityMapping(row) {
15452
16481
  InvalidFileShareAddressError,
15453
16482
  InvalidMcpAppShareAddressError,
15454
16483
  InvalidMcpAppShareVisibilityError,
16484
+ InvalidUserExternalIdentityRowError,
15455
16485
  MenuStore,
15456
16486
  MigrationManager,
15457
16487
  PGVectorStoreProvider,
@@ -15468,10 +16498,16 @@ function mapRowToChannelIdentityMapping(row) {
15468
16498
  PostgreSQLConnectionStore,
15469
16499
  PostgreSQLDatabaseConfigStore,
15470
16500
  PostgreSQLEvalStore,
16501
+ PostgreSQLInviteLinkStore,
15471
16502
  PostgreSQLMcpServerConfigStore,
15472
16503
  PostgreSQLMetricsServerConfigStore,
15473
16504
  PostgreSQLModelProviderStore,
16505
+ PostgreSQLOidcIdentityClaimStore,
16506
+ PostgreSQLOidcProviderConfigStore,
15474
16507
  PostgreSQLOpenAuditStore,
16508
+ PostgreSQLPlatformInvitationStore,
16509
+ PostgreSQLPlatformMemberStore,
16510
+ PostgreSQLPlatformSettingsStore,
15475
16511
  PostgreSQLPluginDeploymentStore,
15476
16512
  PostgreSQLProjectBotMembershipStore,
15477
16513
  PostgreSQLProjectMembershipStore,
@@ -15485,6 +16521,7 @@ function mapRowToChannelIdentityMapping(row) {
15485
16521
  PostgreSQLTaskWorkItemStore,
15486
16522
  PostgreSQLTenantStore,
15487
16523
  PostgreSQLThreadStore,
16524
+ PostgreSQLUserExternalIdentityStore,
15488
16525
  PostgreSQLUserStore,
15489
16526
  PostgreSQLUserTenantLinkStore,
15490
16527
  PostgreSQLWorkflowTrackingStore,
@@ -15502,6 +16539,7 @@ function mapRowToChannelIdentityMapping(row) {
15502
16539
  addInterruptColumnsToEval,
15503
16540
  addInterruptPolicyToEvalCases,
15504
16541
  addMcpAppContentType,
16542
+ addOidcIdentityClaimInviteFields,
15505
16543
  addProjectConfigColumn,
15506
16544
  addScheduleTenantId,
15507
16545
  addShareKind,
@@ -15531,11 +16569,17 @@ function mapRowToChannelIdentityMapping(row) {
15531
16569
  createEvalRunResultsTable,
15532
16570
  createEvalRunsTable,
15533
16571
  createEvalSuitesTable,
16572
+ createInviteLinksTable,
15534
16573
  createMcpServerConfigsTable,
15535
16574
  createMenuItemsTable,
15536
16575
  createMetricsConfigsTable,
16576
+ createOidcIdentityClaimsTable,
16577
+ createOidcProviderConfigsTable,
15537
16578
  createPGVectorStoreProvider,
15538
16579
  createPgStoreConfig,
16580
+ createPlatformInvitationsTable,
16581
+ createPlatformMembersTable,
16582
+ createPlatformSettingsTable,
15539
16583
  createPluginDeployments,
15540
16584
  createProjectRoomReadStates,
15541
16585
  createProjectRoomTables,
@@ -15547,6 +16591,8 @@ function mapRowToChannelIdentityMapping(row) {
15547
16591
  createTenantsTable,
15548
16592
  createThreadMessageQueueTable,
15549
16593
  createThreadsTable,
16594
+ createUserEmailLowerUniqueIndex,
16595
+ createUserExternalIdentityTable,
15550
16596
  createUsersTable,
15551
16597
  createWorkflowTrackingTables,
15552
16598
  createWorkspacesTable,