@axiom-lattice/gateway 2.1.38 → 2.1.39

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.
package/dist/index.js CHANGED
@@ -44,6 +44,14 @@ var import_messages = require("@langchain/core/messages");
44
44
  var import_langgraph = require("@langchain/langgraph");
45
45
  var import_uuid = require("uuid");
46
46
  var import_core = require("@axiom-lattice/core");
47
+ async function checkAgentExists(tenant_id, assistant_id) {
48
+ try {
49
+ const agentLattice = import_core.agentLatticeManager.getAgentLatticeWithTenant(tenant_id, assistant_id);
50
+ return agentLattice !== void 0;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
47
55
  function getOrCreateChunkBuffer() {
48
56
  if (!(0, import_core.hasChunkBuffer)("default")) {
49
57
  const buffer = new import_core.InMemoryChunkBuffer({
@@ -67,7 +75,7 @@ async function agent_invoke({
67
75
  run_id,
68
76
  custom_run_config
69
77
  }) {
70
- const agentLattice = (0, import_core.getAgentLattice)(assistant_id);
78
+ const agentLattice = import_core.agentLatticeManager.getAgentLatticeWithTenant(tenant_id, assistant_id);
71
79
  const runnable_agent = agentLattice?.client;
72
80
  const { message, ...rest } = input;
73
81
  const humanMessage = new import_messages.HumanMessage(message || "");
@@ -77,6 +85,7 @@ async function agent_invoke({
77
85
  }
78
86
  const runConfig = {
79
87
  ...agentLattice?.config?.runConfig || {},
88
+ tenantId: tenant_id,
80
89
  workspaceId: workspace_id,
81
90
  projectId: project_id,
82
91
  ...custom_run_config || {},
@@ -119,8 +128,8 @@ async function agent_stream({
119
128
  run_id,
120
129
  custom_run_config
121
130
  }) {
122
- const runnable_agent = (0, import_core.getAgentClient)(assistant_id);
123
- const agentLattice = (0, import_core.getAgentLattice)(assistant_id);
131
+ const runnable_agent = await (0, import_core.getAgentClient)(tenant_id, assistant_id);
132
+ const agentLattice = import_core.agentLatticeManager.getAgentLatticeWithTenant(tenant_id, assistant_id);
124
133
  const { message, ...rest } = input;
125
134
  let messages = [];
126
135
  if (!command) {
@@ -130,6 +139,7 @@ async function agent_stream({
130
139
  const chunkBuffer = getOrCreateChunkBuffer();
131
140
  const runConfig = {
132
141
  ...agentLattice?.config?.runConfig || {},
142
+ tenantId: tenant_id,
133
143
  workspaceId: workspace_id,
134
144
  projectId: project_id,
135
145
  ...custom_run_config || {},
@@ -208,10 +218,11 @@ async function agent_stream({
208
218
  }
209
219
  }
210
220
  async function agent_state({
221
+ assistant_id,
211
222
  thread_id,
212
- assistant_id
223
+ tenant_id
213
224
  }) {
214
- const runnable_agent = (0, import_core.getAgentClient)(assistant_id);
225
+ const runnable_agent = await (0, import_core.getAgentClient)(tenant_id, assistant_id);
215
226
  if (!runnable_agent) {
216
227
  throw new Error(`Agent ${assistant_id} not found`);
217
228
  }
@@ -225,7 +236,7 @@ async function agent_messages({
225
236
  tenant_id,
226
237
  assistant_id
227
238
  }) {
228
- const runnable_agent = (0, import_core.getAgentClient)(assistant_id);
239
+ const runnable_agent = await (0, import_core.getAgentClient)(tenant_id, assistant_id);
229
240
  if (!runnable_agent) {
230
241
  throw new Error(`Agent ${assistant_id} not found`);
231
242
  }
@@ -246,8 +257,8 @@ async function agent_messages({
246
257
  const new_messages = messagesArray;
247
258
  return new_messages;
248
259
  }
249
- async function draw_graph(assistant_id) {
250
- const runnable_agent = (0, import_core.getAgentClient)(assistant_id);
260
+ async function draw_graph(assistant_id, tenant_id) {
261
+ const runnable_agent = await (0, import_core.getAgentClient)(tenant_id, assistant_id);
251
262
  if (!runnable_agent) {
252
263
  throw new Error(`Agent ${assistant_id} not found`);
253
264
  }
@@ -285,9 +296,18 @@ async function resume_stream({
285
296
  var import_core2 = require("@axiom-lattice/core");
286
297
  var import_crypto = require("crypto");
287
298
  var import_core3 = require("@axiom-lattice/core");
299
+ function getTenantId(request) {
300
+ const userTenantId = request.user?.tenantId;
301
+ if (userTenantId) {
302
+ return userTenantId;
303
+ }
304
+ return request.headers["x-tenant-id"] || "default";
305
+ }
288
306
  function convertAgentConfigToAssistant(config) {
289
307
  return {
290
308
  id: config.key,
309
+ tenantId: "default",
310
+ // Code-configured agents belong to default tenant
291
311
  name: config.name,
292
312
  description: config.description,
293
313
  graphDefinition: config,
@@ -299,13 +319,14 @@ function convertAgentConfigToAssistant(config) {
299
319
  };
300
320
  }
301
321
  async function getAssistantList(request, reply) {
302
- const agentConfigs = await (0, import_core3.getAllAgentConfigs)();
322
+ const tenantId = getTenantId(request);
323
+ const agentConfigs = await import_core3.agentLatticeManager.getAllAgentConfigsByTenant(tenantId);
303
324
  const codeConfiguredAssistants = agentConfigs.map(
304
325
  convertAgentConfigToAssistant
305
326
  );
306
327
  const storeLattice = (0, import_core2.getStoreLattice)("default", "assistant");
307
328
  const assistantStore = storeLattice.store;
308
- const storedAssistants = await assistantStore.getAllAssistants();
329
+ const storedAssistants = await assistantStore.getAllAssistants(tenantId);
309
330
  const assistantMap = /* @__PURE__ */ new Map();
310
331
  codeConfiguredAssistants.forEach((assistant) => {
311
332
  assistantMap.set(assistant.id, assistant);
@@ -325,12 +346,12 @@ async function getAssistantList(request, reply) {
325
346
  }
326
347
  async function getAssistant(request, reply) {
327
348
  const { id } = request.params;
349
+ const tenantId = getTenantId(request);
328
350
  const storeLattice = (0, import_core2.getStoreLattice)("default", "assistant");
329
351
  const assistantStore = storeLattice.store;
330
- let assistant = await assistantStore.getAssistantById(id);
352
+ let assistant = await assistantStore.getAssistantById(tenantId, id);
331
353
  if (!assistant) {
332
- const agentConfigs = await (0, import_core3.getAllAgentConfigs)();
333
- const agentConfig = agentConfigs.find((config) => config.key === id);
354
+ const agentConfig = await import_core3.agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
334
355
  if (agentConfig) {
335
356
  assistant = convertAgentConfigToAssistant(agentConfig);
336
357
  }
@@ -347,20 +368,20 @@ async function getAssistant(request, reply) {
347
368
  data: assistant
348
369
  };
349
370
  }
350
- async function upsertAssistant(id, data, reply, requireFields = false) {
371
+ async function upsertAssistant(tenantId, id, data, reply, requireFields = false) {
351
372
  const storeLattice = (0, import_core2.getStoreLattice)("default", "assistant");
352
373
  const assistantStore = storeLattice.store;
353
- const exists = await assistantStore.hasAssistant(id);
374
+ const exists = await assistantStore.hasAssistant(tenantId, id);
354
375
  let assistant;
355
376
  if (exists) {
356
- assistant = await assistantStore.updateAssistant(id, data);
377
+ assistant = await assistantStore.updateAssistant(tenantId, id, data);
357
378
  if (!assistant) {
358
379
  return reply.status(500).send({
359
380
  success: false,
360
381
  message: "Failed to update assistant"
361
382
  });
362
383
  }
363
- import_core3.eventBus.publish("assistant:updated", { id: assistant.id, name: assistant.name });
384
+ import_core3.eventBus.publish("assistant:updated", { id: assistant.id, name: assistant.name, tenantId });
364
385
  return {
365
386
  success: true,
366
387
  message: "Updated assistant",
@@ -376,8 +397,8 @@ async function upsertAssistant(id, data, reply, requireFields = false) {
376
397
  });
377
398
  }
378
399
  }
379
- assistant = await assistantStore.createAssistant(id, data);
380
- import_core3.eventBus.publish("assistant:created", { id: assistant.id, name: assistant.name });
400
+ assistant = await assistantStore.createAssistant(tenantId, id, data);
401
+ import_core3.eventBus.publish("assistant:created", { id: assistant.id, name: assistant.name, tenantId });
381
402
  return reply.status(201).send({
382
403
  success: true,
383
404
  message: "Created assistant",
@@ -385,6 +406,7 @@ async function upsertAssistant(id, data, reply, requireFields = false) {
385
406
  });
386
407
  }
387
408
  async function createAssistant(request, reply) {
409
+ const tenantId = getTenantId(request);
388
410
  const data = request.body;
389
411
  if (!data.name || !data.graphDefinition) {
390
412
  return reply.status(400).send({
@@ -393,49 +415,51 @@ async function createAssistant(request, reply) {
393
415
  });
394
416
  }
395
417
  const id = data.id ?? (0, import_crypto.randomUUID)();
396
- return upsertAssistant(id, data, reply, true);
418
+ return upsertAssistant(tenantId, id, data, reply, true);
397
419
  }
398
420
  async function updateAssistant(request, reply) {
421
+ const tenantId = getTenantId(request);
399
422
  const { id } = request.params;
400
423
  const updates = request.body;
401
- return upsertAssistant(id, updates, reply, false);
424
+ return upsertAssistant(tenantId, id, updates, reply, false);
402
425
  }
403
426
  async function deleteAssistant(request, reply) {
427
+ const tenantId = getTenantId(request);
404
428
  const { id } = request.params;
405
429
  const storeLattice = (0, import_core2.getStoreLattice)("default", "assistant");
406
430
  const assistantStore = storeLattice.store;
407
- const agentConfigs = await (0, import_core3.getAllAgentConfigs)();
408
- const isCodeConfigured = agentConfigs.some((config) => config.key === id);
431
+ const agentConfig = await import_core3.agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
432
+ const isCodeConfigured = !!agentConfig;
409
433
  if (isCodeConfigured) {
410
- const exists2 = await assistantStore.hasAssistant(id);
434
+ const exists2 = await assistantStore.hasAssistant(tenantId, id);
411
435
  if (!exists2) {
412
436
  return reply.status(404).send({
413
437
  success: false,
414
438
  message: "Assistant not found (code-configured assistants cannot be deleted from code, only from store)"
415
439
  });
416
440
  }
417
- await assistantStore.deleteAssistant(id);
418
- import_core3.eventBus.publish("assistant:deleted", { id });
441
+ await assistantStore.deleteAssistant(tenantId, id);
442
+ import_core3.eventBus.publish("assistant:deleted", { id, tenantId });
419
443
  return {
420
444
  success: true,
421
445
  message: "Deleted assistant from store (code-configured registration remains)"
422
446
  };
423
447
  }
424
- const exists = await assistantStore.hasAssistant(id);
448
+ const exists = await assistantStore.hasAssistant(tenantId, id);
425
449
  if (!exists) {
426
450
  return reply.status(404).send({
427
451
  success: false,
428
452
  message: "Assistant not found"
429
453
  });
430
454
  }
431
- const deleted = await assistantStore.deleteAssistant(id);
455
+ const deleted = await assistantStore.deleteAssistant(tenantId, id);
432
456
  if (!deleted) {
433
457
  return reply.status(500).send({
434
458
  success: false,
435
459
  message: "Failed to delete assistant"
436
460
  });
437
461
  }
438
- import_core3.eventBus.publish("assistant:deleted", { id });
462
+ import_core3.eventBus.publish("assistant:deleted", { id, tenantId });
439
463
  return {
440
464
  success: true,
441
465
  message: "Successfully deleted assistant"
@@ -444,7 +468,8 @@ async function deleteAssistant(request, reply) {
444
468
  var getAgentGraph = async (request, reply) => {
445
469
  try {
446
470
  const { assistantId } = request.params;
447
- const imageData = await draw_graph(assistantId);
471
+ const tenant_id = getTenantId(request);
472
+ const imageData = await draw_graph(assistantId, tenant_id);
448
473
  reply.header("Content-Type", "application/json").send({
449
474
  image: imageData
450
475
  });
@@ -481,6 +506,14 @@ var createRun = async (request, reply) => {
481
506
  return;
482
507
  }
483
508
  if (streaming) {
509
+ const agentExists = await checkAgentExists(tenant_id, assistant_id);
510
+ if (!agentExists) {
511
+ reply.status(404).send({
512
+ success: false,
513
+ error: `Agent ${assistant_id} not found for tenant ${tenant_id}`
514
+ });
515
+ return;
516
+ }
484
517
  const stream = await agent_stream({
485
518
  assistant_id,
486
519
  input,
@@ -500,13 +533,27 @@ var createRun = async (request, reply) => {
500
533
  "Access-Control-Allow-Origin": "*"
501
534
  });
502
535
  try {
536
+ let chunkCount = 0;
503
537
  for await (const chunk of stream) {
504
- reply.raw.write(`data: ${JSON.stringify(chunk)}
538
+ chunkCount++;
539
+ const success = reply.raw.write(`data: ${JSON.stringify(chunk)}
505
540
 
506
541
  `);
542
+ if (!success) {
543
+ await new Promise((resolve) => reply.raw.once("drain", resolve));
544
+ }
507
545
  }
508
546
  } catch (error) {
509
- console.error("Stream processing error:", error);
547
+ const errorEvent = {
548
+ type: "error",
549
+ data: {
550
+ id: (0, import_uuid2.v4)(),
551
+ content: error.message || "Stream processing error"
552
+ }
553
+ };
554
+ reply.raw.write(`data: ${JSON.stringify(errorEvent)}
555
+
556
+ `);
510
557
  } finally {
511
558
  reply.raw.end();
512
559
  }
@@ -561,7 +608,16 @@ var resumeStream = async (request, reply) => {
561
608
  `);
562
609
  }
563
610
  } catch (error) {
564
- console.error("Resume stream processing error:", error);
611
+ const errorEvent = {
612
+ type: "error",
613
+ data: {
614
+ id: (0, import_uuid2.v4)(),
615
+ content: error.message || "Resume stream processing error"
616
+ }
617
+ };
618
+ reply.raw.write(`data: ${JSON.stringify(errorEvent)}
619
+
620
+ `);
565
621
  } finally {
566
622
  reply.raw.end();
567
623
  }
@@ -672,9 +728,11 @@ var getAgentState = async (request, reply) => {
672
728
  });
673
729
  return;
674
730
  }
731
+ const tenant_id = request.headers["x-tenant-id"];
675
732
  const result = await agent_state({
676
733
  assistant_id: assistantId,
677
- thread_id
734
+ thread_id,
735
+ tenant_id
678
736
  });
679
737
  if (!result) {
680
738
  reply.status(500).send(result);
@@ -774,11 +832,19 @@ var triggerAgentTask = async (request, reply) => {
774
832
  // src/controllers/threads.ts
775
833
  var import_core5 = require("@axiom-lattice/core");
776
834
  var import_crypto2 = require("crypto");
835
+ function getTenantId2(request) {
836
+ const userTenantId = request.user?.tenantId;
837
+ if (userTenantId) {
838
+ return userTenantId;
839
+ }
840
+ return request.headers["x-tenant-id"] || "default";
841
+ }
777
842
  async function getThreadList(request, reply) {
843
+ const tenantId = getTenantId2(request);
778
844
  const { assistantId } = request.params;
779
845
  const storeLattice = (0, import_core5.getStoreLattice)("default", "thread");
780
846
  const threadStore = storeLattice.store;
781
- const threads = await threadStore.getThreadsByAssistantId(assistantId);
847
+ const threads = await threadStore.getThreadsByAssistantId(tenantId, assistantId);
782
848
  return {
783
849
  success: true,
784
850
  message: "Successfully retrieved thread list",
@@ -789,10 +855,11 @@ async function getThreadList(request, reply) {
789
855
  };
790
856
  }
791
857
  async function getThread(request, reply) {
792
- const { assistantId, threadId } = request.params;
858
+ const tenantId = getTenantId2(request);
859
+ const { threadId } = request.params;
793
860
  const storeLattice = (0, import_core5.getStoreLattice)("default", "thread");
794
861
  const threadStore = storeLattice.store;
795
- const thread = await threadStore.getThreadById(assistantId, threadId);
862
+ const thread = await threadStore.getThreadById(tenantId, threadId);
796
863
  if (!thread) {
797
864
  return reply.status(404).send({
798
865
  success: false,
@@ -806,12 +873,13 @@ async function getThread(request, reply) {
806
873
  };
807
874
  }
808
875
  async function createThread(request, reply) {
876
+ const tenantId = getTenantId2(request);
809
877
  const { assistantId } = request.params;
810
878
  const data = request.body;
811
879
  const threadId = (0, import_crypto2.randomUUID)();
812
880
  const storeLattice = (0, import_core5.getStoreLattice)("default", "thread");
813
881
  const threadStore = storeLattice.store;
814
- const newThread = await threadStore.createThread(assistantId, threadId, data);
882
+ const newThread = await threadStore.createThread(tenantId, assistantId, threadId, data);
815
883
  return reply.status(201).send({
816
884
  success: true,
817
885
  message: "Successfully created thread",
@@ -819,11 +887,12 @@ async function createThread(request, reply) {
819
887
  });
820
888
  }
821
889
  async function updateThread(request, reply) {
822
- const { assistantId, threadId } = request.params;
890
+ const tenantId = getTenantId2(request);
891
+ const { threadId } = request.params;
823
892
  const updates = request.body;
824
893
  const storeLattice = (0, import_core5.getStoreLattice)("default", "thread");
825
894
  const threadStore = storeLattice.store;
826
- const exists = await threadStore.hasThread(assistantId, threadId);
895
+ const exists = await threadStore.hasThread(tenantId, threadId);
827
896
  if (!exists) {
828
897
  return reply.status(404).send({
829
898
  success: false,
@@ -831,7 +900,7 @@ async function updateThread(request, reply) {
831
900
  });
832
901
  }
833
902
  const updatedThread = await threadStore.updateThread(
834
- assistantId,
903
+ tenantId,
835
904
  threadId,
836
905
  updates
837
906
  );
@@ -848,17 +917,18 @@ async function updateThread(request, reply) {
848
917
  };
849
918
  }
850
919
  async function deleteThread(request, reply) {
851
- const { assistantId, threadId } = request.params;
920
+ const tenantId = getTenantId2(request);
921
+ const { threadId } = request.params;
852
922
  const storeLattice = (0, import_core5.getStoreLattice)("default", "thread");
853
923
  const threadStore = storeLattice.store;
854
- const exists = await threadStore.hasThread(assistantId, threadId);
924
+ const exists = await threadStore.hasThread(tenantId, threadId);
855
925
  if (!exists) {
856
926
  return reply.status(404).send({
857
927
  success: false,
858
928
  message: "Thread not found"
859
929
  });
860
930
  }
861
- const deleted = await threadStore.deleteThread(assistantId, threadId);
931
+ const deleted = await threadStore.deleteThread(tenantId, threadId);
862
932
  if (!deleted) {
863
933
  return reply.status(500).send({
864
934
  success: false,
@@ -873,6 +943,13 @@ async function deleteThread(request, reply) {
873
943
 
874
944
  // src/controllers/schedules.ts
875
945
  var import_core6 = require("@axiom-lattice/core");
946
+ function getTenantId3(request) {
947
+ const userTenantId = request.user?.tenantId;
948
+ if (userTenantId) {
949
+ return userTenantId;
950
+ }
951
+ return request.headers["x-tenant-id"] || "default";
952
+ }
876
953
  function getScheduleLattice() {
877
954
  const keys = import_core6.scheduleLatticeManager.getLatticeKeys();
878
955
  if (keys.length === 0) {
@@ -909,7 +986,9 @@ async function getThreadSchedules(request, reply) {
909
986
  }
910
987
  };
911
988
  }
989
+ const tenantId = getTenantId3(request);
912
990
  const filters = {
991
+ tenantId,
913
992
  threadId,
914
993
  assistantId
915
994
  };
@@ -924,6 +1003,7 @@ async function getThreadSchedules(request, reply) {
924
1003
  }
925
1004
  const tasks = await storage.getAllTasks(filters);
926
1005
  const total = await storage.countTasks({
1006
+ tenantId,
927
1007
  threadId,
928
1008
  assistantId,
929
1009
  status
@@ -1382,6 +1462,13 @@ async function getHealth(request, reply) {
1382
1462
  // src/controllers/skills.ts
1383
1463
  var import_core9 = require("@axiom-lattice/core");
1384
1464
  var import_core10 = require("@axiom-lattice/core");
1465
+ function getTenantId4(request) {
1466
+ const userTenantId = request.user?.tenantId;
1467
+ if (userTenantId) {
1468
+ return userTenantId;
1469
+ }
1470
+ return request.headers["x-tenant-id"] || "default";
1471
+ }
1385
1472
  function serializeSkill(skill) {
1386
1473
  const serialized = {
1387
1474
  id: skill.id,
@@ -1404,9 +1491,10 @@ function serializeSkill(skill) {
1404
1491
  }
1405
1492
  async function getSkillList(request, reply) {
1406
1493
  try {
1494
+ const tenantId = getTenantId4(request);
1407
1495
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1408
1496
  const skillStore = storeLattice.store;
1409
- const skills = await skillStore.getAllSkills();
1497
+ const skills = await skillStore.getAllSkills(tenantId);
1410
1498
  const serializedSkills = skills.map(serializeSkill);
1411
1499
  return {
1412
1500
  success: true,
@@ -1429,10 +1517,11 @@ async function getSkillList(request, reply) {
1429
1517
  }
1430
1518
  async function getSkill(request, reply) {
1431
1519
  try {
1520
+ const tenantId = getTenantId4(request);
1432
1521
  const { id } = request.params;
1433
1522
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1434
1523
  const skillStore = storeLattice.store;
1435
- const skill = await skillStore.getSkillById(id);
1524
+ const skill = await skillStore.getSkillById(tenantId, id);
1436
1525
  if (!skill) {
1437
1526
  return reply.status(404).send({
1438
1527
  success: false,
@@ -1481,16 +1570,17 @@ async function createSkill(request, reply) {
1481
1570
  message: `id "${id}" must equal name "${data.name}" (name is used for path addressing)`
1482
1571
  });
1483
1572
  }
1573
+ const tenantId = getTenantId4(request);
1484
1574
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1485
1575
  const skillStore = storeLattice.store;
1486
- const exists = await skillStore.hasSkill(id);
1576
+ const exists = await skillStore.hasSkill(tenantId, id);
1487
1577
  if (exists) {
1488
1578
  return reply.status(409).send({
1489
1579
  success: false,
1490
1580
  message: `Skill with id "${id}" already exists`
1491
1581
  });
1492
1582
  }
1493
- const newSkill = await skillStore.createSkill(id, data);
1583
+ const newSkill = await skillStore.createSkill(tenantId, id, data);
1494
1584
  return reply.status(201).send({
1495
1585
  success: true,
1496
1586
  message: "Successfully created skill",
@@ -1517,16 +1607,17 @@ async function updateSkill(request, reply) {
1517
1607
  });
1518
1608
  }
1519
1609
  }
1610
+ const tenantId = getTenantId4(request);
1520
1611
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1521
1612
  const skillStore = storeLattice.store;
1522
- const exists = await skillStore.hasSkill(id);
1613
+ const exists = await skillStore.hasSkill(tenantId, id);
1523
1614
  if (!exists) {
1524
1615
  return reply.status(404).send({
1525
1616
  success: false,
1526
1617
  message: "Skill not found"
1527
1618
  });
1528
1619
  }
1529
- const updatedSkill = await skillStore.updateSkill(id, updates);
1620
+ const updatedSkill = await skillStore.updateSkill(tenantId, id, updates);
1530
1621
  if (!updatedSkill) {
1531
1622
  return reply.status(500).send({
1532
1623
  success: false,
@@ -1548,16 +1639,17 @@ async function updateSkill(request, reply) {
1548
1639
  async function deleteSkill(request, reply) {
1549
1640
  try {
1550
1641
  const { id } = request.params;
1642
+ const tenantId = getTenantId4(request);
1551
1643
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1552
1644
  const skillStore = storeLattice.store;
1553
- const exists = await skillStore.hasSkill(id);
1645
+ const exists = await skillStore.hasSkill(tenantId, id);
1554
1646
  if (!exists) {
1555
1647
  return reply.status(404).send({
1556
1648
  success: false,
1557
1649
  message: "Skill not found"
1558
1650
  });
1559
1651
  }
1560
- const deleted = await skillStore.deleteSkill(id);
1652
+ const deleted = await skillStore.deleteSkill(tenantId, id);
1561
1653
  if (!deleted) {
1562
1654
  return reply.status(500).send({
1563
1655
  success: false,
@@ -1588,9 +1680,10 @@ async function searchSkillsByMetadata(request, reply) {
1588
1680
  }
1589
1681
  });
1590
1682
  }
1683
+ const tenantId = getTenantId4(request);
1591
1684
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1592
1685
  const skillStore = storeLattice.store;
1593
- const skills = await skillStore.searchByMetadata(key, value);
1686
+ const skills = await skillStore.searchByMetadata(tenantId, key, value);
1594
1687
  const serializedSkills = skills.map(serializeSkill);
1595
1688
  return {
1596
1689
  success: true,
@@ -1624,9 +1717,10 @@ async function filterSkillsByCompatibility(request, reply) {
1624
1717
  }
1625
1718
  });
1626
1719
  }
1720
+ const tenantId = getTenantId4(request);
1627
1721
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1628
1722
  const skillStore = storeLattice.store;
1629
- const skills = await skillStore.filterByCompatibility(compatibility);
1723
+ const skills = await skillStore.filterByCompatibility(tenantId, compatibility);
1630
1724
  const serializedSkills = skills.map(serializeSkill);
1631
1725
  return {
1632
1726
  success: true,
@@ -1660,9 +1754,10 @@ async function filterSkillsByLicense(request, reply) {
1660
1754
  }
1661
1755
  });
1662
1756
  }
1757
+ const tenantId = getTenantId4(request);
1663
1758
  const storeLattice = (0, import_core9.getStoreLattice)("default", "skill");
1664
1759
  const skillStore = storeLattice.store;
1665
- const skills = await skillStore.filterByLicense(license);
1760
+ const skills = await skillStore.filterByLicense(tenantId, license);
1666
1761
  const serializedSkills = skills.map(serializeSkill);
1667
1762
  return {
1668
1763
  success: true,
@@ -2152,17 +2247,17 @@ var ERROR_HTML = `<!DOCTYPE html>
2152
2247
  </body>
2153
2248
  </html>`;
2154
2249
  var SandboxService = class {
2155
- getFilesystemIsolatedLevel(assistantId) {
2156
- const agentConfig = (0, import_core12.getAgentConfig)(assistantId);
2157
- if (!agentConfig) {
2250
+ getFilesystemIsolatedLevel(tenantId, assistantId) {
2251
+ const agentLattice = import_core12.agentLatticeManager.getAgentLatticeWithTenant(tenantId, assistantId);
2252
+ if (!agentLattice) {
2158
2253
  return null;
2159
2254
  }
2160
- const agentLattice = (0, import_core12.getAgentLattice)(assistantId);
2161
2255
  const filesystemConfig = agentLattice?.config?.middleware?.find((m) => m.type === "filesystem");
2162
2256
  if (!filesystemConfig) {
2163
2257
  return null;
2164
2258
  }
2165
- return filesystemConfig.config?.isolatedLevel || null;
2259
+ const config = filesystemConfig.config;
2260
+ return config?.isolatedLevel || null;
2166
2261
  }
2167
2262
  computeSandboxName(assistantId, threadId, isolatedLevel) {
2168
2263
  let sandboxName;
@@ -2181,7 +2276,7 @@ var SandboxService = class {
2181
2276
  return (0, import_core12.normalizeSandboxName)(sandboxName);
2182
2277
  }
2183
2278
  getTargetUrl(sandboxName) {
2184
- const sandboxManager = (0, import_core12.getSandBoxManager)("default");
2279
+ const sandboxManager = (0, import_core12.getSandBoxManager)();
2185
2280
  return `${sandboxManager.getBaseURL()}/sandbox/${sandboxName}`;
2186
2281
  }
2187
2282
  async getVncHtml(sandboxName) {
@@ -2258,7 +2353,8 @@ function registerSandboxProxyRoutes(app2) {
2258
2353
  async (request, reply) => {
2259
2354
  console.log("[Sandbox Upload] Route matched:", request.url);
2260
2355
  const { assistantId, threadId } = request.params;
2261
- const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(assistantId);
2356
+ const tenantId = request.headers["x-tenant-id"] || "default";
2357
+ const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(tenantId, assistantId);
2262
2358
  if (!isolatedLevel) {
2263
2359
  return reply.status(500).send({ error: "Assistant sandbox config not found" });
2264
2360
  }
@@ -2267,7 +2363,7 @@ function registerSandboxProxyRoutes(app2) {
2267
2363
  threadId,
2268
2364
  isolatedLevel
2269
2365
  );
2270
- const sandboxManager = (0, import_core13.getSandBoxManager)("default");
2366
+ const sandboxManager = (0, import_core13.getSandBoxManager)();
2271
2367
  const sandbox = await sandboxManager.createSandbox(sandboxName);
2272
2368
  try {
2273
2369
  const data = await request.file();
@@ -2301,10 +2397,11 @@ function registerSandboxProxyRoutes(app2) {
2301
2397
  async (request, reply) => {
2302
2398
  const { assistantId, threadId } = request.params;
2303
2399
  const { path: filePath } = request.query;
2400
+ const tenantId = request.headers["x-tenant-id"] || "default";
2304
2401
  if (!filePath || typeof filePath !== "string") {
2305
2402
  return reply.status(400).send({ error: "Query parameter 'path' is required" });
2306
2403
  }
2307
- const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(assistantId);
2404
+ const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(tenantId, assistantId);
2308
2405
  if (!isolatedLevel) {
2309
2406
  return reply.status(500).send({ error: "Assistant filesystem isolated level not found" });
2310
2407
  }
@@ -2313,7 +2410,7 @@ function registerSandboxProxyRoutes(app2) {
2313
2410
  threadId,
2314
2411
  isolatedLevel
2315
2412
  );
2316
- const sandboxManager = (0, import_core13.getSandBoxManager)("default");
2413
+ const sandboxManager = (0, import_core13.getSandBoxManager)();
2317
2414
  const sandbox = await sandboxManager.createSandbox(sandboxName);
2318
2415
  try {
2319
2416
  const resolvedPath = filePath.startsWith("/home/gem") ? filePath : `/home/gem/${filePath.replace(/^\//, "")}`;
@@ -2379,6 +2476,14 @@ var WorkspaceController = class {
2379
2476
  this.projectStore = (0, import_core14.getStoreLattice)("default", "project").store;
2380
2477
  }
2381
2478
  getTenantId(request) {
2479
+ const userTenantId = request.user?.tenantId;
2480
+ if (userTenantId) {
2481
+ return userTenantId;
2482
+ }
2483
+ const queryTenantId = request.query?.tenantId;
2484
+ if (queryTenantId) {
2485
+ return String(queryTenantId);
2486
+ }
2382
2487
  return request.headers["x-tenant-id"] || "default";
2383
2488
  }
2384
2489
  // ==================== Workspace CRUD ====================
@@ -2492,8 +2597,7 @@ var WorkspaceController = class {
2492
2597
  return { success: true };
2493
2598
  }
2494
2599
  // ==================== File Operations ====================
2495
- async getBackend(workspaceId, projectId) {
2496
- const tenantId = "default";
2600
+ async getBackend(tenantId, workspaceId, projectId) {
2497
2601
  const workspace = await this.workspaceStore.getWorkspaceById(
2498
2602
  tenantId,
2499
2603
  workspaceId
@@ -2502,18 +2606,24 @@ var WorkspaceController = class {
2502
2606
  throw new Error("Workspace not found");
2503
2607
  }
2504
2608
  if (workspace.storageType === "sandbox") {
2505
- const sandboxManager = (0, import_core16.getSandBoxManager)("default");
2609
+ const sandboxManager = (0, import_core16.getSandBoxManager)();
2506
2610
  const sandboxName = "global";
2507
2611
  const sandbox = await sandboxManager.createSandbox(sandboxName);
2508
- return { backend: new import_core15.SandboxFilesystem({
2509
- sandboxInstance: sandbox,
2510
- workingDirectory: `/workspaces/${workspaceId}/${projectId}`
2511
- }), workspace };
2612
+ return {
2613
+ backend: new import_core15.SandboxFilesystem({
2614
+ sandboxInstance: sandbox,
2615
+ workingDirectory: `/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`
2616
+ }),
2617
+ workspace
2618
+ };
2512
2619
  } else {
2513
- return { backend: new import_core15.FilesystemBackend({
2514
- rootDir: `/lattice_store/workspaces/${workspaceId}/${projectId}`,
2515
- virtualMode: true
2516
- }), workspace };
2620
+ return {
2621
+ backend: new import_core15.FilesystemBackend({
2622
+ rootDir: `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`,
2623
+ virtualMode: true
2624
+ }),
2625
+ workspace
2626
+ };
2517
2627
  }
2518
2628
  }
2519
2629
  getFilenameFromPath(filePath) {
@@ -2543,18 +2653,19 @@ var WorkspaceController = class {
2543
2653
  return mimeTypes[ext] || "application/octet-stream";
2544
2654
  }
2545
2655
  async downloadFile(request, reply) {
2656
+ const tenantId = this.getTenantId(request);
2546
2657
  const { workspaceId, projectId } = request.params;
2547
2658
  const filePath = request.query.path;
2548
2659
  if (!filePath) {
2549
2660
  return reply.status(400).send({ success: false, error: "Path is required" });
2550
2661
  }
2551
2662
  try {
2552
- const { workspace } = await this.getBackend(workspaceId, projectId);
2663
+ const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
2553
2664
  const resolvedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
2554
2665
  if (workspace.storageType === "sandbox") {
2555
- const sandboxManager = (0, import_core16.getSandBoxManager)("default");
2666
+ const sandboxManager = (0, import_core16.getSandBoxManager)();
2556
2667
  const sandbox = await sandboxManager.createSandbox("global");
2557
- const realPath = path.join("/home/gem/workspaces", workspaceId, projectId, resolvedPath);
2668
+ const realPath = path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId, resolvedPath);
2558
2669
  const filename2 = this.getFilenameFromPath(resolvedPath);
2559
2670
  const inferredContentType = this.getMimeType(filename2);
2560
2671
  const downloadResult = await sandbox.file.downloadFile({
@@ -2571,13 +2682,13 @@ var WorkspaceController = class {
2571
2682
  const webStream = body.stream();
2572
2683
  const nodeStream = import_stream2.Readable.fromWeb(webStream);
2573
2684
  const contentType2 = body.contentType ?? inferredContentType;
2574
- const contentDisposition2 = body.contentDisposition ?? `inline; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2685
+ const contentDisposition2 = body.contentDisposition ?? `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2575
2686
  return reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
2576
2687
  }
2577
2688
  const bodyUnknown = downloadResult.body;
2578
2689
  let buf;
2579
2690
  let contentType = inferredContentType;
2580
- let contentDisposition = `inline; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2691
+ let contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2581
2692
  if (bodyUnknown instanceof ArrayBuffer) {
2582
2693
  buf = Buffer.from(bodyUnknown);
2583
2694
  } else if (bodyUnknown instanceof Buffer) {
@@ -2595,28 +2706,96 @@ var WorkspaceController = class {
2595
2706
  }
2596
2707
  return reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
2597
2708
  }
2598
- const { backend } = await this.getBackend(workspaceId, projectId);
2709
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
2599
2710
  const content = await backend.read(resolvedPath, 0, Infinity);
2600
2711
  const filename = this.getFilenameFromPath(resolvedPath);
2601
2712
  const mimeType = this.getMimeType(filename);
2602
2713
  const buffer = Buffer.from(content, "utf-8");
2603
- return reply.status(200).type(mimeType).header("Content-Disposition", `inline; filename*=UTF-8''${encodeURIComponent(filename)}`).send(buffer);
2714
+ return reply.status(200).type(mimeType).header("Content-Disposition", `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`).send(buffer);
2604
2715
  } catch (error) {
2605
2716
  const message = error instanceof Error ? error.message : String(error);
2606
2717
  return reply.status(502).send({ success: false, error: `Download proxy error: ${message}` });
2607
2718
  }
2608
2719
  }
2720
+ /**
2721
+ * View a file inline in the browser (not download).
2722
+ * Returns file content with inline Content-Disposition for preview.
2723
+ */
2724
+ async viewFile(request, reply) {
2725
+ const tenantId = this.getTenantId(request);
2726
+ const { workspaceId, projectId } = request.params;
2727
+ const filePath = request.query.path;
2728
+ if (!filePath) {
2729
+ return reply.status(400).send({ success: false, error: "Path is required" });
2730
+ }
2731
+ try {
2732
+ const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
2733
+ const resolvedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
2734
+ if (workspace.storageType === "sandbox") {
2735
+ const sandboxManager = (0, import_core16.getSandBoxManager)();
2736
+ const sandbox = await sandboxManager.createSandbox("global");
2737
+ const realPath = path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId, resolvedPath);
2738
+ const filename2 = this.getFilenameFromPath(resolvedPath);
2739
+ const inferredContentType = this.getMimeType(filename2);
2740
+ const downloadResult = await sandbox.file.downloadFile({
2741
+ path: realPath
2742
+ });
2743
+ if (!downloadResult.ok) {
2744
+ return reply.status(502).send({
2745
+ success: false,
2746
+ error: `View error: ${JSON.stringify(downloadResult.error)}`
2747
+ });
2748
+ }
2749
+ const body = downloadResult.body;
2750
+ if (typeof body?.stream === "function") {
2751
+ const webStream = body.stream();
2752
+ const nodeStream = import_stream2.Readable.fromWeb(webStream);
2753
+ const contentType2 = body.contentType ?? inferredContentType;
2754
+ return reply.status(200).type(contentType2).header("Content-Disposition", "inline").send(nodeStream);
2755
+ }
2756
+ const bodyUnknown = downloadResult.body;
2757
+ let buf;
2758
+ let contentType = inferredContentType;
2759
+ if (bodyUnknown instanceof ArrayBuffer) {
2760
+ buf = Buffer.from(bodyUnknown);
2761
+ } else if (bodyUnknown instanceof Buffer) {
2762
+ buf = bodyUnknown;
2763
+ } else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
2764
+ const res = bodyUnknown;
2765
+ buf = Buffer.from(await res.arrayBuffer());
2766
+ if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
2767
+ } else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
2768
+ const blob = await bodyUnknown.blob();
2769
+ buf = Buffer.from(await blob.arrayBuffer());
2770
+ } else {
2771
+ return reply.status(502).send({ success: false, error: "Unexpected view response format" });
2772
+ }
2773
+ return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(buf);
2774
+ }
2775
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
2776
+ const content = await backend.read(resolvedPath, 0, Infinity);
2777
+ const filename = this.getFilenameFromPath(resolvedPath);
2778
+ const mimeType = this.getMimeType(filename);
2779
+ const buffer = Buffer.from(content, "utf-8");
2780
+ return reply.status(200).type(mimeType).header("Content-Disposition", "inline").send(buffer);
2781
+ } catch (error) {
2782
+ const message = error instanceof Error ? error.message : String(error);
2783
+ return reply.status(502).send({ success: false, error: `View proxy error: ${message}` });
2784
+ }
2785
+ }
2609
2786
  async listPath(request) {
2787
+ const tenantId = this.getTenantId(request);
2610
2788
  const { workspaceId, projectId } = request.params;
2611
2789
  const path2 = request.query.path || "/";
2612
- const { backend } = await this.getBackend(workspaceId, projectId);
2790
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
2613
2791
  const files = await backend.lsInfo(path2);
2614
2792
  return { success: true, data: files };
2615
2793
  }
2616
2794
  async readFile(request) {
2795
+ const tenantId = this.getTenantId(request);
2617
2796
  const { workspaceId, projectId } = request.params;
2618
2797
  const { path: path2, offset = 0, limit = 1e3 } = request.query;
2619
- const { backend } = await this.getBackend(workspaceId, projectId);
2798
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
2620
2799
  const content = await backend.read(path2, Number(offset), Number(limit));
2621
2800
  return { success: true, data: { content, offset, limit } };
2622
2801
  }
@@ -2653,10 +2832,10 @@ var WorkspaceController = class {
2653
2832
  return reply.status(400).send({ success: false, error: "Invalid path parameter" });
2654
2833
  }
2655
2834
  if (workspace.storageType === "sandbox") {
2656
- const sandboxManager = (0, import_core16.getSandBoxManager)("default");
2835
+ const sandboxManager = (0, import_core16.getSandBoxManager)();
2657
2836
  const sandboxName = "global";
2658
2837
  const sandbox = await sandboxManager.createSandbox(sandboxName);
2659
- const baseDir = path.join("/home/gem/workspaces", workspaceId, projectId);
2838
+ const baseDir = path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId);
2660
2839
  const realPath = pathValue ? path.join(baseDir, pathValue, filename) : path.join(baseDir, filename);
2661
2840
  const uploadResult = await sandbox.file.uploadFile({
2662
2841
  file: buffer,
@@ -2668,7 +2847,7 @@ var WorkspaceController = class {
2668
2847
  error: `Upload error: ${JSON.stringify(uploadResult.error)}`
2669
2848
  });
2670
2849
  }
2671
- const relativePath = uploadResult.body?.data?.file_path?.replace(path.join("/home/gem/workspaces", workspaceId, projectId), "") || (pathValue ? `/${pathValue}/${filename}` : `/${filename}`);
2850
+ const relativePath = uploadResult.body?.data?.file_path?.replace(path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId), "") || (pathValue ? `/${pathValue}/${filename}` : `/${filename}`);
2672
2851
  const result2 = {
2673
2852
  path: relativePath,
2674
2853
  name: filename,
@@ -2678,7 +2857,7 @@ var WorkspaceController = class {
2678
2857
  };
2679
2858
  return reply.status(200).send({ success: true, data: result2 });
2680
2859
  }
2681
- const rootDir = `/lattice_store/workspaces/${workspaceId}/${projectId}`;
2860
+ const rootDir = `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
2682
2861
  const targetDir = pathValue ? path.join(rootDir, pathValue) : rootDir;
2683
2862
  const targetPath = path.join(targetDir, filename);
2684
2863
  await fs.mkdir(path.dirname(targetPath), { recursive: true });
@@ -2752,16 +2931,24 @@ function registerWorkspaceRoutes(app2) {
2752
2931
  "/api/workspaces/:workspaceId/projects/:projectId/downloadfile",
2753
2932
  controller.downloadFile.bind(controller)
2754
2933
  );
2934
+ app2.get(
2935
+ "/api/workspaces/:workspaceId/projects/:projectId/viewfile",
2936
+ controller.viewFile.bind(controller)
2937
+ );
2755
2938
  }
2756
2939
 
2757
2940
  // src/controllers/database-configs.ts
2758
2941
  var import_core17 = require("@axiom-lattice/core");
2759
2942
  var import_crypto3 = require("crypto");
2760
- function getTenantId(request) {
2943
+ function getTenantId5(request) {
2944
+ const userTenantId = request.user?.tenantId;
2945
+ if (userTenantId) {
2946
+ return userTenantId;
2947
+ }
2761
2948
  return request.headers["x-tenant-id"] || "default";
2762
2949
  }
2763
2950
  async function getDatabaseConfigList(request, reply) {
2764
- const tenantId = getTenantId(request);
2951
+ const tenantId = getTenantId5(request);
2765
2952
  try {
2766
2953
  const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
2767
2954
  const store = storeLattice.store;
@@ -2791,7 +2978,7 @@ async function getDatabaseConfigList(request, reply) {
2791
2978
  }
2792
2979
  }
2793
2980
  async function getDatabaseConfig(request, reply) {
2794
- const tenantId = getTenantId(request);
2981
+ const tenantId = getTenantId5(request);
2795
2982
  const { key } = request.params;
2796
2983
  try {
2797
2984
  const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
@@ -2817,7 +3004,7 @@ async function getDatabaseConfig(request, reply) {
2817
3004
  }
2818
3005
  }
2819
3006
  async function createDatabaseConfig(request, reply) {
2820
- const tenantId = getTenantId(request);
3007
+ const tenantId = getTenantId5(request);
2821
3008
  const body = request.body;
2822
3009
  try {
2823
3010
  const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
@@ -2833,7 +3020,7 @@ async function createDatabaseConfig(request, reply) {
2833
3020
  const id = body.id || (0, import_crypto3.randomUUID)();
2834
3021
  const config = await store.createConfig(tenantId, id, body);
2835
3022
  try {
2836
- import_core17.sqlDatabaseManager.registerDatabase(config.key, config.config);
3023
+ import_core17.sqlDatabaseManager.registerDatabase(tenantId, config.key, config.config);
2837
3024
  } catch (error) {
2838
3025
  console.warn("Failed to auto-register database:", error);
2839
3026
  }
@@ -2852,7 +3039,7 @@ async function createDatabaseConfig(request, reply) {
2852
3039
  }
2853
3040
  }
2854
3041
  async function updateDatabaseConfig(request, reply) {
2855
- const tenantId = getTenantId(request);
3042
+ const tenantId = getTenantId5(request);
2856
3043
  const { key } = request.params;
2857
3044
  const updates = request.body;
2858
3045
  try {
@@ -2875,7 +3062,7 @@ async function updateDatabaseConfig(request, reply) {
2875
3062
  }
2876
3063
  if (updates.config) {
2877
3064
  try {
2878
- import_core17.sqlDatabaseManager.registerDatabase(updated.key, updated.config);
3065
+ import_core17.sqlDatabaseManager.registerDatabase(tenantId, updated.key, updated.config);
2879
3066
  } catch (error) {
2880
3067
  console.warn("Failed to re-register database:", error);
2881
3068
  }
@@ -2894,7 +3081,7 @@ async function updateDatabaseConfig(request, reply) {
2894
3081
  }
2895
3082
  }
2896
3083
  async function deleteDatabaseConfig(request, reply) {
2897
- const tenantId = getTenantId(request);
3084
+ const tenantId = getTenantId5(request);
2898
3085
  const { keyOrId } = request.params;
2899
3086
  try {
2900
3087
  const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
@@ -2924,8 +3111,8 @@ async function deleteDatabaseConfig(request, reply) {
2924
3111
  };
2925
3112
  }
2926
3113
  try {
2927
- if (import_core17.sqlDatabaseManager.hasDatabase(configKey)) {
2928
- await import_core17.sqlDatabaseManager.removeDatabase(configKey);
3114
+ if (import_core17.sqlDatabaseManager.hasDatabase(tenantId, configKey)) {
3115
+ await import_core17.sqlDatabaseManager.removeDatabase(tenantId, configKey);
2929
3116
  }
2930
3117
  } catch (error) {
2931
3118
  console.warn("Failed to remove from SqlDatabaseManager:", error);
@@ -2943,7 +3130,7 @@ async function deleteDatabaseConfig(request, reply) {
2943
3130
  }
2944
3131
  }
2945
3132
  async function testDatabaseConnection(request, reply) {
2946
- const tenantId = getTenantId(request);
3133
+ const tenantId = getTenantId5(request);
2947
3134
  const { key } = request.params;
2948
3135
  try {
2949
3136
  const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
@@ -2957,16 +3144,16 @@ async function testDatabaseConnection(request, reply) {
2957
3144
  };
2958
3145
  }
2959
3146
  const testKey = `__test_${key}_${Date.now()}`;
2960
- import_core17.sqlDatabaseManager.registerDatabase(testKey, config.config);
3147
+ import_core17.sqlDatabaseManager.registerDatabase(tenantId, testKey, config.config);
2961
3148
  const startTime = Date.now();
2962
- const db = import_core17.sqlDatabaseManager.getDatabase(testKey);
3149
+ const db = import_core17.sqlDatabaseManager.getDatabase(tenantId, testKey);
2963
3150
  try {
2964
3151
  await db.connect();
2965
3152
  await db.listTables();
2966
3153
  const latency = Date.now() - startTime;
2967
3154
  await new Promise((resolve) => setTimeout(resolve, 100));
2968
3155
  await db.disconnect();
2969
- await import_core17.sqlDatabaseManager.removeDatabase(testKey);
3156
+ await import_core17.sqlDatabaseManager.removeDatabase(tenantId, testKey);
2970
3157
  return {
2971
3158
  success: true,
2972
3159
  message: "Connection test successful",
@@ -2978,7 +3165,7 @@ async function testDatabaseConnection(request, reply) {
2978
3165
  } catch (error) {
2979
3166
  try {
2980
3167
  await db.disconnect();
2981
- await import_core17.sqlDatabaseManager.removeDatabase(testKey);
3168
+ await import_core17.sqlDatabaseManager.removeDatabase(tenantId, testKey);
2982
3169
  } catch {
2983
3170
  }
2984
3171
  return {
@@ -3032,11 +3219,15 @@ function registerDatabaseConfigRoutes(app2) {
3032
3219
  // src/controllers/metrics-configs.ts
3033
3220
  var import_core18 = require("@axiom-lattice/core");
3034
3221
  var import_crypto4 = require("crypto");
3035
- function getTenantId2(request) {
3222
+ function getTenantId6(request) {
3223
+ const userTenantId = request.user?.tenantId;
3224
+ if (userTenantId) {
3225
+ return userTenantId;
3226
+ }
3036
3227
  return request.headers["x-tenant-id"] || "default";
3037
3228
  }
3038
3229
  async function getMetricsServerConfigList(request, reply) {
3039
- const tenantId = getTenantId2(request);
3230
+ const tenantId = getTenantId6(request);
3040
3231
  try {
3041
3232
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
3042
3233
  const store = storeLattice.store;
@@ -3062,7 +3253,7 @@ async function getMetricsServerConfigList(request, reply) {
3062
3253
  }
3063
3254
  }
3064
3255
  async function getMetricsServerConfig(request, reply) {
3065
- const tenantId = getTenantId2(request);
3256
+ const tenantId = getTenantId6(request);
3066
3257
  const { key } = request.params;
3067
3258
  try {
3068
3259
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3088,7 +3279,7 @@ async function getMetricsServerConfig(request, reply) {
3088
3279
  }
3089
3280
  }
3090
3281
  async function createMetricsServerConfig(request, reply) {
3091
- const tenantId = getTenantId2(request);
3282
+ const tenantId = getTenantId6(request);
3092
3283
  const body = request.body;
3093
3284
  try {
3094
3285
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3120,7 +3311,7 @@ async function createMetricsServerConfig(request, reply) {
3120
3311
  };
3121
3312
  const config = await store.createConfig(tenantId, id, configData);
3122
3313
  try {
3123
- import_core18.metricsServerManager.registerServer(config.key, config.config);
3314
+ import_core18.metricsServerManager.registerServer(tenantId, config.key, config.config);
3124
3315
  } catch (error) {
3125
3316
  console.warn("Failed to auto-register metrics server:", error);
3126
3317
  }
@@ -3139,7 +3330,7 @@ async function createMetricsServerConfig(request, reply) {
3139
3330
  }
3140
3331
  }
3141
3332
  async function updateMetricsServerConfig(request, reply) {
3142
- const tenantId = getTenantId2(request);
3333
+ const tenantId = getTenantId6(request);
3143
3334
  const { key } = request.params;
3144
3335
  const updates = request.body;
3145
3336
  try {
@@ -3171,7 +3362,7 @@ async function updateMetricsServerConfig(request, reply) {
3171
3362
  }
3172
3363
  if (updates.config) {
3173
3364
  try {
3174
- import_core18.metricsServerManager.registerServer(updated.key, updated.config);
3365
+ import_core18.metricsServerManager.registerServer(tenantId, updated.key, updated.config);
3175
3366
  } catch (error) {
3176
3367
  console.warn("Failed to re-register metrics server:", error);
3177
3368
  }
@@ -3190,7 +3381,7 @@ async function updateMetricsServerConfig(request, reply) {
3190
3381
  }
3191
3382
  }
3192
3383
  async function deleteMetricsServerConfig(request, reply) {
3193
- const tenantId = getTenantId2(request);
3384
+ const tenantId = getTenantId6(request);
3194
3385
  const { keyOrId } = request.params;
3195
3386
  try {
3196
3387
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3218,8 +3409,8 @@ async function deleteMetricsServerConfig(request, reply) {
3218
3409
  };
3219
3410
  }
3220
3411
  try {
3221
- if (import_core18.metricsServerManager.hasServer(configKey)) {
3222
- import_core18.metricsServerManager.removeServer(configKey);
3412
+ if (import_core18.metricsServerManager.hasServer(tenantId, configKey)) {
3413
+ import_core18.metricsServerManager.removeServer(tenantId, configKey);
3223
3414
  }
3224
3415
  } catch (error) {
3225
3416
  console.warn("Failed to remove from MetricsServerManager:", error);
@@ -3237,7 +3428,7 @@ async function deleteMetricsServerConfig(request, reply) {
3237
3428
  }
3238
3429
  }
3239
3430
  async function testMetricsServerConnection(request, reply) {
3240
- const tenantId = getTenantId2(request);
3431
+ const tenantId = getTenantId6(request);
3241
3432
  const { key } = request.params;
3242
3433
  try {
3243
3434
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3251,11 +3442,11 @@ async function testMetricsServerConnection(request, reply) {
3251
3442
  };
3252
3443
  }
3253
3444
  const testKey = `__test_${key}_${Date.now()}`;
3254
- import_core18.metricsServerManager.registerServer(testKey, config.config);
3445
+ import_core18.metricsServerManager.registerServer(tenantId, testKey, config.config);
3255
3446
  try {
3256
- const client = import_core18.metricsServerManager.getClient(testKey);
3447
+ const client = import_core18.metricsServerManager.getClient(tenantId, testKey);
3257
3448
  const result = await client.testConnection();
3258
- import_core18.metricsServerManager.removeServer(testKey);
3449
+ import_core18.metricsServerManager.removeServer(tenantId, testKey);
3259
3450
  return {
3260
3451
  success: true,
3261
3452
  message: result.connected ? "Connection test successful" : "Connection test failed",
@@ -3263,7 +3454,7 @@ async function testMetricsServerConnection(request, reply) {
3263
3454
  };
3264
3455
  } catch (error) {
3265
3456
  try {
3266
- import_core18.metricsServerManager.removeServer(testKey);
3457
+ import_core18.metricsServerManager.removeServer(tenantId, testKey);
3267
3458
  } catch {
3268
3459
  }
3269
3460
  return {
@@ -3288,7 +3479,7 @@ async function testMetricsServerConnection(request, reply) {
3288
3479
  }
3289
3480
  }
3290
3481
  async function listAvailableMetrics(request, reply) {
3291
- const tenantId = getTenantId2(request);
3482
+ const tenantId = getTenantId6(request);
3292
3483
  const { key } = request.params;
3293
3484
  try {
3294
3485
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3301,10 +3492,10 @@ async function listAvailableMetrics(request, reply) {
3301
3492
  message: "Metrics server configuration not found"
3302
3493
  };
3303
3494
  }
3304
- if (!import_core18.metricsServerManager.hasServer(key)) {
3305
- import_core18.metricsServerManager.registerServer(key, config.config);
3495
+ if (!import_core18.metricsServerManager.hasServer(tenantId, key)) {
3496
+ import_core18.metricsServerManager.registerServer(tenantId, key, config.config);
3306
3497
  }
3307
- const client = import_core18.metricsServerManager.getClient(key);
3498
+ const client = import_core18.metricsServerManager.getClient(tenantId, key);
3308
3499
  const metrics = await client.listMetrics();
3309
3500
  return {
3310
3501
  success: true,
@@ -3326,7 +3517,7 @@ async function listAvailableMetrics(request, reply) {
3326
3517
  }
3327
3518
  }
3328
3519
  async function queryMetricsData(request, reply) {
3329
- const tenantId = getTenantId2(request);
3520
+ const tenantId = getTenantId6(request);
3330
3521
  const { key } = request.params;
3331
3522
  const { metricName, startTime, endTime, step, labels } = request.body;
3332
3523
  try {
@@ -3347,10 +3538,10 @@ async function queryMetricsData(request, reply) {
3347
3538
  message: "metricName is required"
3348
3539
  };
3349
3540
  }
3350
- if (!import_core18.metricsServerManager.hasServer(key)) {
3351
- import_core18.metricsServerManager.registerServer(key, config.config);
3541
+ if (!import_core18.metricsServerManager.hasServer(tenantId, key)) {
3542
+ import_core18.metricsServerManager.registerServer(tenantId, key, config.config);
3352
3543
  }
3353
- const client = import_core18.metricsServerManager.getClient(key);
3544
+ const client = import_core18.metricsServerManager.getClient(tenantId, key);
3354
3545
  const result = await client.queryMetricData(metricName, {
3355
3546
  startTime,
3356
3547
  endTime,
@@ -3374,7 +3565,7 @@ async function queryMetricsData(request, reply) {
3374
3565
  }
3375
3566
  }
3376
3567
  async function getDataSources(request, reply) {
3377
- const tenantId = getTenantId2(request);
3568
+ const tenantId = getTenantId6(request);
3378
3569
  const { key } = request.params;
3379
3570
  try {
3380
3571
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3415,7 +3606,7 @@ async function getDataSources(request, reply) {
3415
3606
  }
3416
3607
  }
3417
3608
  async function getDatasourceMetrics(request, reply) {
3418
- const tenantId = getTenantId2(request);
3609
+ const tenantId = getTenantId6(request);
3419
3610
  const { key, datasourceId } = request.params;
3420
3611
  try {
3421
3612
  const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
@@ -3452,7 +3643,7 @@ async function getDatasourceMetrics(request, reply) {
3452
3643
  }
3453
3644
  }
3454
3645
  async function querySemanticMetrics(request, reply) {
3455
- const tenantId = getTenantId2(request);
3646
+ const tenantId = getTenantId6(request);
3456
3647
  const { key } = request.params;
3457
3648
  const body = request.body;
3458
3649
  try {
@@ -3483,14 +3674,32 @@ async function querySemanticMetrics(request, reply) {
3483
3674
  const semanticConfig = config.config;
3484
3675
  const client = new import_core18.SemanticMetricsClient(semanticConfig);
3485
3676
  const result = await client.semanticQuery(body);
3677
+ const allDataPoints = [];
3678
+ const metricNames = [];
3679
+ for (const metricResult of result.results) {
3680
+ metricNames.push(metricResult.metricName);
3681
+ for (const row of metricResult.rows) {
3682
+ allDataPoints.push({
3683
+ timestamp: row.timestamp ? new Date(row.timestamp).getTime() : void 0,
3684
+ value: typeof row.value === "number" ? row.value : 0,
3685
+ metricName: metricResult.metricName,
3686
+ labels: Object.fromEntries(
3687
+ Object.entries(row).filter(([k]) => k !== "value" && k !== "timestamp").map(([k, v]) => [k, String(v)])
3688
+ )
3689
+ });
3690
+ }
3691
+ }
3486
3692
  return {
3487
3693
  success: true,
3488
3694
  message: "Semantic query executed successfully",
3489
3695
  data: {
3490
3696
  datasourceId: result.datasourceId,
3491
- metrics: result.metrics,
3492
- dataPoints: result.dataPoints,
3493
- metadata: result.metadata
3697
+ metrics: metricNames,
3698
+ dataPoints: allDataPoints,
3699
+ metadata: {
3700
+ rowCount: allDataPoints.length,
3701
+ queryTimeMs: result.totalExecutionTimeMs
3702
+ }
3494
3703
  }
3495
3704
  };
3496
3705
  } catch (error) {
@@ -3589,11 +3798,15 @@ function registerMetricsServerConfigRoutes(app2) {
3589
3798
  // src/controllers/mcp-configs.ts
3590
3799
  var import_core19 = require("@axiom-lattice/core");
3591
3800
  var import_crypto5 = require("crypto");
3592
- function getTenantId3(request) {
3801
+ function getTenantId7(request) {
3802
+ const userTenantId = request.user?.tenantId;
3803
+ if (userTenantId) {
3804
+ return userTenantId;
3805
+ }
3593
3806
  return request.headers["x-tenant-id"] || "default";
3594
3807
  }
3595
3808
  async function getMcpServerConfigList(request, reply) {
3596
- const tenantId = getTenantId3(request);
3809
+ const tenantId = getTenantId7(request);
3597
3810
  try {
3598
3811
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
3599
3812
  const store = storeLattice.store;
@@ -3619,7 +3832,7 @@ async function getMcpServerConfigList(request, reply) {
3619
3832
  }
3620
3833
  }
3621
3834
  async function getMcpServerConfig(request, reply) {
3622
- const tenantId = getTenantId3(request);
3835
+ const tenantId = getTenantId7(request);
3623
3836
  const { key } = request.params;
3624
3837
  try {
3625
3838
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -3645,7 +3858,7 @@ async function getMcpServerConfig(request, reply) {
3645
3858
  }
3646
3859
  }
3647
3860
  async function createMcpServerConfig(request, reply) {
3648
- const tenantId = getTenantId3(request);
3861
+ const tenantId = getTenantId7(request);
3649
3862
  const body = request.body;
3650
3863
  try {
3651
3864
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -3684,7 +3897,7 @@ async function createMcpServerConfig(request, reply) {
3684
3897
  }
3685
3898
  }
3686
3899
  async function updateMcpServerConfig(request, reply) {
3687
- const tenantId = getTenantId3(request);
3900
+ const tenantId = getTenantId7(request);
3688
3901
  const { key } = request.params;
3689
3902
  const updates = request.body;
3690
3903
  try {
@@ -3734,7 +3947,7 @@ async function updateMcpServerConfig(request, reply) {
3734
3947
  }
3735
3948
  }
3736
3949
  async function deleteMcpServerConfig(request, reply) {
3737
- const tenantId = getTenantId3(request);
3950
+ const tenantId = getTenantId7(request);
3738
3951
  const { keyOrId } = request.params;
3739
3952
  try {
3740
3953
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -3781,7 +3994,7 @@ async function deleteMcpServerConfig(request, reply) {
3781
3994
  }
3782
3995
  }
3783
3996
  async function testMcpServerConnection(request, reply) {
3784
- const tenantId = getTenantId3(request);
3997
+ const tenantId = getTenantId7(request);
3785
3998
  const { key } = request.params;
3786
3999
  try {
3787
4000
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -3834,7 +4047,7 @@ async function testMcpServerConnection(request, reply) {
3834
4047
  }
3835
4048
  }
3836
4049
  async function listMcpServerTools(request, reply) {
3837
- const tenantId = getTenantId3(request);
4050
+ const tenantId = getTenantId7(request);
3838
4051
  const { key } = request.params;
3839
4052
  try {
3840
4053
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -3867,7 +4080,7 @@ async function listMcpServerTools(request, reply) {
3867
4080
  }
3868
4081
  }
3869
4082
  async function connectMcpServer(request, reply) {
3870
- const tenantId = getTenantId3(request);
4083
+ const tenantId = getTenantId7(request);
3871
4084
  const { key } = request.params;
3872
4085
  try {
3873
4086
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -3904,7 +4117,7 @@ async function connectMcpServer(request, reply) {
3904
4117
  }
3905
4118
  }
3906
4119
  async function disconnectMcpServer(request, reply) {
3907
- const tenantId = getTenantId3(request);
4120
+ const tenantId = getTenantId7(request);
3908
4121
  const { key } = request.params;
3909
4122
  try {
3910
4123
  const storeLattice = (0, import_core19.getStoreLattice)("default", "mcp");
@@ -4747,7 +4960,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
4747
4960
  reader.releaseLock();
4748
4961
  }
4749
4962
  if (callback_event) {
4750
- const state = await agent_state({ assistant_id, thread_id });
4963
+ const state = await agent_state({ assistant_id, thread_id, tenant_id });
4751
4964
  import_core23.eventBus.publish(callback_event, {
4752
4965
  success: true,
4753
4966
  state,
@@ -4761,7 +4974,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
4761
4974
  } else {
4762
4975
  await response.text();
4763
4976
  if (callback_event) {
4764
- const state = await agent_state({ assistant_id, thread_id });
4977
+ const state = await agent_state({ assistant_id, thread_id, tenant_id });
4765
4978
  import_core23.eventBus.publish(callback_event, {
4766
4979
  success: true,
4767
4980
  state,
@@ -5075,6 +5288,13 @@ var start = async (config) => {
5075
5288
  logger = loggerLattice.client;
5076
5289
  }
5077
5290
  app.decorate("loggerLattice", loggerLattice);
5291
+ if (!import_core24.sandboxLatticeManager.hasLattice("default")) {
5292
+ const sandboxBaseURL = process.env.SANDBOX_BASE_URL || "http://localhost:8080";
5293
+ import_core24.sandboxLatticeManager.registerLattice("default", {
5294
+ baseURL: sandboxBaseURL
5295
+ });
5296
+ logger.info(`Registered sandbox manager with baseURL: ${sandboxBaseURL}`);
5297
+ }
5078
5298
  const target_port = config?.port || Number(process.env.PORT) || 4001;
5079
5299
  await app.listen({ port: target_port, host: "0.0.0.0" });
5080
5300
  logger.info(`Lattice Gateway is running on port: ${target_port}`);