@axiom-lattice/gateway 8.2.0 → 8.4.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 (111) hide show
  1. package/dist/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
  2. package/dist/{a2a-BHBLACJV.mjs → a2a-TDZ3TT6H.mjs} +3 -3
  3. package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-INVCUSAW.mjs} +39 -2
  4. package/dist/a2a-standard-INVCUSAW.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-HYYLYIMH.mjs → chunk-OBODBHIS.mjs} +2 -2
  16. package/dist/chunk-T2HIWZGP.mjs +580 -0
  17. package/dist/chunk-T2HIWZGP.mjs.map +1 -0
  18. package/dist/chunk-Y7OVVPVU.mjs +13 -0
  19. package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
  20. package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
  21. package/dist/chunk-YR32MWEJ.mjs.map +1 -0
  22. package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
  23. package/dist/embed/{embed-page-AgentDetailSideApp-WDDE7BEH.js → embed-page-AgentDetailSideApp-TQEJK6ZF.js} +1 -1
  24. package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
  25. package/dist/embed/{embed-page-AgentGovernanceWorkbench-7YPU3QB2.js → embed-page-AgentGovernanceWorkbench-II2MRGCG.js} +1 -1
  26. package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-7OANGQXY.js} +1 -1
  27. package/dist/embed/embed-page-MessageGraph-KJPQWTHE.js +6 -0
  28. package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
  29. package/dist/embed/{embed-page-ReactChart-D7RZQB2H.js → embed-page-ReactChart-2CAIM4JD.js} +1 -1
  30. package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
  31. package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-243MXRXC.js} +1 -1
  32. package/dist/embed/{embed-page-TasksSideApp-KD4S22SL.js → embed-page-TasksSideApp-OA7CULHS.js} +1 -1
  33. package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
  34. package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-Z4424X3J.js} +1 -1
  35. package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
  36. package/dist/embed/{embed-page-TopologyRuntimeView-Z7MY6U5T.js → embed-page-TopologyRuntimeView-THTCZUS7.js} +1 -1
  37. package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
  38. package/dist/embed/{embed-page-WorkflowAutomationView-4MY4HG7R.js → embed-page-WorkflowAutomationView-YDIDG35E.js} +1 -1
  39. package/dist/embed/{embed-page-chunk-4XKCMM5C.js → embed-page-chunk-7ULTMRM2.js} +1 -1
  40. package/dist/embed/{embed-page-chunk-GSUSX54K.js → embed-page-chunk-FE6KYRCO.js} +1 -1
  41. package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-FZX4A32D.js} +5 -5
  42. package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-JMP7EQEF.js} +1 -1
  43. package/dist/embed/{embed-page-chunk-EM6VGUZ2.js → embed-page-chunk-L2EGVVJS.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-HZZ5HNTF.js → embed-page-chunk-LDQGH44H.js} +2 -2
  45. package/dist/embed/{embed-page-chunk-BZKAPY7M.js → embed-page-chunk-OMN6G52V.js} +1 -1
  46. package/dist/embed/{embed-page-chunk-G5MXFDNZ.js → embed-page-chunk-RKQJBKN2.js} +1 -1
  47. package/dist/embed/{embed-page-chunk-G5PDHDBZ.js → embed-page-chunk-VYS3TAK4.js} +1 -1
  48. package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-W5OP2CZG.js} +2 -2
  49. package/dist/embed/embed-page-chunk-Z6KWW64I.js +6837 -0
  50. package/dist/embed/{embed-page-en-E35GE7TA.js → embed-page-en-D4M3DPFT.js} +1 -1
  51. package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
  52. package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
  53. package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
  54. package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
  55. package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
  56. package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
  57. package/dist/embed/{embed-page-zh-ZLK3GLM3.js → embed-page-zh-NEXLMDHF.js} +1 -1
  58. package/dist/embed/embed-page.css +1 -1
  59. package/dist/embed/embed-page.js +3 -3
  60. package/dist/embed/mcp-app-page.js +61 -0
  61. package/dist/index.d.mts +29 -0
  62. package/dist/index.d.ts +29 -0
  63. package/dist/index.js +2609 -1491
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +174 -705
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
  68. package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
  69. package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
  70. package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
  71. package/dist/open-27ZPLBIW.mjs.map +1 -0
  72. package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
  73. package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
  74. package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-BPLYUIX6.mjs} +4 -4
  75. package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
  76. package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
  77. package/dist/open-session-CLZT3HVG.mjs.map +1 -0
  78. package/dist/openLattice-D5YTSZBY.mjs +28 -0
  79. package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
  80. package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
  81. package/dist/resources-YD5DOPGD.mjs.map +1 -0
  82. package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
  83. package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
  84. package/package.json +10 -8
  85. package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
  86. package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
  87. package/dist/chunk-GL7RV5L3.mjs.map +0 -1
  88. package/dist/chunk-GPHOYR3X.mjs.map +0 -1
  89. package/dist/chunk-GXZFO3VU.mjs +0 -42
  90. package/dist/chunk-GXZFO3VU.mjs.map +0 -1
  91. package/dist/chunk-HVIKCUOF.mjs.map +0 -1
  92. package/dist/chunk-L6MBEEII.mjs.map +0 -1
  93. package/dist/embed/embed-page-MessageGraph-LNNMCH6I.js +0 -6
  94. package/dist/embed/embed-page-chunk-CFFDP526.js +0 -6835
  95. package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
  96. package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
  97. package/dist/open-7ED27I5B.mjs.map +0 -1
  98. package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
  99. package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
  100. package/dist/openLattice-Q6M5S6CY.mjs +0 -18
  101. package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
  102. package/dist/resources-DAU5ZIPB.mjs.map +0 -1
  103. package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
  104. /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
  105. /package/dist/{a2a-BHBLACJV.mjs.map → a2a-TDZ3TT6H.mjs.map} +0 -0
  106. /package/dist/{chunk-HYYLYIMH.mjs.map → chunk-OBODBHIS.mjs.map} +0 -0
  107. /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
  108. /package/dist/{open-exposure-I3DN2X4E.mjs.map → open-exposure-BPLYUIX6.mjs.map} +0 -0
  109. /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
  110. /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
  111. /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";
@@ -38,16 +48,24 @@ import "./chunk-EXPERMZR.mjs";
38
48
  import {
39
49
  CollectionService
40
50
  } from "./chunk-PWHUWFXL.mjs";
51
+ import {
52
+ getMcpUiResourceResolver,
53
+ listTenantPlugins,
54
+ registerMcpAppRoutes,
55
+ resolveTenantPluginByType
56
+ } from "./chunk-T2HIWZGP.mjs";
57
+ import "./chunk-XUWD4VKY.mjs";
41
58
  import {
42
59
  A2A_KEY_CHANGED_TOPIC,
43
60
  handleA2AKeyChanged,
44
61
  publishA2AKeyChanged,
45
62
  refreshStoreKeyMap
46
- } from "./chunk-HYYLYIMH.mjs";
63
+ } from "./chunk-OBODBHIS.mjs";
47
64
  import {
48
65
  A2AAuthService,
49
66
  parseEnvKeys
50
- } from "./chunk-GPHOYR3X.mjs";
67
+ } from "./chunk-LCM4OQTI.mjs";
68
+ import "./chunk-MFWTGNQR.mjs";
51
69
  import {
52
70
  createOrphanRecoveryRuntime,
53
71
  createTaskRunLiveness,
@@ -59,14 +77,6 @@ import {
59
77
  resolveTopCaller,
60
78
  runOrphanRecovery
61
79
  } 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
80
  import "./chunk-NFEGQTCC.mjs";
71
81
 
72
82
  // src/index.ts
@@ -10003,14 +10013,6 @@ async function importApply(request, reply) {
10003
10013
 
10004
10014
  // src/controllers/middleware-types.ts
10005
10015
  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
10016
  async function collectMiddlewareMetas(tenantId) {
10015
10017
  return (await listTenantPlugins(tenantId)).map(serializePluginMeta);
10016
10018
  }
@@ -12018,15 +12020,25 @@ var ProjectAccessService = class {
12018
12020
  input.projectId,
12019
12021
  input.userId
12020
12022
  );
12021
- if (membership?.status === "active") {
12023
+ if (project.kind === "personal") {
12024
+ if (!this.isActiveMembership(membership, input)) this.accessDenied();
12022
12025
  this.requireCapability(membership, input.capability);
12023
12026
  return { project, membership };
12024
12027
  }
12028
+ if (this.isActiveMembership(membership, input) && this.hasCapability(membership, input.capability)) {
12029
+ return { project, membership };
12030
+ }
12031
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
12032
+ if (this.isActiveMembership(membership, input)) {
12033
+ if (tenantAdmin) return { project, membership: tenantAdmin };
12034
+ this.requireCapability(membership, input.capability);
12035
+ }
12025
12036
  const memberships = await this.deps.membershipStore.list(input.tenantId, input.projectId);
12026
- if (memberships.length !== 0) this.accessDenied();
12027
- if (project.kind === "personal") this.accessDenied();
12028
- const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
12029
- if (tenantLink?.role !== "owner" && tenantLink?.role !== "admin") this.accessDenied();
12037
+ if (memberships.length !== 0) {
12038
+ if (tenantAdmin) return { project, membership: tenantAdmin };
12039
+ this.accessDenied();
12040
+ }
12041
+ if (!tenantAdmin) this.accessDenied();
12030
12042
  let claim;
12031
12043
  try {
12032
12044
  claim = await this.deps.membershipStore.createInitialOwner({
@@ -12069,9 +12081,19 @@ var ProjectAccessService = class {
12069
12081
  input.projectId,
12070
12082
  input.userId
12071
12083
  );
12072
- if (!membership || membership.tenantId !== input.tenantId || membership.projectId !== input.projectId || membership.userId !== input.userId || membership.status !== "active") this.accessDenied();
12073
- this.requireCapability(membership, input.capability);
12074
- return { project, membership };
12084
+ const activeMembership = this.isActiveMembership(membership, input) ? membership : void 0;
12085
+ if (project.kind === "personal") {
12086
+ if (!activeMembership) this.accessDenied();
12087
+ this.requireCapability(activeMembership, input.capability);
12088
+ return { project, membership: activeMembership };
12089
+ }
12090
+ if (activeMembership && this.hasCapability(activeMembership, input.capability)) {
12091
+ return { project, membership: activeMembership };
12092
+ }
12093
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
12094
+ if (tenantAdmin) return { project, membership: tenantAdmin };
12095
+ if (activeMembership) this.requireCapability(activeMembership, input.capability);
12096
+ this.accessDenied();
12075
12097
  }
12076
12098
  /**
12077
12099
  * Authorizes roster visibility for any active participant (owner/admin/member/viewer).
@@ -12093,6 +12115,9 @@ var ProjectAccessService = class {
12093
12115
  input.userId
12094
12116
  );
12095
12117
  if (membership?.status === "active") {
12118
+ if (this.hasCapability(membership, input.capability)) return { project, membership };
12119
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
12120
+ if (tenantAdmin) return { project, membership: tenantAdmin };
12096
12121
  this.requireCapability(membership, input.capability);
12097
12122
  return { project, membership };
12098
12123
  }
@@ -12107,6 +12132,9 @@ var ProjectAccessService = class {
12107
12132
  input.userId
12108
12133
  );
12109
12134
  if (membership?.status === "active") {
12135
+ if (this.hasCapability(membership, input.capability)) return { project, membership };
12136
+ const tenantAdmin = await this.tenantAdminMembership(project, input);
12137
+ if (tenantAdmin) return { project, membership: tenantAdmin };
12110
12138
  this.requireCapability(membership, input.capability);
12111
12139
  return { project, membership };
12112
12140
  }
@@ -12158,8 +12186,29 @@ var ProjectAccessService = class {
12158
12186
  throw error;
12159
12187
  }
12160
12188
  }
12189
+ hasCapability(membership, capability2) {
12190
+ return allowedRoles[capability2].has(membership.role);
12191
+ }
12161
12192
  requireCapability(membership, capability2) {
12162
- if (!allowedRoles[capability2].has(membership.role)) this.accessDenied();
12193
+ if (!this.hasCapability(membership, capability2)) this.accessDenied();
12194
+ }
12195
+ async tenantAdminMembership(project, input) {
12196
+ if (project.kind === "personal") return void 0;
12197
+ const tenantLink = await this.deps.userTenantLinkStore.getLink(input.userId, input.tenantId);
12198
+ if (tenantLink?.role !== "owner" && tenantLink?.role !== "admin") return void 0;
12199
+ return {
12200
+ id: `tenant-admin:${input.userId}`,
12201
+ tenantId: input.tenantId,
12202
+ projectId: project.id,
12203
+ userId: input.userId,
12204
+ role: "admin",
12205
+ status: "active",
12206
+ joinedAt: project.createdAt,
12207
+ updatedAt: project.updatedAt
12208
+ };
12209
+ }
12210
+ isActiveMembership(membership, input) {
12211
+ return membership != null && membership.tenantId === input.tenantId && membership.projectId === input.projectId && membership.userId === input.userId && membership.status === "active";
12163
12212
  }
12164
12213
  accessDenied() {
12165
12214
  throw new ProjectCollaborationError("PROJECT_ACCESS_DENIED", 403, "Project access denied");
@@ -13679,562 +13728,6 @@ function registerMcpServerConfigRoutes(app2) {
13679
13728
  app2.post("/api/mcp-servers/test-tools", testMcpServerTools);
13680
13729
  }
13681
13730
 
13682
- // src/routes/mcp-apps.ts
13683
- import {
13684
- parseMcpUiRef
13685
- } from "@axiom-lattice/protocols";
13686
- import {
13687
- getSandboxPluginCatalog as getSandboxPluginCatalog2,
13688
- mcpManager as mcpManager2,
13689
- resolvePluginConnections,
13690
- verifyMcpUiScope
13691
- } from "@axiom-lattice/core";
13692
-
13693
- // src/services/mcp-ui/UiResourceResolver.ts
13694
- import { createHash as createHash3 } from "crypto";
13695
- var DEFAULT_TTL_MS = 5 * 60 * 1e3;
13696
- function publicFieldsOf(payload) {
13697
- return {
13698
- mimeType: payload.mimeType,
13699
- html: payload.html,
13700
- ...payload.csp !== void 0 ? { csp: payload.csp } : {},
13701
- ...payload.permissions !== void 0 ? { permissions: payload.permissions } : {},
13702
- ...payload.prefersBorder !== void 0 ? { prefersBorder: payload.prefersBorder } : {}
13703
- };
13704
- }
13705
- var UiResourceResolver = class {
13706
- constructor(deps) {
13707
- this.deps = deps;
13708
- this.inFlight = /* @__PURE__ */ new Map();
13709
- }
13710
- keyOf(ref) {
13711
- return ref.kind === "mcp" ? `mcp\0${ref.serverKey}\0${ref.resourceUri}` : `plugin\0${ref.pluginType}\0${ref.resource}`;
13712
- }
13713
- fullKey(tenantId, ref) {
13714
- return `${tenantId}\0${this.keyOf(ref)}`;
13715
- }
13716
- /**
13717
- * Resolve a UI resource for a tenant.
13718
- *
13719
- * @param tenantId Tenant scope.
13720
- * @param ref The fence ref.
13721
- * @returns Public UI resource payload (no cache metadata).
13722
- */
13723
- async resolve(tenantId, ref) {
13724
- const key = this.fullKey(tenantId, ref);
13725
- const ttl = this.deps.ttlMs ?? DEFAULT_TTL_MS;
13726
- const cached = await this.deps.store.get(key);
13727
- if (cached && Date.now() - cached.fetchedAt < ttl) return publicFieldsOf(cached);
13728
- const existing = this.inFlight.get(key);
13729
- if (existing) return existing;
13730
- const promise = this.fetchAndCache(tenantId, ref, key, cached);
13731
- this.inFlight.set(key, promise);
13732
- try {
13733
- return await promise;
13734
- } finally {
13735
- this.inFlight.delete(key);
13736
- }
13737
- }
13738
- /**
13739
- * Evict the cached entry for a tenant/ref.
13740
- *
13741
- * @param tenantId Tenant scope.
13742
- * @param ref The fence ref.
13743
- */
13744
- async invalidate(tenantId, ref) {
13745
- await this.deps.store.delete(this.fullKey(tenantId, ref));
13746
- }
13747
- /**
13748
- * Evict every cached plugin UI resource for a tenant.
13749
- *
13750
- * @param tenantId Tenant whose plugin UI resources to drop.
13751
- * @remarks Called when a tenant's plugin catalog changes so an edited
13752
- * `ui/*.html` is re-fetched instead of being served from the TTL cache
13753
- * (which is keyed by ref identity, not content).
13754
- */
13755
- async invalidateTenantPlugins(tenantId) {
13756
- await this.deps.store.deleteByPrefix(`${tenantId}\0plugin\0`);
13757
- }
13758
- async fetchAndCache(tenantId, ref, key, cached) {
13759
- try {
13760
- const fresh = ref.kind === "mcp" ? await this.deps.fetchMcp(tenantId, ref.serverKey, ref.resourceUri) : await this.deps.fetchPlugin(tenantId, ref.pluginType, ref.resource);
13761
- const entry = {
13762
- ...publicFieldsOf(fresh),
13763
- hash: createHash3("sha256").update(fresh.html).digest("hex"),
13764
- fetchedAt: Date.now()
13765
- };
13766
- await this.deps.store.set(key, entry);
13767
- return publicFieldsOf(fresh);
13768
- } catch (error) {
13769
- if (cached) return { ...publicFieldsOf(cached), stale: true };
13770
- throw error;
13771
- }
13772
- }
13773
- };
13774
-
13775
- // src/services/mcp-ui/McpUiResourceCacheStore.ts
13776
- var InMemoryMcpUiResourceCacheStore = class {
13777
- constructor() {
13778
- this.map = /* @__PURE__ */ new Map();
13779
- }
13780
- async get(key) {
13781
- return this.map.get(key);
13782
- }
13783
- async set(key, entry) {
13784
- this.map.set(key, entry);
13785
- }
13786
- async delete(key) {
13787
- this.map.delete(key);
13788
- }
13789
- async deleteByPrefix(prefix) {
13790
- for (const key of this.map.keys()) {
13791
- if (key.startsWith(prefix)) this.map.delete(key);
13792
- }
13793
- }
13794
- };
13795
-
13796
- // src/services/mcp-ui/buildCsp.ts
13797
- var DEFAULT_CSP = [
13798
- "default-src 'none'",
13799
- "script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'",
13800
- "style-src 'self' 'unsafe-inline'",
13801
- "img-src 'self' data:",
13802
- "media-src 'self' data:",
13803
- // Fonts are restricted to the document origin; declared `resourceDomains`
13804
- // are the only origins ever added (no `data:` — a base64-embedded font must
13805
- // be served from a declared origin).
13806
- "font-src 'self'",
13807
- "object-src 'none'",
13808
- "connect-src 'none'",
13809
- "frame-src 'none'",
13810
- "base-uri 'self'"
13811
- ].join("; ");
13812
- var ORIGIN_PATTERN = /^(https?|wss?):\/\/(\*\.)?[^\s;,'"*]+$/;
13813
- function declaredOrigins(domains) {
13814
- if (!Array.isArray(domains)) return [];
13815
- return domains.filter((domain) => typeof domain === "string" && ORIGIN_PATTERN.test(domain));
13816
- }
13817
- function buildCsp(csp) {
13818
- if (!csp) return DEFAULT_CSP;
13819
- const resourceOrigins = declaredOrigins(csp.resourceDomains);
13820
- const resource = resourceOrigins.join(" ");
13821
- const withResourceOrigins = (base) => resource ? `${base} ${resource}` : base;
13822
- const img = ["'self'", "data:", ...resourceOrigins].join(" ");
13823
- const connect = declaredOrigins(csp.connectDomains);
13824
- const frame = declaredOrigins(csp.frameDomains);
13825
- const baseUri = declaredOrigins(csp.baseUriDomains);
13826
- const parts = [
13827
- "default-src 'none'",
13828
- withResourceOrigins("script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'"),
13829
- withResourceOrigins("style-src 'self' 'unsafe-inline'"),
13830
- `img-src ${img}`,
13831
- `media-src ${["'self'", "data:", ...resourceOrigins].join(" ")}`,
13832
- withResourceOrigins("font-src 'self'"),
13833
- "object-src 'none'",
13834
- `connect-src ${connect.length ? connect.join(" ") : "'none'"}`,
13835
- `frame-src ${frame.length ? frame.join(" ") : "'none'"}`,
13836
- `base-uri ${baseUri.length ? baseUri.join(" ") : "'self'"}`
13837
- ];
13838
- return parts.join("; ");
13839
- }
13840
-
13841
- // src/services/mcp-ui/sandboxPage.ts
13842
- var DEFAULT_CSP_LITERAL = JSON.stringify(buildCsp(void 0));
13843
- var SANDBOX_HTML = `<!doctype html>
13844
- <html lang="en">
13845
- <head>
13846
- <meta charset="utf-8" />
13847
- <meta name="viewport" content="width=device-width, initial-scale=1" />
13848
- <title>MCP App Sandbox</title>
13849
- <style>html, body { margin: 0; height: 100%; }</style>
13850
- </head>
13851
- <body>
13852
- <script>
13853
- (function () {
13854
- var HOST_READY = "ui/notifications/sandbox-proxy-ready";
13855
- var RESOURCE_READY = "ui/notifications/sandbox-resource-ready";
13856
- var CONTROL_PREFIX = "ui/notifications/sandbox-";
13857
- var DEFAULT_CSP = ${DEFAULT_CSP_LITERAL};
13858
-
13859
- function withCsp(html, csp) {
13860
- var policy = typeof csp === "string" && csp.length > 0 ? csp : DEFAULT_CSP;
13861
- var escaped = policy.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
13862
- var meta = '<meta http-equiv="Content-Security-Policy" content="' + escaped + '">';
13863
- // Insert at the very start (after an optional doctype) so the parser
13864
- // places the meta in the implicit head. Never search for a literal
13865
- // <head>: a resource could hide a decoy inside a comment/string and
13866
- // slip its real head past the splice.
13867
- var doctype = /^\\s*<!doctype[^>]*>/i.exec(html);
13868
- if (doctype) {
13869
- return html.slice(0, doctype[0].length) + meta + html.slice(doctype[0].length);
13870
- }
13871
- return meta + html;
13872
- }
13873
-
13874
- // Size reporter injected into every View document. The host renders the
13875
- // View inside an opaque-origin iframe it cannot measure, so auto-height
13876
- // only happens when the View posts ui/notifications/size-changed. Most
13877
- // hand-written plugin apps never do; this script makes every app report
13878
- // its own content size. It only measures and postMessages \u2014 it never
13879
- // mutates the app's DOM.
13880
- var SIZE_REPORTER = [
13881
- "(function(){",
13882
- "function send(){",
13883
- "try{",
13884
- "var d=document.documentElement,b=document.body;",
13885
- "var w=Math.ceil(Math.max(d?d.scrollWidth:0,b?b.scrollWidth:0));",
13886
- "var h=Math.ceil(Math.max(d?d.scrollHeight:0,b?b.scrollHeight:0));",
13887
- "window.parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/size-changed',params:{width:w,height:h}},'*');",
13888
- "}catch(e){}",
13889
- "}",
13890
- "send();",
13891
- "window.addEventListener('load',send);",
13892
- "if(typeof ResizeObserver!=='undefined'){",
13893
- "var ro=new ResizeObserver(send);",
13894
- "ro.observe(document.documentElement);",
13895
- "if(document.body)ro.observe(document.body);",
13896
- "}else{window.addEventListener('resize',send);}",
13897
- "})();"
13898
- ].join("");
13899
-
13900
- function withSizeReporter(html) {
13901
- // Split the closing tag so this template literal never contains a
13902
- // literal script terminator that would end the sandbox page's own
13903
- // script block during HTML parsing.
13904
- var tag = "<scr" + "ipt>" + SIZE_REPORTER + "<" + "/scr" + "ipt>";
13905
- var lower = html.toLowerCase();
13906
- var bodyEnd = lower.lastIndexOf("</body>");
13907
- if (bodyEnd >= 0) return html.slice(0, bodyEnd) + tag + html.slice(bodyEnd);
13908
- return html + tag;
13909
- }
13910
-
13911
- var inner = null;
13912
-
13913
- window.parent.postMessage({ jsonrpc: "2.0", method: HOST_READY, params: {} }, "*");
13914
-
13915
- window.addEventListener("message", function (event) {
13916
- if (event.source !== window.parent) return;
13917
- var data = event.data;
13918
- if (!data || typeof data !== "object") return;
13919
-
13920
- if (data.method === RESOURCE_READY) {
13921
- var params = data.params || {};
13922
- if (inner && inner.parentNode) inner.parentNode.removeChild(inner);
13923
- inner = document.createElement("iframe");
13924
- inner.setAttribute("sandbox", params.sandbox || "allow-scripts");
13925
- inner.setAttribute("srcdoc", withCsp(withSizeReporter(String(params.html || "")), params.csp));
13926
- inner.style.cssText = "width:100%;height:100%;border:0;display:block";
13927
- document.body.appendChild(inner);
13928
- return;
13929
- }
13930
-
13931
- if (typeof data.method === "string" && data.method.indexOf(CONTROL_PREFIX) === 0) return;
13932
- if (inner && inner.contentWindow) inner.contentWindow.postMessage(data, "*");
13933
- });
13934
-
13935
- window.addEventListener("message", function (event) {
13936
- if (inner && event.source === inner.contentWindow) {
13937
- var data = event.data;
13938
- if (data && typeof data === "object" && typeof data.method === "string"
13939
- && data.method.indexOf(CONTROL_PREFIX) === 0) return;
13940
- window.parent.postMessage(event.data, "*");
13941
- }
13942
- });
13943
- })();
13944
- </script>
13945
- </body>
13946
- </html>
13947
- `;
13948
-
13949
- // src/routes/mcp-apps.ts
13950
- var SANDBOX_HTML_CONTENT_TYPE = "text/html; charset=utf-8";
13951
- var PluginMethodNotSupportedError = class extends Error {
13952
- /**
13953
- * @param method - Unsupported JSON-RPC method.
13954
- */
13955
- constructor(method) {
13956
- super(`plugin backend does not support ${method}`);
13957
- this.name = "PluginMethodNotSupportedError";
13958
- }
13959
- };
13960
- var McpAppScopeError = class extends Error {
13961
- /**
13962
- * @param status - HTTP status to return.
13963
- * @param code - Stable machine-readable denial code.
13964
- */
13965
- constructor(status, code) {
13966
- super(code);
13967
- this.status = status;
13968
- this.code = code;
13969
- this.name = "McpAppScopeError";
13970
- }
13971
- };
13972
- function denyScope(status, code, detail) {
13973
- console.warn(JSON.stringify({ event: "mcp-apps:scope-denied", ...detail, code }));
13974
- return new McpAppScopeError(status, code);
13975
- }
13976
- var ALLOWED_METHODS = /* @__PURE__ */ new Set(["tools/call", "tools/list", "resources/read"]);
13977
- function asRecord(value) {
13978
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
13979
- }
13980
- function makeMcpAppHandlers(deps) {
13981
- return {
13982
- /**
13983
- * Resolve the `mcp_app` UI resource for a parsed ref.
13984
- *
13985
- * @param tenantId Tenant scope.
13986
- * @param rawRef Untrusted ref payload.
13987
- * @returns The resource payload plus a ready-to-use `cspPolicy` string
13988
- * ({@link buildCsp}, always non-empty), or a 400/502 error envelope.
13989
- */
13990
- async resource(tenantId, rawRef) {
13991
- const ref = parseMcpUiRef(rawRef);
13992
- if (!ref) return { status: 400, body: { error: "INVALID_REF" } };
13993
- if (ref.kind === "plugin" && ref.pluginType === "") {
13994
- return { status: 400, body: { error: "INVALID_REF" } };
13995
- }
13996
- try {
13997
- const payload = await deps.resolveUiResource(tenantId, ref);
13998
- return { status: 200, body: { ...payload, cspPolicy: buildCsp(payload.csp) } };
13999
- } catch (error) {
14000
- console.warn(JSON.stringify({
14001
- event: "mcp-apps:resource-failed",
14002
- tenantId,
14003
- kind: ref.kind,
14004
- error: error instanceof Error ? error.message : String(error)
14005
- }));
14006
- return { status: 502, body: { error: "RESOURCE_UNAVAILABLE" } };
14007
- }
14008
- },
14009
- /**
14010
- * Proxy an allow-listed JSON-RPC method for a parsed ref.
14011
- *
14012
- * @param tenantId Tenant scope.
14013
- * @param rawRef Untrusted ref payload.
14014
- * @param method JSON-RPC method name.
14015
- * @param params JSON-RPC params.
14016
- * @param context Request-scoped run context forwarded to `tools/call`.
14017
- * @returns The backend result, or a 400/403/502 error envelope.
14018
- */
14019
- async rpc(tenantId, rawRef, method, params, context) {
14020
- const ref = parseMcpUiRef(rawRef);
14021
- if (!ref) return { status: 400, body: { error: "INVALID_REF" } };
14022
- if (!ALLOWED_METHODS.has(method)) return { status: 403, body: { error: "METHOD_NOT_ALLOWED" } };
14023
- try {
14024
- if (method === "tools/call") {
14025
- const name = typeof params.name === "string" ? params.name : "";
14026
- if (name === "") return { status: 400, body: { error: "INVALID_PARAMS" } };
14027
- if ("pluginType" in ref && ref.pluginType === "") {
14028
- return { status: 400, body: { error: "INVALID_REF" } };
14029
- }
14030
- return {
14031
- status: 200,
14032
- body: await deps.callTool(tenantId, ref, name, asRecord(params.arguments) ?? {}, context)
14033
- };
14034
- }
14035
- if (method === "resources/read") {
14036
- const uri = typeof params.uri === "string" ? params.uri : "";
14037
- if (uri === "") return { status: 400, body: { error: "INVALID_PARAMS" } };
14038
- return { status: 200, body: await deps.readResource(tenantId, ref, uri) };
14039
- }
14040
- return { status: 200, body: { tools: await deps.listTools(tenantId, ref) } };
14041
- } catch (error) {
14042
- if (error instanceof McpAppScopeError) {
14043
- return { status: error.status, body: { error: error.code } };
14044
- }
14045
- if (error instanceof PluginToolScopeError) {
14046
- return { status: 403, body: { error: "PLUGIN_TOOL_SCOPE" } };
14047
- }
14048
- if (error instanceof PluginMethodNotSupportedError) {
14049
- return { status: 403, body: { error: "PLUGIN_METHOD_UNSUPPORTED" } };
14050
- }
14051
- const context2 = { tenantId, kind: ref.kind, method };
14052
- if (method === "resources/read") context2.uri = typeof params.uri === "string" ? params.uri : "";
14053
- if (method === "tools/call") context2.name = typeof params.name === "string" ? params.name : "";
14054
- console.warn(JSON.stringify({
14055
- event: "mcp-apps:rpc-failed",
14056
- ...context2,
14057
- error: error instanceof Error ? error.message : String(error)
14058
- }));
14059
- return { status: 502, body: { error: "RPC_FAILED" } };
14060
- }
14061
- }
14062
- };
14063
- }
14064
- async function fetchPluginUiResource(backend, tenantId, pluginType, resource) {
14065
- const plugin = await backend.resolvePlugin(tenantId, pluginType);
14066
- if (!plugin) throw new Error(`plugin "${pluginType}" not found`);
14067
- const tool = (plugin.meta.tools ?? []).find((t2) => t2.ui?.resource === resource);
14068
- if (!tool?.ui) throw new Error(`plugin "${pluginType}" has no tool declaring ui resource "${resource}"`);
14069
- const inline = plugin.meta.uiResources?.[resource];
14070
- if (inline) {
14071
- return {
14072
- mimeType: inline.mimeType ?? "text/html;profile=mcp-app",
14073
- html: inline.html,
14074
- csp: inline.csp ?? tool.ui.csp,
14075
- permissions: tool.ui.permissions,
14076
- prefersBorder: tool.ui.prefersBorder
14077
- };
14078
- }
14079
- const html = await backend.catalog.readPluginFile(tenantId, pluginType, resource);
14080
- return {
14081
- mimeType: "text/html",
14082
- html,
14083
- csp: tool.ui.csp,
14084
- permissions: tool.ui.permissions,
14085
- prefersBorder: tool.ui.prefersBorder
14086
- };
14087
- }
14088
- async function callPluginTool(backend, tenantId, ref, name, args, context, deps) {
14089
- const scope = ref.scope ? (deps?.verifyScope ?? verifyMcpUiScope)(ref.scope) : null;
14090
- if (ref.scope && !scope) {
14091
- throw denyScope(403, "SCOPE_INVALID", {
14092
- tenantId,
14093
- pluginType: ref.pluginType,
14094
- tool: name
14095
- });
14096
- }
14097
- if (scope) {
14098
- const mismatched = scope.tid !== tenantId || scope.plugin !== ref.pluginType || scope.resource !== ref.resource;
14099
- if (mismatched) {
14100
- throw denyScope(403, "SCOPE_MISMATCH", {
14101
- tenantId,
14102
- pluginType: ref.pluginType,
14103
- tool: name,
14104
- jti: scope.jti,
14105
- threadId: scope.thid
14106
- });
14107
- }
14108
- }
14109
- const manifest = await backend.catalog.getValidatedManifest(tenantId, ref.pluginType);
14110
- const rawConnectionKey = args.connectionKey;
14111
- const explicit = typeof rawConnectionKey === "string" ? rawConnectionKey : void 0;
14112
- const scoped = scope?.ck;
14113
- const connectionRelevant = Boolean(manifest?.connection) || scoped !== void 0;
14114
- if (connectionRelevant && (explicit !== void 0 ? explicit !== scoped : rawConnectionKey !== void 0)) {
14115
- throw denyScope(403, "CONNECTION_NOT_ALLOWED", {
14116
- tenantId,
14117
- pluginType: ref.pluginType,
14118
- tool: name,
14119
- jti: scope?.jti,
14120
- threadId: scope?.thid
14121
- });
14122
- }
14123
- let connection;
14124
- let connectionKey;
14125
- if (scoped !== void 0) {
14126
- const resolveConnections = deps?.resolveConnections ?? resolvePluginConnections;
14127
- const resolved = await resolveConnections(
14128
- ref.pluginType,
14129
- { connections: [scoped] },
14130
- { tenantId }
14131
- );
14132
- if (resolved.length === 0) {
14133
- throw denyScope(403, "CONNECTION_NOT_ALLOWED", {
14134
- tenantId,
14135
- pluginType: ref.pluginType,
14136
- tool: name,
14137
- jti: scope?.jti,
14138
- threadId: scope?.thid
14139
- });
14140
- }
14141
- connection = resolved[0].config;
14142
- connectionKey = resolved[0].key;
14143
- }
14144
- if (manifest?.connection && !connectionKey) {
14145
- throw denyScope(409, "CONNECTION_REQUIRED", {
14146
- tenantId,
14147
- pluginType: ref.pluginType,
14148
- tool: name,
14149
- jti: scope?.jti,
14150
- threadId: scope?.thid
14151
- });
14152
- }
14153
- const identity = scope ? {
14154
- workspaceId: scope.wid,
14155
- projectId: scope.pid,
14156
- threadId: scope.thid,
14157
- assistantId: scope.aid
14158
- } : {
14159
- workspaceId: context?.workspaceId,
14160
- projectId: context?.projectId
14161
- };
14162
- return invokePluginTool(backend, tenantId, ref.pluginType, name, args, {
14163
- ...identity,
14164
- ...connection === void 0 ? {} : { connection }
14165
- });
14166
- }
14167
- function createDefaultPluginBackend() {
14168
- return {
14169
- resolvePlugin: (tenantId, type) => resolveTenantPluginByType(tenantId, type),
14170
- catalog: {
14171
- readPluginFile: (tenantId, pluginType, relPath) => getSandboxPluginCatalog2().readPluginFile(tenantId, pluginType, relPath),
14172
- getValidatedManifest: (tenantId, pluginType) => getSandboxPluginCatalog2().getValidatedManifest(tenantId, pluginType),
14173
- getExecutor: () => getSandboxPluginCatalog2().getExecutor()
14174
- }
14175
- };
14176
- }
14177
- var uiResourceResolverSingleton;
14178
- function getMcpUiResourceResolver() {
14179
- return uiResourceResolverSingleton;
14180
- }
14181
- function resolveMcpAppDeps(pluginBackend = createDefaultPluginBackend()) {
14182
- const uiResourceResolver = new UiResourceResolver({
14183
- store: new InMemoryMcpUiResourceCacheStore(),
14184
- fetchMcp: (tenantId, serverKey, resourceUri) => mcpManager2.readUiResource(tenantId, serverKey, resourceUri),
14185
- fetchPlugin: (tenantId, pluginType, resource) => fetchPluginUiResource(pluginBackend, tenantId, pluginType, resource)
14186
- });
14187
- uiResourceResolverSingleton = uiResourceResolver;
14188
- return {
14189
- resolveUiResource: (tenantId, ref) => uiResourceResolver.resolve(tenantId, ref),
14190
- callTool: (tenantId, ref, name, args, context) => ref.kind === "mcp" ? mcpManager2.callServerTool(tenantId, ref.serverKey, name, args) : callPluginTool(pluginBackend, tenantId, ref, name, args, context),
14191
- // Plugin refs reach these only after `tools/call`; the handler treats the
14192
- // rejection as an unsupported method (403). The branches are live because
14193
- // the pure handler deliberately delegates method policy to the deps.
14194
- readResource: (tenantId, ref, uri) => ref.kind === "mcp" ? mcpManager2.readServerResource(tenantId, ref.serverKey, uri) : Promise.reject(new PluginMethodNotSupportedError("resources/read")),
14195
- listTools: (tenantId, ref) => ref.kind === "mcp" ? mcpManager2.getAvailableTools(tenantId, ref.serverKey) : Promise.reject(new PluginMethodNotSupportedError("tools/list"))
14196
- };
14197
- }
14198
- function firstHeader(request, name) {
14199
- const header2 = request.headers[name];
14200
- return Array.isArray(header2) ? header2[0] : header2;
14201
- }
14202
- function tenantOf(request) {
14203
- const user = request.user;
14204
- return user?.tenantId ?? firstHeader(request, "x-tenant-id") ?? "default";
14205
- }
14206
- function runContextOf(request) {
14207
- return {
14208
- tenantId: tenantOf(request),
14209
- workspaceId: firstHeader(request, "x-workspace-id"),
14210
- projectId: firstHeader(request, "x-project-id")
14211
- };
14212
- }
14213
- function registerMcpAppRoutes(fastify2, deps = resolveMcpAppDeps()) {
14214
- const handlers = makeMcpAppHandlers(deps);
14215
- fastify2.get("/mcp-apps/sandbox.html", async (_request, reply) => {
14216
- return reply.header("content-type", SANDBOX_HTML_CONTENT_TYPE).header("x-content-type-options", "nosniff").header("cache-control", "no-store").send(SANDBOX_HTML);
14217
- });
14218
- fastify2.post("/api/mcp-apps/resource", async (request, reply) => {
14219
- const body = asRecord(request.body) ?? {};
14220
- const result = await handlers.resource(tenantOf(request), body.ref);
14221
- return reply.code(result.status).send(result.body);
14222
- });
14223
- fastify2.post("/api/mcp-apps/rpc", async (request, reply) => {
14224
- const body = asRecord(request.body) ?? {};
14225
- const params = asRecord(body.params) ?? {};
14226
- const context = runContextOf(request);
14227
- const result = await handlers.rpc(
14228
- context.tenantId,
14229
- body.ref,
14230
- String(body.method ?? ""),
14231
- params,
14232
- context
14233
- );
14234
- return reply.code(result.status).send(result.body);
14235
- });
14236
- }
14237
-
14238
13731
  // src/controllers/eval.ts
14239
13732
  import { getStoreLattice as getStoreLattice21, hasModelLatticeForTenant as hasModelLatticeForTenant2, modelLatticeManager as modelLatticeManager2 } from "@axiom-lattice/core";
14240
13733
  import { v4 as uuidv43 } from "uuid";
@@ -15384,6 +14877,16 @@ var AuthController = class {
15384
14877
  }
15385
14878
  async login(request, reply) {
15386
14879
  const { email, password } = request.body;
14880
+ const rateLimit = await getRateLimitService().consume("auth.login", {
14881
+ ip: request.ip
14882
+ });
14883
+ if (!rateLimit.allowed) {
14884
+ return reply.header("Retry-After", String(rateLimit.retryAfterSeconds)).status(429).send({
14885
+ success: false,
14886
+ error: "RATE_LIMITED",
14887
+ retryAfterSeconds: rateLimit.retryAfterSeconds
14888
+ });
14889
+ }
15387
14890
  try {
15388
14891
  const user = await this.userStore.getUserByEmail(email);
15389
14892
  if (!user) {
@@ -15752,7 +15255,7 @@ function isStaleConsoleSessionCredential(authHeader) {
15752
15255
  function bypassesConsoleAuth(request) {
15753
15256
  const path5 = request.url.split("?")[0];
15754
15257
  const method = request.method;
15755
- 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;
15258
+ 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;
15756
15259
  if (request.method === "GET" && (/^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path5) || /^\/a2a\/(?:v1\/)?[^/]+\/\.well-known\/agent-card\.json$/.test(path5))) {
15757
15260
  return true;
15758
15261
  }
@@ -17518,6 +17021,7 @@ import {
17518
17021
  ProjectTaskOwnerPolicyError as ProjectTaskOwnerPolicyError2,
17519
17022
  projectMutationMutex as projectMutationMutex2,
17520
17023
  projectTaskAdmissionNotifier,
17024
+ projectRoomMessageNotifier,
17521
17025
  taskLifecycleNotifier as taskLifecycleNotifier2
17522
17026
  } from "@axiom-lattice/core";
17523
17027
 
@@ -17856,7 +17360,7 @@ function snapshotEnsureInput(input) {
17856
17360
  }
17857
17361
 
17858
17362
  // src/services/tasks/TaskCommandService.ts
17859
- import { createHash as createHash4 } from "crypto";
17363
+ import { createHash as createHash3 } from "crypto";
17860
17364
 
17861
17365
  // src/services/collaboration/ProjectTaskRunService.ts
17862
17366
  import { randomUUID as randomUUID21 } from "crypto";
@@ -18993,7 +18497,7 @@ var _TaskCommandService = class _TaskCommandService {
18993
18497
  threadId,
18994
18498
  summary: "User comment",
18995
18499
  detail: { markdown: text, type: "user_comment" },
18996
- fallbackEventKey: `task-comment:${task.id}:${createHash4("sha256").update(text).digest("hex")}`
18500
+ fallbackEventKey: `task-comment:${task.id}:${createHash3("sha256").update(text).digest("hex")}`
18997
18501
  });
18998
18502
  const agent = this.deps.agents.getAgent(this.agentScope(task, threadId));
18999
18503
  await agent.addMessage({ input: { message: text } });
@@ -19966,7 +19470,7 @@ import { createHmac as createHmac3 } from "crypto";
19966
19470
  import { z as z5, ZodError as ZodError2 } from "zod";
19967
19471
 
19968
19472
  // src/services/agent-web-app/AgentWebAppRuntimeService.ts
19969
- import { createHash as createHash5, randomUUID as randomUUID22 } from "crypto";
19473
+ import { createHash as createHash4, randomUUID as randomUUID22 } from "crypto";
19970
19474
  import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock, buildAffinityMissChunk as buildAffinityMissChunk2 } from "@axiom-lattice/protocols";
19971
19475
  import { getReplicaId as getReplicaId7, getThreadRunOwner as getThreadRunOwner2, normalizeAgentInterrupts as normalizeAgentInterrupts2 } from "@axiom-lattice/core";
19972
19476
  function isRecord9(value) {
@@ -20261,8 +19765,8 @@ ${formatFileRefsSection(refs)}` : input.content;
20261
19765
  const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
20262
19766
  this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
20263
19767
  if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
20264
- const userDigest = createHash5("sha256").update(userId, "utf8").digest("hex");
20265
- const contentDigest = createHash5("sha256").update(file.bytes).digest("hex");
19768
+ const userDigest = createHash4("sha256").update(userId, "utf8").digest("hex");
19769
+ const contentDigest = createHash4("sha256").update(file.bytes).digest("hex");
20266
19770
  const displayName = file.name;
20267
19771
  const physicalName = `${randomUUID22()}-${sanitizePhysicalFilename(file.name)}`;
20268
19772
  const saved = await this.deps.saveProjectFile({
@@ -20470,7 +19974,7 @@ function implicitThreadId(runtime, userId) {
20470
19974
  userId,
20471
19975
  runtime.project.id
20472
19976
  ]);
20473
- const bytes = createHash5("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
19977
+ const bytes = createHash4("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
20474
19978
  bytes[6] = bytes[6] & 15 | 128;
20475
19979
  bytes[8] = bytes[8] & 63 | 128;
20476
19980
  const hex = bytes.toString("hex");
@@ -21284,17 +20788,19 @@ var CSS_CONTENT_TYPE = "text/css; charset=utf-8";
21284
20788
  var EXACT_ASSETS = {
21285
20789
  "agent-chat-loader.js": { contentType: JS_CONTENT_TYPE },
21286
20790
  "embed-page.js": { contentType: JS_CONTENT_TYPE },
21287
- "embed-page.css": { contentType: CSS_CONTENT_TYPE }
20791
+ "embed-page.css": { contentType: CSS_CONTENT_TYPE },
20792
+ "mcp-app-page.js": { contentType: JS_CONTENT_TYPE },
20793
+ "mcp-app-page.css": { contentType: CSS_CONTENT_TYPE }
21288
20794
  };
21289
- var HASHED_CHUNK_PATTERN = /^embed-page-[A-Za-z0-9._-]+\.(js|css)$/;
21290
- var CHUNK_CSS_PATTERN = /^embed-page-[A-Za-z0-9._-]+\.css$/;
20795
+ var HASHED_CHUNK_PATTERN = /^(embed-page|mcp-app-page)-[A-Za-z0-9._-]+\.(js|css)$/;
20796
+ var CHUNK_CSS_PATTERN = /^(embed-page|mcp-app-page)-[A-Za-z0-9._-]+\.css$/;
21291
20797
  function resolveAsset(asset) {
21292
20798
  if (asset.includes("..")) return null;
21293
20799
  const exact2 = EXACT_ASSETS[asset];
21294
20800
  if (exact2 !== void 0) return { contentType: exact2.contentType, immutable: false };
21295
20801
  const hashed = HASHED_CHUNK_PATTERN.exec(asset);
21296
20802
  if (hashed) {
21297
- return { contentType: hashed[1] === "css" ? CSS_CONTENT_TYPE : JS_CONTENT_TYPE, immutable: true };
20803
+ return { contentType: hashed[2] === "css" ? CSS_CONTENT_TYPE : JS_CONTENT_TYPE, immutable: true };
21298
20804
  }
21299
20805
  return null;
21300
20806
  }
@@ -21399,7 +20905,7 @@ function registerAgentWebAppEmbedRoutes(app2, deps = {}) {
21399
20905
  }
21400
20906
 
21401
20907
  // src/services/agent-web-app/AgentWebAppAttachmentRef.ts
21402
- import { createCipheriv, createDecipheriv, createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
20908
+ import { createCipheriv, createDecipheriv, createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
21403
20909
  var TTL_MS = 15 * 6e4;
21404
20910
  function resolveAgentWebAppAttachmentSecret(consoleSecret) {
21405
20911
  return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
@@ -21408,7 +20914,7 @@ var AgentWebAppAttachmentRefService = class {
21408
20914
  constructor(secret, now = Date.now) {
21409
20915
  this.now = now;
21410
20916
  if (!secret) throw new Error("Attachment signing secret is required");
21411
- this.key = createHash6("sha256").update(secret, "utf8").digest();
20917
+ this.key = createHash5("sha256").update(secret, "utf8").digest();
21412
20918
  }
21413
20919
  sign(input) {
21414
20920
  const payload = {
@@ -21470,7 +20976,7 @@ function decodeCanonical(value, expectedBytes) {
21470
20976
  return decoded;
21471
20977
  }
21472
20978
  function digestUser(userId) {
21473
- return createHash6("sha256").update(userId, "utf8").digest("hex");
20979
+ return createHash5("sha256").update(userId, "utf8").digest("hex");
21474
20980
  }
21475
20981
  function isAttachmentPayload(value) {
21476
20982
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
@@ -23449,8 +22955,6 @@ var ProjectMembershipService = class {
23449
22955
  }
23450
22956
  async list(actor3) {
23451
22957
  await this.deps.accessService.requireRosterAccess(actor3);
23452
- const membership = await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, actor3.userId);
23453
- if (!membership || membership.status !== "active") this.accessDenied();
23454
22958
  return this.deps.membershipStore.list(actor3.tenantId, actor3.projectId);
23455
22959
  }
23456
22960
  async add(actor3, input) {
@@ -23508,10 +23012,8 @@ var ProjectMembershipService = class {
23508
23012
  });
23509
23013
  }
23510
23014
  async authorize(actor3) {
23511
- await this.deps.accessService.requireProjectAccess({ ...actor3, capability: "manage" });
23512
- const membership = await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, actor3.userId);
23513
- if (!membership || membership.status !== "active") this.accessDenied();
23514
- return membership;
23015
+ const access = await this.deps.accessService.requireProjectAccess({ ...actor3, capability: "manage" });
23016
+ return access.membership;
23515
23017
  }
23516
23018
  async target(actor3, id3) {
23517
23019
  const target = (await this.deps.membershipStore.list(actor3.tenantId, actor3.projectId)).find((item) => item.id === id3);
@@ -24048,7 +23550,7 @@ function virtualPublicProjectBot(project, room, agent, mentionName, now) {
24048
23550
  }
24049
23551
 
24050
23552
  // src/services/collaboration/RoomMessageService.ts
24051
- import { createHash as createHash7, randomUUID as randomUUID27 } from "crypto";
23553
+ import { createHash as createHash6, randomUUID as randomUUID27 } from "crypto";
24052
23554
 
24053
23555
  // src/services/collaboration/ProjectBotMembershipSnapshot.ts
24054
23556
  import { snapshotExactRecord as snapshotExactRecord5 } from "@axiom-lattice/protocols";
@@ -24351,7 +23853,7 @@ var RoomMessageService = class {
24351
23853
  let idempotencyKey;
24352
23854
  if (value.requestIdempotencyKey !== void 0) {
24353
23855
  if (typeof value.requestIdempotencyKey !== "string" || !/^[\x21-\x7e]{1,200}$/.test(value.requestIdempotencyKey.trim())) throw new RoomMessageServiceError("INVALID_MESSAGE", 400, "Invalid idempotency key");
24354
- idempotencyKey = `room-human:${actor3.userId}:${createHash7("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
23856
+ idempotencyKey = `room-human:${actor3.userId}:${createHash6("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
24355
23857
  }
24356
23858
  return { text, mentions, botIds, ...value.replyToMessageId === void 0 ? {} : { replyToMessageId: value.replyToMessageId }, ...idempotencyKey === void 0 ? {} : { idempotencyKey } };
24357
23859
  }
@@ -24408,7 +23910,7 @@ function cloneRoomMessage(message) {
24408
23910
  };
24409
23911
  }
24410
23912
  function fingerprintRequest(input) {
24411
- return createHash7("sha256").update(JSON.stringify({
23913
+ return createHash6("sha256").update(JSON.stringify({
24412
23914
  text: input.text,
24413
23915
  mentions: input.mentions,
24414
23916
  replyToMessageId: input.replyToMessageId ?? null
@@ -26472,7 +25974,7 @@ var TaskInterruptCardProjector = class {
26472
25974
  };
26473
25975
 
26474
25976
  // src/services/collaboration/ProjectTaskObserver.ts
26475
- import { createHash as createHash8 } from "crypto";
25977
+ import { createHash as createHash7 } from "crypto";
26476
25978
  import {
26477
25979
  createTaskRunBridge as createTaskRunBridge3,
26478
25980
  probeThreadRunOwner as defaultProbeThreadRunOwner
@@ -27043,7 +26545,7 @@ var ProjectTaskObserver = class {
27043
26545
  action: "activity",
27044
26546
  summary: "Project task recovery warning",
27045
26547
  detail: { type: "project_task_recovery_warning", phase, message },
27046
- eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash8("sha256").update(message).digest("hex")}`
26548
+ eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash7("sha256").update(message).digest("hex")}`
27047
26549
  });
27048
26550
  }
27049
26551
  async safeWarning(task, phase, error) {
@@ -27286,7 +26788,7 @@ var ProjectTaskObserver = class {
27286
26788
  }
27287
26789
  }
27288
26790
  async persistOverflowWarning(event) {
27289
- const scopeDigest = createHash8("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
26791
+ const scopeDigest = createHash7("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
27290
26792
  try {
27291
26793
  await this.deps.workItems.createIfAbsentByEventKey({
27292
26794
  tenantId: event.tenantId,
@@ -27391,7 +26893,7 @@ function combineSignals(left, right) {
27391
26893
  }
27392
26894
 
27393
26895
  // src/services/collaboration/RoomProjectionService.ts
27394
- import { createHash as createHash9 } from "crypto";
26896
+ import { createHash as createHash8 } from "crypto";
27395
26897
  import {
27396
26898
  PROJECT_ROOM_TASK_DELEGATED_LABEL,
27397
26899
  PROJECT_ROOM_TASK_DELEGATED_SEPARATOR,
@@ -27612,7 +27114,7 @@ function safeRepairIdentity(value, scope) {
27612
27114
  return taskId && eventKey && tenantId === scope.tenantId && workspaceId === scope.workspaceId && projectId === scope.projectId ? { taskId, eventKey } : void 0;
27613
27115
  }
27614
27116
  function tupleKey(prefix, values) {
27615
- const digest = createHash9("sha256").update(JSON.stringify(values)).digest("hex");
27117
+ const digest = createHash8("sha256").update(JSON.stringify(values)).digest("hex");
27616
27118
  return `${prefix}:${digest}`;
27617
27119
  }
27618
27120
  function parseProjectionInput(taskValue, itemValue) {
@@ -28191,7 +27693,7 @@ function snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
28191
27693
  }
28192
27694
 
28193
27695
  // src/services/collaboration/ProjectRoomRealtimePublisher.ts
28194
- import { createHash as createHash10 } from "crypto";
27696
+ import { createHash as createHash9 } from "crypto";
28195
27697
  import {
28196
27698
  projectRoomUserEventScope,
28197
27699
  snapshotExactRecord as snapshotExactRecord11,
@@ -28313,7 +27815,7 @@ var BrokerProjectRoomRealtimePublisher = class {
28313
27815
  try {
28314
27816
  event = build();
28315
27817
  if (!event) throw new Error("Invalid Project Room realtime projection");
28316
- const dedupeKey = createHash10("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
27818
+ const dedupeKey = createHash9("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
28317
27819
  if (this.sourceKeys.has(dedupeKey)) return;
28318
27820
  this.deps.broker.publish(event.scope, event.draft, event.sourceKey);
28319
27821
  this.sourceKeys.set(dedupeKey, true);
@@ -28558,12 +28060,21 @@ function createProjectRoomRealtimeRuntime(deps) {
28558
28060
  shutdownTimeoutMs: deps.shutdownTimeoutMs
28559
28061
  });
28560
28062
  }
28063
+ var PUSH_DEDUPE_LIMIT = 2e3;
28561
28064
  function withPushNotifier(target, notifier) {
28065
+ const pushedMessageIds = /* @__PURE__ */ new Set();
28562
28066
  return new Proxy(target, {
28563
28067
  get(inner, prop, receiver) {
28564
28068
  if (prop === "messageCreated") {
28565
28069
  return (message) => {
28566
28070
  inner.messageCreated(message);
28071
+ if (pushedMessageIds.has(message.id)) return;
28072
+ pushedMessageIds.add(message.id);
28073
+ while (pushedMessageIds.size > PUSH_DEDUPE_LIMIT) {
28074
+ const oldest = pushedMessageIds.values().next().value;
28075
+ if (oldest === void 0) break;
28076
+ pushedMessageIds.delete(oldest);
28077
+ }
28567
28078
  void notifier.notifyMessage(message).catch(() => void 0);
28568
28079
  };
28569
28080
  }
@@ -29478,7 +28989,14 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29478
28989
  log: realtimeLog
29479
28990
  }) : void 0;
29480
28991
  if (projectRoomRealtimeRuntime) {
29481
- app2.addHook("onClose", async () => projectRoomRealtimeRuntime?.close());
28992
+ const runtime = projectRoomRealtimeRuntime;
28993
+ const unsubscribeProjectRoomMessages = projectRoomMessageNotifier.subscribe((message) => {
28994
+ runtime.publisher.messageCreated(message);
28995
+ });
28996
+ app2.addHook("onClose", async () => {
28997
+ unsubscribeProjectRoomMessages();
28998
+ await runtime.close();
28999
+ });
29482
29000
  const workspaceRoomEventsService = new WorkspaceRoomEventsService({
29483
29001
  broker: projectRoomRealtimeRuntime.broker,
29484
29002
  listMemberRooms: createWorkspaceMemberRoomLister({
@@ -31749,50 +31267,6 @@ var ChannelAdapterRegistry = class {
31749
31267
  }
31750
31268
  };
31751
31269
 
31752
- // src/router/middlewares/channelSharedState.ts
31753
- import { createClient as createClient2 } from "redis";
31754
- var clientPromise;
31755
- async function redis() {
31756
- const url = process.env.REDIS_URL?.trim();
31757
- if (!url) return void 0;
31758
- if (!clientPromise) {
31759
- clientPromise = (async () => {
31760
- const client = createClient2({ url });
31761
- client.on("error", () => void 0);
31762
- await client.connect();
31763
- return client;
31764
- })();
31765
- }
31766
- try {
31767
- const client = await clientPromise;
31768
- return client.isOpen ? client : void 0;
31769
- } catch {
31770
- clientPromise = void 0;
31771
- return void 0;
31772
- }
31773
- }
31774
- async function claimDedupKey(key, ttlMs) {
31775
- try {
31776
- const client = await redis();
31777
- if (!client) return void 0;
31778
- const result = await client.set(key, "1", { NX: true, PX: ttlMs });
31779
- return result === "OK";
31780
- } catch {
31781
- return void 0;
31782
- }
31783
- }
31784
- async function incrementRateCounter(key, windowMs) {
31785
- try {
31786
- const client = await redis();
31787
- if (!client) return void 0;
31788
- const count = await client.incr(key);
31789
- if (count === 1) await client.pExpire(key, windowMs);
31790
- return count;
31791
- } catch {
31792
- return void 0;
31793
- }
31794
- }
31795
-
31796
31270
  // src/router/middlewares/deduplication.ts
31797
31271
  var processedMessages = /* @__PURE__ */ new Map();
31798
31272
  function createDeduplicationMiddleware(ttlMs = 5 * 60 * 1e3) {
@@ -32861,7 +32335,7 @@ import {
32861
32335
  agentInstanceManager as agentInstanceManager13,
32862
32336
  createSandboxProvider,
32863
32337
  TokenCache,
32864
- mcpManager as mcpManager3,
32338
+ mcpManager as mcpManager2,
32865
32339
  convertToConnection as convertToConnection2,
32866
32340
  createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2,
32867
32341
  ChannelConnectionLeader,
@@ -32881,7 +32355,7 @@ import {
32881
32355
  COLLECTION_CHANGED_TOPIC,
32882
32356
  vectorStoreLatticeManager as vectorStoreLatticeManager2,
32883
32357
  observabilityLatticeManager,
32884
- getSandboxPluginCatalog as getSandboxPluginCatalog3,
32358
+ getSandboxPluginCatalog as getSandboxPluginCatalog2,
32885
32359
  getSandBoxManager as getSandBoxManager4,
32886
32360
  installPluginHost,
32887
32361
  ThreadExecutionSupervisor,
@@ -32892,7 +32366,7 @@ import {
32892
32366
  } from "@axiom-lattice/core";
32893
32367
 
32894
32368
  // src/services/chunk/redisAppendLog.ts
32895
- import { createClient as createClient3 } from "redis";
32369
+ import { createClient as createClient2 } from "redis";
32896
32370
  var VALUE_FIELD = "v";
32897
32371
  var START_ID = "0-0";
32898
32372
  var ChunkLogCorruptionError = class extends Error {
@@ -32909,7 +32383,7 @@ var ChunkLogCorruptionError = class extends Error {
32909
32383
  }
32910
32384
  };
32911
32385
  async function createRedisAppendLog(url, options = {}) {
32912
- const client = createClient3({
32386
+ const client = createClient2({
32913
32387
  url,
32914
32388
  disableOfflineQueue: true,
32915
32389
  socket: {
@@ -33099,7 +32573,7 @@ function readField(fields, name) {
33099
32573
  }
33100
32574
 
33101
32575
  // src/services/run-cancel/RedisRunCancelNotifier.ts
33102
- import { createClient as createClient4 } from "redis";
32576
+ import { createClient as createClient3 } from "redis";
33103
32577
  import {
33104
32578
  runCancelSignalSchema as runCancelSignalSchema2
33105
32579
  } from "@axiom-lattice/protocols";
@@ -33500,7 +32974,7 @@ function createRedisRunCancelNotifier(options) {
33500
32974
  });
33501
32975
  }
33502
32976
  function createNodeRedisClient2(url) {
33503
- const client = createClient4({
32977
+ const client = createClient3({
33504
32978
  url,
33505
32979
  disableOfflineQueue: true,
33506
32980
  socket: {
@@ -34814,7 +34288,7 @@ import {
34814
34288
  } from "@axiom-lattice/core";
34815
34289
 
34816
34290
  // src/services/plugins/pluginDeploymentNaming.ts
34817
- import { createHash as createHash11 } from "crypto";
34291
+ import { createHash as createHash10 } from "crypto";
34818
34292
  var PLUGIN_GUEST_PORT = 8080;
34819
34293
  var SANDBOX_NAME_HASH_LENGTH = 16;
34820
34294
  function pluginSandboxName(tenantId, pluginType) {
@@ -34824,7 +34298,7 @@ function pluginSandboxName(tenantId, pluginType) {
34824
34298
  if (!pluginType || typeof pluginType !== "string") {
34825
34299
  throw new Error("pluginSandboxName: pluginType must be a non-empty string");
34826
34300
  }
34827
- const hash = createHash11("sha256").update(`${tenantId}\0${pluginType}`).digest("hex").slice(0, SANDBOX_NAME_HASH_LENGTH);
34301
+ const hash = createHash10("sha256").update(`${tenantId}\0${pluginType}`).digest("hex").slice(0, SANDBOX_NAME_HASH_LENGTH);
34828
34302
  return `plugin-${hash}`;
34829
34303
  }
34830
34304
 
@@ -37401,7 +36875,7 @@ function createAgentRunRetentionJob(input) {
37401
36875
  }
37402
36876
 
37403
36877
  // src/services/run-task/RedisRunTaskWake.ts
37404
- import { createClient as createClient5 } from "redis";
36878
+ import { createClient as createClient4 } from "redis";
37405
36879
  var RUN_TASK_WAKE_CHANNEL = "run-task:wake";
37406
36880
  var DEFAULT_OPERATION_TIMEOUT_MS = 2e3;
37407
36881
  var DEFAULT_RETRY_DELAYS_MS = [500, 2e3, 5e3, 1e4];
@@ -37794,7 +37268,7 @@ function createConfiguredRedisRunTaskWake(options) {
37794
37268
  return createRedisRunTaskWake({ ...options, url });
37795
37269
  }
37796
37270
  function createNodeRedisClient3(url) {
37797
- const client = createClient5({
37271
+ const client = createClient4({
37798
37272
  url,
37799
37273
  disableOfflineQueue: true,
37800
37274
  socket: {
@@ -38885,7 +38359,7 @@ async function restoreMcpConnections() {
38885
38359
  logger8.info(`Restoring ${configs.length} MCP server connections...`);
38886
38360
  const deps = {
38887
38361
  store: store2,
38888
- manager: mcpManager3,
38362
+ manager: mcpManager2,
38889
38363
  convertToConnection: convertToConnection2
38890
38364
  };
38891
38365
  const results = await Promise.allSettled(
@@ -38923,6 +38397,8 @@ var start = async (config) => {
38923
38397
  logger8 = loggerLattice.client;
38924
38398
  }
38925
38399
  app.decorate("loggerLattice", loggerLattice);
38400
+ const rateLimitService = createRateLimitService(config?.rateLimits);
38401
+ setRateLimitService(rateLimitService);
38926
38402
  const observabilityProvider = observabilityLatticeManager.getDefault();
38927
38403
  if (observabilityProvider) {
38928
38404
  try {
@@ -39162,9 +38638,9 @@ var start = async (config) => {
39162
38638
  projectRoomRealtimeRuntime
39163
38639
  });
39164
38640
  try {
39165
- const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await import("./A2AAuthService-7UGQ7GMW.mjs");
39166
- const { registerA2AStandardRoutes } = await import("./a2a-standard-VL5I7B4W.mjs");
39167
- const a2a = await import("./a2a-BHBLACJV.mjs");
38641
+ const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await import("./A2AAuthService-UKN53ZGP.mjs");
38642
+ const { registerA2AStandardRoutes } = await import("./a2a-standard-INVCUSAW.mjs");
38643
+ const a2a = await import("./a2a-TDZ3TT6H.mjs");
39168
38644
  const a2aKeyStore = getStoreLattice42("default", "a2aApiKey").store;
39169
38645
  const assistantStore = getStoreLattice42("default", "assistant").store;
39170
38646
  const projectStore = getStoreLattice42("default", "project").store;
@@ -39193,18 +38669,17 @@ var start = async (config) => {
39193
38669
  });
39194
38670
  }
39195
38671
  try {
39196
- const { registerOpenRoutes } = await import("./open-7ED27I5B.mjs");
39197
- const { registerOpenSessionRoutes } = await import("./open-session-2Y7XJJW2.mjs");
39198
- const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
39199
- const { registerOpenFilesRoutes } = await import("./open-files-V3R2BFTV.mjs");
38672
+ const { registerOpenRoutes } = await import("./open-27ZPLBIW.mjs");
38673
+ const { registerOpenSessionRoutes } = await import("./open-session-CLZT3HVG.mjs");
38674
+ const { registerOpenCatalogRoutes } = await import("./open-catalog-NVGP6SQP.mjs");
38675
+ const { registerOpenFilesRoutes } = await import("./open-files-JPB2NBL6.mjs");
39200
38676
  const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
39201
- const { registerOpenExposureRoutes } = await import("./open-exposure-I3DN2X4E.mjs");
39202
- const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
39203
- const { OpenRateLimiter } = await import("./openRateLimiter-XEOL3A5G.mjs");
38677
+ const { registerOpenExposureRoutes } = await import("./open-exposure-BPLYUIX6.mjs");
38678
+ const { openLattice } = await import("./openLattice-D5YTSZBY.mjs");
39204
38679
  const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
39205
38680
  const { createOpenSandboxProvider } = await import("./sandboxProvider-FILMFVIM.mjs");
39206
38681
  const { createAgentProvider } = await import("./agentProvider-X4FVQPKW.mjs");
39207
- const { registerExposedPluginTools } = await import("./registerExposedPlugins-6GCOE64C.mjs");
38682
+ const { registerExposedPluginTools } = await import("./registerExposedPlugins-2JGXSMAL.mjs");
39208
38683
  const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
39209
38684
  openLattice.register(createKbProvider(new CollectionService2()));
39210
38685
  openLattice.register(createOpenSandboxProvider());
@@ -39220,18 +38695,7 @@ var start = async (config) => {
39220
38695
  })
39221
38696
  );
39222
38697
  await registerExposedPluginTools();
39223
- const openRateLimitPerMinute = Number(
39224
- process.env.OPEN_RATE_LIMIT_PER_MINUTE
39225
- );
39226
- const openAgentRateLimitPerMinute = Number(
39227
- process.env.OPEN_AGENT_RATE_LIMIT_PER_MINUTE
39228
- );
39229
- registerOpenRoutes(app, {
39230
- rateLimiter: new OpenRateLimiter({
39231
- ...Number.isFinite(openRateLimitPerMinute) && openRateLimitPerMinute > 0 ? { defaultPerMinute: openRateLimitPerMinute } : {},
39232
- ...Number.isFinite(openAgentRateLimitPerMinute) && openAgentRateLimitPerMinute > 0 ? { agentPerMinute: openAgentRateLimitPerMinute } : {}
39233
- })
39234
- });
38698
+ registerOpenRoutes(app, { rateLimits: rateLimitService });
39235
38699
  registerOpenSessionRoutes(app);
39236
38700
  registerOpenCatalogRoutes(app);
39237
38701
  registerOpenFilesRoutes(app);
@@ -39244,7 +38708,7 @@ var start = async (config) => {
39244
38708
  });
39245
38709
  }
39246
38710
  try {
39247
- const { registerGatewaySandboxOpenCredentialProvider } = await import("./sandboxOpenCredentialProvider-AT227D4N.mjs");
38711
+ const { registerGatewaySandboxOpenCredentialProvider } = await import("./sandboxOpenCredentialProvider-RTYOYRCT.mjs");
39248
38712
  registerGatewaySandboxOpenCredentialProvider((error) => {
39249
38713
  logger8.warn("sandbox open-credential provider failed", { error });
39250
38714
  });
@@ -39276,7 +38740,7 @@ var start = async (config) => {
39276
38740
  wiring: {
39277
38741
  store: pluginDeploymentStore,
39278
38742
  sandboxManager: getSandBoxManager4(),
39279
- catalog: getSandboxPluginCatalog3(),
38743
+ catalog: getSandboxPluginCatalog2(),
39280
38744
  installPluginHost,
39281
38745
  limits: readPluginDeploymentLimits(),
39282
38746
  serviceKey: process.env.MICROSANDBOX_API_KEY,
@@ -39303,7 +38767,7 @@ var start = async (config) => {
39303
38767
  }
39304
38768
  }
39305
38769
  try {
39306
- const { ResourceController } = await import("./resources-DAU5ZIPB.mjs");
38770
+ const { ResourceController } = await import("./resources-YD5DOPGD.mjs");
39307
38771
  const sharedResourceStore = getStoreLattice42("default", "sharedResource").store;
39308
38772
  const cache = new TokenCache();
39309
38773
  const resourceController = new ResourceController({
@@ -39313,6 +38777,13 @@ var start = async (config) => {
39313
38777
  logger: logger8
39314
38778
  });
39315
38779
  registerResourceRoutes(app, resourceController);
38780
+ const { registerMcpAppShareRoutes } = await import("./mcp-app-shares-SHPBJQXS.mjs");
38781
+ const { makeMcpAppHandlers, resolveMcpAppDeps } = await import("./mcp-apps-QVUK6UBA.mjs");
38782
+ registerMcpAppShareRoutes(app, {
38783
+ store: sharedResourceStore,
38784
+ handlers: makeMcpAppHandlers(resolveMcpAppDeps()),
38785
+ rateLimits: rateLimitService
38786
+ });
39316
38787
  logger8.info("Resource share routes registered");
39317
38788
  } catch (err) {
39318
38789
  logger8.warn("Resource share infrastructure unavailable", {
@@ -39392,7 +38863,6 @@ var start = async (config) => {
39392
38863
  "assistant-changed": (payload) => handleAssistantChanged(payload, async (tenantId) => {
39393
38864
  await agentLatticeManager6.initializeStoredAssistantsForTenant(tenantId);
39394
38865
  invalidateAgentReflectionCache(tenantId);
39395
- invalidatePluginReflectionCache(tenantId);
39396
38866
  }),
39397
38867
  [AGENT_LIFECYCLE_TOPIC]: async (payload) => {
39398
38868
  handleAgentLifecycle(payload);
@@ -39400,7 +38870,7 @@ var start = async (config) => {
39400
38870
  [A2A_KEY_CHANGED_TOPIC]: (payload) => handleA2AKeyChanged(payload, () => refreshStoreKeyMap()),
39401
38871
  [MCP_CONFIG_CHANGED_TOPIC]: (payload) => reconcileMcpConfigChange(payload, {
39402
38872
  store: getStoreLattice42("default", "mcp").store,
39403
- manager: mcpManager3,
38873
+ manager: mcpManager2,
39404
38874
  convertToConnection: convertToConnection2
39405
38875
  }),
39406
38876
  [COLLECTION_CHANGED_TOPIC]: async (payload) => {
@@ -39412,8 +38882,7 @@ var start = async (config) => {
39412
38882
  [PLUGIN_CATALOG_CHANGED_TOPIC]: async (payload) => {
39413
38883
  const tenantId = payload?.tenantId;
39414
38884
  if (typeof tenantId === "string" && tenantId.length > 0) {
39415
- getSandboxPluginCatalog3().invalidate(tenantId);
39416
- invalidatePluginReflectionCache(tenantId);
38885
+ getSandboxPluginCatalog2().invalidate(tenantId);
39417
38886
  invalidateAgentReflectionCache(tenantId);
39418
38887
  await getMcpUiResourceResolver()?.invalidateTenantPlugins(tenantId);
39419
38888
  }