@axiom-lattice/gateway 2.1.53 → 2.1.55

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 (42) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/CHANGELOG.md +19 -0
  3. package/dist/index.js +1043 -526
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +977 -449
  6. package/dist/index.mjs.map +1 -1
  7. package/jest.config.js +5 -0
  8. package/package.json +6 -5
  9. package/src/__tests__/__mocks__/e2b.ts +1 -0
  10. package/src/__tests__/channel-installations.test.ts +199 -0
  11. package/src/__tests__/sandbox-provider-registration.test.ts +74 -0
  12. package/src/__tests__/workspace.test.ts +119 -8
  13. package/src/channels/__tests__/routes.test.ts +71 -0
  14. package/src/channels/lark/README.md +187 -0
  15. package/src/channels/lark/__tests__/aggregator.test.ts +23 -0
  16. package/src/channels/lark/__tests__/controller.test.ts +270 -0
  17. package/src/channels/lark/__tests__/mapping-service.test.ts +118 -0
  18. package/src/channels/lark/__tests__/parser.test.ts +72 -0
  19. package/src/channels/lark/__tests__/sender.test.ts +37 -0
  20. package/src/channels/lark/__tests__/verification.test.ts +157 -0
  21. package/src/channels/lark/aggregator.ts +16 -0
  22. package/src/channels/lark/config.ts +44 -0
  23. package/src/channels/lark/controller.ts +189 -0
  24. package/src/channels/lark/mapping-service.ts +138 -0
  25. package/src/channels/lark/parser.ts +68 -0
  26. package/src/channels/lark/routes.ts +121 -0
  27. package/src/channels/lark/runner.ts +37 -0
  28. package/src/channels/lark/sender.ts +58 -0
  29. package/src/channels/lark/types.ts +33 -0
  30. package/src/channels/lark/verification.ts +67 -0
  31. package/src/channels/routes.ts +25 -0
  32. package/src/controllers/channel-installations.ts +354 -0
  33. package/src/controllers/sandbox.ts +30 -80
  34. package/src/controllers/skills.ts +71 -321
  35. package/src/controllers/threads.ts +8 -6
  36. package/src/controllers/workspace.ts +64 -179
  37. package/src/index.ts +28 -5
  38. package/src/routes/channel-installations.ts +33 -0
  39. package/src/routes/index.ts +6 -0
  40. package/src/schemas/index.ts +2 -2
  41. package/src/services/sandbox_service.ts +21 -21
  42. package/src/services/skill_service.ts +97 -0
package/dist/index.mjs CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/index.ts
2
9
  import fastify from "fastify";
3
10
  import cors from "@fastify/cors";
@@ -591,11 +598,13 @@ async function getThreadList(request, reply) {
591
598
  const tenantId = getTenantId2(request);
592
599
  const { assistantId } = request.params;
593
600
  const metadataFilter = {};
594
- if (request.query.workspaceId) {
595
- metadataFilter.workspaceId = request.query.workspaceId;
601
+ const workspaceId = request.headers["x-workspace-id"];
602
+ const projectId = request.headers["x-project-id"];
603
+ if (workspaceId) {
604
+ metadataFilter.workspaceId = workspaceId;
596
605
  }
597
- if (request.query.projectId) {
598
- metadataFilter.projectId = request.query.projectId;
606
+ if (projectId) {
607
+ metadataFilter.projectId = projectId;
599
608
  }
600
609
  const storeLattice = getStoreLattice2("default", "thread");
601
610
  const threadStore = storeLattice.store;
@@ -1239,16 +1248,63 @@ async function getHealth(request, reply) {
1239
1248
  }
1240
1249
  }
1241
1250
 
1242
- // src/controllers/skills.ts
1243
- import { getStoreLattice as getStoreLattice3 } from "@axiom-lattice/core";
1244
- import { validateSkillName } from "@axiom-lattice/core";
1251
+ // src/services/skill_service.ts
1252
+ import { SandboxSkillStore } from "@axiom-lattice/core";
1245
1253
  function getTenantId4(request) {
1246
- const userTenantId = request.user?.tenantId;
1247
- if (userTenantId) {
1248
- return userTenantId;
1249
- }
1250
- return request.headers["x-tenant-id"] || "default";
1254
+ return request.user?.tenantId || request.headers["x-tenant-id"] || "default";
1251
1255
  }
1256
+ function getWorkspaceId(request) {
1257
+ return request.headers["x-workspace-id"];
1258
+ }
1259
+ function getProjectId(request) {
1260
+ return request.headers["x-project-id"];
1261
+ }
1262
+ function buildContext(request) {
1263
+ return {
1264
+ workspaceId: getWorkspaceId(request),
1265
+ projectId: getProjectId(request)
1266
+ };
1267
+ }
1268
+ var SkillService = class {
1269
+ constructor() {
1270
+ this.store = new SandboxSkillStore();
1271
+ }
1272
+ async getAllSkills(request) {
1273
+ const tenantId = getTenantId4(request);
1274
+ return this.store.getAllSkills(tenantId, buildContext(request));
1275
+ }
1276
+ async getSkillById(request, id) {
1277
+ const tenantId = getTenantId4(request);
1278
+ return this.store.getSkillById(tenantId, id, buildContext(request));
1279
+ }
1280
+ async createSkill(request, id, data) {
1281
+ const tenantId = getTenantId4(request);
1282
+ return this.store.createSkill(tenantId, id, data, buildContext(request));
1283
+ }
1284
+ async updateSkill(request, id, updates) {
1285
+ const tenantId = getTenantId4(request);
1286
+ return this.store.updateSkill(tenantId, id, updates, buildContext(request));
1287
+ }
1288
+ async deleteSkill(request, id) {
1289
+ const tenantId = getTenantId4(request);
1290
+ return this.store.deleteSkill(tenantId, id, buildContext(request));
1291
+ }
1292
+ async searchByMetadata(request, key, value) {
1293
+ const tenantId = getTenantId4(request);
1294
+ return this.store.searchByMetadata(tenantId, key, value, buildContext(request));
1295
+ }
1296
+ async filterByCompatibility(request, compatibility) {
1297
+ const tenantId = getTenantId4(request);
1298
+ return this.store.filterByCompatibility(tenantId, compatibility, buildContext(request));
1299
+ }
1300
+ async filterByLicense(request, license) {
1301
+ const tenantId = getTenantId4(request);
1302
+ return this.store.filterByLicense(tenantId, license, buildContext(request));
1303
+ }
1304
+ };
1305
+
1306
+ // src/controllers/skills.ts
1307
+ var skillService = new SkillService();
1252
1308
  function serializeSkill(skill) {
1253
1309
  const serialized = {
1254
1310
  id: skill.id,
@@ -1259,192 +1315,100 @@ function serializeSkill(skill) {
1259
1315
  metadata: skill.metadata || {},
1260
1316
  content: skill.content,
1261
1317
  subSkills: skill.subSkills,
1262
- createdAt: skill.createdAt instanceof Date ? skill.createdAt.toISOString() : skill.createdAt ? new Date(skill.createdAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
1263
- updatedAt: skill.updatedAt instanceof Date ? skill.updatedAt.toISOString() : skill.updatedAt ? new Date(skill.updatedAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
1318
+ createdAt: skill.createdAt instanceof Date ? skill.createdAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
1319
+ updatedAt: skill.updatedAt instanceof Date ? skill.updatedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
1264
1320
  };
1265
1321
  Object.keys(serialized).forEach((key) => {
1266
- if (serialized[key] === void 0) {
1267
- delete serialized[key];
1268
- }
1322
+ if (serialized[key] === void 0) delete serialized[key];
1269
1323
  });
1270
1324
  return serialized;
1271
1325
  }
1272
1326
  async function getSkillList(request, reply) {
1273
1327
  try {
1274
- const tenantId = getTenantId4(request);
1275
- const storeLattice = getStoreLattice3("default", "skill");
1276
- const skillStore = storeLattice.store;
1277
- const skills = await skillStore.getAllSkills(tenantId);
1278
- const serializedSkills = skills.map(serializeSkill);
1328
+ const skills = await skillService.getAllSkills(request);
1279
1329
  return {
1280
1330
  success: true,
1281
1331
  message: "Successfully retrieved skill list",
1282
- data: {
1283
- records: serializedSkills,
1284
- total: serializedSkills.length
1285
- }
1332
+ data: { records: skills.map(serializeSkill), total: skills.length }
1286
1333
  };
1287
1334
  } catch (error) {
1288
1335
  return reply.status(500).send({
1289
1336
  success: false,
1290
1337
  message: `Failed to retrieve skills: ${error.message}`,
1291
- data: {
1292
- records: [],
1293
- total: 0
1294
- }
1338
+ data: { records: [], total: 0 }
1295
1339
  });
1296
1340
  }
1297
1341
  }
1298
1342
  async function getSkill(request, reply) {
1299
1343
  try {
1300
- const tenantId = getTenantId4(request);
1301
1344
  const { id } = request.params;
1302
- const storeLattice = getStoreLattice3("default", "skill");
1303
- const skillStore = storeLattice.store;
1304
- const skill = await skillStore.getSkillById(tenantId, id);
1345
+ const skill = await skillService.getSkillById(request, id);
1305
1346
  if (!skill) {
1306
- return reply.status(404).send({
1307
- success: false,
1308
- message: "Skill not found"
1309
- });
1347
+ return reply.status(404).send({ success: false, message: "Skill not found" });
1310
1348
  }
1311
- return {
1312
- success: true,
1313
- message: "Successfully retrieved skill",
1314
- data: serializeSkill(skill)
1315
- };
1349
+ return { success: true, message: "Successfully retrieved skill", data: serializeSkill(skill) };
1316
1350
  } catch (error) {
1317
- return reply.status(500).send({
1318
- success: false,
1319
- message: `Failed to retrieve skill: ${error.message}`
1320
- });
1351
+ return reply.status(500).send({ success: false, message: `Failed to retrieve skill: ${error.message}` });
1321
1352
  }
1322
1353
  }
1323
1354
  async function createSkill(request, reply) {
1324
1355
  try {
1325
1356
  const data = request.body;
1326
1357
  if (!data.name) {
1327
- return reply.status(400).send({
1328
- success: false,
1329
- message: "name is required"
1330
- });
1358
+ return reply.status(400).send({ success: false, message: "name is required" });
1331
1359
  }
1332
1360
  if (!data.description) {
1333
- return reply.status(400).send({
1334
- success: false,
1335
- message: "description is required"
1336
- });
1337
- }
1338
- try {
1339
- validateSkillName(data.name);
1340
- } catch (error) {
1341
- return reply.status(400).send({
1342
- success: false,
1343
- message: error.message || "Invalid skill name format"
1344
- });
1361
+ return reply.status(400).send({ success: false, message: "description is required" });
1345
1362
  }
1346
1363
  const id = request.body.id || data.name;
1347
1364
  if (id !== data.name) {
1348
1365
  return reply.status(400).send({
1349
1366
  success: false,
1350
- message: `id "${id}" must equal name "${data.name}" (name is used for path addressing)`
1367
+ message: `id "${id}" must equal name "${data.name}"`
1351
1368
  });
1352
1369
  }
1353
- const tenantId = getTenantId4(request);
1354
- const storeLattice = getStoreLattice3("default", "skill");
1355
- const skillStore = storeLattice.store;
1356
- const exists = await skillStore.hasSkill(tenantId, id);
1357
- if (exists) {
1358
- return reply.status(409).send({
1359
- success: false,
1360
- message: `Skill with id "${id}" already exists`
1361
- });
1362
- }
1363
- const newSkill = await skillStore.createSkill(tenantId, id, data);
1370
+ const skill = await skillService.createSkill(request, id, data);
1364
1371
  return reply.status(201).send({
1365
1372
  success: true,
1366
1373
  message: "Successfully created skill",
1367
- data: serializeSkill(newSkill)
1374
+ data: serializeSkill(skill)
1368
1375
  });
1369
1376
  } catch (error) {
1370
- return reply.status(500).send({
1371
- success: false,
1372
- message: `Failed to create skill: ${error.message}`
1373
- });
1377
+ if (error.message?.includes("already exists")) {
1378
+ return reply.status(409).send({ success: false, message: error.message });
1379
+ }
1380
+ if (error.message?.includes("must equal name") || error.message?.includes("Invalid skill name")) {
1381
+ return reply.status(400).send({ success: false, message: error.message });
1382
+ }
1383
+ return reply.status(500).send({ success: false, message: `Failed to create skill: ${error.message}` });
1374
1384
  }
1375
1385
  }
1376
1386
  async function updateSkill(request, reply) {
1377
1387
  try {
1378
1388
  const { id } = request.params;
1379
1389
  const updates = request.body;
1380
- if (updates.name !== void 0) {
1381
- try {
1382
- validateSkillName(updates.name);
1383
- } catch (error) {
1384
- return reply.status(400).send({
1385
- success: false,
1386
- message: error.message || "Invalid skill name format"
1387
- });
1388
- }
1389
- }
1390
- const tenantId = getTenantId4(request);
1391
- const storeLattice = getStoreLattice3("default", "skill");
1392
- const skillStore = storeLattice.store;
1393
- const exists = await skillStore.hasSkill(tenantId, id);
1394
- if (!exists) {
1395
- return reply.status(404).send({
1396
- success: false,
1397
- message: "Skill not found"
1398
- });
1399
- }
1400
- const updatedSkill = await skillStore.updateSkill(tenantId, id, updates);
1401
- if (!updatedSkill) {
1402
- return reply.status(500).send({
1403
- success: false,
1404
- message: "Failed to update skill"
1405
- });
1390
+ const skill = await skillService.updateSkill(request, id, updates);
1391
+ if (!skill) {
1392
+ return reply.status(404).send({ success: false, message: "Skill not found" });
1406
1393
  }
1407
- return {
1408
- success: true,
1409
- message: "Successfully updated skill",
1410
- data: serializeSkill(updatedSkill)
1411
- };
1394
+ return { success: true, message: "Successfully updated skill", data: serializeSkill(skill) };
1412
1395
  } catch (error) {
1413
- return reply.status(500).send({
1414
- success: false,
1415
- message: `Failed to update skill: ${error.message}`
1416
- });
1396
+ if (error.message?.includes("Invalid skill name")) {
1397
+ return reply.status(400).send({ success: false, message: error.message });
1398
+ }
1399
+ return reply.status(500).send({ success: false, message: `Failed to update skill: ${error.message}` });
1417
1400
  }
1418
1401
  }
1419
1402
  async function deleteSkill(request, reply) {
1420
1403
  try {
1421
1404
  const { id } = request.params;
1422
- const tenantId = getTenantId4(request);
1423
- const storeLattice = getStoreLattice3("default", "skill");
1424
- const skillStore = storeLattice.store;
1425
- const exists = await skillStore.hasSkill(tenantId, id);
1426
- if (!exists) {
1427
- return reply.status(404).send({
1428
- success: false,
1429
- message: "Skill not found"
1430
- });
1431
- }
1432
- const deleted = await skillStore.deleteSkill(tenantId, id);
1433
- if (!deleted) {
1434
- return reply.status(500).send({
1435
- success: false,
1436
- message: "Failed to delete skill"
1437
- });
1405
+ const result = await skillService.deleteSkill(request, id);
1406
+ if (!result) {
1407
+ return reply.status(404).send({ success: false, message: "Skill not found" });
1438
1408
  }
1439
- return {
1440
- success: true,
1441
- message: "Successfully deleted skill"
1442
- };
1409
+ return { success: true, message: "Successfully deleted skill" };
1443
1410
  } catch (error) {
1444
- return reply.status(500).send({
1445
- success: false,
1446
- message: `Failed to delete skill: ${error.message}`
1447
- });
1411
+ return reply.status(500).send({ success: false, message: `Failed to delete skill: ${error.message}` });
1448
1412
  }
1449
1413
  }
1450
1414
  async function searchSkillsByMetadata(request, reply) {
@@ -1454,33 +1418,20 @@ async function searchSkillsByMetadata(request, reply) {
1454
1418
  return reply.status(400).send({
1455
1419
  success: false,
1456
1420
  message: "key and value query parameters are required",
1457
- data: {
1458
- records: [],
1459
- total: 0
1460
- }
1421
+ data: { records: [], total: 0 }
1461
1422
  });
1462
1423
  }
1463
- const tenantId = getTenantId4(request);
1464
- const storeLattice = getStoreLattice3("default", "skill");
1465
- const skillStore = storeLattice.store;
1466
- const skills = await skillStore.searchByMetadata(tenantId, key, value);
1467
- const serializedSkills = skills.map(serializeSkill);
1424
+ const skills = await skillService.searchByMetadata(request, key, value);
1468
1425
  return {
1469
1426
  success: true,
1470
1427
  message: "Successfully searched skills",
1471
- data: {
1472
- records: serializedSkills,
1473
- total: serializedSkills.length
1474
- }
1428
+ data: { records: skills.map(serializeSkill), total: skills.length }
1475
1429
  };
1476
1430
  } catch (error) {
1477
1431
  return reply.status(500).send({
1478
1432
  success: false,
1479
1433
  message: `Failed to search skills: ${error.message}`,
1480
- data: {
1481
- records: [],
1482
- total: 0
1483
- }
1434
+ data: { records: [], total: 0 }
1484
1435
  });
1485
1436
  }
1486
1437
  }
@@ -1491,33 +1442,20 @@ async function filterSkillsByCompatibility(request, reply) {
1491
1442
  return reply.status(400).send({
1492
1443
  success: false,
1493
1444
  message: "compatibility query parameter is required",
1494
- data: {
1495
- records: [],
1496
- total: 0
1497
- }
1445
+ data: { records: [], total: 0 }
1498
1446
  });
1499
1447
  }
1500
- const tenantId = getTenantId4(request);
1501
- const storeLattice = getStoreLattice3("default", "skill");
1502
- const skillStore = storeLattice.store;
1503
- const skills = await skillStore.filterByCompatibility(tenantId, compatibility);
1504
- const serializedSkills = skills.map(serializeSkill);
1448
+ const skills = await skillService.filterByCompatibility(request, compatibility);
1505
1449
  return {
1506
1450
  success: true,
1507
1451
  message: "Successfully filtered skills",
1508
- data: {
1509
- records: serializedSkills,
1510
- total: serializedSkills.length
1511
- }
1452
+ data: { records: skills.map(serializeSkill), total: skills.length }
1512
1453
  };
1513
1454
  } catch (error) {
1514
1455
  return reply.status(500).send({
1515
1456
  success: false,
1516
1457
  message: `Failed to filter skills: ${error.message}`,
1517
- data: {
1518
- records: [],
1519
- total: 0
1520
- }
1458
+ data: { records: [], total: 0 }
1521
1459
  });
1522
1460
  }
1523
1461
  }
@@ -1528,33 +1466,20 @@ async function filterSkillsByLicense(request, reply) {
1528
1466
  return reply.status(400).send({
1529
1467
  success: false,
1530
1468
  message: "license query parameter is required",
1531
- data: {
1532
- records: [],
1533
- total: 0
1534
- }
1469
+ data: { records: [], total: 0 }
1535
1470
  });
1536
1471
  }
1537
- const tenantId = getTenantId4(request);
1538
- const storeLattice = getStoreLattice3("default", "skill");
1539
- const skillStore = storeLattice.store;
1540
- const skills = await skillStore.filterByLicense(tenantId, license);
1541
- const serializedSkills = skills.map(serializeSkill);
1472
+ const skills = await skillService.filterByLicense(request, license);
1542
1473
  return {
1543
1474
  success: true,
1544
1475
  message: "Successfully filtered skills",
1545
- data: {
1546
- records: serializedSkills,
1547
- total: serializedSkills.length
1548
- }
1476
+ data: { records: skills.map(serializeSkill), total: skills.length }
1549
1477
  };
1550
1478
  } catch (error) {
1551
1479
  return reply.status(500).send({
1552
1480
  success: false,
1553
1481
  message: `Failed to filter skills: ${error.message}`,
1554
- data: {
1555
- records: [],
1556
- total: 0
1557
- }
1482
+ data: { records: [], total: 0 }
1558
1483
  });
1559
1484
  }
1560
1485
  }
@@ -1643,7 +1568,7 @@ async function getToolConfigs(request, reply) {
1643
1568
 
1644
1569
  // src/controllers/data-query.ts
1645
1570
  import {
1646
- getStoreLattice as getStoreLattice4,
1571
+ getStoreLattice as getStoreLattice3,
1647
1572
  metricsServerManager
1648
1573
  } from "@axiom-lattice/core";
1649
1574
  function getTenantId5(request) {
@@ -1676,7 +1601,7 @@ async function executeDataQuery(request, reply) {
1676
1601
  message: "Cannot provide both metrics and customSql. Use one query type only."
1677
1602
  };
1678
1603
  }
1679
- const storeLattice = getStoreLattice4("default", "metrics");
1604
+ const storeLattice = getStoreLattice3("default", "metrics");
1680
1605
  const store = storeLattice.store;
1681
1606
  if (!body.serverKey) {
1682
1607
  reply.code(400);
@@ -2168,11 +2093,8 @@ async function removePendingMessageHandler(request, reply) {
2168
2093
  }
2169
2094
  }
2170
2095
 
2171
- // src/controllers/sandbox.ts
2172
- import { Readable } from "stream";
2173
-
2174
2096
  // src/services/sandbox_service.ts
2175
- import { agentLatticeManager as agentLatticeManager3, getSandBoxManager, normalizeSandboxName } from "@axiom-lattice/core";
2097
+ import { agentLatticeManager as agentLatticeManager3, normalizeSandboxName } from "@axiom-lattice/core";
2176
2098
  var ERROR_HTML = `<!DOCTYPE html>
2177
2099
  <html lang="zh-CN">
2178
2100
  <head>
@@ -2264,7 +2186,7 @@ var ERROR_HTML = `<!DOCTYPE html>
2264
2186
  </div>
2265
2187
  <div class="info-item">
2266
2188
  <span class="label">\u9694\u79BB\u7EA7\u522B</span>
2267
- <span class="value" id="isolatedLevel">-</span>
2189
+ <span class="value" id="vmIsolation">-</span>
2268
2190
  </div>
2269
2191
  <div class="info-item">
2270
2192
  <span class="label">\u9519\u8BEF\u4FE1\u606F</span>
@@ -2277,13 +2199,13 @@ var ERROR_HTML = `<!DOCTYPE html>
2277
2199
  const params = new URLSearchParams(window.location.search);
2278
2200
  document.getElementById('assistantId').textContent = params.get('assistantId') || '-';
2279
2201
  document.getElementById('threadId').textContent = params.get('threadId') || '-';
2280
- document.getElementById('isolatedLevel').textContent = params.get('isolatedLevel') || '-';
2202
+ document.getElementById('vmIsolation').textContent = params.get('vmIsolation') || '-';
2281
2203
  document.getElementById('errorMsg').textContent = params.get('error') || '\u672A\u77E5\u9519\u8BEF';
2282
2204
  </script>
2283
2205
  </body>
2284
2206
  </html>`;
2285
2207
  var SandboxService = class {
2286
- getFilesystemIsolatedLevel(tenantId, assistantId) {
2208
+ getFilesystemVmIsolation(tenantId, assistantId) {
2287
2209
  const agentLattice = agentLatticeManager3.getAgentLatticeWithTenant(tenantId, assistantId);
2288
2210
  if (!agentLattice) {
2289
2211
  return null;
@@ -2293,27 +2215,26 @@ var SandboxService = class {
2293
2215
  return null;
2294
2216
  }
2295
2217
  const config = filesystemConfig.config;
2296
- return config?.isolatedLevel || null;
2218
+ return config?.vmIsolation || null;
2297
2219
  }
2298
- computeSandboxName(assistantId, threadId, isolatedLevel) {
2299
- let sandboxName;
2300
- switch (isolatedLevel) {
2220
+ computeSandboxName(assistantId, threadId, vmIsolation, tenantId, workspaceId, projectId) {
2221
+ switch (vmIsolation) {
2301
2222
  case "agent":
2302
- sandboxName = assistantId;
2303
- break;
2304
- case "thread":
2305
- sandboxName = threadId;
2306
- break;
2223
+ return normalizeSandboxName(`${tenantId ?? "default"}-${assistantId}`);
2224
+ case "project":
2225
+ return normalizeSandboxName(
2226
+ `${tenantId ?? "default"}-${workspaceId ?? "default"}-${projectId ?? "default"}`
2227
+ );
2307
2228
  case "global":
2308
2229
  default:
2309
- sandboxName = "global";
2310
- break;
2230
+ return "global";
2311
2231
  }
2312
- return normalizeSandboxName(sandboxName);
2232
+ }
2233
+ getBrowserSandboxBaseURL() {
2234
+ return process.env.AGENT_INFRA_SANDBOX_BASE_URL || "http://localhost:8080";
2313
2235
  }
2314
2236
  getTargetUrl(sandboxName) {
2315
- const sandboxManager = getSandBoxManager();
2316
- return `${sandboxManager.getBaseURL()}/sandbox/${sandboxName}`;
2237
+ return `${this.getBrowserSandboxBaseURL()}/sandbox/${sandboxName}`;
2317
2238
  }
2318
2239
  async getVncHtml(sandboxName) {
2319
2240
  const response = await fetch(`${this.getTargetUrl(sandboxName)}/vnc/index.html`);
@@ -2349,9 +2270,9 @@ var SandboxService = class {
2349
2270
  );
2350
2271
  return rewritten;
2351
2272
  }
2352
- generateErrorHtml(assistantId, threadId, isolatedLevel, errorMessage) {
2273
+ generateErrorHtml(assistantId, threadId, vmIsolation, errorMessage) {
2353
2274
  const encodedError = encodeURIComponent(errorMessage);
2354
- return ERROR_HTML.replace("{assistantId}", assistantId).replace("{threadId}", threadId).replace("{isolatedLevel}", isolatedLevel).replace("{errorMessage}", errorMessage);
2275
+ return ERROR_HTML.replace("{assistantId}", assistantId).replace("{threadId}", threadId).replace("{vmIsolation}", vmIsolation).replace("{errorMessage}", errorMessage);
2355
2276
  }
2356
2277
  };
2357
2278
  var sandboxService = new SandboxService();
@@ -2390,14 +2311,14 @@ function registerSandboxProxyRoutes(app2) {
2390
2311
  console.log("[Sandbox Upload] Route matched:", request.url);
2391
2312
  const { assistantId, threadId } = request.params;
2392
2313
  const tenantId = request.headers["x-tenant-id"] || "default";
2393
- const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(tenantId, assistantId);
2394
- if (!isolatedLevel) {
2314
+ const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
2315
+ if (!vmIsolation) {
2395
2316
  return reply.status(500).send({ error: "Assistant sandbox config not found" });
2396
2317
  }
2397
2318
  const sandboxName = sandboxService.computeSandboxName(
2398
2319
  assistantId,
2399
2320
  threadId,
2400
- isolatedLevel
2321
+ vmIsolation
2401
2322
  );
2402
2323
  const sandboxManager = getSandBoxManager2();
2403
2324
  const sandbox = await sandboxManager.createSandbox(sandboxName);
@@ -2409,17 +2330,14 @@ function registerSandboxProxyRoutes(app2) {
2409
2330
  const buffer = await data.toBuffer();
2410
2331
  const pathEntry = data.fields?.path;
2411
2332
  const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
2412
- const formData = new FormData();
2413
- formData.append("file", new Blob([buffer]), data.filename ?? "file");
2414
- const path3 = `/home/gem/uploads/${pathValue ? pathValue : ""}${data.filename}`;
2415
- const uploadResult = await sandbox.file.uploadFile({
2416
- file: buffer,
2417
- path: path3
2418
- });
2419
- if (!uploadResult.ok) {
2420
- return reply.status(502).send({ error: `Upload error: ${uploadResult.error}` });
2333
+ const filePath = `~/uploads/${pathValue ? pathValue : ""}${data.filename}`;
2334
+ try {
2335
+ await sandbox.file.uploadFile({ file: filePath, data: buffer });
2336
+ } catch (err) {
2337
+ reply.status(500).send({ error: String(err) });
2338
+ return;
2421
2339
  }
2422
- const relativePath = uploadResult.body?.data?.file_path.replace(`/home/gem`, "");
2340
+ const relativePath = filePath.replace(`~/`, "");
2423
2341
  const result = { id: relativePath, name: data.filename, size: buffer.length };
2424
2342
  return reply.status(200).send({ message: "File uploaded successfully", ...result });
2425
2343
  } catch (error) {
@@ -2437,59 +2355,30 @@ function registerSandboxProxyRoutes(app2) {
2437
2355
  if (!filePath || typeof filePath !== "string") {
2438
2356
  return reply.status(400).send({ error: "Query parameter 'path' is required" });
2439
2357
  }
2440
- const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(tenantId, assistantId);
2441
- if (!isolatedLevel) {
2442
- return reply.status(500).send({ error: "Assistant filesystem isolated level not found" });
2358
+ const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
2359
+ if (!vmIsolation) {
2360
+ return reply.status(500).send({ error: "Assistant filesystem vmIsolation not found" });
2443
2361
  }
2444
2362
  const sandboxName = sandboxService.computeSandboxName(
2445
2363
  assistantId,
2446
2364
  threadId,
2447
- isolatedLevel
2365
+ vmIsolation
2448
2366
  );
2449
2367
  const sandboxManager = getSandBoxManager2();
2450
2368
  const sandbox = await sandboxManager.createSandbox(sandboxName);
2451
2369
  try {
2452
- const resolvedPath = filePath.startsWith("/home/gem") ? filePath : `/home/gem/${filePath.replace(/^\//, "")}`;
2370
+ const resolvedPath = filePath.startsWith("~/") ? filePath : `~/${filePath.replace(/^\//, "")}`;
2453
2371
  const filename = getFilenameFromPath(resolvedPath);
2454
2372
  const inferredContentType = getContentTypeFromFilename(filename);
2455
- const downloadResult = await sandbox.file.downloadFile({
2456
- path: resolvedPath
2457
- });
2458
- if (!downloadResult.ok) {
2459
- return reply.status(502).send({
2460
- error: `Download error: ${JSON.stringify(downloadResult.error)}`
2461
- });
2462
- }
2463
- const body = downloadResult.body;
2464
- if (typeof body?.stream === "function") {
2465
- const webStream = body.stream();
2466
- const nodeStream = Readable.fromWeb(webStream);
2467
- const contentType2 = body.contentType ?? inferredContentType;
2468
- const contentDisposition2 = body.contentDisposition ?? `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
2469
- reply = reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
2373
+ try {
2374
+ const buf = await sandbox.file.downloadFile({ file: resolvedPath });
2375
+ const contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
2376
+ reply = reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
2470
2377
  return reply;
2378
+ } catch (err) {
2379
+ reply.status(500).send({ error: String(err) });
2380
+ return;
2471
2381
  }
2472
- const bodyUnknown = downloadResult.body;
2473
- let buf;
2474
- let contentType = inferredContentType;
2475
- let contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
2476
- if (bodyUnknown instanceof ArrayBuffer) {
2477
- buf = Buffer.from(bodyUnknown);
2478
- } else if (bodyUnknown instanceof Buffer) {
2479
- buf = bodyUnknown;
2480
- } else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
2481
- const res = bodyUnknown;
2482
- buf = Buffer.from(await res.arrayBuffer());
2483
- if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
2484
- if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
2485
- } else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
2486
- const blob = await bodyUnknown.blob();
2487
- buf = Buffer.from(await blob.arrayBuffer());
2488
- } else {
2489
- return reply.status(502).send({ error: "Unexpected download response format" });
2490
- }
2491
- reply = reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
2492
- return reply;
2493
2382
  } catch (error) {
2494
2383
  const message = error instanceof Error ? error.message : String(error);
2495
2384
  return reply.status(502).send({ error: `Download proxy error: ${message}` });
@@ -2501,15 +2390,14 @@ function registerSandboxProxyRoutes(app2) {
2501
2390
  // src/controllers/workspace.ts
2502
2391
  import * as fs from "fs/promises";
2503
2392
  import * as path from "path";
2504
- import { Readable as Readable2 } from "stream";
2505
- import { getStoreLattice as getStoreLattice5 } from "@axiom-lattice/core";
2393
+ import { getStoreLattice as getStoreLattice4 } from "@axiom-lattice/core";
2506
2394
  import { SandboxFilesystem, FilesystemBackend } from "@axiom-lattice/core";
2507
2395
  import { getSandBoxManager as getSandBoxManager3 } from "@axiom-lattice/core";
2508
2396
  import { v4 as uuidv4 } from "uuid";
2509
2397
  var WorkspaceController = class {
2510
2398
  constructor() {
2511
- this.workspaceStore = getStoreLattice5("default", "workspace").store;
2512
- this.projectStore = getStoreLattice5("default", "project").store;
2399
+ this.workspaceStore = getStoreLattice4("default", "workspace").store;
2400
+ this.projectStore = getStoreLattice4("default", "project").store;
2513
2401
  }
2514
2402
  getTenantId(request) {
2515
2403
  const userTenantId = request.user?.tenantId;
@@ -2643,12 +2531,16 @@ var WorkspaceController = class {
2643
2531
  }
2644
2532
  if (workspace.storageType === "sandbox") {
2645
2533
  const sandboxManager = getSandBoxManager3();
2646
- const sandboxName = "global";
2647
- const sandbox = await sandboxManager.createSandbox(sandboxName);
2534
+ const sandbox = await sandboxManager.getSandboxFromConfig({
2535
+ assistant_id: "",
2536
+ thread_id: "",
2537
+ tenantId,
2538
+ workspaceId,
2539
+ projectId
2540
+ });
2648
2541
  return {
2649
2542
  backend: new SandboxFilesystem({
2650
- sandboxInstance: sandbox,
2651
- workingDirectory: `/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`
2543
+ sandboxInstance: sandbox
2652
2544
  }),
2653
2545
  workspace
2654
2546
  };
@@ -2697,50 +2589,26 @@ var WorkspaceController = class {
2697
2589
  }
2698
2590
  try {
2699
2591
  const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
2700
- const resolvedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
2592
+ const resolvedPath = filePath;
2701
2593
  if (workspace.storageType === "sandbox") {
2702
2594
  const sandboxManager = getSandBoxManager3();
2703
- const sandbox = await sandboxManager.createSandbox("global");
2704
- const realPath = path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId, resolvedPath);
2595
+ const sandbox = await sandboxManager.getSandboxFromConfig({
2596
+ assistant_id: "",
2597
+ thread_id: "",
2598
+ tenantId,
2599
+ workspaceId,
2600
+ projectId
2601
+ });
2602
+ const realPath = resolvedPath;
2705
2603
  const filename2 = this.getFilenameFromPath(resolvedPath);
2706
2604
  const inferredContentType = this.getMimeType(filename2);
2707
- const downloadResult = await sandbox.file.downloadFile({
2708
- path: realPath
2709
- });
2710
- if (!downloadResult.ok) {
2711
- return reply.status(502).send({
2712
- success: false,
2713
- error: `Download error: ${JSON.stringify(downloadResult.error)}`
2714
- });
2605
+ try {
2606
+ const buf = await sandbox.file.downloadFile({ file: realPath });
2607
+ const contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2608
+ return reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
2609
+ } catch (err) {
2610
+ return reply.status(502).send({ success: false, error: String(err) });
2715
2611
  }
2716
- const body = downloadResult.body;
2717
- if (typeof body?.stream === "function") {
2718
- const webStream = body.stream();
2719
- const nodeStream = Readable2.fromWeb(webStream);
2720
- const contentType2 = body.contentType ?? inferredContentType;
2721
- const contentDisposition2 = body.contentDisposition ?? `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2722
- return reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
2723
- }
2724
- const bodyUnknown = downloadResult.body;
2725
- let buf;
2726
- let contentType = inferredContentType;
2727
- let contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
2728
- if (bodyUnknown instanceof ArrayBuffer) {
2729
- buf = Buffer.from(bodyUnknown);
2730
- } else if (bodyUnknown instanceof Buffer) {
2731
- buf = bodyUnknown;
2732
- } else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
2733
- const res = bodyUnknown;
2734
- buf = Buffer.from(await res.arrayBuffer());
2735
- if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
2736
- if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
2737
- } else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
2738
- const blob = await bodyUnknown.blob();
2739
- buf = Buffer.from(await blob.arrayBuffer());
2740
- } else {
2741
- return reply.status(502).send({ success: false, error: "Unexpected download response format" });
2742
- }
2743
- return reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
2744
2612
  }
2745
2613
  const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
2746
2614
  const content = await backend.read(resolvedPath, 0, Infinity);
@@ -2766,36 +2634,27 @@ var WorkspaceController = class {
2766
2634
  }
2767
2635
  try {
2768
2636
  const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
2769
- const resolvedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
2637
+ const resolvedPath = filePath;
2770
2638
  if (workspace.storageType === "sandbox") {
2771
2639
  const sandboxManager = getSandBoxManager3();
2772
- const sandbox = await sandboxManager.createSandbox("global");
2773
- const realPath = path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId, resolvedPath);
2640
+ const sandbox = await sandboxManager.getSandboxFromConfig({
2641
+ assistant_id: "",
2642
+ thread_id: "",
2643
+ tenantId,
2644
+ workspaceId,
2645
+ projectId
2646
+ });
2647
+ const realPath = resolvedPath;
2774
2648
  const filename2 = this.getFilenameFromPath(resolvedPath);
2775
2649
  const inferredContentType = this.getMimeType(filename2);
2776
- const downloadResult = await sandbox.file.downloadFile({
2777
- path: realPath
2778
- });
2779
- if (!downloadResult.ok) {
2780
- return reply.status(502).send({
2781
- success: false,
2782
- error: `View error: ${JSON.stringify(downloadResult.error)}`
2783
- });
2784
- }
2785
- const body = downloadResult.body;
2786
- if (typeof body?.stream === "function") {
2787
- const webStream = body.stream();
2788
- const nodeStream = Readable2.fromWeb(webStream);
2789
- const contentType2 = body.contentType ?? inferredContentType;
2790
- console.log(`[viewFile] Sandbox returned stream, contentType: ${contentType2}, filename: ${filename2}`);
2791
- const isHtml2 = contentType2?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
2792
- if (isHtml2) {
2793
- console.log(`[viewFile] HTML stream detected, collecting for context injection`);
2794
- const chunks = [];
2795
- for await (const chunk of nodeStream) {
2796
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
2797
- }
2798
- let content2 = Buffer.concat(chunks).toString("utf-8");
2650
+ try {
2651
+ const buf = await sandbox.file.downloadFile({ file: realPath });
2652
+ let contentType = inferredContentType;
2653
+ const isHtml = contentType?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
2654
+ let outputBuf = buf;
2655
+ if (isHtml) {
2656
+ console.log(`[viewFile] Injecting AI2APP context for sandbox HTML file: ${filename2}, tenantId: ${tenantId}, contentType: ${contentType}`);
2657
+ let content2 = buf.toString("utf-8");
2799
2658
  const contextScript = `<script>window.__AI2APP_CONTEXT__=${JSON.stringify({
2800
2659
  tenantId,
2801
2660
  workspaceId,
@@ -2804,58 +2663,20 @@ var WorkspaceController = class {
2804
2663
  })};</script>`;
2805
2664
  if (content2.toLowerCase().includes("</head>")) {
2806
2665
  content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
2807
- console.log(`[viewFile] Context script injected before </head> (stream)`);
2666
+ console.log(`[viewFile] Context script injected before </head>`);
2808
2667
  } else if (content2.toLowerCase().includes("<html>")) {
2809
2668
  content2 = content2.replace(/<html>/i, `<html>${contextScript}`);
2810
- console.log(`[viewFile] Context script injected after <html> (stream)`);
2669
+ console.log(`[viewFile] Context script injected after <html>`);
2811
2670
  } else {
2812
2671
  content2 = contextScript + content2;
2813
- console.log(`[viewFile] Context script prepended to content (stream)`);
2672
+ console.log(`[viewFile] Context script prepended to content`);
2814
2673
  }
2815
- return reply.status(200).type(contentType2).header("Content-Disposition", "inline").send(Buffer.from(content2, "utf-8"));
2816
- }
2817
- return reply.status(200).type(contentType2).header("Content-Disposition", "inline").send(nodeStream);
2818
- }
2819
- const bodyUnknown = downloadResult.body;
2820
- let buf;
2821
- let contentType = inferredContentType;
2822
- if (bodyUnknown instanceof ArrayBuffer) {
2823
- buf = Buffer.from(bodyUnknown);
2824
- } else if (bodyUnknown instanceof Buffer) {
2825
- buf = bodyUnknown;
2826
- } else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
2827
- const res = bodyUnknown;
2828
- buf = Buffer.from(await res.arrayBuffer());
2829
- if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
2830
- } else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
2831
- const blob = await bodyUnknown.blob();
2832
- buf = Buffer.from(await blob.arrayBuffer());
2833
- } else {
2834
- return reply.status(502).send({ success: false, error: "Unexpected view response format" });
2835
- }
2836
- const isHtml = contentType?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
2837
- if (isHtml) {
2838
- console.log(`[viewFile] Injecting AI2APP context for sandbox HTML file: ${filename2}, tenantId: ${tenantId}, contentType: ${contentType}`);
2839
- let content2 = buf.toString("utf-8");
2840
- const contextScript = `<script>window.__AI2APP_CONTEXT__=${JSON.stringify({
2841
- tenantId,
2842
- workspaceId,
2843
- projectId,
2844
- timestamp: Date.now()
2845
- })};</script>`;
2846
- if (content2.toLowerCase().includes("</head>")) {
2847
- content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
2848
- console.log(`[viewFile] Context script injected before </head>`);
2849
- } else if (content2.toLowerCase().includes("<html>")) {
2850
- content2 = content2.replace(/<html>/i, `<html>${contextScript}`);
2851
- console.log(`[viewFile] Context script injected after <html>`);
2852
- } else {
2853
- content2 = contextScript + content2;
2854
- console.log(`[viewFile] Context script prepended to content`);
2674
+ outputBuf = Buffer.from(content2, "utf-8");
2855
2675
  }
2856
- buf = Buffer.from(content2, "utf-8");
2676
+ return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(outputBuf);
2677
+ } catch (err) {
2678
+ return reply.status(502).send({ success: false, error: String(err) });
2857
2679
  }
2858
- return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(buf);
2859
2680
  }
2860
2681
  const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
2861
2682
  const content = await backend.read(resolvedPath, 0, Infinity);
@@ -2934,26 +2755,25 @@ var WorkspaceController = class {
2934
2755
  const filename = data.filename || "file";
2935
2756
  const pathEntry = data.fields?.path;
2936
2757
  const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
2937
- if (pathValue && !/^[a-zA-Z0-9_./-]+$/.test(pathValue)) {
2758
+ if (pathValue && !/^[a-zA-Z0-9_./~\-]+$/.test(pathValue)) {
2938
2759
  return reply.status(400).send({ success: false, error: "Invalid path parameter" });
2939
2760
  }
2940
2761
  if (workspace.storageType === "sandbox") {
2941
2762
  const sandboxManager = getSandBoxManager3();
2942
- const sandboxName = "global";
2943
- const sandbox = await sandboxManager.createSandbox(sandboxName);
2944
- const baseDir = path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId);
2945
- const realPath = pathValue ? path.join(baseDir, pathValue, filename) : path.join(baseDir, filename);
2946
- const uploadResult = await sandbox.file.uploadFile({
2947
- file: buffer,
2948
- path: realPath
2949
- }, { timeoutInSeconds: 300 });
2950
- if (!uploadResult.ok) {
2951
- return reply.status(502).send({
2952
- success: false,
2953
- error: `Upload error: ${JSON.stringify(uploadResult.error)}`
2954
- });
2763
+ const sandbox = await sandboxManager.getSandboxFromConfig({
2764
+ assistant_id: "",
2765
+ thread_id: "",
2766
+ tenantId,
2767
+ workspaceId,
2768
+ projectId
2769
+ });
2770
+ const realPath = pathValue ? path.posix.join(pathValue, filename) : filename;
2771
+ try {
2772
+ await sandbox.file.uploadFile({ file: realPath, data: buffer });
2773
+ } catch (err) {
2774
+ return reply.status(500).send({ success: false, error: String(err) });
2955
2775
  }
2956
- const relativePath = uploadResult.body?.data?.file_path?.replace(path.join("/home/gem/tenants", tenantId, "workspaces", workspaceId, projectId), "") || (pathValue ? `/${pathValue}/${filename}` : `/${filename}`);
2776
+ const relativePath = pathValue ? path.posix.join(pathValue, filename) : `/${filename}`;
2957
2777
  const result2 = {
2958
2778
  path: relativePath,
2959
2779
  name: filename,
@@ -3045,7 +2865,7 @@ function registerWorkspaceRoutes(app2) {
3045
2865
 
3046
2866
  // src/controllers/database-configs.ts
3047
2867
  import {
3048
- getStoreLattice as getStoreLattice6,
2868
+ getStoreLattice as getStoreLattice5,
3049
2869
  sqlDatabaseManager
3050
2870
  } from "@axiom-lattice/core";
3051
2871
  import { randomUUID as randomUUID3 } from "crypto";
@@ -3059,7 +2879,7 @@ function getTenantId6(request) {
3059
2879
  async function getDatabaseConfigList(request, reply) {
3060
2880
  const tenantId = getTenantId6(request);
3061
2881
  try {
3062
- const storeLattice = getStoreLattice6("default", "database");
2882
+ const storeLattice = getStoreLattice5("default", "database");
3063
2883
  const store = storeLattice.store;
3064
2884
  const configs = await store.getAllConfigs(tenantId);
3065
2885
  console.log("Backend: getAllConfigs returned:", configs);
@@ -3090,7 +2910,7 @@ async function getDatabaseConfig(request, reply) {
3090
2910
  const tenantId = getTenantId6(request);
3091
2911
  const { key } = request.params;
3092
2912
  try {
3093
- const storeLattice = getStoreLattice6("default", "database");
2913
+ const storeLattice = getStoreLattice5("default", "database");
3094
2914
  const store = storeLattice.store;
3095
2915
  const config = await store.getConfigByKey(tenantId, key);
3096
2916
  if (!config) {
@@ -3116,7 +2936,7 @@ async function createDatabaseConfig(request, reply) {
3116
2936
  const tenantId = getTenantId6(request);
3117
2937
  const body = request.body;
3118
2938
  try {
3119
- const storeLattice = getStoreLattice6("default", "database");
2939
+ const storeLattice = getStoreLattice5("default", "database");
3120
2940
  const store = storeLattice.store;
3121
2941
  const existing = await store.getConfigByKey(tenantId, body.key);
3122
2942
  if (existing) {
@@ -3152,7 +2972,7 @@ async function updateDatabaseConfig(request, reply) {
3152
2972
  const { key } = request.params;
3153
2973
  const updates = request.body;
3154
2974
  try {
3155
- const storeLattice = getStoreLattice6("default", "database");
2975
+ const storeLattice = getStoreLattice5("default", "database");
3156
2976
  const store = storeLattice.store;
3157
2977
  const existing = await store.getConfigByKey(tenantId, key);
3158
2978
  if (!existing) {
@@ -3193,7 +3013,7 @@ async function deleteDatabaseConfig(request, reply) {
3193
3013
  const tenantId = getTenantId6(request);
3194
3014
  const { keyOrId } = request.params;
3195
3015
  try {
3196
- const storeLattice = getStoreLattice6("default", "database");
3016
+ const storeLattice = getStoreLattice5("default", "database");
3197
3017
  const store = storeLattice.store;
3198
3018
  console.log("Delete request - keyOrId:", keyOrId);
3199
3019
  let config = await store.getConfigByKey(tenantId, keyOrId);
@@ -3242,7 +3062,7 @@ async function testDatabaseConnection(request, reply) {
3242
3062
  const tenantId = getTenantId6(request);
3243
3063
  const { key } = request.params;
3244
3064
  try {
3245
- const storeLattice = getStoreLattice6("default", "database");
3065
+ const storeLattice = getStoreLattice5("default", "database");
3246
3066
  const store = storeLattice.store;
3247
3067
  const config = await store.getConfigByKey(tenantId, key);
3248
3068
  if (!config) {
@@ -3327,7 +3147,7 @@ function registerDatabaseConfigRoutes(app2) {
3327
3147
 
3328
3148
  // src/controllers/metrics-configs.ts
3329
3149
  import {
3330
- getStoreLattice as getStoreLattice7,
3150
+ getStoreLattice as getStoreLattice6,
3331
3151
  metricsServerManager as metricsServerManager2,
3332
3152
  SemanticMetricsClient as SemanticMetricsClient2
3333
3153
  } from "@axiom-lattice/core";
@@ -3342,7 +3162,7 @@ function getTenantId7(request) {
3342
3162
  async function getMetricsServerConfigList(request, reply) {
3343
3163
  const tenantId = getTenantId7(request);
3344
3164
  try {
3345
- const storeLattice = getStoreLattice7("default", "metrics");
3165
+ const storeLattice = getStoreLattice6("default", "metrics");
3346
3166
  const store = storeLattice.store;
3347
3167
  const configs = await store.getAllConfigs(tenantId);
3348
3168
  return {
@@ -3369,7 +3189,7 @@ async function getMetricsServerConfig(request, reply) {
3369
3189
  const tenantId = getTenantId7(request);
3370
3190
  const { key } = request.params;
3371
3191
  try {
3372
- const storeLattice = getStoreLattice7("default", "metrics");
3192
+ const storeLattice = getStoreLattice6("default", "metrics");
3373
3193
  const store = storeLattice.store;
3374
3194
  const config = await store.getConfigByKey(tenantId, key);
3375
3195
  if (!config) {
@@ -3395,7 +3215,7 @@ async function createMetricsServerConfig(request, reply) {
3395
3215
  const tenantId = getTenantId7(request);
3396
3216
  const body = request.body;
3397
3217
  try {
3398
- const storeLattice = getStoreLattice7("default", "metrics");
3218
+ const storeLattice = getStoreLattice6("default", "metrics");
3399
3219
  const store = storeLattice.store;
3400
3220
  const existing = await store.getConfigByKey(tenantId, body.key);
3401
3221
  if (existing) {
@@ -3447,7 +3267,7 @@ async function updateMetricsServerConfig(request, reply) {
3447
3267
  const { key } = request.params;
3448
3268
  const updates = request.body;
3449
3269
  try {
3450
- const storeLattice = getStoreLattice7("default", "metrics");
3270
+ const storeLattice = getStoreLattice6("default", "metrics");
3451
3271
  const store = storeLattice.store;
3452
3272
  const existing = await store.getConfigByKey(tenantId, key);
3453
3273
  if (!existing) {
@@ -3497,7 +3317,7 @@ async function deleteMetricsServerConfig(request, reply) {
3497
3317
  const tenantId = getTenantId7(request);
3498
3318
  const { keyOrId } = request.params;
3499
3319
  try {
3500
- const storeLattice = getStoreLattice7("default", "metrics");
3320
+ const storeLattice = getStoreLattice6("default", "metrics");
3501
3321
  const store = storeLattice.store;
3502
3322
  let config = await store.getConfigByKey(tenantId, keyOrId);
3503
3323
  let configKey = keyOrId;
@@ -3544,7 +3364,7 @@ async function testMetricsServerConnection(request, reply) {
3544
3364
  const tenantId = getTenantId7(request);
3545
3365
  const { key } = request.params;
3546
3366
  try {
3547
- const storeLattice = getStoreLattice7("default", "metrics");
3367
+ const storeLattice = getStoreLattice6("default", "metrics");
3548
3368
  const store = storeLattice.store;
3549
3369
  const config = await store.getConfigByKey(tenantId, key);
3550
3370
  if (!config) {
@@ -3595,7 +3415,7 @@ async function listAvailableMetrics(request, reply) {
3595
3415
  const tenantId = getTenantId7(request);
3596
3416
  const { key } = request.params;
3597
3417
  try {
3598
- const storeLattice = getStoreLattice7("default", "metrics");
3418
+ const storeLattice = getStoreLattice6("default", "metrics");
3599
3419
  const store = storeLattice.store;
3600
3420
  const config = await store.getConfigByKey(tenantId, key);
3601
3421
  if (!config) {
@@ -3634,7 +3454,7 @@ async function queryMetricsData(request, reply) {
3634
3454
  const { key } = request.params;
3635
3455
  const { metricName, startTime, endTime, step, labels } = request.body;
3636
3456
  try {
3637
- const storeLattice = getStoreLattice7("default", "metrics");
3457
+ const storeLattice = getStoreLattice6("default", "metrics");
3638
3458
  const store = storeLattice.store;
3639
3459
  const config = await store.getConfigByKey(tenantId, key);
3640
3460
  if (!config) {
@@ -3681,7 +3501,7 @@ async function getDataSources(request, reply) {
3681
3501
  const tenantId = getTenantId7(request);
3682
3502
  const { key } = request.params;
3683
3503
  try {
3684
- const storeLattice = getStoreLattice7("default", "metrics");
3504
+ const storeLattice = getStoreLattice6("default", "metrics");
3685
3505
  const store = storeLattice.store;
3686
3506
  const config = await store.getConfigByKey(tenantId, key);
3687
3507
  if (!config) {
@@ -3722,7 +3542,7 @@ async function getDatasourceMetrics(request, reply) {
3722
3542
  const tenantId = getTenantId7(request);
3723
3543
  const { key, datasourceId } = request.params;
3724
3544
  try {
3725
- const storeLattice = getStoreLattice7("default", "metrics");
3545
+ const storeLattice = getStoreLattice6("default", "metrics");
3726
3546
  const store = storeLattice.store;
3727
3547
  const config = await store.getConfigByKey(tenantId, key);
3728
3548
  if (!config) {
@@ -3760,7 +3580,7 @@ async function querySemanticMetrics(request, reply) {
3760
3580
  const { key } = request.params;
3761
3581
  const body = request.body;
3762
3582
  try {
3763
- const storeLattice = getStoreLattice7("default", "metrics");
3583
+ const storeLattice = getStoreLattice6("default", "metrics");
3764
3584
  const store = storeLattice.store;
3765
3585
  const config = await store.getConfigByKey(tenantId, key);
3766
3586
  if (!config) {
@@ -3914,7 +3734,7 @@ function registerMetricsServerConfigRoutes(app2) {
3914
3734
 
3915
3735
  // src/controllers/mcp-configs.ts
3916
3736
  import {
3917
- getStoreLattice as getStoreLattice8,
3737
+ getStoreLattice as getStoreLattice7,
3918
3738
  mcpManager,
3919
3739
  toolLatticeManager as toolLatticeManager2
3920
3740
  } from "@axiom-lattice/core";
@@ -3929,7 +3749,7 @@ function getTenantId8(request) {
3929
3749
  async function getMcpServerConfigList(request, reply) {
3930
3750
  const tenantId = getTenantId8(request);
3931
3751
  try {
3932
- const storeLattice = getStoreLattice8("default", "mcp");
3752
+ const storeLattice = getStoreLattice7("default", "mcp");
3933
3753
  const store = storeLattice.store;
3934
3754
  const configs = await store.getAllConfigs(tenantId);
3935
3755
  return {
@@ -3956,7 +3776,7 @@ async function getMcpServerConfig(request, reply) {
3956
3776
  const tenantId = getTenantId8(request);
3957
3777
  const { key } = request.params;
3958
3778
  try {
3959
- const storeLattice = getStoreLattice8("default", "mcp");
3779
+ const storeLattice = getStoreLattice7("default", "mcp");
3960
3780
  const store = storeLattice.store;
3961
3781
  const config = await store.getConfigByKey(tenantId, key);
3962
3782
  if (!config) {
@@ -3982,7 +3802,7 @@ async function createMcpServerConfig(request, reply) {
3982
3802
  const tenantId = getTenantId8(request);
3983
3803
  const body = request.body;
3984
3804
  try {
3985
- const storeLattice = getStoreLattice8("default", "mcp");
3805
+ const storeLattice = getStoreLattice7("default", "mcp");
3986
3806
  const store = storeLattice.store;
3987
3807
  const existing = await store.getConfigByKey(tenantId, body.key);
3988
3808
  if (existing) {
@@ -4022,7 +3842,7 @@ async function updateMcpServerConfig(request, reply) {
4022
3842
  const { key } = request.params;
4023
3843
  const updates = request.body;
4024
3844
  try {
4025
- const storeLattice = getStoreLattice8("default", "mcp");
3845
+ const storeLattice = getStoreLattice7("default", "mcp");
4026
3846
  const store = storeLattice.store;
4027
3847
  const existing = await store.getConfigByKey(tenantId, key);
4028
3848
  if (!existing) {
@@ -4071,7 +3891,7 @@ async function deleteMcpServerConfig(request, reply) {
4071
3891
  const tenantId = getTenantId8(request);
4072
3892
  const { keyOrId } = request.params;
4073
3893
  try {
4074
- const storeLattice = getStoreLattice8("default", "mcp");
3894
+ const storeLattice = getStoreLattice7("default", "mcp");
4075
3895
  const store = storeLattice.store;
4076
3896
  let config = await store.getConfigByKey(tenantId, keyOrId);
4077
3897
  let configKey = keyOrId;
@@ -4118,7 +3938,7 @@ async function testMcpServerConnection(request, reply) {
4118
3938
  const tenantId = getTenantId8(request);
4119
3939
  const { key } = request.params;
4120
3940
  try {
4121
- const storeLattice = getStoreLattice8("default", "mcp");
3941
+ const storeLattice = getStoreLattice7("default", "mcp");
4122
3942
  const store = storeLattice.store;
4123
3943
  const config = await store.getConfigByKey(tenantId, key);
4124
3944
  if (!config) {
@@ -4171,7 +3991,7 @@ async function listMcpServerTools(request, reply) {
4171
3991
  const tenantId = getTenantId8(request);
4172
3992
  const { key } = request.params;
4173
3993
  try {
4174
- const storeLattice = getStoreLattice8("default", "mcp");
3994
+ const storeLattice = getStoreLattice7("default", "mcp");
4175
3995
  const store = storeLattice.store;
4176
3996
  const config = await store.getConfigByKey(tenantId, key);
4177
3997
  if (!config) {
@@ -4204,7 +4024,7 @@ async function connectMcpServer(request, reply) {
4204
4024
  const tenantId = getTenantId8(request);
4205
4025
  const { key } = request.params;
4206
4026
  try {
4207
- const storeLattice = getStoreLattice8("default", "mcp");
4027
+ const storeLattice = getStoreLattice7("default", "mcp");
4208
4028
  const store = storeLattice.store;
4209
4029
  const config = await store.getConfigByKey(tenantId, key);
4210
4030
  if (!config) {
@@ -4225,7 +4045,7 @@ async function connectMcpServer(request, reply) {
4225
4045
  };
4226
4046
  } catch (error) {
4227
4047
  console.error("Failed to connect MCP server:", error);
4228
- const storeLattice = getStoreLattice8("default", "mcp");
4048
+ const storeLattice = getStoreLattice7("default", "mcp");
4229
4049
  const store = storeLattice.store;
4230
4050
  const config = await store.getConfigByKey(tenantId, key);
4231
4051
  if (config) {
@@ -4241,7 +4061,7 @@ async function disconnectMcpServer(request, reply) {
4241
4061
  const tenantId = getTenantId8(request);
4242
4062
  const { key } = request.params;
4243
4063
  try {
4244
- const storeLattice = getStoreLattice8("default", "mcp");
4064
+ const storeLattice = getStoreLattice7("default", "mcp");
4245
4065
  const store = storeLattice.store;
4246
4066
  const config = await store.getConfigByKey(tenantId, key);
4247
4067
  if (!config) {
@@ -4346,11 +4166,11 @@ function registerMcpServerConfigRoutes(app2) {
4346
4166
  }
4347
4167
 
4348
4168
  // src/controllers/users.ts
4349
- import { getStoreLattice as getStoreLattice9 } from "@axiom-lattice/core";
4169
+ import { getStoreLattice as getStoreLattice8 } from "@axiom-lattice/core";
4350
4170
  import { v4 as uuidv42 } from "uuid";
4351
4171
  var UsersController = class {
4352
4172
  constructor() {
4353
- this.userStore = getStoreLattice9("default", "user").store;
4173
+ this.userStore = getStoreLattice8("default", "user").store;
4354
4174
  }
4355
4175
  async listUsers(request, reply) {
4356
4176
  const { email } = request.query;
@@ -4428,11 +4248,11 @@ function registerUserRoutes(app2) {
4428
4248
  }
4429
4249
 
4430
4250
  // src/controllers/tenants.ts
4431
- import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
4251
+ import { getStoreLattice as getStoreLattice9 } from "@axiom-lattice/core";
4432
4252
  import { v4 as uuidv43 } from "uuid";
4433
4253
  var TenantsController = class {
4434
4254
  constructor() {
4435
- this.tenantStore = getStoreLattice10("default", "tenant").store;
4255
+ this.tenantStore = getStoreLattice9("default", "tenant").store;
4436
4256
  }
4437
4257
  // ==================== Tenant CRUD ====================
4438
4258
  async listTenants(request, reply) {
@@ -4496,7 +4316,7 @@ function registerTenantRoutes(app2) {
4496
4316
  }
4497
4317
 
4498
4318
  // src/controllers/auth.ts
4499
- import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
4319
+ import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
4500
4320
  import { v4 as uuidv44 } from "uuid";
4501
4321
  var defaultAuthConfig = {
4502
4322
  autoApproveUsers: true,
@@ -4505,9 +4325,9 @@ var defaultAuthConfig = {
4505
4325
  };
4506
4326
  var AuthController = class {
4507
4327
  constructor(config = {}) {
4508
- this.userStore = getStoreLattice11("default", "user").store;
4509
- this.tenantStore = getStoreLattice11("default", "tenant").store;
4510
- this.userTenantLinkStore = getStoreLattice11("default", "userTenantLink").store;
4328
+ this.userStore = getStoreLattice10("default", "user").store;
4329
+ this.tenantStore = getStoreLattice10("default", "tenant").store;
4330
+ this.userTenantLinkStore = getStoreLattice10("default", "userTenantLink").store;
4511
4331
  this.config = { ...defaultAuthConfig, ...config };
4512
4332
  }
4513
4333
  async register(request, reply) {
@@ -4878,6 +4698,698 @@ function registerAuthRoutes(app2, config) {
4878
4698
  );
4879
4699
  }
4880
4700
 
4701
+ // src/channels/lark/routes.ts
4702
+ import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
4703
+ import {
4704
+ ChannelIdentityMappingStore,
4705
+ PostgreSQLChannelInstallationStore
4706
+ } from "@axiom-lattice/pg-stores";
4707
+
4708
+ // src/channels/lark/parser.ts
4709
+ function parseLarkMessageEvent(payload) {
4710
+ const raw = payload;
4711
+ if (raw.header?.event_type !== "im.message.receive_v1") {
4712
+ return null;
4713
+ }
4714
+ const message = raw.event?.message;
4715
+ const openId = raw.event?.sender?.sender_id?.open_id;
4716
+ if (!message || !openId || message.message_type !== "text") {
4717
+ return null;
4718
+ }
4719
+ const parsedContent = parseLarkTextContent(message.content);
4720
+ if (!message.message_id || !message.chat_id || !parsedContent) {
4721
+ return null;
4722
+ }
4723
+ return {
4724
+ messageId: message.message_id,
4725
+ openId,
4726
+ chatId: message.chat_id,
4727
+ chatType: normalizeChatType(message.chat_type),
4728
+ text: parsedContent
4729
+ };
4730
+ }
4731
+ function parseLarkTextContent(content) {
4732
+ if (!content) {
4733
+ return null;
4734
+ }
4735
+ try {
4736
+ const parsed = JSON.parse(content);
4737
+ return typeof parsed.text === "string" ? parsed.text : null;
4738
+ } catch {
4739
+ return null;
4740
+ }
4741
+ }
4742
+ function normalizeChatType(chatType) {
4743
+ return chatType === "p2p" ? "direct" : "group";
4744
+ }
4745
+
4746
+ // src/channels/lark/controller.ts
4747
+ function createLarkEventHandler(dependencies) {
4748
+ return async function handleLarkEvent2(request, reply) {
4749
+ const installationId = request.params?.installationId;
4750
+ if (!installationId) {
4751
+ reply.status(400).send({ success: false, message: "Missing installationId" });
4752
+ return;
4753
+ }
4754
+ const config = await dependencies.getInstallationConfig(installationId);
4755
+ if (!config) {
4756
+ reply.status(404).send({ success: false, message: "Lark installation not found" });
4757
+ return;
4758
+ }
4759
+ const body = dependencies.parseRequestBody(
4760
+ request.body,
4761
+ config.encryptKey
4762
+ );
4763
+ if (!dependencies.verifyParsedBody(body, config)) {
4764
+ reply.status(401).send({ success: false, message: "Invalid Lark request" });
4765
+ return;
4766
+ }
4767
+ if (body.type === "url_verification" && body.challenge) {
4768
+ reply.status(200).send({ challenge: body.challenge });
4769
+ return;
4770
+ }
4771
+ const parsed = dependencies.parseEvent(request.body);
4772
+ if (!parsed) {
4773
+ reply.status(200).send({ success: true, ignored: true });
4774
+ return;
4775
+ }
4776
+ const receipt = await dependencies.claimInboundReceipt({
4777
+ channel: "lark",
4778
+ channelAppId: config.appId,
4779
+ externalMessageId: parsed.messageId,
4780
+ tenantId: config.tenantId
4781
+ });
4782
+ if (!receipt.accepted) {
4783
+ reply.status(200).send(
4784
+ receipt.status === "processing" ? { success: true, processing: true } : { success: true, duplicate: true }
4785
+ );
4786
+ return;
4787
+ }
4788
+ try {
4789
+ const { threadId } = await dependencies.resolveThread({
4790
+ channel: "lark",
4791
+ channelAppId: config.appId,
4792
+ tenantId: config.tenantId,
4793
+ assistantId: config.assistantId,
4794
+ mappingMode: config.mappingMode,
4795
+ openId: parsed.openId,
4796
+ chatId: parsed.chatId,
4797
+ chatType: parsed.chatType,
4798
+ messageId: parsed.messageId,
4799
+ workspaceId: config.workspaceId,
4800
+ projectId: config.projectId
4801
+ });
4802
+ const text = await dependencies.runAgentAndCollectText({
4803
+ tenantId: config.tenantId,
4804
+ assistantId: config.assistantId,
4805
+ threadId,
4806
+ text: parsed.text,
4807
+ workspaceId: config.workspaceId,
4808
+ projectId: config.projectId
4809
+ });
4810
+ await dependencies.sendTextReply({
4811
+ chatId: parsed.chatId,
4812
+ text,
4813
+ config
4814
+ });
4815
+ await dependencies.markInboundReceiptCompleted({
4816
+ channel: "lark",
4817
+ channelAppId: config.appId,
4818
+ externalMessageId: parsed.messageId,
4819
+ tenantId: config.tenantId,
4820
+ threadId
4821
+ });
4822
+ reply.status(200).send({ success: true, threadId });
4823
+ } catch (error) {
4824
+ await dependencies.markInboundReceiptFailed({
4825
+ channel: "lark",
4826
+ channelAppId: config.appId,
4827
+ externalMessageId: parsed.messageId,
4828
+ tenantId: config.tenantId
4829
+ });
4830
+ throw error;
4831
+ }
4832
+ };
4833
+ }
4834
+ var handleLarkEvent = createLarkEventHandler({
4835
+ getInstallationConfig: async () => null,
4836
+ parseRequestBody: (body) => body || {},
4837
+ verifyParsedBody: () => true,
4838
+ parseEvent: parseLarkMessageEvent,
4839
+ claimInboundReceipt: async () => ({ accepted: true, status: "processing" }),
4840
+ markInboundReceiptCompleted: async () => void 0,
4841
+ markInboundReceiptFailed: async () => void 0,
4842
+ resolveThread: async () => ({ threadId: "" }),
4843
+ runAgentAndCollectText: async () => "",
4844
+ sendTextReply: async () => void 0
4845
+ });
4846
+
4847
+ // src/channels/lark/config.ts
4848
+ function loadLarkIngressConfig() {
4849
+ return {
4850
+ enabled: process.env.LARK_ENABLED !== "false",
4851
+ appId: process.env.LARK_APP_ID || "",
4852
+ appSecret: process.env.LARK_APP_SECRET || "",
4853
+ verificationToken: process.env.LARK_VERIFICATION_TOKEN,
4854
+ encryptKey: process.env.LARK_ENCRYPT_KEY,
4855
+ tenantId: process.env.LARK_TENANT_ID || "default",
4856
+ assistantId: process.env.LARK_ASSISTANT_ID || "default_agent",
4857
+ workspaceId: process.env.LARK_WORKSPACE_ID,
4858
+ projectId: process.env.LARK_PROJECT_ID,
4859
+ mappingMode: process.env.LARK_MAPPING_MODE || "hybrid"
4860
+ };
4861
+ }
4862
+ function isLarkIngressEnabled(config) {
4863
+ return config.enabled;
4864
+ }
4865
+
4866
+ // src/channels/lark/mapping-service.ts
4867
+ import { randomUUID as randomUUID6 } from "crypto";
4868
+ function createChannelThreadMappingService(deps) {
4869
+ return {
4870
+ async getOrCreateThread(input) {
4871
+ const externalSubjectKey = buildExternalSubjectKey(input);
4872
+ const existing = await deps.mappingStore.getMappingBySubject({
4873
+ channel: input.channel,
4874
+ channelAppId: input.channelAppId,
4875
+ tenantId: input.tenantId,
4876
+ assistantId: input.assistantId,
4877
+ externalSubjectKey
4878
+ });
4879
+ if (existing) {
4880
+ return { threadId: existing.threadId };
4881
+ }
4882
+ const threadId = (deps.uuid || randomUUID6)();
4883
+ await deps.threadStore.createThread(input.tenantId, input.assistantId, threadId, {
4884
+ metadata: {
4885
+ tenantId: input.tenantId,
4886
+ workspaceId: input.workspaceId,
4887
+ projectId: input.projectId,
4888
+ channel: input.channel,
4889
+ larkChatId: input.chatId,
4890
+ larkOpenId: input.openId
4891
+ }
4892
+ });
4893
+ try {
4894
+ await deps.mappingStore.createMapping({
4895
+ channel: input.channel,
4896
+ channelAppId: input.channelAppId,
4897
+ tenantId: input.tenantId,
4898
+ assistantId: input.assistantId,
4899
+ mappingMode: input.mappingMode,
4900
+ externalSubjectType: resolveSubjectType(input.mappingMode, input.chatType) === "user" ? "user" : "chat",
4901
+ externalSubjectKey,
4902
+ larkOpenId: input.openId,
4903
+ larkChatId: input.chatId,
4904
+ larkMessageId: input.messageId,
4905
+ threadId
4906
+ });
4907
+ } catch (error) {
4908
+ if (!isUniqueViolation(error)) {
4909
+ throw error;
4910
+ }
4911
+ const canonical = await deps.mappingStore.getMappingBySubject({
4912
+ channel: input.channel,
4913
+ channelAppId: input.channelAppId,
4914
+ tenantId: input.tenantId,
4915
+ assistantId: input.assistantId,
4916
+ externalSubjectKey
4917
+ });
4918
+ if (!canonical) {
4919
+ throw error;
4920
+ }
4921
+ await deps.threadStore.deleteThread(input.tenantId, threadId);
4922
+ return { threadId: canonical.threadId };
4923
+ }
4924
+ return { threadId };
4925
+ }
4926
+ };
4927
+ }
4928
+ function isUniqueViolation(error) {
4929
+ return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
4930
+ }
4931
+ function buildExternalSubjectKey(input) {
4932
+ const subjectType = resolveSubjectType(input.mappingMode, input.chatType);
4933
+ const subjectValue = subjectType === "user" ? input.openId : input.chatId;
4934
+ return [
4935
+ input.channel,
4936
+ input.channelAppId,
4937
+ `tenant:${input.tenantId}`,
4938
+ `assistant:${input.assistantId}`,
4939
+ `${subjectType}:${subjectValue}`
4940
+ ].join(":");
4941
+ }
4942
+ function resolveSubjectType(mappingMode, chatType) {
4943
+ if (mappingMode === "user") {
4944
+ return "user";
4945
+ }
4946
+ if (mappingMode === "group") {
4947
+ return "chat";
4948
+ }
4949
+ return chatType === "direct" ? "user" : "chat";
4950
+ }
4951
+
4952
+ // src/channels/lark/runner.ts
4953
+ import { agentInstanceManager as agentInstanceManager5 } from "@axiom-lattice/core";
4954
+ import { MessageChunkTypes as MessageChunkTypes3 } from "@axiom-lattice/protocols";
4955
+
4956
+ // src/channels/lark/aggregator.ts
4957
+ import { MessageChunkTypes as MessageChunkTypes2 } from "@axiom-lattice/protocols";
4958
+ function aggregateLarkReply(messageId, chunks) {
4959
+ return chunks.filter(
4960
+ (chunk) => chunk.type === MessageChunkTypes2.AI && chunk.data.id === messageId
4961
+ ).map((chunk) => chunk.data.content || "").join("").trim();
4962
+ }
4963
+
4964
+ // src/channels/lark/runner.ts
4965
+ async function runAgentAndCollectLarkReply(input) {
4966
+ const agent = agentInstanceManager5.getAgent({
4967
+ tenant_id: input.tenantId,
4968
+ assistant_id: input.assistantId,
4969
+ thread_id: input.threadId,
4970
+ workspace_id: input.workspaceId,
4971
+ project_id: input.projectId
4972
+ });
4973
+ const result = await agent.addMessage({
4974
+ input: {
4975
+ message: input.text
4976
+ }
4977
+ });
4978
+ const chunks = [];
4979
+ const stream = agent.chunkStream(result.messageId, [
4980
+ MessageChunkTypes3.MESSAGE_COMPLETED
4981
+ ]);
4982
+ for await (const chunk of stream) {
4983
+ chunks.push(chunk);
4984
+ }
4985
+ return aggregateLarkReply(result.messageId, chunks);
4986
+ }
4987
+
4988
+ // src/channels/lark/sender.ts
4989
+ function createLarkSender(config, client = createDefaultLarkClient(config)) {
4990
+ return {
4991
+ async sendTextReply(input) {
4992
+ const response = await client.im.v1.message.create({
4993
+ params: {
4994
+ receive_id_type: "chat_id"
4995
+ },
4996
+ data: {
4997
+ receive_id: input.chatId,
4998
+ msg_type: "text",
4999
+ content: JSON.stringify({ text: input.text })
5000
+ }
5001
+ });
5002
+ if (response.code && response.code !== 0) {
5003
+ throw new Error("Failed to send Lark reply");
5004
+ }
5005
+ }
5006
+ };
5007
+ }
5008
+ function createDefaultLarkClient(config) {
5009
+ const Lark = __require("@larksuiteoapi/node-sdk");
5010
+ return new Lark.Client({
5011
+ appId: config.appId,
5012
+ appSecret: config.appSecret
5013
+ });
5014
+ }
5015
+
5016
+ // src/channels/lark/verification.ts
5017
+ import crypto2 from "crypto";
5018
+ function parseLarkRequestBody(body, encryptKey) {
5019
+ const parsed = body || {};
5020
+ if (encryptKey && typeof parsed.encrypt === "string") {
5021
+ return decryptLarkPayload(encryptKey, parsed.encrypt);
5022
+ }
5023
+ return parsed;
5024
+ }
5025
+ function decryptLarkPayload(encryptKey, encryptedPayload) {
5026
+ const key = crypto2.createHash("sha256").update(encryptKey).digest();
5027
+ const buffer = Buffer.from(encryptedPayload, "base64");
5028
+ const iv = buffer.subarray(0, 16);
5029
+ const ciphertext = buffer.subarray(16);
5030
+ const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
5031
+ const plaintext = Buffer.concat([
5032
+ decipher.update(ciphertext),
5033
+ decipher.final()
5034
+ ]).toString("utf8");
5035
+ return JSON.parse(plaintext);
5036
+ }
5037
+ function createLarkRequestVerifier(config) {
5038
+ return function verifyRequest(request) {
5039
+ const body = parseLarkRequestBody(request.body, config.encryptKey);
5040
+ return verifyLarkParsedBody(body, config);
5041
+ };
5042
+ }
5043
+ function verifyLarkParsedBody(body, config) {
5044
+ if (!config.verificationToken) {
5045
+ return true;
5046
+ }
5047
+ return extractVerificationToken(body) === config.verificationToken;
5048
+ }
5049
+ function extractVerificationToken(body) {
5050
+ if (typeof body.token === "string") {
5051
+ return body.token;
5052
+ }
5053
+ if (typeof body.header?.token === "string") {
5054
+ return body.header.token;
5055
+ }
5056
+ return void 0;
5057
+ }
5058
+
5059
+ // src/channels/lark/routes.ts
5060
+ function registerLarkChannelRoutes(app2, dependencies) {
5061
+ const config = loadLarkIngressConfig();
5062
+ if (!dependencies && !isLarkIngressEnabled(config)) {
5063
+ return;
5064
+ }
5065
+ const handlerDependencies = dependencies || createDefaultLarkDependencies();
5066
+ app2.post(
5067
+ "/api/channels/lark/installations/:installationId/events",
5068
+ createLarkEventHandler({
5069
+ ...handlerDependencies
5070
+ })
5071
+ );
5072
+ }
5073
+ function createDefaultLarkDependencies() {
5074
+ const installationStore = new PostgreSQLChannelInstallationStore({
5075
+ poolConfig: getDatabaseUrl()
5076
+ });
5077
+ const threadStore = getStoreLattice11("default", "thread").store;
5078
+ const mappingStore = new ChannelIdentityMappingStore({
5079
+ poolConfig: getDatabaseUrl()
5080
+ });
5081
+ const mappingService = createChannelThreadMappingService({
5082
+ mappingStore,
5083
+ threadStore
5084
+ });
5085
+ return {
5086
+ getInstallationConfig: async (installationId) => {
5087
+ const installation = await installationStore.getInstallationById(
5088
+ installationId
5089
+ );
5090
+ if (!installation || installation.channel !== "lark") {
5091
+ return null;
5092
+ }
5093
+ return {
5094
+ enabled: true,
5095
+ installationId: installation.id,
5096
+ tenantId: installation.tenantId,
5097
+ assistantId: installation.config.assistantId,
5098
+ appId: installation.config.appId,
5099
+ appSecret: installation.config.appSecret,
5100
+ verificationToken: installation.config.verificationToken,
5101
+ encryptKey: installation.config.encryptKey,
5102
+ workspaceId: installation.config.workspaceId,
5103
+ projectId: installation.config.projectId,
5104
+ mappingMode: installation.config.mappingMode
5105
+ };
5106
+ },
5107
+ parseRequestBody: (body, encryptKey) => parseLarkRequestBody(body, encryptKey),
5108
+ verifyParsedBody: (body, config) => {
5109
+ if (!config.verificationToken) {
5110
+ return true;
5111
+ }
5112
+ return createLarkRequestVerifier(config)({
5113
+ body
5114
+ });
5115
+ },
5116
+ parseEvent: parseLarkMessageEvent,
5117
+ claimInboundReceipt: (input) => mappingStore.claimInboundReceipt(input),
5118
+ markInboundReceiptCompleted: (input) => mappingStore.markInboundReceiptCompleted(input),
5119
+ markInboundReceiptFailed: (input) => mappingStore.markInboundReceiptFailed(input),
5120
+ resolveThread: (input) => mappingService.getOrCreateThread(input),
5121
+ runAgentAndCollectText: ({ tenantId, assistantId, threadId, text, workspaceId, projectId }) => runAgentAndCollectLarkReply({
5122
+ tenantId,
5123
+ assistantId,
5124
+ threadId,
5125
+ text,
5126
+ workspaceId,
5127
+ projectId
5128
+ }),
5129
+ sendTextReply: async ({ chatId, text, config }) => {
5130
+ const sender = createLarkSender({
5131
+ appId: config.appId,
5132
+ appSecret: config.appSecret
5133
+ });
5134
+ await sender.sendTextReply({ chatId, text });
5135
+ }
5136
+ };
5137
+ }
5138
+ function getDatabaseUrl() {
5139
+ const databaseUrl = process.env.DATABASE_URL;
5140
+ if (!databaseUrl) {
5141
+ throw new Error("DATABASE_URL is required for Lark channel ingress");
5142
+ }
5143
+ return databaseUrl;
5144
+ }
5145
+
5146
+ // src/channels/routes.ts
5147
+ var channelRouteRegistrars = [
5148
+ (app2, dependencies) => registerLarkChannelRoutes(app2, dependencies.lark)
5149
+ ];
5150
+ function registerChannelRoutes(app2, dependencies = {}) {
5151
+ for (const registerRoutes of channelRouteRegistrars) {
5152
+ registerRoutes(app2, dependencies);
5153
+ }
5154
+ }
5155
+
5156
+ // src/controllers/channel-installations.ts
5157
+ import { randomUUID as randomUUID7 } from "crypto";
5158
+ function getTenantId9(request) {
5159
+ const userTenantId = request.user?.tenantId;
5160
+ if (userTenantId) {
5161
+ return userTenantId;
5162
+ }
5163
+ return request.headers["x-tenant-id"] || "default";
5164
+ }
5165
+ function getInstallationStore() {
5166
+ const { PostgreSQLChannelInstallationStore: PostgreSQLChannelInstallationStore2 } = __require("@axiom-lattice/pg-stores");
5167
+ const databaseUrl = process.env.DATABASE_URL;
5168
+ if (!databaseUrl) {
5169
+ throw new Error("DATABASE_URL is required for channel installation store");
5170
+ }
5171
+ return new PostgreSQLChannelInstallationStore2({
5172
+ poolConfig: databaseUrl
5173
+ });
5174
+ }
5175
+ async function getChannelInstallationList(request, reply) {
5176
+ const tenantId = getTenantId9(request);
5177
+ const { channel } = request.query;
5178
+ try {
5179
+ const store = getInstallationStore();
5180
+ const installations = await store.getInstallationsByTenant(tenantId, channel);
5181
+ return {
5182
+ success: true,
5183
+ message: "Channel installations retrieved successfully",
5184
+ data: {
5185
+ records: installations,
5186
+ total: installations.length
5187
+ }
5188
+ };
5189
+ } catch (error) {
5190
+ console.error("Failed to get channel installations:", error);
5191
+ return {
5192
+ success: false,
5193
+ message: "Failed to retrieve channel installations",
5194
+ data: {
5195
+ records: [],
5196
+ total: 0
5197
+ }
5198
+ };
5199
+ }
5200
+ }
5201
+ async function getChannelInstallation(request, reply) {
5202
+ const tenantId = getTenantId9(request);
5203
+ const { installationId } = request.params;
5204
+ try {
5205
+ const store = getInstallationStore();
5206
+ const installation = await store.getInstallationById(installationId);
5207
+ if (!installation) {
5208
+ reply.code(404);
5209
+ return {
5210
+ success: false,
5211
+ message: "Channel installation not found"
5212
+ };
5213
+ }
5214
+ if (installation.tenantId !== tenantId) {
5215
+ reply.code(403);
5216
+ return {
5217
+ success: false,
5218
+ message: "Access denied"
5219
+ };
5220
+ }
5221
+ return {
5222
+ success: true,
5223
+ message: "Channel installation retrieved successfully",
5224
+ data: installation
5225
+ };
5226
+ } catch (error) {
5227
+ console.error("Failed to get channel installation:", error);
5228
+ return {
5229
+ success: false,
5230
+ message: "Failed to retrieve channel installation"
5231
+ };
5232
+ }
5233
+ }
5234
+ async function createChannelInstallation(request, reply) {
5235
+ const tenantId = getTenantId9(request);
5236
+ const body = request.body;
5237
+ try {
5238
+ if (!body.channel) {
5239
+ reply.code(400);
5240
+ return {
5241
+ success: false,
5242
+ message: "Channel type is required"
5243
+ };
5244
+ }
5245
+ if (!body.config) {
5246
+ reply.code(400);
5247
+ return {
5248
+ success: false,
5249
+ message: "Configuration is required"
5250
+ };
5251
+ }
5252
+ if (body.channel === "lark") {
5253
+ const larkConfig = body.config;
5254
+ if (!larkConfig.appId || !larkConfig.appSecret) {
5255
+ reply.code(400);
5256
+ return {
5257
+ success: false,
5258
+ message: "appId and appSecret are required for Lark installations"
5259
+ };
5260
+ }
5261
+ if (!larkConfig.assistantId) {
5262
+ reply.code(400);
5263
+ return {
5264
+ success: false,
5265
+ message: "assistantId is required for Lark installations"
5266
+ };
5267
+ }
5268
+ }
5269
+ const store = getInstallationStore();
5270
+ const installationId = body.id || randomUUID7();
5271
+ const installation = await store.createInstallation(
5272
+ tenantId,
5273
+ installationId,
5274
+ body
5275
+ );
5276
+ reply.code(201);
5277
+ return {
5278
+ success: true,
5279
+ message: "Channel installation created successfully",
5280
+ data: installation
5281
+ };
5282
+ } catch (error) {
5283
+ console.error("Failed to create channel installation:", error);
5284
+ if (error.message?.includes("duplicate") || error.code === "23505") {
5285
+ reply.code(409);
5286
+ return {
5287
+ success: false,
5288
+ message: "Channel installation with this ID already exists"
5289
+ };
5290
+ }
5291
+ return {
5292
+ success: false,
5293
+ message: "Failed to create channel installation"
5294
+ };
5295
+ }
5296
+ }
5297
+ async function updateChannelInstallation(request, reply) {
5298
+ const tenantId = getTenantId9(request);
5299
+ const { installationId } = request.params;
5300
+ const body = request.body;
5301
+ try {
5302
+ const store = getInstallationStore();
5303
+ const existing = await store.getInstallationById(installationId);
5304
+ if (!existing) {
5305
+ reply.code(404);
5306
+ return {
5307
+ success: false,
5308
+ message: "Channel installation not found"
5309
+ };
5310
+ }
5311
+ if (existing.tenantId !== tenantId) {
5312
+ reply.code(403);
5313
+ return {
5314
+ success: false,
5315
+ message: "Access denied"
5316
+ };
5317
+ }
5318
+ const installation = await store.updateInstallation(
5319
+ tenantId,
5320
+ installationId,
5321
+ body
5322
+ );
5323
+ if (!installation) {
5324
+ reply.code(404);
5325
+ return {
5326
+ success: false,
5327
+ message: "Channel installation not found"
5328
+ };
5329
+ }
5330
+ return {
5331
+ success: true,
5332
+ message: "Channel installation updated successfully",
5333
+ data: installation
5334
+ };
5335
+ } catch (error) {
5336
+ console.error("Failed to update channel installation:", error);
5337
+ return {
5338
+ success: false,
5339
+ message: "Failed to update channel installation"
5340
+ };
5341
+ }
5342
+ }
5343
+ async function deleteChannelInstallation(request, reply) {
5344
+ const tenantId = getTenantId9(request);
5345
+ const { installationId } = request.params;
5346
+ try {
5347
+ const store = getInstallationStore();
5348
+ const existing = await store.getInstallationById(installationId);
5349
+ if (!existing) {
5350
+ reply.code(404);
5351
+ return {
5352
+ success: false,
5353
+ message: "Channel installation not found"
5354
+ };
5355
+ }
5356
+ if (existing.tenantId !== tenantId) {
5357
+ reply.code(403);
5358
+ return {
5359
+ success: false,
5360
+ message: "Access denied"
5361
+ };
5362
+ }
5363
+ const deleted = await store.deleteInstallation(tenantId, installationId);
5364
+ if (!deleted) {
5365
+ reply.code(404);
5366
+ return {
5367
+ success: false,
5368
+ message: "Channel installation not found"
5369
+ };
5370
+ }
5371
+ return {
5372
+ success: true,
5373
+ message: "Channel installation deleted successfully"
5374
+ };
5375
+ } catch (error) {
5376
+ console.error("Failed to delete channel installation:", error);
5377
+ return {
5378
+ success: false,
5379
+ message: "Failed to delete channel installation"
5380
+ };
5381
+ }
5382
+ }
5383
+
5384
+ // src/routes/channel-installations.ts
5385
+ function registerChannelInstallationRoutes(app2) {
5386
+ app2.get("/api/channel-installations", getChannelInstallationList);
5387
+ app2.get("/api/channel-installations/:installationId", getChannelInstallation);
5388
+ app2.post("/api/channel-installations", createChannelInstallation);
5389
+ app2.put("/api/channel-installations/:installationId", updateChannelInstallation);
5390
+ app2.delete("/api/channel-installations/:installationId", deleteChannelInstallation);
5391
+ }
5392
+
4881
5393
  // src/routes/index.ts
4882
5394
  var registerLatticeRoutes = (app2) => {
4883
5395
  app2.post("/api/runs", createRun);
@@ -5013,6 +5525,8 @@ var registerLatticeRoutes = (app2) => {
5013
5525
  autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
5014
5526
  allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
5015
5527
  });
5528
+ registerChannelRoutes(app2);
5529
+ registerChannelInstallationRoutes(app2);
5016
5530
  app2.delete(
5017
5531
  "/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
5018
5532
  removePendingMessageHandler
@@ -5082,7 +5596,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
5082
5596
  };
5083
5597
 
5084
5598
  // src/services/agent_task_consumer.ts
5085
- import { eventBus as eventBus2, AGENT_TASK_EVENT, agentInstanceManager as agentInstanceManager5, QueueMode as QueueMode2 } from "@axiom-lattice/core";
5599
+ import { eventBus as eventBus2, AGENT_TASK_EVENT, agentInstanceManager as agentInstanceManager6, QueueMode as QueueMode2 } from "@axiom-lattice/core";
5086
5600
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
5087
5601
  const {
5088
5602
  assistant_id,
@@ -5097,7 +5611,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
5097
5611
  console.log(
5098
5612
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
5099
5613
  );
5100
- const agent = agentInstanceManager5.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
5614
+ const agent = agentInstanceManager6.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
5101
5615
  await agent.addMessage({ input, command, custom_run_config: runConfig }, QueueMode2.STEER);
5102
5616
  if (callback_event) {
5103
5617
  agent.subscribeOnce("message:completed", (evt) => {
@@ -5316,7 +5830,8 @@ import {
5316
5830
  sandboxLatticeManager as sandboxLatticeManager2,
5317
5831
  sqlDatabaseManager as sqlDatabaseManager2,
5318
5832
  getStoreLattice as getStoreLattice12,
5319
- agentInstanceManager as agentInstanceManager6
5833
+ agentInstanceManager as agentInstanceManager7,
5834
+ createSandboxProvider
5320
5835
  } from "@axiom-lattice/core";
5321
5836
  import {
5322
5837
  LoggerType
@@ -5430,6 +5945,22 @@ app.setErrorHandler((error, request, reply) => {
5430
5945
  error: error.message || "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF"
5431
5946
  });
5432
5947
  });
5948
+ function getConfiguredSandboxProvider() {
5949
+ const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox";
5950
+ return createSandboxProvider({
5951
+ type: sandboxProviderType,
5952
+ remoteBaseURL: process.env.SANDBOX_BASE_URL,
5953
+ microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
5954
+ e2bApiKey: process.env.E2B_API_KEY,
5955
+ e2bTemplate: process.env.E2B_TEMPLATE,
5956
+ e2bTimeoutMs: process.env.E2B_TIMEOUT_MS ? parseInt(process.env.E2B_TIMEOUT_MS, 10) : void 0,
5957
+ daytonaApiKey: process.env.DAYTONA_API_KEY,
5958
+ daytonaApiUrl: process.env.DAYTONA_API_URL,
5959
+ daytonaTarget: process.env.DAYTONA_TARGET,
5960
+ daytonaTimeout: process.env.DAYTONA_TIMEOUT ? parseInt(process.env.DAYTONA_TIMEOUT, 10) : void 0,
5961
+ daytonaVolumeName: process.env.DAYTONA_VOLUME_NAME
5962
+ });
5963
+ }
5433
5964
  var start = async (config) => {
5434
5965
  try {
5435
5966
  if (config?.loggerConfig) {
@@ -5453,11 +5984,8 @@ var start = async (config) => {
5453
5984
  logger.warn("Failed to set database config store: " + (error instanceof Error ? error.message : String(error)));
5454
5985
  }
5455
5986
  if (!sandboxLatticeManager2.hasLattice("default")) {
5456
- const sandboxBaseURL = process.env.SANDBOX_BASE_URL || "http://localhost:8080";
5457
- sandboxLatticeManager2.registerLattice("default", {
5458
- baseURL: sandboxBaseURL
5459
- });
5460
- logger.info(`Registered sandbox manager with baseURL: ${sandboxBaseURL}`);
5987
+ sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
5988
+ logger.info("Registered sandbox manager from env configuration");
5461
5989
  }
5462
5990
  const target_port = config?.port || Number(process.env.PORT) || 4001;
5463
5991
  await app.listen({ port: target_port, host: "0.0.0.0" });
@@ -5477,7 +6005,7 @@ var start = async (config) => {
5477
6005
  }
5478
6006
  try {
5479
6007
  logger.info("Starting agent instance recovery...");
5480
- const restoreStats = await agentInstanceManager6.restore();
6008
+ const restoreStats = await agentInstanceManager7.restore();
5481
6009
  logger.info(`Agent recovery complete: ${restoreStats.restored} threads restored, ${restoreStats.errors} errors`);
5482
6010
  } catch (error) {
5483
6011
  logger.error("Agent recovery failed", { error });