@axiom-lattice/gateway 8.3.0 → 9.0.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 (126) hide show
  1. package/dist/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
  2. package/dist/{a2a-BHBLACJV.mjs → a2a-YLKZE5JG.mjs} +7 -5
  3. package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-MD7WNDTP.mjs} +79 -127
  4. package/dist/a2a-standard-MD7WNDTP.mjs.map +1 -0
  5. package/dist/chunk-A7YNAU5L.mjs +377 -0
  6. package/dist/chunk-A7YNAU5L.mjs.map +1 -0
  7. package/dist/{chunk-3XIIQ6EK.mjs → chunk-BOPNXTMO.mjs} +66 -5
  8. package/dist/chunk-BOPNXTMO.mjs.map +1 -0
  9. package/dist/{chunk-L6MBEEII.mjs → chunk-JGQURU44.mjs} +24 -6
  10. package/dist/chunk-JGQURU44.mjs.map +1 -0
  11. package/dist/{chunk-GPHOYR3X.mjs → chunk-LCM4OQTI.mjs} +30 -1
  12. package/dist/chunk-LCM4OQTI.mjs.map +1 -0
  13. package/dist/{chunk-HVIKCUOF.mjs → chunk-MFWTGNQR.mjs} +74 -5
  14. package/dist/chunk-MFWTGNQR.mjs.map +1 -0
  15. package/dist/chunk-NYYSBNDG.mjs +575 -0
  16. package/dist/chunk-NYYSBNDG.mjs.map +1 -0
  17. package/dist/{chunk-W3YH4S6G.mjs → chunk-SIMEG2JT.mjs} +19 -2
  18. package/dist/chunk-SIMEG2JT.mjs.map +1 -0
  19. package/dist/chunk-T2HIWZGP.mjs +580 -0
  20. package/dist/chunk-T2HIWZGP.mjs.map +1 -0
  21. package/dist/chunk-Y7OVVPVU.mjs +13 -0
  22. package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
  23. package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
  24. package/dist/chunk-YR32MWEJ.mjs.map +1 -0
  25. package/dist/embed/{embed-page-AgentDetailSideApp-7YTMN643.js → embed-page-AgentDetailSideApp-6RYNHS5F.js} +1 -1
  26. package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
  27. package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
  28. package/dist/embed/embed-page-AgentGovernanceWorkbench-UAPXF3SZ.js +1 -0
  29. package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-ZYGZNW7P.js} +1 -1
  30. package/dist/embed/{embed-page-MessageGraph-N2DXVMEK.js → embed-page-MessageGraph-S2P3WACC.js} +1 -1
  31. package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
  32. package/dist/embed/{embed-page-ReactChart-3QDZX2WG.js → embed-page-ReactChart-I3UZVE4Z.js} +1 -1
  33. package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
  34. package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-JMIJ46FX.js} +1 -1
  35. package/dist/embed/{embed-page-TasksSideApp-MYIRJWFM.js → embed-page-TasksSideApp-IVFEFLSH.js} +1 -1
  36. package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
  37. package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-KQPPAEWJ.js} +1 -1
  38. package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
  39. package/dist/embed/{embed-page-TopologyRuntimeView-3O6VJUZR.js → embed-page-TopologyRuntimeView-XCFQ23HF.js} +1 -1
  40. package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
  41. package/dist/embed/embed-page-WorkflowAutomationView-UTKCKP75.js +3 -0
  42. package/dist/embed/embed-page-chunk-5X4MNQAW.js +242 -0
  43. package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-77CQOFKU.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-PROZXFWS.js → embed-page-chunk-7VDAOX3D.js} +1 -1
  45. package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-MD4U4DHC.js} +5 -5
  46. package/dist/embed/embed-page-chunk-RANB2CUO.js +3 -0
  47. package/dist/embed/{embed-page-chunk-IELKCYQX.js → embed-page-chunk-RVIS6CKE.js} +1 -1
  48. package/dist/embed/embed-page-chunk-SEOTZ4Q7.js +6829 -0
  49. package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-TCVPYXPL.js} +2 -2
  50. package/dist/embed/embed-page-chunk-WHGU76CU.js +211 -0
  51. package/dist/embed/{embed-page-chunk-WGEXK773.js → embed-page-chunk-ZZFTCBM6.js} +1 -1
  52. package/dist/embed/embed-page-en-4HSTKY4Q.js +1 -0
  53. package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
  54. package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
  55. package/dist/embed/embed-page-en-MIVBTEEL.js +1 -0
  56. package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
  57. package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
  58. package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
  59. package/dist/embed/embed-page-zh-7PJNJJZY.js +1 -0
  60. package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
  61. package/dist/embed/embed-page-zh-UZ4GPIU7.js +1 -0
  62. package/dist/embed/embed-page.css +1 -1
  63. package/dist/embed/embed-page.js +1 -1
  64. package/dist/embed/mcp-app-page.js +61 -0
  65. package/dist/index.d.mts +29 -0
  66. package/dist/index.d.ts +29 -0
  67. package/dist/index.js +3574 -2349
  68. package/dist/index.js.map +1 -1
  69. package/dist/index.mjs +327 -1049
  70. package/dist/index.mjs.map +1 -1
  71. package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
  72. package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
  73. package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
  74. package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
  75. package/dist/open-27ZPLBIW.mjs.map +1 -0
  76. package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
  77. package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
  78. package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-HGYT5VFC.mjs} +8 -9
  79. package/dist/open-exposure-HGYT5VFC.mjs.map +1 -0
  80. package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
  81. package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
  82. package/dist/open-session-CLZT3HVG.mjs.map +1 -0
  83. package/dist/openLattice-D5YTSZBY.mjs +28 -0
  84. package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
  85. package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
  86. package/dist/resources-YD5DOPGD.mjs.map +1 -0
  87. package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
  88. package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
  89. package/package.json +10 -8
  90. package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
  91. package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
  92. package/dist/chunk-GL7RV5L3.mjs.map +0 -1
  93. package/dist/chunk-GPHOYR3X.mjs.map +0 -1
  94. package/dist/chunk-GXZFO3VU.mjs +0 -42
  95. package/dist/chunk-GXZFO3VU.mjs.map +0 -1
  96. package/dist/chunk-HVIKCUOF.mjs.map +0 -1
  97. package/dist/chunk-HYYLYIMH.mjs +0 -230
  98. package/dist/chunk-HYYLYIMH.mjs.map +0 -1
  99. package/dist/chunk-L6MBEEII.mjs.map +0 -1
  100. package/dist/chunk-W3YH4S6G.mjs.map +0 -1
  101. package/dist/embed/embed-page-AgentGovernanceWorkbench-YWJ4ZLEV.js +0 -1
  102. package/dist/embed/embed-page-WorkflowAutomationView-YHOS4WQR.js +0 -3
  103. package/dist/embed/embed-page-chunk-4XKCMM5C.js +0 -211
  104. package/dist/embed/embed-page-chunk-IBRMWDHE.js +0 -6835
  105. package/dist/embed/embed-page-chunk-JTL73EBV.js +0 -3
  106. package/dist/embed/embed-page-chunk-YMVKQQXJ.js +0 -242
  107. package/dist/embed/embed-page-en-E35GE7TA.js +0 -1
  108. package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
  109. package/dist/embed/embed-page-en-JOTELYAL.js +0 -1
  110. package/dist/embed/embed-page-zh-BPO6ZSJM.js +0 -1
  111. package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
  112. package/dist/embed/embed-page-zh-ZLK3GLM3.js +0 -1
  113. package/dist/open-7ED27I5B.mjs.map +0 -1
  114. package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
  115. package/dist/open-exposure-I3DN2X4E.mjs.map +0 -1
  116. package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
  117. package/dist/openLattice-Q6M5S6CY.mjs +0 -18
  118. package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
  119. package/dist/resources-DAU5ZIPB.mjs.map +0 -1
  120. package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
  121. /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
  122. /package/dist/{a2a-BHBLACJV.mjs.map → a2a-YLKZE5JG.mjs.map} +0 -0
  123. /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
  124. /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
  125. /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
  126. /package/dist/{registerExposedPlugins-6GCOE64C.mjs.map → registerExposedPlugins-2JGXSMAL.mjs.map} +0 -0
@@ -0,0 +1,575 @@
1
+ import {
2
+ A2AAuthError
3
+ } from "./chunk-LCM4OQTI.mjs";
4
+
5
+ // src/routes/a2a.ts
6
+ import { getStoreLattice, getReplicaId } from "@axiom-lattice/core";
7
+
8
+ // src/services/a2aKeySync.ts
9
+ var A2A_KEY_CHANGED_TOPIC = "a2a-key-changed";
10
+ async function publishA2AKeyChanged(outbox, owner, tenantId) {
11
+ if (!tenantId) return;
12
+ try {
13
+ await outbox.append({
14
+ topic: A2A_KEY_CHANGED_TOPIC,
15
+ payload: { tenantId },
16
+ originReplicaId: owner
17
+ });
18
+ } catch {
19
+ }
20
+ }
21
+ async function handleA2AKeyChanged(payload, reload) {
22
+ const tenantId = payload?.tenantId;
23
+ if (typeof tenantId !== "string" || tenantId.length === 0) return;
24
+ await reload();
25
+ }
26
+
27
+ // src/services/collaboration/ProjectAccessService.ts
28
+ import { randomUUID } from "crypto";
29
+ var ProjectCollaborationError = class extends Error {
30
+ /** Creates a project collaboration error. */
31
+ constructor(code, statusCode, message) {
32
+ super(message);
33
+ this.code = code;
34
+ this.statusCode = statusCode;
35
+ this.name = "ProjectCollaborationError";
36
+ }
37
+ };
38
+ var allowedRoles = {
39
+ read: /* @__PURE__ */ new Set(["owner", "admin", "member", "viewer"]),
40
+ send: /* @__PURE__ */ new Set(["owner", "admin", "member"]),
41
+ manage: /* @__PURE__ */ new Set(["owner", "admin"])
42
+ };
43
+ var membershipConflictNames = /* @__PURE__ */ new Set([
44
+ "DuplicateProjectMembershipError",
45
+ "ProjectMembershipIdConflictError"
46
+ ]);
47
+ function isProjectMembershipConflict(error) {
48
+ return error instanceof Error && membershipConflictNames.has(error.name);
49
+ }
50
+ var ProjectAccessService = class {
51
+ /** Creates a project access service from explicitly injected stores. */
52
+ constructor(deps) {
53
+ this.deps = deps;
54
+ this.idFactory = deps.idFactory ?? randomUUID;
55
+ }
56
+ /** Returns the project and active membership when the user has the requested capability. */
57
+ async requireProjectAccess(input) {
58
+ const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
59
+ if (!project) {
60
+ throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
61
+ }
62
+ if (project.kind === "public") {
63
+ return this.requirePublicProjectAccess(project, input);
64
+ }
65
+ const membership = await this.deps.membershipStore.findByUser(
66
+ input.tenantId,
67
+ input.projectId,
68
+ input.userId
69
+ );
70
+ if (project.kind === "personal") {
71
+ if (!this.isActiveMembership(membership, input)) this.accessDenied();
72
+ this.requireCapability(membership, input.capability);
73
+ return { project, membership };
74
+ }
75
+ if (this.isActiveMembership(membership, input) && this.hasCapability(membership, input.capability)) {
76
+ return { project, membership };
77
+ }
78
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
79
+ if (this.isActiveMembership(membership, input)) {
80
+ if (tenantAdmin) return { project, membership: tenantAdmin };
81
+ this.requireCapability(membership, input.capability);
82
+ }
83
+ const memberships = await this.deps.membershipStore.list(input.tenantId, input.projectId);
84
+ if (memberships.length !== 0) {
85
+ if (tenantAdmin) return { project, membership: tenantAdmin };
86
+ this.accessDenied();
87
+ }
88
+ if (!tenantAdmin) this.accessDenied();
89
+ let claim;
90
+ try {
91
+ claim = await this.deps.membershipStore.createInitialOwner({
92
+ id: this.idFactory(),
93
+ tenantId: input.tenantId,
94
+ projectId: input.projectId,
95
+ userId: input.userId
96
+ });
97
+ } catch (error) {
98
+ if (isProjectMembershipConflict(error)) {
99
+ throw new ProjectCollaborationError(
100
+ "PROJECT_MEMBERSHIP_CONFLICT",
101
+ 409,
102
+ "Project membership could not be initialized"
103
+ );
104
+ }
105
+ throw error;
106
+ }
107
+ if (claim.kind === "created" || claim.kind === "existing") {
108
+ return { project, membership: claim.membership };
109
+ }
110
+ const racedMembership = await this.deps.membershipStore.findByUser(
111
+ input.tenantId,
112
+ input.projectId,
113
+ input.userId
114
+ );
115
+ if (racedMembership?.status !== "active") this.accessDenied();
116
+ this.requireCapability(racedMembership, input.capability);
117
+ return { project, membership: racedMembership };
118
+ }
119
+ /** Authorizes an already-existing membership, or any tenant member for public rooms; never mutates persistence. */
120
+ async requireExistingProjectAccess(input) {
121
+ const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
122
+ if (!project) throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
123
+ if (project.kind === "public") {
124
+ return this.requireExistingPublicProjectAccess(project, input);
125
+ }
126
+ const membership = await this.deps.membershipStore.findByUser(
127
+ input.tenantId,
128
+ input.projectId,
129
+ input.userId
130
+ );
131
+ const activeMembership = this.isActiveMembership(membership, input) ? membership : void 0;
132
+ if (project.kind === "personal") {
133
+ if (!activeMembership) this.accessDenied();
134
+ this.requireCapability(activeMembership, input.capability);
135
+ return { project, membership: activeMembership };
136
+ }
137
+ if (activeMembership && this.hasCapability(activeMembership, input.capability)) {
138
+ return { project, membership: activeMembership };
139
+ }
140
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
141
+ if (tenantAdmin) return { project, membership: tenantAdmin };
142
+ if (activeMembership) this.requireCapability(activeMembership, input.capability);
143
+ this.accessDenied();
144
+ }
145
+ /**
146
+ * Authorizes roster visibility for any active participant (owner/admin/member/viewer).
147
+ * Mutating the roster requires `manage` separately; reading it never does, because every
148
+ * room participant needs the member list to render the room and derive their own capabilities.
149
+ */
150
+ async requireRosterAccess(input) {
151
+ const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
152
+ if (!project) throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
153
+ return this.requireProjectAccess({
154
+ ...input,
155
+ capability: "read"
156
+ });
157
+ }
158
+ /** Returns projects in a workspace that the user may read. */
159
+ async listReadableProjects(input) {
160
+ const [tenantLink, projects] = await Promise.all([
161
+ this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId),
162
+ this.deps.projectStore.getProjectsByWorkspace(input.tenantId, input.workspaceId)
163
+ ]);
164
+ const filtered = input.kind ? projects.filter((project) => (project.kind ?? "business") === input.kind) : projects;
165
+ const isTenantAdmin = tenantLink?.role === "owner" || tenantLink?.role === "admin";
166
+ const visible = [];
167
+ for (const project of filtered) {
168
+ if (project.kind === "public") {
169
+ if (tenantLink || await this.hasActiveMembership(input.tenantId, project.id, input.userId)) {
170
+ visible.push(project);
171
+ }
172
+ continue;
173
+ }
174
+ if (project.kind === "personal") {
175
+ if (await this.hasActiveMembership(input.tenantId, project.id, input.userId)) {
176
+ visible.push(project);
177
+ }
178
+ continue;
179
+ }
180
+ if (isTenantAdmin || await this.hasActiveMembership(input.tenantId, project.id, input.userId)) {
181
+ visible.push(project);
182
+ }
183
+ }
184
+ return visible;
185
+ }
186
+ async hasActiveMembership(tenantId, projectId, userId) {
187
+ const membership = await this.deps.membershipStore.findByUser(tenantId, projectId, userId);
188
+ return this.matchesActiveMembership(membership, tenantId, projectId, userId);
189
+ }
190
+ matchesActiveMembership(membership, tenantId, projectId, userId) {
191
+ return membership != null && membership.tenantId === tenantId && membership.projectId === projectId && membership.userId === userId && membership.status === "active";
192
+ }
193
+ async requirePublicProjectAccess(project, input) {
194
+ const membership = await this.deps.membershipStore.findByUser(
195
+ input.tenantId,
196
+ input.projectId,
197
+ input.userId
198
+ );
199
+ if (membership?.status === "active") {
200
+ if (this.hasCapability(membership, input.capability)) return { project, membership };
201
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
202
+ if (tenantAdmin) return { project, membership: tenantAdmin };
203
+ this.requireCapability(membership, input.capability);
204
+ return { project, membership };
205
+ }
206
+ const materialized = await this.materializePublicMembership(input);
207
+ this.requireCapability(materialized, input.capability);
208
+ return { project, membership: materialized };
209
+ }
210
+ async requireExistingPublicProjectAccess(project, input) {
211
+ const membership = await this.deps.membershipStore.findByUser(
212
+ input.tenantId,
213
+ input.projectId,
214
+ input.userId
215
+ );
216
+ if (membership?.status === "active") {
217
+ if (this.hasCapability(membership, input.capability)) return { project, membership };
218
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
219
+ if (tenantAdmin) return { project, membership: tenantAdmin };
220
+ this.requireCapability(membership, input.capability);
221
+ return { project, membership };
222
+ }
223
+ const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
224
+ if (!tenantLink) this.accessDenied();
225
+ const role = tenantLink.role === "owner" || tenantLink.role === "admin" ? "admin" : "member";
226
+ const synthetic = {
227
+ id: `public:${input.userId}`,
228
+ tenantId: input.tenantId,
229
+ projectId: input.projectId,
230
+ userId: input.userId,
231
+ role,
232
+ status: "active",
233
+ joinedAt: project.createdAt,
234
+ updatedAt: project.updatedAt
235
+ };
236
+ this.requireCapability(synthetic, input.capability);
237
+ return { project, membership: synthetic };
238
+ }
239
+ async materializePublicMembership(input) {
240
+ const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
241
+ if (!tenantLink) this.accessDenied();
242
+ const existing = await this.deps.membershipStore.findByUser(
243
+ input.tenantId,
244
+ input.projectId,
245
+ input.userId
246
+ );
247
+ if (existing) this.accessDenied();
248
+ const role = tenantLink.role === "owner" || tenantLink.role === "admin" ? "admin" : "member";
249
+ try {
250
+ return await this.deps.membershipStore.create({
251
+ id: this.idFactory(),
252
+ tenantId: input.tenantId,
253
+ projectId: input.projectId,
254
+ userId: input.userId,
255
+ role,
256
+ status: "active"
257
+ });
258
+ } catch (error) {
259
+ if (isProjectMembershipConflict(error)) {
260
+ const raced = await this.deps.membershipStore.findByUser(
261
+ input.tenantId,
262
+ input.projectId,
263
+ input.userId
264
+ );
265
+ if (raced?.status === "active") return raced;
266
+ }
267
+ throw error;
268
+ }
269
+ }
270
+ hasCapability(membership, capability) {
271
+ return allowedRoles[capability].has(membership.role);
272
+ }
273
+ requireCapability(membership, capability) {
274
+ if (!this.hasCapability(membership, capability)) this.accessDenied();
275
+ }
276
+ async tenantAdminMembership(project, input) {
277
+ if (project.kind === "personal") return void 0;
278
+ const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
279
+ if (tenantLink?.role !== "owner" && tenantLink?.role !== "admin") return void 0;
280
+ return {
281
+ id: `tenant-admin:${input.userId}`,
282
+ tenantId: input.tenantId,
283
+ projectId: project.id,
284
+ userId: input.userId,
285
+ role: "admin",
286
+ status: "active",
287
+ joinedAt: project.createdAt,
288
+ updatedAt: project.updatedAt
289
+ };
290
+ }
291
+ isActiveMembership(membership, input) {
292
+ return this.matchesActiveMembership(
293
+ membership,
294
+ input.tenantId,
295
+ input.projectId,
296
+ input.userId
297
+ );
298
+ }
299
+ accessDenied() {
300
+ throw new ProjectCollaborationError("PROJECT_ACCESS_DENIED", 403, "Project access denied");
301
+ }
302
+ };
303
+
304
+ // src/routes/a2a.ts
305
+ var _a2aKeyStore = null;
306
+ var _a2aAuthService = null;
307
+ var _a2aProjectAccess = null;
308
+ function setA2AProjectAccess(authorizer) {
309
+ _a2aProjectAccess = authorizer;
310
+ }
311
+ function setA2AKeyStore(store) {
312
+ _a2aKeyStore = store;
313
+ }
314
+ function setA2AAuthService(service) {
315
+ _a2aAuthService = service;
316
+ }
317
+ async function refreshStoreKeyMap() {
318
+ if (_a2aAuthService) await _a2aAuthService.refresh();
319
+ }
320
+ function broadcastA2AKeyChanged(tenantId) {
321
+ if (!tenantId) return;
322
+ try {
323
+ const outbox = getStoreLattice("default", "outbox").store;
324
+ void publishA2AKeyChanged(outbox, getReplicaId(), tenantId);
325
+ } catch {
326
+ }
327
+ }
328
+ async function requireTenantSession(request, reply) {
329
+ if (!getUserTenantId(request) || !getUserId(request)) {
330
+ await reply.status(401).send({
331
+ success: false,
332
+ code: "UNAUTHORIZED",
333
+ error: "Unauthorized"
334
+ });
335
+ }
336
+ }
337
+ async function authorizeProjectBinding(request, reply, projectId) {
338
+ const tenantId = getUserTenantId(request);
339
+ const userId = getUserId(request);
340
+ if (!tenantId || !userId) {
341
+ await reply.status(401).send({
342
+ success: false,
343
+ code: "UNAUTHORIZED",
344
+ error: "Unauthorized"
345
+ });
346
+ return false;
347
+ }
348
+ if (!_a2aProjectAccess) {
349
+ await reply.status(500).send({ success: false, error: "Project access not configured" });
350
+ return false;
351
+ }
352
+ try {
353
+ await _a2aProjectAccess.requireExistingProjectAccess({
354
+ tenantId,
355
+ userId,
356
+ projectId,
357
+ capability: "read"
358
+ });
359
+ return true;
360
+ } catch (error) {
361
+ if (error instanceof ProjectCollaborationError) {
362
+ await reply.status(404).send({
363
+ success: false,
364
+ code: "PROJECT_NOT_FOUND",
365
+ error: "Project not found"
366
+ });
367
+ return false;
368
+ }
369
+ throw error;
370
+ }
371
+ }
372
+ async function authorizeKeyMutation(request, reply) {
373
+ if (!_a2aKeyStore) {
374
+ await reply.status(501).send({ error: "Key store not configured" });
375
+ return null;
376
+ }
377
+ const record = await _a2aKeyStore.findById(request.params.id);
378
+ if (!record) {
379
+ await reply.status(404).send({ success: false, error: "A2A API key not found" });
380
+ return null;
381
+ }
382
+ if (record.tenantId !== getUserTenantId(request)) {
383
+ await reply.status(403).send({ success: false, error: "Forbidden" });
384
+ return null;
385
+ }
386
+ return record;
387
+ }
388
+ function getUserTenantId(request) {
389
+ return request.user?.tenantId;
390
+ }
391
+ function getUserId(request) {
392
+ return request.user?.id;
393
+ }
394
+ async function handleApiKeyList(request, reply) {
395
+ if (!_a2aKeyStore) {
396
+ reply.status(501).send({ error: "Key store not configured" });
397
+ return;
398
+ }
399
+ const records = await _a2aKeyStore.list({
400
+ tenantId: getUserTenantId(request),
401
+ limit: request.query.limit ? parseInt(request.query.limit, 10) : void 0,
402
+ offset: request.query.offset ? parseInt(request.query.offset, 10) : void 0
403
+ });
404
+ reply.status(200).send({
405
+ success: true,
406
+ data: {
407
+ records: records.map((r) => ({ ...r, key: r.key.slice(0, 8) + "..." })),
408
+ total: records.length
409
+ }
410
+ });
411
+ }
412
+ async function handleApiKeyCreate(request, reply) {
413
+ if (!_a2aKeyStore || !_a2aAuthService) {
414
+ reply.status(501).send({ error: "Key store not configured" });
415
+ return;
416
+ }
417
+ const body = request.body;
418
+ if (body.projectId === "") {
419
+ reply.status(400).send({ success: false, error: "projectId is required" });
420
+ return;
421
+ }
422
+ const tenantId = getUserTenantId(request);
423
+ if (body.projectId && !await authorizeProjectBinding(request, reply, body.projectId)) {
424
+ return;
425
+ }
426
+ try {
427
+ const record = await _a2aAuthService.createKeyScoped({
428
+ tenantId,
429
+ projectId: body.projectId,
430
+ assistantIds: body.assistantIds,
431
+ grants: body.grants,
432
+ label: body.label
433
+ });
434
+ await refreshStoreKeyMap();
435
+ broadcastA2AKeyChanged(tenantId);
436
+ reply.status(201).send({ success: true, data: record });
437
+ } catch (err) {
438
+ if (err instanceof A2AAuthError) {
439
+ reply.status(err.statusCode).send({ success: false, error: err.message });
440
+ return;
441
+ }
442
+ request.log.error({ err }, "a2a:keys:create");
443
+ reply.status(500).send({ success: false, error: "Failed to create key" });
444
+ }
445
+ }
446
+ async function handleApiKeyUpdate(request, reply) {
447
+ if (!_a2aKeyStore || !_a2aAuthService) {
448
+ reply.status(501).send({ error: "Key store not configured" });
449
+ return;
450
+ }
451
+ const existing = await authorizeKeyMutation(request, reply);
452
+ if (!existing) return;
453
+ const body = request.body;
454
+ if (body.projectId === "") {
455
+ reply.status(400).send({ success: false, error: "projectId must not be empty" });
456
+ return;
457
+ }
458
+ const projectChanged = body.projectId !== void 0 && body.projectId !== existing.projectId;
459
+ const permissionsChanged = body.assistantIds !== void 0 || body.grants !== void 0;
460
+ if (projectChanged || permissionsChanged) {
461
+ const targetProjectId = projectChanged ? body.projectId : existing.projectId;
462
+ if (!await authorizeProjectBinding(request, reply, targetProjectId)) return;
463
+ }
464
+ const tenantId = getUserTenantId(request);
465
+ try {
466
+ const record = await _a2aAuthService.updateKeyScoped(
467
+ request.params.id,
468
+ tenantId,
469
+ {
470
+ label: body.label,
471
+ projectId: body.projectId,
472
+ assistantIds: body.assistantIds,
473
+ grants: body.grants
474
+ }
475
+ );
476
+ await refreshStoreKeyMap();
477
+ broadcastA2AKeyChanged(tenantId);
478
+ reply.status(200).send({ success: true, data: record });
479
+ } catch (err) {
480
+ if (err instanceof A2AAuthError) {
481
+ reply.status(err.statusCode).send({ success: false, error: err.message });
482
+ return;
483
+ }
484
+ request.log.error({ err }, "a2a:keys:update");
485
+ reply.status(500).send({ success: false, error: "Failed to update key" });
486
+ }
487
+ }
488
+ async function handleApiKeyDelete(request, reply) {
489
+ const record = await authorizeKeyMutation(request, reply);
490
+ if (!record) return;
491
+ await _a2aKeyStore.delete(request.params.id);
492
+ await refreshStoreKeyMap();
493
+ broadcastA2AKeyChanged(record.tenantId);
494
+ reply.status(200).send({ success: true });
495
+ }
496
+ async function handleApiKeyDisable(request, reply) {
497
+ const record = await authorizeKeyMutation(request, reply);
498
+ if (!record) return;
499
+ await _a2aKeyStore.disable(request.params.id);
500
+ await refreshStoreKeyMap();
501
+ broadcastA2AKeyChanged(record.tenantId);
502
+ reply.status(200).send({ success: true });
503
+ }
504
+ async function handleApiKeyEnable(request, reply) {
505
+ const record = await authorizeKeyMutation(request, reply);
506
+ if (!record) return;
507
+ await _a2aKeyStore.enable(request.params.id);
508
+ await refreshStoreKeyMap();
509
+ broadcastA2AKeyChanged(record.tenantId);
510
+ reply.status(200).send({ success: true });
511
+ }
512
+ async function handleApiKeyRotate(request, reply) {
513
+ const record = await authorizeKeyMutation(request, reply);
514
+ if (!record) return;
515
+ if (!await authorizeProjectBinding(request, reply, record.projectId)) return;
516
+ const rotated = await _a2aKeyStore.rotate(request.params.id);
517
+ await refreshStoreKeyMap();
518
+ broadcastA2AKeyChanged(record.tenantId);
519
+ reply.status(200).send({ success: true, data: rotated });
520
+ }
521
+ function registerA2ARoutes(app) {
522
+ const options = { preHandler: requireTenantSession };
523
+ for (const base of ["/api/keys", "/api/a2a/keys"]) {
524
+ app.get(
525
+ base,
526
+ options,
527
+ handleApiKeyList
528
+ );
529
+ app.post(
530
+ base,
531
+ options,
532
+ handleApiKeyCreate
533
+ );
534
+ app.delete(
535
+ `${base}/:id`,
536
+ options,
537
+ handleApiKeyDelete
538
+ );
539
+ app.post(
540
+ `${base}/:id/disable`,
541
+ options,
542
+ handleApiKeyDisable
543
+ );
544
+ app.post(
545
+ `${base}/:id/enable`,
546
+ options,
547
+ handleApiKeyEnable
548
+ );
549
+ app.post(
550
+ `${base}/:id/rotate`,
551
+ options,
552
+ handleApiKeyRotate
553
+ );
554
+ app.patch(
555
+ `${base}/:id`,
556
+ options,
557
+ handleApiKeyUpdate
558
+ );
559
+ }
560
+ }
561
+
562
+ export {
563
+ ProjectCollaborationError,
564
+ ProjectAccessService,
565
+ A2A_KEY_CHANGED_TOPIC,
566
+ publishA2AKeyChanged,
567
+ handleA2AKeyChanged,
568
+ setA2AProjectAccess,
569
+ setA2AKeyStore,
570
+ setA2AAuthService,
571
+ refreshStoreKeyMap,
572
+ broadcastA2AKeyChanged,
573
+ registerA2ARoutes
574
+ };
575
+ //# sourceMappingURL=chunk-NYYSBNDG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/routes/a2a.ts","../src/services/a2aKeySync.ts","../src/services/collaboration/ProjectAccessService.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest, FastifyReply } from \"fastify\";\nimport type {\n A2AApiKeyRecord,\n A2AApiKeyStore,\n CreateA2AApiKeyInput,\n OutboxStore,\n} from \"@axiom-lattice/protocols\";\nimport { getStoreLattice, getReplicaId } from \"@axiom-lattice/core\";\nimport { A2AAuthError } from \"../services/a2a/A2AAuthService\";\nimport type { A2AAuthService } from \"../services/a2a/A2AAuthService\";\nimport { publishA2AKeyChanged } from \"../services/a2aKeySync\";\nimport { ProjectCollaborationError } from \"../services/collaboration/ProjectAccessService\";\n\n// ─── Key Store ────────────────────────────────────────────────────────────\n\nlet _a2aKeyStore: A2AApiKeyStore | null = null;\nlet _a2aAuthService: A2AAuthService | null = null;\n\n/** Authorizer contract used by A2A key project binding mutations. */\nexport type ProjectReadAuthorizer = {\n requireExistingProjectAccess(input: {\n tenantId: string;\n userId: string;\n projectId: string;\n capability: \"read\";\n }): Promise<unknown>;\n};\n\nlet _a2aProjectAccess: ProjectReadAuthorizer | null = null;\n\n/** Inject the workspace project authorizer used by key binding mutations. */\nexport function setA2AProjectAccess(authorizer: ProjectReadAuthorizer): void {\n _a2aProjectAccess = authorizer;\n}\n\n/** Inject the A2A key store used by the keys management routes. */\nexport function setA2AKeyStore(store: A2AApiKeyStore): void {\n _a2aKeyStore = store;\n}\n\n/** Inject the A2A auth service used to create keys and refresh the key cache. */\nexport function setA2AAuthService(service: A2AAuthService): void {\n _a2aAuthService = service;\n}\n\n/**\n * Reload cached key maps after a keys CRUD mutation. Delegates to the auth\n * service's key-map cache so authentication reflects the latest keys without\n * hitting the store on every request.\n */\nexport async function refreshStoreKeyMap(): Promise<void> {\n if (_a2aAuthService) await _a2aAuthService.refresh();\n}\n\n/**\n * Best-effort broadcast of an A2A key change so peer replicas reload their key map.\n *\n * @param tenantId Tenant whose keys changed.\n * @remarks Fire-and-forget; never affects the request outcome.\n */\nexport function broadcastA2AKeyChanged(tenantId: string | undefined): void {\n if (!tenantId) return;\n try {\n const outbox = getStoreLattice(\"default\", \"outbox\").store as OutboxStore;\n void publishA2AKeyChanged(outbox, getReplicaId(), tenantId);\n } catch {\n // Best-effort: startup load / the next change corrects a miss.\n }\n}\n\ninterface RequestWithUser extends FastifyRequest {\n user?: { id: string; tenantId?: string };\n}\n\nasync function requireTenantSession(\n request: FastifyRequest,\n reply: FastifyReply,\n): Promise<void> {\n if (!getUserTenantId(request) || !getUserId(request)) {\n await reply.status(401).send({\n success: false,\n code: \"UNAUTHORIZED\",\n error: \"Unauthorized\",\n });\n }\n}\n\nasync function authorizeProjectBinding(\n request: FastifyRequest,\n reply: FastifyReply,\n projectId: string,\n): Promise<boolean> {\n const tenantId = getUserTenantId(request);\n const userId = getUserId(request);\n if (!tenantId || !userId) {\n await reply.status(401).send({\n success: false,\n code: \"UNAUTHORIZED\",\n error: \"Unauthorized\",\n });\n return false;\n }\n if (!_a2aProjectAccess) {\n await reply.status(500).send({ success: false, error: \"Project access not configured\" });\n return false;\n }\n try {\n await _a2aProjectAccess.requireExistingProjectAccess({\n tenantId,\n userId,\n projectId,\n capability: \"read\",\n });\n return true;\n } catch (error) {\n if (error instanceof ProjectCollaborationError) {\n await reply.status(404).send({\n success: false,\n code: \"PROJECT_NOT_FOUND\",\n error: \"Project not found\",\n });\n return false;\n }\n throw error;\n }\n}\n\nasync function authorizeKeyMutation(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<A2AApiKeyRecord | null> {\n if (!_a2aKeyStore) {\n await reply.status(501).send({ error: \"Key store not configured\" });\n return null;\n }\n const record = await _a2aKeyStore.findById(request.params.id);\n if (!record) {\n await reply.status(404).send({ success: false, error: \"A2A API key not found\" });\n return null;\n }\n if (record.tenantId !== getUserTenantId(request)) {\n await reply.status(403).send({ success: false, error: \"Forbidden\" });\n return null;\n }\n return record;\n}\n\nfunction getUserTenantId(request: FastifyRequest): string | undefined {\n return (request as RequestWithUser).user?.tenantId;\n}\n\nfunction getUserId(request: FastifyRequest): string | undefined {\n return (request as RequestWithUser).user?.id;\n}\n\n// ─── API Key Management Handlers ─────────────────────────────────────────\n\nasync function handleApiKeyList(\n request: FastifyRequest<{ Querystring: { tenantId?: string; limit?: string; offset?: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const records = await _a2aKeyStore.list({\n tenantId: getUserTenantId(request),\n limit: request.query.limit ? parseInt(request.query.limit, 10) : undefined,\n offset: request.query.offset ? parseInt(request.query.offset, 10) : undefined,\n });\n\n reply.status(200).send({\n success: true,\n data: {\n records: records.map((r) => ({ ...r, key: r.key.slice(0, 8) + \"...\" })),\n total: records.length,\n },\n });\n}\n\nasync function handleApiKeyCreate(\n request: FastifyRequest<{ Body: Partial<CreateA2AApiKeyInput> }>,\n reply: FastifyReply,\n): Promise<void> {\n if (!_a2aKeyStore || !_a2aAuthService) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const body = request.body as Partial<CreateA2AApiKeyInput>;\n if (body.projectId === \"\") {\n reply.status(400).send({ success: false, error: \"projectId is required\" });\n return;\n }\n const tenantId = getUserTenantId(request)!;\n if (body.projectId && !(await authorizeProjectBinding(request, reply, body.projectId))) {\n return;\n }\n\n try {\n const record = await _a2aAuthService.createKeyScoped({\n tenantId,\n projectId: body.projectId,\n assistantIds: body.assistantIds,\n grants: body.grants,\n label: body.label,\n });\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(tenantId);\n reply.status(201).send({ success: true, data: record });\n } catch (err) {\n if (err instanceof A2AAuthError) {\n reply.status(err.statusCode).send({ success: false, error: err.message });\n return;\n }\n request.log.error({ err }, \"a2a:keys:create\");\n reply.status(500).send({ success: false, error: \"Failed to create key\" });\n }\n}\n\nasync function handleApiKeyUpdate(\n request: FastifyRequest<{ Params: { id: string }; Body: Partial<CreateA2AApiKeyInput> }>,\n reply: FastifyReply,\n): Promise<void> {\n if (!_a2aKeyStore || !_a2aAuthService) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const existing = await authorizeKeyMutation(request, reply);\n if (!existing) return;\n\n const body = request.body as Partial<CreateA2AApiKeyInput>;\n if (body.projectId === \"\") {\n reply.status(400).send({ success: false, error: \"projectId must not be empty\" });\n return;\n }\n const projectChanged = body.projectId !== undefined && body.projectId !== existing.projectId;\n const permissionsChanged = body.assistantIds !== undefined || body.grants !== undefined;\n if (projectChanged || permissionsChanged) {\n const targetProjectId = projectChanged ? body.projectId! : existing.projectId;\n if (!(await authorizeProjectBinding(request, reply, targetProjectId))) return;\n }\n const tenantId = getUserTenantId(request)!;\n try {\n const record = await _a2aAuthService.updateKeyScoped(\n request.params.id,\n tenantId,\n {\n label: body.label,\n projectId: body.projectId,\n assistantIds: body.assistantIds,\n grants: body.grants,\n },\n );\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(tenantId);\n reply.status(200).send({ success: true, data: record });\n } catch (err) {\n if (err instanceof A2AAuthError) {\n reply.status(err.statusCode).send({ success: false, error: err.message });\n return;\n }\n request.log.error({ err }, \"a2a:keys:update\");\n reply.status(500).send({ success: false, error: \"Failed to update key\" });\n }\n}\n\nasync function handleApiKeyDelete(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n await _a2aKeyStore!.delete(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyDisable(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n await _a2aKeyStore!.disable(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyEnable(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n await _a2aKeyStore!.enable(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyRotate(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n if (!(await authorizeProjectBinding(request, reply, record.projectId))) return;\n\n const rotated = await _a2aKeyStore!.rotate(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true, data: rotated });\n}\n\n// ─── Route Registration ───────────────────────────────────────────────────\n\n/**\n * Register the API key management routes.\n *\n * `/api/keys` is the canonical unified surface; `/api/a2a/keys` remains as a\n * backward-compatible alias for existing clients.\n */\nexport function registerA2ARoutes(app: FastifyInstance): void {\n const options = { preHandler: requireTenantSession };\n for (const base of [\"/api/keys\", \"/api/a2a/keys\"]) {\n app.get<{ Querystring: { tenantId?: string; limit?: string; offset?: string } }>(\n base,\n options,\n handleApiKeyList,\n );\n\n app.post<{ Body: Partial<CreateA2AApiKeyInput> }>(\n base,\n options,\n handleApiKeyCreate,\n );\n\n app.delete<{ Params: { id: string } }>(\n `${base}/:id`,\n options,\n handleApiKeyDelete,\n );\n\n app.post<{ Params: { id: string } }>(\n `${base}/:id/disable`,\n options,\n handleApiKeyDisable,\n );\n\n app.post<{ Params: { id: string } }>(\n `${base}/:id/enable`,\n options,\n handleApiKeyEnable,\n );\n\n app.post<{ Params: { id: string } }>(\n `${base}/:id/rotate`,\n options,\n handleApiKeyRotate,\n );\n app.patch<{ Params: { id: string }; Body: Partial<CreateA2AApiKeyInput> }>(\n `${base}/:id`,\n options,\n handleApiKeyUpdate,\n );\n }\n}\n","import type { OutboxStore } from \"@axiom-lattice/protocols\";\n\n/** Outbox topic carrying cross-replica A2A API-key invalidation. */\nexport const A2A_KEY_CHANGED_TOPIC = \"a2a-key-changed\";\n\n/**\n * Best-effort publish of an A2A key change to the durable outbox.\n *\n * @param outbox Shared outbox store.\n * @param owner This replica id (recorded as the event origin).\n * @param tenantId Tenant whose keys changed.\n * @remarks Never throws; peers fall back to startup load / the next change.\n */\nexport async function publishA2AKeyChanged(\n outbox: OutboxStore,\n owner: string,\n tenantId: string,\n): Promise<void> {\n if (!tenantId) return;\n try {\n await outbox.append({\n topic: A2A_KEY_CHANGED_TOPIC,\n payload: { tenantId },\n originReplicaId: owner,\n });\n } catch {\n // Best-effort: startup load / the next change corrects a miss.\n }\n}\n\n/**\n * Reloads this replica's key map after a foreign key change.\n *\n * @param payload Outbox payload, expected to carry a non-empty `tenantId`.\n * @param reload Store-backed reload callback (refreshes the global key map).\n */\nexport async function handleA2AKeyChanged(\n payload: unknown,\n reload: () => Promise<void>,\n): Promise<void> {\n const tenantId = (payload as { tenantId?: unknown } | null)?.tenantId;\n if (typeof tenantId !== \"string\" || tenantId.length === 0) return;\n await reload();\n}\n","import { randomUUID } from \"node:crypto\";\nimport type {\n Project,\n ProjectKind,\n ProjectMembership,\n ProjectMembershipStore,\n ProjectStore,\n UserTenantLinkStore,\n} from \"@axiom-lattice/protocols\";\n\n/** Capabilities enforced by project collaboration services. */\nexport type ProjectCapability = \"read\" | \"send\" | \"manage\";\n\n/** Stable error codes returned by project collaboration services. */\nexport type ProjectCollaborationErrorCode =\n | \"PROJECT_NOT_FOUND\"\n | \"PROJECT_ACCESS_DENIED\"\n | \"PROJECT_MEMBERSHIP_CONFLICT\";\n\n/** Typed service error with stable machine-readable code and HTTP status. */\nexport class ProjectCollaborationError extends Error {\n /** Creates a project collaboration error. */\n constructor(\n public readonly code: ProjectCollaborationErrorCode,\n public readonly statusCode: 403 | 404 | 409,\n message: string,\n ) {\n super(message);\n this.name = \"ProjectCollaborationError\";\n }\n}\n\n/** Trusted project scope and capability checked by the access service. */\nexport interface RequireProjectAccessInput {\n tenantId: string;\n userId: string;\n projectId: string;\n capability: ProjectCapability;\n}\n\n/** Input for bulk project visibility filtering. */\nexport interface ListReadableProjectsInput {\n tenantId: string;\n userId: string;\n workspaceId: string;\n kind?: ProjectKind;\n}\n\n/** Authorized project scope together with the active membership granting access. */\nexport interface ProjectAccessResult {\n project: Project;\n membership: ProjectMembership;\n}\n\n/** Narrow persistence dependencies needed to authorize project collaboration. */\nexport interface ProjectAccessServiceDependencies {\n projectStore: Pick<ProjectStore, \"getProjectById\" | \"getProjectsByWorkspace\">;\n membershipStore: Pick<ProjectMembershipStore, \"list\" | \"findByUser\" | \"create\" | \"createInitialOwner\">;\n userTenantLinkStore: Pick<UserTenantLinkStore, \"getLink\">;\n idFactory?: () => string;\n}\n\nconst allowedRoles = {\n read: new Set([\"owner\", \"admin\", \"member\", \"viewer\"]),\n send: new Set([\"owner\", \"admin\", \"member\"]),\n manage: new Set([\"owner\", \"admin\"]),\n} satisfies Record<ProjectCapability, ReadonlySet<ProjectMembership[\"role\"]>>;\n\nconst membershipConflictNames = new Set([\n \"DuplicateProjectMembershipError\",\n \"ProjectMembershipIdConflictError\",\n]);\n\nfunction isProjectMembershipConflict(error: unknown): boolean {\n return error instanceof Error && membershipConflictNames.has(error.name);\n}\n\n/** Authorizes project collaboration and initializes eligible legacy projects. */\nexport class ProjectAccessService {\n private readonly idFactory: () => string;\n\n /** Creates a project access service from explicitly injected stores. */\n constructor(private readonly deps: ProjectAccessServiceDependencies) {\n this.idFactory = deps.idFactory ?? randomUUID;\n }\n\n /** Returns the project and active membership when the user has the requested capability. */\n async requireProjectAccess(input: RequireProjectAccessInput): Promise<ProjectAccessResult> {\n const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);\n if (!project) {\n throw new ProjectCollaborationError(\"PROJECT_NOT_FOUND\", 404, \"Project not found\");\n }\n\n if (project.kind === \"public\") {\n return this.requirePublicProjectAccess(project, input);\n }\n\n const membership = await this.deps.membershipStore.findByUser(\n input.tenantId,\n input.projectId,\n input.userId,\n );\n\n if (project.kind === \"personal\") {\n if (!this.isActiveMembership(membership, input)) this.accessDenied();\n this.requireCapability(membership, input.capability);\n return { project, membership };\n }\n\n if (this.isActiveMembership(membership, input) && this.hasCapability(membership, input.capability)) {\n return { project, membership };\n }\n\n const tenantAdmin = await this.tenantAdminMembership(project, input);\n if (this.isActiveMembership(membership, input)) {\n if (tenantAdmin) return { project, membership: tenantAdmin };\n this.requireCapability(membership, input.capability);\n }\n\n const memberships = await this.deps.membershipStore.list(input.tenantId, input.projectId);\n if (memberships.length !== 0) {\n if (tenantAdmin) return { project, membership: tenantAdmin };\n this.accessDenied();\n }\n // A tenant admin may claim a legacy project that has no memberships at all.\n if (!tenantAdmin) this.accessDenied();\n\n let claim: Awaited<ReturnType<ProjectMembershipStore[\"createInitialOwner\"]>>;\n try {\n claim = await this.deps.membershipStore.createInitialOwner({\n id: this.idFactory(),\n tenantId: input.tenantId,\n projectId: input.projectId,\n userId: input.userId,\n });\n } catch (error) {\n if (isProjectMembershipConflict(error)) {\n throw new ProjectCollaborationError(\n \"PROJECT_MEMBERSHIP_CONFLICT\",\n 409,\n \"Project membership could not be initialized\",\n );\n }\n throw error;\n }\n if (claim.kind === \"created\" || claim.kind === \"existing\") {\n return { project, membership: claim.membership };\n }\n\n const racedMembership = await this.deps.membershipStore.findByUser(\n input.tenantId,\n input.projectId,\n input.userId,\n );\n if (racedMembership?.status !== \"active\") this.accessDenied();\n this.requireCapability(racedMembership, input.capability);\n return { project, membership: racedMembership };\n }\n\n /** Authorizes an already-existing membership, or any tenant member for public rooms; never mutates persistence. */\n async requireExistingProjectAccess(input: RequireProjectAccessInput): Promise<ProjectAccessResult> {\n const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);\n if (!project) throw new ProjectCollaborationError(\"PROJECT_NOT_FOUND\", 404, \"Project not found\");\n if (project.kind === \"public\") {\n return this.requireExistingPublicProjectAccess(project, input);\n }\n\n const membership = await this.deps.membershipStore.findByUser(\n input.tenantId,\n input.projectId,\n input.userId,\n );\n const activeMembership = this.isActiveMembership(membership, input) ? membership : undefined;\n\n if (project.kind === \"personal\") {\n if (!activeMembership) this.accessDenied();\n this.requireCapability(activeMembership, input.capability);\n return { project, membership: activeMembership };\n }\n\n if (activeMembership && this.hasCapability(activeMembership, input.capability)) {\n return { project, membership: activeMembership };\n }\n\n const tenantAdmin = await this.tenantAdminMembership(project, input);\n if (tenantAdmin) return { project, membership: tenantAdmin };\n\n if (activeMembership) this.requireCapability(activeMembership, input.capability);\n this.accessDenied();\n }\n\n /**\n * Authorizes roster visibility for any active participant (owner/admin/member/viewer).\n * Mutating the roster requires `manage` separately; reading it never does, because every\n * room participant needs the member list to render the room and derive their own capabilities.\n */\n async requireRosterAccess(\n input: Omit<RequireProjectAccessInput, \"capability\">,\n ): Promise<ProjectAccessResult> {\n const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);\n if (!project) throw new ProjectCollaborationError(\"PROJECT_NOT_FOUND\", 404, \"Project not found\");\n return this.requireProjectAccess({\n ...input,\n capability: \"read\",\n });\n }\n\n /** Returns projects in a workspace that the user may read. */\n async listReadableProjects(input: ListReadableProjectsInput): Promise<Project[]> {\n const [tenantLink, projects] = await Promise.all([\n this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId),\n this.deps.projectStore.getProjectsByWorkspace(input.tenantId, input.workspaceId),\n ]);\n const filtered = input.kind\n ? projects.filter((project) => (project.kind ?? \"business\") === input.kind)\n : projects;\n const isTenantAdmin = tenantLink?.role === \"owner\" || tenantLink?.role === \"admin\";\n\n const visible: Project[] = [];\n for (const project of filtered) {\n if (project.kind === \"public\") {\n if (tenantLink || await this.hasActiveMembership(input.tenantId, project.id, input.userId)) {\n visible.push(project);\n }\n continue;\n }\n if (project.kind === \"personal\") {\n if (await this.hasActiveMembership(input.tenantId, project.id, input.userId)) {\n visible.push(project);\n }\n continue;\n }\n if (isTenantAdmin || await this.hasActiveMembership(input.tenantId, project.id, input.userId)) {\n visible.push(project);\n }\n }\n return visible;\n }\n\n private async hasActiveMembership(\n tenantId: string,\n projectId: string,\n userId: string,\n ): Promise<boolean> {\n const membership = await this.deps.membershipStore.findByUser(tenantId, projectId, userId);\n return this.matchesActiveMembership(membership, tenantId, projectId, userId);\n }\n\n private matchesActiveMembership(\n membership: ProjectMembership | null,\n tenantId: string,\n projectId: string,\n userId: string,\n ): membership is ProjectMembership {\n return membership != null\n && membership.tenantId === tenantId\n && membership.projectId === projectId\n && membership.userId === userId\n && membership.status === \"active\";\n }\n\n private async requirePublicProjectAccess(\n project: Project,\n input: RequireProjectAccessInput,\n ): Promise<ProjectAccessResult> {\n const membership = await this.deps.membershipStore.findByUser(\n input.tenantId, input.projectId, input.userId,\n );\n if (membership?.status === \"active\") {\n if (this.hasCapability(membership, input.capability)) return { project, membership };\n const tenantAdmin = await this.tenantAdminMembership(project, input);\n if (tenantAdmin) return { project, membership: tenantAdmin };\n this.requireCapability(membership, input.capability);\n return { project, membership };\n }\n const materialized = await this.materializePublicMembership(input);\n this.requireCapability(materialized, input.capability);\n return { project, membership: materialized };\n }\n\n private async requireExistingPublicProjectAccess(\n project: Project,\n input: RequireProjectAccessInput,\n ): Promise<ProjectAccessResult> {\n const membership = await this.deps.membershipStore.findByUser(\n input.tenantId, input.projectId, input.userId,\n );\n if (membership?.status === \"active\") {\n if (this.hasCapability(membership, input.capability)) return { project, membership };\n const tenantAdmin = await this.tenantAdminMembership(project, input);\n if (tenantAdmin) return { project, membership: tenantAdmin };\n this.requireCapability(membership, input.capability);\n return { project, membership };\n }\n const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);\n if (!tenantLink) this.accessDenied();\n const role: ProjectMembership[\"role\"] =\n tenantLink.role === \"owner\" || tenantLink.role === \"admin\" ? \"admin\" : \"member\";\n // Read-only authorization artifact — never persisted; callers must not\n // treat `id` as a store key or `updatedAt` as a CAS token.\n const synthetic: ProjectMembership = {\n id: `public:${input.userId}`,\n tenantId: input.tenantId,\n projectId: input.projectId,\n userId: input.userId,\n role,\n status: \"active\",\n joinedAt: project.createdAt,\n updatedAt: project.updatedAt,\n };\n this.requireCapability(synthetic, input.capability);\n return { project, membership: synthetic };\n }\n\n private async materializePublicMembership(\n input: RequireProjectAccessInput,\n ): Promise<ProjectMembership> {\n const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);\n if (!tenantLink) this.accessDenied();\n const existing = await this.deps.membershipStore.findByUser(\n input.tenantId, input.projectId, input.userId,\n );\n if (existing) this.accessDenied();\n const role: ProjectMembership[\"role\"] =\n tenantLink.role === \"owner\" || tenantLink.role === \"admin\" ? \"admin\" : \"member\";\n try {\n return await this.deps.membershipStore.create({\n id: this.idFactory(),\n tenantId: input.tenantId,\n projectId: input.projectId,\n userId: input.userId,\n role,\n status: \"active\",\n });\n } catch (error) {\n if (isProjectMembershipConflict(error)) {\n const raced = await this.deps.membershipStore.findByUser(\n input.tenantId, input.projectId, input.userId,\n );\n if (raced?.status === \"active\") return raced;\n }\n throw error;\n }\n }\n\n private hasCapability(membership: ProjectMembership, capability: ProjectCapability): boolean {\n return (allowedRoles[capability] as ReadonlySet<ProjectMembership[\"role\"]>).has(membership.role);\n }\n\n private requireCapability(membership: ProjectMembership, capability: ProjectCapability): void {\n if (!this.hasCapability(membership, capability)) this.accessDenied();\n }\n\n private async tenantAdminMembership(\n project: Project,\n input: RequireProjectAccessInput,\n ): Promise<ProjectMembership | undefined> {\n if (project.kind === \"personal\") return undefined;\n const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);\n if (tenantLink?.role !== \"owner\" && tenantLink?.role !== \"admin\") return undefined;\n // Authorization artifact — never persisted; callers must not use `id` or\n // `updatedAt` as a store key or CAS token.\n return {\n id: `tenant-admin:${input.userId}`,\n tenantId: input.tenantId,\n projectId: project.id,\n userId: input.userId,\n role: \"admin\",\n status: \"active\",\n joinedAt: project.createdAt,\n updatedAt: project.updatedAt,\n };\n }\n\n private isActiveMembership(\n membership: ProjectMembership | null,\n input: RequireProjectAccessInput,\n ): membership is ProjectMembership {\n return this.matchesActiveMembership(\n membership,\n input.tenantId,\n input.projectId,\n input.userId,\n );\n }\n\n private accessDenied(): never {\n throw new ProjectCollaborationError(\"PROJECT_ACCESS_DENIED\", 403, \"Project access denied\");\n }\n}\n"],"mappings":"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","names":[]}