@axiom-lattice/gateway 4.2.0 → 4.2.1

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 (103) hide show
  1. package/dist/{A2AAuthService-DK5X6VIL.mjs → A2AAuthService-7UGQ7GMW.mjs} +3 -2
  2. package/dist/{WechatChannelAdapter-WSDKR4OA.mjs → WechatChannelAdapter-7WHE5UGY.mjs} +2 -1
  3. package/dist/{WechatChannelAdapter-WSDKR4OA.mjs.map → WechatChannelAdapter-7WHE5UGY.mjs.map} +1 -1
  4. package/dist/{a2a-7N3WDAOP.mjs → a2a-FGYX2HH3.mjs} +71 -32
  5. package/dist/a2a-FGYX2HH3.mjs.map +1 -0
  6. package/dist/{a2a-standard-WC2BSOFV.mjs → a2a-standard-VEQBXBMM.mjs} +93 -115
  7. package/dist/a2a-standard-VEQBXBMM.mjs.map +1 -0
  8. package/dist/agentProvider-X4FVQPKW.mjs +88 -0
  9. package/dist/agentProvider-X4FVQPKW.mjs.map +1 -0
  10. package/dist/chunk-2POW27EI.mjs +27 -0
  11. package/dist/chunk-2POW27EI.mjs.map +1 -0
  12. package/dist/chunk-52SUWMOX.mjs +121 -0
  13. package/dist/chunk-52SUWMOX.mjs.map +1 -0
  14. package/dist/chunk-GF4GGTWJ.mjs +164 -0
  15. package/dist/chunk-GF4GGTWJ.mjs.map +1 -0
  16. package/dist/{chunk-2HOCF46L.mjs → chunk-GPHOYR3X.mjs} +33 -11
  17. package/dist/chunk-GPHOYR3X.mjs.map +1 -0
  18. package/dist/chunk-NFEGQTCC.mjs +27 -0
  19. package/dist/chunk-PWHUWFXL.mjs +173 -0
  20. package/dist/chunk-PWHUWFXL.mjs.map +1 -0
  21. package/dist/chunk-QUT2DS4R.mjs +50 -0
  22. package/dist/chunk-QUT2DS4R.mjs.map +1 -0
  23. package/dist/chunk-TJAUXMYJ.mjs +109 -0
  24. package/dist/chunk-TJAUXMYJ.mjs.map +1 -0
  25. package/dist/chunk-WKNZ7GBC.mjs +56 -0
  26. package/dist/chunk-WKNZ7GBC.mjs.map +1 -0
  27. package/dist/collection_service-RTRJEQFL.mjs +8 -0
  28. package/dist/embed/agent-chat-loader.js +1 -1
  29. package/dist/embed/{embed-page-AgentDetailSideApp-6CVXM3P3.js → embed-page-AgentDetailSideApp-KGYKTMZR.js} +1 -1
  30. package/dist/embed/{embed-page-AgentDetailSideApp-DRGKJ6C5.css → embed-page-AgentDetailSideApp-O6G4NJ7I.css} +1 -1
  31. package/dist/embed/{embed-page-AgentGovernanceWorkbench-4B3NJPCY.js → embed-page-AgentGovernanceWorkbench-F42AJH77.js} +1 -1
  32. package/dist/embed/embed-page-AgentGovernanceWorkbench-SFZNVMUH.css +1 -0
  33. package/dist/embed/embed-page-MessageGraph-ACHQORG7.js +6 -0
  34. package/dist/embed/{embed-page-ReactChart-OG5Z43H7.js → embed-page-ReactChart-FGUHXBED.js} +1 -1
  35. package/dist/embed/{embed-page-SkillFlow-GS4OUCJJ.js → embed-page-SkillFlow-SFZ5HLMK.js} +1 -1
  36. package/dist/embed/{embed-page-TasksSideApp-I6Z7BOT2.js → embed-page-TasksSideApp-4GY5YBTN.js} +1 -1
  37. package/dist/embed/{embed-page-TeamWorkspace-LSCUIJDA.js → embed-page-TeamWorkspace-YWE6ZTLB.js} +4 -4
  38. package/dist/embed/{embed-page-TopologyRuntimeView-RY7NOUB7.js → embed-page-TopologyRuntimeView-MKFPCHT3.js} +1 -1
  39. package/dist/embed/{embed-page-WorkflowAutomationView-RNKIKDUX.js → embed-page-WorkflowAutomationView-WT6VH5HD.js} +1 -1
  40. package/dist/embed/{embed-page-chunk-7CFGO35V.js → embed-page-chunk-3PY2YGBU.js} +1 -1
  41. package/dist/embed/embed-page-chunk-3WXEO6CG.js +245 -0
  42. package/dist/embed/embed-page-chunk-42S7N5Y5.js +4084 -0
  43. package/dist/embed/{embed-page-chunk-SU5GXLKX.js → embed-page-chunk-67U2KZFL.js} +4 -4
  44. package/dist/embed/{embed-page-chunk-4X5XHAMX.js → embed-page-chunk-74BPRLSV.js} +4 -4
  45. package/dist/embed/{embed-page-chunk-DVHOG6AY.js → embed-page-chunk-AC6X2XRH.js} +1 -1
  46. package/dist/embed/{embed-page-chunk-4BIOW547.js → embed-page-chunk-FDID7LZP.js} +1 -1
  47. package/dist/embed/{embed-page-chunk-JLWIJ4BP.js → embed-page-chunk-FU2KMTSK.js} +1 -1
  48. package/dist/embed/{embed-page-chunk-EPEKIAZP.js → embed-page-chunk-HS5VQFWH.js} +1 -1
  49. package/dist/embed/{embed-page-chunk-Q32Y7CP2.js → embed-page-chunk-IV5C4DYL.js} +1 -1
  50. package/dist/embed/{embed-page-chunk-KGX7G7XQ.js → embed-page-chunk-KEL6XU6R.js} +1 -1
  51. package/dist/embed/{embed-page-chunk-AYOVLRHX.js → embed-page-chunk-MGZK7ELC.js} +94 -69
  52. package/dist/embed/{embed-page-chunk-BZI3YCE7.js → embed-page-chunk-N46WJ2FS.js} +1 -1
  53. package/dist/embed/{embed-page-chunk-KG5A66WX.js → embed-page-chunk-O4QK5LWZ.js} +83 -83
  54. package/dist/embed/{embed-page-chunk-ESLOGPBN.js → embed-page-chunk-Z7USDLHI.js} +1 -1
  55. package/dist/embed/{embed-page-code-highlighter-TBT4ICNZ.js → embed-page-code-highlighter-Y6CQA4CN.js} +1 -1
  56. package/dist/embed/{embed-page-mermaid-TBO3M7GX.js → embed-page-mermaid-CW4QCAH3.js} +1 -1
  57. package/dist/embed/embed-page.css +1 -1
  58. package/dist/embed/embed-page.js +1 -1
  59. package/dist/index.js +2074 -619
  60. package/dist/index.js.map +1 -1
  61. package/dist/index.mjs +345 -346
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/kbProvider-2D6DZ34N.mjs +50 -0
  64. package/dist/kbProvider-2D6DZ34N.mjs.map +1 -0
  65. package/dist/{mcp-configs-IIKQM5GM.mjs → mcp-configs-WCY5EXCW.mjs} +2 -1
  66. package/dist/mcp-configs-WCY5EXCW.mjs.map +1 -0
  67. package/dist/{mcpTransportRegistry-XMJ5Y2RG.mjs → mcpTransportRegistry-VXUVJKBC.mjs} +2 -1
  68. package/dist/mcpTransportRegistry-VXUVJKBC.mjs.map +1 -0
  69. package/dist/open-HH7RKGML.mjs +260 -0
  70. package/dist/open-HH7RKGML.mjs.map +1 -0
  71. package/dist/open-audit-UJ3NXCGW.mjs +32 -0
  72. package/dist/open-audit-UJ3NXCGW.mjs.map +1 -0
  73. package/dist/open-catalog-WYS2AENM.mjs +59 -0
  74. package/dist/open-catalog-WYS2AENM.mjs.map +1 -0
  75. package/dist/open-exposure-FK4G6O6L.mjs +67 -0
  76. package/dist/open-exposure-FK4G6O6L.mjs.map +1 -0
  77. package/dist/open-files-GVRMK5PL.mjs +146 -0
  78. package/dist/open-files-GVRMK5PL.mjs.map +1 -0
  79. package/dist/open-session-3VN65PRR.mjs +53 -0
  80. package/dist/open-session-3VN65PRR.mjs.map +1 -0
  81. package/dist/openLattice-GAAGQJST.mjs +16 -0
  82. package/dist/openLattice-GAAGQJST.mjs.map +1 -0
  83. package/dist/registerExposedPlugins-63EUF4VS.mjs +99 -0
  84. package/dist/registerExposedPlugins-63EUF4VS.mjs.map +1 -0
  85. package/dist/{resources-VA7LSDKN.mjs → resources-SRQRKW5G.mjs} +47 -4
  86. package/dist/resources-SRQRKW5G.mjs.map +1 -0
  87. package/dist/sandboxProvider-I3P66VXT.mjs +10 -0
  88. package/dist/sandboxProvider-I3P66VXT.mjs.map +1 -0
  89. package/dist/{sender-PX32VSHB.mjs → sender-PLDU5EZ3.mjs} +3 -1
  90. package/dist/{sender-PX32VSHB.mjs.map → sender-PLDU5EZ3.mjs.map} +1 -1
  91. package/package.json +7 -7
  92. package/dist/a2a-7N3WDAOP.mjs.map +0 -1
  93. package/dist/a2a-standard-WC2BSOFV.mjs.map +0 -1
  94. package/dist/chunk-2HOCF46L.mjs.map +0 -1
  95. package/dist/embed/embed-page-chunk-A2LQIHX4.js +0 -261
  96. package/dist/embed/embed-page-chunk-OWGBADV6.js +0 -3880
  97. package/dist/open-DCAOL5CG.mjs +0 -176
  98. package/dist/open-DCAOL5CG.mjs.map +0 -1
  99. package/dist/resources-VA7LSDKN.mjs.map +0 -1
  100. /package/dist/{A2AAuthService-DK5X6VIL.mjs.map → A2AAuthService-7UGQ7GMW.mjs.map} +0 -0
  101. /package/dist/{mcp-configs-IIKQM5GM.mjs.map → chunk-NFEGQTCC.mjs.map} +0 -0
  102. /package/dist/{mcpTransportRegistry-XMJ5Y2RG.mjs.map → collection_service-RTRJEQFL.mjs.map} +0 -0
  103. /package/dist/embed/{embed-page-AgentGovernanceWorkbench-SDS3GBCC.css → embed-page-MessageGraph-TYOOATYV.css} +0 -0
package/dist/index.js CHANGED
@@ -30,6 +30,178 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
+ // src/services/collection_service.ts
34
+ var collection_service_exports = {};
35
+ __export(collection_service_exports, {
36
+ CollectionService: () => CollectionService
37
+ });
38
+ function getTenantId5(request) {
39
+ return request.user?.tenantId || request.headers["x-tenant-id"] || "default";
40
+ }
41
+ var import_uuid2, import_documents, import_core11, CollectionService;
42
+ var init_collection_service = __esm({
43
+ "src/services/collection_service.ts"() {
44
+ "use strict";
45
+ import_uuid2 = require("uuid");
46
+ import_documents = require("@langchain/core/documents");
47
+ import_core11 = require("@axiom-lattice/core");
48
+ CollectionService = class {
49
+ // ─── Collection CRUD ──────────────────────────────────────────
50
+ async getAllCollections(request) {
51
+ const tenantId = getTenantId5(request);
52
+ const collections = await import_core11.collectionLatticeManager.getAllCollections(tenantId);
53
+ for (const c2 of collections) {
54
+ try {
55
+ c2._entryCount = await (0, import_core11.getCollectionEntryCount)(c2.name, c2.embeddingKey, c2.tenantId);
56
+ } catch {
57
+ c2._entryCount = 0;
58
+ }
59
+ }
60
+ return collections;
61
+ }
62
+ async getCollectionByName(request, name) {
63
+ const tenantId = getTenantId5(request);
64
+ return import_core11.collectionLatticeManager.getCollection(tenantId, name);
65
+ }
66
+ async createCollection(request, data) {
67
+ const tenantId = getTenantId5(request);
68
+ const collection = await import_core11.collectionLatticeManager.registerCollection(tenantId, data);
69
+ try {
70
+ await (0, import_core11.getOrCreateCollectionVectorStore)(data.name, data.embeddingKey, tenantId);
71
+ } catch (err) {
72
+ console.warn(`[CollectionService] Failed to init vector store for ${data.name}:`, err);
73
+ }
74
+ return collection;
75
+ }
76
+ async updateCollection(request, name, updates) {
77
+ const tenantId = getTenantId5(request);
78
+ return import_core11.collectionLatticeManager.updateCollection(tenantId, name, updates);
79
+ }
80
+ async deleteCollection(request, name) {
81
+ const tenantId = getTenantId5(request);
82
+ await (0, import_core11.removeCollectionVectorStore)(name, tenantId);
83
+ return import_core11.collectionLatticeManager.removeCollection(tenantId, name);
84
+ }
85
+ // ─── Entry CRUD ───────────────────────────────────────────────
86
+ async getEntries(request, collectionName) {
87
+ const collection = await this.getCollectionByName(request, collectionName);
88
+ if (!collection) throw new Error(`Collection "${collectionName}" not found`);
89
+ const docs = await (0, import_core11.listCollectionEntries)(collectionName, collection.embeddingKey, collection.tenantId);
90
+ return docs.map((doc) => ({
91
+ id: doc.metadata._id || "",
92
+ content: doc.pageContent,
93
+ metadata: Object.fromEntries(
94
+ Object.entries(doc.metadata).filter(([k]) => !k.startsWith("_"))
95
+ )
96
+ }));
97
+ }
98
+ async createEntry(request, collectionName, data) {
99
+ const collection = await this.getCollectionByName(request, collectionName);
100
+ if (!collection) throw new Error(`Collection "${collectionName}" not found`);
101
+ const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
102
+ collectionName,
103
+ collection.embeddingKey,
104
+ collection.tenantId
105
+ );
106
+ const entryId = (0, import_uuid2.v4)();
107
+ const now = (/* @__PURE__ */ new Date()).toISOString();
108
+ const doc = new import_documents.Document({
109
+ pageContent: data.content,
110
+ metadata: {
111
+ _id: entryId,
112
+ _created_at: now,
113
+ _updated_at: now,
114
+ ...data.metadata || {}
115
+ }
116
+ });
117
+ await vectorStore.addDocuments([doc]);
118
+ return { id: entryId, content: data.content, metadata: data.metadata || {} };
119
+ }
120
+ async getEntry(request, collectionName, entryId) {
121
+ const collection = await this.getCollectionByName(request, collectionName);
122
+ if (!collection) throw new Error(`Collection "${collectionName}" not found`);
123
+ const docs = await (0, import_core11.listCollectionEntries)(collectionName, collection.embeddingKey, collection.tenantId);
124
+ const doc = docs.find((d) => d.metadata._id === entryId);
125
+ if (!doc) return null;
126
+ return {
127
+ id: entryId,
128
+ content: doc.pageContent,
129
+ metadata: Object.fromEntries(
130
+ Object.entries(doc.metadata).filter(([k]) => !k.startsWith("_"))
131
+ )
132
+ };
133
+ }
134
+ async updateEntry(request, collectionName, entryId, data) {
135
+ const existing = await this.getEntry(request, collectionName, entryId);
136
+ if (!existing) return null;
137
+ const collection = await this.getCollectionByName(request, collectionName);
138
+ if (!collection) throw new Error(`Collection "${collectionName}" not found`);
139
+ const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
140
+ collectionName,
141
+ collection.embeddingKey,
142
+ collection.tenantId
143
+ );
144
+ try {
145
+ await vectorStore.delete({ filter: { _id: entryId } });
146
+ } catch {
147
+ }
148
+ const now = (/* @__PURE__ */ new Date()).toISOString();
149
+ const mergedMetadata = { ...existing.metadata, ...data.metadata || {} };
150
+ const doc = new import_documents.Document({
151
+ pageContent: data.content ?? existing.content,
152
+ metadata: { _id: entryId, _created_at: now, _updated_at: now, ...mergedMetadata }
153
+ });
154
+ await vectorStore.addDocuments([doc]);
155
+ return { id: entryId, content: data.content ?? existing.content, metadata: mergedMetadata };
156
+ }
157
+ async deleteEntry(request, collectionName, entryId) {
158
+ const collection = await this.getCollectionByName(request, collectionName);
159
+ if (!collection) throw new Error(`Collection "${collectionName}" not found`);
160
+ const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
161
+ collectionName,
162
+ collection.embeddingKey,
163
+ collection.tenantId
164
+ );
165
+ try {
166
+ await vectorStore.delete({ filter: { _id: entryId } });
167
+ return true;
168
+ } catch {
169
+ return false;
170
+ }
171
+ }
172
+ async searchEntries(request, collectionName, params) {
173
+ const collection = await this.getCollectionByName(request, collectionName);
174
+ if (!collection) throw new Error(`Collection "${collectionName}" not found`);
175
+ const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
176
+ collectionName,
177
+ collection.embeddingKey,
178
+ collection.tenantId
179
+ );
180
+ let embeddings;
181
+ try {
182
+ embeddings = import_core11.embeddingsLatticeManager.getEmbeddingsClient(collection.embeddingKey);
183
+ } catch {
184
+ embeddings = import_core11.embeddingsLatticeManager.getEmbeddingsClient("default");
185
+ }
186
+ const queryVector = await embeddings.embedQuery(params.query);
187
+ const results = await vectorStore.similaritySearchVectorWithScore(
188
+ queryVector,
189
+ params.top_k || 5,
190
+ params.filter
191
+ );
192
+ return results.map(([doc, score]) => ({
193
+ id: doc.metadata._id || (0, import_uuid2.v4)(),
194
+ content: doc.pageContent,
195
+ metadata: Object.fromEntries(
196
+ Object.entries(doc.metadata).filter(([k]) => !k.startsWith("_"))
197
+ ),
198
+ score
199
+ }));
200
+ }
201
+ };
202
+ }
203
+ });
204
+
33
205
  // src/utils/mime.ts
34
206
  function getContentTypeFromFilename(filename) {
35
207
  const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";
@@ -78,6 +250,74 @@ var init_mime = __esm({
78
250
  }
79
251
  });
80
252
 
253
+ // src/services/open/openToken.ts
254
+ var openToken_exports = {};
255
+ __export(openToken_exports, {
256
+ signOpenToken: () => signOpenToken,
257
+ verifyOpenToken: () => verifyOpenToken
258
+ });
259
+ function secret() {
260
+ return process.env.OPEN_TOKEN_SECRET || process.env.AUTH_TOKEN_SECRET || "open-token-dev-secret-fallback";
261
+ }
262
+ function signOpenToken(payload) {
263
+ const data = { ...payload, exp: payload.exp ?? Date.now() + 15 * 60 * 1e3 };
264
+ const encoded = Buffer.from(JSON.stringify(data)).toString("base64url");
265
+ const sig = (0, import_crypto5.createHmac)("sha256", secret()).update(encoded).digest("base64url");
266
+ return `${encoded}.${sig}`;
267
+ }
268
+ function verifyOpenToken(bearer) {
269
+ if (!bearer) return null;
270
+ const parts = bearer.split(".");
271
+ if (parts.length !== 2) return null;
272
+ const [encoded, sigStr] = parts;
273
+ if (!/^[A-Za-z0-9_-]{43}$/.test(sigStr)) return null;
274
+ try {
275
+ const actual = Buffer.from(sigStr, "base64url");
276
+ if (actual.toString("base64url") !== sigStr) return null;
277
+ const expected = (0, import_crypto5.createHmac)("sha256", secret()).update(encoded).digest();
278
+ if (actual.length !== expected.length || !(0, import_crypto5.timingSafeEqual)(actual, expected)) return null;
279
+ const payload = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
280
+ if (typeof payload !== "object" || payload === null) return null;
281
+ const p = payload;
282
+ if (typeof p.exp !== "number" || p.exp < Date.now() || p.kind !== "sandbox_token" && p.kind !== "browser_token" || typeof p.tenantId !== "string" || typeof p.projectId !== "string" || typeof p.grants !== "object") {
283
+ return null;
284
+ }
285
+ return p;
286
+ } catch {
287
+ return null;
288
+ }
289
+ }
290
+ var import_crypto5;
291
+ var init_openToken = __esm({
292
+ "src/services/open/openToken.ts"() {
293
+ "use strict";
294
+ import_crypto5 = require("crypto");
295
+ }
296
+ });
297
+
298
+ // src/services/open/previewToken.ts
299
+ function buildPreviewToken(ids) {
300
+ return signOpenToken({
301
+ kind: "browser_token",
302
+ tenantId: ids.tenantId,
303
+ workspaceId: ids.workspaceId,
304
+ projectId: ids.projectId,
305
+ preview: true,
306
+ grants: BROWSER_GRANTS
307
+ });
308
+ }
309
+ function buildPreviewTokenScript(ids) {
310
+ return `<script>window.__OPEN_API_TOKEN__=${JSON.stringify(buildPreviewToken(ids))};</script>`;
311
+ }
312
+ var BROWSER_GRANTS;
313
+ var init_previewToken = __esm({
314
+ "src/services/open/previewToken.ts"() {
315
+ "use strict";
316
+ init_openToken();
317
+ BROWSER_GRANTS = [{ domain: "kb", items: ["search"] }];
318
+ }
319
+ });
320
+
81
321
  // src/utils/path-containment.ts
82
322
  function isPathWithinRoot(rootPath, targetPath) {
83
323
  const relative2 = path2.relative(rootPath, targetPath);
@@ -333,7 +573,7 @@ async function createMcpServerConfig(request, reply) {
333
573
  message: "MCP server configuration with this key already exists"
334
574
  };
335
575
  }
336
- const id3 = body.id || (0, import_crypto7.randomUUID)();
576
+ const id3 = body.id || (0, import_crypto8.randomUUID)();
337
577
  const config = await store2.createConfig(tenantId, id3, body);
338
578
  try {
339
579
  await connectAndRegisterTools(config);
@@ -670,12 +910,12 @@ function registerMcpServerConfigRoutes(app2) {
670
910
  app2.post("/api/mcp-servers/:key/disconnect", disconnectMcpServer);
671
911
  app2.post("/api/mcp-servers/test-tools", testMcpServerTools);
672
912
  }
673
- var import_core30, import_crypto7;
913
+ var import_core30, import_crypto8;
674
914
  var init_mcp_configs = __esm({
675
915
  "src/controllers/mcp-configs.ts"() {
676
916
  "use strict";
677
917
  import_core30 = require("@axiom-lattice/core");
678
- import_crypto7 = require("crypto");
918
+ import_crypto8 = require("crypto");
679
919
  }
680
920
  });
681
921
 
@@ -1264,10 +1504,11 @@ function devModeDefaults() {
1264
1504
  const projectId = process.env[DEFAULT_PROJECT_ENV] || void 0;
1265
1505
  return { tenantId, projectId };
1266
1506
  }
1267
- var A2AAuthError, DEFAULT_TENANT_ENV, DEFAULT_TENANT_ID, DEFAULT_PROJECT_ENV, BEARER_SCHEME_RE, A2AAuthService;
1507
+ var import_protocols29, A2AAuthError, DEFAULT_TENANT_ENV, DEFAULT_TENANT_ID, DEFAULT_PROJECT_ENV, BEARER_SCHEME_RE, A2AAuthService;
1268
1508
  var init_A2AAuthService = __esm({
1269
1509
  "src/services/a2a/A2AAuthService.ts"() {
1270
1510
  "use strict";
1511
+ import_protocols29 = require("@axiom-lattice/protocols");
1271
1512
  A2AAuthError = class extends Error {
1272
1513
  constructor(message, statusCode) {
1273
1514
  super(message);
@@ -1321,20 +1562,26 @@ var init_A2AAuthService = __esm({
1321
1562
  const merged = new Map([...this.envKeyMap, ...storeMap]);
1322
1563
  const entry = merged.get(token);
1323
1564
  if (!entry) return { authenticated: false };
1565
+ const hasExplicitGrants = Array.isArray(entry.grants) && entry.grants.length > 0;
1566
+ const agentGrant = (0, import_protocols29.effectiveGrants)(entry).find((g) => g.domain === "agent");
1567
+ const assistantIds = hasExplicitGrants ? agentGrant?.items ?? [] : entry.assistantIds;
1324
1568
  return {
1325
1569
  authenticated: true,
1326
1570
  apiKey: token,
1327
1571
  tenantId: entry.tenantId,
1328
1572
  projectId: entry.projectId,
1329
- assistantIds: entry.assistantIds,
1573
+ assistantIds,
1330
1574
  source: header2 && BEARER_SCHEME_RE.test(header2) ? "bearer" : "x-api-key"
1331
1575
  };
1332
1576
  }
1333
1577
  /**
1334
- * Assert that the given assistant is reachable under the provided auth context:
1335
- * authenticated, within the key's assistant whitelist, same tenant, and exposed.
1578
+ * Assert that the given assistant is reachable under the provided auth
1579
+ * context: authenticated, same tenant, and within the key's whitelist.
1580
+ *
1581
+ * Authorization is unified on the API key — the agent no longer carries an
1582
+ * exposure flag; the key's assistant whitelist / grants is the single source.
1336
1583
  */
1337
- async assertExposedAgent(auth, assistantId) {
1584
+ async assertAgentAccess(auth, assistantId) {
1338
1585
  if (!auth.authenticated || !auth.tenantId) {
1339
1586
  throw new A2AAuthError("Unauthorized", 401);
1340
1587
  }
@@ -1343,12 +1590,9 @@ var init_A2AAuthService = __esm({
1343
1590
  throw new A2AAuthError("Agent not found", 404);
1344
1591
  }
1345
1592
  const whitelist = auth.assistantIds;
1346
- if (whitelist && whitelist.length > 0 && !whitelist.includes(assistantId)) {
1593
+ if (whitelist !== void 0 && !whitelist.includes(assistantId)) {
1347
1594
  throw new A2AAuthError("Agent not allowed for this key", 403);
1348
1595
  }
1349
- if (assistant.graphDefinition?.a2aExposure?.enabled !== true) {
1350
- throw new A2AAuthError("Agent not exposed", 404);
1351
- }
1352
1596
  return assistant;
1353
1597
  }
1354
1598
  /**
@@ -1356,7 +1600,7 @@ var init_A2AAuthService = __esm({
1356
1600
  * in the tenant and every assistant belongs to the same tenant.
1357
1601
  */
1358
1602
  async createKeyScoped(input) {
1359
- const { tenantId, projectId, assistantIds, label } = input;
1603
+ const { tenantId, projectId, assistantIds, grants, label } = input;
1360
1604
  if (!projectId) {
1361
1605
  throw new A2AAuthError("projectId is required", 400);
1362
1606
  }
@@ -1372,7 +1616,25 @@ var init_A2AAuthService = __esm({
1372
1616
  }
1373
1617
  }
1374
1618
  }
1375
- return this.keyStore.create({ tenantId, projectId, assistantIds, label });
1619
+ return this.keyStore.create({ tenantId, projectId, assistantIds, grants, label });
1620
+ }
1621
+ async updateKeyScoped(id3, tenantId, input) {
1622
+ const { projectId, assistantIds, grants, label } = input;
1623
+ if (projectId) {
1624
+ const project = await this.projectStore.getProjectById(tenantId, projectId);
1625
+ if (!project || project.tenantId !== tenantId) {
1626
+ throw new A2AAuthError("Project not found", 400);
1627
+ }
1628
+ }
1629
+ if (assistantIds && assistantIds.length > 0) {
1630
+ for (const aId of assistantIds) {
1631
+ const assistant = await this.assistantStore.getAssistantById(tenantId, aId);
1632
+ if (!assistant || assistant.tenantId !== tenantId) {
1633
+ throw new A2AAuthError("Assistant not found", 400);
1634
+ }
1635
+ }
1636
+ }
1637
+ return this.keyStore.update(id3, { label, projectId, assistantIds, grants });
1376
1638
  }
1377
1639
  extractToken(header2) {
1378
1640
  if (!header2) return void 0;
@@ -1387,42 +1649,45 @@ var init_A2AAuthService = __esm({
1387
1649
  });
1388
1650
 
1389
1651
  // src/services/a2a/A2AAgentCardBuilder.ts
1652
+ function deriveAgentSkills(assistant) {
1653
+ const enabled = assistant.graphDefinition?.middleware?.filter((m) => m.enabled) ?? [];
1654
+ return enabled.map((m) => ({
1655
+ id: m.type || m.id,
1656
+ name: m.name || m.type || m.id,
1657
+ description: m.description || "",
1658
+ tags: [],
1659
+ examples: []
1660
+ }));
1661
+ }
1390
1662
  function buildAgentCardForAssistant(params) {
1391
- const { assistant, exposure, baseUrl } = params;
1663
+ const { assistant, baseUrl } = params;
1392
1664
  const jsonRpcUrl = `${baseUrl}/api/a2a/agents/${assistant.id}/jsonrpc`;
1393
- const skills = (exposure?.skills ?? []).map((s) => ({
1394
- id: s.id,
1395
- name: s.name,
1396
- description: s.description,
1397
- tags: s.tags ?? [],
1398
- examples: s.examples ?? []
1399
- }));
1400
1665
  return {
1401
1666
  name: assistant.name,
1402
1667
  description: assistant.description ?? "",
1403
1668
  url: jsonRpcUrl,
1404
1669
  protocolVersion: "0.3.0",
1405
1670
  version: "1.0.0",
1406
- defaultInputModes: exposure?.inputModes ?? ["text"],
1407
- defaultOutputModes: exposure?.outputModes ?? ["text"],
1671
+ defaultInputModes: ["text"],
1672
+ defaultOutputModes: ["text"],
1408
1673
  capabilities: {
1409
1674
  streaming: true,
1410
1675
  pushNotifications: false,
1411
1676
  stateTransitionHistory: true
1412
1677
  },
1413
- skills,
1678
+ skills: deriveAgentSkills(assistant),
1414
1679
  additionalInterfaces: [{ transport: "JSONRPC", url: jsonRpcUrl }],
1415
1680
  securitySchemes: {
1416
1681
  bearer: {
1417
1682
  type: "http",
1418
1683
  scheme: "Bearer",
1419
- description: "A2A API key sent as an Authorization Bearer token"
1684
+ description: "API key sent as an Authorization Bearer token"
1420
1685
  },
1421
1686
  apiKeyHeader: {
1422
1687
  type: "apiKey",
1423
1688
  in: "header",
1424
1689
  name: "x-api-key",
1425
- description: "A2A API key sent via the x-api-key header"
1690
+ description: "API key sent via the x-api-key header"
1426
1691
  }
1427
1692
  },
1428
1693
  security: [{ bearer: [] }, { apiKeyHeader: [] }]
@@ -1896,13 +2161,20 @@ function agentMessage(text) {
1896
2161
  parts: [{ kind: "text", text }]
1897
2162
  };
1898
2163
  }
1899
- var import_uuid11, import_core66, import_protocols29, _AxiomAgentExecutor, AxiomAgentExecutor;
2164
+ function assistantHasGovernedTask(assistant) {
2165
+ const graph = assistant.graphDefinition;
2166
+ const middleware = graph && typeof graph === "object" ? graph.middleware : void 0;
2167
+ const taskMiddleware = Array.isArray(middleware) ? middleware.find((entry) => !!entry && typeof entry === "object" && entry.type === "task" && entry.enabled === true) : void 0;
2168
+ const allowedTools = taskMiddleware?.allowedTools;
2169
+ return !!taskMiddleware && (allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task")));
2170
+ }
2171
+ var import_uuid11, import_core66, import_protocols30, _AxiomAgentExecutor, AxiomAgentExecutor;
1900
2172
  var init_AxiomAgentExecutor = __esm({
1901
2173
  "src/services/a2a/AxiomAgentExecutor.ts"() {
1902
2174
  "use strict";
1903
2175
  import_uuid11 = require("uuid");
1904
2176
  import_core66 = require("@axiom-lattice/core");
1905
- import_protocols29 = require("@axiom-lattice/protocols");
2177
+ import_protocols30 = require("@axiom-lattice/protocols");
1906
2178
  init_a2aFileRefs();
1907
2179
  init_task_file_refs();
1908
2180
  _AxiomAgentExecutor = class _AxiomAgentExecutor {
@@ -1949,18 +2221,13 @@ var init_AxiomAgentExecutor = __esm({
1949
2221
  });
1950
2222
  };
1951
2223
  try {
1952
- try {
1953
- if (!this.taskStore) {
1954
- throw new Error("A2A governed execution requires a canonical task store");
1955
- }
1956
- this.assertGovernedTaskCapability();
1957
- } catch (err) {
2224
+ if (!this.taskStore) {
1958
2225
  const initialTask2 = createTask([]);
1959
- if (this.taskStore) await this.taskStore.save(initialTask2);
1960
2226
  bus.publish(initialTask2);
1961
- publishFailed(err);
2227
+ publishFailed(new Error("A2A execution requires a canonical task store"));
1962
2228
  return;
1963
2229
  }
2230
+ const governed = this.hasGovernedTaskCapability();
1964
2231
  const interruption = existing?.metadata?.interruption;
1965
2232
  const reviewInterruption = interruption && typeof interruption === "object" && interruption.type === "review_required";
1966
2233
  const reviewCommand = reviewInterruption ? this.reviewCommand(text) : void 0;
@@ -1997,13 +2264,13 @@ var init_AxiomAgentExecutor = __esm({
1997
2264
  const requestText = inputFiles.length > 0 ? `${text}
1998
2265
 
1999
2266
  ${formatFileRefsSection(inputFiles)}` : text;
2000
- const messageText = [
2267
+ const messageText = governed ? [
2001
2268
  `A2A governed task instruction: get and update the existing TaskItem ${ctx.taskId}; do not create a replacement task.`,
2002
2269
  "Before doing the requested work, initialize its canonical description through manage_task with ## Objective, ## Acceptance Criteria, and ## Belief State sections.",
2003
2270
  "Keep lifecycle status truthful. Change status only through manage_task set_status; complete with a nonblank result and structured beliefImpact, and use interrupted, failed, or cancelled when appropriate. Use manage_task update for content fields only.",
2004
2271
  "",
2005
2272
  requestText
2006
- ].join("\n");
2273
+ ].join("\n") : requestText;
2007
2274
  const continuationCommand = interruption && typeof interruption === "object" ? { resume: text } : void 0;
2008
2275
  const { messageId } = await agent.addMessage({
2009
2276
  input: { message: messageText },
@@ -2011,16 +2278,22 @@ ${formatFileRefsSection(inputFiles)}` : text;
2011
2278
  custom_run_config: { taskId: ctx.taskId }
2012
2279
  });
2013
2280
  const stream = agent.chunkStream(messageId, [
2014
- import_protocols29.MessageChunkTypes.MESSAGE_COMPLETED
2281
+ import_protocols30.MessageChunkTypes.MESSAGE_COMPLETED
2015
2282
  ]);
2283
+ let outputText = "";
2284
+ let interrupted = false;
2016
2285
  for await (const chunk of stream) {
2017
2286
  if (execution.canceled) return;
2018
2287
  const chunkType = chunk.type;
2019
2288
  const chunkText = chunk.data?.content ?? "";
2020
- if (chunkType === import_protocols29.MessageChunkTypes.INTERRUPT) {
2289
+ if (chunkType === import_protocols30.MessageChunkTypes.INTERRUPT) {
2290
+ interrupted = true;
2021
2291
  break;
2022
2292
  }
2023
- if (chunkType === import_protocols29.MessageChunkTypes.AI || chunkType === import_protocols29.MessageChunkTypes.TOOL) {
2293
+ if (chunkType === import_protocols30.MessageChunkTypes.AI && chunkText) {
2294
+ outputText += chunkText;
2295
+ }
2296
+ if (chunkType === import_protocols30.MessageChunkTypes.AI || chunkType === import_protocols30.MessageChunkTypes.TOOL) {
2024
2297
  if (chunkText) {
2025
2298
  bus.publish({
2026
2299
  kind: "status-update",
@@ -2037,6 +2310,48 @@ ${formatFileRefsSection(inputFiles)}` : text;
2037
2310
  }
2038
2311
  }
2039
2312
  if (execution.canceled) return;
2313
+ if (!governed) {
2314
+ if (interrupted) {
2315
+ const paused = {
2316
+ ...createTask(inputFiles),
2317
+ status: { state: "input-required", timestamp: isoNow() }
2318
+ };
2319
+ await this.taskStore.save(paused);
2320
+ bus.publish({
2321
+ kind: "status-update",
2322
+ taskId: ctx.taskId,
2323
+ contextId: ctx.contextId,
2324
+ status: { state: "input-required", timestamp: isoNow() },
2325
+ final: true
2326
+ });
2327
+ return;
2328
+ }
2329
+ const resultArtifact = {
2330
+ artifactId: (0, import_uuid11.v4)(),
2331
+ name: "result",
2332
+ parts: [{ kind: "text", text: outputText }]
2333
+ };
2334
+ const completed = {
2335
+ ...createTask(inputFiles),
2336
+ artifacts: [...existing?.artifacts ?? [], resultArtifact],
2337
+ status: { state: "completed", timestamp: isoNow() }
2338
+ };
2339
+ await this.taskStore.save(completed);
2340
+ bus.publish({
2341
+ kind: "artifact-update",
2342
+ taskId: ctx.taskId,
2343
+ contextId: ctx.contextId,
2344
+ artifact: resultArtifact
2345
+ });
2346
+ bus.publish({
2347
+ kind: "status-update",
2348
+ taskId: ctx.taskId,
2349
+ contextId: ctx.contextId,
2350
+ status: { state: "completed", timestamp: isoNow() },
2351
+ final: true
2352
+ });
2353
+ return;
2354
+ }
2040
2355
  const canonical = await this.taskStore.load(ctx.taskId);
2041
2356
  const canonicalState = canonical?.status.state;
2042
2357
  if (canonicalState === "completed") {
@@ -2116,15 +2431,8 @@ ${formatFileRefsSection(inputFiles)}` : text;
2116
2431
  taskKey(taskId) {
2117
2432
  return `${this.scope.tenantId}:${this.scope.projectId}:${this.scope.assistantId}:${taskId}`;
2118
2433
  }
2119
- assertGovernedTaskCapability() {
2120
- const graph = this.assistant.graphDefinition;
2121
- const middleware = graph && typeof graph === "object" ? graph.middleware : void 0;
2122
- const taskMiddleware = Array.isArray(middleware) ? middleware.find((entry) => !!entry && typeof entry === "object" && entry.type === "task" && entry.enabled === true) : void 0;
2123
- const allowedTools = taskMiddleware?.allowedTools;
2124
- const exposesManageTask = taskMiddleware && (allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task")));
2125
- if (!exposesManageTask) {
2126
- throw new Error("A2A execution requires enabled TaskMiddleware exposing manage_task");
2127
- }
2434
+ hasGovernedTaskCapability() {
2435
+ return assistantHasGovernedTask(this.assistant);
2128
2436
  }
2129
2437
  /**
2130
2438
  * Cancel a running task: resolve its thread id from the task store, abort the
@@ -2444,27 +2752,15 @@ function getHeader(value) {
2444
2752
  if (Array.isArray(value)) return value[0];
2445
2753
  return value;
2446
2754
  }
2447
- function getTenantHeader(request) {
2448
- return getHeader(request.headers["x-tenant-id"]) ?? "default";
2449
- }
2450
2755
  function getSessionUser(request) {
2451
2756
  return request.user;
2452
2757
  }
2453
- function getSessionTenantId(request) {
2454
- const userTenantId = getSessionUser(request)?.tenantId;
2455
- if (userTenantId) return userTenantId;
2456
- return getTenantHeader(request);
2457
- }
2458
2758
  function buildBaseUrl(request) {
2459
2759
  const protocol = getHeader(request.headers["x-forwarded-proto"]) ?? request.protocol;
2460
2760
  const forwardedHost = getHeader(request.headers["x-forwarded-host"]);
2461
2761
  const host = forwardedHost ? forwardedHost.split(",")[0].trim() : request.host;
2462
2762
  return `${protocol}://${host}`;
2463
2763
  }
2464
- function getExposure(assistant) {
2465
- const graphDefinition = assistant.graphDefinition;
2466
- return graphDefinition?.a2aExposure;
2467
- }
2468
2764
  function makeFileIngestor(tenantId, maxFileBytes) {
2469
2765
  return new A2AFileIngestor({
2470
2766
  saveProjectFile: (params) => saveProjectFile({ tenantId, ...params }),
@@ -2504,33 +2800,6 @@ function registerA2AStandardRoutes(app2, deps) {
2504
2800
  const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => (0, import_core67.getStoreLattice)("default", "taskWorkItem").store);
2505
2801
  const getAssistantStore2 = deps.getAssistantStore ?? (() => (0, import_core67.getStoreLattice)("default", "assistant").store);
2506
2802
  const getProjectStore2 = deps.getProjectStore ?? (() => (0, import_core67.getStoreLattice)("default", "project").store);
2507
- const getTenantStore = deps.getTenantStore ?? (() => {
2508
- try {
2509
- return (0, import_core67.getStoreLattice)("default", "tenant").store;
2510
- } catch {
2511
- return void 0;
2512
- }
2513
- });
2514
- async function findExposedAssistant(request, assistantId) {
2515
- const tenantId = getSessionTenantId(request);
2516
- const direct = await getAssistantStore2().getAssistantById(
2517
- tenantId,
2518
- assistantId
2519
- );
2520
- if (direct) return direct;
2521
- const tenantStore = getTenantStore();
2522
- if (!tenantStore) return null;
2523
- const tenants = await tenantStore.getAllTenants();
2524
- for (const tenant2 of tenants) {
2525
- if (tenant2.id === tenantId) continue;
2526
- const found = await getAssistantStore2().getAssistantById(
2527
- tenant2.id,
2528
- assistantId
2529
- );
2530
- if (found) return found;
2531
- }
2532
- return null;
2533
- }
2534
2803
  const runtimeRegistry = deps.runtimeRegistry ?? new A2ARuntimeRegistry();
2535
2804
  app2.addHook("onClose", async () => {
2536
2805
  runtimeRegistry.close();
@@ -2538,19 +2807,32 @@ function registerA2AStandardRoutes(app2, deps) {
2538
2807
  app2.get(
2539
2808
  "/api/a2a/agents/:assistantId/.well-known/agent-card.json",
2540
2809
  async (request, reply) => {
2541
- const assistant = await findExposedAssistant(
2542
- request,
2543
- request.params.assistantId
2810
+ const auth = await authService.authenticate(
2811
+ getHeader(request.headers.authorization) ?? getHeader(request.headers["x-api-key"])
2544
2812
  );
2545
- if (!assistant || getExposure(assistant)?.enabled !== true) {
2546
- reply.status(404).send({ error: "Not found" });
2813
+ if (!auth.authenticated) {
2814
+ reply.status(401).send({ error: "Unauthorized" });
2815
+ return;
2816
+ }
2817
+ let assistant;
2818
+ try {
2819
+ assistant = await authService.assertAgentAccess(
2820
+ auth,
2821
+ request.params.assistantId
2822
+ );
2823
+ } catch (err) {
2824
+ if (err instanceof A2AAuthError) {
2825
+ reply.status(err.statusCode).send({ error: err.message });
2826
+ return;
2827
+ }
2828
+ request.log.error({ err }, "a2a:agent-card:auth");
2829
+ reply.status(500).send({ error: "Internal error" });
2547
2830
  return;
2548
2831
  }
2549
2832
  reply.header("A2A-Version", "0.3");
2550
2833
  reply.status(200).send(
2551
2834
  buildAgentCardForAssistant({
2552
2835
  assistant,
2553
- exposure: getExposure(assistant),
2554
2836
  baseUrl: buildBaseUrl(request)
2555
2837
  })
2556
2838
  );
@@ -2569,7 +2851,7 @@ function registerA2AStandardRoutes(app2, deps) {
2569
2851
  }
2570
2852
  let assistant;
2571
2853
  try {
2572
- assistant = await authService.assertExposedAgent(
2854
+ assistant = await authService.assertAgentAccess(
2573
2855
  auth,
2574
2856
  request.params.assistantId
2575
2857
  );
@@ -2595,7 +2877,7 @@ function registerA2AStandardRoutes(app2, deps) {
2595
2877
  };
2596
2878
  const internalTasks = resolveTasks();
2597
2879
  const workItems = resolveTaskWorkItems();
2598
- const taskStore = new A2ATaskStoreAdapter({
2880
+ const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
2599
2881
  taskStore: internalTasks,
2600
2882
  workItemStore: workItems,
2601
2883
  lifecycle: (0, import_core67.createTaskLifecycleService)({
@@ -2603,12 +2885,11 @@ function registerA2AStandardRoutes(app2, deps) {
2603
2885
  workItemStore: workItems
2604
2886
  }),
2605
2887
  scope
2606
- });
2888
+ }) : new import_server2.InMemoryTaskStore();
2607
2889
  const ingestor = makeFileIngestor(scope.tenantId, deps.maxFileBytes);
2608
2890
  const executor = new AxiomAgentExecutor({ scope, assistant, taskStore, ingestor });
2609
2891
  const card = buildAgentCardForAssistant({
2610
2892
  assistant,
2611
- exposure: getExposure(assistant),
2612
2893
  baseUrl: buildBaseUrl(request)
2613
2894
  });
2614
2895
  let runtimeLease;
@@ -2714,7 +2995,7 @@ data: ${JSON.stringify(errorResponse)}
2714
2995
  const ingestor = makeFileIngestor(scope.tenantId, deps.maxFileBytes);
2715
2996
  const internalTasks = resolveTasks();
2716
2997
  const workItems = resolveTaskWorkItems();
2717
- const taskStore = new A2ATaskStoreAdapter({
2998
+ const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
2718
2999
  taskStore: internalTasks,
2719
3000
  workItemStore: workItems,
2720
3001
  lifecycle: (0, import_core67.createTaskLifecycleService)({
@@ -2722,7 +3003,7 @@ data: ${JSON.stringify(errorResponse)}
2722
3003
  workItemStore: workItems
2723
3004
  }),
2724
3005
  scope
2725
- });
3006
+ }) : new import_server2.InMemoryTaskStore();
2726
3007
  const executor = new AxiomAgentExecutor({ scope, assistant, taskStore, ingestor });
2727
3008
  const taskId = (0, import_uuid12.v4)();
2728
3009
  const contextId = (0, import_uuid12.v4)();
@@ -2857,6 +3138,7 @@ async function handleApiKeyCreate(request, reply) {
2857
3138
  tenantId,
2858
3139
  projectId: body.projectId,
2859
3140
  assistantIds: body.assistantIds,
3141
+ grants: body.grants,
2860
3142
  label: body.label
2861
3143
  });
2862
3144
  await refreshStoreKeyMap();
@@ -2870,6 +3152,36 @@ async function handleApiKeyCreate(request, reply) {
2870
3152
  reply.status(500).send({ success: false, error: "Failed to create key" });
2871
3153
  }
2872
3154
  }
3155
+ async function handleApiKeyUpdate(request, reply) {
3156
+ if (!_a2aKeyStore || !_a2aAuthService) {
3157
+ reply.status(501).send({ error: "Key store not configured" });
3158
+ return;
3159
+ }
3160
+ if (!await authorizeKeyMutation(request, reply)) return;
3161
+ const body = request.body;
3162
+ const tenantId = getUserTenantId(request);
3163
+ try {
3164
+ const record = await _a2aAuthService.updateKeyScoped(
3165
+ request.params.id,
3166
+ tenantId,
3167
+ {
3168
+ label: body.label,
3169
+ projectId: body.projectId,
3170
+ assistantIds: body.assistantIds,
3171
+ grants: body.grants
3172
+ }
3173
+ );
3174
+ await refreshStoreKeyMap();
3175
+ reply.status(200).send({ success: true, data: record });
3176
+ } catch (err) {
3177
+ if (err instanceof A2AAuthError) {
3178
+ reply.status(err.statusCode).send({ success: false, error: err.message });
3179
+ return;
3180
+ }
3181
+ request.log.error({ err }, "a2a:keys:update");
3182
+ reply.status(500).send({ success: false, error: "Failed to update key" });
3183
+ }
3184
+ }
2873
3185
  async function handleApiKeyDelete(request, reply) {
2874
3186
  if (!await authorizeKeyMutation(request, reply)) return;
2875
3187
  await _a2aKeyStore.delete(request.params.id);
@@ -2896,36 +3208,43 @@ async function handleApiKeyRotate(request, reply) {
2896
3208
  }
2897
3209
  function registerA2ARoutes(app2) {
2898
3210
  const options = { preHandler: requireTenantSession };
2899
- app2.get(
2900
- "/api/a2a/keys",
2901
- options,
2902
- handleApiKeyList
2903
- );
2904
- app2.post(
2905
- "/api/a2a/keys",
2906
- options,
2907
- handleApiKeyCreate
2908
- );
2909
- app2.delete(
2910
- "/api/a2a/keys/:id",
2911
- options,
2912
- handleApiKeyDelete
2913
- );
2914
- app2.post(
2915
- "/api/a2a/keys/:id/disable",
2916
- options,
2917
- handleApiKeyDisable
2918
- );
2919
- app2.post(
2920
- "/api/a2a/keys/:id/enable",
2921
- options,
2922
- handleApiKeyEnable
2923
- );
2924
- app2.post(
2925
- "/api/a2a/keys/:id/rotate",
2926
- options,
2927
- handleApiKeyRotate
2928
- );
3211
+ for (const base of ["/api/keys", "/api/a2a/keys"]) {
3212
+ app2.get(
3213
+ base,
3214
+ options,
3215
+ handleApiKeyList
3216
+ );
3217
+ app2.post(
3218
+ base,
3219
+ options,
3220
+ handleApiKeyCreate
3221
+ );
3222
+ app2.delete(
3223
+ `${base}/:id`,
3224
+ options,
3225
+ handleApiKeyDelete
3226
+ );
3227
+ app2.post(
3228
+ `${base}/:id/disable`,
3229
+ options,
3230
+ handleApiKeyDisable
3231
+ );
3232
+ app2.post(
3233
+ `${base}/:id/enable`,
3234
+ options,
3235
+ handleApiKeyEnable
3236
+ );
3237
+ app2.post(
3238
+ `${base}/:id/rotate`,
3239
+ options,
3240
+ handleApiKeyRotate
3241
+ );
3242
+ app2.patch(
3243
+ `${base}/:id`,
3244
+ options,
3245
+ handleApiKeyUpdate
3246
+ );
3247
+ }
2929
3248
  }
2930
3249
  var _a2aKeyStore, _a2aAuthService;
2931
3250
  var init_a2a = __esm({
@@ -2937,233 +3256,1358 @@ var init_a2a = __esm({
2937
3256
  }
2938
3257
  });
2939
3258
 
2940
- // src/services/open/openCredentials.ts
2941
- function resolveSpikeCredential(token) {
2942
- if (!token) return null;
2943
- return SPIKE_CREDENTIALS.get(token) ?? null;
2944
- }
2945
- var SPIKE_CREDENTIALS;
2946
- var init_openCredentials = __esm({
2947
- "src/services/open/openCredentials.ts"() {
3259
+ // src/services/open/openAuthService.ts
3260
+ var import_protocols31, OpenAuthService;
3261
+ var init_openAuthService = __esm({
3262
+ "src/services/open/openAuthService.ts"() {
2948
3263
  "use strict";
2949
- SPIKE_CREDENTIALS = /* @__PURE__ */ new Map([
2950
- [
2951
- "spike_all",
2952
- {
2953
- credentialId: "spike_all",
3264
+ import_protocols31 = require("@axiom-lattice/protocols");
3265
+ OpenAuthService = class {
3266
+ constructor(keyStore) {
3267
+ this.keyStore = keyStore;
3268
+ }
3269
+ async resolve(bearer) {
3270
+ if (!bearer) return null;
3271
+ const record = await this.keyStore.findByKey(bearer);
3272
+ if (!record || !record.enabled) return null;
3273
+ return {
3274
+ credentialId: record.id,
2954
3275
  kind: "api_key",
2955
- tenantId: "spike-tenant",
2956
- projectId: "spike-project",
2957
- grants: [{ domain: "kb" }, { domain: "sandbox" }]
3276
+ tenantId: record.tenantId,
3277
+ projectId: record.projectId,
3278
+ grants: (0, import_protocols31.effectiveGrants)(record)
3279
+ };
3280
+ }
3281
+ };
3282
+ }
3283
+ });
3284
+
3285
+ // src/services/open/openCredentialService.ts
3286
+ var import_protocols32, OpenCredentialService;
3287
+ var init_openCredentialService = __esm({
3288
+ "src/services/open/openCredentialService.ts"() {
3289
+ "use strict";
3290
+ import_protocols32 = require("@axiom-lattice/protocols");
3291
+ init_openToken();
3292
+ init_openAuthService();
3293
+ OpenCredentialService = class {
3294
+ constructor(keyStore, projectStore) {
3295
+ this.projectStore = projectStore;
3296
+ this.auth = new OpenAuthService(keyStore);
3297
+ }
3298
+ async resolve(bearer) {
3299
+ if (!bearer) return null;
3300
+ const token = verifyOpenToken(bearer);
3301
+ if (token) {
3302
+ if (token.kind === "sandbox_token") {
3303
+ return {
3304
+ credentialId: `sandbox:${token.sandboxName}`,
3305
+ kind: "sandbox_token",
3306
+ tenantId: token.tenantId,
3307
+ projectId: token.projectId,
3308
+ workspaceId: token.workspaceId,
3309
+ sandboxName: token.sandboxName,
3310
+ grants: token.grants
3311
+ };
3312
+ }
3313
+ return {
3314
+ credentialId: `browser:${token.preview ? "preview:" : ""}${token.shareId ?? token.tenantId}`,
3315
+ kind: "browser_token",
3316
+ tenantId: token.tenantId,
3317
+ projectId: token.projectId,
3318
+ workspaceId: token.workspaceId,
3319
+ grants: token.grants
3320
+ };
2958
3321
  }
2959
- ],
2960
- [
2961
- "spike_kb",
2962
- {
2963
- credentialId: "spike_kb",
2964
- kind: "api_key",
2965
- tenantId: "spike-tenant",
2966
- projectId: "spike-project",
2967
- grants: [{ domain: "kb" }]
3322
+ const ctx = await this.auth.resolve(bearer);
3323
+ if (!ctx) return null;
3324
+ let workspaceId;
3325
+ if (this.projectStore) {
3326
+ try {
3327
+ const project = await this.projectStore.getProjectById(
3328
+ ctx.tenantId,
3329
+ ctx.projectId
3330
+ );
3331
+ workspaceId = project?.workspaceId;
3332
+ } catch {
3333
+ }
2968
3334
  }
2969
- ]
2970
- ]);
3335
+ return {
3336
+ credentialId: ctx.credentialId,
3337
+ kind: "api_key",
3338
+ tenantId: ctx.tenantId,
3339
+ projectId: ctx.projectId,
3340
+ workspaceId,
3341
+ grants: ctx.grants
3342
+ };
3343
+ }
3344
+ };
2971
3345
  }
2972
3346
  });
2973
3347
 
2974
- // src/services/open/mcpTransportRegistry.ts
2975
- var mcpTransportRegistry_exports = {};
2976
- __export(mcpTransportRegistry_exports, {
2977
- clearSessions: () => clearSessions,
2978
- deleteSession: () => deleteSession,
2979
- getSession: () => getSession,
2980
- registerSession: () => registerSession
2981
- });
2982
- async function registerSession(sessionId, credential, parts) {
2983
- sessions.set(sessionId, { ...parts, credential });
2984
- }
2985
- async function getSession(sessionId) {
2986
- return sessions.get(sessionId) ?? null;
3348
+ // src/services/open/openAuditService.ts
3349
+ function redactArgs(args, depth = 0) {
3350
+ if (args === null || typeof args !== "object") return args;
3351
+ if (depth > 6) return "[depth-limit]";
3352
+ if (Array.isArray(args)) return args.map((v) => redactArgs(v, depth + 1));
3353
+ const out = {};
3354
+ for (const [key, value] of Object.entries(args)) {
3355
+ out[key] = SENSITIVE_KEY.test(key) ? "[redacted]" : redactArgs(value, depth + 1);
3356
+ }
3357
+ return out;
2987
3358
  }
2988
- async function deleteSession(sessionId) {
2989
- const entry = sessions.get(sessionId);
2990
- if (!entry) return;
2991
- sessions.delete(sessionId);
2992
- await entry.transport.close().catch(() => void 0);
2993
- await entry.server.close().catch(() => void 0);
3359
+ function truncate2(value) {
3360
+ const s = JSON.stringify(value ?? null);
3361
+ if (s.length <= MAX_ARGS_CHARS) return value ?? null;
3362
+ return `${s.slice(0, MAX_ARGS_CHARS)}...[truncated]`;
2994
3363
  }
2995
- async function clearSessions() {
2996
- for (const sessionId of [...sessions.keys()]) {
2997
- await deleteSession(sessionId);
3364
+ var SENSITIVE_KEY, MAX_ARGS_CHARS, OpenAuditService;
3365
+ var init_openAuditService = __esm({
3366
+ "src/services/open/openAuditService.ts"() {
3367
+ "use strict";
3368
+ SENSITIVE_KEY = /pass(word)?|secret|token|api[-_]?key|authorization|credential/i;
3369
+ MAX_ARGS_CHARS = 2e3;
3370
+ OpenAuditService = class {
3371
+ constructor(store2) {
3372
+ this.store = store2;
3373
+ }
3374
+ async record(input) {
3375
+ try {
3376
+ await this.store.append({
3377
+ ...input,
3378
+ args: truncate2(redactArgs(input.args))
3379
+ });
3380
+ } catch {
3381
+ }
3382
+ }
3383
+ };
3384
+ }
3385
+ });
3386
+
3387
+ // src/services/open/openRateLimiter.ts
3388
+ var OpenRateLimiter;
3389
+ var init_openRateLimiter = __esm({
3390
+ "src/services/open/openRateLimiter.ts"() {
3391
+ "use strict";
3392
+ OpenRateLimiter = class {
3393
+ constructor(options = {}) {
3394
+ this.buckets = /* @__PURE__ */ new Map();
3395
+ this.defaultPerMinute = options.defaultPerMinute ?? 60;
3396
+ this.agentPerMinute = options.agentPerMinute ?? 10;
3397
+ }
3398
+ bucketClass(domain) {
3399
+ return domain === "agent" ? "agent" : "default";
3400
+ }
3401
+ limitFor(domain) {
3402
+ return domain === "agent" ? this.agentPerMinute : this.defaultPerMinute;
3403
+ }
3404
+ /** Consume one token; denied calls report how long to wait. */
3405
+ check(credentialId, domain) {
3406
+ const limit = this.limitFor(domain);
3407
+ const key = `${this.bucketClass(domain)}:${credentialId}`;
3408
+ const now = Date.now();
3409
+ const bucket = this.buckets.get(key) ?? { tokens: limit, updatedAt: now };
3410
+ const refillPerMs = limit / 6e4;
3411
+ const elapsed = now - bucket.updatedAt;
3412
+ bucket.tokens = Math.min(limit, bucket.tokens + elapsed * refillPerMs);
3413
+ bucket.updatedAt = now;
3414
+ if (bucket.tokens >= 1) {
3415
+ bucket.tokens -= 1;
3416
+ this.buckets.set(key, bucket);
3417
+ return { allowed: true };
3418
+ }
3419
+ this.buckets.set(key, bucket);
3420
+ const deficit = 1 - bucket.tokens;
3421
+ return { allowed: false, retryAfterMs: Math.ceil(deficit / refillPerMs) };
3422
+ }
3423
+ /** Test helper. */
3424
+ reset() {
3425
+ this.buckets.clear();
3426
+ }
3427
+ };
2998
3428
  }
3429
+ });
3430
+
3431
+ // src/services/open/openLattice.ts
3432
+ var openLattice_exports = {};
3433
+ __export(openLattice_exports, {
3434
+ ActionNotFoundError: () => ActionNotFoundError,
3435
+ GrantDeniedError: () => GrantDeniedError,
3436
+ OpenLattice: () => OpenLattice,
3437
+ ValidationError: () => ValidationError,
3438
+ openLattice: () => openLattice
3439
+ });
3440
+ function effectiveToolName(domain, action) {
3441
+ return action.toolName ?? `${domain}_${action.name}`;
2999
3442
  }
3000
- var sessions;
3001
- var init_mcpTransportRegistry = __esm({
3002
- "src/services/open/mcpTransportRegistry.ts"() {
3443
+ function grantCoversAction(grants, domain, action) {
3444
+ return grants.some(
3445
+ (g) => g.domain === domain && (!g.items || g.items.length === 0 || g.items.includes(action))
3446
+ );
3447
+ }
3448
+ var import_zod16, GrantDeniedError, ValidationError, ActionNotFoundError, OpenLattice, openLattice;
3449
+ var init_openLattice = __esm({
3450
+ "src/services/open/openLattice.ts"() {
3451
+ "use strict";
3452
+ import_zod16 = require("zod");
3453
+ GrantDeniedError = class extends Error {
3454
+ constructor(toolName) {
3455
+ super(`GRANT_DENIED: no grant covers ${toolName}`);
3456
+ this.code = "GRANT_DENIED";
3457
+ this.name = "GrantDeniedError";
3458
+ }
3459
+ };
3460
+ ValidationError = class extends Error {
3461
+ constructor(detail) {
3462
+ super(`VALIDATION_FAILED: ${detail}`);
3463
+ this.code = "VALIDATION_FAILED";
3464
+ this.name = "ValidationError";
3465
+ }
3466
+ };
3467
+ ActionNotFoundError = class extends Error {
3468
+ constructor(toolName) {
3469
+ super(`ACTION_NOT_FOUND: ${toolName}`);
3470
+ this.code = "ACTION_NOT_FOUND";
3471
+ this.name = "ActionNotFoundError";
3472
+ }
3473
+ };
3474
+ OpenLattice = class {
3475
+ constructor() {
3476
+ this.providers = /* @__PURE__ */ new Map();
3477
+ }
3478
+ /** Register a provider; domain+action collisions are rejected. */
3479
+ register(declaration) {
3480
+ const existing = this.providers.get(declaration.domain);
3481
+ if (existing) {
3482
+ throw new Error(
3483
+ `Open provider domain already registered: ${declaration.domain}`
3484
+ );
3485
+ }
3486
+ const actions = /* @__PURE__ */ new Map();
3487
+ for (const action of declaration.actions) {
3488
+ actions.set(action.name, action);
3489
+ }
3490
+ this.providers.set(declaration.domain, { declaration, actions });
3491
+ }
3492
+ /** Tool descriptors visible under the given grants (default-deny). */
3493
+ async catalogFor(grants, ctx) {
3494
+ const catalog = [];
3495
+ for (const [domain, entry] of this.providers) {
3496
+ for (const [actionName, action] of entry.actions) {
3497
+ if (!grantCoversAction(grants, domain, actionName)) continue;
3498
+ catalog.push({
3499
+ toolName: effectiveToolName(domain, action),
3500
+ domain,
3501
+ action: actionName,
3502
+ description: action.description,
3503
+ schema: action.schema,
3504
+ annotations: action.annotations
3505
+ });
3506
+ }
3507
+ if (entry.declaration.instanceActions) {
3508
+ const instances = await entry.declaration.instanceActions(grants, ctx);
3509
+ for (const action of instances) {
3510
+ catalog.push({
3511
+ toolName: effectiveToolName(domain, action),
3512
+ domain,
3513
+ action: action.name,
3514
+ description: action.description,
3515
+ schema: action.schema,
3516
+ annotations: action.annotations
3517
+ });
3518
+ }
3519
+ }
3520
+ }
3521
+ return catalog;
3522
+ }
3523
+ /**
3524
+ * Full grantable catalog (unfiltered by grants) for the management picker.
3525
+ * Instance domains (agent) return an empty item list — the catalog service
3526
+ * fills their per-tenant instances.
3527
+ */
3528
+ describeCatalog() {
3529
+ const domains = [];
3530
+ for (const [domain, entry] of this.providers) {
3531
+ const declaration = entry.declaration;
3532
+ const items = [];
3533
+ if ((declaration.itemKind ?? "action") === "action") {
3534
+ for (const [actionName, action] of entry.actions) {
3535
+ items.push({
3536
+ id: actionName,
3537
+ toolName: `${domain}_${actionName}`,
3538
+ label: action.label ?? actionName,
3539
+ description: action.description,
3540
+ annotations: action.annotations
3541
+ });
3542
+ }
3543
+ }
3544
+ domains.push({
3545
+ domain,
3546
+ label: declaration.label ?? domain,
3547
+ source: declaration.source ?? "builtin",
3548
+ scopeKind: declaration.scopeKind ?? "tenant",
3549
+ itemKind: declaration.itemKind ?? "action",
3550
+ items
3551
+ });
3552
+ }
3553
+ return domains;
3554
+ }
3555
+ async resolveAction(grants, toolName, ctx) {
3556
+ for (const [domain, entry] of this.providers) {
3557
+ for (const [actionName, action] of entry.actions) {
3558
+ if (effectiveToolName(domain, action) === toolName) {
3559
+ return { domain, entry, action, actionName };
3560
+ }
3561
+ }
3562
+ if (entry.declaration.instanceActions) {
3563
+ const instances = await entry.declaration.instanceActions(grants, ctx);
3564
+ for (const action of instances) {
3565
+ if (effectiveToolName(domain, action) === toolName) {
3566
+ return { domain, entry, action, actionName: action.name };
3567
+ }
3568
+ }
3569
+ }
3570
+ }
3571
+ return null;
3572
+ }
3573
+ /** Whether the given grants cover a tool (`{domain}_{action}`). */
3574
+ isAllowed(grants, toolName) {
3575
+ const sep2 = toolName.indexOf("_");
3576
+ const domain = sep2 > 0 ? toolName.slice(0, sep2) : toolName;
3577
+ const action = sep2 > 0 ? toolName.slice(sep2 + 1) : "";
3578
+ return grantCoversAction(grants, domain, action);
3579
+ }
3580
+ /** The single credential-driven execution path. */
3581
+ async invoke(grants, ctx, toolName, args) {
3582
+ const resolved = await this.resolveAction(grants, toolName, { tenantId: ctx.tenantId });
3583
+ if (!resolved) {
3584
+ throw new ActionNotFoundError(toolName);
3585
+ }
3586
+ if (!grantCoversAction(grants, resolved.domain, resolved.actionName)) {
3587
+ throw new GrantDeniedError(toolName);
3588
+ }
3589
+ const resolvedSchema = resolved.action.schema;
3590
+ const parsed = "safeParse" in resolvedSchema ? resolvedSchema.safeParse(args) : import_zod16.z.object(resolvedSchema).safeParse(args);
3591
+ if (!parsed.success) {
3592
+ throw new ValidationError(parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "));
3593
+ }
3594
+ return resolved.entry.declaration.execute(resolved.actionName, parsed.data, ctx);
3595
+ }
3596
+ };
3597
+ openLattice = new OpenLattice();
3598
+ }
3599
+ });
3600
+
3601
+ // src/services/open/mcpTransportRegistry.ts
3602
+ var mcpTransportRegistry_exports = {};
3603
+ __export(mcpTransportRegistry_exports, {
3604
+ clearSessions: () => clearSessions,
3605
+ deleteSession: () => deleteSession,
3606
+ getSession: () => getSession,
3607
+ registerSession: () => registerSession
3608
+ });
3609
+ async function registerSession(sessionId, credential, parts) {
3610
+ sessions.set(sessionId, { ...parts, credential });
3611
+ }
3612
+ async function getSession(sessionId) {
3613
+ return sessions.get(sessionId) ?? null;
3614
+ }
3615
+ async function deleteSession(sessionId) {
3616
+ const entry = sessions.get(sessionId);
3617
+ if (!entry) return;
3618
+ sessions.delete(sessionId);
3619
+ await entry.transport.close().catch(() => void 0);
3620
+ await entry.server.close().catch(() => void 0);
3621
+ }
3622
+ async function clearSessions() {
3623
+ for (const sessionId of [...sessions.keys()]) {
3624
+ await deleteSession(sessionId);
3625
+ }
3626
+ }
3627
+ var sessions;
3628
+ var init_mcpTransportRegistry = __esm({
3629
+ "src/services/open/mcpTransportRegistry.ts"() {
3630
+ "use strict";
3631
+ sessions = /* @__PURE__ */ new Map();
3632
+ }
3633
+ });
3634
+
3635
+ // src/services/open/openMcpServer.ts
3636
+ async function buildSessionServer(options) {
3637
+ const { credential, grants, catalog, invoke } = options;
3638
+ const server = new import_mcp.McpServer(OPEN_MCP_SERVER_INFO);
3639
+ for (const tool of catalog) {
3640
+ server.registerTool(
3641
+ tool.toolName,
3642
+ {
3643
+ description: tool.description,
3644
+ inputSchema: tool.schema,
3645
+ annotations: {
3646
+ readOnlyHint: tool.annotations.readOnlyHint,
3647
+ destructiveHint: tool.annotations.destructiveHint
3648
+ }
3649
+ },
3650
+ async (args) => {
3651
+ const result = await invoke(tool.toolName, args);
3652
+ return { content: result.content, isError: result.isError };
3653
+ }
3654
+ );
3655
+ }
3656
+ const transport = new import_streamableHttp.StreamableHTTPServerTransport({
3657
+ sessionIdGenerator: () => {
3658
+ const sessionId = (0, import_crypto13.randomUUID)();
3659
+ void registerSession(
3660
+ sessionId,
3661
+ { credentialId: credential.credentialId, kind: credential.kind },
3662
+ { server, transport }
3663
+ );
3664
+ return sessionId;
3665
+ },
3666
+ enableJsonResponse: true,
3667
+ onsessionclosed: (closedId) => {
3668
+ void Promise.resolve().then(() => (init_mcpTransportRegistry(), mcpTransportRegistry_exports)).then(
3669
+ ({ deleteSession: deleteSession2 }) => deleteSession2(closedId)
3670
+ );
3671
+ }
3672
+ });
3673
+ await server.connect(transport);
3674
+ return { server, transport, tools: catalog };
3675
+ }
3676
+ var import_mcp, import_streamableHttp, import_crypto13, OPEN_MCP_SERVER_INFO;
3677
+ var init_openMcpServer = __esm({
3678
+ "src/services/open/openMcpServer.ts"() {
3679
+ "use strict";
3680
+ import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
3681
+ import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
3682
+ import_crypto13 = require("crypto");
3683
+ init_mcpTransportRegistry();
3684
+ OPEN_MCP_SERVER_INFO = {
3685
+ name: "axiom-open",
3686
+ version: "0.1.0"
3687
+ };
3688
+ }
3689
+ });
3690
+
3691
+ // src/routes/open.ts
3692
+ var open_exports = {};
3693
+ __export(open_exports, {
3694
+ registerOpenRoutes: () => registerOpenRoutes
3695
+ });
3696
+ function extractBearer(request) {
3697
+ const header2 = request.headers.authorization;
3698
+ if (!header2?.startsWith("Bearer ")) return void 0;
3699
+ return header2.slice("Bearer ".length).trim() || void 0;
3700
+ }
3701
+ function readSessionId(request) {
3702
+ const raw = request.headers["mcp-session-id"];
3703
+ const value = Array.isArray(raw) ? raw[0] : raw;
3704
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3705
+ }
3706
+ function registerOpenRoutes(app2, deps = {}) {
3707
+ const openAuth = deps.openAuth ?? new OpenCredentialService(
3708
+ (0, import_core68.getStoreLattice)("default", "a2aApiKey").store,
3709
+ (0, import_core68.getStoreLattice)("default", "project").store
3710
+ );
3711
+ const grantsSource = deps.grantsSource ?? ((ctx) => ctx.grants);
3712
+ const audit = deps.audit ?? (() => {
3713
+ try {
3714
+ const store2 = (0, import_core68.getStoreLattice)("default", "openAudit").store;
3715
+ return new OpenAuditService(store2);
3716
+ } catch {
3717
+ return void 0;
3718
+ }
3719
+ })();
3720
+ const rateLimiter = deps.rateLimiter ?? new OpenRateLimiter();
3721
+ app2.post("/open/mcp", async (request, reply) => {
3722
+ const context = await openAuth.resolve(extractBearer(request));
3723
+ if (!context) {
3724
+ return reply.status(401).send({ error: "AUTH_FAILED" });
3725
+ }
3726
+ const body = request.body;
3727
+ const sessionId = readSessionId(request);
3728
+ if (sessionId) {
3729
+ const session = await getSession(sessionId);
3730
+ if (!session || session.credential.credentialId !== context.credentialId) {
3731
+ return reply.status(404).send({ error: "SESSION_NOT_FOUND" });
3732
+ }
3733
+ reply.hijack();
3734
+ await session.transport.handleRequest(request.raw, reply.raw, body);
3735
+ return;
3736
+ }
3737
+ if (!(0, import_types.isInitializeRequest)(body)) {
3738
+ return reply.status(400).send({ error: "SESSION_REQUIRED" });
3739
+ }
3740
+ const grants = grantsSource(context);
3741
+ const catalog = await openLattice.catalogFor(grants, { tenantId: context.tenantId });
3742
+ const built = await buildSessionServer({
3743
+ credential: { credentialId: context.credentialId, kind: context.kind },
3744
+ grants,
3745
+ catalog,
3746
+ invoke: async (toolName, args) => {
3747
+ const startedAt = Date.now();
3748
+ const sep2 = toolName.indexOf("_");
3749
+ const auditDomain = sep2 > 0 ? toolName.slice(0, sep2) : toolName;
3750
+ const auditAction = sep2 > 0 ? toolName.slice(sep2 + 1) : "";
3751
+ const decision = rateLimiter.check(context.credentialId, auditDomain);
3752
+ if (!decision.allowed) {
3753
+ await audit?.record({
3754
+ tenantId: context.tenantId,
3755
+ credentialId: context.credentialId,
3756
+ credentialKind: context.kind,
3757
+ projectId: context.projectId,
3758
+ domain: auditDomain,
3759
+ action: auditAction,
3760
+ args,
3761
+ status: "error",
3762
+ errorCode: "RATE_LIMITED",
3763
+ durationMs: Date.now() - startedAt
3764
+ });
3765
+ throw Object.assign(
3766
+ new Error(
3767
+ `RATE_LIMITED: retry after ${decision.retryAfterMs ?? 1e3}ms`
3768
+ ),
3769
+ { rpcCode: RPC_ERROR.RATE_LIMITED }
3770
+ );
3771
+ }
3772
+ try {
3773
+ const result = await openLattice.invoke(
3774
+ grants,
3775
+ {
3776
+ tenantId: context.tenantId,
3777
+ projectId: context.projectId,
3778
+ workspaceId: context.workspaceId,
3779
+ runConfig: {
3780
+ sandboxName: context.sandboxName,
3781
+ credentialId: context.credentialId
3782
+ }
3783
+ },
3784
+ toolName,
3785
+ args
3786
+ );
3787
+ await audit?.record({
3788
+ tenantId: context.tenantId,
3789
+ credentialId: context.credentialId,
3790
+ credentialKind: context.kind,
3791
+ projectId: context.projectId,
3792
+ domain: auditDomain,
3793
+ action: auditAction,
3794
+ args,
3795
+ status: "ok",
3796
+ durationMs: Date.now() - startedAt
3797
+ });
3798
+ return result;
3799
+ } catch (err) {
3800
+ const code = err instanceof GrantDeniedError ? RPC_ERROR.GRANT_DENIED : err instanceof ActionNotFoundError ? RPC_ERROR.ACTION_NOT_FOUND : err instanceof ValidationError ? RPC_ERROR.VALIDATION_FAILED : RPC_ERROR.EXECUTION_ERROR;
3801
+ await audit?.record({
3802
+ tenantId: context.tenantId,
3803
+ credentialId: context.credentialId,
3804
+ credentialKind: context.kind,
3805
+ projectId: context.projectId,
3806
+ domain: auditDomain,
3807
+ action: auditAction,
3808
+ args,
3809
+ status: "error",
3810
+ errorCode: err instanceof GrantDeniedError ? "GRANT_DENIED" : err instanceof ActionNotFoundError ? "ACTION_NOT_FOUND" : err instanceof ValidationError ? "VALIDATION_FAILED" : "EXECUTION_ERROR",
3811
+ durationMs: Date.now() - startedAt
3812
+ });
3813
+ throw Object.assign(
3814
+ new Error(err instanceof Error ? err.message : String(err)),
3815
+ { rpcCode: code }
3816
+ );
3817
+ }
3818
+ }
3819
+ });
3820
+ reply.hijack();
3821
+ await built.transport.handleRequest(request.raw, reply.raw, body);
3822
+ });
3823
+ app2.delete(
3824
+ "/open/mcp",
3825
+ async (request, reply) => {
3826
+ const sessionId = readSessionId(request);
3827
+ if (!sessionId) {
3828
+ return reply.status(400).send({ error: "SESSION_REQUIRED" });
3829
+ }
3830
+ await deleteSession(sessionId);
3831
+ return reply.status(204).send();
3832
+ }
3833
+ );
3834
+ }
3835
+ var import_types, import_core68, RPC_ERROR;
3836
+ var init_open = __esm({
3837
+ "src/routes/open.ts"() {
3838
+ "use strict";
3839
+ import_types = require("@modelcontextprotocol/sdk/types.js");
3840
+ import_core68 = require("@axiom-lattice/core");
3841
+ init_openCredentialService();
3842
+ init_openAuditService();
3843
+ init_openRateLimiter();
3844
+ init_openLattice();
3845
+ init_openMcpServer();
3846
+ init_mcpTransportRegistry();
3847
+ RPC_ERROR = {
3848
+ GRANT_DENIED: -32001,
3849
+ ACTION_NOT_FOUND: -32002,
3850
+ VALIDATION_FAILED: -32003,
3851
+ EXECUTION_ERROR: -32004,
3852
+ RATE_LIMITED: -32005
3853
+ };
3854
+ }
3855
+ });
3856
+
3857
+ // src/routes/open-session.ts
3858
+ var open_session_exports = {};
3859
+ __export(open_session_exports, {
3860
+ registerOpenSessionRoutes: () => registerOpenSessionRoutes
3861
+ });
3862
+ function registerOpenSessionRoutes(app2) {
3863
+ app2.post(
3864
+ "/open/session",
3865
+ async (request, reply) => {
3866
+ const body = request.body ?? {};
3867
+ const shareToken = body.shareToken;
3868
+ if (typeof shareToken !== "string" || shareToken.length === 0) {
3869
+ return reply.status(400).send({ error: "SHARE_TOKEN_REQUIRED" });
3870
+ }
3871
+ const store2 = (0, import_core69.getStoreLattice)("default", "sharedResource").store;
3872
+ const record = await store2.findByToken(shareToken);
3873
+ if (!record || record.revoked) {
3874
+ return reply.status(404).send({ error: "SHARE_NOT_FOUND" });
3875
+ }
3876
+ if (record.expiresAt && new Date(record.expiresAt) < /* @__PURE__ */ new Date()) {
3877
+ return reply.status(410).send({ error: "SHARE_EXPIRED" });
3878
+ }
3879
+ const grants = BROWSER_GRANTS;
3880
+ const browserToken = signOpenToken({
3881
+ kind: "browser_token",
3882
+ tenantId: record.tenantId,
3883
+ projectId: record.projectId,
3884
+ shareId: record.token,
3885
+ grants
3886
+ });
3887
+ const actions = await openLattice.catalogFor(grants, { tenantId: record.tenantId });
3888
+ return reply.status(200).send({
3889
+ browserToken,
3890
+ expiresAt: Date.now() + 15 * 60 * 1e3,
3891
+ actions: actions.map((a) => a.toolName)
3892
+ });
3893
+ }
3894
+ );
3895
+ }
3896
+ var import_core69;
3897
+ var init_open_session = __esm({
3898
+ "src/routes/open-session.ts"() {
3899
+ "use strict";
3900
+ import_core69 = require("@axiom-lattice/core");
3901
+ init_openToken();
3902
+ init_previewToken();
3903
+ init_openLattice();
3904
+ }
3905
+ });
3906
+
3907
+ // src/services/open/openCatalogService.ts
3908
+ var OpenCatalogService;
3909
+ var init_openCatalogService = __esm({
3910
+ "src/services/open/openCatalogService.ts"() {
3911
+ "use strict";
3912
+ init_openLattice();
3913
+ OpenCatalogService = class {
3914
+ constructor(assistantStore, lattice = openLattice) {
3915
+ this.assistantStore = assistantStore;
3916
+ this.lattice = lattice;
3917
+ }
3918
+ async buildForTenant(tenantId) {
3919
+ const domains = this.lattice.describeCatalog();
3920
+ const enriched = [];
3921
+ for (const domain of domains) {
3922
+ if (domain.domain === "agent" && domain.itemKind === "instance") {
3923
+ let items = [];
3924
+ try {
3925
+ const assistants = await this.assistantStore.getAllAssistants(tenantId);
3926
+ items = assistants.map((a) => ({
3927
+ id: a.id,
3928
+ toolName: `invoke_${a.id}`,
3929
+ label: a.name || a.id
3930
+ }));
3931
+ } catch {
3932
+ items = [];
3933
+ }
3934
+ enriched.push({ ...domain, items });
3935
+ } else {
3936
+ enriched.push(domain);
3937
+ }
3938
+ }
3939
+ return { domains: enriched };
3940
+ }
3941
+ };
3942
+ }
3943
+ });
3944
+
3945
+ // src/routes/open-catalog.ts
3946
+ var open_catalog_exports = {};
3947
+ __export(open_catalog_exports, {
3948
+ registerOpenCatalogRoutes: () => registerOpenCatalogRoutes
3949
+ });
3950
+ function tenantIdOf(request) {
3951
+ return request.user?.tenantId;
3952
+ }
3953
+ function registerOpenCatalogRoutes(app2) {
3954
+ app2.get("/api/open/catalog", async (request, reply) => {
3955
+ const tenantId = tenantIdOf(request);
3956
+ if (!tenantId) {
3957
+ return reply.status(401).send({ success: false, error: "Unauthorized" });
3958
+ }
3959
+ const assistantStore = (0, import_core70.getStoreLattice)("default", "assistant").store;
3960
+ const service2 = new OpenCatalogService(assistantStore);
3961
+ const catalog = await service2.buildForTenant(tenantId);
3962
+ return reply.status(200).send({ success: true, data: catalog });
3963
+ });
3964
+ }
3965
+ var import_core70;
3966
+ var init_open_catalog = __esm({
3967
+ "src/routes/open-catalog.ts"() {
3968
+ "use strict";
3969
+ import_core70 = require("@axiom-lattice/core");
3970
+ init_openCatalogService();
3971
+ }
3972
+ });
3973
+
3974
+ // src/services/open/providers/sandboxProvider.ts
3975
+ var sandboxProvider_exports = {};
3976
+ __export(sandboxProvider_exports, {
3977
+ createOpenSandboxProvider: () => createOpenSandboxProvider,
3978
+ resolveContextSandbox: () => resolveContextSandbox
3979
+ });
3980
+ async function resolveContextSandbox(ctx) {
3981
+ return import_core71.sandboxLatticeManager.getSandboxFromConfig({
3982
+ assistant_id: "",
3983
+ thread_id: "",
3984
+ tenantId: ctx.tenantId,
3985
+ workspaceId: ctx.workspaceId ?? "",
3986
+ projectId: ctx.projectId
3987
+ });
3988
+ }
3989
+ function createOpenSandboxProvider() {
3990
+ return {
3991
+ domain: "sandbox",
3992
+ label: "Sandbox",
3993
+ source: "builtin",
3994
+ scopeKind: "project",
3995
+ actions: [
3996
+ {
3997
+ name: "files_read",
3998
+ label: "Read files",
3999
+ description: "Read a file from the project sandbox.",
4000
+ schema: { path: import_zod17.z.string().min(1) },
4001
+ annotations: { readOnlyHint: true, destructiveHint: false }
4002
+ },
4003
+ {
4004
+ name: "files_list",
4005
+ label: "List files",
4006
+ description: "List files under a directory in the project sandbox.",
4007
+ schema: { path: import_zod17.z.string(), recursive: import_zod17.z.boolean().optional() },
4008
+ annotations: { readOnlyHint: true, destructiveHint: false }
4009
+ },
4010
+ {
4011
+ name: "files_write",
4012
+ label: "Write files",
4013
+ description: "Write content to a file in the project sandbox.",
4014
+ schema: { path: import_zod17.z.string().min(1), content: import_zod17.z.string() },
4015
+ annotations: { readOnlyHint: false, destructiveHint: true }
4016
+ },
4017
+ {
4018
+ name: "shell_exec",
4019
+ label: "Run shell commands",
4020
+ description: "Execute a shell command in the project sandbox. Returns stdout (up to 64KB) and exit code.",
4021
+ schema: {
4022
+ command: import_zod17.z.string().min(1),
4023
+ exec_dir: import_zod17.z.string().optional(),
4024
+ timeout: import_zod17.z.number().int().min(1e3).optional()
4025
+ },
4026
+ annotations: { readOnlyHint: false, destructiveHint: true }
4027
+ }
4028
+ ],
4029
+ execute: async (actionName, args, ctx) => {
4030
+ const sandbox = await resolveContextSandbox(ctx);
4031
+ switch (actionName) {
4032
+ case "files_read": {
4033
+ const { path: path7 } = args;
4034
+ const file = await sandbox.file.readFile(path7);
4035
+ return { content: [{ type: "text", text: file.content }] };
4036
+ }
4037
+ case "files_list": {
4038
+ const { path: path7, recursive } = args;
4039
+ const listing = await sandbox.file.listPath(path7, { recursive });
4040
+ return {
4041
+ content: [{ type: "text", text: JSON.stringify(listing.files) }]
4042
+ };
4043
+ }
4044
+ case "files_write": {
4045
+ const { path: path7, content } = args;
4046
+ await sandbox.file.writeFile(path7, content);
4047
+ return { content: [{ type: "text", text: `wrote ${path7}` }] };
4048
+ }
4049
+ case "shell_exec": {
4050
+ const { command, exec_dir, timeout } = args;
4051
+ const result = await sandbox.shell.execCommand({
4052
+ command,
4053
+ exec_dir,
4054
+ timeout: timeout ?? 12e4
4055
+ });
4056
+ const truncated = result.output.length > MAX_OUTPUT_BYTES ? result.output.slice(0, MAX_OUTPUT_BYTES) + `
4057
+ ...[truncated ${result.output.length - MAX_OUTPUT_BYTES} bytes]` : result.output;
4058
+ return {
4059
+ content: [
4060
+ {
4061
+ type: "text",
4062
+ text: `exit_code: ${result.exit_code}
4063
+ ${truncated}`
4064
+ }
4065
+ ]
4066
+ };
4067
+ }
4068
+ default:
4069
+ return {
4070
+ content: [
4071
+ { type: "text", text: `Unknown sandbox action: ${actionName}` }
4072
+ ],
4073
+ isError: true
4074
+ };
4075
+ }
4076
+ }
4077
+ };
4078
+ }
4079
+ var import_zod17, import_core71, MAX_OUTPUT_BYTES;
4080
+ var init_sandboxProvider = __esm({
4081
+ "src/services/open/providers/sandboxProvider.ts"() {
4082
+ "use strict";
4083
+ import_zod17 = require("zod");
4084
+ import_core71 = require("@axiom-lattice/core");
4085
+ MAX_OUTPUT_BYTES = 64e3;
4086
+ }
4087
+ });
4088
+
4089
+ // src/routes/open-files.ts
4090
+ var open_files_exports = {};
4091
+ __export(open_files_exports, {
4092
+ registerOpenFilesRoutes: () => registerOpenFilesRoutes
4093
+ });
4094
+ function extractBearer2(request) {
4095
+ const header2 = request.headers.authorization;
4096
+ if (!header2?.startsWith("Bearer ")) return void 0;
4097
+ return header2.slice("Bearer ".length).trim() || void 0;
4098
+ }
4099
+ function safePath(raw) {
4100
+ if (typeof raw !== "string" || raw.length === 0) return null;
4101
+ if (raw.includes("..")) return null;
4102
+ return raw.startsWith("/") ? raw : `/${raw}`;
4103
+ }
4104
+ function registerOpenFilesRoutes(app2, deps = {}) {
4105
+ const openAuth = deps.openAuth ?? new OpenCredentialService(
4106
+ (0, import_core72.getStoreLattice)("default", "a2aApiKey").store,
4107
+ (0, import_core72.getStoreLattice)("default", "project").store
4108
+ );
4109
+ const audit = deps.audit ?? (() => {
4110
+ try {
4111
+ const store2 = (0, import_core72.getStoreLattice)("default", "openAudit").store;
4112
+ return new OpenAuditService(store2);
4113
+ } catch {
4114
+ return void 0;
4115
+ }
4116
+ })();
4117
+ app2.addContentTypeParser(
4118
+ "application/octet-stream",
4119
+ { parseAs: "buffer" },
4120
+ (_req, body, done) => done(null, body)
4121
+ );
4122
+ app2.get("/open/sandbox/files/download", async (request, reply) => {
4123
+ const context = await openAuth.resolve(extractBearer2(request));
4124
+ if (!context) return reply.status(401).send({ error: "AUTH_FAILED" });
4125
+ const path7 = safePath(request.query.path);
4126
+ if (!path7) return reply.status(400).send({ error: "PATH_REQUIRED" });
4127
+ if (!openLattice.isAllowed(context.grants, "sandbox_files_read")) {
4128
+ return reply.status(403).send({ error: "GRANT_DENIED" });
4129
+ }
4130
+ const startedAt = Date.now();
4131
+ try {
4132
+ const sandbox = await resolveContextSandbox({
4133
+ tenantId: context.tenantId,
4134
+ projectId: context.projectId,
4135
+ workspaceId: context.workspaceId,
4136
+ runConfig: {}
4137
+ });
4138
+ const data = await sandbox.file.downloadFile({ file: path7 });
4139
+ await audit?.record({
4140
+ tenantId: context.tenantId,
4141
+ credentialId: context.credentialId,
4142
+ credentialKind: context.kind,
4143
+ projectId: context.projectId,
4144
+ domain: "sandbox",
4145
+ action: "files_read",
4146
+ args: { path: path7 },
4147
+ status: "ok",
4148
+ durationMs: Date.now() - startedAt
4149
+ });
4150
+ return reply.status(200).type("application/octet-stream").header("Content-Disposition", "attachment").send(data);
4151
+ } catch (err) {
4152
+ await audit?.record({
4153
+ tenantId: context.tenantId,
4154
+ credentialId: context.credentialId,
4155
+ credentialKind: context.kind,
4156
+ projectId: context.projectId,
4157
+ domain: "sandbox",
4158
+ action: "files_read",
4159
+ args: { path: path7 },
4160
+ status: "error",
4161
+ errorCode: "EXECUTION_ERROR",
4162
+ durationMs: Date.now() - startedAt
4163
+ });
4164
+ return reply.status(502).send({
4165
+ error: err instanceof Error ? err.message : "SANDBOX_ERROR"
4166
+ });
4167
+ }
4168
+ });
4169
+ app2.post("/open/sandbox/files/upload", async (request, reply) => {
4170
+ const context = await openAuth.resolve(extractBearer2(request));
4171
+ if (!context) return reply.status(401).send({ error: "AUTH_FAILED" });
4172
+ const path7 = safePath(request.query.path);
4173
+ if (!path7) return reply.status(400).send({ error: "PATH_REQUIRED" });
4174
+ if (!openLattice.isAllowed(context.grants, "sandbox_files_write")) {
4175
+ return reply.status(403).send({ error: "GRANT_DENIED" });
4176
+ }
4177
+ const body = request.body;
4178
+ if (!Buffer.isBuffer(body)) {
4179
+ return reply.status(400).send({ error: "BINARY_BODY_REQUIRED" });
4180
+ }
4181
+ const startedAt = Date.now();
4182
+ try {
4183
+ const sandbox = await resolveContextSandbox({
4184
+ tenantId: context.tenantId,
4185
+ projectId: context.projectId,
4186
+ workspaceId: context.workspaceId,
4187
+ runConfig: {}
4188
+ });
4189
+ await sandbox.file.uploadFile({ file: path7, data: body });
4190
+ await audit?.record({
4191
+ tenantId: context.tenantId,
4192
+ credentialId: context.credentialId,
4193
+ credentialKind: context.kind,
4194
+ projectId: context.projectId,
4195
+ domain: "sandbox",
4196
+ action: "files_write",
4197
+ args: { path: path7, bytes: body.length },
4198
+ status: "ok",
4199
+ durationMs: Date.now() - startedAt
4200
+ });
4201
+ return reply.status(200).send({ success: true, path: path7 });
4202
+ } catch (err) {
4203
+ await audit?.record({
4204
+ tenantId: context.tenantId,
4205
+ credentialId: context.credentialId,
4206
+ credentialKind: context.kind,
4207
+ projectId: context.projectId,
4208
+ domain: "sandbox",
4209
+ action: "files_write",
4210
+ args: { path: path7, bytes: body.length },
4211
+ status: "error",
4212
+ errorCode: "EXECUTION_ERROR",
4213
+ durationMs: Date.now() - startedAt
4214
+ });
4215
+ return reply.status(502).send({
4216
+ error: err instanceof Error ? err.message : "SANDBOX_ERROR"
4217
+ });
4218
+ }
4219
+ });
4220
+ }
4221
+ var import_core72;
4222
+ var init_open_files = __esm({
4223
+ "src/routes/open-files.ts"() {
4224
+ "use strict";
4225
+ import_core72 = require("@axiom-lattice/core");
4226
+ init_openCredentialService();
4227
+ init_openLattice();
4228
+ init_sandboxProvider();
4229
+ init_openAuditService();
4230
+ }
4231
+ });
4232
+
4233
+ // src/routes/open-audit.ts
4234
+ var open_audit_exports = {};
4235
+ __export(open_audit_exports, {
4236
+ registerOpenAuditRoutes: () => registerOpenAuditRoutes
4237
+ });
4238
+ function registerOpenAuditRoutes(app2) {
4239
+ app2.get(
4240
+ "/api/open/audit",
4241
+ async (request, reply) => {
4242
+ const tenantId = request.user?.tenantId;
4243
+ if (!tenantId) {
4244
+ return reply.status(401).send({ success: false, error: "Unauthorized" });
4245
+ }
4246
+ const status = request.query.status === "ok" || request.query.status === "error" ? request.query.status : void 0;
4247
+ const store2 = (0, import_core73.getStoreLattice)("default", "openAudit").store;
4248
+ const records = await store2.query({
4249
+ tenantId,
4250
+ credentialId: request.query.credentialId,
4251
+ domain: request.query.domain,
4252
+ status,
4253
+ from: request.query.from,
4254
+ to: request.query.to,
4255
+ limit: request.query.limit ? parseInt(request.query.limit, 10) : void 0,
4256
+ offset: request.query.offset ? parseInt(request.query.offset, 10) : void 0
4257
+ });
4258
+ return reply.status(200).send({ success: true, data: { records } });
4259
+ }
4260
+ );
4261
+ }
4262
+ var import_core73;
4263
+ var init_open_audit = __esm({
4264
+ "src/routes/open-audit.ts"() {
4265
+ "use strict";
4266
+ import_core73 = require("@axiom-lattice/core");
4267
+ }
4268
+ });
4269
+
4270
+ // src/routes/open-exposure.ts
4271
+ var open_exposure_exports = {};
4272
+ __export(open_exposure_exports, {
4273
+ registerOpenExposureRoutes: () => registerOpenExposureRoutes
4274
+ });
4275
+ function registerOpenExposureRoutes(app2) {
4276
+ app2.get(
4277
+ "/api/keys/:id/exposure",
4278
+ async (request, reply) => {
4279
+ const tenantId = request.user?.tenantId;
4280
+ if (!tenantId) {
4281
+ return reply.status(401).send({ success: false, error: "Unauthorized" });
4282
+ }
4283
+ const keyStore = (0, import_core74.getStoreLattice)("default", "a2aApiKey").store;
4284
+ const record = await keyStore.findById(request.params.id);
4285
+ if (!record || record.tenantId !== tenantId) {
4286
+ return reply.status(404).send({ success: false, error: "Key not found" });
4287
+ }
4288
+ const grants = (0, import_protocols33.effectiveGrants)(record);
4289
+ const forwardedHost = request.headers["x-forwarded-host"];
4290
+ const host = (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ?? (Array.isArray(request.headers.host) ? request.headers.host[0] : request.headers.host) ?? request.hostname;
4291
+ const baseUrl = `${request.protocol}://${host}`;
4292
+ const tools = (await openLattice.catalogFor(grants, { tenantId })).map((t2) => ({
4293
+ domain: t2.domain,
4294
+ toolName: t2.toolName,
4295
+ description: t2.description,
4296
+ annotations: t2.annotations
4297
+ }));
4298
+ const agentGrant = grants.find((g) => g.domain === "agent");
4299
+ const assistantStore = (0, import_core74.getStoreLattice)("default", "assistant").store;
4300
+ const agents = [];
4301
+ for (const assistantId of agentGrant?.items ?? []) {
4302
+ const assistant = await assistantStore.getAssistantById(
4303
+ tenantId,
4304
+ assistantId
4305
+ );
4306
+ if (!assistant) continue;
4307
+ agents.push({
4308
+ assistantId,
4309
+ cardUrl: `${baseUrl}/api/a2a/agents/${assistantId}/.well-known/agent-card.json`,
4310
+ jsonrpcUrl: `${baseUrl}/api/a2a/agents/${assistantId}/jsonrpc`,
4311
+ card: buildAgentCardForAssistant({ assistant, baseUrl })
4312
+ });
4313
+ }
4314
+ return reply.status(200).send({
4315
+ success: true,
4316
+ data: {
4317
+ tenantId,
4318
+ projectId: record.projectId,
4319
+ grants,
4320
+ mcp: { endpoint: `${baseUrl}/open/mcp`, tools },
4321
+ a2a: { agents }
4322
+ }
4323
+ });
4324
+ }
4325
+ );
4326
+ }
4327
+ var import_core74, import_protocols33;
4328
+ var init_open_exposure = __esm({
4329
+ "src/routes/open-exposure.ts"() {
4330
+ "use strict";
4331
+ import_core74 = require("@axiom-lattice/core");
4332
+ import_protocols33 = require("@axiom-lattice/protocols");
4333
+ init_openLattice();
4334
+ init_A2AAgentCardBuilder();
4335
+ }
4336
+ });
4337
+
4338
+ // src/services/open/providers/kbProvider.ts
4339
+ var kbProvider_exports = {};
4340
+ __export(kbProvider_exports, {
4341
+ createKbProvider: () => createKbProvider
4342
+ });
4343
+ function createKbProvider(collectionService3) {
4344
+ return {
4345
+ domain: "kb",
4346
+ label: "Knowledge Base",
4347
+ source: "builtin",
4348
+ scopeKind: "tenant",
4349
+ actions: [
4350
+ {
4351
+ name: "search",
4352
+ label: "Search collections",
4353
+ description: "Search a knowledge base collection. Returns matched entries with content, metadata and similarity score.",
4354
+ schema: {
4355
+ collection: import_zod18.z.string().min(1),
4356
+ query: import_zod18.z.string().min(1),
4357
+ limit: import_zod18.z.number().int().min(1).max(50).optional()
4358
+ },
4359
+ annotations: { readOnlyHint: true, destructiveHint: false }
4360
+ }
4361
+ ],
4362
+ execute: async (action, args, ctx) => {
4363
+ if (action !== "search") {
4364
+ return {
4365
+ content: [{ type: "text", text: `Unknown kb action: ${action}` }],
4366
+ isError: true
4367
+ };
4368
+ }
4369
+ const { collection, query, limit } = args;
4370
+ const req = {
4371
+ user: { tenantId: ctx.tenantId },
4372
+ headers: {}
4373
+ };
4374
+ const entries = await collectionService3.searchEntries(
4375
+ req,
4376
+ collection,
4377
+ { query, top_k: limit ?? 5 }
4378
+ );
4379
+ return {
4380
+ content: [{ type: "text", text: JSON.stringify(entries, null, 2) }]
4381
+ };
4382
+ }
4383
+ };
4384
+ }
4385
+ var import_zod18;
4386
+ var init_kbProvider = __esm({
4387
+ "src/services/open/providers/kbProvider.ts"() {
4388
+ "use strict";
4389
+ import_zod18 = require("zod");
4390
+ }
4391
+ });
4392
+
4393
+ // src/services/open/providers/agentProvider.ts
4394
+ var agentProvider_exports = {};
4395
+ __export(agentProvider_exports, {
4396
+ createAgentProvider: () => createAgentProvider
4397
+ });
4398
+ function createAgentProvider(deps = {}) {
4399
+ return {
4400
+ domain: "agent",
4401
+ label: "Agents",
4402
+ source: "agent",
4403
+ scopeKind: "project",
4404
+ itemKind: "instance",
4405
+ actions: [],
4406
+ instanceActions: async (grants, ctx) => {
4407
+ const agentGrant = grants.find((g) => g.domain === "agent");
4408
+ const assistantIds = agentGrant?.items ?? [];
4409
+ const tenantId = ctx?.tenantId;
4410
+ return Promise.all(
4411
+ assistantIds.map(async (assistantId) => {
4412
+ let name = assistantId;
4413
+ let description = "";
4414
+ if (tenantId && deps.resolveAssistant) {
4415
+ try {
4416
+ const meta = await deps.resolveAssistant(tenantId, assistantId);
4417
+ if (meta?.name) name = meta.name;
4418
+ if (meta?.description) description = meta.description;
4419
+ } catch {
4420
+ }
4421
+ }
4422
+ return {
4423
+ name: assistantId,
4424
+ toolName: `invoke_${assistantId}`,
4425
+ label: name,
4426
+ description: `Invoke the "${name}" agent${description ? ` \u2014 ${description}` : ""}. Returns its final reply.`,
4427
+ schema: { message: import_zod19.z.string().min(1) },
4428
+ annotations: { readOnlyHint: false, destructiveHint: false }
4429
+ };
4430
+ })
4431
+ );
4432
+ },
4433
+ execute: async (assistantId, args, ctx) => {
4434
+ const { message } = args;
4435
+ const credentialId = ctx.runConfig.credentialId ?? ctx.projectId;
4436
+ const threadId = `open:${credentialId}:${assistantId}`;
4437
+ if (deps.threadStore) {
4438
+ try {
4439
+ await deps.threadStore.createThread(ctx.tenantId, assistantId, threadId, {
4440
+ metadata: {
4441
+ source: "open_api",
4442
+ credentialId,
4443
+ workspaceId: ctx.workspaceId ?? void 0,
4444
+ projectId: ctx.projectId
4445
+ }
4446
+ });
4447
+ } catch {
4448
+ }
4449
+ }
4450
+ const agent = import_core75.agentInstanceManager.getAgent({
4451
+ tenant_id: ctx.tenantId,
4452
+ assistant_id: assistantId,
4453
+ thread_id: threadId,
4454
+ workspace_id: ctx.workspaceId ?? "",
4455
+ project_id: ctx.projectId
4456
+ });
4457
+ const { messageId } = await agent.addMessage({
4458
+ input: { message }
4459
+ });
4460
+ const parts = [];
4461
+ const stream = agent.chunkStream(messageId, [...STOP_TYPES]);
4462
+ for await (const chunk of stream) {
4463
+ const text = chunk.data?.content;
4464
+ if (chunk.type === import_protocols34.MessageChunkTypes.INTERRUPT) break;
4465
+ if (chunk.type === import_protocols34.MessageChunkTypes.AI && text) {
4466
+ parts.push(text);
4467
+ }
4468
+ }
4469
+ return {
4470
+ content: [{ type: "text", text: parts.join("") || "(no output)" }]
4471
+ };
4472
+ }
4473
+ };
4474
+ }
4475
+ var import_zod19, import_core75, import_protocols34, STOP_TYPES;
4476
+ var init_agentProvider = __esm({
4477
+ "src/services/open/providers/agentProvider.ts"() {
3003
4478
  "use strict";
3004
- sessions = /* @__PURE__ */ new Map();
4479
+ import_zod19 = require("zod");
4480
+ import_core75 = require("@axiom-lattice/core");
4481
+ import_protocols34 = require("@axiom-lattice/protocols");
4482
+ STOP_TYPES = [import_protocols34.MessageChunkTypes.MESSAGE_COMPLETED];
3005
4483
  }
3006
4484
  });
3007
4485
 
3008
- // src/services/open/mcpCatalog.ts
3009
- function textResult(text) {
3010
- return [{ type: "text", text }];
3011
- }
3012
- function filterCatalogByGrants(credential) {
3013
- const domains = new Set(credential.grants.map((g) => g.domain));
3014
- return SPIKE_TOOL_CATALOG.filter((tool) => domains.has(tool.domain));
4486
+ // src/services/open/pluginExposeAdapter.ts
4487
+ function schemaOf(tool) {
4488
+ return tool.lc_kwargs?.schema ?? tool.schema ?? import_zod20.z.object({}).passthrough();
3015
4489
  }
3016
- var import_zod16, SPIKE_TOOL_CATALOG;
3017
- var init_mcpCatalog = __esm({
3018
- "src/services/open/mcpCatalog.ts"() {
3019
- "use strict";
3020
- import_zod16 = require("zod");
3021
- SPIKE_TOOL_CATALOG = [
3022
- {
3023
- toolName: "kb_search",
3024
- domain: "kb",
3025
- description: "Spike: pretend knowledge-base search.",
3026
- schema: {
3027
- query: import_zod16.z.string(),
3028
- limit: import_zod16.z.number().int().optional()
3029
- },
3030
- annotations: { readOnlyHint: true, destructiveHint: false },
3031
- execute: async (args) => {
3032
- const { query } = args;
3033
- return textResult(`kb_search spike result for: ${query}`);
3034
- }
3035
- },
3036
- {
3037
- toolName: "echo",
3038
- domain: "sandbox",
3039
- description: "Spike: echoes the message back.",
3040
- schema: { message: import_zod16.z.string() },
3041
- annotations: { readOnlyHint: true, destructiveHint: false },
3042
- execute: async (args) => {
3043
- const { message } = args;
3044
- return textResult(`echo: ${message}`);
3045
- }
4490
+ function createPluginProvider(domain, tools, options = {}) {
4491
+ function toAction(info) {
4492
+ return {
4493
+ name: info.name,
4494
+ description: info.description || info.tool.description || `Plugin tool ${info.name}`,
4495
+ schema: schemaOf(info.tool),
4496
+ annotations: {
4497
+ readOnlyHint: info.readOnly ?? false,
4498
+ destructiveHint: info.destructive ?? false
3046
4499
  }
3047
- ];
4500
+ };
3048
4501
  }
3049
- });
3050
-
3051
- // src/services/open/openMcpServer.ts
3052
- function registerTools(server, tools) {
3053
- for (const tool of tools) {
3054
- server.registerTool(
3055
- tool.toolName,
3056
- {
3057
- description: tool.description,
3058
- inputSchema: tool.schema,
3059
- annotations: {
3060
- readOnlyHint: tool.annotations.readOnlyHint,
3061
- destructiveHint: tool.annotations.destructiveHint
4502
+ const actions = tools.map(toAction);
4503
+ return {
4504
+ domain,
4505
+ label: options.label ?? domain,
4506
+ source: "plugin",
4507
+ scopeKind: "tenant",
4508
+ actions,
4509
+ execute: async (action, args, ctx) => {
4510
+ const info = tools.find((t2) => t2.name === action);
4511
+ if (!info) {
4512
+ return {
4513
+ content: [{ type: "text", text: `Unknown action: ${action}` }],
4514
+ isError: true
4515
+ };
4516
+ }
4517
+ const exeConfig = {
4518
+ configurable: {
4519
+ runConfig: {
4520
+ tenantId: ctx.tenantId,
4521
+ _resolvedConnections: ctx.runConfig._resolvedConnections ?? []
4522
+ }
3062
4523
  }
3063
- },
3064
- async (args) => ({ content: await tool.execute(args) })
3065
- );
3066
- }
3067
- }
3068
- async function buildSessionServer(credential) {
3069
- const tools = filterCatalogByGrants(credential);
3070
- const server = new import_mcp.McpServer(OPEN_MCP_SERVER_INFO);
3071
- registerTools(server, tools);
3072
- const transport = new import_streamableHttp.StreamableHTTPServerTransport({
3073
- sessionIdGenerator: () => {
3074
- const sessionId = (0, import_crypto12.randomUUID)();
3075
- void registerSession(
3076
- sessionId,
3077
- { credentialId: credential.credentialId, kind: credential.kind },
3078
- { server, transport }
3079
- );
3080
- return sessionId;
3081
- },
3082
- enableJsonResponse: true,
3083
- onsessionclosed: (closedId) => {
3084
- void Promise.resolve().then(() => (init_mcpTransportRegistry(), mcpTransportRegistry_exports)).then(
3085
- ({ deleteSession: deleteSession2 }) => deleteSession2(closedId)
3086
- );
4524
+ };
4525
+ const output = await info.tool.invoke(args, exeConfig);
4526
+ return { content: [{ type: "text", text: output }] };
3087
4527
  }
3088
- });
3089
- await server.connect(transport);
3090
- return { server, transport, tools };
4528
+ };
3091
4529
  }
3092
- var import_mcp, import_streamableHttp, import_crypto12, OPEN_MCP_SERVER_INFO;
3093
- var init_openMcpServer = __esm({
3094
- "src/services/open/openMcpServer.ts"() {
4530
+ var import_zod20;
4531
+ var init_pluginExposeAdapter = __esm({
4532
+ "src/services/open/pluginExposeAdapter.ts"() {
3095
4533
  "use strict";
3096
- import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
3097
- import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
3098
- import_crypto12 = require("crypto");
3099
- init_mcpTransportRegistry();
3100
- init_mcpCatalog();
3101
- OPEN_MCP_SERVER_INFO = {
3102
- name: "axiom-open",
3103
- version: "0.1.0"
3104
- };
4534
+ import_zod20 = require("zod");
3105
4535
  }
3106
4536
  });
3107
4537
 
3108
- // src/routes/open.ts
3109
- var open_exports = {};
3110
- __export(open_exports, {
3111
- registerOpenRoutes: () => registerOpenRoutes
4538
+ // src/services/open/registerExposedPlugins.ts
4539
+ var registerExposedPlugins_exports = {};
4540
+ __export(registerExposedPlugins_exports, {
4541
+ registerExposedPluginTools: () => registerExposedPluginTools
3112
4542
  });
3113
- function extractBearer(request) {
3114
- const header2 = request.headers.authorization;
3115
- if (!header2?.startsWith("Bearer ")) return void 0;
3116
- return header2.slice("Bearer ".length).trim() || void 0;
3117
- }
3118
- function readSessionId(request) {
3119
- const raw = request.headers["mcp-session-id"];
3120
- const value = Array.isArray(raw) ? raw[0] : raw;
3121
- return typeof value === "string" && value.length > 0 ? value : void 0;
3122
- }
3123
- function registerOpenRoutes(app2) {
3124
- app2.post("/open/mcp", async (request, reply) => {
3125
- const credential = resolveSpikeCredential(extractBearer(request));
3126
- if (!credential) {
3127
- return reply.status(401).send({ error: "AUTH_FAILED" });
3128
- }
3129
- const body = request.body;
3130
- const sessionId = readSessionId(request);
3131
- if (sessionId) {
3132
- const session = await getSession(sessionId);
3133
- if (!session || session.credential.credentialId !== credential.credentialId) {
3134
- return reply.status(404).send({ error: "SESSION_NOT_FOUND" });
3135
- }
3136
- reply.hijack();
3137
- await session.transport.handleRequest(request.raw, reply.raw, body);
3138
- return;
3139
- }
3140
- if (!(0, import_types.isInitializeRequest)(body)) {
3141
- return reply.status(400).send({ error: "SESSION_REQUIRED" });
3142
- }
3143
- const built = await buildSessionServer(credential);
3144
- reply.hijack();
3145
- await built.transport.handleRequest(request.raw, reply.raw, body);
3146
- });
3147
- app2.delete(
3148
- "/open/mcp",
3149
- async (request, reply) => {
3150
- const sessionId = readSessionId(request);
3151
- if (!sessionId) {
3152
- return reply.status(400).send({ error: "SESSION_REQUIRED" });
4543
+ async function registerExposedPluginTools() {
4544
+ for (const key of import_core76.PluginRegistry.list()) {
4545
+ const plugin = import_core76.PluginRegistry.get(key);
4546
+ if (!plugin?.middleware) continue;
4547
+ const openExpose = plugin.meta.openExpose;
4548
+ const exposed = openExpose ? openExpose.map(
4549
+ (entry) => typeof entry === "string" ? { name: entry, description: "", expose: true } : {
4550
+ name: entry.name,
4551
+ description: "",
4552
+ expose: true,
4553
+ readOnly: entry.readOnly,
4554
+ destructive: entry.destructive
3153
4555
  }
3154
- await deleteSession(sessionId);
3155
- return reply.status(204).send();
4556
+ ) : plugin.meta.tools?.filter((t2) => t2.expose) ?? [];
4557
+ if (exposed.length === 0) continue;
4558
+ try {
4559
+ const middleware = await plugin.middleware({
4560
+ ...plugin.meta.defaultConfig ?? {},
4561
+ connectAll: true
4562
+ });
4563
+ const toolObjects = middleware.tools ?? [];
4564
+ const matched = exposed.map((tm) => {
4565
+ const tool = toolObjects.find((t2) => t2.name === tm.name);
4566
+ if (!tool) return null;
4567
+ const annotations = tm;
4568
+ return {
4569
+ name: tm.name,
4570
+ description: tm.description,
4571
+ tool,
4572
+ readOnly: annotations.readOnly,
4573
+ destructive: annotations.destructive
4574
+ };
4575
+ }).filter(Boolean);
4576
+ if (matched.length === 0) continue;
4577
+ const provider = createPluginProvider(key, matched, { label: plugin.meta.name });
4578
+ openLattice.register(provider);
4579
+ } catch {
3156
4580
  }
3157
- );
4581
+ }
3158
4582
  }
3159
- var import_types;
3160
- var init_open = __esm({
3161
- "src/routes/open.ts"() {
4583
+ var import_core76;
4584
+ var init_registerExposedPlugins = __esm({
4585
+ "src/services/open/registerExposedPlugins.ts"() {
3162
4586
  "use strict";
3163
- import_types = require("@modelcontextprotocol/sdk/types.js");
3164
- init_openCredentials();
3165
- init_openMcpServer();
3166
- init_mcpTransportRegistry();
4587
+ import_core76 = require("@axiom-lattice/core");
4588
+ init_openLattice();
4589
+ init_pluginExposeAdapter();
4590
+ }
4591
+ });
4592
+
4593
+ // src/services/open/sandboxGrants.ts
4594
+ function sanitizeShareOpenGrants(grants) {
4595
+ const out = [];
4596
+ for (const grant of grants ?? []) {
4597
+ const allowed = ALLOWED[grant.domain];
4598
+ if (!allowed) continue;
4599
+ const items = grant.items && grant.items.length > 0 ? grant.items.filter((item) => allowed.includes(item)) : allowed;
4600
+ if (items.length > 0) out.push({ domain: grant.domain, items });
4601
+ }
4602
+ return out;
4603
+ }
4604
+ var ALLOWED;
4605
+ var init_sandboxGrants = __esm({
4606
+ "src/services/open/sandboxGrants.ts"() {
4607
+ "use strict";
4608
+ ALLOWED = {
4609
+ kb: ["search", "entries_read"]
4610
+ };
3167
4611
  }
3168
4612
  });
3169
4613
 
@@ -3172,13 +4616,14 @@ var resources_exports = {};
3172
4616
  __export(resources_exports, {
3173
4617
  ResourceController: () => ResourceController
3174
4618
  });
3175
- var import_bcryptjs, import_core68, ResourceController;
4619
+ var import_bcryptjs, import_core77, ResourceController;
3176
4620
  var init_resources = __esm({
3177
4621
  "src/controllers/resources.ts"() {
3178
4622
  "use strict";
3179
4623
  import_bcryptjs = __toESM(require("bcryptjs"));
3180
- import_core68 = require("@axiom-lattice/core");
4624
+ import_core77 = require("@axiom-lattice/core");
3181
4625
  init_mime();
4626
+ init_sandboxGrants();
3182
4627
  ResourceController = class {
3183
4628
  constructor(deps) {
3184
4629
  this.deps = deps;
@@ -3192,14 +4637,14 @@ var init_resources = __esm({
3192
4637
  if (!workspaceId || !projectId) {
3193
4638
  return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
3194
4639
  }
3195
- const payload = (0, import_core68.createSharePayload)(
4640
+ const payload = (0, import_core77.createSharePayload)(
3196
4641
  tenantId,
3197
4642
  workspaceId,
3198
4643
  projectId,
3199
4644
  userId,
3200
4645
  body
3201
4646
  );
3202
- const token = (0, import_core68.generateToken)();
4647
+ const token = (0, import_core77.generateToken)();
3203
4648
  try {
3204
4649
  await this.deps.store.create({ ...payload, token });
3205
4650
  this.deps.logger.info(
@@ -3351,7 +4796,7 @@ var init_resources = __esm({
3351
4796
  resourcePath: subPath
3352
4797
  }, subPath, ext, request, reply);
3353
4798
  }
3354
- const volume = (0, import_core68.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
4799
+ const volume = (0, import_core77.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
3355
4800
  return this._resolveAndServe(
3356
4801
  volume,
3357
4802
  subPath,
@@ -3463,7 +4908,8 @@ var init_resources = __esm({
3463
4908
  workspaceId: record.workspaceId,
3464
4909
  projectId: record.projectId,
3465
4910
  assistantId: record.assistantId,
3466
- resourcePath: basePath + apiSubPath
4911
+ resourcePath: basePath + apiSubPath,
4912
+ openGrants: record.openGrants
3467
4913
  }, apiSubPath, ext, request, reply);
3468
4914
  }
3469
4915
  async _execApiCore(config, apiSubPath, ext, request, reply) {
@@ -3494,9 +4940,10 @@ var init_resources = __esm({
3494
4940
  `export API_QUERY='${JSON.stringify(queryObj).replace(/'/g, "'\\''")}'`,
3495
4941
  `export API_BODY='${bodyStr.replace(/'/g, "'\\''")}'`,
3496
4942
  `export API_METHOD='${request.method}'`,
3497
- `export API_GATEWAY_URL='${request.protocol}://${request.hostname}'`,
4943
+ `export API_GATEWAY_URL='${request.protocol}://${request.headers["x-forwarded-host"] ?? request.headers.host ?? request.hostname}'`,
3498
4944
  `export API_TENANT_ID='${config.tenantId}'`,
3499
- `export API_ASSISTANT_ID='${config.assistantId || ""}'`
4945
+ `export API_ASSISTANT_ID='${config.assistantId || ""}'`,
4946
+ `export OPEN_API_TOKEN='${this._mintSandboxToken(sandbox.name, config)}'`
3500
4947
  ].join(" && ");
3501
4948
  const fullCmd = `${envVars} && ${cmd}`;
3502
4949
  this.deps.logger.info("[api] exec", { scriptPath, cmd: cmd.substring(0, 80) });
@@ -3524,6 +4971,23 @@ var init_resources = __esm({
3524
4971
  _passwordPage(token) {
3525
4972
  return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Password Protected</title><style>body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f5f5f5}.card{background:#fff;padding:32px;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.1);width:100%;max-width:360px;text-align:center}h2{margin:0 0 8px;font-size:20px}p{margin:0 0 20px;color:#666;font-size:14px}input{width:100%;padding:10px 12px;border:1px solid #d9d9d9;border-radius:8px;font-size:14px;box-sizing:border-box;margin-bottom:12px}button{width:100%;padding:10px;background:#6366f1;color:#fff;border:none;border-radius:8px;font-size:14px;cursor:pointer}.error{color:#ef4444;font-size:13px;margin-top:8px;display:none}</style></head><body><div class="card"><h2>Password Protected</h2><p>This share requires a password to access.</p><form id="f"><input type="password" id="p" placeholder="Enter password" required><button type="submit">Unlock</button><div class="error" id="e">Incorrect password</div></form></div><script>document.getElementById('f').onsubmit=async(e)=>{e.preventDefault();const r=await fetch('/s/${token}/unlock',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({password:document.getElementById('p').value})});if(r.ok)location.reload();else document.getElementById('e').style.display='block'}</script></body></html>`;
3526
4973
  }
4974
+ _mintSandboxToken(sandboxName, config) {
4975
+ const { signOpenToken: signOpenToken2 } = (init_openToken(), __toCommonJS(openToken_exports));
4976
+ return signOpenToken2({
4977
+ kind: "sandbox_token",
4978
+ tenantId: config.tenantId,
4979
+ workspaceId: config.workspaceId,
4980
+ projectId: config.projectId,
4981
+ sandboxName,
4982
+ grants: [
4983
+ {
4984
+ domain: "sandbox",
4985
+ items: ["files_read", "files_list", "files_write", "shell_exec"]
4986
+ },
4987
+ ...sanitizeShareOpenGrants(config.openGrants)
4988
+ ]
4989
+ });
4990
+ }
3527
4991
  };
3528
4992
  }
3529
4993
  });
@@ -5560,6 +7024,36 @@ var SkillService = class {
5560
7024
  constructor() {
5561
7025
  this.store = new import_core10.SandboxSkillStore();
5562
7026
  }
7027
+ /**
7028
+ * Read the read-only skills contributed by the tenant's sandbox plugins.
7029
+ * Sandbox failures degrade silently so the system view still returns
7030
+ * built-in and store skills.
7031
+ */
7032
+ async tenantPluginSkills(tenantId) {
7033
+ const items = [];
7034
+ try {
7035
+ const loaded = await (0, import_core10.getSandboxPluginCatalog)().ensureLoaded(tenantId);
7036
+ for (const plugin of loaded.plugins) {
7037
+ for (const [name, definition] of Object.entries(plugin.skills ?? {})) {
7038
+ const { meta } = (0, import_core10.parseSkillFrontmatter)(definition.content);
7039
+ items.push({
7040
+ id: name,
7041
+ name: meta.name || name,
7042
+ description: meta.description || "",
7043
+ version: definition.version,
7044
+ pluginType: plugin.meta.type,
7045
+ resourcePaths: Object.entries(definition.resources ?? {}).map(([path7, resource]) => ({
7046
+ path: path7,
7047
+ mimeType: resource.mimeType
7048
+ })),
7049
+ content: definition.content
7050
+ });
7051
+ }
7052
+ }
7053
+ } catch {
7054
+ }
7055
+ return items;
7056
+ }
5563
7057
  async getAllSkills(request) {
5564
7058
  const tenantId = getTenantId4(request);
5565
7059
  const storeSkills = await this.store.getAllSkills(tenantId, buildContext(request));
@@ -5570,6 +7064,9 @@ var SkillService = class {
5570
7064
  for (const item of import_core10.PluginRegistry.listSkills()) {
5571
7065
  merged.set(item.id, pluginSkillToSkill(item, tenantId));
5572
7066
  }
7067
+ for (const item of await this.tenantPluginSkills(tenantId)) {
7068
+ merged.set(item.id, pluginSkillToSkill(item, tenantId, item.content));
7069
+ }
5573
7070
  return Array.from(merged.values());
5574
7071
  }
5575
7072
  async getSkillById(request, id3) {
@@ -5582,21 +7079,23 @@ var SkillService = class {
5582
7079
  return pluginSkillToSkill(item, tenantId, definition.content);
5583
7080
  }
5584
7081
  }
7082
+ const tenantItem = (await this.tenantPluginSkills(tenantId)).find((entry) => entry.id === id3);
7083
+ if (tenantItem) return pluginSkillToSkill(tenantItem, tenantId, tenantItem.content);
5585
7084
  return this.store.getSkillById(tenantId, id3, buildContext(request));
5586
7085
  }
5587
7086
  async createSkill(request, id3, data) {
5588
7087
  const tenantId = getTenantId4(request);
5589
- this.assertNotPluginSkill(id3, "created");
7088
+ await this.assertMutable(tenantId, id3, "created");
5590
7089
  return this.store.createSkill(tenantId, id3, data, buildContext(request));
5591
7090
  }
5592
7091
  async updateSkill(request, id3, updates) {
5593
7092
  const tenantId = getTenantId4(request);
5594
- this.assertNotPluginSkill(id3, "modified");
7093
+ await this.assertMutable(tenantId, id3, "modified");
5595
7094
  return this.store.updateSkill(tenantId, id3, updates, buildContext(request));
5596
7095
  }
5597
7096
  async deleteSkill(request, id3) {
5598
7097
  const tenantId = getTenantId4(request);
5599
- this.assertNotPluginSkill(id3, "deleted");
7098
+ await this.assertMutable(tenantId, id3, "deleted");
5600
7099
  return this.store.deleteSkill(tenantId, id3, buildContext(request));
5601
7100
  }
5602
7101
  async searchByMetadata(request, key, value) {
@@ -5612,7 +7111,18 @@ var SkillService = class {
5612
7111
  return this.store.filterByLicense(tenantId, license, buildContext(request));
5613
7112
  }
5614
7113
  /**
5615
- * Guard against mutating a registered plugin skill.
7114
+ * Guard against mutating a built-in or tenant-plugin skill.
7115
+ * Message style mirrors SandboxSkillStore built-in errors so the controller's
7116
+ * existing "built-in skill" -> 403 mapping applies. Runs before any store write.
7117
+ */
7118
+ async assertMutable(tenantId, id3, action) {
7119
+ this.assertNotPluginSkill(id3, action);
7120
+ if ((await this.tenantPluginSkills(tenantId)).some((skill) => skill.id === id3)) {
7121
+ throw new Error(`Skill "${id3}" is a built-in skill and cannot be ${action}`);
7122
+ }
7123
+ }
7124
+ /**
7125
+ * Guard against mutating a globally registered plugin skill.
5616
7126
  * Message style mirrors SandboxSkillStore built-in errors so the controller's
5617
7127
  * existing "built-in skill" -> 403 mapping applies.
5618
7128
  */
@@ -5818,169 +7328,8 @@ async function filterSkillsByLicense(request, reply) {
5818
7328
  }
5819
7329
  }
5820
7330
 
5821
- // src/services/collection_service.ts
5822
- var import_uuid2 = require("uuid");
5823
- var import_documents = require("@langchain/core/documents");
5824
- var import_core11 = require("@axiom-lattice/core");
5825
- function getTenantId5(request) {
5826
- return request.user?.tenantId || request.headers["x-tenant-id"] || "default";
5827
- }
5828
- var CollectionService = class {
5829
- // ─── Collection CRUD ──────────────────────────────────────────
5830
- async getAllCollections(request) {
5831
- const tenantId = getTenantId5(request);
5832
- const collections = await import_core11.collectionLatticeManager.getAllCollections(tenantId);
5833
- for (const c2 of collections) {
5834
- try {
5835
- c2._entryCount = await (0, import_core11.getCollectionEntryCount)(c2.name, c2.embeddingKey, c2.tenantId);
5836
- } catch {
5837
- c2._entryCount = 0;
5838
- }
5839
- }
5840
- return collections;
5841
- }
5842
- async getCollectionByName(request, name) {
5843
- const tenantId = getTenantId5(request);
5844
- return import_core11.collectionLatticeManager.getCollection(tenantId, name);
5845
- }
5846
- async createCollection(request, data) {
5847
- const tenantId = getTenantId5(request);
5848
- const collection = await import_core11.collectionLatticeManager.registerCollection(tenantId, data);
5849
- try {
5850
- await (0, import_core11.getOrCreateCollectionVectorStore)(data.name, data.embeddingKey, tenantId);
5851
- } catch (err) {
5852
- console.warn(`[CollectionService] Failed to init vector store for ${data.name}:`, err);
5853
- }
5854
- return collection;
5855
- }
5856
- async updateCollection(request, name, updates) {
5857
- const tenantId = getTenantId5(request);
5858
- return import_core11.collectionLatticeManager.updateCollection(tenantId, name, updates);
5859
- }
5860
- async deleteCollection(request, name) {
5861
- const tenantId = getTenantId5(request);
5862
- await (0, import_core11.removeCollectionVectorStore)(name, tenantId);
5863
- return import_core11.collectionLatticeManager.removeCollection(tenantId, name);
5864
- }
5865
- // ─── Entry CRUD ───────────────────────────────────────────────
5866
- async getEntries(request, collectionName) {
5867
- const collection = await this.getCollectionByName(request, collectionName);
5868
- if (!collection) throw new Error(`Collection "${collectionName}" not found`);
5869
- const docs = await (0, import_core11.listCollectionEntries)(collectionName, collection.embeddingKey, collection.tenantId);
5870
- return docs.map((doc) => ({
5871
- id: doc.metadata._id || "",
5872
- content: doc.pageContent,
5873
- metadata: Object.fromEntries(
5874
- Object.entries(doc.metadata).filter(([k]) => !k.startsWith("_"))
5875
- )
5876
- }));
5877
- }
5878
- async createEntry(request, collectionName, data) {
5879
- const collection = await this.getCollectionByName(request, collectionName);
5880
- if (!collection) throw new Error(`Collection "${collectionName}" not found`);
5881
- const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
5882
- collectionName,
5883
- collection.embeddingKey,
5884
- collection.tenantId
5885
- );
5886
- const entryId = (0, import_uuid2.v4)();
5887
- const now = (/* @__PURE__ */ new Date()).toISOString();
5888
- const doc = new import_documents.Document({
5889
- pageContent: data.content,
5890
- metadata: {
5891
- _id: entryId,
5892
- _created_at: now,
5893
- _updated_at: now,
5894
- ...data.metadata || {}
5895
- }
5896
- });
5897
- await vectorStore.addDocuments([doc]);
5898
- return { id: entryId, content: data.content, metadata: data.metadata || {} };
5899
- }
5900
- async getEntry(request, collectionName, entryId) {
5901
- const collection = await this.getCollectionByName(request, collectionName);
5902
- if (!collection) throw new Error(`Collection "${collectionName}" not found`);
5903
- const docs = await (0, import_core11.listCollectionEntries)(collectionName, collection.embeddingKey, collection.tenantId);
5904
- const doc = docs.find((d) => d.metadata._id === entryId);
5905
- if (!doc) return null;
5906
- return {
5907
- id: entryId,
5908
- content: doc.pageContent,
5909
- metadata: Object.fromEntries(
5910
- Object.entries(doc.metadata).filter(([k]) => !k.startsWith("_"))
5911
- )
5912
- };
5913
- }
5914
- async updateEntry(request, collectionName, entryId, data) {
5915
- const existing = await this.getEntry(request, collectionName, entryId);
5916
- if (!existing) return null;
5917
- const collection = await this.getCollectionByName(request, collectionName);
5918
- if (!collection) throw new Error(`Collection "${collectionName}" not found`);
5919
- const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
5920
- collectionName,
5921
- collection.embeddingKey,
5922
- collection.tenantId
5923
- );
5924
- try {
5925
- await vectorStore.delete({ filter: { _id: entryId } });
5926
- } catch {
5927
- }
5928
- const now = (/* @__PURE__ */ new Date()).toISOString();
5929
- const mergedMetadata = { ...existing.metadata, ...data.metadata || {} };
5930
- const doc = new import_documents.Document({
5931
- pageContent: data.content ?? existing.content,
5932
- metadata: { _id: entryId, _created_at: now, _updated_at: now, ...mergedMetadata }
5933
- });
5934
- await vectorStore.addDocuments([doc]);
5935
- return { id: entryId, content: data.content ?? existing.content, metadata: mergedMetadata };
5936
- }
5937
- async deleteEntry(request, collectionName, entryId) {
5938
- const collection = await this.getCollectionByName(request, collectionName);
5939
- if (!collection) throw new Error(`Collection "${collectionName}" not found`);
5940
- const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
5941
- collectionName,
5942
- collection.embeddingKey,
5943
- collection.tenantId
5944
- );
5945
- try {
5946
- await vectorStore.delete({ filter: { _id: entryId } });
5947
- return true;
5948
- } catch {
5949
- return false;
5950
- }
5951
- }
5952
- async searchEntries(request, collectionName, params) {
5953
- const collection = await this.getCollectionByName(request, collectionName);
5954
- if (!collection) throw new Error(`Collection "${collectionName}" not found`);
5955
- const vectorStore = await (0, import_core11.getOrCreateCollectionVectorStore)(
5956
- collectionName,
5957
- collection.embeddingKey,
5958
- collection.tenantId
5959
- );
5960
- let embeddings;
5961
- try {
5962
- embeddings = import_core11.embeddingsLatticeManager.getEmbeddingsClient(collection.embeddingKey);
5963
- } catch {
5964
- embeddings = import_core11.embeddingsLatticeManager.getEmbeddingsClient("default");
5965
- }
5966
- const queryVector = await embeddings.embedQuery(params.query);
5967
- const results = await vectorStore.similaritySearchVectorWithScore(
5968
- queryVector,
5969
- params.top_k || 5,
5970
- params.filter
5971
- );
5972
- return results.map(([doc, score]) => ({
5973
- id: doc.metadata._id || (0, import_uuid2.v4)(),
5974
- content: doc.pageContent,
5975
- metadata: Object.fromEntries(
5976
- Object.entries(doc.metadata).filter(([k]) => !k.startsWith("_"))
5977
- ),
5978
- score
5979
- }));
5980
- }
5981
- };
5982
-
5983
7331
  // src/controllers/collections.ts
7332
+ init_collection_service();
5984
7333
  var collectionService = new CollectionService();
5985
7334
  function serializeCollection(collection) {
5986
7335
  return {
@@ -6120,6 +7469,7 @@ async function listEmbeddings(_request, _reply) {
6120
7469
  }
6121
7470
 
6122
7471
  // src/controllers/entries.ts
7472
+ init_collection_service();
6123
7473
  var collectionService2 = new CollectionService();
6124
7474
  async function listEntries(request, reply) {
6125
7475
  try {
@@ -7989,10 +9339,20 @@ async function importApply(request, reply) {
7989
9339
 
7990
9340
  // src/controllers/middleware-types.ts
7991
9341
  var import_core19 = require("@axiom-lattice/core");
9342
+ async function collectMiddlewareMetas(tenantId) {
9343
+ let tenantPlugins = [];
9344
+ try {
9345
+ tenantPlugins = (await (0, import_core19.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
9346
+ } catch {
9347
+ tenantPlugins = [];
9348
+ }
9349
+ const globalPlugins = import_core19.PluginRegistry.list().map((type) => import_core19.PluginRegistry.get(type)).filter((p) => Boolean(p));
9350
+ return (0, import_core19.mergePluginSources)(globalPlugins, tenantPlugins).map(import_core19.serializePluginMeta);
9351
+ }
7992
9352
  function middlewareTypesRoutes(fastify2) {
7993
- fastify2.get("/api/middleware-types", async (_request, reply) => {
7994
- const metas = import_core19.PluginRegistry.listMeta();
7995
- return reply.send(metas);
9353
+ fastify2.get("/api/middleware-types", async (request, reply) => {
9354
+ const tenantId = request.user?.tenantId || request.headers["x-tenant-id"] || "default";
9355
+ return reply.send(await collectMiddlewareMetas(tenantId));
7996
9356
  });
7997
9357
  }
7998
9358
 
@@ -8714,6 +10074,7 @@ var import_core25 = require("@axiom-lattice/core");
8714
10074
  var import_core26 = require("@axiom-lattice/core");
8715
10075
  var import_uuid3 = require("uuid");
8716
10076
  init_mime();
10077
+ init_previewToken();
8717
10078
  init_workspace_file_service();
8718
10079
  init_path_containment();
8719
10080
 
@@ -9666,7 +11027,7 @@ var WorkspaceController = class {
9666
11027
  workspaceId,
9667
11028
  projectId,
9668
11029
  timestamp: Date.now()
9669
- })};</script>`;
11030
+ })};</script>` + buildPreviewTokenScript({ tenantId, workspaceId, projectId });
9670
11031
  if (content2.toLowerCase().includes("</head>")) {
9671
11032
  content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
9672
11033
  console.log(`[viewFile] Context script injected before </head>`);
@@ -9697,7 +11058,7 @@ var WorkspaceController = class {
9697
11058
  workspaceId,
9698
11059
  projectId,
9699
11060
  timestamp: Date.now()
9700
- })};</script>`;
11061
+ })};</script>` + buildPreviewTokenScript({ tenantId, workspaceId, projectId });
9701
11062
  if (content.toLowerCase().includes("</head>")) {
9702
11063
  finalContent = content.replace(/<\/head>/i, `${contextScript}</head>`);
9703
11064
  console.log(`[viewFile] Context script injected before </head>`);
@@ -10020,13 +11381,17 @@ var ProjectAccessService = class {
10020
11381
  this.requireCapability(membership, input.capability);
10021
11382
  return { project, membership };
10022
11383
  }
10023
- /** Authorizes roster visibility: read for public rooms, manage elsewhere. */
11384
+ /**
11385
+ * Authorizes roster visibility for any active participant (owner/admin/member/viewer).
11386
+ * Mutating the roster requires `manage` separately; reading it never does, because every
11387
+ * room participant needs the member list to render the room and derive their own capabilities.
11388
+ */
10024
11389
  async requireRosterAccess(input) {
10025
11390
  const project = await this.deps.projectStore.getProjectById(input.tenantId, input.projectId);
10026
11391
  if (!project) throw new ProjectCollaborationError("PROJECT_NOT_FOUND", 404, "Project not found");
10027
11392
  return this.requireProjectAccess({
10028
11393
  ...input,
10029
- capability: project.kind === "public" ? "read" : "manage"
11394
+ capability: "read"
10030
11395
  });
10031
11396
  }
10032
11397
  async requirePublicProjectAccess(project, input) {
@@ -10111,7 +11476,7 @@ var ProjectAccessService = class {
10111
11476
 
10112
11477
  // src/controllers/database-configs.ts
10113
11478
  var import_core27 = require("@axiom-lattice/core");
10114
- var import_crypto5 = require("crypto");
11479
+ var import_crypto6 = require("crypto");
10115
11480
  function getTenantId9(request) {
10116
11481
  const userTenantId = request.user?.tenantId;
10117
11482
  if (userTenantId) {
@@ -10189,7 +11554,7 @@ async function createDatabaseConfig(request, reply) {
10189
11554
  message: "Database configuration with this key already exists"
10190
11555
  };
10191
11556
  }
10192
- const id3 = body.id || (0, import_crypto5.randomUUID)();
11557
+ const id3 = body.id || (0, import_crypto6.randomUUID)();
10193
11558
  const config = await store2.createConfig(tenantId, id3, body);
10194
11559
  try {
10195
11560
  import_core27.sqlDatabaseManager.registerDatabase(tenantId, config.key, config.config);
@@ -10390,7 +11755,7 @@ function registerDatabaseConfigRoutes(app2) {
10390
11755
 
10391
11756
  // src/controllers/metrics-configs.ts
10392
11757
  var import_core28 = require("@axiom-lattice/core");
10393
- var import_crypto6 = require("crypto");
11758
+ var import_crypto7 = require("crypto");
10394
11759
  function getTenantId10(request) {
10395
11760
  const userTenantId = request.user?.tenantId;
10396
11761
  if (userTenantId) {
@@ -10471,7 +11836,7 @@ async function createMetricsServerConfig(request, reply) {
10471
11836
  message: "selectedDataSources is required for semantic metrics servers"
10472
11837
  };
10473
11838
  }
10474
- const id3 = body.id || (0, import_crypto6.randomUUID)();
11839
+ const id3 = body.id || (0, import_crypto7.randomUUID)();
10475
11840
  const configData = {
10476
11841
  key: body.key,
10477
11842
  name: body.name,
@@ -10992,6 +12357,17 @@ function getTenant(request) {
10992
12357
  }
10993
12358
  return request.headers["x-tenant-id"] || "default";
10994
12359
  }
12360
+ async function getTenantPlugin(request, type) {
12361
+ const tenantId = getTenant(request);
12362
+ let tenantPlugins = [];
12363
+ try {
12364
+ tenantPlugins = (await (0, import_core29.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
12365
+ } catch {
12366
+ tenantPlugins = [];
12367
+ }
12368
+ const globalPlugins = import_core29.PluginRegistry.list().map((t2) => import_core29.PluginRegistry.get(t2)).filter((p) => Boolean(p));
12369
+ return (0, import_core29.mergePluginSources)(globalPlugins, tenantPlugins).find((p) => p.meta.type === type);
12370
+ }
10995
12371
  function maskSecrets(config) {
10996
12372
  const sensitive = /* @__PURE__ */ new Set([
10997
12373
  "password",
@@ -11075,7 +12451,7 @@ function connectionRoutes(fastify2) {
11075
12451
  fastify2.post("/api/connections/test", async (request, reply) => {
11076
12452
  const { type } = request.query;
11077
12453
  if (!type) return reply.status(400).send({ error: "query 'type' required" });
11078
- const plugin = import_core29.PluginRegistry.get(type);
12454
+ const plugin = await getTenantPlugin(request, type);
11079
12455
  if (!plugin?.connection?.test) {
11080
12456
  return reply.status(400).send({ error: "plugin does not support connection testing" });
11081
12457
  }
@@ -11103,7 +12479,7 @@ function connectionRoutes(fastify2) {
11103
12479
  fastify2.post("/api/connections/discover", async (request, reply) => {
11104
12480
  const { type } = request.query;
11105
12481
  if (!type) return reply.status(400).send({ error: "query 'type' required" });
11106
- const plugin = import_core29.PluginRegistry.get(type);
12482
+ const plugin = await getTenantPlugin(request, type);
11107
12483
  if (!plugin?.connection?.discover) {
11108
12484
  return reply.status(400).send({ error: "plugin does not support resource discovery" });
11109
12485
  }
@@ -12526,9 +13902,9 @@ function resolveConsoleTokenSecret(_authRequired) {
12526
13902
  return devSessionSecret;
12527
13903
  }
12528
13904
  var devSessionSecret;
12529
- function createConsoleSessionToken(payload, secret) {
13905
+ function createConsoleSessionToken(payload, secret2) {
12530
13906
  const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
12531
- const signature = (0, import_node_crypto4.createHmac)("sha256", secret).update(encodedPayload).digest("base64url");
13907
+ const signature = (0, import_node_crypto4.createHmac)("sha256", secret2).update(encodedPayload).digest("base64url");
12532
13908
  return `${encodedPayload}.${signature}`;
12533
13909
  }
12534
13910
  function isConsoleSessionPayload(value) {
@@ -12536,17 +13912,17 @@ function isConsoleSessionPayload(value) {
12536
13912
  const payload = value;
12537
13913
  return typeof payload.userId === "string" && payload.userId.length > 0 && typeof payload.exp === "number" && (payload.tenantId === void 0 || typeof payload.tenantId === "string");
12538
13914
  }
12539
- function extractUserFromAuthHeader(authHeader, secret) {
12540
- const session = verifySignedConsoleSession(authHeader, secret);
13915
+ function extractUserFromAuthHeader(authHeader, secret2) {
13916
+ const session = verifySignedConsoleSession(authHeader, secret2);
12541
13917
  return session ? { id: session.id, tenantId: session.tenantId } : void 0;
12542
13918
  }
12543
- function verifyConsoleSessionAuthHeader(authHeader, secret) {
12544
- const session = verifySignedConsoleSession(authHeader, secret);
13919
+ function verifyConsoleSessionAuthHeader(authHeader, secret2) {
13920
+ const session = verifySignedConsoleSession(authHeader, secret2);
12545
13921
  if (!session?.tenantId || session.tenantId.trim() !== session.tenantId) return void 0;
12546
13922
  return { id: session.id, tenantId: session.tenantId, expiresAt: session.expiresAt };
12547
13923
  }
12548
- function verifySignedConsoleSession(authHeader, secret) {
12549
- if (!secret || !authHeader?.startsWith("Bearer ")) return void 0;
13924
+ function verifySignedConsoleSession(authHeader, secret2) {
13925
+ if (!secret2 || !authHeader?.startsWith("Bearer ")) return void 0;
12550
13926
  const token = authHeader.substring(7);
12551
13927
  const parts = token.split(".");
12552
13928
  if (parts.length !== 2) return void 0;
@@ -12555,7 +13931,7 @@ function verifySignedConsoleSession(authHeader, secret) {
12555
13931
  try {
12556
13932
  const actualSignature = Buffer.from(encodedSignature, "base64url");
12557
13933
  if (actualSignature.toString("base64url") !== encodedSignature) return void 0;
12558
- const expectedSignature = (0, import_node_crypto4.createHmac)("sha256", secret).update(encodedPayload).digest();
13934
+ const expectedSignature = (0, import_node_crypto4.createHmac)("sha256", secret2).update(encodedPayload).digest();
12559
13935
  if (actualSignature.length !== expectedSignature.length || !(0, import_node_crypto4.timingSafeEqual)(actualSignature, expectedSignature)) {
12560
13936
  return void 0;
12561
13937
  }
@@ -12588,7 +13964,7 @@ function isStaleConsoleSessionCredential(authHeader) {
12588
13964
  function bypassesConsoleAuth(request) {
12589
13965
  const path7 = request.url.split("?")[0];
12590
13966
  const method = request.method;
12591
- if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path7) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path7) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path7) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path7) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path7) || method === "GET" && /^\/embed\/[^/]+$/.test(path7) || (method === "POST" || method === "DELETE") && path7 === "/open/mcp") return true;
13967
+ if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path7) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path7) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path7) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path7) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path7) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path7) || method === "GET" && /^\/embed\/[^/]+$/.test(path7) || (method === "POST" || method === "DELETE") && path7 === "/open/mcp" || method === "POST" && path7 === "/open/session" || (method === "GET" || method === "POST") && path7.startsWith("/open/sandbox/files/")) return true;
12592
13968
  if (request.method === "GET" && /^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path7)) {
12593
13969
  return true;
12594
13970
  }
@@ -12596,8 +13972,8 @@ function bypassesConsoleAuth(request) {
12596
13972
  }
12597
13973
  function registerConsoleAuthHook(app2, options) {
12598
13974
  app2.addHook("preHandler", async (request, reply) => {
12599
- const secret = options.tokenSecret ?? resolveConsoleTokenSecret(options.authRequired);
12600
- const session = verifySignedConsoleSession(request.headers.authorization, secret);
13975
+ const secret2 = options.tokenSecret ?? resolveConsoleTokenSecret(options.authRequired);
13976
+ const session = verifySignedConsoleSession(request.headers.authorization, secret2);
12601
13977
  if (session) {
12602
13978
  const user = { id: session.id, tenantId: session.tenantId, tokenExpiresAt: session.expiresAt };
12603
13979
  request.user = user;
@@ -12850,7 +14226,7 @@ var larkChannelAdapter = {
12850
14226
  };
12851
14227
 
12852
14228
  // src/channels/lark/verification.ts
12853
- var import_crypto8 = __toESM(require("crypto"));
14229
+ var import_crypto9 = __toESM(require("crypto"));
12854
14230
  function parseLarkRequestBody(body, encryptKey) {
12855
14231
  const parsed = body || {};
12856
14232
  if (encryptKey && typeof parsed.encrypt === "string") {
@@ -12859,11 +14235,11 @@ function parseLarkRequestBody(body, encryptKey) {
12859
14235
  return parsed;
12860
14236
  }
12861
14237
  function decryptLarkPayload(encryptKey, encryptedPayload) {
12862
- const key = import_crypto8.default.createHash("sha256").update(encryptKey).digest();
14238
+ const key = import_crypto9.default.createHash("sha256").update(encryptKey).digest();
12863
14239
  const buffer = Buffer.from(encryptedPayload, "base64");
12864
14240
  const iv = buffer.subarray(0, 16);
12865
14241
  const ciphertext = buffer.subarray(16);
12866
- const decipher = import_crypto8.default.createDecipheriv("aes-256-cbc", key, iv);
14242
+ const decipher = import_crypto9.default.createDecipheriv("aes-256-cbc", key, iv);
12867
14243
  const plaintext = Buffer.concat([
12868
14244
  decipher.update(ciphertext),
12869
14245
  decipher.final()
@@ -12980,7 +14356,7 @@ function registerChannelRoutes(app2, dependencies) {
12980
14356
  }
12981
14357
 
12982
14358
  // src/controllers/channel-installations.ts
12983
- var import_crypto9 = require("crypto");
14359
+ var import_crypto10 = require("crypto");
12984
14360
  var import_protocols5 = require("@axiom-lattice/protocols");
12985
14361
  var _adapterRegistry;
12986
14362
  var _router;
@@ -13105,8 +14481,8 @@ function snapshotPlainObject(value) {
13105
14481
  }
13106
14482
  }
13107
14483
  async function getInstallationStore() {
13108
- const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
13109
- const store2 = getStoreLattice31("default", "channelInstallation").store;
14484
+ const { getStoreLattice: getStoreLattice37 } = await import("@axiom-lattice/core");
14485
+ const store2 = getStoreLattice37("default", "channelInstallation").store;
13110
14486
  if (store2) return store2;
13111
14487
  const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
13112
14488
  const databaseUrl = process.env.DATABASE_URL;
@@ -13182,7 +14558,7 @@ async function createChannelInstallation(request, reply) {
13182
14558
  };
13183
14559
  }
13184
14560
  const store2 = await getInstallationStore();
13185
- const installationId = (0, import_crypto9.randomUUID)();
14561
+ const installationId = (0, import_crypto10.randomUUID)();
13186
14562
  const createInput = {
13187
14563
  channel: body.channel,
13188
14564
  name: body.name,
@@ -16612,10 +17988,10 @@ function resolveAgentWebAppAttachmentSecret(consoleSecret) {
16612
17988
  return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
16613
17989
  }
16614
17990
  var AgentWebAppAttachmentRefService = class {
16615
- constructor(secret, now = Date.now) {
17991
+ constructor(secret2, now = Date.now) {
16616
17992
  this.now = now;
16617
- if (!secret) throw new Error("Attachment signing secret is required");
16618
- this.key = (0, import_node_crypto9.createHash)("sha256").update(secret, "utf8").digest();
17993
+ if (!secret2) throw new Error("Attachment signing secret is required");
17994
+ this.key = (0, import_node_crypto9.createHash)("sha256").update(secret2, "utf8").digest();
16619
17995
  }
16620
17996
  sign(input) {
16621
17997
  const payload = {
@@ -17875,7 +19251,14 @@ function registerProjectRoomRoutes(app2, deps) {
17875
19251
  await deps.readStateService.mark(actor(request, params.projectId));
17876
19252
  return { data: { marked: true } };
17877
19253
  }));
17878
- app2.get("/api/projects/:projectId/members", async (request, reply) => run(reply, async () => ({ data: await deps.membershipService.list(actor(request, projectRoomParamsSchema.parse(request.params).projectId)) })));
19254
+ app2.get("/api/projects/:projectId/members", async (request, reply) => run(reply, async () => {
19255
+ const memberships = await deps.membershipService.list(actor(request, projectRoomParamsSchema.parse(request.params).projectId));
19256
+ const enriched = await Promise.all(memberships.map(async (membership) => {
19257
+ const user = await deps.userDirectory.getUserById(membership.userId);
19258
+ return user === null ? membership : { ...membership, name: user.name };
19259
+ }));
19260
+ return { data: enriched };
19261
+ }));
17879
19262
  app2.post("/api/projects/:projectId/members", async (request, reply) => run(reply, async () => {
17880
19263
  const p = projectRoomParamsSchema.parse(request.params);
17881
19264
  return { statusCode: 201, data: await deps.membershipService.add(actor(request, p.projectId), addProjectMemberSchema.parse(request.body)) };
@@ -18047,13 +19430,16 @@ function registerCoSpaceRoutes(app2, deps) {
18047
19430
  project.id
18048
19431
  );
18049
19432
  const participantNames = [];
19433
+ const otherHumanNames = [];
18050
19434
  for (const m of memberships) {
18051
19435
  if (m.status !== "active") continue;
18052
19436
  const user = await deps.userStore.getUserById(m.userId);
18053
19437
  if (user) {
18054
19438
  participantNames.push(user.name);
19439
+ if (m.userId !== actor2.userId) otherHumanNames.push(user.name);
18055
19440
  }
18056
19441
  }
19442
+ const displayName = project.kind === "personal" && otherHumanNames.length === 1 ? otherHumanNames[0] : void 0;
18057
19443
  rooms.push({
18058
19444
  project,
18059
19445
  room,
@@ -18062,6 +19448,7 @@ function registerCoSpaceRoutes(app2, deps) {
18062
19448
  (m) => m.status === "active"
18063
19449
  ).length,
18064
19450
  participantNames,
19451
+ ...displayName === void 0 ? {} : { displayName },
18065
19452
  unreadCount: await computeUnread(
18066
19453
  deps,
18067
19454
  actor2.tenantId,
@@ -18079,41 +19466,45 @@ function registerCoSpaceRoutes(app2, deps) {
18079
19466
  const bTime = b.lastMessage ? Date.parse(b.lastMessage.createdAt) : 0;
18080
19467
  return bTime - aTime;
18081
19468
  });
18082
- const publicRoom = await deps.publicRoom.ensure(actor2.tenantId, params.workspaceId);
18083
- if (publicRoom) {
18084
- const publicMessages = await deps.messageStore.list({
18085
- tenantId: actor2.tenantId,
18086
- roomId: publicRoom.room.id,
18087
- limit: 1
18088
- });
18089
- const publicLastMessage = publicMessages[0];
18090
- const publicMemberships = await deps.membershipStore.list(
18091
- actor2.tenantId,
18092
- publicRoom.project.id
18093
- );
18094
- const publicParticipantNames = [];
18095
- for (const m of publicMemberships) {
18096
- if (m.status !== "active") continue;
18097
- const user = await deps.userStore.getUserById(m.userId);
18098
- if (user) {
18099
- publicParticipantNames.push(user.name);
19469
+ try {
19470
+ const publicRoom = await deps.publicRoom.ensure(actor2.tenantId, params.workspaceId);
19471
+ if (publicRoom) {
19472
+ const publicMessages = await deps.messageStore.list({
19473
+ tenantId: actor2.tenantId,
19474
+ roomId: publicRoom.room.id,
19475
+ limit: 1
19476
+ });
19477
+ const publicLastMessage = publicMessages[0];
19478
+ const publicMemberships = await deps.membershipStore.list(
19479
+ actor2.tenantId,
19480
+ publicRoom.project.id
19481
+ );
19482
+ const publicParticipantNames = [];
19483
+ for (const m of publicMemberships) {
19484
+ if (m.status !== "active") continue;
19485
+ const user = await deps.userStore.getUserById(m.userId);
19486
+ if (user) {
19487
+ publicParticipantNames.push(user.name);
19488
+ }
18100
19489
  }
19490
+ rooms.unshift({
19491
+ project: publicRoom.project,
19492
+ room: publicRoom.room,
19493
+ lastMessage: publicLastMessage === void 0 ? void 0 : (0, import_protocols15.toProjectRoomPublicMessage)(publicLastMessage),
19494
+ participantCount: publicMemberships.filter((m) => m.status === "active").length,
19495
+ participantNames: publicParticipantNames,
19496
+ isPublic: true,
19497
+ unreadCount: await computeUnread(
19498
+ deps,
19499
+ actor2.tenantId,
19500
+ actor2.userId,
19501
+ publicRoom.room.id,
19502
+ publicLastMessage
19503
+ )
19504
+ });
18101
19505
  }
18102
- rooms.unshift({
18103
- project: publicRoom.project,
18104
- room: publicRoom.room,
18105
- lastMessage: publicLastMessage === void 0 ? void 0 : (0, import_protocols15.toProjectRoomPublicMessage)(publicLastMessage),
18106
- participantCount: publicMemberships.filter((m) => m.status === "active").length,
18107
- participantNames: publicParticipantNames,
18108
- isPublic: true,
18109
- unreadCount: await computeUnread(
18110
- deps,
18111
- actor2.tenantId,
18112
- actor2.userId,
18113
- publicRoom.room.id,
18114
- publicLastMessage
18115
- )
18116
- });
19506
+ } catch (e) {
19507
+ console.error("[CoSpace] Failed to assemble public room:", e);
18117
19508
  }
18118
19509
  return { data: { rooms } };
18119
19510
  });
@@ -18138,7 +19529,6 @@ function registerCoSpaceRoutes(app2, deps) {
18138
19529
  );
18139
19530
  }
18140
19531
  let projectId;
18141
- let otherPartyId;
18142
19532
  if (body.type === "human") {
18143
19533
  projectId = (0, import_core44.generateDirectHumanProjectId)(
18144
19534
  actor2.tenantId,
@@ -18146,7 +19536,6 @@ function registerCoSpaceRoutes(app2, deps) {
18146
19536
  actor2.userId,
18147
19537
  body.targetUserId
18148
19538
  );
18149
- otherPartyId = body.targetUserId;
18150
19539
  } else {
18151
19540
  projectId = (0, import_core44.generateDirectBotProjectId)(
18152
19541
  actor2.tenantId,
@@ -18154,14 +19543,24 @@ function registerCoSpaceRoutes(app2, deps) {
18154
19543
  actor2.userId,
18155
19544
  body.assistantId
18156
19545
  );
18157
- otherPartyId = body.assistantId;
18158
19546
  }
18159
19547
  let project = await deps.projectStore.getProjectById(
18160
19548
  actor2.tenantId,
18161
19549
  projectId
18162
19550
  );
18163
19551
  if (!project) {
18164
- const name = body.type === "human" ? `Direct with ${otherPartyId}` : `Chat with ${otherPartyId}`;
19552
+ let displayName;
19553
+ if (body.type === "human") {
19554
+ const targetUser = await deps.userStore.getUserById(body.targetUserId);
19555
+ displayName = targetUser?.name ?? body.targetUserId;
19556
+ } else {
19557
+ const targetAssistant = await deps.assistantStore.getAssistantById(
19558
+ actor2.tenantId,
19559
+ body.assistantId
19560
+ );
19561
+ displayName = targetAssistant?.name ?? body.assistantId;
19562
+ }
19563
+ const name = body.type === "human" ? `Direct with ${displayName}` : `Chat with ${displayName}`;
18165
19564
  project = await deps.projectStore.createProject(
18166
19565
  actor2.tenantId,
18167
19566
  params.workspaceId,
@@ -18354,12 +19753,28 @@ function registerCoSpaceRoutes(app2, deps) {
18354
19753
  actor2.tenantId,
18355
19754
  directProjectId
18356
19755
  );
18357
- bots.push({
19756
+ const bot = {
18358
19757
  assistantId: assistant.id,
18359
19758
  name: assistant.name,
18360
19759
  description: assistant.description,
18361
19760
  hasDirectRoom: existingProject !== null
18362
- });
19761
+ };
19762
+ if (assistant.createdAt) bot.createdAt = assistant.createdAt.toISOString();
19763
+ if (existingProject) {
19764
+ const membership = await deps.botMembershipStore.findByAssistant(
19765
+ actor2.tenantId,
19766
+ directProjectId,
19767
+ assistant.id
19768
+ );
19769
+ if (membership) {
19770
+ if (membership.role) bot.role = membership.role;
19771
+ if (membership.mentionName) bot.mentionName = membership.mentionName;
19772
+ if (membership.responsibility) bot.responsibility = membership.responsibility;
19773
+ bot.status = membership.status;
19774
+ if (membership.joinedAt) bot.joinedAt = membership.joinedAt.toISOString();
19775
+ }
19776
+ }
19777
+ bots.push(bot);
18363
19778
  }
18364
19779
  return { data: { bots } };
18365
19780
  });
@@ -18400,13 +19815,15 @@ function registerCoSpaceRoutes(app2, deps) {
18400
19815
  actor2.tenantId,
18401
19816
  directProjectId
18402
19817
  );
18403
- members.push({
19818
+ const member = {
18404
19819
  userId: link.userId,
18405
19820
  name: user.name,
18406
19821
  email: user.email,
18407
19822
  role: link.role,
18408
19823
  hasDirectRoom: existingProject !== null
18409
- });
19824
+ };
19825
+ if (link.joinedAt) member.joinedAt = link.joinedAt.toISOString();
19826
+ members.push(member);
18410
19827
  }
18411
19828
  return { data: { members } };
18412
19829
  });
@@ -22252,7 +23669,15 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
22252
23669
  });
22253
23670
  }
22254
23671
  };
22255
- registerProjectRoomRoutes(app2, { roomService, membershipService, messageService, readStateService });
23672
+ registerProjectRoomRoutes(app2, {
23673
+ roomService,
23674
+ membershipService,
23675
+ messageService,
23676
+ readStateService,
23677
+ userDirectory: {
23678
+ getUserById: (id3) => (0, import_core51.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
23679
+ }
23680
+ });
22256
23681
  registerProjectRosterRoutes(app2, { service: rosterService });
22257
23682
  registerCoSpaceRoutes(app2, {
22258
23683
  projectStore: {
@@ -23617,7 +25042,7 @@ var collectionRegistration = {
23617
25042
 
23618
25043
  // src/export_registrations/channel-installation.registration.ts
23619
25044
  var import_core59 = require("@axiom-lattice/core");
23620
- var import_crypto10 = require("crypto");
25045
+ var import_crypto11 = require("crypto");
23621
25046
  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;
23622
25047
  var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
23623
25048
  function isPublicChannel(channel) {
@@ -23776,7 +25201,7 @@ var channelInstallationRegistration = {
23776
25201
  }
23777
25202
  id3 = sourceId;
23778
25203
  } else {
23779
- id3 = (0, import_crypto10.randomUUID)();
25204
+ id3 = (0, import_crypto11.randomUUID)();
23780
25205
  }
23781
25206
  await store2.createInstallation(tenantId, id3, createInput);
23782
25207
  return { newId: id3 };
@@ -24323,7 +25748,7 @@ function createAuditLoggerMiddleware() {
24323
25748
  }
24324
25749
 
24325
25750
  // src/index.ts
24326
- var import_core69 = require("@axiom-lattice/core");
25751
+ var import_core78 = require("@axiom-lattice/core");
24327
25752
 
24328
25753
  // src/swagger.ts
24329
25754
  var import_swagger = __toESM(require("@fastify/swagger"));
@@ -24640,7 +26065,7 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
24640
26065
  var AgentTaskConsumer = _AgentTaskConsumer;
24641
26066
 
24642
26067
  // src/index.ts
24643
- var import_core70 = require("@axiom-lattice/core");
26068
+ var import_core79 = require("@axiom-lattice/core");
24644
26069
 
24645
26070
  // src/channels/ChannelInfrastructure.ts
24646
26071
  var import_core65 = require("@axiom-lattice/core");
@@ -24648,7 +26073,7 @@ var import_core65 = require("@axiom-lattice/core");
24648
26073
  // src/router/MessageRouter.ts
24649
26074
  var import_core64 = require("@axiom-lattice/core");
24650
26075
  var import_protocols27 = require("@axiom-lattice/protocols");
24651
- var import_crypto11 = require("crypto");
26076
+ var import_crypto12 = require("crypto");
24652
26077
  var BindingNotFoundError = class extends Error {
24653
26078
  constructor(message) {
24654
26079
  super(message);
@@ -24963,7 +26388,7 @@ var MessageRouter = class {
24963
26388
  }
24964
26389
  if (!threadId) {
24965
26390
  const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
24966
- const newThreadId = (0, import_crypto11.randomUUID)();
26391
+ const newThreadId = (0, import_crypto12.randomUUID)();
24967
26392
  console.log({
24968
26393
  event: "dispatch:thread:create",
24969
26394
  agentId,
@@ -25009,7 +26434,7 @@ var MessageRouter = class {
25009
26434
  workspace_id: binding?.workspaceId || "",
25010
26435
  project_id: binding?.projectId || ""
25011
26436
  });
25012
- const inputMessageId = (0, import_crypto11.randomUUID)();
26437
+ const inputMessageId = (0, import_crypto12.randomUUID)();
25013
26438
  const replyTarget = internalRoom ? correlatedRoomReplyTarget(message.replyTarget, inputMessageId) : message.replyTarget;
25014
26439
  const replyAdapter = replyTarget ? this.adapterRegistry.get(replyTarget.adapterChannel) : void 0;
25015
26440
  if (options.trustedMessageOptions && (!this.trustedDispatcher || !replyTarget || !replyAdapter)) {
@@ -25207,25 +26632,25 @@ async function prepareProjectTaskRuntime(runtime, log2) {
25207
26632
  }
25208
26633
 
25209
26634
  // src/index.ts
25210
- var import_protocols30 = require("@axiom-lattice/protocols");
26635
+ var import_protocols35 = require("@axiom-lattice/protocols");
25211
26636
  process.on("unhandledRejection", (reason, promise) => {
25212
26637
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
25213
26638
  });
25214
26639
  var DEFAULT_LOGGER_CONFIG = {
25215
26640
  name: "default",
25216
26641
  description: "Default logger for lattice-gateway service",
25217
- type: import_protocols30.LoggerType.PINO,
26642
+ type: import_protocols35.LoggerType.PINO,
25218
26643
  serviceName: "lattice/gateway",
25219
26644
  loggerName: "lattice/gateway"
25220
26645
  };
25221
26646
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
25222
26647
  var logger6 = loggerLattice.client;
25223
26648
  function initializeLogger(config) {
25224
- if (import_core70.loggerLatticeManager.hasLattice("default")) {
25225
- import_core70.loggerLatticeManager.removeLattice("default");
26649
+ if (import_core79.loggerLatticeManager.hasLattice("default")) {
26650
+ import_core79.loggerLatticeManager.removeLattice("default");
25226
26651
  }
25227
- (0, import_core70.registerLoggerLattice)("default", config);
25228
- return (0, import_core70.getLoggerLattice)("default");
26652
+ (0, import_core79.registerLoggerLattice)("default", config);
26653
+ return (0, import_core79.getLoggerLattice)("default");
25229
26654
  }
25230
26655
  var app = (0, import_fastify.default)({
25231
26656
  logger: false,
@@ -25322,7 +26747,7 @@ app.setErrorHandler((error, request, reply) => {
25322
26747
  });
25323
26748
  function getConfiguredSandboxProvider() {
25324
26749
  const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
25325
- return (0, import_core70.createSandboxProvider)({
26750
+ return (0, import_core79.createSandboxProvider)({
25326
26751
  type: sandboxProviderType,
25327
26752
  remoteBaseURL: process.env.SANDBOX_BASE_URL,
25328
26753
  microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
@@ -25338,7 +26763,7 @@ function getConfiguredSandboxProvider() {
25338
26763
  }
25339
26764
  async function restoreMcpConnections() {
25340
26765
  try {
25341
- const storeLattice = (0, import_core70.getStoreLattice)("default", "mcp");
26766
+ const storeLattice = (0, import_core79.getStoreLattice)("default", "mcp");
25342
26767
  const store2 = storeLattice.store;
25343
26768
  if (!store2) {
25344
26769
  logger6.info("MCP store not configured, skipping connection restoration");
@@ -25352,15 +26777,15 @@ async function restoreMcpConnections() {
25352
26777
  for (const config of connectedConfigs) {
25353
26778
  try {
25354
26779
  const connection = convertToConnection2(config.config);
25355
- import_core70.mcpManager.addServer(config.key, connection);
26780
+ import_core79.mcpManager.addServer(config.key, connection);
25356
26781
  } catch (err) {
25357
26782
  logger6.warn(`Failed to prepare MCP server "${config.key}" for restoration`, { error: err });
25358
26783
  }
25359
26784
  }
25360
- await import_core70.mcpManager.connect();
26785
+ await import_core79.mcpManager.connect();
25361
26786
  for (const config of connectedConfigs) {
25362
26787
  try {
25363
- await import_core70.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
26788
+ await import_core79.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
25364
26789
  logger6.info(`MCP server "${config.key}" restored with ${config.selectedTools.length} tools`);
25365
26790
  } catch (err) {
25366
26791
  logger6.warn(`Failed to register tools for MCP server "${config.key}"`, { error: err });
@@ -25388,9 +26813,9 @@ var start = async (config) => {
25388
26813
  let projectRoomRealtimeRuntime;
25389
26814
  if (projectRoomRealtimeMode === "single-gateway") {
25390
26815
  projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
25391
- tasks: (0, import_core70.getStoreLattice)("default", "task").store,
25392
- rooms: (0, import_core70.getStoreLattice)("default", "projectRoom").store,
25393
- memberships: (0, import_core70.getStoreLattice)("default", "projectBotMembership").store,
26816
+ tasks: (0, import_core79.getStoreLattice)("default", "task").store,
26817
+ rooms: (0, import_core79.getStoreLattice)("default", "projectRoom").store,
26818
+ memberships: (0, import_core79.getStoreLattice)("default", "projectBotMembership").store,
25394
26819
  log: { warn: (message, context) => logger6.warn(message, context) }
25395
26820
  });
25396
26821
  }
@@ -25404,7 +26829,7 @@ var start = async (config) => {
25404
26829
  const infrastructure = createChannelInfrastructure({
25405
26830
  adapterRegistry,
25406
26831
  getStore: ((kind) => getStore11("default", kind).store),
25407
- trustedDispatcher: (0, import_core70.createTrustedAgentMessageDispatcher)((scope) => import_core70.agentInstanceManager.getAgent(scope)),
26832
+ trustedDispatcher: (0, import_core79.createTrustedAgentMessageDispatcher)((scope) => import_core79.agentInstanceManager.getAgent(scope)),
25408
26833
  middlewares: [
25409
26834
  createDeduplicationMiddleware(),
25410
26835
  createRateLimitMiddleware(),
@@ -25415,7 +26840,7 @@ var start = async (config) => {
25415
26840
  });
25416
26841
  const bindingStore = infrastructure.bindingRegistry;
25417
26842
  const installationStore = infrastructure.installationStore;
25418
- (0, import_core69.setBindingRegistry)(bindingStore);
26843
+ (0, import_core78.setBindingRegistry)(bindingStore);
25419
26844
  const router = infrastructure.router;
25420
26845
  app.addHook("onClose", async () => router.dispose());
25421
26846
  channelDeps = {
@@ -25431,10 +26856,10 @@ var start = async (config) => {
25431
26856
  error: err instanceof Error ? err.message : String(err)
25432
26857
  });
25433
26858
  }
25434
- (0, import_core69.setEvalRunService)(evalRunner);
26859
+ (0, import_core78.setEvalRunService)(evalRunner);
25435
26860
  try {
25436
- const menuStore = (0, import_core70.getStoreLattice)("default", "menu").store;
25437
- (0, import_core69.setMenuRegistry)(menuStore);
26861
+ const menuStore = (0, import_core79.getStoreLattice)("default", "menu").store;
26862
+ (0, import_core78.setMenuRegistry)(menuStore);
25438
26863
  logger6.info("Menu registry initialized");
25439
26864
  } catch {
25440
26865
  }
@@ -25451,10 +26876,10 @@ var start = async (config) => {
25451
26876
  const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await Promise.resolve().then(() => (init_A2AAuthService(), A2AAuthService_exports));
25452
26877
  const { registerA2AStandardRoutes: registerA2AStandardRoutes2 } = await Promise.resolve().then(() => (init_a2a_standard(), a2a_standard_exports));
25453
26878
  const a2a = await Promise.resolve().then(() => (init_a2a(), a2a_exports));
25454
- const a2aKeyStore = (0, import_core70.getStoreLattice)("default", "a2aApiKey").store;
25455
- const assistantStore = (0, import_core70.getStoreLattice)("default", "assistant").store;
25456
- const projectStore = (0, import_core70.getStoreLattice)("default", "project").store;
25457
- const taskStore = (0, import_core70.getStoreLattice)("default", "task").store;
26879
+ const a2aKeyStore = (0, import_core79.getStoreLattice)("default", "a2aApiKey").store;
26880
+ const assistantStore = (0, import_core79.getStoreLattice)("default", "assistant").store;
26881
+ const projectStore = (0, import_core79.getStoreLattice)("default", "project").store;
26882
+ const taskStore = (0, import_core79.getStoreLattice)("default", "task").store;
25458
26883
  const authService = new A2AAuthService2({
25459
26884
  keyStore: a2aKeyStore,
25460
26885
  assistantStore,
@@ -25480,24 +26905,54 @@ var start = async (config) => {
25480
26905
  }
25481
26906
  try {
25482
26907
  const { registerOpenRoutes: registerOpenRoutes2 } = await Promise.resolve().then(() => (init_open(), open_exports));
26908
+ const { registerOpenSessionRoutes: registerOpenSessionRoutes2 } = await Promise.resolve().then(() => (init_open_session(), open_session_exports));
26909
+ const { registerOpenCatalogRoutes: registerOpenCatalogRoutes2 } = await Promise.resolve().then(() => (init_open_catalog(), open_catalog_exports));
26910
+ const { registerOpenFilesRoutes: registerOpenFilesRoutes2 } = await Promise.resolve().then(() => (init_open_files(), open_files_exports));
26911
+ const { registerOpenAuditRoutes: registerOpenAuditRoutes2 } = await Promise.resolve().then(() => (init_open_audit(), open_audit_exports));
26912
+ const { registerOpenExposureRoutes: registerOpenExposureRoutes2 } = await Promise.resolve().then(() => (init_open_exposure(), open_exposure_exports));
26913
+ const { openLattice: openLattice2 } = await Promise.resolve().then(() => (init_openLattice(), openLattice_exports));
26914
+ const { createKbProvider: createKbProvider2 } = await Promise.resolve().then(() => (init_kbProvider(), kbProvider_exports));
26915
+ const { createOpenSandboxProvider: createOpenSandboxProvider2 } = await Promise.resolve().then(() => (init_sandboxProvider(), sandboxProvider_exports));
26916
+ const { createAgentProvider: createAgentProvider2 } = await Promise.resolve().then(() => (init_agentProvider(), agentProvider_exports));
26917
+ const { registerExposedPluginTools: registerExposedPluginTools2 } = await Promise.resolve().then(() => (init_registerExposedPlugins(), registerExposedPlugins_exports));
26918
+ const { CollectionService: CollectionService2 } = await Promise.resolve().then(() => (init_collection_service(), collection_service_exports));
26919
+ openLattice2.register(createKbProvider2(new CollectionService2()));
26920
+ openLattice2.register(createOpenSandboxProvider2());
26921
+ const threadStore = (0, import_core79.getStoreLattice)("default", "thread").store;
26922
+ const assistantStore = (0, import_core79.getStoreLattice)("default", "assistant").store;
26923
+ openLattice2.register(
26924
+ createAgentProvider2({
26925
+ threadStore,
26926
+ resolveAssistant: async (tenantId, assistantId) => {
26927
+ const assistant = await assistantStore.getAssistantById(tenantId, assistantId);
26928
+ return assistant ? { name: assistant.name, description: assistant.description } : null;
26929
+ }
26930
+ })
26931
+ );
26932
+ await registerExposedPluginTools2();
25483
26933
  registerOpenRoutes2(app);
25484
- logger6.info("Open MCP spike routes registered");
26934
+ registerOpenSessionRoutes2(app);
26935
+ registerOpenCatalogRoutes2(app);
26936
+ registerOpenFilesRoutes2(app);
26937
+ registerOpenAuditRoutes2(app);
26938
+ registerOpenExposureRoutes2(app);
26939
+ logger6.info("Open API routes registered (kb + sandbox providers, session exchange)");
25485
26940
  } catch (err) {
25486
- logger6.warn("Open MCP spike routes unavailable", {
26941
+ logger6.warn("Open API routes unavailable", {
25487
26942
  error: err instanceof Error ? err.message : String(err)
25488
26943
  });
25489
26944
  }
25490
- if (!import_core70.sandboxLatticeManager.hasLattice("default")) {
25491
- import_core70.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
26945
+ if (!import_core79.sandboxLatticeManager.hasLattice("default")) {
26946
+ import_core79.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
25492
26947
  logger6.info("Registered sandbox manager from env configuration");
25493
26948
  }
25494
26949
  try {
25495
26950
  const { ResourceController: ResourceController2 } = await Promise.resolve().then(() => (init_resources(), resources_exports));
25496
- const sharedResourceStore = (0, import_core70.getStoreLattice)("default", "sharedResource").store;
25497
- const cache = new import_core70.TokenCache();
26951
+ const sharedResourceStore = (0, import_core79.getStoreLattice)("default", "sharedResource").store;
26952
+ const cache = new import_core79.TokenCache();
25498
26953
  const resourceController = new ResourceController2({
25499
26954
  store: sharedResourceStore,
25500
- sandboxManager: import_core70.sandboxLatticeManager,
26955
+ sandboxManager: import_core79.sandboxLatticeManager,
25501
26956
  cache,
25502
26957
  logger: logger6
25503
26958
  });
@@ -25536,7 +26991,7 @@ var start = async (config) => {
25536
26991
  }
25537
26992
  }
25538
26993
  if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
25539
- import_core70.agentInstanceManager.restore().then((stats) => {
26994
+ import_core79.agentInstanceManager.restore().then((stats) => {
25540
26995
  logger6.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
25541
26996
  }).catch((error) => {
25542
26997
  logger6.error("Agent recovery failed", { error });