@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
package/dist/index.mjs CHANGED
@@ -1,12 +1,19 @@
1
1
  import {
2
- invalidateAgentReflectionCache,
3
- invalidatePluginReflectionCache
4
- } from "./chunk-GL7RV5L3.mjs";
2
+ invalidateAgentReflectionCache
3
+ } from "./chunk-YR32MWEJ.mjs";
5
4
  import {
6
5
  getContentTypeFromFilename,
7
6
  getFilenameFromPath,
8
7
  sendBufferWithRange
9
8
  } from "./chunk-EMYRCJOI.mjs";
9
+ import {
10
+ createRecoveryCallerDeps
11
+ } from "./chunk-NOXGKYUV.mjs";
12
+ import {
13
+ Logger,
14
+ getQrCode,
15
+ getQrCodeStatus
16
+ } from "./chunk-6CUQGDJI.mjs";
10
17
  import {
11
18
  PathOutsideRootError,
12
19
  ProjectFileValidationError,
@@ -28,9 +35,12 @@ import {
28
35
  wakeRunTaskQueue
29
36
  } from "./chunk-2SR4F3ZD.mjs";
30
37
  import {
31
- PluginToolScopeError,
32
- invokePluginTool
33
- } from "./chunk-XUWD4VKY.mjs";
38
+ claimDedupKey,
39
+ createRateLimitService,
40
+ getRateLimitService,
41
+ incrementRateCounter,
42
+ setRateLimitService
43
+ } from "./chunk-A7YNAU5L.mjs";
34
44
  import {
35
45
  buildPreviewTokenScript
36
46
  } from "./chunk-7CG57LEF.mjs";
@@ -40,14 +50,25 @@ import {
40
50
  } from "./chunk-PWHUWFXL.mjs";
41
51
  import {
42
52
  A2A_KEY_CHANGED_TOPIC,
53
+ ProjectAccessService,
54
+ ProjectCollaborationError,
43
55
  handleA2AKeyChanged,
44
56
  publishA2AKeyChanged,
45
- refreshStoreKeyMap
46
- } from "./chunk-HYYLYIMH.mjs";
57
+ refreshStoreKeyMap,
58
+ setA2AProjectAccess
59
+ } from "./chunk-NYYSBNDG.mjs";
47
60
  import {
48
61
  A2AAuthService,
49
62
  parseEnvKeys
50
- } from "./chunk-GPHOYR3X.mjs";
63
+ } from "./chunk-LCM4OQTI.mjs";
64
+ import {
65
+ getMcpUiResourceResolver,
66
+ listTenantPlugins,
67
+ registerMcpAppRoutes,
68
+ resolveTenantPluginByType
69
+ } from "./chunk-T2HIWZGP.mjs";
70
+ import "./chunk-XUWD4VKY.mjs";
71
+ import "./chunk-MFWTGNQR.mjs";
51
72
  import {
52
73
  createOrphanRecoveryRuntime,
53
74
  createTaskRunLiveness,
@@ -59,14 +80,6 @@ import {
59
80
  resolveTopCaller,
60
81
  runOrphanRecovery
61
82
  } from "./chunk-NZXKECNN.mjs";
62
- import {
63
- createRecoveryCallerDeps
64
- } from "./chunk-NOXGKYUV.mjs";
65
- import {
66
- Logger,
67
- getQrCode,
68
- getQrCodeStatus
69
- } from "./chunk-6CUQGDJI.mjs";
70
83
  import "./chunk-NFEGQTCC.mjs";
71
84
 
72
85
  // src/index.ts
@@ -80,7 +93,7 @@ import staticPlugin from "@fastify/static";
80
93
  import path4 from "path";
81
94
 
82
95
  // src/routes/index.ts
83
- import { randomUUID as randomUUID32 } from "crypto";
96
+ import { randomUUID as randomUUID31 } from "crypto";
84
97
  import {
85
98
  descriptorDataValue as descriptorDataValue3,
86
99
  snapshotExactRecord as snapshotExactRecord14,
@@ -10003,14 +10016,6 @@ async function importApply(request, reply) {
10003
10016
 
10004
10017
  // src/controllers/middleware-types.ts
10005
10018
  import { serializePluginMeta } from "@axiom-lattice/core";
10006
-
10007
- // src/services/plugins/tenantPluginResolver.ts
10008
- import { listTenantPlugins } from "@axiom-lattice/core";
10009
- async function resolveTenantPluginByType(tenantId, type) {
10010
- return (await listTenantPlugins(tenantId)).find((p) => p.meta.type === type);
10011
- }
10012
-
10013
- // src/controllers/middleware-types.ts
10014
10019
  async function collectMiddlewareMetas(tenantId) {
10015
10020
  return (await listTenantPlugins(tenantId)).map(serializePluginMeta);
10016
10021
  }
@@ -10998,8 +11003,9 @@ function createProjectCollaborationInitializer(deps) {
10998
11003
  };
10999
11004
  }
11000
11005
  var WorkspaceController = class {
11001
- constructor(projectCollaboration) {
11006
+ constructor(projectCollaboration, projectAccess) {
11002
11007
  this.projectCollaboration = projectCollaboration;
11008
+ this.projectAccess = projectAccess;
11003
11009
  this.workspaceStore = getStoreLattice16("default", "workspace").store;
11004
11010
  this.projectStore = getStoreLattice16("default", "project").store;
11005
11011
  const roomStore = getStoreLattice16("default", "projectRoom").store;
@@ -11041,6 +11047,48 @@ var WorkspaceController = class {
11041
11047
  }
11042
11048
  return request.headers["x-tenant-id"] || "default";
11043
11049
  }
11050
+ requireActor(request, reply) {
11051
+ const user = request.user;
11052
+ if (!user?.id || !user.tenantId) {
11053
+ void reply.status(401).send({
11054
+ success: false,
11055
+ code: "UNAUTHORIZED",
11056
+ error: "Unauthorized"
11057
+ });
11058
+ return null;
11059
+ }
11060
+ return { tenantId: user.tenantId, userId: user.id };
11061
+ }
11062
+ accessService() {
11063
+ if (!this.projectAccess) {
11064
+ throw new Error("Project access service is not configured");
11065
+ }
11066
+ return this.projectAccess;
11067
+ }
11068
+ async requireReadableProject(actor3, workspaceId, projectId, capability2) {
11069
+ try {
11070
+ const result = await this.accessService().requireExistingProjectAccess({
11071
+ tenantId: actor3.tenantId,
11072
+ userId: actor3.userId,
11073
+ projectId,
11074
+ capability: capability2
11075
+ });
11076
+ if (result.project.workspaceId !== workspaceId) return null;
11077
+ return result.project;
11078
+ } catch (error) {
11079
+ if (error instanceof ProjectCollaborationError && (error.code === "PROJECT_ACCESS_DENIED" || error.code === "PROJECT_NOT_FOUND")) {
11080
+ return null;
11081
+ }
11082
+ throw error;
11083
+ }
11084
+ }
11085
+ projectNotFound(reply) {
11086
+ return reply.status(404).send({
11087
+ success: false,
11088
+ code: "PROJECT_NOT_FOUND",
11089
+ error: "Project not found"
11090
+ });
11091
+ }
11044
11092
  // ==================== Workspace CRUD ====================
11045
11093
  async listWorkspaces(request, reply) {
11046
11094
  const tenantId = this.getTenantId(request);
@@ -11098,14 +11146,18 @@ var WorkspaceController = class {
11098
11146
  }
11099
11147
  // ==================== Project CRUD ====================
11100
11148
  async listProjects(request, reply) {
11101
- const tenantId = this.getTenantId(request);
11149
+ const actor3 = this.requireActor(request, reply);
11150
+ if (!actor3) return;
11102
11151
  const { workspaceId } = request.params;
11103
11152
  const kindFilter = request.query.kind;
11104
- await this.ensureTrainingProject(tenantId, workspaceId);
11105
- await this.publicRoom.ensure(tenantId, workspaceId);
11106
- const projects = kindFilter ? await this.projectStore.getProjectsByWorkspace(tenantId, workspaceId, {
11153
+ await this.ensureTrainingProject(actor3.tenantId, workspaceId);
11154
+ await this.publicRoom.ensure(actor3.tenantId, workspaceId);
11155
+ const projects = await this.accessService().listReadableProjects({
11156
+ tenantId: actor3.tenantId,
11157
+ userId: actor3.userId,
11158
+ workspaceId,
11107
11159
  kind: kindFilter
11108
- }) : await this.projectStore.getProjectsByWorkspace(tenantId, workspaceId);
11160
+ });
11109
11161
  return { success: true, data: projects };
11110
11162
  }
11111
11163
  /**
@@ -11130,13 +11182,9 @@ var WorkspaceController = class {
11130
11182
  });
11131
11183
  }
11132
11184
  async createProject(request, reply) {
11133
- const user = request.user;
11134
- const requestedTenantId = request.query?.tenantId ?? request.headers["x-tenant-id"];
11135
- const selectedTenantId = requestedTenantId === void 0 ? void 0 : String(requestedTenantId);
11136
- if (user?.tenantId && selectedTenantId && user.tenantId !== selectedTenantId) {
11137
- return reply.status(403).send({ success: false, code: "TENANT_ACCESS_DENIED", error: "Tenant access denied" });
11138
- }
11139
- const tenantId = user?.tenantId ?? selectedTenantId ?? "default";
11185
+ const actor3 = this.requireActor(request, reply);
11186
+ if (!actor3) return;
11187
+ const tenantId = actor3.tenantId;
11140
11188
  const { workspaceId } = request.params;
11141
11189
  const data = request.body;
11142
11190
  if (writesCapabilityBundleIds(data.config)) {
@@ -11146,9 +11194,9 @@ var WorkspaceController = class {
11146
11194
  message: CAPABILITY_BUNDLE_CONFIG_MESSAGE
11147
11195
  });
11148
11196
  }
11149
- if (user?.id && this.projectCollaboration) {
11197
+ if (this.projectCollaboration) {
11150
11198
  try {
11151
- const authorized = await this.projectCollaboration.authorizeCreator({ tenantId, userId: user.id });
11199
+ const authorized = await this.projectCollaboration.authorizeCreator({ tenantId, userId: actor3.userId });
11152
11200
  if (!authorized) {
11153
11201
  return reply.status(403).send({ success: false, code: "TENANT_ACCESS_DENIED", error: "Tenant access denied" });
11154
11202
  }
@@ -11159,7 +11207,7 @@ var WorkspaceController = class {
11159
11207
  console.error({
11160
11208
  event: "project:collaboration-preflight-failed",
11161
11209
  tenantId,
11162
- userId: user.id,
11210
+ userId: actor3.userId,
11163
11211
  errorName: error instanceof Error ? error.name : "UnknownError"
11164
11212
  });
11165
11213
  return reply.status(500).send({
@@ -11177,42 +11225,72 @@ var WorkspaceController = class {
11177
11225
  id3,
11178
11226
  data
11179
11227
  );
11180
- if (user?.id && this.projectCollaboration) {
11228
+ if (this.projectCollaboration) {
11181
11229
  try {
11182
- await this.projectCollaboration.initialize({ tenantId, userId: user.id, project });
11230
+ await this.projectCollaboration.initialize({ tenantId, userId: actor3.userId, project });
11183
11231
  } catch (error) {
11184
- const phase = error instanceof ProjectCollaborationInitializationError ? error.phase : "owner";
11232
+ const phase = error instanceof ProjectCollaborationInitializationError ? error.phase : void 0;
11233
+ let rolledBack = false;
11234
+ if (phase === "owner") {
11235
+ try {
11236
+ rolledBack = await this.projectStore.deleteProject(tenantId, project.id);
11237
+ if (!rolledBack) {
11238
+ console.error({
11239
+ event: "project:owner-initialization-rollback-missed",
11240
+ tenantId,
11241
+ projectId: project.id
11242
+ });
11243
+ }
11244
+ } catch (cleanupError) {
11245
+ console.error({
11246
+ event: "project:owner-initialization-rollback-failed",
11247
+ tenantId,
11248
+ projectId: project.id,
11249
+ errorName: cleanupError instanceof Error ? cleanupError.name : "UnknownError"
11250
+ });
11251
+ }
11252
+ }
11185
11253
  console.error({
11186
11254
  event: "project:collaboration-initialization-failed",
11187
11255
  phase,
11188
11256
  tenantId,
11189
11257
  projectId: project.id,
11190
- userId: user.id,
11258
+ userId: actor3.userId,
11191
11259
  errorName: error instanceof Error ? error.name : "UnknownError"
11192
11260
  });
11193
11261
  return reply.status(500).send({
11194
11262
  success: false,
11195
11263
  error: "Internal server error",
11196
- code: phase === "owner" ? "PROJECT_OWNER_INITIALIZATION_FAILED" : "PROJECT_ROOM_INITIALIZATION_FAILED",
11264
+ code: phase === "owner" ? "PROJECT_OWNER_INITIALIZATION_FAILED" : phase === "room" ? "PROJECT_ROOM_INITIALIZATION_FAILED" : "PROJECT_COLLABORATION_INITIALIZATION_FAILED",
11197
11265
  projectId: project.id,
11198
- retryable: phase === "room"
11266
+ retryable: phase === "room" || rolledBack
11199
11267
  });
11200
11268
  }
11201
11269
  }
11202
11270
  return reply.status(201).send({ success: true, data: project });
11203
11271
  }
11204
11272
  async getProject(request, reply) {
11205
- const tenantId = this.getTenantId(request);
11206
- const { projectId } = request.params;
11207
- const project = await this.projectStore.getProjectById(tenantId, projectId);
11208
- if (!project) {
11209
- return reply.status(404).send({ success: false, error: "Project not found" });
11210
- }
11273
+ const actor3 = this.requireActor(request, reply);
11274
+ if (!actor3) return;
11275
+ const project = await this.requireReadableProject(
11276
+ actor3,
11277
+ request.params.workspaceId,
11278
+ request.params.projectId,
11279
+ "read"
11280
+ );
11281
+ if (!project) return this.projectNotFound(reply);
11211
11282
  return { success: true, data: project };
11212
11283
  }
11213
11284
  async updateProject(request, reply) {
11214
- const tenantId = this.getTenantId(request);
11215
- const { projectId } = request.params;
11285
+ const actor3 = this.requireActor(request, reply);
11286
+ if (!actor3) return;
11287
+ const project = await this.requireReadableProject(
11288
+ actor3,
11289
+ request.params.workspaceId,
11290
+ request.params.projectId,
11291
+ "manage"
11292
+ );
11293
+ if (!project) return this.projectNotFound(reply);
11216
11294
  const updates = request.body;
11217
11295
  if (writesCapabilityBundleIds(updates.config)) {
11218
11296
  return reply.status(400).send({
@@ -11221,36 +11299,35 @@ var WorkspaceController = class {
11221
11299
  message: CAPABILITY_BUNDLE_CONFIG_MESSAGE
11222
11300
  });
11223
11301
  }
11224
- if (updates.kind !== void 0) {
11225
- const existingProject = await this.projectStore.getProjectById(tenantId, projectId);
11226
- if (existingProject && existingProject.id === workspacePublicRoomProjectId(tenantId, existingProject.workspaceId)) {
11227
- return reply.status(409).send({
11228
- success: false,
11229
- error: "The workspace public room classification cannot be changed"
11230
- });
11231
- }
11302
+ if (project.id === workspacePublicRoomProjectId(actor3.tenantId, project.workspaceId) && (updates.kind !== void 0 || updates.name !== void 0)) {
11303
+ return reply.status(409).send({
11304
+ success: false,
11305
+ error: "The workspace public room cannot be renamed or reclassified"
11306
+ });
11232
11307
  }
11233
- const project = await this.projectStore.updateProject(
11234
- tenantId,
11235
- projectId,
11308
+ const updated = await this.projectStore.updateProject(
11309
+ actor3.tenantId,
11310
+ project.id,
11236
11311
  updates
11237
11312
  );
11238
- if (!project) {
11239
- return reply.status(404).send({ success: false, error: "Project not found" });
11240
- }
11241
- return { success: true, data: project };
11313
+ if (!updated) return this.projectNotFound(reply);
11314
+ return { success: true, data: updated };
11242
11315
  }
11243
11316
  async deleteProject(request, reply) {
11244
- const tenantId = this.getTenantId(request);
11245
- const { workspaceId, projectId } = request.params;
11246
- const project = await this.projectStore.getProjectById(tenantId, projectId);
11247
- if (!project) {
11248
- return reply.status(404).send({ success: false, error: "Project not found" });
11249
- }
11317
+ const actor3 = this.requireActor(request, reply);
11318
+ if (!actor3) return;
11319
+ const project = await this.requireReadableProject(
11320
+ actor3,
11321
+ request.params.workspaceId,
11322
+ request.params.projectId,
11323
+ "manage"
11324
+ );
11325
+ if (!project) return this.projectNotFound(reply);
11326
+ const { workspaceId } = request.params;
11250
11327
  if (project.kind === "training") {
11251
11328
  const training = await this.projectStore.getProjectsByWorkspace(
11252
- tenantId,
11253
- workspaceId,
11329
+ actor3.tenantId,
11330
+ project.workspaceId,
11254
11331
  { kind: "training" }
11255
11332
  );
11256
11333
  if (training.length <= 1) {
@@ -11260,16 +11337,14 @@ var WorkspaceController = class {
11260
11337
  });
11261
11338
  }
11262
11339
  }
11263
- if (project.id === workspacePublicRoomProjectId(tenantId, project.workspaceId ?? workspaceId)) {
11340
+ if (project.id === workspacePublicRoomProjectId(actor3.tenantId, project.workspaceId ?? workspaceId)) {
11264
11341
  return reply.status(409).send({
11265
11342
  success: false,
11266
11343
  error: "The workspace public room cannot be deleted"
11267
11344
  });
11268
11345
  }
11269
- const deleted = await this.projectStore.deleteProject(tenantId, projectId);
11270
- if (!deleted) {
11271
- return reply.status(404).send({ success: false, error: "Project not found" });
11272
- }
11346
+ const deleted = await this.projectStore.deleteProject(actor3.tenantId, project.id);
11347
+ if (!deleted) return this.projectNotFound(reply);
11273
11348
  return { success: true };
11274
11349
  }
11275
11350
  // ==================== File Operations ====================
@@ -11907,8 +11982,8 @@ var WorkspaceController = class {
11907
11982
  }
11908
11983
  }
11909
11984
  };
11910
- function registerWorkspaceRoutes(app2, projectCollaboration) {
11911
- const controller = new WorkspaceController(projectCollaboration);
11985
+ function registerWorkspaceRoutes(app2, projectCollaboration, projectAccess) {
11986
+ const controller = new WorkspaceController(projectCollaboration, projectAccess);
11912
11987
  app2.get("/api/workspaces", controller.listWorkspaces.bind(controller));
11913
11988
  app2.post("/api/workspaces", controller.createWorkspace.bind(controller));
11914
11989
  app2.get(
@@ -11975,250 +12050,12 @@ function registerWorkspaceRoutes(app2, projectCollaboration) {
11975
12050
  return controller;
11976
12051
  }
11977
12052
 
11978
- // src/services/collaboration/ProjectAccessService.ts
11979
- import { randomUUID as randomUUID14 } from "crypto";
11980
- var ProjectCollaborationError = class extends Error {
11981
- /** Creates a project collaboration error. */
11982
- constructor(code, statusCode, message) {
11983
- super(message);
11984
- this.code = code;
11985
- this.statusCode = statusCode;
11986
- this.name = "ProjectCollaborationError";
11987
- }
11988
- };
11989
- var allowedRoles = {
11990
- read: /* @__PURE__ */ new Set(["owner", "admin", "member", "viewer"]),
11991
- send: /* @__PURE__ */ new Set(["owner", "admin", "member"]),
11992
- manage: /* @__PURE__ */ new Set(["owner", "admin"])
11993
- };
11994
- var membershipConflictNames = /* @__PURE__ */ new Set([
11995
- "DuplicateProjectMembershipError",
11996
- "ProjectMembershipIdConflictError"
11997
- ]);
11998
- function isProjectMembershipConflict(error) {
11999
- return error instanceof Error && membershipConflictNames.has(error.name);
12000
- }
12001
- var ProjectAccessService = class {
12002
- /** Creates a project access service from explicitly injected stores. */
12003
- constructor(deps) {
12004
- this.deps = deps;
12005
- this.idFactory = deps.idFactory ?? randomUUID14;
12006
- }
12007
- /** Returns the project and active membership when the user has the requested capability. */
12008
- async requireProjectAccess(input) {
12009
- const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
12010
- if (!project) {
12011
- throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
12012
- }
12013
- if (project.kind === "public") {
12014
- return this.requirePublicProjectAccess(project, input);
12015
- }
12016
- const membership = await this.deps.membershipStore.findByUser(
12017
- input.tenantId,
12018
- input.projectId,
12019
- input.userId
12020
- );
12021
- if (project.kind === "personal") {
12022
- if (!this.isActiveMembership(membership, input)) this.accessDenied();
12023
- this.requireCapability(membership, input.capability);
12024
- return { project, membership };
12025
- }
12026
- if (this.isActiveMembership(membership, input) && this.hasCapability(membership, input.capability)) {
12027
- return { project, membership };
12028
- }
12029
- const tenantAdmin = await this.tenantAdminMembership(project, input);
12030
- if (this.isActiveMembership(membership, input)) {
12031
- if (tenantAdmin) return { project, membership: tenantAdmin };
12032
- this.requireCapability(membership, input.capability);
12033
- }
12034
- const memberships = await this.deps.membershipStore.list(input.tenantId, input.projectId);
12035
- if (memberships.length !== 0) {
12036
- if (tenantAdmin) return { project, membership: tenantAdmin };
12037
- this.accessDenied();
12038
- }
12039
- if (!tenantAdmin) this.accessDenied();
12040
- let claim;
12041
- try {
12042
- claim = await this.deps.membershipStore.createInitialOwner({
12043
- id: this.idFactory(),
12044
- tenantId: input.tenantId,
12045
- projectId: input.projectId,
12046
- userId: input.userId
12047
- });
12048
- } catch (error) {
12049
- if (isProjectMembershipConflict(error)) {
12050
- throw new ProjectCollaborationError(
12051
- "PROJECT_MEMBERSHIP_CONFLICT",
12052
- 409,
12053
- "Project membership could not be initialized"
12054
- );
12055
- }
12056
- throw error;
12057
- }
12058
- if (claim.kind === "created" || claim.kind === "existing") {
12059
- return { project, membership: claim.membership };
12060
- }
12061
- const racedMembership = await this.deps.membershipStore.findByUser(
12062
- input.tenantId,
12063
- input.projectId,
12064
- input.userId
12065
- );
12066
- if (racedMembership?.status !== "active") this.accessDenied();
12067
- this.requireCapability(racedMembership, input.capability);
12068
- return { project, membership: racedMembership };
12069
- }
12070
- /** Authorizes an already-existing membership, or any tenant member for public rooms; never mutates persistence. */
12071
- async requireExistingProjectAccess(input) {
12072
- const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
12073
- if (!project) throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
12074
- if (project.kind === "public") {
12075
- return this.requireExistingPublicProjectAccess(project, input);
12076
- }
12077
- const membership = await this.deps.membershipStore.findByUser(
12078
- input.tenantId,
12079
- input.projectId,
12080
- input.userId
12081
- );
12082
- const activeMembership = this.isActiveMembership(membership, input) ? membership : void 0;
12083
- if (project.kind === "personal") {
12084
- if (!activeMembership) this.accessDenied();
12085
- this.requireCapability(activeMembership, input.capability);
12086
- return { project, membership: activeMembership };
12087
- }
12088
- if (activeMembership && this.hasCapability(activeMembership, input.capability)) {
12089
- return { project, membership: activeMembership };
12090
- }
12091
- const tenantAdmin = await this.tenantAdminMembership(project, input);
12092
- if (tenantAdmin) return { project, membership: tenantAdmin };
12093
- if (activeMembership) this.requireCapability(activeMembership, input.capability);
12094
- this.accessDenied();
12095
- }
12096
- /**
12097
- * Authorizes roster visibility for any active participant (owner/admin/member/viewer).
12098
- * Mutating the roster requires `manage` separately; reading it never does, because every
12099
- * room participant needs the member list to render the room and derive their own capabilities.
12100
- */
12101
- async requireRosterAccess(input) {
12102
- const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
12103
- if (!project) throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
12104
- return this.requireProjectAccess({
12105
- ...input,
12106
- capability: "read"
12107
- });
12108
- }
12109
- async requirePublicProjectAccess(project, input) {
12110
- const membership = await this.deps.membershipStore.findByUser(
12111
- input.tenantId,
12112
- input.projectId,
12113
- input.userId
12114
- );
12115
- if (membership?.status === "active") {
12116
- if (this.hasCapability(membership, input.capability)) return { project, membership };
12117
- const tenantAdmin = await this.tenantAdminMembership(project, input);
12118
- if (tenantAdmin) return { project, membership: tenantAdmin };
12119
- this.requireCapability(membership, input.capability);
12120
- return { project, membership };
12121
- }
12122
- const materialized = await this.materializePublicMembership(input);
12123
- this.requireCapability(materialized, input.capability);
12124
- return { project, membership: materialized };
12125
- }
12126
- async requireExistingPublicProjectAccess(project, input) {
12127
- const membership = await this.deps.membershipStore.findByUser(
12128
- input.tenantId,
12129
- input.projectId,
12130
- input.userId
12131
- );
12132
- if (membership?.status === "active") {
12133
- if (this.hasCapability(membership, input.capability)) return { project, membership };
12134
- const tenantAdmin = await this.tenantAdminMembership(project, input);
12135
- if (tenantAdmin) return { project, membership: tenantAdmin };
12136
- this.requireCapability(membership, input.capability);
12137
- return { project, membership };
12138
- }
12139
- if (membership) this.accessDenied();
12140
- const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
12141
- if (!tenantLink) this.accessDenied();
12142
- const role = tenantLink.role === "owner" || tenantLink.role === "admin" ? "admin" : "member";
12143
- const synthetic = {
12144
- id: `public:${input.userId}`,
12145
- tenantId: input.tenantId,
12146
- projectId: input.projectId,
12147
- userId: input.userId,
12148
- role,
12149
- status: "active",
12150
- joinedAt: project.createdAt,
12151
- updatedAt: project.updatedAt
12152
- };
12153
- this.requireCapability(synthetic, input.capability);
12154
- return { project, membership: synthetic };
12155
- }
12156
- async materializePublicMembership(input) {
12157
- const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
12158
- if (!tenantLink) this.accessDenied();
12159
- const existing = await this.deps.membershipStore.findByUser(
12160
- input.tenantId,
12161
- input.projectId,
12162
- input.userId
12163
- );
12164
- if (existing) this.accessDenied();
12165
- const role = tenantLink.role === "owner" || tenantLink.role === "admin" ? "admin" : "member";
12166
- try {
12167
- return await this.deps.membershipStore.create({
12168
- id: this.idFactory(),
12169
- tenantId: input.tenantId,
12170
- projectId: input.projectId,
12171
- userId: input.userId,
12172
- role,
12173
- status: "active"
12174
- });
12175
- } catch (error) {
12176
- if (isProjectMembershipConflict(error)) {
12177
- const raced = await this.deps.membershipStore.findByUser(
12178
- input.tenantId,
12179
- input.projectId,
12180
- input.userId
12181
- );
12182
- if (raced?.status === "active") return raced;
12183
- }
12184
- throw error;
12185
- }
12186
- }
12187
- hasCapability(membership, capability2) {
12188
- return allowedRoles[capability2].has(membership.role);
12189
- }
12190
- requireCapability(membership, capability2) {
12191
- if (!this.hasCapability(membership, capability2)) this.accessDenied();
12192
- }
12193
- async tenantAdminMembership(project, input) {
12194
- if (project.kind === "personal") return void 0;
12195
- const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
12196
- if (tenantLink?.role !== "owner" && tenantLink?.role !== "admin") return void 0;
12197
- return {
12198
- id: `tenant-admin:${input.userId}`,
12199
- tenantId: input.tenantId,
12200
- projectId: project.id,
12201
- userId: input.userId,
12202
- role: "admin",
12203
- status: "active",
12204
- joinedAt: project.createdAt,
12205
- updatedAt: project.updatedAt
12206
- };
12207
- }
12208
- isActiveMembership(membership, input) {
12209
- return membership != null && membership.tenantId === input.tenantId && membership.projectId === input.projectId && membership.userId === input.userId && membership.status === "active";
12210
- }
12211
- accessDenied() {
12212
- throw new ProjectCollaborationError("PROJECT_ACCESS_DENIED", 403, "Project access denied");
12213
- }
12214
- };
12215
-
12216
12053
  // src/controllers/database-configs.ts
12217
12054
  import {
12218
12055
  getStoreLattice as getStoreLattice17,
12219
12056
  sqlDatabaseManager
12220
12057
  } from "@axiom-lattice/core";
12221
- import { randomUUID as randomUUID15 } from "crypto";
12058
+ import { randomUUID as randomUUID14 } from "crypto";
12222
12059
  function getTenantId16(request) {
12223
12060
  const userTenantId = request.user?.tenantId;
12224
12061
  if (userTenantId) {
@@ -12296,7 +12133,7 @@ async function createDatabaseConfig(request, reply) {
12296
12133
  message: "Database configuration with this key already exists"
12297
12134
  };
12298
12135
  }
12299
- const id3 = body.id || randomUUID15();
12136
+ const id3 = body.id || randomUUID14();
12300
12137
  const config = await store2.createConfig(tenantId, id3, body);
12301
12138
  try {
12302
12139
  sqlDatabaseManager.registerDatabase(tenantId, config.key, config.config);
@@ -12501,7 +12338,7 @@ import {
12501
12338
  metricsServerManager as metricsServerManager2,
12502
12339
  SemanticMetricsClient as SemanticMetricsClient2
12503
12340
  } from "@axiom-lattice/core";
12504
- import { randomUUID as randomUUID16 } from "crypto";
12341
+ import { randomUUID as randomUUID15 } from "crypto";
12505
12342
  function getTenantId17(request) {
12506
12343
  const userTenantId = request.user?.tenantId;
12507
12344
  if (userTenantId) {
@@ -12582,7 +12419,7 @@ async function createMetricsServerConfig(request, reply) {
12582
12419
  message: "selectedDataSources is required for semantic metrics servers"
12583
12420
  };
12584
12421
  }
12585
- const id3 = body.id || randomUUID16();
12422
+ const id3 = body.id || randomUUID15();
12586
12423
  const configData = {
12587
12424
  key: body.key,
12588
12425
  name: body.name,
@@ -13245,7 +13082,7 @@ import {
13245
13082
  McpLatticeManager,
13246
13083
  convertToConnection
13247
13084
  } from "@axiom-lattice/core";
13248
- import { randomUUID as randomUUID17 } from "crypto";
13085
+ import { randomUUID as randomUUID16 } from "crypto";
13249
13086
 
13250
13087
  // src/services/mcpConfigSync.ts
13251
13088
  var MCP_CONFIG_CHANGED_TOPIC = "mcp-config-changed";
@@ -13364,7 +13201,7 @@ async function createMcpServerConfig(request, reply) {
13364
13201
  message: "MCP server configuration with this key already exists"
13365
13202
  };
13366
13203
  }
13367
- const id3 = body.id || randomUUID17();
13204
+ const id3 = body.id || randomUUID16();
13368
13205
  const config = await store2.createConfig(tenantId, id3, body);
13369
13206
  try {
13370
13207
  await connectAndRegisterTools(config);
@@ -13726,562 +13563,6 @@ function registerMcpServerConfigRoutes(app2) {
13726
13563
  app2.post("/api/mcp-servers/test-tools", testMcpServerTools);
13727
13564
  }
13728
13565
 
13729
- // src/routes/mcp-apps.ts
13730
- import {
13731
- parseMcpUiRef
13732
- } from "@axiom-lattice/protocols";
13733
- import {
13734
- getSandboxPluginCatalog as getSandboxPluginCatalog2,
13735
- mcpManager as mcpManager2,
13736
- resolvePluginConnections,
13737
- verifyMcpUiScope
13738
- } from "@axiom-lattice/core";
13739
-
13740
- // src/services/mcp-ui/UiResourceResolver.ts
13741
- import { createHash as createHash3 } from "crypto";
13742
- var DEFAULT_TTL_MS = 5 * 60 * 1e3;
13743
- function publicFieldsOf(payload) {
13744
- return {
13745
- mimeType: payload.mimeType,
13746
- html: payload.html,
13747
- ...payload.csp !== void 0 ? { csp: payload.csp } : {},
13748
- ...payload.permissions !== void 0 ? { permissions: payload.permissions } : {},
13749
- ...payload.prefersBorder !== void 0 ? { prefersBorder: payload.prefersBorder } : {}
13750
- };
13751
- }
13752
- var UiResourceResolver = class {
13753
- constructor(deps) {
13754
- this.deps = deps;
13755
- this.inFlight = /* @__PURE__ */ new Map();
13756
- }
13757
- keyOf(ref) {
13758
- return ref.kind === "mcp" ? `mcp\0${ref.serverKey}\0${ref.resourceUri}` : `plugin\0${ref.pluginType}\0${ref.resource}`;
13759
- }
13760
- fullKey(tenantId, ref) {
13761
- return `${tenantId}\0${this.keyOf(ref)}`;
13762
- }
13763
- /**
13764
- * Resolve a UI resource for a tenant.
13765
- *
13766
- * @param tenantId Tenant scope.
13767
- * @param ref The fence ref.
13768
- * @returns Public UI resource payload (no cache metadata).
13769
- */
13770
- async resolve(tenantId, ref) {
13771
- const key = this.fullKey(tenantId, ref);
13772
- const ttl = this.deps.ttlMs ?? DEFAULT_TTL_MS;
13773
- const cached = await this.deps.store.get(key);
13774
- if (cached && Date.now() - cached.fetchedAt < ttl) return publicFieldsOf(cached);
13775
- const existing = this.inFlight.get(key);
13776
- if (existing) return existing;
13777
- const promise = this.fetchAndCache(tenantId, ref, key, cached);
13778
- this.inFlight.set(key, promise);
13779
- try {
13780
- return await promise;
13781
- } finally {
13782
- this.inFlight.delete(key);
13783
- }
13784
- }
13785
- /**
13786
- * Evict the cached entry for a tenant/ref.
13787
- *
13788
- * @param tenantId Tenant scope.
13789
- * @param ref The fence ref.
13790
- */
13791
- async invalidate(tenantId, ref) {
13792
- await this.deps.store.delete(this.fullKey(tenantId, ref));
13793
- }
13794
- /**
13795
- * Evict every cached plugin UI resource for a tenant.
13796
- *
13797
- * @param tenantId Tenant whose plugin UI resources to drop.
13798
- * @remarks Called when a tenant's plugin catalog changes so an edited
13799
- * `ui/*.html` is re-fetched instead of being served from the TTL cache
13800
- * (which is keyed by ref identity, not content).
13801
- */
13802
- async invalidateTenantPlugins(tenantId) {
13803
- await this.deps.store.deleteByPrefix(`${tenantId}\0plugin\0`);
13804
- }
13805
- async fetchAndCache(tenantId, ref, key, cached) {
13806
- try {
13807
- const fresh = ref.kind === "mcp" ? await this.deps.fetchMcp(tenantId, ref.serverKey, ref.resourceUri) : await this.deps.fetchPlugin(tenantId, ref.pluginType, ref.resource);
13808
- const entry = {
13809
- ...publicFieldsOf(fresh),
13810
- hash: createHash3("sha256").update(fresh.html).digest("hex"),
13811
- fetchedAt: Date.now()
13812
- };
13813
- await this.deps.store.set(key, entry);
13814
- return publicFieldsOf(fresh);
13815
- } catch (error) {
13816
- if (cached) return { ...publicFieldsOf(cached), stale: true };
13817
- throw error;
13818
- }
13819
- }
13820
- };
13821
-
13822
- // src/services/mcp-ui/McpUiResourceCacheStore.ts
13823
- var InMemoryMcpUiResourceCacheStore = class {
13824
- constructor() {
13825
- this.map = /* @__PURE__ */ new Map();
13826
- }
13827
- async get(key) {
13828
- return this.map.get(key);
13829
- }
13830
- async set(key, entry) {
13831
- this.map.set(key, entry);
13832
- }
13833
- async delete(key) {
13834
- this.map.delete(key);
13835
- }
13836
- async deleteByPrefix(prefix) {
13837
- for (const key of this.map.keys()) {
13838
- if (key.startsWith(prefix)) this.map.delete(key);
13839
- }
13840
- }
13841
- };
13842
-
13843
- // src/services/mcp-ui/buildCsp.ts
13844
- var DEFAULT_CSP = [
13845
- "default-src 'none'",
13846
- "script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'",
13847
- "style-src 'self' 'unsafe-inline'",
13848
- "img-src 'self' data:",
13849
- "media-src 'self' data:",
13850
- // Fonts are restricted to the document origin; declared `resourceDomains`
13851
- // are the only origins ever added (no `data:` — a base64-embedded font must
13852
- // be served from a declared origin).
13853
- "font-src 'self'",
13854
- "object-src 'none'",
13855
- "connect-src 'none'",
13856
- "frame-src 'none'",
13857
- "base-uri 'self'"
13858
- ].join("; ");
13859
- var ORIGIN_PATTERN = /^(https?|wss?):\/\/(\*\.)?[^\s;,'"*]+$/;
13860
- function declaredOrigins(domains) {
13861
- if (!Array.isArray(domains)) return [];
13862
- return domains.filter((domain) => typeof domain === "string" && ORIGIN_PATTERN.test(domain));
13863
- }
13864
- function buildCsp(csp) {
13865
- if (!csp) return DEFAULT_CSP;
13866
- const resourceOrigins = declaredOrigins(csp.resourceDomains);
13867
- const resource = resourceOrigins.join(" ");
13868
- const withResourceOrigins = (base) => resource ? `${base} ${resource}` : base;
13869
- const img = ["'self'", "data:", ...resourceOrigins].join(" ");
13870
- const connect = declaredOrigins(csp.connectDomains);
13871
- const frame = declaredOrigins(csp.frameDomains);
13872
- const baseUri = declaredOrigins(csp.baseUriDomains);
13873
- const parts = [
13874
- "default-src 'none'",
13875
- withResourceOrigins("script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'"),
13876
- withResourceOrigins("style-src 'self' 'unsafe-inline'"),
13877
- `img-src ${img}`,
13878
- `media-src ${["'self'", "data:", ...resourceOrigins].join(" ")}`,
13879
- withResourceOrigins("font-src 'self'"),
13880
- "object-src 'none'",
13881
- `connect-src ${connect.length ? connect.join(" ") : "'none'"}`,
13882
- `frame-src ${frame.length ? frame.join(" ") : "'none'"}`,
13883
- `base-uri ${baseUri.length ? baseUri.join(" ") : "'self'"}`
13884
- ];
13885
- return parts.join("; ");
13886
- }
13887
-
13888
- // src/services/mcp-ui/sandboxPage.ts
13889
- var DEFAULT_CSP_LITERAL = JSON.stringify(buildCsp(void 0));
13890
- var SANDBOX_HTML = `<!doctype html>
13891
- <html lang="en">
13892
- <head>
13893
- <meta charset="utf-8" />
13894
- <meta name="viewport" content="width=device-width, initial-scale=1" />
13895
- <title>MCP App Sandbox</title>
13896
- <style>html, body { margin: 0; height: 100%; }</style>
13897
- </head>
13898
- <body>
13899
- <script>
13900
- (function () {
13901
- var HOST_READY = "ui/notifications/sandbox-proxy-ready";
13902
- var RESOURCE_READY = "ui/notifications/sandbox-resource-ready";
13903
- var CONTROL_PREFIX = "ui/notifications/sandbox-";
13904
- var DEFAULT_CSP = ${DEFAULT_CSP_LITERAL};
13905
-
13906
- function withCsp(html, csp) {
13907
- var policy = typeof csp === "string" && csp.length > 0 ? csp : DEFAULT_CSP;
13908
- var escaped = policy.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
13909
- var meta = '<meta http-equiv="Content-Security-Policy" content="' + escaped + '">';
13910
- // Insert at the very start (after an optional doctype) so the parser
13911
- // places the meta in the implicit head. Never search for a literal
13912
- // <head>: a resource could hide a decoy inside a comment/string and
13913
- // slip its real head past the splice.
13914
- var doctype = /^\\s*<!doctype[^>]*>/i.exec(html);
13915
- if (doctype) {
13916
- return html.slice(0, doctype[0].length) + meta + html.slice(doctype[0].length);
13917
- }
13918
- return meta + html;
13919
- }
13920
-
13921
- // Size reporter injected into every View document. The host renders the
13922
- // View inside an opaque-origin iframe it cannot measure, so auto-height
13923
- // only happens when the View posts ui/notifications/size-changed. Most
13924
- // hand-written plugin apps never do; this script makes every app report
13925
- // its own content size. It only measures and postMessages \u2014 it never
13926
- // mutates the app's DOM.
13927
- var SIZE_REPORTER = [
13928
- "(function(){",
13929
- "function send(){",
13930
- "try{",
13931
- "var d=document.documentElement,b=document.body;",
13932
- "var w=Math.ceil(Math.max(d?d.scrollWidth:0,b?b.scrollWidth:0));",
13933
- "var h=Math.ceil(Math.max(d?d.scrollHeight:0,b?b.scrollHeight:0));",
13934
- "window.parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/size-changed',params:{width:w,height:h}},'*');",
13935
- "}catch(e){}",
13936
- "}",
13937
- "send();",
13938
- "window.addEventListener('load',send);",
13939
- "if(typeof ResizeObserver!=='undefined'){",
13940
- "var ro=new ResizeObserver(send);",
13941
- "ro.observe(document.documentElement);",
13942
- "if(document.body)ro.observe(document.body);",
13943
- "}else{window.addEventListener('resize',send);}",
13944
- "})();"
13945
- ].join("");
13946
-
13947
- function withSizeReporter(html) {
13948
- // Split the closing tag so this template literal never contains a
13949
- // literal script terminator that would end the sandbox page's own
13950
- // script block during HTML parsing.
13951
- var tag = "<scr" + "ipt>" + SIZE_REPORTER + "<" + "/scr" + "ipt>";
13952
- var lower = html.toLowerCase();
13953
- var bodyEnd = lower.lastIndexOf("</body>");
13954
- if (bodyEnd >= 0) return html.slice(0, bodyEnd) + tag + html.slice(bodyEnd);
13955
- return html + tag;
13956
- }
13957
-
13958
- var inner = null;
13959
-
13960
- window.parent.postMessage({ jsonrpc: "2.0", method: HOST_READY, params: {} }, "*");
13961
-
13962
- window.addEventListener("message", function (event) {
13963
- if (event.source !== window.parent) return;
13964
- var data = event.data;
13965
- if (!data || typeof data !== "object") return;
13966
-
13967
- if (data.method === RESOURCE_READY) {
13968
- var params = data.params || {};
13969
- if (inner && inner.parentNode) inner.parentNode.removeChild(inner);
13970
- inner = document.createElement("iframe");
13971
- inner.setAttribute("sandbox", params.sandbox || "allow-scripts");
13972
- inner.setAttribute("srcdoc", withCsp(withSizeReporter(String(params.html || "")), params.csp));
13973
- inner.style.cssText = "width:100%;height:100%;border:0;display:block";
13974
- document.body.appendChild(inner);
13975
- return;
13976
- }
13977
-
13978
- if (typeof data.method === "string" && data.method.indexOf(CONTROL_PREFIX) === 0) return;
13979
- if (inner && inner.contentWindow) inner.contentWindow.postMessage(data, "*");
13980
- });
13981
-
13982
- window.addEventListener("message", function (event) {
13983
- if (inner && event.source === inner.contentWindow) {
13984
- var data = event.data;
13985
- if (data && typeof data === "object" && typeof data.method === "string"
13986
- && data.method.indexOf(CONTROL_PREFIX) === 0) return;
13987
- window.parent.postMessage(event.data, "*");
13988
- }
13989
- });
13990
- })();
13991
- </script>
13992
- </body>
13993
- </html>
13994
- `;
13995
-
13996
- // src/routes/mcp-apps.ts
13997
- var SANDBOX_HTML_CONTENT_TYPE = "text/html; charset=utf-8";
13998
- var PluginMethodNotSupportedError = class extends Error {
13999
- /**
14000
- * @param method - Unsupported JSON-RPC method.
14001
- */
14002
- constructor(method) {
14003
- super(`plugin backend does not support ${method}`);
14004
- this.name = "PluginMethodNotSupportedError";
14005
- }
14006
- };
14007
- var McpAppScopeError = class extends Error {
14008
- /**
14009
- * @param status - HTTP status to return.
14010
- * @param code - Stable machine-readable denial code.
14011
- */
14012
- constructor(status, code) {
14013
- super(code);
14014
- this.status = status;
14015
- this.code = code;
14016
- this.name = "McpAppScopeError";
14017
- }
14018
- };
14019
- function denyScope(status, code, detail) {
14020
- console.warn(JSON.stringify({ event: "mcp-apps:scope-denied", ...detail, code }));
14021
- return new McpAppScopeError(status, code);
14022
- }
14023
- var ALLOWED_METHODS = /* @__PURE__ */ new Set(["tools/call", "tools/list", "resources/read"]);
14024
- function asRecord(value) {
14025
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
14026
- }
14027
- function makeMcpAppHandlers(deps) {
14028
- return {
14029
- /**
14030
- * Resolve the `mcp_app` UI resource for a parsed ref.
14031
- *
14032
- * @param tenantId Tenant scope.
14033
- * @param rawRef Untrusted ref payload.
14034
- * @returns The resource payload plus a ready-to-use `cspPolicy` string
14035
- * ({@link buildCsp}, always non-empty), or a 400/502 error envelope.
14036
- */
14037
- async resource(tenantId, rawRef) {
14038
- const ref = parseMcpUiRef(rawRef);
14039
- if (!ref) return { status: 400, body: { error: "INVALID_REF" } };
14040
- if (ref.kind === "plugin" && ref.pluginType === "") {
14041
- return { status: 400, body: { error: "INVALID_REF" } };
14042
- }
14043
- try {
14044
- const payload = await deps.resolveUiResource(tenantId, ref);
14045
- return { status: 200, body: { ...payload, cspPolicy: buildCsp(payload.csp) } };
14046
- } catch (error) {
14047
- console.warn(JSON.stringify({
14048
- event: "mcp-apps:resource-failed",
14049
- tenantId,
14050
- kind: ref.kind,
14051
- error: error instanceof Error ? error.message : String(error)
14052
- }));
14053
- return { status: 502, body: { error: "RESOURCE_UNAVAILABLE" } };
14054
- }
14055
- },
14056
- /**
14057
- * Proxy an allow-listed JSON-RPC method for a parsed ref.
14058
- *
14059
- * @param tenantId Tenant scope.
14060
- * @param rawRef Untrusted ref payload.
14061
- * @param method JSON-RPC method name.
14062
- * @param params JSON-RPC params.
14063
- * @param context Request-scoped run context forwarded to `tools/call`.
14064
- * @returns The backend result, or a 400/403/502 error envelope.
14065
- */
14066
- async rpc(tenantId, rawRef, method, params, context) {
14067
- const ref = parseMcpUiRef(rawRef);
14068
- if (!ref) return { status: 400, body: { error: "INVALID_REF" } };
14069
- if (!ALLOWED_METHODS.has(method)) return { status: 403, body: { error: "METHOD_NOT_ALLOWED" } };
14070
- try {
14071
- if (method === "tools/call") {
14072
- const name = typeof params.name === "string" ? params.name : "";
14073
- if (name === "") return { status: 400, body: { error: "INVALID_PARAMS" } };
14074
- if ("pluginType" in ref && ref.pluginType === "") {
14075
- return { status: 400, body: { error: "INVALID_REF" } };
14076
- }
14077
- return {
14078
- status: 200,
14079
- body: await deps.callTool(tenantId, ref, name, asRecord(params.arguments) ?? {}, context)
14080
- };
14081
- }
14082
- if (method === "resources/read") {
14083
- const uri = typeof params.uri === "string" ? params.uri : "";
14084
- if (uri === "") return { status: 400, body: { error: "INVALID_PARAMS" } };
14085
- return { status: 200, body: await deps.readResource(tenantId, ref, uri) };
14086
- }
14087
- return { status: 200, body: { tools: await deps.listTools(tenantId, ref) } };
14088
- } catch (error) {
14089
- if (error instanceof McpAppScopeError) {
14090
- return { status: error.status, body: { error: error.code } };
14091
- }
14092
- if (error instanceof PluginToolScopeError) {
14093
- return { status: 403, body: { error: "PLUGIN_TOOL_SCOPE" } };
14094
- }
14095
- if (error instanceof PluginMethodNotSupportedError) {
14096
- return { status: 403, body: { error: "PLUGIN_METHOD_UNSUPPORTED" } };
14097
- }
14098
- const context2 = { tenantId, kind: ref.kind, method };
14099
- if (method === "resources/read") context2.uri = typeof params.uri === "string" ? params.uri : "";
14100
- if (method === "tools/call") context2.name = typeof params.name === "string" ? params.name : "";
14101
- console.warn(JSON.stringify({
14102
- event: "mcp-apps:rpc-failed",
14103
- ...context2,
14104
- error: error instanceof Error ? error.message : String(error)
14105
- }));
14106
- return { status: 502, body: { error: "RPC_FAILED" } };
14107
- }
14108
- }
14109
- };
14110
- }
14111
- async function fetchPluginUiResource(backend, tenantId, pluginType, resource) {
14112
- const plugin = await backend.resolvePlugin(tenantId, pluginType);
14113
- if (!plugin) throw new Error(`plugin "${pluginType}" not found`);
14114
- const tool = (plugin.meta.tools ?? []).find((t2) => t2.ui?.resource === resource);
14115
- if (!tool?.ui) throw new Error(`plugin "${pluginType}" has no tool declaring ui resource "${resource}"`);
14116
- const inline = plugin.meta.uiResources?.[resource];
14117
- if (inline) {
14118
- return {
14119
- mimeType: inline.mimeType ?? "text/html;profile=mcp-app",
14120
- html: inline.html,
14121
- csp: inline.csp ?? tool.ui.csp,
14122
- permissions: tool.ui.permissions,
14123
- prefersBorder: tool.ui.prefersBorder
14124
- };
14125
- }
14126
- const html = await backend.catalog.readPluginFile(tenantId, pluginType, resource);
14127
- return {
14128
- mimeType: "text/html",
14129
- html,
14130
- csp: tool.ui.csp,
14131
- permissions: tool.ui.permissions,
14132
- prefersBorder: tool.ui.prefersBorder
14133
- };
14134
- }
14135
- async function callPluginTool(backend, tenantId, ref, name, args, context, deps) {
14136
- const scope = ref.scope ? (deps?.verifyScope ?? verifyMcpUiScope)(ref.scope) : null;
14137
- if (ref.scope && !scope) {
14138
- throw denyScope(403, "SCOPE_INVALID", {
14139
- tenantId,
14140
- pluginType: ref.pluginType,
14141
- tool: name
14142
- });
14143
- }
14144
- if (scope) {
14145
- const mismatched = scope.tid !== tenantId || scope.plugin !== ref.pluginType || scope.resource !== ref.resource;
14146
- if (mismatched) {
14147
- throw denyScope(403, "SCOPE_MISMATCH", {
14148
- tenantId,
14149
- pluginType: ref.pluginType,
14150
- tool: name,
14151
- jti: scope.jti,
14152
- threadId: scope.thid
14153
- });
14154
- }
14155
- }
14156
- const manifest = await backend.catalog.getValidatedManifest(tenantId, ref.pluginType);
14157
- const rawConnectionKey = args.connectionKey;
14158
- const explicit = typeof rawConnectionKey === "string" ? rawConnectionKey : void 0;
14159
- const scoped = scope?.ck;
14160
- const connectionRelevant = Boolean(manifest?.connection) || scoped !== void 0;
14161
- if (connectionRelevant && (explicit !== void 0 ? explicit !== scoped : rawConnectionKey !== void 0)) {
14162
- throw denyScope(403, "CONNECTION_NOT_ALLOWED", {
14163
- tenantId,
14164
- pluginType: ref.pluginType,
14165
- tool: name,
14166
- jti: scope?.jti,
14167
- threadId: scope?.thid
14168
- });
14169
- }
14170
- let connection;
14171
- let connectionKey;
14172
- if (scoped !== void 0) {
14173
- const resolveConnections = deps?.resolveConnections ?? resolvePluginConnections;
14174
- const resolved = await resolveConnections(
14175
- ref.pluginType,
14176
- { connections: [scoped] },
14177
- { tenantId }
14178
- );
14179
- if (resolved.length === 0) {
14180
- throw denyScope(403, "CONNECTION_NOT_ALLOWED", {
14181
- tenantId,
14182
- pluginType: ref.pluginType,
14183
- tool: name,
14184
- jti: scope?.jti,
14185
- threadId: scope?.thid
14186
- });
14187
- }
14188
- connection = resolved[0].config;
14189
- connectionKey = resolved[0].key;
14190
- }
14191
- if (manifest?.connection && !connectionKey) {
14192
- throw denyScope(409, "CONNECTION_REQUIRED", {
14193
- tenantId,
14194
- pluginType: ref.pluginType,
14195
- tool: name,
14196
- jti: scope?.jti,
14197
- threadId: scope?.thid
14198
- });
14199
- }
14200
- const identity = scope ? {
14201
- workspaceId: scope.wid,
14202
- projectId: scope.pid,
14203
- threadId: scope.thid,
14204
- assistantId: scope.aid
14205
- } : {
14206
- workspaceId: context?.workspaceId,
14207
- projectId: context?.projectId
14208
- };
14209
- return invokePluginTool(backend, tenantId, ref.pluginType, name, args, {
14210
- ...identity,
14211
- ...connection === void 0 ? {} : { connection }
14212
- });
14213
- }
14214
- function createDefaultPluginBackend() {
14215
- return {
14216
- resolvePlugin: (tenantId, type) => resolveTenantPluginByType(tenantId, type),
14217
- catalog: {
14218
- readPluginFile: (tenantId, pluginType, relPath) => getSandboxPluginCatalog2().readPluginFile(tenantId, pluginType, relPath),
14219
- getValidatedManifest: (tenantId, pluginType) => getSandboxPluginCatalog2().getValidatedManifest(tenantId, pluginType),
14220
- getExecutor: () => getSandboxPluginCatalog2().getExecutor()
14221
- }
14222
- };
14223
- }
14224
- var uiResourceResolverSingleton;
14225
- function getMcpUiResourceResolver() {
14226
- return uiResourceResolverSingleton;
14227
- }
14228
- function resolveMcpAppDeps(pluginBackend = createDefaultPluginBackend()) {
14229
- const uiResourceResolver = new UiResourceResolver({
14230
- store: new InMemoryMcpUiResourceCacheStore(),
14231
- fetchMcp: (tenantId, serverKey, resourceUri) => mcpManager2.readUiResource(tenantId, serverKey, resourceUri),
14232
- fetchPlugin: (tenantId, pluginType, resource) => fetchPluginUiResource(pluginBackend, tenantId, pluginType, resource)
14233
- });
14234
- uiResourceResolverSingleton = uiResourceResolver;
14235
- return {
14236
- resolveUiResource: (tenantId, ref) => uiResourceResolver.resolve(tenantId, ref),
14237
- callTool: (tenantId, ref, name, args, context) => ref.kind === "mcp" ? mcpManager2.callServerTool(tenantId, ref.serverKey, name, args) : callPluginTool(pluginBackend, tenantId, ref, name, args, context),
14238
- // Plugin refs reach these only after `tools/call`; the handler treats the
14239
- // rejection as an unsupported method (403). The branches are live because
14240
- // the pure handler deliberately delegates method policy to the deps.
14241
- readResource: (tenantId, ref, uri) => ref.kind === "mcp" ? mcpManager2.readServerResource(tenantId, ref.serverKey, uri) : Promise.reject(new PluginMethodNotSupportedError("resources/read")),
14242
- listTools: (tenantId, ref) => ref.kind === "mcp" ? mcpManager2.getAvailableTools(tenantId, ref.serverKey) : Promise.reject(new PluginMethodNotSupportedError("tools/list"))
14243
- };
14244
- }
14245
- function firstHeader(request, name) {
14246
- const header2 = request.headers[name];
14247
- return Array.isArray(header2) ? header2[0] : header2;
14248
- }
14249
- function tenantOf(request) {
14250
- const user = request.user;
14251
- return user?.tenantId ?? firstHeader(request, "x-tenant-id") ?? "default";
14252
- }
14253
- function runContextOf(request) {
14254
- return {
14255
- tenantId: tenantOf(request),
14256
- workspaceId: firstHeader(request, "x-workspace-id"),
14257
- projectId: firstHeader(request, "x-project-id")
14258
- };
14259
- }
14260
- function registerMcpAppRoutes(fastify2, deps = resolveMcpAppDeps()) {
14261
- const handlers = makeMcpAppHandlers(deps);
14262
- fastify2.get("/mcp-apps/sandbox.html", async (_request, reply) => {
14263
- return reply.header("content-type", SANDBOX_HTML_CONTENT_TYPE).header("x-content-type-options", "nosniff").header("cache-control", "no-store").send(SANDBOX_HTML);
14264
- });
14265
- fastify2.post("/api/mcp-apps/resource", async (request, reply) => {
14266
- const body = asRecord(request.body) ?? {};
14267
- const result = await handlers.resource(tenantOf(request), body.ref);
14268
- return reply.code(result.status).send(result.body);
14269
- });
14270
- fastify2.post("/api/mcp-apps/rpc", async (request, reply) => {
14271
- const body = asRecord(request.body) ?? {};
14272
- const params = asRecord(body.params) ?? {};
14273
- const context = runContextOf(request);
14274
- const result = await handlers.rpc(
14275
- context.tenantId,
14276
- body.ref,
14277
- String(body.method ?? ""),
14278
- params,
14279
- context
14280
- );
14281
- return reply.code(result.status).send(result.body);
14282
- });
14283
- }
14284
-
14285
13566
  // src/controllers/eval.ts
14286
13567
  import { getStoreLattice as getStoreLattice21, hasModelLatticeForTenant as hasModelLatticeForTenant2, modelLatticeManager as modelLatticeManager2 } from "@axiom-lattice/core";
14287
13568
  import { v4 as uuidv43 } from "uuid";
@@ -15431,6 +14712,16 @@ var AuthController = class {
15431
14712
  }
15432
14713
  async login(request, reply) {
15433
14714
  const { email, password } = request.body;
14715
+ const rateLimit = await getRateLimitService().consume("auth.login", {
14716
+ ip: request.ip
14717
+ });
14718
+ if (!rateLimit.allowed) {
14719
+ return reply.header("Retry-After", String(rateLimit.retryAfterSeconds)).status(429).send({
14720
+ success: false,
14721
+ error: "RATE_LIMITED",
14722
+ retryAfterSeconds: rateLimit.retryAfterSeconds
14723
+ });
14724
+ }
15434
14725
  try {
15435
14726
  const user = await this.userStore.getUserByEmail(email);
15436
14727
  if (!user) {
@@ -15799,7 +15090,7 @@ function isStaleConsoleSessionCredential(authHeader) {
15799
15090
  function bypassesConsoleAuth(request) {
15800
15091
  const path5 = request.url.split("?")[0];
15801
15092
  const method = request.method;
15802
- if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path5) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path5) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+\/join$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+\/cancel$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path5) || method === "GET" && /^\/embed\/[^/]+$/.test(path5) || method === "GET" && path5 === "/mcp-apps/sandbox.html" || (method === "POST" || method === "DELETE") && path5 === "/open/mcp" || (method === "POST" || method === "GET") && (path5 === "/mcp" || path5 === "/mcp/") || method === "POST" && path5 === "/open/session" || (method === "GET" || method === "POST") && path5.startsWith("/open/sandbox/files/")) return true;
15093
+ if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path5) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path5) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+\/join$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+\/cancel$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path5) || method === "GET" && /^\/embed\/[^/]+$/.test(path5) || method === "GET" && path5 === "/mcp-apps/sandbox.html" || method === "GET" && /^\/api\/mcp-app-shares\/[^/]+\/bootstrap$/.test(path5) || method === "POST" && /^\/api\/mcp-app-shares\/[^/]+\/(resource|rpc|unlock)$/.test(path5) || (method === "POST" || method === "DELETE") && path5 === "/open/mcp" || (method === "POST" || method === "GET") && (path5 === "/mcp" || path5 === "/mcp/") || method === "POST" && path5 === "/open/session" || (method === "GET" || method === "POST") && path5.startsWith("/open/sandbox/files/")) return true;
15803
15094
  if (request.method === "GET" && (/^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path5) || /^\/a2a\/(?:v1\/)?[^/]+\/\.well-known\/agent-card\.json$/.test(path5))) {
15804
15095
  return true;
15805
15096
  }
@@ -16221,7 +15512,7 @@ function registerChannelRoutes(app2, dependencies) {
16221
15512
  }
16222
15513
 
16223
15514
  // src/controllers/channel-installations.ts
16224
- import { randomUUID as randomUUID18 } from "crypto";
15515
+ import { randomUUID as randomUUID17 } from "crypto";
16225
15516
  import {
16226
15517
  descriptorDataValue
16227
15518
  } from "@axiom-lattice/protocols";
@@ -16442,7 +15733,7 @@ async function createChannelInstallation(request, reply) {
16442
15733
  };
16443
15734
  }
16444
15735
  const store2 = await getInstallationStore();
16445
- const installationId = randomUUID18();
15736
+ const installationId = randomUUID17();
16446
15737
  const createInput = {
16447
15738
  channel: body.channel,
16448
15739
  name: body.name,
@@ -17434,7 +16725,7 @@ async function getLocalA2ATemplates(_request, reply) {
17434
16725
  }
17435
16726
 
17436
16727
  // src/routes/a2a-test-connection.ts
17437
- import { randomUUID as randomUUID19 } from "crypto";
16728
+ import { randomUUID as randomUUID18 } from "crypto";
17438
16729
  function describeTestConnectionError(error, agentCardUrl) {
17439
16730
  if (!(error instanceof Error)) {
17440
16731
  return `Failed to fetch agent card from ${agentCardUrl}: ${String(error)}`;
@@ -17474,7 +16765,7 @@ async function ensureLocalRuntimeForConnectionTest(localRuntime, agentCardUrl) {
17474
16765
  return { error: portValidation.error };
17475
16766
  }
17476
16767
  const state = await localA2AService.startProvider(
17477
- `test-${randomUUID19()}`,
16768
+ `test-${randomUUID18()}`,
17478
16769
  testRuntime
17479
16770
  );
17480
16771
  if (state.status === "running") return { runtimeId: state.runtimeId };
@@ -17565,6 +16856,7 @@ import {
17565
16856
  ProjectTaskOwnerPolicyError as ProjectTaskOwnerPolicyError2,
17566
16857
  projectMutationMutex as projectMutationMutex2,
17567
16858
  projectTaskAdmissionNotifier,
16859
+ projectRoomMessageNotifier,
17568
16860
  taskLifecycleNotifier as taskLifecycleNotifier2
17569
16861
  } from "@axiom-lattice/core";
17570
16862
 
@@ -17903,16 +17195,16 @@ function snapshotEnsureInput(input) {
17903
17195
  }
17904
17196
 
17905
17197
  // src/services/tasks/TaskCommandService.ts
17906
- import { createHash as createHash4 } from "crypto";
17198
+ import { createHash as createHash3 } from "crypto";
17907
17199
 
17908
17200
  // src/services/collaboration/ProjectTaskRunService.ts
17909
- import { randomUUID as randomUUID21 } from "crypto";
17201
+ import { randomUUID as randomUUID20 } from "crypto";
17910
17202
  import {
17911
17203
  createTaskRunBridge
17912
17204
  } from "@axiom-lattice/core";
17913
17205
 
17914
17206
  // src/services/collaboration/ProjectTaskRunPlanner.ts
17915
- import { randomUUID as randomUUID20 } from "crypto";
17207
+ import { randomUUID as randomUUID19 } from "crypto";
17916
17208
 
17917
17209
  // src/services/collaboration/ProjectTaskRunProtocol.ts
17918
17210
  import {
@@ -18228,7 +17520,7 @@ var ProjectTaskRunPlanner = class {
18228
17520
  } catch (error) {
18229
17521
  throw asPlanError(error);
18230
17522
  }
18231
- const inputId = (this.deps.idFactory ?? randomUUID20)();
17523
+ const inputId = (this.deps.idFactory ?? randomUUID19)();
18232
17524
  const actor3 = input.actor ?? SYSTEM_ACTOR;
18233
17525
  const userId = actor3.startsWith("user:") && actor3.length > "user:".length ? actor3.slice("user:".length) : void 0;
18234
17526
  const trustedContext = {
@@ -18371,7 +17663,7 @@ var ProjectTaskRunService = class {
18371
17663
  ...deps.idFactory === void 0 ? {} : { idFactory: deps.idFactory }
18372
17664
  });
18373
17665
  this.runBridge = createTaskRunBridge({ lifecycle: deps.lifecycle });
18374
- this.idFactory = deps.idFactory ?? randomUUID21;
17666
+ this.idFactory = deps.idFactory ?? randomUUID20;
18375
17667
  this.log = deps.log ?? { warn: () => void 0 };
18376
17668
  }
18377
17669
  /**
@@ -19040,7 +18332,7 @@ var _TaskCommandService = class _TaskCommandService {
19040
18332
  threadId,
19041
18333
  summary: "User comment",
19042
18334
  detail: { markdown: text, type: "user_comment" },
19043
- fallbackEventKey: `task-comment:${task.id}:${createHash4("sha256").update(text).digest("hex")}`
18335
+ fallbackEventKey: `task-comment:${task.id}:${createHash3("sha256").update(text).digest("hex")}`
19044
18336
  });
19045
18337
  const agent = this.deps.agents.getAgent(this.agentScope(task, threadId));
19046
18338
  await agent.addMessage({ input: { message: text } });
@@ -19728,7 +19020,7 @@ var AgentWebAppService = class {
19728
19020
  }
19729
19021
  async create(context, input) {
19730
19022
  await this.validateAssistant(context, input.assistantId);
19731
- const normalized = await this.validateConfig(context.tenantId, input);
19023
+ const normalized = await this.validateConfig(context, input);
19732
19024
  return this.deps.webAppStore.create(context.tenantId, normalized);
19733
19025
  }
19734
19026
  async list(context, assistantId) {
@@ -19755,7 +19047,7 @@ var AgentWebAppService = class {
19755
19047
  identity: patch.identity ?? current3.identity
19756
19048
  };
19757
19049
  await this.validateAssistant(context, current3.assistantId);
19758
- const validated = await this.validateConfig(context.tenantId, effective);
19050
+ const validated = await this.validateConfig(context, effective, current3);
19759
19051
  const update = {
19760
19052
  ...patch.name !== void 0 ? { name: validated.name } : {},
19761
19053
  ...Object.prototype.hasOwnProperty.call(patch, "description") ? { description: validated.description } : {},
@@ -19768,7 +19060,7 @@ var AgentWebAppService = class {
19768
19060
  }
19769
19061
  async enable(context, webAppId) {
19770
19062
  const current3 = await this.requireAuthorizedWebApp(context, webAppId);
19771
- await this.validateConfig(context.tenantId, current3);
19063
+ await this.validateConfig(context, current3, current3);
19772
19064
  return this.writeSnapshot(context, current3, { status: "active" });
19773
19065
  }
19774
19066
  async disable(context, webAppId) {
@@ -19790,7 +19082,7 @@ var AgentWebAppService = class {
19790
19082
  if (await this.deps.configuredAgentResolver.exists(context.tenantId, assistantId)) return;
19791
19083
  throw new AgentWebAppError("ASSISTANT_NOT_FOUND", 404, "Assistant not found");
19792
19084
  }
19793
- async validateConfig(tenantId, input) {
19085
+ async validateConfig(context, input, existing) {
19794
19086
  const allowedProjectIds = [...new Set(input.scope.allowedProjectIds)];
19795
19087
  if (!allowedProjectIds.length || !allowedProjectIds.includes(input.scope.defaultProjectId)) {
19796
19088
  throw new AgentWebAppError("PROJECT_SCOPE_INVALID", 400, "Default project must be in the project allowlist");
@@ -19798,13 +19090,28 @@ var AgentWebAppService = class {
19798
19090
  if (!input.features.projectSelector && allowedProjectIds.length !== 1) {
19799
19091
  throw new AgentWebAppError("PROJECT_SCOPE_INVALID", 400, "Disabled project selection requires exactly one project");
19800
19092
  }
19093
+ const existingIds = new Set(existing?.scope.allowedProjectIds ?? []);
19801
19094
  for (const projectId of allowedProjectIds) {
19802
- if (!await this.deps.projectStore.getProjectById(tenantId, projectId)) {
19095
+ if (!await this.deps.projectStore.getProjectById(context.tenantId, projectId)) {
19803
19096
  throw new AgentWebAppError("PROJECT_NOT_FOUND", 404, "Project not found");
19804
19097
  }
19098
+ if (existingIds.has(projectId)) continue;
19099
+ try {
19100
+ await this.deps.projectAccess.requireExistingProjectAccess({
19101
+ tenantId: context.tenantId,
19102
+ userId: context.userId,
19103
+ projectId,
19104
+ capability: "read"
19105
+ });
19106
+ } catch (error) {
19107
+ if (error instanceof ProjectCollaborationError) {
19108
+ throw new AgentWebAppError("PROJECT_NOT_FOUND", 404, "Project not found");
19109
+ }
19110
+ throw error;
19111
+ }
19805
19112
  }
19806
19113
  const scope = await this.validateModels(
19807
- tenantId,
19114
+ context.tenantId,
19808
19115
  input.scope,
19809
19116
  input.features.modelSelector
19810
19117
  );
@@ -20013,7 +19320,7 @@ import { createHmac as createHmac3 } from "crypto";
20013
19320
  import { z as z5, ZodError as ZodError2 } from "zod";
20014
19321
 
20015
19322
  // src/services/agent-web-app/AgentWebAppRuntimeService.ts
20016
- import { createHash as createHash5, randomUUID as randomUUID22 } from "crypto";
19323
+ import { createHash as createHash4, randomUUID as randomUUID21 } from "crypto";
20017
19324
  import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock, buildAffinityMissChunk as buildAffinityMissChunk2 } from "@axiom-lattice/protocols";
20018
19325
  import { getReplicaId as getReplicaId7, getThreadRunOwner as getThreadRunOwner2, normalizeAgentInterrupts as normalizeAgentInterrupts2 } from "@axiom-lattice/core";
20019
19326
  function isRecord9(value) {
@@ -20308,10 +19615,10 @@ ${formatFileRefsSection(refs)}` : input.content;
20308
19615
  const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
20309
19616
  this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
20310
19617
  if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
20311
- const userDigest = createHash5("sha256").update(userId, "utf8").digest("hex");
20312
- const contentDigest = createHash5("sha256").update(file.bytes).digest("hex");
19618
+ const userDigest = createHash4("sha256").update(userId, "utf8").digest("hex");
19619
+ const contentDigest = createHash4("sha256").update(file.bytes).digest("hex");
20313
19620
  const displayName = file.name;
20314
- const physicalName = `${randomUUID22()}-${sanitizePhysicalFilename(file.name)}`;
19621
+ const physicalName = `${randomUUID21()}-${sanitizePhysicalFilename(file.name)}`;
20315
19622
  const saved = await this.deps.saveProjectFile({
20316
19623
  tenantId: runtime.webApp.tenantId,
20317
19624
  workspaceId: runtime.project.workspaceId,
@@ -20472,7 +19779,7 @@ ${formatFileRefsSection(refs)}` : input.content;
20472
19779
  const thread = await this.deps.threadStore.createThread(
20473
19780
  runtime.webApp.tenantId,
20474
19781
  runtime.webApp.assistantId,
20475
- randomUUID22(),
19782
+ randomUUID21(),
20476
19783
  { metadata: this.metadata(runtime, userId, normalizeLabel(rawLabel)) }
20477
19784
  );
20478
19785
  return toRuntimeThread(thread);
@@ -20517,7 +19824,7 @@ function implicitThreadId(runtime, userId) {
20517
19824
  userId,
20518
19825
  runtime.project.id
20519
19826
  ]);
20520
- const bytes = createHash5("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
19827
+ const bytes = createHash4("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
20521
19828
  bytes[6] = bytes[6] & 15 | 128;
20522
19829
  bytes[8] = bytes[8] & 63 | 128;
20523
19830
  const hex = bytes.toString("hex");
@@ -21331,17 +20638,19 @@ var CSS_CONTENT_TYPE = "text/css; charset=utf-8";
21331
20638
  var EXACT_ASSETS = {
21332
20639
  "agent-chat-loader.js": { contentType: JS_CONTENT_TYPE },
21333
20640
  "embed-page.js": { contentType: JS_CONTENT_TYPE },
21334
- "embed-page.css": { contentType: CSS_CONTENT_TYPE }
20641
+ "embed-page.css": { contentType: CSS_CONTENT_TYPE },
20642
+ "mcp-app-page.js": { contentType: JS_CONTENT_TYPE },
20643
+ "mcp-app-page.css": { contentType: CSS_CONTENT_TYPE }
21335
20644
  };
21336
- var HASHED_CHUNK_PATTERN = /^embed-page-[A-Za-z0-9._-]+\.(js|css)$/;
21337
- var CHUNK_CSS_PATTERN = /^embed-page-[A-Za-z0-9._-]+\.css$/;
20645
+ var HASHED_CHUNK_PATTERN = /^(embed-page|mcp-app-page)-[A-Za-z0-9._-]+\.(js|css)$/;
20646
+ var CHUNK_CSS_PATTERN = /^(embed-page|mcp-app-page)-[A-Za-z0-9._-]+\.css$/;
21338
20647
  function resolveAsset(asset) {
21339
20648
  if (asset.includes("..")) return null;
21340
20649
  const exact2 = EXACT_ASSETS[asset];
21341
20650
  if (exact2 !== void 0) return { contentType: exact2.contentType, immutable: false };
21342
20651
  const hashed = HASHED_CHUNK_PATTERN.exec(asset);
21343
20652
  if (hashed) {
21344
- return { contentType: hashed[1] === "css" ? CSS_CONTENT_TYPE : JS_CONTENT_TYPE, immutable: true };
20653
+ return { contentType: hashed[2] === "css" ? CSS_CONTENT_TYPE : JS_CONTENT_TYPE, immutable: true };
21345
20654
  }
21346
20655
  return null;
21347
20656
  }
@@ -21446,7 +20755,7 @@ function registerAgentWebAppEmbedRoutes(app2, deps = {}) {
21446
20755
  }
21447
20756
 
21448
20757
  // src/services/agent-web-app/AgentWebAppAttachmentRef.ts
21449
- import { createCipheriv, createDecipheriv, createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
20758
+ import { createCipheriv, createDecipheriv, createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
21450
20759
  var TTL_MS = 15 * 6e4;
21451
20760
  function resolveAgentWebAppAttachmentSecret(consoleSecret) {
21452
20761
  return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
@@ -21455,7 +20764,7 @@ var AgentWebAppAttachmentRefService = class {
21455
20764
  constructor(secret, now = Date.now) {
21456
20765
  this.now = now;
21457
20766
  if (!secret) throw new Error("Attachment signing secret is required");
21458
- this.key = createHash6("sha256").update(secret, "utf8").digest();
20767
+ this.key = createHash5("sha256").update(secret, "utf8").digest();
21459
20768
  }
21460
20769
  sign(input) {
21461
20770
  const payload = {
@@ -21517,7 +20826,7 @@ function decodeCanonical(value, expectedBytes) {
21517
20826
  return decoded;
21518
20827
  }
21519
20828
  function digestUser(userId) {
21520
- return createHash6("sha256").update(userId, "utf8").digest("hex");
20829
+ return createHash5("sha256").update(userId, "utf8").digest("hex");
21521
20830
  }
21522
20831
  function isAttachmentPayload(value) {
21523
20832
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
@@ -21880,7 +21189,7 @@ function registerCapabilityBundleRoutes(app2, controller) {
21880
21189
  import { ZodError as ZodError3 } from "zod";
21881
21190
 
21882
21191
  // src/services/model-provider/ModelProviderService.ts
21883
- import { randomUUID as randomUUID23 } from "crypto";
21192
+ import { randomUUID as randomUUID22 } from "crypto";
21884
21193
  import { toModelKey } from "@axiom-lattice/protocols";
21885
21194
  import {
21886
21195
  assertSafeUpstreamUrl,
@@ -22219,7 +21528,7 @@ var unsafeReasonToErrorCode = {
22219
21528
  var ModelProviderService = class {
22220
21529
  constructor(deps) {
22221
21530
  this.deps = deps;
22222
- this.idFactory = deps.idFactory ?? (() => randomUUID23());
21531
+ this.idFactory = deps.idFactory ?? (() => randomUUID22());
22223
21532
  }
22224
21533
  /**
22225
21534
  * List the tenant's provider configurations.
@@ -23410,12 +22719,12 @@ async function handleModelProviderChanged(payload, reconcile) {
23410
22719
  }
23411
22720
 
23412
22721
  // src/services/collaboration/ProjectRoomService.ts
23413
- import { randomUUID as randomUUID24 } from "crypto";
22722
+ import { randomUUID as randomUUID23 } from "crypto";
23414
22723
  var ProjectRoomService = class {
23415
22724
  /** Creates a project room service from explicitly injected collaborators. */
23416
22725
  constructor(deps) {
23417
22726
  this.deps = deps;
23418
- this.idFactory = deps.idFactory ?? randomUUID24;
22727
+ this.idFactory = deps.idFactory ?? randomUUID23;
23419
22728
  }
23420
22729
  /**
23421
22730
  * Authorizes read access and ensures the project's main room.
@@ -23475,7 +22784,7 @@ var ProjectRoomService = class {
23475
22784
  };
23476
22785
 
23477
22786
  // src/services/collaboration/ProjectMembershipService.ts
23478
- import { randomUUID as randomUUID25 } from "crypto";
22787
+ import { randomUUID as randomUUID24 } from "crypto";
23479
22788
  var ProjectMembershipServiceError = class extends Error {
23480
22789
  constructor(code, statusCode, message) {
23481
22790
  super(message);
@@ -23484,14 +22793,14 @@ var ProjectMembershipServiceError = class extends Error {
23484
22793
  this.name = "ProjectMembershipServiceError";
23485
22794
  }
23486
22795
  };
23487
- var membershipConflictNames2 = /* @__PURE__ */ new Set(["DuplicateProjectMembershipError", "ProjectMembershipIdConflictError"]);
22796
+ var membershipConflictNames = /* @__PURE__ */ new Set(["DuplicateProjectMembershipError", "ProjectMembershipIdConflictError"]);
23488
22797
  function isMembershipConflict(error) {
23489
- return error instanceof Error && membershipConflictNames2.has(error.name);
22798
+ return error instanceof Error && membershipConflictNames.has(error.name);
23490
22799
  }
23491
22800
  var ProjectMembershipService = class {
23492
22801
  constructor(deps) {
23493
22802
  this.deps = deps;
23494
- this.idFactory = deps.idFactory ?? randomUUID25;
22803
+ this.idFactory = deps.idFactory ?? randomUUID24;
23495
22804
  this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
23496
22805
  }
23497
22806
  async list(actor3) {
@@ -23621,7 +22930,7 @@ var ProjectMembershipService = class {
23621
22930
  };
23622
22931
 
23623
22932
  // src/services/collaboration/ProjectRosterService.ts
23624
- import { randomUUID as randomUUID26 } from "crypto";
22933
+ import { randomUUID as randomUUID25 } from "crypto";
23625
22934
  var ProjectRosterServiceError = class extends Error {
23626
22935
  /** Creates a stable roster service error. */
23627
22936
  constructor(code, statusCode, message) {
@@ -23640,7 +22949,7 @@ var ProjectRosterService = class {
23640
22949
  /** Creates a roster service from explicitly injected collaborators. */
23641
22950
  constructor(deps) {
23642
22951
  this.deps = deps;
23643
- this.idFactory = deps.idFactory ?? randomUUID26;
22952
+ this.idFactory = deps.idFactory ?? randomUUID25;
23644
22953
  this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
23645
22954
  }
23646
22955
  /**
@@ -23747,7 +23056,7 @@ var ProjectRosterService = class {
23747
23056
  const agent = (await this.deps.agents.list(actor3.tenantId, actor3.userId)).find((item) => item.assistantId === assistantId);
23748
23057
  if (!agent || agent.ownerUserId !== void 0) throw new ProjectRosterServiceError("ASSISTANT_NOT_FOUND", 404, "Agent not found");
23749
23058
  const room = await this.deps.roomStore.ensureMainRoom({
23750
- id: randomUUID26(),
23059
+ id: randomUUID25(),
23751
23060
  tenantId: actor3.tenantId,
23752
23061
  workspaceId: project.workspaceId,
23753
23062
  projectId: actor3.projectId,
@@ -24091,7 +23400,7 @@ function virtualPublicProjectBot(project, room, agent, mentionName, now) {
24091
23400
  }
24092
23401
 
24093
23402
  // src/services/collaboration/RoomMessageService.ts
24094
- import { createHash as createHash7, randomUUID as randomUUID27 } from "crypto";
23403
+ import { createHash as createHash6, randomUUID as randomUUID26 } from "crypto";
24095
23404
 
24096
23405
  // src/services/collaboration/ProjectBotMembershipSnapshot.ts
24097
23406
  import { snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
@@ -24180,7 +23489,7 @@ var RoomMessageService = class {
24180
23489
  constructor(deps, options = {}) {
24181
23490
  this.deps = deps;
24182
23491
  this.idempotentOutcomes = /* @__PURE__ */ new Map();
24183
- this.idFactory = deps.idFactory ?? randomUUID27;
23492
+ this.idFactory = deps.idFactory ?? randomUUID26;
24184
23493
  const limit = options.idempotentOutcomeLimit ?? 1e3;
24185
23494
  if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("idempotentOutcomeLimit must be a positive safe integer");
24186
23495
  this.idempotentOutcomeLimit = limit;
@@ -24394,7 +23703,7 @@ var RoomMessageService = class {
24394
23703
  let idempotencyKey;
24395
23704
  if (value.requestIdempotencyKey !== void 0) {
24396
23705
  if (typeof value.requestIdempotencyKey !== "string" || !/^[\x21-\x7e]{1,200}$/.test(value.requestIdempotencyKey.trim())) throw new RoomMessageServiceError("INVALID_MESSAGE", 400, "Invalid idempotency key");
24397
- idempotencyKey = `room-human:${actor3.userId}:${createHash7("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
23706
+ idempotencyKey = `room-human:${actor3.userId}:${createHash6("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
24398
23707
  }
24399
23708
  return { text, mentions, botIds, ...value.replyToMessageId === void 0 ? {} : { replyToMessageId: value.replyToMessageId }, ...idempotencyKey === void 0 ? {} : { idempotencyKey } };
24400
23709
  }
@@ -24451,7 +23760,7 @@ function cloneRoomMessage(message) {
24451
23760
  };
24452
23761
  }
24453
23762
  function fingerprintRequest(input) {
24454
- return createHash7("sha256").update(JSON.stringify({
23763
+ return createHash6("sha256").update(JSON.stringify({
24455
23764
  text: input.text,
24456
23765
  mentions: input.mentions,
24457
23766
  replyToMessageId: input.replyToMessageId ?? null
@@ -24466,7 +23775,7 @@ import { ZodError as ZodError4 } from "zod";
24466
23775
  import { toProjectRoomPublicMessage } from "@axiom-lattice/protocols";
24467
23776
 
24468
23777
  // src/services/collaboration/RoomInterruptService.ts
24469
- import { randomUUID as randomUUID28 } from "crypto";
23778
+ import { randomUUID as randomUUID27 } from "crypto";
24470
23779
  var RESUMABLE_TASK_INTERRUPT_CODES = /* @__PURE__ */ new Set([
24471
23780
  "PROJECT_TASK_NOT_RESUMABLE",
24472
23781
  "PROJECT_TASK_NOT_READY",
@@ -24504,7 +23813,7 @@ var RoomInterruptService = class {
24504
23813
  /** Creates the service from explicitly injected stores and dispatcher. */
24505
23814
  constructor(deps) {
24506
23815
  this.deps = deps;
24507
- this.idFactory = deps.idFactory ?? randomUUID28;
23816
+ this.idFactory = deps.idFactory ?? randomUUID27;
24508
23817
  }
24509
23818
  /** Resolves a pending clarify interrupt with no answers. */
24510
23819
  async skip(input) {
@@ -24867,7 +24176,7 @@ async function execute(reply, operation) {
24867
24176
  }
24868
24177
 
24869
24178
  // src/routes/cospace.ts
24870
- import { randomUUID as randomUUID29 } from "crypto";
24179
+ import { randomUUID as randomUUID28 } from "crypto";
24871
24180
  import { ZodError as ZodError6 } from "zod";
24872
24181
  import {
24873
24182
  generateDirectHumanProjectId,
@@ -24901,7 +24210,7 @@ async function computeUnread(deps, tenantId, userId, roomId, lastMessage) {
24901
24210
  });
24902
24211
  }
24903
24212
  function registerCoSpaceRoutes(app2, deps) {
24904
- const idFactory = deps.idFactory ?? (() => randomUUID29());
24213
+ const idFactory = deps.idFactory ?? (() => randomUUID28());
24905
24214
  app2.get(
24906
24215
  "/api/workspaces/:workspaceId/rooms",
24907
24216
  async (request, reply) => {
@@ -25868,7 +25177,7 @@ function exactValues(value, required, optional = []) {
25868
25177
  }
25869
25178
 
25870
25179
  // src/services/collaboration/CoordinatorWakeNotifier.ts
25871
- import { randomUUID as randomUUID30 } from "crypto";
25180
+ import { randomUUID as randomUUID29 } from "crypto";
25872
25181
  import {
25873
25182
  snapshotExactRecord as snapshotExactRecord7
25874
25183
  } from "@axiom-lattice/protocols";
@@ -25884,7 +25193,7 @@ var CoordinatorWakeNotifier = class {
25884
25193
  this.accepted = /* @__PURE__ */ new Set();
25885
25194
  this.maxPending = deps.maxPending ?? 1024;
25886
25195
  if (!Number.isSafeInteger(this.maxPending) || this.maxPending < 1) throw new RangeError("maxPending must be a positive safe integer");
25887
- this.idFactory = deps.idFactory ?? randomUUID30;
25196
+ this.idFactory = deps.idFactory ?? randomUUID29;
25888
25197
  }
25889
25198
  /** Admits one notification without awaiting any store or dispatch operation. */
25890
25199
  handle(value, anchorMessageId) {
@@ -26515,7 +25824,7 @@ var TaskInterruptCardProjector = class {
26515
25824
  };
26516
25825
 
26517
25826
  // src/services/collaboration/ProjectTaskObserver.ts
26518
- import { createHash as createHash8 } from "crypto";
25827
+ import { createHash as createHash7 } from "crypto";
26519
25828
  import {
26520
25829
  createTaskRunBridge as createTaskRunBridge3,
26521
25830
  probeThreadRunOwner as defaultProbeThreadRunOwner
@@ -27086,7 +26395,7 @@ var ProjectTaskObserver = class {
27086
26395
  action: "activity",
27087
26396
  summary: "Project task recovery warning",
27088
26397
  detail: { type: "project_task_recovery_warning", phase, message },
27089
- eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash8("sha256").update(message).digest("hex")}`
26398
+ eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash7("sha256").update(message).digest("hex")}`
27090
26399
  });
27091
26400
  }
27092
26401
  async safeWarning(task, phase, error) {
@@ -27329,7 +26638,7 @@ var ProjectTaskObserver = class {
27329
26638
  }
27330
26639
  }
27331
26640
  async persistOverflowWarning(event) {
27332
- const scopeDigest = createHash8("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
26641
+ const scopeDigest = createHash7("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
27333
26642
  try {
27334
26643
  await this.deps.workItems.createIfAbsentByEventKey({
27335
26644
  tenantId: event.tenantId,
@@ -27434,7 +26743,7 @@ function combineSignals(left, right) {
27434
26743
  }
27435
26744
 
27436
26745
  // src/services/collaboration/RoomProjectionService.ts
27437
- import { createHash as createHash9 } from "crypto";
26746
+ import { createHash as createHash8 } from "crypto";
27438
26747
  import {
27439
26748
  PROJECT_ROOM_TASK_DELEGATED_LABEL,
27440
26749
  PROJECT_ROOM_TASK_DELEGATED_SEPARATOR,
@@ -27655,7 +26964,7 @@ function safeRepairIdentity(value, scope) {
27655
26964
  return taskId && eventKey && tenantId === scope.tenantId && workspaceId === scope.workspaceId && projectId === scope.projectId ? { taskId, eventKey } : void 0;
27656
26965
  }
27657
26966
  function tupleKey(prefix, values) {
27658
- const digest = createHash9("sha256").update(JSON.stringify(values)).digest("hex");
26967
+ const digest = createHash8("sha256").update(JSON.stringify(values)).digest("hex");
27659
26968
  return `${prefix}:${digest}`;
27660
26969
  }
27661
26970
  function parseProjectionInput(taskValue, itemValue) {
@@ -27815,7 +27124,7 @@ function isRecord10(value) {
27815
27124
  import { taskLifecycleNotifier } from "@axiom-lattice/core";
27816
27125
 
27817
27126
  // src/services/collaboration/ProjectRoomEventBroker.ts
27818
- import { randomUUID as randomUUID31 } from "crypto";
27127
+ import { randomUUID as randomUUID30 } from "crypto";
27819
27128
  import {
27820
27129
  parseProjectRoomEventId,
27821
27130
  ProjectRoomBrokerCapacityError,
@@ -27846,7 +27155,7 @@ var ProjectRoomEventBroker = class {
27846
27155
  this.subscriberCount = 0;
27847
27156
  this.usageClock = 0;
27848
27157
  this.closed = false;
27849
- this.epoch = options.epoch ?? randomUUID31();
27158
+ this.epoch = options.epoch ?? randomUUID30();
27850
27159
  if (!UUID_PATTERN.test(this.epoch)) {
27851
27160
  throw new TypeError("Project Room event epoch must be a lowercase canonical UUID");
27852
27161
  }
@@ -28234,7 +27543,7 @@ function snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
28234
27543
  }
28235
27544
 
28236
27545
  // src/services/collaboration/ProjectRoomRealtimePublisher.ts
28237
- import { createHash as createHash10 } from "crypto";
27546
+ import { createHash as createHash9 } from "crypto";
28238
27547
  import {
28239
27548
  projectRoomUserEventScope,
28240
27549
  snapshotExactRecord as snapshotExactRecord11,
@@ -28356,7 +27665,7 @@ var BrokerProjectRoomRealtimePublisher = class {
28356
27665
  try {
28357
27666
  event = build();
28358
27667
  if (!event) throw new Error("Invalid Project Room realtime projection");
28359
- const dedupeKey = createHash10("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
27668
+ const dedupeKey = createHash9("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
28360
27669
  if (this.sourceKeys.has(dedupeKey)) return;
28361
27670
  this.deps.broker.publish(event.scope, event.draft, event.sourceKey);
28362
27671
  this.sourceKeys.set(dedupeKey, true);
@@ -28601,12 +27910,21 @@ function createProjectRoomRealtimeRuntime(deps) {
28601
27910
  shutdownTimeoutMs: deps.shutdownTimeoutMs
28602
27911
  });
28603
27912
  }
27913
+ var PUSH_DEDUPE_LIMIT = 2e3;
28604
27914
  function withPushNotifier(target, notifier) {
27915
+ const pushedMessageIds = /* @__PURE__ */ new Set();
28605
27916
  return new Proxy(target, {
28606
27917
  get(inner, prop, receiver) {
28607
27918
  if (prop === "messageCreated") {
28608
27919
  return (message) => {
28609
27920
  inner.messageCreated(message);
27921
+ if (pushedMessageIds.has(message.id)) return;
27922
+ pushedMessageIds.add(message.id);
27923
+ while (pushedMessageIds.size > PUSH_DEDUPE_LIMIT) {
27924
+ const oldest = pushedMessageIds.values().next().value;
27925
+ if (oldest === void 0) break;
27926
+ pushedMessageIds.delete(oldest);
27927
+ }
28610
27928
  void notifier.notifyMessage(message).catch(() => void 0);
28611
27929
  };
28612
27930
  }
@@ -29491,6 +28809,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29491
28809
  const taskWorkItemStore = getStoreLattice28("default", "taskWorkItem").store;
29492
28810
  const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
29493
28811
  const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
28812
+ setA2AProjectAccess(accessService);
29494
28813
  const realtimeMode = resolveProjectRoomRealtimeMode();
29495
28814
  const realtimeLog = options.projectTaskLog ?? { warn: () => void 0 };
29496
28815
  const roomMessageRetention = createRoomMessageRetentionJob({
@@ -29521,7 +28840,14 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29521
28840
  log: realtimeLog
29522
28841
  }) : void 0;
29523
28842
  if (projectRoomRealtimeRuntime) {
29524
- app2.addHook("onClose", async () => projectRoomRealtimeRuntime?.close());
28843
+ const runtime = projectRoomRealtimeRuntime;
28844
+ const unsubscribeProjectRoomMessages = projectRoomMessageNotifier.subscribe((message) => {
28845
+ runtime.publisher.messageCreated(message);
28846
+ });
28847
+ app2.addHook("onClose", async () => {
28848
+ unsubscribeProjectRoomMessages();
28849
+ await runtime.close();
28850
+ });
29525
28851
  const workspaceRoomEventsService = new WorkspaceRoomEventsService({
29526
28852
  broker: projectRoomRealtimeRuntime.broker,
29527
28853
  listMemberRooms: createWorkspaceMemberRoomLister({
@@ -29664,7 +28990,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29664
28990
  });
29665
28991
  }
29666
28992
  if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
29667
- const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: randomUUID32 });
28993
+ const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: randomUUID31 });
29668
28994
  const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? createTaskLifecycleService({ taskStore, workItemStore: taskWorkItemStore });
29669
28995
  const reassignmentAuthorizer = createProjectTaskReassignmentAuthorizer(
29670
28996
  new ProjectTaskOwnerPolicy(projectBotMembershipStore),
@@ -29845,7 +29171,8 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29845
29171
  configuredAgentResolver: createConfiguredAgentResolver({
29846
29172
  hydrate: (tenantId) => agentLatticeManager5.initializeStoredAssistantsForTenant(tenantId),
29847
29173
  has: (tenantId, assistantId) => agentLatticeManager5.hasAgentLatticeWithTenant(tenantId, assistantId)
29848
- })
29174
+ }),
29175
+ projectAccess: accessService
29849
29176
  })
29850
29177
  });
29851
29178
  registerAgentWebAppRuntimeRoutes(app2, {
@@ -29855,7 +29182,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29855
29182
  userTenantLinkStore,
29856
29183
  ensureCoordinator: (scope) => coordinatorEnsurer.ensure(scope),
29857
29184
  log: realtimeLog
29858
- })),
29185
+ }), accessService),
29859
29186
  taskController: createTaskController({
29860
29187
  access: {
29861
29188
  requireProjectAccess: async (input) => {
@@ -30151,7 +29478,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
30151
29478
  userTenantLinkStore,
30152
29479
  ensureCoordinator: (scope) => coordinatorEnsurer.ensure(scope),
30153
29480
  log: realtimeLog
30154
- }));
29481
+ }), accessService);
30155
29482
  registerDatabaseConfigRoutes(app2);
30156
29483
  registerMetricsServerConfigRoutes(app2);
30157
29484
  connectionRoutes(app2);
@@ -30349,7 +29676,7 @@ function tryCreateProjectTaskRuntime(input) {
30349
29676
  function createRuntime(input, stores, log2) {
30350
29677
  const controller = new AbortController();
30351
29678
  const mutex = new ProjectTaskExecutionMutex();
30352
- const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: randomUUID32 });
29679
+ const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: randomUUID31 });
30353
29680
  const lifecycle = createTaskLifecycleService({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
30354
29681
  const projection = new RoomProjectionService({
30355
29682
  messages: input.projectRoomMessageStore,
@@ -30358,7 +29685,7 @@ function createRuntime(input, stores, log2) {
30358
29685
  warnings: input.taskWorkItemStore,
30359
29686
  log: log2,
30360
29687
  realtime: input.realtime,
30361
- idFactory: randomUUID32
29688
+ idFactory: randomUUID31
30362
29689
  });
30363
29690
  const runService = new ProjectTaskRunService({
30364
29691
  tasks: input.taskStore,
@@ -31182,7 +30509,7 @@ var collectionRegistration = {
31182
30509
 
31183
30510
  // src/export_registrations/channel-installation.registration.ts
31184
30511
  import { getStoreLattice as getStoreLattice35 } from "@axiom-lattice/core";
31185
- import { randomUUID as randomUUID33 } from "crypto";
30512
+ import { randomUUID as randomUUID32 } from "crypto";
31186
30513
  var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
31187
30514
  var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat", "wecom"]);
31188
30515
  function isPublicChannel(channel) {
@@ -31344,7 +30671,7 @@ var channelInstallationRegistration = {
31344
30671
  }
31345
30672
  id3 = sourceId;
31346
30673
  } else {
31347
- id3 = randomUUID33();
30674
+ id3 = randomUUID32();
31348
30675
  }
31349
30676
  await store2.createInstallation(tenantId, id3, createInput);
31350
30677
  return { newId: id3 };
@@ -31792,50 +31119,6 @@ var ChannelAdapterRegistry = class {
31792
31119
  }
31793
31120
  };
31794
31121
 
31795
- // src/router/middlewares/channelSharedState.ts
31796
- import { createClient as createClient2 } from "redis";
31797
- var clientPromise;
31798
- async function redis() {
31799
- const url = process.env.REDIS_URL?.trim();
31800
- if (!url) return void 0;
31801
- if (!clientPromise) {
31802
- clientPromise = (async () => {
31803
- const client = createClient2({ url });
31804
- client.on("error", () => void 0);
31805
- await client.connect();
31806
- return client;
31807
- })();
31808
- }
31809
- try {
31810
- const client = await clientPromise;
31811
- return client.isOpen ? client : void 0;
31812
- } catch {
31813
- clientPromise = void 0;
31814
- return void 0;
31815
- }
31816
- }
31817
- async function claimDedupKey(key, ttlMs) {
31818
- try {
31819
- const client = await redis();
31820
- if (!client) return void 0;
31821
- const result = await client.set(key, "1", { NX: true, PX: ttlMs });
31822
- return result === "OK";
31823
- } catch {
31824
- return void 0;
31825
- }
31826
- }
31827
- async function incrementRateCounter(key, windowMs) {
31828
- try {
31829
- const client = await redis();
31830
- if (!client) return void 0;
31831
- const count = await client.incr(key);
31832
- if (count === 1) await client.pExpire(key, windowMs);
31833
- return count;
31834
- } catch {
31835
- return void 0;
31836
- }
31837
- }
31838
-
31839
31122
  // src/router/middlewares/deduplication.ts
31840
31123
  var processedMessages = /* @__PURE__ */ new Map();
31841
31124
  function createDeduplicationMiddleware(ttlMs = 5 * 60 * 1e3) {
@@ -31948,7 +31231,7 @@ function createAuditLoggerMiddleware() {
31948
31231
  import { setBindingRegistry, setMenuRegistry, setEvalRunService } from "@axiom-lattice/core";
31949
31232
 
31950
31233
  // src/channels/wecom/WecomChannelAdapter.ts
31951
- import { randomUUID as randomUUID34 } from "crypto";
31234
+ import { randomUUID as randomUUID33 } from "crypto";
31952
31235
  import { z as z13 } from "zod";
31953
31236
  import * as WecomAiBot from "@wecom/aibot-node-sdk";
31954
31237
 
@@ -32312,7 +31595,7 @@ function streamIdFor(replyTarget) {
32312
31595
  const key = replyKeyForTarget(replyTarget);
32313
31596
  let streamId = streamIdsByReqId.get(key);
32314
31597
  if (!streamId) {
32315
- streamId = randomUUID34();
31598
+ streamId = randomUUID33();
32316
31599
  streamIdsByReqId.set(key, streamId);
32317
31600
  if (streamIdsByReqId.size > MAX_TRACKED_STREAMS) {
32318
31601
  const oldest = streamIdsByReqId.keys().next().value;
@@ -32904,7 +32187,7 @@ import {
32904
32187
  agentInstanceManager as agentInstanceManager13,
32905
32188
  createSandboxProvider,
32906
32189
  TokenCache,
32907
- mcpManager as mcpManager3,
32190
+ mcpManager as mcpManager2,
32908
32191
  convertToConnection as convertToConnection2,
32909
32192
  createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2,
32910
32193
  ChannelConnectionLeader,
@@ -32924,7 +32207,7 @@ import {
32924
32207
  COLLECTION_CHANGED_TOPIC,
32925
32208
  vectorStoreLatticeManager as vectorStoreLatticeManager2,
32926
32209
  observabilityLatticeManager,
32927
- getSandboxPluginCatalog as getSandboxPluginCatalog3,
32210
+ getSandboxPluginCatalog as getSandboxPluginCatalog2,
32928
32211
  getSandBoxManager as getSandBoxManager4,
32929
32212
  installPluginHost,
32930
32213
  ThreadExecutionSupervisor,
@@ -32935,7 +32218,7 @@ import {
32935
32218
  } from "@axiom-lattice/core";
32936
32219
 
32937
32220
  // src/services/chunk/redisAppendLog.ts
32938
- import { createClient as createClient3 } from "redis";
32221
+ import { createClient as createClient2 } from "redis";
32939
32222
  var VALUE_FIELD = "v";
32940
32223
  var START_ID = "0-0";
32941
32224
  var ChunkLogCorruptionError = class extends Error {
@@ -32952,7 +32235,7 @@ var ChunkLogCorruptionError = class extends Error {
32952
32235
  }
32953
32236
  };
32954
32237
  async function createRedisAppendLog(url, options = {}) {
32955
- const client = createClient3({
32238
+ const client = createClient2({
32956
32239
  url,
32957
32240
  disableOfflineQueue: true,
32958
32241
  socket: {
@@ -33142,7 +32425,7 @@ function readField(fields, name) {
33142
32425
  }
33143
32426
 
33144
32427
  // src/services/run-cancel/RedisRunCancelNotifier.ts
33145
- import { createClient as createClient4 } from "redis";
32428
+ import { createClient as createClient3 } from "redis";
33146
32429
  import {
33147
32430
  runCancelSignalSchema as runCancelSignalSchema2
33148
32431
  } from "@axiom-lattice/protocols";
@@ -33543,7 +32826,7 @@ function createRedisRunCancelNotifier(options) {
33543
32826
  });
33544
32827
  }
33545
32828
  function createNodeRedisClient2(url) {
33546
- const client = createClient4({
32829
+ const client = createClient3({
33547
32830
  url,
33548
32831
  disableOfflineQueue: true,
33549
32832
  socket: {
@@ -33607,7 +32890,7 @@ import {
33607
32890
  snapshotExactRecord as snapshotExactRecord15,
33608
32891
  MessageChunkTypes as MessageChunkTypes4
33609
32892
  } from "@axiom-lattice/protocols";
33610
- import { randomUUID as randomUUID35 } from "crypto";
32893
+ import { randomUUID as randomUUID34 } from "crypto";
33611
32894
 
33612
32895
  // src/services/channel-reply/channelReplyRuntimeRegistry.ts
33613
32896
  var active;
@@ -33962,7 +33245,7 @@ var _MessageRouter = class _MessageRouter {
33962
33245
  }
33963
33246
  if (!threadId) {
33964
33247
  const threadStore = getStoreLattice39("default", "thread").store;
33965
- const newThreadId = randomUUID35();
33248
+ const newThreadId = randomUUID34();
33966
33249
  console.log({
33967
33250
  event: "dispatch:thread:create",
33968
33251
  agentId,
@@ -34001,7 +33284,7 @@ var _MessageRouter = class _MessageRouter {
34001
33284
  senderId: message.sender.id,
34002
33285
  contentLength: message.content.text.length
34003
33286
  }, "Dispatching to agent");
34004
- const inputMessageId = randomUUID35();
33287
+ const inputMessageId = randomUUID34();
34005
33288
  const replyTarget = internalRoom ? correlatedRoomReplyTarget(message.replyTarget, inputMessageId) : message.replyTarget;
34006
33289
  const replyAdapter = replyTarget ? this.adapterRegistry.get(replyTarget.adapterChannel) : void 0;
34007
33290
  if (options.trustedMessageOptions && (!this.trustedDispatcher || !replyTarget || !replyAdapter)) {
@@ -34198,7 +33481,7 @@ var _MessageRouter = class _MessageRouter {
34198
33481
  };
34199
33482
  const runLedger = getStoreLattice39("default", "agentRun").store;
34200
33483
  const jobStore = getStoreLattice39("default", "channelReplyJob").store;
34201
- const runId = randomUUID35();
33484
+ const runId = randomUUID34();
34202
33485
  await jobStore.create({
34203
33486
  runId,
34204
33487
  tenantId,
@@ -34850,14 +34133,14 @@ async function prepareProjectTaskRuntime(runtime, log2) {
34850
34133
  import { setPluginDeploymentService } from "@axiom-lattice/core";
34851
34134
 
34852
34135
  // src/services/plugins/PluginDeploymentService.ts
34853
- import { randomUUID as randomUUID36 } from "crypto";
34136
+ import { randomUUID as randomUUID35 } from "crypto";
34854
34137
  import {
34855
34138
  PLUGIN_HOST_DIR,
34856
34139
  PluginDeploymentRuntimeRegistry
34857
34140
  } from "@axiom-lattice/core";
34858
34141
 
34859
34142
  // src/services/plugins/pluginDeploymentNaming.ts
34860
- import { createHash as createHash11 } from "crypto";
34143
+ import { createHash as createHash10 } from "crypto";
34861
34144
  var PLUGIN_GUEST_PORT = 8080;
34862
34145
  var SANDBOX_NAME_HASH_LENGTH = 16;
34863
34146
  function pluginSandboxName(tenantId, pluginType) {
@@ -34867,7 +34150,7 @@ function pluginSandboxName(tenantId, pluginType) {
34867
34150
  if (!pluginType || typeof pluginType !== "string") {
34868
34151
  throw new Error("pluginSandboxName: pluginType must be a non-empty string");
34869
34152
  }
34870
- const hash = createHash11("sha256").update(`${tenantId}\0${pluginType}`).digest("hex").slice(0, SANDBOX_NAME_HASH_LENGTH);
34153
+ const hash = createHash10("sha256").update(`${tenantId}\0${pluginType}`).digest("hex").slice(0, SANDBOX_NAME_HASH_LENGTH);
34871
34154
  return `plugin-${hash}`;
34872
34155
  }
34873
34156
 
@@ -34994,7 +34277,7 @@ var PluginDeploymentService = class {
34994
34277
  const failedAt = this.nowIso();
34995
34278
  await this.store.upsert(
34996
34279
  this.buildRecord({
34997
- id: existing?.id ?? randomUUID36(),
34280
+ id: existing?.id ?? randomUUID35(),
34998
34281
  tenantId,
34999
34282
  pluginType,
35000
34283
  pluginVersion: manifest.version,
@@ -35016,7 +34299,7 @@ var PluginDeploymentService = class {
35016
34299
  );
35017
34300
  }
35018
34301
  const record = this.buildRecord({
35019
- id: existing?.id ?? randomUUID36(),
34302
+ id: existing?.id ?? randomUUID35(),
35020
34303
  tenantId,
35021
34304
  pluginType,
35022
34305
  pluginVersion: manifest.version,
@@ -37444,7 +36727,7 @@ function createAgentRunRetentionJob(input) {
37444
36727
  }
37445
36728
 
37446
36729
  // src/services/run-task/RedisRunTaskWake.ts
37447
- import { createClient as createClient5 } from "redis";
36730
+ import { createClient as createClient4 } from "redis";
37448
36731
  var RUN_TASK_WAKE_CHANNEL = "run-task:wake";
37449
36732
  var DEFAULT_OPERATION_TIMEOUT_MS = 2e3;
37450
36733
  var DEFAULT_RETRY_DELAYS_MS = [500, 2e3, 5e3, 1e4];
@@ -37837,7 +37120,7 @@ function createConfiguredRedisRunTaskWake(options) {
37837
37120
  return createRedisRunTaskWake({ ...options, url });
37838
37121
  }
37839
37122
  function createNodeRedisClient3(url) {
37840
- const client = createClient5({
37123
+ const client = createClient4({
37841
37124
  url,
37842
37125
  disableOfflineQueue: true,
37843
37126
  socket: {
@@ -38928,7 +38211,7 @@ async function restoreMcpConnections() {
38928
38211
  logger8.info(`Restoring ${configs.length} MCP server connections...`);
38929
38212
  const deps = {
38930
38213
  store: store2,
38931
- manager: mcpManager3,
38214
+ manager: mcpManager2,
38932
38215
  convertToConnection: convertToConnection2
38933
38216
  };
38934
38217
  const results = await Promise.allSettled(
@@ -38966,6 +38249,8 @@ var start = async (config) => {
38966
38249
  logger8 = loggerLattice.client;
38967
38250
  }
38968
38251
  app.decorate("loggerLattice", loggerLattice);
38252
+ const rateLimitService = createRateLimitService(config?.rateLimits);
38253
+ setRateLimitService(rateLimitService);
38969
38254
  const observabilityProvider = observabilityLatticeManager.getDefault();
38970
38255
  if (observabilityProvider) {
38971
38256
  try {
@@ -39205,9 +38490,9 @@ var start = async (config) => {
39205
38490
  projectRoomRealtimeRuntime
39206
38491
  });
39207
38492
  try {
39208
- const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await import("./A2AAuthService-7UGQ7GMW.mjs");
39209
- const { registerA2AStandardRoutes } = await import("./a2a-standard-VL5I7B4W.mjs");
39210
- const a2a = await import("./a2a-BHBLACJV.mjs");
38493
+ const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await import("./A2AAuthService-UKN53ZGP.mjs");
38494
+ const { registerA2AStandardRoutes } = await import("./a2a-standard-MD7WNDTP.mjs");
38495
+ const a2a = await import("./a2a-YLKZE5JG.mjs");
39211
38496
  const a2aKeyStore = getStoreLattice42("default", "a2aApiKey").store;
39212
38497
  const assistantStore = getStoreLattice42("default", "assistant").store;
39213
38498
  const projectStore = getStoreLattice42("default", "project").store;
@@ -39236,18 +38521,17 @@ var start = async (config) => {
39236
38521
  });
39237
38522
  }
39238
38523
  try {
39239
- const { registerOpenRoutes } = await import("./open-7ED27I5B.mjs");
39240
- const { registerOpenSessionRoutes } = await import("./open-session-2Y7XJJW2.mjs");
39241
- const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
39242
- const { registerOpenFilesRoutes } = await import("./open-files-V3R2BFTV.mjs");
38524
+ const { registerOpenRoutes } = await import("./open-27ZPLBIW.mjs");
38525
+ const { registerOpenSessionRoutes } = await import("./open-session-CLZT3HVG.mjs");
38526
+ const { registerOpenCatalogRoutes } = await import("./open-catalog-NVGP6SQP.mjs");
38527
+ const { registerOpenFilesRoutes } = await import("./open-files-JPB2NBL6.mjs");
39243
38528
  const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
39244
- const { registerOpenExposureRoutes } = await import("./open-exposure-I3DN2X4E.mjs");
39245
- const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
39246
- const { OpenRateLimiter } = await import("./openRateLimiter-XEOL3A5G.mjs");
38529
+ const { registerOpenExposureRoutes } = await import("./open-exposure-HGYT5VFC.mjs");
38530
+ const { openLattice } = await import("./openLattice-D5YTSZBY.mjs");
39247
38531
  const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
39248
38532
  const { createOpenSandboxProvider } = await import("./sandboxProvider-FILMFVIM.mjs");
39249
38533
  const { createAgentProvider } = await import("./agentProvider-X4FVQPKW.mjs");
39250
- const { registerExposedPluginTools } = await import("./registerExposedPlugins-6GCOE64C.mjs");
38534
+ const { registerExposedPluginTools } = await import("./registerExposedPlugins-2JGXSMAL.mjs");
39251
38535
  const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
39252
38536
  openLattice.register(createKbProvider(new CollectionService2()));
39253
38537
  openLattice.register(createOpenSandboxProvider());
@@ -39263,18 +38547,7 @@ var start = async (config) => {
39263
38547
  })
39264
38548
  );
39265
38549
  await registerExposedPluginTools();
39266
- const openRateLimitPerMinute = Number(
39267
- process.env.OPEN_RATE_LIMIT_PER_MINUTE
39268
- );
39269
- const openAgentRateLimitPerMinute = Number(
39270
- process.env.OPEN_AGENT_RATE_LIMIT_PER_MINUTE
39271
- );
39272
- registerOpenRoutes(app, {
39273
- rateLimiter: new OpenRateLimiter({
39274
- ...Number.isFinite(openRateLimitPerMinute) && openRateLimitPerMinute > 0 ? { defaultPerMinute: openRateLimitPerMinute } : {},
39275
- ...Number.isFinite(openAgentRateLimitPerMinute) && openAgentRateLimitPerMinute > 0 ? { agentPerMinute: openAgentRateLimitPerMinute } : {}
39276
- })
39277
- });
38550
+ registerOpenRoutes(app, { rateLimits: rateLimitService });
39278
38551
  registerOpenSessionRoutes(app);
39279
38552
  registerOpenCatalogRoutes(app);
39280
38553
  registerOpenFilesRoutes(app);
@@ -39287,7 +38560,7 @@ var start = async (config) => {
39287
38560
  });
39288
38561
  }
39289
38562
  try {
39290
- const { registerGatewaySandboxOpenCredentialProvider } = await import("./sandboxOpenCredentialProvider-AT227D4N.mjs");
38563
+ const { registerGatewaySandboxOpenCredentialProvider } = await import("./sandboxOpenCredentialProvider-RTYOYRCT.mjs");
39291
38564
  registerGatewaySandboxOpenCredentialProvider((error) => {
39292
38565
  logger8.warn("sandbox open-credential provider failed", { error });
39293
38566
  });
@@ -39319,7 +38592,7 @@ var start = async (config) => {
39319
38592
  wiring: {
39320
38593
  store: pluginDeploymentStore,
39321
38594
  sandboxManager: getSandBoxManager4(),
39322
- catalog: getSandboxPluginCatalog3(),
38595
+ catalog: getSandboxPluginCatalog2(),
39323
38596
  installPluginHost,
39324
38597
  limits: readPluginDeploymentLimits(),
39325
38598
  serviceKey: process.env.MICROSANDBOX_API_KEY,
@@ -39346,7 +38619,7 @@ var start = async (config) => {
39346
38619
  }
39347
38620
  }
39348
38621
  try {
39349
- const { ResourceController } = await import("./resources-DAU5ZIPB.mjs");
38622
+ const { ResourceController } = await import("./resources-YD5DOPGD.mjs");
39350
38623
  const sharedResourceStore = getStoreLattice42("default", "sharedResource").store;
39351
38624
  const cache = new TokenCache();
39352
38625
  const resourceController = new ResourceController({
@@ -39356,6 +38629,13 @@ var start = async (config) => {
39356
38629
  logger: logger8
39357
38630
  });
39358
38631
  registerResourceRoutes(app, resourceController);
38632
+ const { registerMcpAppShareRoutes } = await import("./mcp-app-shares-SHPBJQXS.mjs");
38633
+ const { makeMcpAppHandlers, resolveMcpAppDeps } = await import("./mcp-apps-QVUK6UBA.mjs");
38634
+ registerMcpAppShareRoutes(app, {
38635
+ store: sharedResourceStore,
38636
+ handlers: makeMcpAppHandlers(resolveMcpAppDeps()),
38637
+ rateLimits: rateLimitService
38638
+ });
39359
38639
  logger8.info("Resource share routes registered");
39360
38640
  } catch (err) {
39361
38641
  logger8.warn("Resource share infrastructure unavailable", {
@@ -39435,7 +38715,6 @@ var start = async (config) => {
39435
38715
  "assistant-changed": (payload) => handleAssistantChanged(payload, async (tenantId) => {
39436
38716
  await agentLatticeManager6.initializeStoredAssistantsForTenant(tenantId);
39437
38717
  invalidateAgentReflectionCache(tenantId);
39438
- invalidatePluginReflectionCache(tenantId);
39439
38718
  }),
39440
38719
  [AGENT_LIFECYCLE_TOPIC]: async (payload) => {
39441
38720
  handleAgentLifecycle(payload);
@@ -39443,7 +38722,7 @@ var start = async (config) => {
39443
38722
  [A2A_KEY_CHANGED_TOPIC]: (payload) => handleA2AKeyChanged(payload, () => refreshStoreKeyMap()),
39444
38723
  [MCP_CONFIG_CHANGED_TOPIC]: (payload) => reconcileMcpConfigChange(payload, {
39445
38724
  store: getStoreLattice42("default", "mcp").store,
39446
- manager: mcpManager3,
38725
+ manager: mcpManager2,
39447
38726
  convertToConnection: convertToConnection2
39448
38727
  }),
39449
38728
  [COLLECTION_CHANGED_TOPIC]: async (payload) => {
@@ -39455,8 +38734,7 @@ var start = async (config) => {
39455
38734
  [PLUGIN_CATALOG_CHANGED_TOPIC]: async (payload) => {
39456
38735
  const tenantId = payload?.tenantId;
39457
38736
  if (typeof tenantId === "string" && tenantId.length > 0) {
39458
- getSandboxPluginCatalog3().invalidate(tenantId);
39459
- invalidatePluginReflectionCache(tenantId);
38737
+ getSandboxPluginCatalog2().invalidate(tenantId);
39460
38738
  invalidateAgentReflectionCache(tenantId);
39461
38739
  await getMcpUiResourceResolver()?.invalidateTenantPlugins(tenantId);
39462
38740
  }