@axiom-lattice/gateway 2.1.117 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.turbo/turbo-build.log +12 -12
  2. package/CHANGELOG.md +26 -0
  3. package/dist/{a2a-ERG5RMUW.mjs → a2a-QMHEH3ZT.mjs} +77 -29
  4. package/dist/a2a-QMHEH3ZT.mjs.map +1 -0
  5. package/dist/index.js +1064 -176
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +933 -91
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +6 -5
  10. package/src/__tests__/a2a.test.ts +82 -0
  11. package/src/__tests__/workspace.test.ts +13 -3
  12. package/src/config/local-a2a-templates.ts +59 -0
  13. package/src/controllers/__tests__/assistant-local-a2a.test.ts +147 -0
  14. package/src/controllers/__tests__/local-a2a-templates.test.ts +112 -0
  15. package/src/controllers/assistant.ts +126 -4
  16. package/src/controllers/local-a2a-templates.ts +18 -0
  17. package/src/controllers/local-a2a.ts +149 -0
  18. package/src/controllers/tasks.ts +2 -3
  19. package/src/controllers/workspace.ts +144 -17
  20. package/src/index.ts +1 -9
  21. package/src/routes/__tests__/a2a-test-connection.test.ts +177 -0
  22. package/src/routes/a2a-test-connection.ts +126 -0
  23. package/src/routes/a2a.ts +100 -28
  24. package/src/routes/index.ts +23 -0
  25. package/src/services/__tests__/local-a2a.test.ts +124 -0
  26. package/src/services/eval-runner.ts +6 -0
  27. package/src/services/local-a2a.ts +352 -0
  28. package/src/utils/__tests__/local-a2a-runtime.test.ts +77 -0
  29. package/src/utils/__tests__/path-containment.test.ts +35 -0
  30. package/src/utils/a2a-url.ts +20 -0
  31. package/src/utils/local-a2a-runtime.ts +107 -0
  32. package/src/utils/path-containment.ts +38 -0
  33. package/dist/a2a-ERG5RMUW.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -35,8 +35,8 @@ function getContentTypeFromFilename(filename) {
35
35
  const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";
36
36
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
37
37
  }
38
- function getFilenameFromPath(path3) {
39
- const segments = path3.replace(/\/+$/, "").split("/");
38
+ function getFilenameFromPath(path4) {
39
+ const segments = path4.replace(/\/+$/, "").split("/");
40
40
  return segments[segments.length - 1] || "download";
41
41
  }
42
42
  var EXT_TO_MIME;
@@ -703,17 +703,17 @@ function generateWechatUin() {
703
703
  buf.writeUInt32BE(Math.floor(Math.random() * 4294967295), 0);
704
704
  return buf.toString("base64url");
705
705
  }
706
- async function apiGet(path3, extraHeaders) {
707
- const url = `${getBaseUrl()}${path3}`;
706
+ async function apiGet(path4, extraHeaders) {
707
+ const url = `${getBaseUrl()}${path4}`;
708
708
  const headers = { ...extraHeaders };
709
709
  const res = await fetch(url, { headers });
710
710
  if (!res.ok) {
711
- throw new Error(`iLink GET ${path3} failed: ${res.status} ${res.statusText}`);
711
+ throw new Error(`iLink GET ${path4} failed: ${res.status} ${res.statusText}`);
712
712
  }
713
713
  return res.json();
714
714
  }
715
- async function apiPost(path3, body, botToken, signal) {
716
- const url = `${getBaseUrl()}${path3}`;
715
+ async function apiPost(path4, body, botToken, signal) {
716
+ const url = `${getBaseUrl()}${path4}`;
717
717
  const headers = {
718
718
  "Content-Type": "application/json",
719
719
  "AuthorizationType": "ilink_bot_token",
@@ -727,7 +727,7 @@ async function apiPost(path3, body, botToken, signal) {
727
727
  signal
728
728
  });
729
729
  if (!res.ok) {
730
- throw new Error(`iLink POST ${path3} failed: ${res.status} ${res.statusText}`);
730
+ throw new Error(`iLink POST ${path4} failed: ${res.status} ${res.statusText}`);
731
731
  }
732
732
  return res.json();
733
733
  }
@@ -1031,7 +1031,7 @@ var init_WechatChannelAdapter = __esm({
1031
1031
  installationId,
1032
1032
  error: err instanceof Error ? err.message : String(err)
1033
1033
  });
1034
- await new Promise((resolve) => setTimeout(resolve, reconnectDelay));
1034
+ await new Promise((resolve3) => setTimeout(resolve3, reconnectDelay));
1035
1035
  reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
1036
1036
  }
1037
1037
  scheduleNextPoll();
@@ -1173,14 +1173,16 @@ function buildTextPart(text) {
1173
1173
  function isoNow() {
1174
1174
  return (/* @__PURE__ */ new Date()).toISOString();
1175
1175
  }
1176
- async function streamChunksAsA2A(stream, taskId, sendEvent, onInterrupt) {
1176
+ async function streamChunksAsA2A(stream, taskId, contextId, sendEvent, onInterrupt) {
1177
1177
  let accumulatedText = "";
1178
+ let interrupted = false;
1178
1179
  for await (const chunk of stream) {
1179
1180
  const chunkText = chunk.data?.content || "";
1180
1181
  const chunkType = chunk.type;
1181
- if (chunkType === import_protocols4.MessageChunkTypes.INTERRUPT) {
1182
+ if (chunkType === import_protocols6.MessageChunkTypes.INTERRUPT) {
1182
1183
  sendEvent("status-update", {
1183
1184
  id: taskId,
1185
+ contextId,
1184
1186
  status: {
1185
1187
  state: "input-required",
1186
1188
  message: {
@@ -1192,13 +1194,15 @@ async function streamChunksAsA2A(stream, taskId, sendEvent, onInterrupt) {
1192
1194
  final: false
1193
1195
  });
1194
1196
  onInterrupt?.();
1195
- continue;
1197
+ interrupted = true;
1198
+ break;
1196
1199
  }
1197
- if (chunkType === import_protocols4.MessageChunkTypes.AI || chunkType === import_protocols4.MessageChunkTypes.TOOL) {
1200
+ if (chunkType === import_protocols6.MessageChunkTypes.AI || chunkType === import_protocols6.MessageChunkTypes.TOOL) {
1198
1201
  if (chunkText) {
1199
1202
  accumulatedText += chunkText;
1200
1203
  sendEvent("status-update", {
1201
1204
  id: taskId,
1205
+ contextId,
1202
1206
  status: {
1203
1207
  state: "working",
1204
1208
  message: {
@@ -1212,7 +1216,7 @@ async function streamChunksAsA2A(stream, taskId, sendEvent, onInterrupt) {
1212
1216
  }
1213
1217
  }
1214
1218
  }
1215
- return accumulatedText;
1219
+ return { text: accumulatedText, interrupted };
1216
1220
  }
1217
1221
  async function getTaskStatus(taskId) {
1218
1222
  const record = taskStore.get(taskId);
@@ -1266,13 +1270,48 @@ async function handleTasksSend(request, reply) {
1266
1270
  });
1267
1271
  return;
1268
1272
  }
1273
+ const contextId = body.message.contextId;
1274
+ const referenceTaskIds = body.message.referenceTaskIds;
1269
1275
  const taskId = body.id || (0, import_uuid10.v4)();
1270
- const threadId = (0, import_uuid10.v4)();
1276
+ let threadId;
1277
+ let resolvedReferenceTaskIds;
1278
+ if (body.id) {
1279
+ const existingTask = taskStore.get(body.id);
1280
+ if (existingTask) {
1281
+ if (existingTask.status === "input-required") {
1282
+ threadId = existingTask.threadId;
1283
+ resolvedReferenceTaskIds = referenceTaskIds;
1284
+ } else if (existingTask.status === "completed" || existingTask.status === "failed" || existingTask.status === "canceled") {
1285
+ threadId = (0, import_uuid10.v4)();
1286
+ resolvedReferenceTaskIds = referenceTaskIds ? [...referenceTaskIds, body.id] : [body.id];
1287
+ } else {
1288
+ threadId = (0, import_uuid10.v4)();
1289
+ resolvedReferenceTaskIds = referenceTaskIds;
1290
+ }
1291
+ } else {
1292
+ threadId = (0, import_uuid10.v4)();
1293
+ resolvedReferenceTaskIds = referenceTaskIds ? [...referenceTaskIds, body.id] : [body.id];
1294
+ }
1295
+ } else if (contextId && contextThreadMap.has(contextId)) {
1296
+ threadId = contextThreadMap.get(contextId);
1297
+ console.warn({
1298
+ event: "a2a:deprecated:contextId_thread_reuse",
1299
+ message: "contextId-based thread reuse is deprecated; use referenceTaskIds and application-layer conversation state instead",
1300
+ contextId,
1301
+ threadId
1302
+ });
1303
+ resolvedReferenceTaskIds = referenceTaskIds;
1304
+ } else {
1305
+ threadId = (0, import_uuid10.v4)();
1306
+ resolvedReferenceTaskIds = referenceTaskIds;
1307
+ }
1271
1308
  const tenantId = auth.tenantId || config.defaultTenantId;
1272
1309
  const now = isoNow();
1273
1310
  const record = {
1274
1311
  id: taskId,
1275
1312
  threadId,
1313
+ contextId,
1314
+ referenceTaskIds: resolvedReferenceTaskIds,
1276
1315
  assistantId,
1277
1316
  tenantId,
1278
1317
  projectId: auth.projectId || void 0,
@@ -1290,7 +1329,7 @@ async function handleTasksSend(request, reply) {
1290
1329
  taskStore.set(taskId, record);
1291
1330
  let agent;
1292
1331
  try {
1293
- agent = import_core55.agentInstanceManager.getAgent({
1332
+ agent = import_core56.agentInstanceManager.getAgent({
1294
1333
  assistant_id: assistantId,
1295
1334
  thread_id: threadId,
1296
1335
  tenant_id: tenantId,
@@ -1326,26 +1365,29 @@ data: ${JSON.stringify(data)}
1326
1365
  });
1327
1366
  record.messageId = result.messageId;
1328
1367
  const stream = agent.chunkStream(result.messageId, [
1329
- import_protocols4.MessageChunkTypes.MESSAGE_COMPLETED
1368
+ import_protocols6.MessageChunkTypes.MESSAGE_COMPLETED
1330
1369
  ]);
1331
- const accumulatedText = await streamChunksAsA2A(stream, taskId, sendEvent, () => {
1370
+ const { text: accumulatedText, interrupted } = await streamChunksAsA2A(stream, taskId, contextId, sendEvent, () => {
1332
1371
  record.status = "input-required";
1333
1372
  record.updatedAt = isoNow();
1334
1373
  });
1335
- record.status = "completed";
1336
- record.updatedAt = isoNow();
1337
- record.history.push({
1338
- role: "agent",
1339
- parts: [buildTextPart(accumulatedText)]
1340
- });
1341
- sendEvent("status-update", {
1342
- id: taskId,
1343
- status: {
1344
- state: "completed",
1345
- timestamp: isoNow()
1346
- },
1347
- final: true
1348
- });
1374
+ if (!interrupted) {
1375
+ record.status = "completed";
1376
+ record.updatedAt = isoNow();
1377
+ record.history.push({
1378
+ role: "agent",
1379
+ parts: [buildTextPart(accumulatedText)]
1380
+ });
1381
+ sendEvent("status-update", {
1382
+ id: taskId,
1383
+ contextId,
1384
+ status: {
1385
+ state: "completed",
1386
+ timestamp: isoNow()
1387
+ },
1388
+ final: true
1389
+ });
1390
+ }
1349
1391
  } catch (err) {
1350
1392
  record.status = "failed";
1351
1393
  record.updatedAt = isoNow();
@@ -1355,6 +1397,7 @@ data: ${JSON.stringify(data)}
1355
1397
  });
1356
1398
  sendEvent("status-update", {
1357
1399
  id: taskId,
1400
+ contextId,
1358
1401
  status: {
1359
1402
  state: "failed",
1360
1403
  timestamp: isoNow()
@@ -1382,7 +1425,7 @@ async function handleTasksCancel(request, reply) {
1382
1425
  return;
1383
1426
  }
1384
1427
  try {
1385
- const agent = import_core55.agentInstanceManager.getAgent({
1428
+ const agent = import_core56.agentInstanceManager.getAgent({
1386
1429
  assistant_id: record.assistantId,
1387
1430
  thread_id: record.threadId,
1388
1431
  tenant_id: record.tenantId,
@@ -1419,7 +1462,7 @@ async function handleTasksStream(request, reply) {
1419
1462
  }
1420
1463
  let agent;
1421
1464
  try {
1422
- agent = import_core55.agentInstanceManager.getAgent({
1465
+ agent = import_core56.agentInstanceManager.getAgent({
1423
1466
  assistant_id: record.assistantId,
1424
1467
  thread_id: record.threadId,
1425
1468
  tenant_id: record.tenantId,
@@ -1448,6 +1491,7 @@ data: ${JSON.stringify(data)}
1448
1491
  if (!record.messageId) {
1449
1492
  sendEvent("status-update", {
1450
1493
  id: taskId,
1494
+ contextId: record.contextId,
1451
1495
  status: {
1452
1496
  state: record.status,
1453
1497
  timestamp: record.updatedAt
@@ -1459,17 +1503,20 @@ data: ${JSON.stringify(data)}
1459
1503
  }
1460
1504
  try {
1461
1505
  const stream = agent.chunkStream(record.messageId, [
1462
- import_protocols4.MessageChunkTypes.MESSAGE_COMPLETED
1506
+ import_protocols6.MessageChunkTypes.MESSAGE_COMPLETED
1463
1507
  ]);
1464
- await streamChunksAsA2A(stream, taskId, sendEvent);
1465
- sendEvent("status-update", {
1466
- id: taskId,
1467
- status: {
1468
- state: record.status,
1469
- timestamp: record.updatedAt
1470
- },
1471
- final: true
1472
- });
1508
+ const { interrupted } = await streamChunksAsA2A(stream, taskId, record.contextId, sendEvent);
1509
+ if (!interrupted) {
1510
+ sendEvent("status-update", {
1511
+ id: taskId,
1512
+ contextId: record.contextId,
1513
+ status: {
1514
+ state: record.status,
1515
+ timestamp: record.updatedAt
1516
+ },
1517
+ final: true
1518
+ });
1519
+ }
1473
1520
  } catch (err) {
1474
1521
  sendEvent("error", {
1475
1522
  code: "STREAM_ERROR",
@@ -1614,14 +1661,15 @@ function registerA2ARoutes(app2) {
1614
1661
  handleApiKeyRotate
1615
1662
  );
1616
1663
  }
1617
- var import_uuid10, import_core55, import_protocols4, taskStore, _a2aKeyStore, _storeKeyMap;
1664
+ var import_uuid10, import_core56, import_protocols6, taskStore, contextThreadMap, _a2aKeyStore, _storeKeyMap;
1618
1665
  var init_a2a = __esm({
1619
1666
  "src/routes/a2a.ts"() {
1620
1667
  "use strict";
1621
1668
  import_uuid10 = require("uuid");
1622
- import_core55 = require("@axiom-lattice/core");
1623
- import_protocols4 = require("@axiom-lattice/protocols");
1669
+ import_core56 = require("@axiom-lattice/core");
1670
+ import_protocols6 = require("@axiom-lattice/protocols");
1624
1671
  taskStore = /* @__PURE__ */ new Map();
1672
+ contextThreadMap = /* @__PURE__ */ new Map();
1625
1673
  _a2aKeyStore = null;
1626
1674
  _storeKeyMap = /* @__PURE__ */ new Map();
1627
1675
  }
@@ -1632,12 +1680,12 @@ var resources_exports = {};
1632
1680
  __export(resources_exports, {
1633
1681
  ResourceController: () => ResourceController
1634
1682
  });
1635
- var import_bcryptjs, import_core56, ResourceController;
1683
+ var import_bcryptjs, import_core57, ResourceController;
1636
1684
  var init_resources = __esm({
1637
1685
  "src/controllers/resources.ts"() {
1638
1686
  "use strict";
1639
1687
  import_bcryptjs = __toESM(require("bcryptjs"));
1640
- import_core56 = require("@axiom-lattice/core");
1688
+ import_core57 = require("@axiom-lattice/core");
1641
1689
  init_mime();
1642
1690
  ResourceController = class {
1643
1691
  constructor(deps) {
@@ -1652,14 +1700,14 @@ var init_resources = __esm({
1652
1700
  if (!workspaceId || !projectId) {
1653
1701
  return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
1654
1702
  }
1655
- const payload = (0, import_core56.createSharePayload)(
1703
+ const payload = (0, import_core57.createSharePayload)(
1656
1704
  tenantId,
1657
1705
  workspaceId,
1658
1706
  projectId,
1659
1707
  userId,
1660
1708
  body
1661
1709
  );
1662
- const token = (0, import_core56.generateToken)();
1710
+ const token = (0, import_core57.generateToken)();
1663
1711
  try {
1664
1712
  await this.deps.store.create({ ...payload, token });
1665
1713
  this.deps.logger.info(
@@ -1811,7 +1859,7 @@ var init_resources = __esm({
1811
1859
  resourcePath: subPath
1812
1860
  }, subPath, ext, request, reply);
1813
1861
  }
1814
- const volume = (0, import_core56.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
1862
+ const volume = (0, import_core57.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
1815
1863
  return this._resolveAndServe(
1816
1864
  volume,
1817
1865
  subPath,
@@ -1862,8 +1910,8 @@ var init_resources = __esm({
1862
1910
  return cookie.includes(`share_unlock_${token}=1`);
1863
1911
  }
1864
1912
  /** Returns true if the path has a file extension indicating it's a file, not a directory */
1865
- _isFilePath(path3) {
1866
- return /\.[a-zA-Z0-9]+$/.test(path3);
1913
+ _isFilePath(path4) {
1914
+ return /\.[a-zA-Z0-9]+$/.test(path4);
1867
1915
  }
1868
1916
  /**
1869
1917
  * Shared file resolution: directory → index.html, volume FS → sandbox, base tag, response.
@@ -1994,6 +2042,7 @@ __export(index_exports, {
1994
2042
  LatticeGateway: () => LatticeGateway
1995
2043
  });
1996
2044
  module.exports = __toCommonJS(index_exports);
2045
+ var import_config2 = require("dotenv/config");
1997
2046
  var import_fastify = __toESM(require("fastify"));
1998
2047
  var import_cors = __toESM(require("@fastify/cors"));
1999
2048
  var import_multipart = __toESM(require("@fastify/multipart"));
@@ -2005,8 +2054,374 @@ var import_url = require("url");
2005
2054
 
2006
2055
  // src/controllers/assistant.ts
2007
2056
  var import_core = require("@axiom-lattice/core");
2057
+ var import_protocols = require("@axiom-lattice/protocols");
2008
2058
  var import_crypto = require("crypto");
2009
2059
  var import_core2 = require("@axiom-lattice/core");
2060
+
2061
+ // src/services/local-a2a.ts
2062
+ var import_node_child_process = require("child_process");
2063
+ var import_node_fs = require("fs");
2064
+ var import_node_path = require("path");
2065
+ var import_promises = require("timers/promises");
2066
+ var STDERR_TAIL_LIMIT = 4096;
2067
+ function readBooleanEnv(name, defaultValue, env = process.env) {
2068
+ const value = env[name];
2069
+ if (value === void 0) return defaultValue;
2070
+ return value === "1" || value.toLowerCase() === "true";
2071
+ }
2072
+ function findWorkspaceRoot(startDir) {
2073
+ let current = (0, import_node_path.resolve)(startDir);
2074
+ const { root } = (0, import_node_path.parse)(current);
2075
+ while (current !== root) {
2076
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(current, "pnpm-workspace.yaml"))) {
2077
+ return current;
2078
+ }
2079
+ const parent = (0, import_node_path.dirname)(current);
2080
+ if (parent === current) return null;
2081
+ current = parent;
2082
+ }
2083
+ }
2084
+ function resolveLocalA2AWorkingDirectory(options) {
2085
+ if (options.envWorkdir) return options.envWorkdir;
2086
+ if (options.commandCwd) return options.commandCwd;
2087
+ return findWorkspaceRoot(options.currentCwd) || options.currentCwd;
2088
+ }
2089
+ function validateWorkingDirectory(cwd) {
2090
+ try {
2091
+ const stat2 = (0, import_node_fs.statSync)(cwd);
2092
+ if (!stat2.isDirectory()) {
2093
+ return `Working directory is not a directory: ${cwd}`;
2094
+ }
2095
+ return null;
2096
+ } catch (error) {
2097
+ const message = error instanceof Error ? error.message : String(error);
2098
+ return `Working directory is not accessible: ${cwd} (${message})`;
2099
+ }
2100
+ }
2101
+ function appendStderrTail(current, chunk) {
2102
+ const next = `${current}${chunk}`;
2103
+ if (next.length <= STDERR_TAIL_LIMIT) return next;
2104
+ return next.slice(-STDERR_TAIL_LIMIT);
2105
+ }
2106
+ function formatExitMessage(exitReason, stderrTail) {
2107
+ const stderr = stderrTail.trim();
2108
+ return stderr ? `Exited before becoming ready (${exitReason}): ${stderr}` : `Exited before becoming ready (${exitReason})`;
2109
+ }
2110
+ async function probeReady(agentCardUrl, healthUrl, timeoutMs) {
2111
+ const controller = new AbortController();
2112
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2113
+ try {
2114
+ if (healthUrl) {
2115
+ const resp = await fetch(healthUrl, { signal: controller.signal });
2116
+ if (!resp.ok) return false;
2117
+ }
2118
+ const cardResp = await fetch(agentCardUrl, { signal: controller.signal });
2119
+ return cardResp.ok;
2120
+ } catch {
2121
+ return false;
2122
+ } finally {
2123
+ clearTimeout(timer);
2124
+ }
2125
+ }
2126
+ var _LocalA2AService = class _LocalA2AService {
2127
+ constructor() {
2128
+ this.states = /* @__PURE__ */ new Map();
2129
+ this.autoStartEnabled = readBooleanEnv("LOCAL_A2A_AUTO_START", true);
2130
+ }
2131
+ static getInstance() {
2132
+ if (!_LocalA2AService.instance) {
2133
+ _LocalA2AService.instance = new _LocalA2AService();
2134
+ }
2135
+ return _LocalA2AService.instance;
2136
+ }
2137
+ getOrCreateState(runtimeId, localRuntime) {
2138
+ let state = this.states.get(runtimeId);
2139
+ if (!state) {
2140
+ state = {
2141
+ runtimeId,
2142
+ label: localRuntime.label || runtimeId,
2143
+ status: this.autoStartEnabled ? "stopped" : "disabled",
2144
+ enabled: this.autoStartEnabled,
2145
+ assistantId: runtimeId,
2146
+ agentCardUrl: localRuntime.agentCardUrl,
2147
+ healthUrl: localRuntime.healthUrl,
2148
+ localRuntime
2149
+ };
2150
+ this.states.set(runtimeId, state);
2151
+ } else {
2152
+ state.localRuntime = localRuntime;
2153
+ state.label = localRuntime.label || state.label;
2154
+ state.agentCardUrl = localRuntime.agentCardUrl;
2155
+ state.healthUrl = localRuntime.healthUrl;
2156
+ }
2157
+ return state;
2158
+ }
2159
+ async getRegisteredState(runtimeId, localRuntime) {
2160
+ const state = this.getOrCreateState(runtimeId, localRuntime);
2161
+ if (state.process && state.status === "running") {
2162
+ const alive = await probeReady(
2163
+ localRuntime.agentCardUrl,
2164
+ localRuntime.healthUrl,
2165
+ 2e3
2166
+ );
2167
+ if (!alive) {
2168
+ state.process = void 0;
2169
+ state.pid = void 0;
2170
+ state.status = "stopped";
2171
+ state.message = "Process exited unexpectedly";
2172
+ }
2173
+ }
2174
+ return this.snapshotState(state);
2175
+ }
2176
+ async startProvider(runtimeId, localRuntime) {
2177
+ const state = this.getOrCreateState(runtimeId, localRuntime);
2178
+ if (!state.enabled) {
2179
+ state.status = "disabled";
2180
+ state.message = "Disabled by configuration";
2181
+ return this.snapshotState(state);
2182
+ }
2183
+ if (state.process && state.status === "running") {
2184
+ return this.snapshotState(state);
2185
+ }
2186
+ state.enabled = true;
2187
+ const { executable, args, cwd, env: cmdEnv } = localRuntime.command;
2188
+ const resolvedCwd = resolveLocalA2AWorkingDirectory({
2189
+ envWorkdir: process.env.LOCAL_A2A_WORKDIR || void 0,
2190
+ commandCwd: cwd,
2191
+ currentCwd: process.cwd()
2192
+ });
2193
+ const cwdError = validateWorkingDirectory(resolvedCwd);
2194
+ if (cwdError) {
2195
+ state.process = void 0;
2196
+ state.pid = void 0;
2197
+ state.status = "failed";
2198
+ state.message = cwdError;
2199
+ return this.snapshotState(state);
2200
+ }
2201
+ state.status = "starting";
2202
+ state.message = "Starting";
2203
+ let stderrTail = "";
2204
+ const childEnv = cmdEnv ? { ...process.env, ...cmdEnv } : process.env;
2205
+ const child = (0, import_node_child_process.spawn)(executable, args, {
2206
+ cwd: resolvedCwd,
2207
+ env: childEnv,
2208
+ stdio: ["ignore", "pipe", "pipe"],
2209
+ shell: false
2210
+ });
2211
+ state.process = child;
2212
+ state.pid = child.pid ?? void 0;
2213
+ child.once("error", (error) => {
2214
+ const current = this.states.get(runtimeId);
2215
+ if (!current) return;
2216
+ current.process = void 0;
2217
+ current.pid = void 0;
2218
+ current.status = "failed";
2219
+ const stderr = stderrTail.trim();
2220
+ current.message = stderr ? `Failed to start: ${error.message}: ${stderr}` : `Failed to start: ${error.message}`;
2221
+ });
2222
+ child.stdout.on("data", (chunk) => {
2223
+ const text = chunk.toString("utf8").trim();
2224
+ if (text) console.log(`[local-a2a:${runtimeId}] ${text}`);
2225
+ });
2226
+ child.stderr.on("data", (chunk) => {
2227
+ const rawText = chunk.toString("utf8");
2228
+ stderrTail = appendStderrTail(stderrTail, rawText);
2229
+ const text = rawText.trim();
2230
+ if (text) console.error(`[local-a2a:${runtimeId}] ${text}`);
2231
+ });
2232
+ child.on("close", (code, signal) => {
2233
+ const current = this.states.get(runtimeId);
2234
+ if (!current) return;
2235
+ current.process = void 0;
2236
+ current.pid = void 0;
2237
+ const exitReason = signal ?? code ?? "unknown";
2238
+ if (current.status === "running") {
2239
+ current.status = "stopped";
2240
+ current.message = `Stopped (${exitReason})`;
2241
+ } else if (current.status === "starting") {
2242
+ current.status = "failed";
2243
+ current.message = formatExitMessage(exitReason, stderrTail);
2244
+ }
2245
+ });
2246
+ const ready = await this.waitUntilReady(localRuntime, state, 15e3);
2247
+ if (!ready) {
2248
+ if (state.process) {
2249
+ state.process.kill("SIGTERM");
2250
+ }
2251
+ state.process = void 0;
2252
+ state.pid = void 0;
2253
+ if (state.status !== "failed") {
2254
+ state.status = "failed";
2255
+ state.message = "Server did not become ready";
2256
+ }
2257
+ return this.snapshotState(state);
2258
+ }
2259
+ state.status = "running";
2260
+ state.message = "Running";
2261
+ state.lastStartedAt = (/* @__PURE__ */ new Date()).toISOString();
2262
+ return this.snapshotState(state);
2263
+ }
2264
+ async stopProvider(runtimeId) {
2265
+ const state = this.states.get(runtimeId);
2266
+ if (!state) {
2267
+ return this.emptyState(runtimeId);
2268
+ }
2269
+ const child = state.process;
2270
+ if (child) {
2271
+ child.kill("SIGTERM");
2272
+ await (0, import_promises.setTimeout)(750);
2273
+ if (child.exitCode === null) {
2274
+ child.kill("SIGKILL");
2275
+ }
2276
+ state.process = void 0;
2277
+ }
2278
+ state.pid = void 0;
2279
+ state.status = "stopped";
2280
+ state.message = "Stopped";
2281
+ state.lastStoppedAt = (/* @__PURE__ */ new Date()).toISOString();
2282
+ return this.snapshotState(state);
2283
+ }
2284
+ async restartProvider(runtimeId, localRuntime) {
2285
+ await this.stopProvider(runtimeId);
2286
+ return this.startProvider(runtimeId, localRuntime);
2287
+ }
2288
+ removeState(runtimeId) {
2289
+ const state = this.states.get(runtimeId);
2290
+ if (state?.process) {
2291
+ state.process.kill("SIGTERM");
2292
+ state.process = void 0;
2293
+ }
2294
+ this.states.delete(runtimeId);
2295
+ }
2296
+ async waitUntilReady(localRuntime, state, timeoutMs) {
2297
+ const start2 = Date.now();
2298
+ while (Date.now() - start2 < timeoutMs) {
2299
+ if (state.status === "failed") return false;
2300
+ if (await probeReady(localRuntime.agentCardUrl, localRuntime.healthUrl, 1500)) {
2301
+ return true;
2302
+ }
2303
+ await (0, import_promises.setTimeout)(500);
2304
+ }
2305
+ return false;
2306
+ }
2307
+ emptyState(runtimeId) {
2308
+ return {
2309
+ runtimeId,
2310
+ label: runtimeId,
2311
+ status: "stopped",
2312
+ enabled: false
2313
+ };
2314
+ }
2315
+ snapshotState(state) {
2316
+ return {
2317
+ runtimeId: state.runtimeId,
2318
+ label: state.label,
2319
+ status: state.status,
2320
+ enabled: state.enabled,
2321
+ assistantId: state.assistantId,
2322
+ agentCardUrl: state.agentCardUrl,
2323
+ healthUrl: state.healthUrl,
2324
+ pid: state.pid,
2325
+ message: state.message,
2326
+ lastStartedAt: state.lastStartedAt,
2327
+ lastStoppedAt: state.lastStoppedAt
2328
+ };
2329
+ }
2330
+ };
2331
+ _LocalA2AService.instance = null;
2332
+ var LocalA2AService = _LocalA2AService;
2333
+ var localA2AService = LocalA2AService.getInstance();
2334
+
2335
+ // src/utils/a2a-url.ts
2336
+ var AGENT_CARD_PATH = "/.well-known/agent-card.json";
2337
+ function normalizeA2AAgentCardUrl(value, defaultProtocol = "https://") {
2338
+ const raw = value?.trim() ?? "";
2339
+ if (!raw) return "";
2340
+ const withProtocol = /^https?:\/\//i.test(raw) ? raw : `${defaultProtocol}${raw.replace(/^\/+/, "")}`;
2341
+ const trimmed = withProtocol.replace(/\/+$/, "");
2342
+ if (trimmed.endsWith(AGENT_CARD_PATH)) {
2343
+ return trimmed;
2344
+ }
2345
+ return `${trimmed}${AGENT_CARD_PATH}`;
2346
+ }
2347
+
2348
+ // src/utils/local-a2a-runtime.ts
2349
+ function validateLocalRuntimeCommand(localRuntime) {
2350
+ const runtime = localRuntime;
2351
+ const executable = runtime.command?.executable;
2352
+ if (typeof executable !== "string" || executable.trim().length === 0) {
2353
+ return {
2354
+ ok: false,
2355
+ error: "Local managed A2A command executable is required"
2356
+ };
2357
+ }
2358
+ const args = runtime.command?.args;
2359
+ if (!Array.isArray(args) || !args.every((arg) => typeof arg === "string")) {
2360
+ return {
2361
+ ok: false,
2362
+ error: "Local managed A2A command args must be an array of strings"
2363
+ };
2364
+ }
2365
+ return { ok: true };
2366
+ }
2367
+ function getAgentCardPort(agentCardUrl) {
2368
+ let url;
2369
+ try {
2370
+ url = new URL(agentCardUrl);
2371
+ } catch {
2372
+ return null;
2373
+ }
2374
+ if (url.port) {
2375
+ const port = Number(url.port);
2376
+ return Number.isInteger(port) ? port : null;
2377
+ }
2378
+ if (url.protocol === "http:") return 80;
2379
+ if (url.protocol === "https:") return 443;
2380
+ return null;
2381
+ }
2382
+ function parseCommandPort(args) {
2383
+ for (let index = 0; index < args.length; index += 1) {
2384
+ const arg = args[index];
2385
+ const match = /^(--port|-p)(?:[=\s]+(\d+))?$/.exec(arg);
2386
+ if (!match) continue;
2387
+ const inlinePort = match[2];
2388
+ if (inlinePort) return Number(inlinePort);
2389
+ const nextArg = args[index + 1];
2390
+ if (nextArg && /^\d+$/.test(nextArg)) {
2391
+ return Number(nextArg);
2392
+ }
2393
+ }
2394
+ return null;
2395
+ }
2396
+ function isLoopbackHost(hostname) {
2397
+ const normalized = hostname.toLowerCase();
2398
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
2399
+ }
2400
+ function validateLocalRuntimePort(localRuntime, agentCardUrl) {
2401
+ let url;
2402
+ try {
2403
+ url = new URL(agentCardUrl);
2404
+ } catch {
2405
+ return { ok: true };
2406
+ }
2407
+ if (!isLoopbackHost(url.hostname) || !url.port) {
2408
+ return { ok: true };
2409
+ }
2410
+ const commandPort = parseCommandPort(localRuntime.command.args);
2411
+ if (commandPort === null) {
2412
+ return { ok: true };
2413
+ }
2414
+ const agentCardPort = Number(url.port);
2415
+ if (commandPort === agentCardPort) {
2416
+ return { ok: true };
2417
+ }
2418
+ return {
2419
+ ok: false,
2420
+ error: `Local managed A2A command port (${commandPort}) does not match agentCardUrl port (${agentCardPort}). Update the command args or Agent Card URL so they use the same port.`
2421
+ };
2422
+ }
2423
+
2424
+ // src/controllers/assistant.ts
2010
2425
  function getTenantId(request) {
2011
2426
  const userTenantId = request.user?.tenantId;
2012
2427
  if (userTenantId) {
@@ -2136,6 +2551,79 @@ async function createAssistant(request, reply) {
2136
2551
  });
2137
2552
  }
2138
2553
  const id = data.id ?? (0, import_crypto.randomUUID)();
2554
+ if (data.graphDefinition && data.graphDefinition.type === import_protocols.AgentType.A2A_REMOTE) {
2555
+ const a2aConfig = data.graphDefinition;
2556
+ const warnings = [];
2557
+ const isLocalManaged = !!a2aConfig.localRuntime;
2558
+ a2aConfig.agentCardUrl = normalizeA2AAgentCardUrl(
2559
+ a2aConfig.agentCardUrl,
2560
+ isLocalManaged ? "http://" : "https://"
2561
+ );
2562
+ if (a2aConfig.localRuntime) {
2563
+ const commandValidation = validateLocalRuntimeCommand(
2564
+ a2aConfig.localRuntime
2565
+ );
2566
+ if (!commandValidation.ok) {
2567
+ return reply.status(400).send({
2568
+ success: false,
2569
+ message: commandValidation.error
2570
+ });
2571
+ }
2572
+ a2aConfig.localRuntime.command.executable = a2aConfig.localRuntime.command.executable.trim();
2573
+ a2aConfig.localRuntime.agentCardUrl = normalizeA2AAgentCardUrl(
2574
+ a2aConfig.localRuntime.agentCardUrl || a2aConfig.agentCardUrl,
2575
+ "http://"
2576
+ );
2577
+ a2aConfig.agentCardUrl = a2aConfig.localRuntime.agentCardUrl;
2578
+ const portValidation = validateLocalRuntimePort(
2579
+ a2aConfig.localRuntime,
2580
+ a2aConfig.agentCardUrl
2581
+ );
2582
+ if (!portValidation.ok) {
2583
+ return reply.status(400).send({
2584
+ success: false,
2585
+ message: portValidation.error
2586
+ });
2587
+ }
2588
+ }
2589
+ if (a2aConfig.localRuntime) {
2590
+ const existingAssistants = await (0, import_core.getStoreLattice)("default", "assistant").store.getAllAssistants(tenantId);
2591
+ const targetPort = getAgentCardPort(a2aConfig.localRuntime.agentCardUrl);
2592
+ const duplicate = existingAssistants.find((a) => {
2593
+ if (a.id === id) return false;
2594
+ const gd = a.graphDefinition && typeof a.graphDefinition === "object" ? a.graphDefinition : void 0;
2595
+ if (!gd || gd.type !== import_protocols.AgentType.A2A_REMOTE) return false;
2596
+ const lr = gd.localRuntime;
2597
+ if (!lr) return false;
2598
+ const existingAgentCardUrl = normalizeA2AAgentCardUrl(lr.agentCardUrl, "http://");
2599
+ const existingPort = getAgentCardPort(existingAgentCardUrl);
2600
+ if (targetPort !== null && existingPort !== null) {
2601
+ return existingPort === targetPort;
2602
+ }
2603
+ return existingAgentCardUrl === a2aConfig.localRuntime.agentCardUrl;
2604
+ });
2605
+ if (duplicate) {
2606
+ const conflictDetail = targetPort === null ? `agentCardUrl "${a2aConfig.localRuntime.agentCardUrl}"` : `port ${targetPort}`;
2607
+ return reply.status(409).send({
2608
+ success: false,
2609
+ message: `A local managed A2A assistant using ${conflictDetail} already exists: "${duplicate.name}". Each local managed A2A assistant needs a unique port.`
2610
+ });
2611
+ }
2612
+ }
2613
+ try {
2614
+ const resp = await fetch(a2aConfig.agentCardUrl, {
2615
+ signal: AbortSignal.timeout(5e3)
2616
+ });
2617
+ if (!resp.ok) {
2618
+ warnings.push(`agentCardUrl returned HTTP ${resp.status}`);
2619
+ }
2620
+ } catch {
2621
+ warnings.push(`agentCardUrl is not reachable: ${a2aConfig.agentCardUrl}`);
2622
+ }
2623
+ if (warnings.length > 0) {
2624
+ console.warn(`[createAssistant] A2A warnings for ${a2aConfig.agentCardUrl}:`, warnings);
2625
+ }
2626
+ }
2139
2627
  return upsertAssistant(tenantId, id, data, reply, true);
2140
2628
  }
2141
2629
  async function updateAssistant(request, reply) {
@@ -2149,11 +2637,28 @@ async function deleteAssistant(request, reply) {
2149
2637
  const { id } = request.params;
2150
2638
  const storeLattice = (0, import_core.getStoreLattice)("default", "assistant");
2151
2639
  const assistantStore = storeLattice.store;
2640
+ const storedAssistant = await assistantStore.getAssistantById(tenantId, id);
2641
+ const graphDefinition = storedAssistant?.graphDefinition;
2642
+ const hasLocalRuntime = graphDefinition && typeof graphDefinition === "object" ? !!graphDefinition.localRuntime : false;
2643
+ if (storedAssistant && hasLocalRuntime) {
2644
+ localA2AService.removeState(id);
2645
+ const deleted2 = await assistantStore.deleteAssistant(tenantId, id);
2646
+ if (!deleted2) {
2647
+ return reply.status(500).send({
2648
+ success: false,
2649
+ message: "Failed to delete assistant"
2650
+ });
2651
+ }
2652
+ import_core2.eventBus.publish("assistant:deleted", { id, tenantId });
2653
+ return {
2654
+ success: true,
2655
+ message: "Deleted local A2A assistant"
2656
+ };
2657
+ }
2152
2658
  const agentConfig = await import_core2.agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
2153
2659
  const isCodeConfigured = !!agentConfig;
2154
2660
  if (isCodeConfigured) {
2155
- const exists2 = await assistantStore.hasAssistant(tenantId, id);
2156
- if (!exists2) {
2661
+ if (!storedAssistant) {
2157
2662
  return reply.status(404).send({
2158
2663
  success: false,
2159
2664
  message: "Assistant not found (code-configured assistants cannot be deleted from code, only from store)"
@@ -2166,8 +2671,7 @@ async function deleteAssistant(request, reply) {
2166
2671
  message: "Deleted assistant from store (code-configured registration remains)"
2167
2672
  };
2168
2673
  }
2169
- const exists = await assistantStore.hasAssistant(tenantId, id);
2170
- if (!exists) {
2674
+ if (!storedAssistant) {
2171
2675
  return reply.status(404).send({
2172
2676
  success: false,
2173
2677
  message: "Assistant not found"
@@ -2221,7 +2725,7 @@ var getAgentGraph = async (request, reply) => {
2221
2725
  // src/controllers/run.ts
2222
2726
  var import_uuid = require("uuid");
2223
2727
  var import_core3 = require("@axiom-lattice/core");
2224
- var import_protocols = require("@axiom-lattice/protocols");
2728
+ var import_protocols2 = require("@axiom-lattice/protocols");
2225
2729
  function getUserId2(request) {
2226
2730
  const authUser = request.user;
2227
2731
  if (authUser?.id) return authUser.id;
@@ -2293,13 +2797,13 @@ var createRun = async (request, reply) => {
2293
2797
  command,
2294
2798
  custom_run_config: mergedConfig
2295
2799
  }, mode);
2296
- const stream = agent.chunkStream(result.messageId, [import_protocols.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols.MessageChunkTypes.INTERRUPT, import_protocols.MessageChunkTypes.MESSAGE_FAILED]);
2800
+ const stream = agent.chunkStream(result.messageId, [import_protocols2.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols2.MessageChunkTypes.INTERRUPT, import_protocols2.MessageChunkTypes.MESSAGE_FAILED]);
2297
2801
  for await (const chunk of stream) {
2298
2802
  const success = reply.raw.write(`data: ${JSON.stringify(chunk)}
2299
2803
 
2300
2804
  `);
2301
2805
  if (!success) {
2302
- await new Promise((resolve) => reply.raw.once("drain", resolve));
2806
+ await new Promise((resolve3) => reply.raw.once("drain", resolve3));
2303
2807
  }
2304
2808
  }
2305
2809
  } catch (error) {
@@ -3077,7 +3581,7 @@ var configService = new ConfigService();
3077
3581
 
3078
3582
  // src/services/queue_service.ts
3079
3583
  var import_core8 = require("@axiom-lattice/core");
3080
- var import_protocols2 = require("@axiom-lattice/protocols");
3584
+ var import_protocols3 = require("@axiom-lattice/protocols");
3081
3585
  var import_queue_redis = require("@axiom-lattice/queue-redis");
3082
3586
  var DEFAULT_QUEUE_KEY = "default";
3083
3587
  var queueServiceType = process.env.QUEUE_SERVICE_TYPE || "memory";
@@ -3088,7 +3592,7 @@ var setQueueServiceType = (type) => {
3088
3592
  const config = {
3089
3593
  name: "Default Queue Service",
3090
3594
  description: `Default ${type} queue service`,
3091
- type: type === "redis" ? import_protocols2.QueueType.REDIS : import_protocols2.QueueType.MEMORY,
3595
+ type: type === "redis" ? import_protocols3.QueueType.REDIS : import_protocols3.QueueType.MEMORY,
3092
3596
  queueName,
3093
3597
  options: type === "redis" ? {
3094
3598
  redisUrl: process.env.REDIS_URL,
@@ -4216,7 +4720,7 @@ async function executeSqlQuery(client, body, reply) {
4216
4720
 
4217
4721
  // src/controllers/workflow-tracking.ts
4218
4722
  var import_core15 = require("@axiom-lattice/core");
4219
- var import_protocols3 = require("@axiom-lattice/protocols");
4723
+ var import_protocols4 = require("@axiom-lattice/protocols");
4220
4724
  function getTenantId7(request) {
4221
4725
  const userTenantId = request.user?.tenantId;
4222
4726
  if (userTenantId) return userTenantId;
@@ -4660,7 +5164,7 @@ async function replyInboxTask(request, reply) {
4660
5164
  }).then((result) => {
4661
5165
  const stream = agent.chunkStream(
4662
5166
  result.messageId,
4663
- [import_protocols3.MessageChunkTypes.MESSAGE_COMPLETED]
5167
+ [import_protocols4.MessageChunkTypes.MESSAGE_COMPLETED]
4664
5168
  );
4665
5169
  (async () => {
4666
5170
  for await (const _ of stream) {
@@ -4877,15 +5381,14 @@ function getStore() {
4877
5381
  async function listTasks(request, reply) {
4878
5382
  try {
4879
5383
  const tenantId = getTenantId9(request);
4880
- const userId = getUserId4(request);
4881
5384
  const query = request.query;
4882
5385
  const store2 = getStore();
4883
5386
  const tasks = await store2.list({
4884
5387
  tenantId,
4885
- ownerId: query.ownerId || userId,
4886
5388
  status: query.status,
4887
5389
  priority: query.priority,
4888
- ownerType: query.ownerType,
5390
+ ...query.ownerId && { ownerId: query.ownerId },
5391
+ ...query.ownerType && { ownerType: query.ownerType },
4889
5392
  metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
4890
5393
  projectId: query.projectId,
4891
5394
  limit: query.limit ? parseInt(query.limit) : void 0,
@@ -6021,13 +6524,42 @@ function registerSandboxProxyRoutes(app2) {
6021
6524
  }
6022
6525
 
6023
6526
  // src/controllers/workspace.ts
6024
- var fs = __toESM(require("fs/promises"));
6025
- var path = __toESM(require("path"));
6527
+ var fs2 = __toESM(require("fs/promises"));
6528
+ var path2 = __toESM(require("path"));
6529
+ var os = __toESM(require("os"));
6026
6530
  var import_core24 = require("@axiom-lattice/core");
6027
6531
  var import_core25 = require("@axiom-lattice/core");
6028
6532
  var import_core26 = require("@axiom-lattice/core");
6029
6533
  var import_uuid3 = require("uuid");
6030
6534
  init_mime();
6535
+
6536
+ // src/utils/path-containment.ts
6537
+ var fs = __toESM(require("fs/promises"));
6538
+ var path = __toESM(require("path"));
6539
+ var PathOutsideRootError = class extends Error {
6540
+ constructor(rootPath, targetPath) {
6541
+ super(`Path is outside the allowed root: ${targetPath}`);
6542
+ this.rootPath = rootPath;
6543
+ this.targetPath = targetPath;
6544
+ this.name = "PathOutsideRootError";
6545
+ }
6546
+ };
6547
+ function isPathWithinRoot(rootPath, targetPath) {
6548
+ const relative2 = path.relative(rootPath, targetPath);
6549
+ return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
6550
+ }
6551
+ async function resolvePathWithinRoot(rootPath, targetPath) {
6552
+ const [resolvedRoot, resolvedTarget] = await Promise.all([
6553
+ fs.realpath(rootPath),
6554
+ fs.realpath(targetPath)
6555
+ ]);
6556
+ if (!isPathWithinRoot(resolvedRoot, resolvedTarget)) {
6557
+ throw new PathOutsideRootError(resolvedRoot, resolvedTarget);
6558
+ }
6559
+ return { rootPath: resolvedRoot, targetPath: resolvedTarget };
6560
+ }
6561
+
6562
+ // src/controllers/workspace.ts
6031
6563
  var WorkspaceController = class {
6032
6564
  constructor() {
6033
6565
  this.workspaceStore = (0, import_core24.getStoreLattice)("default", "workspace").store;
@@ -6188,16 +6720,23 @@ var WorkspaceController = class {
6188
6720
  }),
6189
6721
  workspace
6190
6722
  };
6191
- } else {
6192
- console.log(`[getBackend] using FilesystemBackend rootDir=/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`);
6193
- return {
6194
- backend: new import_core25.FilesystemBackend({
6195
- rootDir: `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`,
6196
- virtualMode: true
6197
- }),
6198
- workspace
6199
- };
6200
6723
  }
6724
+ const rootDir = await this.getFilesystemRootDirectory(
6725
+ tenantId,
6726
+ workspaceId,
6727
+ projectId
6728
+ );
6729
+ console.log(`[getBackend] using FilesystemBackend rootDir=${rootDir}`);
6730
+ return {
6731
+ backend: new import_core25.FilesystemBackend({
6732
+ rootDir,
6733
+ virtualMode: true
6734
+ }),
6735
+ workspace
6736
+ };
6737
+ }
6738
+ async getFilesystemRootDirectory(tenantId, workspaceId, projectId) {
6739
+ return `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
6201
6740
  }
6202
6741
  isBinaryContentType(filename) {
6203
6742
  const ext = filename.split(".").pop()?.toLowerCase() || "";
@@ -6386,21 +6925,21 @@ var WorkspaceController = class {
6386
6925
  async listPath(request) {
6387
6926
  const tenantId = this.getTenantId(request);
6388
6927
  const { workspaceId, projectId } = request.params;
6389
- const path3 = request.query.path || "/";
6928
+ const path4 = request.query.path || "/";
6390
6929
  const assistantId = request.query.assistantId;
6391
- console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${path3} assistantId=${assistantId}`);
6392
- const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path3);
6393
- console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${path3})`);
6394
- const files = await backend.lsInfo(path3);
6930
+ console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${path4} assistantId=${assistantId}`);
6931
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path4);
6932
+ console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${path4})`);
6933
+ const files = await backend.lsInfo(path4);
6395
6934
  console.log(`[listPath] result count=${files.length}`);
6396
6935
  return { success: true, data: files };
6397
6936
  }
6398
6937
  async readFile(request) {
6399
6938
  const tenantId = this.getTenantId(request);
6400
6939
  const { workspaceId, projectId } = request.params;
6401
- const { path: path3, offset = 0, limit = 1e3, assistantId } = request.query;
6402
- const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path3);
6403
- const content = await backend.read(path3, Number(offset), Number(limit));
6940
+ const { path: path4, offset = 0, limit = 1e3, assistantId } = request.query;
6941
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path4);
6942
+ const content = await backend.read(path4, Number(offset), Number(limit));
6404
6943
  return { success: true, data: { content, offset, limit } };
6405
6944
  }
6406
6945
  /**
@@ -6412,7 +6951,7 @@ var WorkspaceController = class {
6412
6951
  * - `path`: optional directory path relative to project root (e.g., "docs", "src/utils")
6413
6952
  * - `assistantId`: optional query parameter for sandbox identity matching
6414
6953
  * For sandbox storage, delegates to the sandbox upload API.
6415
- * For filesystem storage, writes directly under /lattice_store/workspaces/{workspaceId}/{projectId}.
6954
+ * For filesystem storage, writes under the same project root used by the file backend.
6416
6955
  */
6417
6956
  async uploadFile(request, reply) {
6418
6957
  const tenantId = this.getTenantId(request);
@@ -6446,7 +6985,7 @@ var WorkspaceController = class {
6446
6985
  workspaceId,
6447
6986
  projectId
6448
6987
  };
6449
- const realPath = pathValue ? path.posix.join(pathValue, filename) : path.posix.join("/project/uploads", filename);
6988
+ const realPath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
6450
6989
  const isBinary = this.isBinaryContentType(filename);
6451
6990
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, realPath);
6452
6991
  if (volumeBackend && !isBinary) {
@@ -6457,7 +6996,7 @@ var WorkspaceController = class {
6457
6996
  const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
6458
6997
  await sandbox.file.uploadFile({ file: realPath, data: buffer });
6459
6998
  }
6460
- const relativePath = pathValue ? path.posix.join(pathValue, filename) : path.posix.join("/project/uploads", filename);
6999
+ const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
6461
7000
  const result2 = {
6462
7001
  path: relativePath,
6463
7002
  name: filename,
@@ -6467,14 +7006,43 @@ var WorkspaceController = class {
6467
7006
  };
6468
7007
  return reply.status(200).send({ success: true, data: result2 });
6469
7008
  }
6470
- const rootDir = `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
6471
- const targetDir = pathValue ? path.join(rootDir, pathValue) : rootDir;
6472
- const targetPath = path.join(targetDir, filename);
6473
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
6474
- await fs.writeFile(targetPath, buffer);
6475
- const stat2 = await fs.stat(targetPath);
7009
+ const rootDir = await this.getFilesystemRootDirectory(
7010
+ tenantId,
7011
+ workspaceId,
7012
+ projectId
7013
+ );
7014
+ await fs2.mkdir(rootDir, { recursive: true });
7015
+ const relativeDirectory = pathValue?.replace(/^[/\\]+/, "") || ".";
7016
+ const targetDir = path2.resolve(rootDir, relativeDirectory);
7017
+ if (!isPathWithinRoot(path2.resolve(rootDir), targetDir)) {
7018
+ return reply.status(400).send({
7019
+ success: false,
7020
+ error: "Upload path must stay within the project directory"
7021
+ });
7022
+ }
7023
+ await fs2.mkdir(targetDir, { recursive: true });
7024
+ const [resolvedRoot, resolvedTargetDir] = await Promise.all([
7025
+ fs2.realpath(rootDir),
7026
+ fs2.realpath(targetDir)
7027
+ ]);
7028
+ if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {
7029
+ return reply.status(400).send({
7030
+ success: false,
7031
+ error: "Upload path must stay within the project directory"
7032
+ });
7033
+ }
7034
+ const safeFilename = path2.basename(filename);
7035
+ if (safeFilename !== filename) {
7036
+ return reply.status(400).send({
7037
+ success: false,
7038
+ error: "Invalid filename"
7039
+ });
7040
+ }
7041
+ const targetPath = path2.join(resolvedTargetDir, safeFilename);
7042
+ await fs2.writeFile(targetPath, buffer);
7043
+ const stat2 = await fs2.stat(targetPath);
6476
7044
  const result = {
6477
- path: pathValue ? `/${pathValue}/${filename}` : `/${filename}`,
7045
+ path: path2.posix.join("/", relativeDirectory, safeFilename),
6478
7046
  name: filename,
6479
7047
  is_dir: false,
6480
7048
  size: stat2.size,
@@ -6482,9 +7050,63 @@ var WorkspaceController = class {
6482
7050
  };
6483
7051
  return reply.status(200).send({ success: true, data: result });
6484
7052
  } catch (error) {
7053
+ const message = error instanceof Error ? error.message : String(error);
6485
7054
  return reply.status(500).send({
6486
7055
  success: false,
6487
- error: `Upload handler error: ${error?.message || String(error)}`
7056
+ error: `Upload handler error: ${message}`
7057
+ });
7058
+ }
7059
+ }
7060
+ /**
7061
+ * Browse local filesystem directories for the folder picker.
7062
+ *
7063
+ * Security constraints:
7064
+ * - Restricted to os.homedir()
7065
+ * - Blacklists system directories
7066
+ * - Returns only directory names, no file contents or hidden files
7067
+ */
7068
+ async browseFilesystem(request, reply) {
7069
+ const homeDir = os.homedir();
7070
+ const requestedPath = request.query.path || homeDir;
7071
+ const SYSTEM_BLACKLIST = ["/etc", "/sys", "/proc", "/var", "/tmp", "/dev"];
7072
+ try {
7073
+ const resolved = await resolvePathWithinRoot(homeDir, requestedPath);
7074
+ const normalized = resolved.targetPath;
7075
+ for (const blacklisted of SYSTEM_BLACKLIST) {
7076
+ if (isPathWithinRoot(blacklisted, normalized)) {
7077
+ return reply.status(403).send({
7078
+ success: false,
7079
+ error: `Cannot browse system directory: ${blacklisted}`
7080
+ });
7081
+ }
7082
+ }
7083
+ const entries = await fs2.readdir(normalized, { withFileTypes: true });
7084
+ const dirEntries = entries.filter((entry) => !entry.name.startsWith(".") && entry.isDirectory()).map((entry) => ({
7085
+ name: entry.name,
7086
+ path: path2.join(normalized, entry.name),
7087
+ isDirectory: true
7088
+ }));
7089
+ const parentPath = path2.dirname(normalized);
7090
+ const hasParent = parentPath !== normalized && isPathWithinRoot(resolved.rootPath, parentPath);
7091
+ return {
7092
+ success: true,
7093
+ data: {
7094
+ path: normalized,
7095
+ parentPath: hasParent ? parentPath : void 0,
7096
+ entries: dirEntries
7097
+ }
7098
+ };
7099
+ } catch (error) {
7100
+ if (error instanceof PathOutsideRootError) {
7101
+ return reply.status(403).send({
7102
+ success: false,
7103
+ error: "Browse restricted to home directory"
7104
+ });
7105
+ }
7106
+ const message = error instanceof Error ? error.message : String(error);
7107
+ return reply.status(400).send({
7108
+ success: false,
7109
+ error: `Cannot read directory: ${message}`
6488
7110
  });
6489
7111
  }
6490
7112
  }
@@ -6545,6 +7167,10 @@ function registerWorkspaceRoutes(app2) {
6545
7167
  "/api/workspaces/:workspaceId/projects/:projectId/viewfile",
6546
7168
  controller.viewFile.bind(controller)
6547
7169
  );
7170
+ app2.get(
7171
+ "/api/filesystem/browse",
7172
+ controller.browseFilesystem.bind(controller)
7173
+ );
6548
7174
  }
6549
7175
 
6550
7176
  // src/controllers/database-configs.ts
@@ -6761,7 +7387,7 @@ async function testDatabaseConnection(request, reply) {
6761
7387
  await db.connect();
6762
7388
  await db.listTables();
6763
7389
  const latency = Date.now() - startTime;
6764
- await new Promise((resolve) => setTimeout(resolve, 100));
7390
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
6765
7391
  await db.disconnect();
6766
7392
  await import_core27.sqlDatabaseManager.removeDatabase(tenantId, testKey);
6767
7393
  return {
@@ -7650,6 +8276,9 @@ var EvalRunner = class {
7650
8276
  const runId = (0, import_uuid4.v4)();
7651
8277
  const concurrency = Math.max(1, Math.floor(project.concurrency) || 3);
7652
8278
  await store2.createRun(tenantId, projectId, runId, { totalCases, concurrency });
8279
+ const tsc = project.targetServerConfig ?? {};
8280
+ const workspaceId = tsc.workspace_id || "";
8281
+ const projectIdFromConfig = tsc.project_id || "";
7653
8282
  const projectConfig = {
7654
8283
  projectName: project.name,
7655
8284
  version: project.version,
@@ -7657,7 +8286,9 @@ var EvalRunner = class {
7657
8286
  suites: evalSuites,
7658
8287
  judge_agent_config: { modelKey: resolvedJudgeModelKey },
7659
8288
  lattice_server_config: {
7660
- tenant_id: tenantId
8289
+ tenant_id: tenantId,
8290
+ workspace_id: workspaceId,
8291
+ project_id: projectIdFromConfig
7661
8292
  },
7662
8293
  concurrency
7663
8294
  };
@@ -9072,8 +9703,8 @@ function getTenantId15(request) {
9072
9703
  return request.headers["x-tenant-id"] || "default";
9073
9704
  }
9074
9705
  async function getInstallationStore() {
9075
- const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
9076
- const store2 = getStoreLattice31("default", "channelInstallation").store;
9706
+ const { getStoreLattice: getStoreLattice32 } = await import("@axiom-lattice/core");
9707
+ const store2 = getStoreLattice32("default", "channelInstallation").store;
9077
9708
  if (store2) return store2;
9078
9709
  const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
9079
9710
  const databaseUrl = process.env.DATABASE_URL;
@@ -9554,8 +10185,8 @@ var log = {
9554
10185
  };
9555
10186
  var connections = /* @__PURE__ */ new Map();
9556
10187
  var pending = /* @__PURE__ */ new Map();
9557
- function getAgentIdFromPath(path3) {
9558
- const match = path3.match(/^\/api\/a2a\/bridge\/proxy\/([^/]+)/);
10188
+ function getAgentIdFromPath(path4) {
10189
+ const match = path4.match(/^\/api\/a2a\/bridge\/proxy\/([^/]+)/);
9559
10190
  return match ? match[1] : null;
9560
10191
  }
9561
10192
  function handleBridgeConnection(socket, _request) {
@@ -9663,12 +10294,12 @@ async function handleBridgeProxy(request, reply) {
9663
10294
  };
9664
10295
  const timeout = 3e5;
9665
10296
  try {
9666
- const response = await new Promise((resolve, reject) => {
10297
+ const response = await new Promise((resolve3, reject) => {
9667
10298
  const timer = setTimeout(() => {
9668
10299
  pending.delete(requestId);
9669
10300
  reject(new Error(`Bridge request timeout after ${timeout}ms`));
9670
10301
  }, timeout);
9671
- pending.set(requestId, { resolve, reject, timer, agentId });
10302
+ pending.set(requestId, { resolve: resolve3, reject, timer, agentId });
9672
10303
  try {
9673
10304
  if (conn.ws.readyState !== conn.ws.OPEN) {
9674
10305
  pending.delete(requestId);
@@ -9791,10 +10422,272 @@ var registerSTTRoutes = (app2) => {
9791
10422
  );
9792
10423
  };
9793
10424
 
10425
+ // src/controllers/local-a2a.ts
10426
+ var import_core39 = require("@axiom-lattice/core");
10427
+ var import_protocols5 = require("@axiom-lattice/protocols");
10428
+ function getTenantId18(request) {
10429
+ const userTenantId = request.user?.tenantId;
10430
+ if (userTenantId) return userTenantId;
10431
+ return request.headers["x-tenant-id"] || "default";
10432
+ }
10433
+ function getLocalRuntime(assistant) {
10434
+ const graphDefinition = assistant.graphDefinition;
10435
+ if (!graphDefinition || typeof graphDefinition !== "object") return void 0;
10436
+ if (graphDefinition.type !== import_protocols5.AgentType.A2A_REMOTE) return void 0;
10437
+ return graphDefinition.localRuntime;
10438
+ }
10439
+ async function getLocalA2AAssistants(tenantId) {
10440
+ const storeLattice = (0, import_core39.getStoreLattice)("default", "assistant");
10441
+ const assistants = await storeLattice.store.getAllAssistants(tenantId);
10442
+ const results = [];
10443
+ for (const assistant of assistants) {
10444
+ const runtime = getLocalRuntime(assistant);
10445
+ if (runtime) {
10446
+ results.push({ assistant, localRuntime: runtime });
10447
+ }
10448
+ }
10449
+ return results;
10450
+ }
10451
+ async function getLocalA2AAssistant(tenantId, assistantId) {
10452
+ const storeLattice = (0, import_core39.getStoreLattice)("default", "assistant");
10453
+ const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
10454
+ if (!assistant) return null;
10455
+ const runtime = getLocalRuntime(assistant);
10456
+ if (!runtime) return null;
10457
+ return { assistant, localRuntime: runtime };
10458
+ }
10459
+ async function requireManagedAssistant(request, reply) {
10460
+ const assistantId = request.params.assistantId;
10461
+ if (!assistantId) {
10462
+ reply.status(400).send({ success: false, error: "assistantId is required" });
10463
+ return null;
10464
+ }
10465
+ const registered = await getLocalA2AAssistant(getTenantId18(request), assistantId);
10466
+ if (!registered) {
10467
+ reply.status(404).send({
10468
+ success: false,
10469
+ error: `Assistant ${assistantId} is not a local managed A2A assistant`
10470
+ });
10471
+ return null;
10472
+ }
10473
+ return { assistantId, localRuntime: registered.localRuntime };
10474
+ }
10475
+ async function getLocalA2AStatus(request, reply) {
10476
+ const tenantId = getTenantId18(request);
10477
+ const managed = await getLocalA2AAssistants(tenantId);
10478
+ const states = await Promise.all(
10479
+ managed.map(
10480
+ ({ assistant, localRuntime }) => localA2AService.getRegisteredState(assistant.id, localRuntime)
10481
+ )
10482
+ );
10483
+ reply.send({ success: true, data: { records: states, total: states.length } });
10484
+ }
10485
+ async function getLocalA2AProvider(request, reply) {
10486
+ const registered = await requireManagedAssistant(request, reply);
10487
+ if (!registered) return;
10488
+ const state = await localA2AService.getRegisteredState(
10489
+ registered.assistantId,
10490
+ registered.localRuntime
10491
+ );
10492
+ reply.send({ success: true, data: state });
10493
+ }
10494
+ async function startLocalA2AProvider(request, reply) {
10495
+ const registered = await requireManagedAssistant(request, reply);
10496
+ if (!registered) return;
10497
+ const state = await localA2AService.startProvider(
10498
+ registered.assistantId,
10499
+ registered.localRuntime
10500
+ );
10501
+ reply.send({ success: true, data: state });
10502
+ }
10503
+ async function stopLocalA2AProvider(request, reply) {
10504
+ const registered = await requireManagedAssistant(request, reply);
10505
+ if (!registered) return;
10506
+ const state = await localA2AService.stopProvider(registered.assistantId);
10507
+ reply.send({ success: true, data: state });
10508
+ }
10509
+ async function restartLocalA2AProvider(request, reply) {
10510
+ const registered = await requireManagedAssistant(request, reply);
10511
+ if (!registered) return;
10512
+ const state = await localA2AService.restartProvider(
10513
+ registered.assistantId,
10514
+ registered.localRuntime
10515
+ );
10516
+ reply.send({ success: true, data: state });
10517
+ }
10518
+
10519
+ // src/config/local-a2a-templates.ts
10520
+ var import_cli_path = require("@axiom-lattice/cli-a2a/cli-path");
10521
+ function buildCliA2ACommand(provider, agentName, agentDescription) {
10522
+ return {
10523
+ executable: process.execPath,
10524
+ args: [
10525
+ import_cli_path.cliA2AEntryPath,
10526
+ "--provider",
10527
+ provider,
10528
+ "--hostname",
10529
+ "localhost",
10530
+ "--advertise-host",
10531
+ "localhost",
10532
+ "--agent-name",
10533
+ agentName,
10534
+ "--agent-description",
10535
+ agentDescription,
10536
+ "--log-level",
10537
+ "info"
10538
+ ]
10539
+ };
10540
+ }
10541
+ var LOCAL_A2A_TEMPLATES = [
10542
+ {
10543
+ key: "claude",
10544
+ name: "Claude Code",
10545
+ command: buildCliA2ACommand(
10546
+ "claude",
10547
+ "Claude Code",
10548
+ "Local Claude Code wrapper exposed through A2A."
10549
+ )
10550
+ },
10551
+ {
10552
+ key: "codex",
10553
+ name: "Codex",
10554
+ command: buildCliA2ACommand(
10555
+ "codex",
10556
+ "Codex",
10557
+ "Local Codex wrapper exposed through A2A."
10558
+ )
10559
+ },
10560
+ {
10561
+ key: "opencode",
10562
+ name: "OpenCode",
10563
+ command: buildCliA2ACommand(
10564
+ "opencode",
10565
+ "OpenCode",
10566
+ "Local OpenCode wrapper exposed through A2A."
10567
+ )
10568
+ }
10569
+ ];
10570
+
10571
+ // src/controllers/local-a2a-templates.ts
10572
+ async function getLocalA2ATemplates(_request, reply) {
10573
+ reply.send({
10574
+ success: true,
10575
+ data: {
10576
+ records: LOCAL_A2A_TEMPLATES,
10577
+ total: LOCAL_A2A_TEMPLATES.length
10578
+ }
10579
+ });
10580
+ }
10581
+
10582
+ // src/routes/a2a-test-connection.ts
10583
+ var import_node_crypto = require("crypto");
10584
+ function describeTestConnectionError(error, agentCardUrl) {
10585
+ if (!(error instanceof Error)) {
10586
+ return `Failed to fetch agent card from ${agentCardUrl}: ${String(error)}`;
10587
+ }
10588
+ const cause = error.cause;
10589
+ const causeParts = [];
10590
+ if (cause instanceof Error) {
10591
+ causeParts.push(cause.message);
10592
+ const causeCode = cause.code;
10593
+ if (causeCode && !causeParts.some((part) => part.includes(causeCode))) {
10594
+ causeParts.push(causeCode);
10595
+ }
10596
+ } else if (typeof cause === "object" && cause !== null) {
10597
+ const code = "code" in cause ? String(cause.code) : "";
10598
+ const message = "message" in cause ? String(cause.message) : "";
10599
+ if (message) causeParts.push(message);
10600
+ if (code && !message.includes(code)) causeParts.push(code);
10601
+ }
10602
+ const detail = causeParts.length > 0 ? ` (${causeParts.join(", ")})` : "";
10603
+ return `Failed to fetch agent card from ${agentCardUrl}: ${error.message}${detail}`;
10604
+ }
10605
+ async function ensureLocalRuntimeForConnectionTest(localRuntime, agentCardUrl) {
10606
+ const commandValidation = validateLocalRuntimeCommand(localRuntime);
10607
+ if (!commandValidation.ok) {
10608
+ return { error: commandValidation.error };
10609
+ }
10610
+ const testRuntime = {
10611
+ ...localRuntime,
10612
+ agentCardUrl,
10613
+ command: {
10614
+ ...localRuntime.command,
10615
+ executable: localRuntime.command.executable.trim()
10616
+ }
10617
+ };
10618
+ const portValidation = validateLocalRuntimePort(testRuntime, agentCardUrl);
10619
+ if (!portValidation.ok) {
10620
+ return { error: portValidation.error };
10621
+ }
10622
+ const state = await localA2AService.startProvider(
10623
+ `test-${(0, import_node_crypto.randomUUID)()}`,
10624
+ testRuntime
10625
+ );
10626
+ if (state.status === "running") return { runtimeId: state.runtimeId };
10627
+ const detail = state.message ? ` ${state.message}` : "";
10628
+ localA2AService.removeState(state.runtimeId);
10629
+ return { error: `Local managed A2A server could not start for connection test.${detail}` };
10630
+ }
10631
+ async function readShortResponseBody(resp) {
10632
+ const body = await resp.text().catch(() => "");
10633
+ return body.length > 500 ? `${body.slice(0, 500)}...` : body;
10634
+ }
10635
+ function registerA2ATestConnectionRoute(app2) {
10636
+ app2.post("/api/a2a/test-connection", async (request, reply) => {
10637
+ const { agentCardUrl, apiKey, localRuntime } = request.body ?? {};
10638
+ const normalizedAgentCardUrl = normalizeA2AAgentCardUrl(
10639
+ agentCardUrl,
10640
+ localRuntime ? "http://" : "https://"
10641
+ );
10642
+ if (!normalizedAgentCardUrl) {
10643
+ return reply.status(400).send({ success: false, error: "agentCardUrl is required" });
10644
+ }
10645
+ let testRuntimeId = null;
10646
+ try {
10647
+ const headers = {};
10648
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
10649
+ if (localRuntime) {
10650
+ const localStartResult = await ensureLocalRuntimeForConnectionTest(localRuntime, normalizedAgentCardUrl);
10651
+ if ("error" in localStartResult) {
10652
+ return reply.send({ success: true, data: { reachable: false, error: localStartResult.error } });
10653
+ }
10654
+ testRuntimeId = localStartResult.runtimeId;
10655
+ }
10656
+ const resp = await fetch(normalizedAgentCardUrl, { headers, signal: AbortSignal.timeout(8e3) });
10657
+ if (!resp.ok) {
10658
+ const body = await readShortResponseBody(resp);
10659
+ const suffix = body ? `: ${body}` : "";
10660
+ return reply.send({ success: true, data: { reachable: false, error: `HTTP ${resp.status} ${resp.statusText}${suffix}` } });
10661
+ }
10662
+ const card = await resp.json();
10663
+ return reply.send({ success: true, data: { reachable: true, agentName: card.name, agentCard: card } });
10664
+ } catch (error) {
10665
+ return reply.send({
10666
+ success: true,
10667
+ data: {
10668
+ reachable: false,
10669
+ error: describeTestConnectionError(error, normalizedAgentCardUrl)
10670
+ }
10671
+ });
10672
+ } finally {
10673
+ if (testRuntimeId) {
10674
+ localA2AService.removeState(testRuntimeId);
10675
+ }
10676
+ }
10677
+ });
10678
+ }
10679
+
9794
10680
  // src/routes/index.ts
9795
10681
  var registerLatticeRoutes = (app2, channelDeps) => {
9796
10682
  registerA2ABridgeRoutes(app2);
9797
10683
  registerSTTRoutes(app2);
10684
+ app2.get("/api/local-a2a", getLocalA2AStatus);
10685
+ app2.get("/api/local-a2a/templates", getLocalA2ATemplates);
10686
+ app2.get("/api/local-a2a/:assistantId", getLocalA2AProvider);
10687
+ app2.post("/api/local-a2a/:assistantId/start", startLocalA2AProvider);
10688
+ app2.post("/api/local-a2a/:assistantId/stop", stopLocalA2AProvider);
10689
+ app2.post("/api/local-a2a/:assistantId/restart", restartLocalA2AProvider);
10690
+ registerA2ATestConnectionRoute(app2);
9798
10691
  app2.post("/api/runs", createRun);
9799
10692
  app2.post("/api/resume_stream", resumeStream);
9800
10693
  app2.post("/api/assistants/:assistantId/threads/:threadId/abort", abortRun);
@@ -10086,12 +10979,12 @@ function registerResourceRoutes(app2, controller) {
10086
10979
  }
10087
10980
 
10088
10981
  // src/export_registrations/index.ts
10089
- var import_core52 = require("@axiom-lattice/core");
10982
+ var import_core53 = require("@axiom-lattice/core");
10090
10983
 
10091
10984
  // src/export_registrations/skill.registration.ts
10092
- var import_core39 = require("@axiom-lattice/core");
10985
+ var import_core40 = require("@axiom-lattice/core");
10093
10986
  function getStore2() {
10094
- return (0, import_core39.getStoreLattice)("default", "skill").store;
10987
+ return (0, import_core40.getStoreLattice)("default", "skill").store;
10095
10988
  }
10096
10989
  var skillRegistration = {
10097
10990
  entityType: "skill",
@@ -10166,9 +11059,9 @@ var skillRegistration = {
10166
11059
  };
10167
11060
 
10168
11061
  // src/export_registrations/agent.registration.ts
10169
- var import_core40 = require("@axiom-lattice/core");
11062
+ var import_core41 = require("@axiom-lattice/core");
10170
11063
  function getStore3() {
10171
- return (0, import_core40.getStoreLattice)("default", "assistant").store;
11064
+ return (0, import_core41.getStoreLattice)("default", "assistant").store;
10172
11065
  }
10173
11066
  var agentRegistration = {
10174
11067
  entityType: "agent",
@@ -10235,9 +11128,9 @@ var agentRegistration = {
10235
11128
  };
10236
11129
 
10237
11130
  // src/export_registrations/binding.registration.ts
10238
- var import_core41 = require("@axiom-lattice/core");
11131
+ var import_core42 = require("@axiom-lattice/core");
10239
11132
  function getStore4() {
10240
- return (0, import_core41.getStoreLattice)("default", "channelBinding").store;
11133
+ return (0, import_core42.getStoreLattice)("default", "channelBinding").store;
10241
11134
  }
10242
11135
  var bindingRegistration = {
10243
11136
  entityType: "binding",
@@ -10325,9 +11218,9 @@ var bindingRegistration = {
10325
11218
  };
10326
11219
 
10327
11220
  // src/export_registrations/database-config.registration.ts
10328
- var import_core42 = require("@axiom-lattice/core");
11221
+ var import_core43 = require("@axiom-lattice/core");
10329
11222
  function getStore5() {
10330
- return (0, import_core42.getStoreLattice)("default", "database").store;
11223
+ return (0, import_core43.getStoreLattice)("default", "database").store;
10331
11224
  }
10332
11225
  var databaseConfigRegistration = {
10333
11226
  entityType: "database_config",
@@ -10407,9 +11300,9 @@ var databaseConfigRegistration = {
10407
11300
  };
10408
11301
 
10409
11302
  // src/export_registrations/metrics-config.registration.ts
10410
- var import_core43 = require("@axiom-lattice/core");
11303
+ var import_core44 = require("@axiom-lattice/core");
10411
11304
  function getStore6() {
10412
- return (0, import_core43.getStoreLattice)("default", "metrics").store;
11305
+ return (0, import_core44.getStoreLattice)("default", "metrics").store;
10413
11306
  }
10414
11307
  var metricsConfigRegistration = {
10415
11308
  entityType: "metrics_config",
@@ -10489,9 +11382,9 @@ var metricsConfigRegistration = {
10489
11382
  };
10490
11383
 
10491
11384
  // src/export_registrations/mcp-config.registration.ts
10492
- var import_core44 = require("@axiom-lattice/core");
11385
+ var import_core45 = require("@axiom-lattice/core");
10493
11386
  function getStore7() {
10494
- return (0, import_core44.getStoreLattice)("default", "mcp").store;
11387
+ return (0, import_core45.getStoreLattice)("default", "mcp").store;
10495
11388
  }
10496
11389
  var mcpConfigRegistration = {
10497
11390
  entityType: "mcp_config",
@@ -10574,9 +11467,9 @@ var mcpConfigRegistration = {
10574
11467
  };
10575
11468
 
10576
11469
  // src/export_registrations/connection.registration.ts
10577
- var import_core45 = require("@axiom-lattice/core");
11470
+ var import_core46 = require("@axiom-lattice/core");
10578
11471
  function getStore8() {
10579
- return (0, import_core45.getStoreLattice)("default", "connection").store;
11472
+ return (0, import_core46.getStoreLattice)("default", "connection").store;
10580
11473
  }
10581
11474
  async function listAllConnections(tenantId) {
10582
11475
  const store2 = getStore8();
@@ -10658,9 +11551,9 @@ var connectionRegistration = {
10658
11551
  };
10659
11552
 
10660
11553
  // src/export_registrations/collection.registration.ts
10661
- var import_core46 = require("@axiom-lattice/core");
11554
+ var import_core47 = require("@axiom-lattice/core");
10662
11555
  function getStore9() {
10663
- return (0, import_core46.getStoreLattice)("default", "collection").store;
11556
+ return (0, import_core47.getStoreLattice)("default", "collection").store;
10664
11557
  }
10665
11558
  var collectionRegistration = {
10666
11559
  entityType: "collection",
@@ -10731,9 +11624,9 @@ var collectionRegistration = {
10731
11624
  };
10732
11625
 
10733
11626
  // src/export_registrations/channel-installation.registration.ts
10734
- var import_core47 = require("@axiom-lattice/core");
11627
+ var import_core48 = require("@axiom-lattice/core");
10735
11628
  function getStore10() {
10736
- return (0, import_core47.getStoreLattice)("default", "channelInstallation").store;
11629
+ return (0, import_core48.getStoreLattice)("default", "channelInstallation").store;
10737
11630
  }
10738
11631
  var channelInstallationRegistration = {
10739
11632
  entityType: "channel_installation",
@@ -10808,9 +11701,9 @@ var channelInstallationRegistration = {
10808
11701
  };
10809
11702
 
10810
11703
  // src/export_registrations/menu.registration.ts
10811
- var import_core48 = require("@axiom-lattice/core");
11704
+ var import_core49 = require("@axiom-lattice/core");
10812
11705
  function getStore11() {
10813
- return (0, import_core48.getStoreLattice)("default", "menu").store;
11706
+ return (0, import_core49.getStoreLattice)("default", "menu").store;
10814
11707
  }
10815
11708
  var menuRegistration = {
10816
11709
  entityType: "menu",
@@ -10888,9 +11781,9 @@ var menuRegistration = {
10888
11781
  };
10889
11782
 
10890
11783
  // src/export_registrations/task.registration.ts
10891
- var import_core49 = require("@axiom-lattice/core");
11784
+ var import_core50 = require("@axiom-lattice/core");
10892
11785
  function getStore12() {
10893
- return (0, import_core49.getStoreLattice)("default", "task").store;
11786
+ return (0, import_core50.getStoreLattice)("default", "task").store;
10894
11787
  }
10895
11788
  var taskRegistration = {
10896
11789
  entityType: "task",
@@ -10976,9 +11869,9 @@ var taskRegistration = {
10976
11869
  };
10977
11870
 
10978
11871
  // src/export_registrations/a2a-api-key.registration.ts
10979
- var import_core50 = require("@axiom-lattice/core");
11872
+ var import_core51 = require("@axiom-lattice/core");
10980
11873
  function getStore13() {
10981
- return (0, import_core50.getStoreLattice)("default", "a2aApiKey").store;
11874
+ return (0, import_core51.getStoreLattice)("default", "a2aApiKey").store;
10982
11875
  }
10983
11876
  var a2aApiKeyRegistration = {
10984
11877
  entityType: "a2a_api_key",
@@ -11049,9 +11942,9 @@ var a2aApiKeyRegistration = {
11049
11942
  };
11050
11943
 
11051
11944
  // src/export_registrations/eval.registration.ts
11052
- var import_core51 = require("@axiom-lattice/core");
11945
+ var import_core52 = require("@axiom-lattice/core");
11053
11946
  function getStore14() {
11054
- return (0, import_core51.getStoreLattice)("default", "eval").store;
11947
+ return (0, import_core52.getStoreLattice)("default", "eval").store;
11055
11948
  }
11056
11949
  var evalRegistration = {
11057
11950
  entityType: "eval",
@@ -11377,7 +12270,7 @@ var evalRegistration = {
11377
12270
 
11378
12271
  // src/export_registrations/index.ts
11379
12272
  function registerAllBuiltinEntities() {
11380
- const registry = import_core52.ExportableEntityRegistry.getInstance();
12273
+ const registry = import_core53.ExportableEntityRegistry.getInstance();
11381
12274
  registry.register(skillRegistration);
11382
12275
  registry.register(agentRegistration);
11383
12276
  registry.register(bindingRegistration);
@@ -11394,7 +12287,7 @@ function registerAllBuiltinEntities() {
11394
12287
  }
11395
12288
 
11396
12289
  // src/router/MessageRouter.ts
11397
- var import_core53 = require("@axiom-lattice/core");
12290
+ var import_core54 = require("@axiom-lattice/core");
11398
12291
  var import_crypto10 = require("crypto");
11399
12292
  var BindingNotFoundError = class extends Error {
11400
12293
  constructor(message) {
@@ -11555,7 +12448,7 @@ var MessageRouter = class {
11555
12448
  channel: message.channel,
11556
12449
  adapterChannel: adapter.channel
11557
12450
  }, "Thread resolved by adapter strategy");
11558
- const threadStore = (0, import_core53.getStoreLattice)("default", "thread").store;
12451
+ const threadStore = (0, import_core54.getStoreLattice)("default", "thread").store;
11559
12452
  try {
11560
12453
  await threadStore.createThread(
11561
12454
  tenantId,
@@ -11592,7 +12485,7 @@ var MessageRouter = class {
11592
12485
  }
11593
12486
  }
11594
12487
  if (!threadId) {
11595
- const threadStore = (0, import_core53.getStoreLattice)("default", "thread").store;
12488
+ const threadStore = (0, import_core54.getStoreLattice)("default", "thread").store;
11596
12489
  const newThreadId = (0, import_crypto10.randomUUID)();
11597
12490
  console.log({
11598
12491
  event: "dispatch:thread:create",
@@ -11632,7 +12525,7 @@ var MessageRouter = class {
11632
12525
  senderId: message.sender.id,
11633
12526
  contentLength: message.content.text.length
11634
12527
  }, "Dispatching to agent");
11635
- const agent = import_core53.agentInstanceManager.getAgent({
12528
+ const agent = import_core54.agentInstanceManager.getAgent({
11636
12529
  tenant_id: tenantId,
11637
12530
  assistant_id: agentId,
11638
12531
  thread_id: threadId,
@@ -11877,7 +12770,7 @@ function createAuditLoggerMiddleware() {
11877
12770
  }
11878
12771
 
11879
12772
  // src/index.ts
11880
- var import_core57 = require("@axiom-lattice/core");
12773
+ var import_core58 = require("@axiom-lattice/core");
11881
12774
 
11882
12775
  // src/swagger.ts
11883
12776
  var import_swagger = __toESM(require("@fastify/swagger"));
@@ -11942,7 +12835,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
11942
12835
  };
11943
12836
 
11944
12837
  // src/services/agent_task_consumer.ts
11945
- var import_core54 = require("@axiom-lattice/core");
12838
+ var import_core55 = require("@axiom-lattice/core");
11946
12839
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
11947
12840
  const {
11948
12841
  assistant_id,
@@ -11960,17 +12853,17 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
11960
12853
  console.log(
11961
12854
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
11962
12855
  );
11963
- const agent = import_core54.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
11964
- await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core54.QueueMode.STEER);
12856
+ const agent = import_core55.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
12857
+ await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core55.QueueMode.STEER);
11965
12858
  if (callback_event) {
11966
12859
  agent.subscribeOnce("message:completed", (evt) => {
11967
- import_core54.eventBus.publish(callback_event, {
12860
+ import_core55.eventBus.publish(callback_event, {
11968
12861
  success: true,
11969
12862
  state: evt.state
11970
12863
  });
11971
12864
  if (main_thread_id && main_tenant_id) {
11972
12865
  try {
11973
- const mainAgent = import_core54.agentInstanceManager.getAgent({
12866
+ const mainAgent = import_core55.agentInstanceManager.getAgent({
11974
12867
  assistant_id: main_assistant_id ?? assistant_id,
11975
12868
  thread_id: main_thread_id,
11976
12869
  tenant_id: main_tenant_id,
@@ -12000,7 +12893,7 @@ ${summary}`
12000
12893
  }
12001
12894
  });
12002
12895
  agent.subscribeOnce("message:interrupted", (evt) => {
12003
- import_core54.eventBus.publish(callback_event, {
12896
+ import_core55.eventBus.publish(callback_event, {
12004
12897
  success: true,
12005
12898
  state: evt.state
12006
12899
  });
@@ -12019,11 +12912,11 @@ ${summary}`
12019
12912
  console.log(
12020
12913
  `\u5C06\u5728 ${delayMs}ms \u540E\u91CD\u8BD5\u4EFB\u52A1 (${nextRetryCount}/${maxRetries})`
12021
12914
  );
12022
- await new Promise((resolve) => setTimeout(resolve, delayMs));
12915
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
12023
12916
  return handleAgentTask(taskRequest, nextRetryCount);
12024
12917
  }
12025
12918
  if (callback_event) {
12026
- import_core54.eventBus.publish(callback_event, {
12919
+ import_core55.eventBus.publish(callback_event, {
12027
12920
  success: false,
12028
12921
  error: error instanceof Error ? error.message : String(error)
12029
12922
  });
@@ -12060,7 +12953,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
12060
12953
  * 初始化事件监听和队列轮询
12061
12954
  */
12062
12955
  initialize() {
12063
- import_core54.eventBus.subscribe(import_core54.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
12956
+ import_core55.eventBus.subscribe(import_core55.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
12064
12957
  this.startPollingQueue();
12065
12958
  console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
12066
12959
  }
@@ -12179,7 +13072,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
12179
13072
  handleAgentTask(taskRequest).catch((error) => {
12180
13073
  console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
12181
13074
  if (taskRequest.callback_event) {
12182
- import_core54.eventBus.publish(taskRequest.callback_event, {
13075
+ import_core55.eventBus.publish(taskRequest.callback_event, {
12183
13076
  success: false,
12184
13077
  error: error instanceof Error ? error.message : String(error)
12185
13078
  });
@@ -12194,8 +13087,8 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
12194
13087
  var AgentTaskConsumer = _AgentTaskConsumer;
12195
13088
 
12196
13089
  // src/index.ts
12197
- var import_core58 = require("@axiom-lattice/core");
12198
- var import_protocols5 = require("@axiom-lattice/protocols");
13090
+ var import_core59 = require("@axiom-lattice/core");
13091
+ var import_protocols7 = require("@axiom-lattice/protocols");
12199
13092
  var import_meta = {};
12200
13093
  process.on("unhandledRejection", (reason, promise) => {
12201
13094
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
@@ -12203,18 +13096,18 @@ process.on("unhandledRejection", (reason, promise) => {
12203
13096
  var DEFAULT_LOGGER_CONFIG = {
12204
13097
  name: "default",
12205
13098
  description: "Default logger for lattice-gateway service",
12206
- type: import_protocols5.LoggerType.PINO,
13099
+ type: import_protocols7.LoggerType.PINO,
12207
13100
  serviceName: "lattice/gateway",
12208
13101
  loggerName: "lattice/gateway"
12209
13102
  };
12210
13103
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
12211
13104
  var logger5 = loggerLattice.client;
12212
13105
  function initializeLogger(config) {
12213
- if (import_core58.loggerLatticeManager.hasLattice("default")) {
12214
- import_core58.loggerLatticeManager.removeLattice("default");
13106
+ if (import_core59.loggerLatticeManager.hasLattice("default")) {
13107
+ import_core59.loggerLatticeManager.removeLattice("default");
12215
13108
  }
12216
- (0, import_core58.registerLoggerLattice)("default", config);
12217
- return (0, import_core58.getLoggerLattice)("default");
13109
+ (0, import_core59.registerLoggerLattice)("default", config);
13110
+ return (0, import_core59.getLoggerLattice)("default");
12218
13111
  }
12219
13112
  var app = (0, import_fastify.default)({
12220
13113
  logger: false,
@@ -12325,7 +13218,7 @@ app.setErrorHandler((error, request, reply) => {
12325
13218
  });
12326
13219
  function getConfiguredSandboxProvider() {
12327
13220
  const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
12328
- return (0, import_core58.createSandboxProvider)({
13221
+ return (0, import_core59.createSandboxProvider)({
12329
13222
  type: sandboxProviderType,
12330
13223
  remoteBaseURL: process.env.SANDBOX_BASE_URL,
12331
13224
  microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
@@ -12341,7 +13234,7 @@ function getConfiguredSandboxProvider() {
12341
13234
  }
12342
13235
  async function restoreMcpConnections() {
12343
13236
  try {
12344
- const storeLattice = (0, import_core58.getStoreLattice)("default", "mcp");
13237
+ const storeLattice = (0, import_core59.getStoreLattice)("default", "mcp");
12345
13238
  const store2 = storeLattice.store;
12346
13239
  if (!store2) {
12347
13240
  logger5.info("MCP store not configured, skipping connection restoration");
@@ -12355,15 +13248,15 @@ async function restoreMcpConnections() {
12355
13248
  for (const config of connectedConfigs) {
12356
13249
  try {
12357
13250
  const connection = convertToConnection2(config.config);
12358
- import_core58.mcpManager.addServer(config.key, connection);
13251
+ import_core59.mcpManager.addServer(config.key, connection);
12359
13252
  } catch (err) {
12360
13253
  logger5.warn(`Failed to prepare MCP server "${config.key}" for restoration`, { error: err });
12361
13254
  }
12362
13255
  }
12363
- await import_core58.mcpManager.connect();
13256
+ await import_core59.mcpManager.connect();
12364
13257
  for (const config of connectedConfigs) {
12365
13258
  try {
12366
- await import_core58.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
13259
+ await import_core59.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
12367
13260
  logger5.info(`MCP server "${config.key}" restored with ${config.selectedTools.length} tools`);
12368
13261
  } catch (err) {
12369
13262
  logger5.warn(`Failed to register tools for MCP server "${config.key}"`, { error: err });
@@ -12396,7 +13289,7 @@ var start = async (config) => {
12396
13289
  const { getStoreLattice: getStore15 } = await import("@axiom-lattice/core");
12397
13290
  const bindingStore = getStore15("default", "channelBinding").store;
12398
13291
  const installationStore = getStore15("default", "channelInstallation").store;
12399
- (0, import_core57.setBindingRegistry)(bindingStore);
13292
+ (0, import_core58.setBindingRegistry)(bindingStore);
12400
13293
  const router = new MessageRouter({
12401
13294
  middlewares: [
12402
13295
  createDeduplicationMiddleware(),
@@ -12421,10 +13314,10 @@ var start = async (config) => {
12421
13314
  error: err instanceof Error ? err.message : String(err)
12422
13315
  });
12423
13316
  }
12424
- (0, import_core57.setEvalRunService)(evalRunner);
13317
+ (0, import_core58.setEvalRunService)(evalRunner);
12425
13318
  try {
12426
- const menuStore = (0, import_core58.getStoreLattice)("default", "menu").store;
12427
- (0, import_core57.setMenuRegistry)(menuStore);
13319
+ const menuStore = (0, import_core59.getStoreLattice)("default", "menu").store;
13320
+ (0, import_core58.setMenuRegistry)(menuStore);
12428
13321
  logger5.info("Menu registry initialized");
12429
13322
  } catch {
12430
13323
  }
@@ -12433,17 +13326,17 @@ var start = async (config) => {
12433
13326
  setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
12434
13327
  }
12435
13328
  registerLatticeRoutes(app, channelDeps);
12436
- if (!import_core58.sandboxLatticeManager.hasLattice("default")) {
12437
- import_core58.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
13329
+ if (!import_core59.sandboxLatticeManager.hasLattice("default")) {
13330
+ import_core59.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
12438
13331
  logger5.info("Registered sandbox manager from env configuration");
12439
13332
  }
12440
13333
  try {
12441
13334
  const { ResourceController: ResourceController2 } = await Promise.resolve().then(() => (init_resources(), resources_exports));
12442
- const sharedResourceStore = (0, import_core58.getStoreLattice)("default", "sharedResource").store;
12443
- const cache = new import_core58.TokenCache();
13335
+ const sharedResourceStore = (0, import_core59.getStoreLattice)("default", "sharedResource").store;
13336
+ const cache = new import_core59.TokenCache();
12444
13337
  const resourceController = new ResourceController2({
12445
13338
  store: sharedResourceStore,
12446
- sandboxManager: import_core58.sandboxLatticeManager,
13339
+ sandboxManager: import_core59.sandboxLatticeManager,
12447
13340
  cache,
12448
13341
  logger: logger5
12449
13342
  });
@@ -12470,11 +13363,6 @@ var start = async (config) => {
12470
13363
  const target_port = config?.port || Number(process.env.PORT) || 4001;
12471
13364
  await app.listen({ port: target_port, host: "0.0.0.0" });
12472
13365
  logger5.info(`Lattice Gateway is running on port: ${target_port}`);
12473
- try {
12474
- logger5.info("AgentLifecycleManager initialized");
12475
- } catch (error) {
12476
- logger5.warn("Failed to initialize AgentLifecycleManager", { error });
12477
- }
12478
13366
  const queueServiceConfig = config?.queueServiceConfig;
12479
13367
  if (queueServiceConfig) {
12480
13368
  setQueueServiceType(queueServiceConfig.type);
@@ -12484,7 +13372,7 @@ var start = async (config) => {
12484
13372
  }
12485
13373
  }
12486
13374
  if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
12487
- import_core58.agentInstanceManager.restore().then((stats) => {
13375
+ import_core59.agentInstanceManager.restore().then((stats) => {
12488
13376
  logger5.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
12489
13377
  }).catch((error) => {
12490
13378
  logger5.error("Agent recovery failed", { error });