@axiom-lattice/gateway 2.1.117 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +12 -12
  2. package/CHANGELOG.md +38 -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 +1098 -181
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +967 -96
  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/eval.ts +6 -2
  17. package/src/controllers/local-a2a-templates.ts +18 -0
  18. package/src/controllers/local-a2a.ts +149 -0
  19. package/src/controllers/tasks.ts +2 -3
  20. package/src/controllers/workspace.ts +144 -17
  21. package/src/index.ts +1 -9
  22. package/src/routes/__tests__/a2a-test-connection.test.ts +177 -0
  23. package/src/routes/a2a-test-connection.ts +126 -0
  24. package/src/routes/a2a.ts +100 -28
  25. package/src/routes/index.ts +23 -0
  26. package/src/services/__tests__/eval-runner.test.ts +131 -0
  27. package/src/services/__tests__/local-a2a.test.ts +124 -0
  28. package/src/services/eval-runner.ts +50 -3
  29. package/src/services/local-a2a.ts +354 -0
  30. package/src/utils/__tests__/local-a2a-runtime.test.ts +77 -0
  31. package/src/utils/__tests__/path-containment.test.ts +35 -0
  32. package/src/utils/a2a-url.ts +20 -0
  33. package/src/utils/local-a2a-runtime.ts +107 -0
  34. package/src/utils/path-containment.ts +38 -0
  35. 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,375 @@ 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
+ return null;
2084
+ }
2085
+ function resolveLocalA2AWorkingDirectory(options) {
2086
+ if (options.envWorkdir) return options.envWorkdir;
2087
+ if (options.commandCwd) return options.commandCwd;
2088
+ return findWorkspaceRoot(options.currentCwd) || options.currentCwd;
2089
+ }
2090
+ function validateWorkingDirectory(cwd) {
2091
+ try {
2092
+ const stat2 = (0, import_node_fs.statSync)(cwd);
2093
+ if (!stat2.isDirectory()) {
2094
+ return `Working directory is not a directory: ${cwd}`;
2095
+ }
2096
+ return null;
2097
+ } catch (error) {
2098
+ const message = error instanceof Error ? error.message : String(error);
2099
+ return `Working directory is not accessible: ${cwd} (${message})`;
2100
+ }
2101
+ }
2102
+ function appendStderrTail(current, chunk) {
2103
+ const next = `${current}${chunk}`;
2104
+ if (next.length <= STDERR_TAIL_LIMIT) return next;
2105
+ return next.slice(-STDERR_TAIL_LIMIT);
2106
+ }
2107
+ function formatExitMessage(exitReason, stderrTail) {
2108
+ const stderr = stderrTail.trim();
2109
+ return stderr ? `Exited before becoming ready (${exitReason}): ${stderr}` : `Exited before becoming ready (${exitReason})`;
2110
+ }
2111
+ async function probeReady(agentCardUrl, healthUrl, timeoutMs) {
2112
+ const controller = new AbortController();
2113
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2114
+ try {
2115
+ if (healthUrl) {
2116
+ const resp = await fetch(healthUrl, { signal: controller.signal });
2117
+ if (!resp.ok) return false;
2118
+ }
2119
+ const cardResp = await fetch(agentCardUrl, { signal: controller.signal });
2120
+ return cardResp.ok;
2121
+ } catch {
2122
+ return false;
2123
+ } finally {
2124
+ clearTimeout(timer);
2125
+ }
2126
+ }
2127
+ var _LocalA2AService = class _LocalA2AService {
2128
+ constructor() {
2129
+ this.states = /* @__PURE__ */ new Map();
2130
+ this.autoStartEnabled = readBooleanEnv("LOCAL_A2A_AUTO_START", true);
2131
+ }
2132
+ static getInstance() {
2133
+ if (!_LocalA2AService.instance) {
2134
+ _LocalA2AService.instance = new _LocalA2AService();
2135
+ }
2136
+ return _LocalA2AService.instance;
2137
+ }
2138
+ getOrCreateState(runtimeId, localRuntime) {
2139
+ let state = this.states.get(runtimeId);
2140
+ if (!state) {
2141
+ state = {
2142
+ runtimeId,
2143
+ label: localRuntime.label || runtimeId,
2144
+ status: this.autoStartEnabled ? "stopped" : "disabled",
2145
+ enabled: this.autoStartEnabled,
2146
+ assistantId: runtimeId,
2147
+ agentCardUrl: localRuntime.agentCardUrl,
2148
+ healthUrl: localRuntime.healthUrl,
2149
+ localRuntime
2150
+ };
2151
+ this.states.set(runtimeId, state);
2152
+ } else {
2153
+ state.localRuntime = localRuntime;
2154
+ state.label = localRuntime.label || state.label;
2155
+ state.agentCardUrl = localRuntime.agentCardUrl;
2156
+ state.healthUrl = localRuntime.healthUrl;
2157
+ }
2158
+ return state;
2159
+ }
2160
+ async getRegisteredState(runtimeId, localRuntime) {
2161
+ const state = this.getOrCreateState(runtimeId, localRuntime);
2162
+ if (state.process && state.status === "running") {
2163
+ const alive = await probeReady(
2164
+ localRuntime.agentCardUrl,
2165
+ localRuntime.healthUrl,
2166
+ 2e3
2167
+ );
2168
+ if (!alive) {
2169
+ state.process = void 0;
2170
+ state.pid = void 0;
2171
+ state.status = "stopped";
2172
+ state.message = "Process exited unexpectedly";
2173
+ }
2174
+ }
2175
+ return this.snapshotState(state);
2176
+ }
2177
+ async startProvider(runtimeId, localRuntime) {
2178
+ const state = this.getOrCreateState(runtimeId, localRuntime);
2179
+ if (!state.enabled) {
2180
+ state.status = "disabled";
2181
+ state.message = "Disabled by configuration";
2182
+ return this.snapshotState(state);
2183
+ }
2184
+ if (state.process && state.status === "running") {
2185
+ return this.snapshotState(state);
2186
+ }
2187
+ state.enabled = true;
2188
+ const { executable, args, cwd, env: cmdEnv } = localRuntime.command;
2189
+ const resolvedCwd = resolveLocalA2AWorkingDirectory({
2190
+ envWorkdir: process.env.LOCAL_A2A_WORKDIR || void 0,
2191
+ commandCwd: cwd,
2192
+ currentCwd: process.cwd()
2193
+ });
2194
+ const cwdError = validateWorkingDirectory(resolvedCwd);
2195
+ if (cwdError) {
2196
+ state.process = void 0;
2197
+ state.pid = void 0;
2198
+ state.status = "failed";
2199
+ state.message = cwdError;
2200
+ return this.snapshotState(state);
2201
+ }
2202
+ state.status = "starting";
2203
+ state.message = "Starting";
2204
+ let stderrTail = "";
2205
+ const childEnv = cmdEnv ? { ...process.env, ...cmdEnv } : process.env;
2206
+ const child = (0, import_node_child_process.spawn)(executable, args, {
2207
+ cwd: resolvedCwd,
2208
+ env: childEnv,
2209
+ stdio: ["ignore", "pipe", "pipe"],
2210
+ shell: false
2211
+ });
2212
+ state.process = child;
2213
+ state.pid = child.pid ?? void 0;
2214
+ child.once("error", (error) => {
2215
+ const current = this.states.get(runtimeId);
2216
+ if (!current) return;
2217
+ current.process = void 0;
2218
+ current.pid = void 0;
2219
+ current.status = "failed";
2220
+ const stderr = stderrTail.trim();
2221
+ current.message = stderr ? `Failed to start: ${error.message}: ${stderr}` : `Failed to start: ${error.message}`;
2222
+ });
2223
+ child.stdout.on("data", (chunk) => {
2224
+ const text = chunk.toString("utf8").trim();
2225
+ if (text) console.log(`[local-a2a:${runtimeId}] ${text}`);
2226
+ });
2227
+ child.stderr.on("data", (chunk) => {
2228
+ const rawText = chunk.toString("utf8");
2229
+ stderrTail = appendStderrTail(stderrTail, rawText);
2230
+ const text = rawText.trim();
2231
+ if (text) console.error(`[local-a2a:${runtimeId}] ${text}`);
2232
+ });
2233
+ child.on("close", (code, signal) => {
2234
+ const current = this.states.get(runtimeId);
2235
+ if (!current) return;
2236
+ current.process = void 0;
2237
+ current.pid = void 0;
2238
+ const exitReason = signal ?? code ?? "unknown";
2239
+ if (current.status === "running") {
2240
+ current.status = "stopped";
2241
+ current.message = `Stopped (${exitReason})`;
2242
+ } else if (current.status === "starting") {
2243
+ current.status = "failed";
2244
+ current.message = formatExitMessage(exitReason, stderrTail);
2245
+ }
2246
+ });
2247
+ const ready = await this.waitUntilReady(localRuntime, state, 15e3);
2248
+ if (!ready) {
2249
+ if (state.process) {
2250
+ state.process.kill("SIGTERM");
2251
+ }
2252
+ state.process = void 0;
2253
+ state.pid = void 0;
2254
+ if (state.status !== "failed") {
2255
+ state.status = "failed";
2256
+ state.message = "Server did not become ready";
2257
+ }
2258
+ return this.snapshotState(state);
2259
+ }
2260
+ state.status = "running";
2261
+ state.message = "Running";
2262
+ state.lastStartedAt = (/* @__PURE__ */ new Date()).toISOString();
2263
+ return this.snapshotState(state);
2264
+ }
2265
+ async stopProvider(runtimeId) {
2266
+ const state = this.states.get(runtimeId);
2267
+ if (!state) {
2268
+ return this.emptyState(runtimeId);
2269
+ }
2270
+ const child = state.process;
2271
+ if (child) {
2272
+ child.kill("SIGTERM");
2273
+ await (0, import_promises.setTimeout)(750);
2274
+ if (child.exitCode === null) {
2275
+ child.kill("SIGKILL");
2276
+ }
2277
+ state.process = void 0;
2278
+ }
2279
+ state.pid = void 0;
2280
+ state.status = "stopped";
2281
+ state.message = "Stopped";
2282
+ state.lastStoppedAt = (/* @__PURE__ */ new Date()).toISOString();
2283
+ return this.snapshotState(state);
2284
+ }
2285
+ async restartProvider(runtimeId, localRuntime) {
2286
+ await this.stopProvider(runtimeId);
2287
+ return this.startProvider(runtimeId, localRuntime);
2288
+ }
2289
+ removeState(runtimeId) {
2290
+ const state = this.states.get(runtimeId);
2291
+ if (state?.process) {
2292
+ state.process.kill("SIGTERM");
2293
+ state.process = void 0;
2294
+ }
2295
+ this.states.delete(runtimeId);
2296
+ }
2297
+ async waitUntilReady(localRuntime, state, timeoutMs) {
2298
+ const start2 = Date.now();
2299
+ while (Date.now() - start2 < timeoutMs) {
2300
+ if (state.status === "failed") return false;
2301
+ if (await probeReady(localRuntime.agentCardUrl, localRuntime.healthUrl, 1500)) {
2302
+ return true;
2303
+ }
2304
+ await (0, import_promises.setTimeout)(500);
2305
+ }
2306
+ return false;
2307
+ }
2308
+ emptyState(runtimeId) {
2309
+ return {
2310
+ runtimeId,
2311
+ label: runtimeId,
2312
+ status: "stopped",
2313
+ enabled: false
2314
+ };
2315
+ }
2316
+ snapshotState(state) {
2317
+ return {
2318
+ runtimeId: state.runtimeId,
2319
+ label: state.label,
2320
+ status: state.status,
2321
+ enabled: state.enabled,
2322
+ assistantId: state.assistantId,
2323
+ agentCardUrl: state.agentCardUrl,
2324
+ healthUrl: state.healthUrl,
2325
+ pid: state.pid,
2326
+ message: state.message,
2327
+ lastStartedAt: state.lastStartedAt,
2328
+ lastStoppedAt: state.lastStoppedAt
2329
+ };
2330
+ }
2331
+ };
2332
+ _LocalA2AService.instance = null;
2333
+ var LocalA2AService = _LocalA2AService;
2334
+ var localA2AService = LocalA2AService.getInstance();
2335
+
2336
+ // src/utils/a2a-url.ts
2337
+ var AGENT_CARD_PATH = "/.well-known/agent-card.json";
2338
+ function normalizeA2AAgentCardUrl(value, defaultProtocol = "https://") {
2339
+ const raw = value?.trim() ?? "";
2340
+ if (!raw) return "";
2341
+ const withProtocol = /^https?:\/\//i.test(raw) ? raw : `${defaultProtocol}${raw.replace(/^\/+/, "")}`;
2342
+ const trimmed = withProtocol.replace(/\/+$/, "");
2343
+ if (trimmed.endsWith(AGENT_CARD_PATH)) {
2344
+ return trimmed;
2345
+ }
2346
+ return `${trimmed}${AGENT_CARD_PATH}`;
2347
+ }
2348
+
2349
+ // src/utils/local-a2a-runtime.ts
2350
+ function validateLocalRuntimeCommand(localRuntime) {
2351
+ const runtime = localRuntime;
2352
+ const executable = runtime.command?.executable;
2353
+ if (typeof executable !== "string" || executable.trim().length === 0) {
2354
+ return {
2355
+ ok: false,
2356
+ error: "Local managed A2A command executable is required"
2357
+ };
2358
+ }
2359
+ const args = runtime.command?.args;
2360
+ if (!Array.isArray(args) || !args.every((arg) => typeof arg === "string")) {
2361
+ return {
2362
+ ok: false,
2363
+ error: "Local managed A2A command args must be an array of strings"
2364
+ };
2365
+ }
2366
+ return { ok: true };
2367
+ }
2368
+ function getAgentCardPort(agentCardUrl) {
2369
+ let url;
2370
+ try {
2371
+ url = new URL(agentCardUrl);
2372
+ } catch {
2373
+ return null;
2374
+ }
2375
+ if (url.port) {
2376
+ const port = Number(url.port);
2377
+ return Number.isInteger(port) ? port : null;
2378
+ }
2379
+ if (url.protocol === "http:") return 80;
2380
+ if (url.protocol === "https:") return 443;
2381
+ return null;
2382
+ }
2383
+ function parseCommandPort(args) {
2384
+ for (let index = 0; index < args.length; index += 1) {
2385
+ const arg = args[index];
2386
+ const match = /^(--port|-p)(?:[=\s]+(\d+))?$/.exec(arg);
2387
+ if (!match) continue;
2388
+ const inlinePort = match[2];
2389
+ if (inlinePort) return Number(inlinePort);
2390
+ const nextArg = args[index + 1];
2391
+ if (nextArg && /^\d+$/.test(nextArg)) {
2392
+ return Number(nextArg);
2393
+ }
2394
+ }
2395
+ return null;
2396
+ }
2397
+ function isLoopbackHost(hostname) {
2398
+ const normalized = hostname.toLowerCase();
2399
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
2400
+ }
2401
+ function validateLocalRuntimePort(localRuntime, agentCardUrl) {
2402
+ let url;
2403
+ try {
2404
+ url = new URL(agentCardUrl);
2405
+ } catch {
2406
+ return { ok: true };
2407
+ }
2408
+ if (!isLoopbackHost(url.hostname) || !url.port) {
2409
+ return { ok: true };
2410
+ }
2411
+ const commandPort = parseCommandPort(localRuntime.command.args);
2412
+ if (commandPort === null) {
2413
+ return { ok: true };
2414
+ }
2415
+ const agentCardPort = Number(url.port);
2416
+ if (commandPort === agentCardPort) {
2417
+ return { ok: true };
2418
+ }
2419
+ return {
2420
+ ok: false,
2421
+ 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.`
2422
+ };
2423
+ }
2424
+
2425
+ // src/controllers/assistant.ts
2010
2426
  function getTenantId(request) {
2011
2427
  const userTenantId = request.user?.tenantId;
2012
2428
  if (userTenantId) {
@@ -2136,6 +2552,79 @@ async function createAssistant(request, reply) {
2136
2552
  });
2137
2553
  }
2138
2554
  const id = data.id ?? (0, import_crypto.randomUUID)();
2555
+ if (data.graphDefinition && data.graphDefinition.type === import_protocols.AgentType.A2A_REMOTE) {
2556
+ const a2aConfig = data.graphDefinition;
2557
+ const warnings = [];
2558
+ const isLocalManaged = !!a2aConfig.localRuntime;
2559
+ a2aConfig.agentCardUrl = normalizeA2AAgentCardUrl(
2560
+ a2aConfig.agentCardUrl,
2561
+ isLocalManaged ? "http://" : "https://"
2562
+ );
2563
+ if (a2aConfig.localRuntime) {
2564
+ const commandValidation = validateLocalRuntimeCommand(
2565
+ a2aConfig.localRuntime
2566
+ );
2567
+ if (!commandValidation.ok) {
2568
+ return reply.status(400).send({
2569
+ success: false,
2570
+ message: commandValidation.error
2571
+ });
2572
+ }
2573
+ a2aConfig.localRuntime.command.executable = a2aConfig.localRuntime.command.executable.trim();
2574
+ a2aConfig.localRuntime.agentCardUrl = normalizeA2AAgentCardUrl(
2575
+ a2aConfig.localRuntime.agentCardUrl || a2aConfig.agentCardUrl,
2576
+ "http://"
2577
+ );
2578
+ a2aConfig.agentCardUrl = a2aConfig.localRuntime.agentCardUrl;
2579
+ const portValidation = validateLocalRuntimePort(
2580
+ a2aConfig.localRuntime,
2581
+ a2aConfig.agentCardUrl
2582
+ );
2583
+ if (!portValidation.ok) {
2584
+ return reply.status(400).send({
2585
+ success: false,
2586
+ message: portValidation.error
2587
+ });
2588
+ }
2589
+ }
2590
+ if (a2aConfig.localRuntime) {
2591
+ const existingAssistants = await (0, import_core.getStoreLattice)("default", "assistant").store.getAllAssistants(tenantId);
2592
+ const targetPort = getAgentCardPort(a2aConfig.localRuntime.agentCardUrl);
2593
+ const duplicate = existingAssistants.find((a) => {
2594
+ if (a.id === id) return false;
2595
+ const gd = a.graphDefinition && typeof a.graphDefinition === "object" ? a.graphDefinition : void 0;
2596
+ if (!gd || gd.type !== import_protocols.AgentType.A2A_REMOTE) return false;
2597
+ const lr = gd.localRuntime;
2598
+ if (!lr) return false;
2599
+ const existingAgentCardUrl = normalizeA2AAgentCardUrl(lr.agentCardUrl, "http://");
2600
+ const existingPort = getAgentCardPort(existingAgentCardUrl);
2601
+ if (targetPort !== null && existingPort !== null) {
2602
+ return existingPort === targetPort;
2603
+ }
2604
+ return existingAgentCardUrl === a2aConfig.localRuntime.agentCardUrl;
2605
+ });
2606
+ if (duplicate) {
2607
+ const conflictDetail = targetPort === null ? `agentCardUrl "${a2aConfig.localRuntime.agentCardUrl}"` : `port ${targetPort}`;
2608
+ return reply.status(409).send({
2609
+ success: false,
2610
+ message: `A local managed A2A assistant using ${conflictDetail} already exists: "${duplicate.name}". Each local managed A2A assistant needs a unique port.`
2611
+ });
2612
+ }
2613
+ }
2614
+ try {
2615
+ const resp = await fetch(a2aConfig.agentCardUrl, {
2616
+ signal: AbortSignal.timeout(5e3)
2617
+ });
2618
+ if (!resp.ok) {
2619
+ warnings.push(`agentCardUrl returned HTTP ${resp.status}`);
2620
+ }
2621
+ } catch {
2622
+ warnings.push(`agentCardUrl is not reachable: ${a2aConfig.agentCardUrl}`);
2623
+ }
2624
+ if (warnings.length > 0) {
2625
+ console.warn(`[createAssistant] A2A warnings for ${a2aConfig.agentCardUrl}:`, warnings);
2626
+ }
2627
+ }
2139
2628
  return upsertAssistant(tenantId, id, data, reply, true);
2140
2629
  }
2141
2630
  async function updateAssistant(request, reply) {
@@ -2149,11 +2638,28 @@ async function deleteAssistant(request, reply) {
2149
2638
  const { id } = request.params;
2150
2639
  const storeLattice = (0, import_core.getStoreLattice)("default", "assistant");
2151
2640
  const assistantStore = storeLattice.store;
2641
+ const storedAssistant = await assistantStore.getAssistantById(tenantId, id);
2642
+ const graphDefinition = storedAssistant?.graphDefinition;
2643
+ const hasLocalRuntime = graphDefinition && typeof graphDefinition === "object" ? !!graphDefinition.localRuntime : false;
2644
+ if (storedAssistant && hasLocalRuntime) {
2645
+ localA2AService.removeState(id);
2646
+ const deleted2 = await assistantStore.deleteAssistant(tenantId, id);
2647
+ if (!deleted2) {
2648
+ return reply.status(500).send({
2649
+ success: false,
2650
+ message: "Failed to delete assistant"
2651
+ });
2652
+ }
2653
+ import_core2.eventBus.publish("assistant:deleted", { id, tenantId });
2654
+ return {
2655
+ success: true,
2656
+ message: "Deleted local A2A assistant"
2657
+ };
2658
+ }
2152
2659
  const agentConfig = await import_core2.agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
2153
2660
  const isCodeConfigured = !!agentConfig;
2154
2661
  if (isCodeConfigured) {
2155
- const exists2 = await assistantStore.hasAssistant(tenantId, id);
2156
- if (!exists2) {
2662
+ if (!storedAssistant) {
2157
2663
  return reply.status(404).send({
2158
2664
  success: false,
2159
2665
  message: "Assistant not found (code-configured assistants cannot be deleted from code, only from store)"
@@ -2166,8 +2672,7 @@ async function deleteAssistant(request, reply) {
2166
2672
  message: "Deleted assistant from store (code-configured registration remains)"
2167
2673
  };
2168
2674
  }
2169
- const exists = await assistantStore.hasAssistant(tenantId, id);
2170
- if (!exists) {
2675
+ if (!storedAssistant) {
2171
2676
  return reply.status(404).send({
2172
2677
  success: false,
2173
2678
  message: "Assistant not found"
@@ -2221,7 +2726,7 @@ var getAgentGraph = async (request, reply) => {
2221
2726
  // src/controllers/run.ts
2222
2727
  var import_uuid = require("uuid");
2223
2728
  var import_core3 = require("@axiom-lattice/core");
2224
- var import_protocols = require("@axiom-lattice/protocols");
2729
+ var import_protocols2 = require("@axiom-lattice/protocols");
2225
2730
  function getUserId2(request) {
2226
2731
  const authUser = request.user;
2227
2732
  if (authUser?.id) return authUser.id;
@@ -2293,13 +2798,13 @@ var createRun = async (request, reply) => {
2293
2798
  command,
2294
2799
  custom_run_config: mergedConfig
2295
2800
  }, mode);
2296
- const stream = agent.chunkStream(result.messageId, [import_protocols.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols.MessageChunkTypes.INTERRUPT, import_protocols.MessageChunkTypes.MESSAGE_FAILED]);
2801
+ const stream = agent.chunkStream(result.messageId, [import_protocols2.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols2.MessageChunkTypes.INTERRUPT, import_protocols2.MessageChunkTypes.MESSAGE_FAILED]);
2297
2802
  for await (const chunk of stream) {
2298
2803
  const success = reply.raw.write(`data: ${JSON.stringify(chunk)}
2299
2804
 
2300
2805
  `);
2301
2806
  if (!success) {
2302
- await new Promise((resolve) => reply.raw.once("drain", resolve));
2807
+ await new Promise((resolve3) => reply.raw.once("drain", resolve3));
2303
2808
  }
2304
2809
  }
2305
2810
  } catch (error) {
@@ -3077,7 +3582,7 @@ var configService = new ConfigService();
3077
3582
 
3078
3583
  // src/services/queue_service.ts
3079
3584
  var import_core8 = require("@axiom-lattice/core");
3080
- var import_protocols2 = require("@axiom-lattice/protocols");
3585
+ var import_protocols3 = require("@axiom-lattice/protocols");
3081
3586
  var import_queue_redis = require("@axiom-lattice/queue-redis");
3082
3587
  var DEFAULT_QUEUE_KEY = "default";
3083
3588
  var queueServiceType = process.env.QUEUE_SERVICE_TYPE || "memory";
@@ -3088,7 +3593,7 @@ var setQueueServiceType = (type) => {
3088
3593
  const config = {
3089
3594
  name: "Default Queue Service",
3090
3595
  description: `Default ${type} queue service`,
3091
- type: type === "redis" ? import_protocols2.QueueType.REDIS : import_protocols2.QueueType.MEMORY,
3596
+ type: type === "redis" ? import_protocols3.QueueType.REDIS : import_protocols3.QueueType.MEMORY,
3092
3597
  queueName,
3093
3598
  options: type === "redis" ? {
3094
3599
  redisUrl: process.env.REDIS_URL,
@@ -4216,7 +4721,7 @@ async function executeSqlQuery(client, body, reply) {
4216
4721
 
4217
4722
  // src/controllers/workflow-tracking.ts
4218
4723
  var import_core15 = require("@axiom-lattice/core");
4219
- var import_protocols3 = require("@axiom-lattice/protocols");
4724
+ var import_protocols4 = require("@axiom-lattice/protocols");
4220
4725
  function getTenantId7(request) {
4221
4726
  const userTenantId = request.user?.tenantId;
4222
4727
  if (userTenantId) return userTenantId;
@@ -4660,7 +5165,7 @@ async function replyInboxTask(request, reply) {
4660
5165
  }).then((result) => {
4661
5166
  const stream = agent.chunkStream(
4662
5167
  result.messageId,
4663
- [import_protocols3.MessageChunkTypes.MESSAGE_COMPLETED]
5168
+ [import_protocols4.MessageChunkTypes.MESSAGE_COMPLETED]
4664
5169
  );
4665
5170
  (async () => {
4666
5171
  for await (const _ of stream) {
@@ -4877,15 +5382,14 @@ function getStore() {
4877
5382
  async function listTasks(request, reply) {
4878
5383
  try {
4879
5384
  const tenantId = getTenantId9(request);
4880
- const userId = getUserId4(request);
4881
5385
  const query = request.query;
4882
5386
  const store2 = getStore();
4883
5387
  const tasks = await store2.list({
4884
5388
  tenantId,
4885
- ownerId: query.ownerId || userId,
4886
5389
  status: query.status,
4887
5390
  priority: query.priority,
4888
- ownerType: query.ownerType,
5391
+ ...query.ownerId && { ownerId: query.ownerId },
5392
+ ...query.ownerType && { ownerType: query.ownerType },
4889
5393
  metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
4890
5394
  projectId: query.projectId,
4891
5395
  limit: query.limit ? parseInt(query.limit) : void 0,
@@ -6021,13 +6525,42 @@ function registerSandboxProxyRoutes(app2) {
6021
6525
  }
6022
6526
 
6023
6527
  // src/controllers/workspace.ts
6024
- var fs = __toESM(require("fs/promises"));
6025
- var path = __toESM(require("path"));
6528
+ var fs2 = __toESM(require("fs/promises"));
6529
+ var path2 = __toESM(require("path"));
6530
+ var os = __toESM(require("os"));
6026
6531
  var import_core24 = require("@axiom-lattice/core");
6027
6532
  var import_core25 = require("@axiom-lattice/core");
6028
6533
  var import_core26 = require("@axiom-lattice/core");
6029
6534
  var import_uuid3 = require("uuid");
6030
6535
  init_mime();
6536
+
6537
+ // src/utils/path-containment.ts
6538
+ var fs = __toESM(require("fs/promises"));
6539
+ var path = __toESM(require("path"));
6540
+ var PathOutsideRootError = class extends Error {
6541
+ constructor(rootPath, targetPath) {
6542
+ super(`Path is outside the allowed root: ${targetPath}`);
6543
+ this.rootPath = rootPath;
6544
+ this.targetPath = targetPath;
6545
+ this.name = "PathOutsideRootError";
6546
+ }
6547
+ };
6548
+ function isPathWithinRoot(rootPath, targetPath) {
6549
+ const relative2 = path.relative(rootPath, targetPath);
6550
+ return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
6551
+ }
6552
+ async function resolvePathWithinRoot(rootPath, targetPath) {
6553
+ const [resolvedRoot, resolvedTarget] = await Promise.all([
6554
+ fs.realpath(rootPath),
6555
+ fs.realpath(targetPath)
6556
+ ]);
6557
+ if (!isPathWithinRoot(resolvedRoot, resolvedTarget)) {
6558
+ throw new PathOutsideRootError(resolvedRoot, resolvedTarget);
6559
+ }
6560
+ return { rootPath: resolvedRoot, targetPath: resolvedTarget };
6561
+ }
6562
+
6563
+ // src/controllers/workspace.ts
6031
6564
  var WorkspaceController = class {
6032
6565
  constructor() {
6033
6566
  this.workspaceStore = (0, import_core24.getStoreLattice)("default", "workspace").store;
@@ -6188,16 +6721,23 @@ var WorkspaceController = class {
6188
6721
  }),
6189
6722
  workspace
6190
6723
  };
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
6724
  }
6725
+ const rootDir = await this.getFilesystemRootDirectory(
6726
+ tenantId,
6727
+ workspaceId,
6728
+ projectId
6729
+ );
6730
+ console.log(`[getBackend] using FilesystemBackend rootDir=${rootDir}`);
6731
+ return {
6732
+ backend: new import_core25.FilesystemBackend({
6733
+ rootDir,
6734
+ virtualMode: true
6735
+ }),
6736
+ workspace
6737
+ };
6738
+ }
6739
+ async getFilesystemRootDirectory(tenantId, workspaceId, projectId) {
6740
+ return `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
6201
6741
  }
6202
6742
  isBinaryContentType(filename) {
6203
6743
  const ext = filename.split(".").pop()?.toLowerCase() || "";
@@ -6386,21 +6926,21 @@ var WorkspaceController = class {
6386
6926
  async listPath(request) {
6387
6927
  const tenantId = this.getTenantId(request);
6388
6928
  const { workspaceId, projectId } = request.params;
6389
- const path3 = request.query.path || "/";
6929
+ const path4 = request.query.path || "/";
6390
6930
  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);
6931
+ console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${path4} assistantId=${assistantId}`);
6932
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path4);
6933
+ console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${path4})`);
6934
+ const files = await backend.lsInfo(path4);
6395
6935
  console.log(`[listPath] result count=${files.length}`);
6396
6936
  return { success: true, data: files };
6397
6937
  }
6398
6938
  async readFile(request) {
6399
6939
  const tenantId = this.getTenantId(request);
6400
6940
  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));
6941
+ const { path: path4, offset = 0, limit = 1e3, assistantId } = request.query;
6942
+ const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path4);
6943
+ const content = await backend.read(path4, Number(offset), Number(limit));
6404
6944
  return { success: true, data: { content, offset, limit } };
6405
6945
  }
6406
6946
  /**
@@ -6412,7 +6952,7 @@ var WorkspaceController = class {
6412
6952
  * - `path`: optional directory path relative to project root (e.g., "docs", "src/utils")
6413
6953
  * - `assistantId`: optional query parameter for sandbox identity matching
6414
6954
  * For sandbox storage, delegates to the sandbox upload API.
6415
- * For filesystem storage, writes directly under /lattice_store/workspaces/{workspaceId}/{projectId}.
6955
+ * For filesystem storage, writes under the same project root used by the file backend.
6416
6956
  */
6417
6957
  async uploadFile(request, reply) {
6418
6958
  const tenantId = this.getTenantId(request);
@@ -6446,7 +6986,7 @@ var WorkspaceController = class {
6446
6986
  workspaceId,
6447
6987
  projectId
6448
6988
  };
6449
- const realPath = pathValue ? path.posix.join(pathValue, filename) : path.posix.join("/project/uploads", filename);
6989
+ const realPath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
6450
6990
  const isBinary = this.isBinaryContentType(filename);
6451
6991
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, realPath);
6452
6992
  if (volumeBackend && !isBinary) {
@@ -6457,7 +6997,7 @@ var WorkspaceController = class {
6457
6997
  const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
6458
6998
  await sandbox.file.uploadFile({ file: realPath, data: buffer });
6459
6999
  }
6460
- const relativePath = pathValue ? path.posix.join(pathValue, filename) : path.posix.join("/project/uploads", filename);
7000
+ const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
6461
7001
  const result2 = {
6462
7002
  path: relativePath,
6463
7003
  name: filename,
@@ -6467,14 +7007,43 @@ var WorkspaceController = class {
6467
7007
  };
6468
7008
  return reply.status(200).send({ success: true, data: result2 });
6469
7009
  }
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);
7010
+ const rootDir = await this.getFilesystemRootDirectory(
7011
+ tenantId,
7012
+ workspaceId,
7013
+ projectId
7014
+ );
7015
+ await fs2.mkdir(rootDir, { recursive: true });
7016
+ const relativeDirectory = pathValue?.replace(/^[/\\]+/, "") || ".";
7017
+ const targetDir = path2.resolve(rootDir, relativeDirectory);
7018
+ if (!isPathWithinRoot(path2.resolve(rootDir), targetDir)) {
7019
+ return reply.status(400).send({
7020
+ success: false,
7021
+ error: "Upload path must stay within the project directory"
7022
+ });
7023
+ }
7024
+ await fs2.mkdir(targetDir, { recursive: true });
7025
+ const [resolvedRoot, resolvedTargetDir] = await Promise.all([
7026
+ fs2.realpath(rootDir),
7027
+ fs2.realpath(targetDir)
7028
+ ]);
7029
+ if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {
7030
+ return reply.status(400).send({
7031
+ success: false,
7032
+ error: "Upload path must stay within the project directory"
7033
+ });
7034
+ }
7035
+ const safeFilename = path2.basename(filename);
7036
+ if (safeFilename !== filename) {
7037
+ return reply.status(400).send({
7038
+ success: false,
7039
+ error: "Invalid filename"
7040
+ });
7041
+ }
7042
+ const targetPath = path2.join(resolvedTargetDir, safeFilename);
7043
+ await fs2.writeFile(targetPath, buffer);
7044
+ const stat2 = await fs2.stat(targetPath);
6476
7045
  const result = {
6477
- path: pathValue ? `/${pathValue}/${filename}` : `/${filename}`,
7046
+ path: path2.posix.join("/", relativeDirectory, safeFilename),
6478
7047
  name: filename,
6479
7048
  is_dir: false,
6480
7049
  size: stat2.size,
@@ -6482,9 +7051,63 @@ var WorkspaceController = class {
6482
7051
  };
6483
7052
  return reply.status(200).send({ success: true, data: result });
6484
7053
  } catch (error) {
7054
+ const message = error instanceof Error ? error.message : String(error);
6485
7055
  return reply.status(500).send({
6486
7056
  success: false,
6487
- error: `Upload handler error: ${error?.message || String(error)}`
7057
+ error: `Upload handler error: ${message}`
7058
+ });
7059
+ }
7060
+ }
7061
+ /**
7062
+ * Browse local filesystem directories for the folder picker.
7063
+ *
7064
+ * Security constraints:
7065
+ * - Restricted to os.homedir()
7066
+ * - Blacklists system directories
7067
+ * - Returns only directory names, no file contents or hidden files
7068
+ */
7069
+ async browseFilesystem(request, reply) {
7070
+ const homeDir = os.homedir();
7071
+ const requestedPath = request.query.path || homeDir;
7072
+ const SYSTEM_BLACKLIST = ["/etc", "/sys", "/proc", "/var", "/tmp", "/dev"];
7073
+ try {
7074
+ const resolved = await resolvePathWithinRoot(homeDir, requestedPath);
7075
+ const normalized = resolved.targetPath;
7076
+ for (const blacklisted of SYSTEM_BLACKLIST) {
7077
+ if (isPathWithinRoot(blacklisted, normalized)) {
7078
+ return reply.status(403).send({
7079
+ success: false,
7080
+ error: `Cannot browse system directory: ${blacklisted}`
7081
+ });
7082
+ }
7083
+ }
7084
+ const entries = await fs2.readdir(normalized, { withFileTypes: true });
7085
+ const dirEntries = entries.filter((entry) => !entry.name.startsWith(".") && entry.isDirectory()).map((entry) => ({
7086
+ name: entry.name,
7087
+ path: path2.join(normalized, entry.name),
7088
+ isDirectory: true
7089
+ }));
7090
+ const parentPath = path2.dirname(normalized);
7091
+ const hasParent = parentPath !== normalized && isPathWithinRoot(resolved.rootPath, parentPath);
7092
+ return {
7093
+ success: true,
7094
+ data: {
7095
+ path: normalized,
7096
+ parentPath: hasParent ? parentPath : void 0,
7097
+ entries: dirEntries
7098
+ }
7099
+ };
7100
+ } catch (error) {
7101
+ if (error instanceof PathOutsideRootError) {
7102
+ return reply.status(403).send({
7103
+ success: false,
7104
+ error: "Browse restricted to home directory"
7105
+ });
7106
+ }
7107
+ const message = error instanceof Error ? error.message : String(error);
7108
+ return reply.status(400).send({
7109
+ success: false,
7110
+ error: `Cannot read directory: ${message}`
6488
7111
  });
6489
7112
  }
6490
7113
  }
@@ -6545,6 +7168,10 @@ function registerWorkspaceRoutes(app2) {
6545
7168
  "/api/workspaces/:workspaceId/projects/:projectId/viewfile",
6546
7169
  controller.viewFile.bind(controller)
6547
7170
  );
7171
+ app2.get(
7172
+ "/api/filesystem/browse",
7173
+ controller.browseFilesystem.bind(controller)
7174
+ );
6548
7175
  }
6549
7176
 
6550
7177
  // src/controllers/database-configs.ts
@@ -6761,7 +7388,7 @@ async function testDatabaseConnection(request, reply) {
6761
7388
  await db.connect();
6762
7389
  await db.listTables();
6763
7390
  const latency = Date.now() - startTime;
6764
- await new Promise((resolve) => setTimeout(resolve, 100));
7391
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
6765
7392
  await db.disconnect();
6766
7393
  await import_core27.sqlDatabaseManager.removeDatabase(tenantId, testKey);
6767
7394
  return {
@@ -7590,11 +8217,12 @@ var EvalRunner = class {
7590
8217
  constructor() {
7591
8218
  this.runs = /* @__PURE__ */ new Map();
7592
8219
  this.eventEmitter = new import_events.EventEmitter();
8220
+ this.processStartedAt = /* @__PURE__ */ new Date();
7593
8221
  }
7594
8222
  getEventEmitter() {
7595
8223
  return this.eventEmitter;
7596
8224
  }
7597
- async startRun(tenantId, projectId) {
8225
+ async startRun(tenantId, projectId, suiteIds) {
7598
8226
  const store2 = this.getEvalStore();
7599
8227
  const project = await store2.getProjectById(tenantId, projectId);
7600
8228
  if (!project) throw new Error("Project not found");
@@ -7604,10 +8232,27 @@ var EvalRunner = class {
7604
8232
  }
7605
8233
  }
7606
8234
  const existingRuns = await store2.getRunsByTenant(tenantId, { projectId, status: "running" });
7607
- if (existingRuns.length > 0) {
8235
+ for (const run of existingRuns) {
8236
+ const orphaned = !this.runs.has(run.id) && run.createdAt < this.processStartedAt;
8237
+ if (orphaned) {
8238
+ await store2.updateRunStatus(tenantId, run.id, {
8239
+ status: "failed",
8240
+ error: "Gateway restarted \u2014 run orphaned",
8241
+ completedAt: /* @__PURE__ */ new Date()
8242
+ });
8243
+ }
8244
+ }
8245
+ const stillRunning = await store2.getRunsByTenant(tenantId, { projectId, status: "running" });
8246
+ if (stillRunning.length > 0) {
7608
8247
  throw new Error("A run is already in progress for this project");
7609
8248
  }
7610
- const suites = await store2.getSuitesByProject(tenantId, projectId);
8249
+ let suites = await store2.getSuitesByProject(tenantId, projectId);
8250
+ if (suiteIds && suiteIds.length > 0) {
8251
+ suites = suites.filter((s) => suiteIds.includes(s.id));
8252
+ if (suites.length === 0) {
8253
+ throw new Error("No suites match the requested suiteIds filter");
8254
+ }
8255
+ }
7611
8256
  const evalSuites = [];
7612
8257
  let totalCases = 0;
7613
8258
  for (const suite of suites) {
@@ -7650,6 +8295,9 @@ var EvalRunner = class {
7650
8295
  const runId = (0, import_uuid4.v4)();
7651
8296
  const concurrency = Math.max(1, Math.floor(project.concurrency) || 3);
7652
8297
  await store2.createRun(tenantId, projectId, runId, { totalCases, concurrency });
8298
+ const tsc = project.targetServerConfig ?? {};
8299
+ const workspaceId = tsc.workspace_id || "";
8300
+ const projectIdFromConfig = tsc.project_id || "";
7653
8301
  const projectConfig = {
7654
8302
  projectName: project.name,
7655
8303
  version: project.version,
@@ -7657,7 +8305,9 @@ var EvalRunner = class {
7657
8305
  suites: evalSuites,
7658
8306
  judge_agent_config: { modelKey: resolvedJudgeModelKey },
7659
8307
  lattice_server_config: {
7660
- tenant_id: tenantId
8308
+ tenant_id: tenantId,
8309
+ workspace_id: workspaceId,
8310
+ project_id: projectIdFromConfig
7661
8311
  },
7662
8312
  concurrency
7663
8313
  };
@@ -7701,6 +8351,15 @@ var EvalRunner = class {
7701
8351
  const runPromise = (async () => {
7702
8352
  try {
7703
8353
  const evalProject = new import_core31.LatticeEvalProject(projectConfig, onCaseComplete);
8354
+ const calibration = await evalProject.calibrateJudge();
8355
+ if (!calibration.ok) {
8356
+ await store2.updateRunStatus(tenantId, runId, {
8357
+ status: "failed",
8358
+ error: `Judge calibration failed: ${calibration.reason}`,
8359
+ completedAt: /* @__PURE__ */ new Date()
8360
+ });
8361
+ throw new Error(`Judge calibration failed: ${calibration.reason}`);
8362
+ }
7704
8363
  const { report } = await evalProject.runAllSuitesBatch(concurrency, abortController.signal);
7705
8364
  if (!abortController.signal.aborted) {
7706
8365
  const completedCount = stats.passed + stats.failed;
@@ -8088,11 +8747,12 @@ function registerEvalRoutes(app2) {
8088
8747
  try {
8089
8748
  const tenantId = getTenantId14(request);
8090
8749
  const { pid } = request.params;
8091
- const runId = await evalRunner.startRun(tenantId, pid);
8750
+ const body = request.body ?? {};
8751
+ const runId = await evalRunner.startRun(tenantId, pid, body?.suiteIds);
8092
8752
  reply.status(202).send({ success: true, message: "Run started", data: { run_id: runId } });
8093
8753
  } catch (err) {
8094
8754
  const msg = err.message;
8095
- const code = msg === "Project not found" ? 404 : msg.includes("already in progress") ? 409 : 500;
8755
+ const code = msg === "Project not found" ? 404 : msg.includes("already in progress") ? 409 : msg.includes("No suites match") ? 400 : 500;
8096
8756
  reply.status(code).send({ success: false, message: msg });
8097
8757
  }
8098
8758
  });
@@ -9072,8 +9732,8 @@ function getTenantId15(request) {
9072
9732
  return request.headers["x-tenant-id"] || "default";
9073
9733
  }
9074
9734
  async function getInstallationStore() {
9075
- const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
9076
- const store2 = getStoreLattice31("default", "channelInstallation").store;
9735
+ const { getStoreLattice: getStoreLattice32 } = await import("@axiom-lattice/core");
9736
+ const store2 = getStoreLattice32("default", "channelInstallation").store;
9077
9737
  if (store2) return store2;
9078
9738
  const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
9079
9739
  const databaseUrl = process.env.DATABASE_URL;
@@ -9554,8 +10214,8 @@ var log = {
9554
10214
  };
9555
10215
  var connections = /* @__PURE__ */ new Map();
9556
10216
  var pending = /* @__PURE__ */ new Map();
9557
- function getAgentIdFromPath(path3) {
9558
- const match = path3.match(/^\/api\/a2a\/bridge\/proxy\/([^/]+)/);
10217
+ function getAgentIdFromPath(path4) {
10218
+ const match = path4.match(/^\/api\/a2a\/bridge\/proxy\/([^/]+)/);
9559
10219
  return match ? match[1] : null;
9560
10220
  }
9561
10221
  function handleBridgeConnection(socket, _request) {
@@ -9663,12 +10323,12 @@ async function handleBridgeProxy(request, reply) {
9663
10323
  };
9664
10324
  const timeout = 3e5;
9665
10325
  try {
9666
- const response = await new Promise((resolve, reject) => {
10326
+ const response = await new Promise((resolve3, reject) => {
9667
10327
  const timer = setTimeout(() => {
9668
10328
  pending.delete(requestId);
9669
10329
  reject(new Error(`Bridge request timeout after ${timeout}ms`));
9670
10330
  }, timeout);
9671
- pending.set(requestId, { resolve, reject, timer, agentId });
10331
+ pending.set(requestId, { resolve: resolve3, reject, timer, agentId });
9672
10332
  try {
9673
10333
  if (conn.ws.readyState !== conn.ws.OPEN) {
9674
10334
  pending.delete(requestId);
@@ -9791,10 +10451,272 @@ var registerSTTRoutes = (app2) => {
9791
10451
  );
9792
10452
  };
9793
10453
 
10454
+ // src/controllers/local-a2a.ts
10455
+ var import_core39 = require("@axiom-lattice/core");
10456
+ var import_protocols5 = require("@axiom-lattice/protocols");
10457
+ function getTenantId18(request) {
10458
+ const userTenantId = request.user?.tenantId;
10459
+ if (userTenantId) return userTenantId;
10460
+ return request.headers["x-tenant-id"] || "default";
10461
+ }
10462
+ function getLocalRuntime(assistant) {
10463
+ const graphDefinition = assistant.graphDefinition;
10464
+ if (!graphDefinition || typeof graphDefinition !== "object") return void 0;
10465
+ if (graphDefinition.type !== import_protocols5.AgentType.A2A_REMOTE) return void 0;
10466
+ return graphDefinition.localRuntime;
10467
+ }
10468
+ async function getLocalA2AAssistants(tenantId) {
10469
+ const storeLattice = (0, import_core39.getStoreLattice)("default", "assistant");
10470
+ const assistants = await storeLattice.store.getAllAssistants(tenantId);
10471
+ const results = [];
10472
+ for (const assistant of assistants) {
10473
+ const runtime = getLocalRuntime(assistant);
10474
+ if (runtime) {
10475
+ results.push({ assistant, localRuntime: runtime });
10476
+ }
10477
+ }
10478
+ return results;
10479
+ }
10480
+ async function getLocalA2AAssistant(tenantId, assistantId) {
10481
+ const storeLattice = (0, import_core39.getStoreLattice)("default", "assistant");
10482
+ const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
10483
+ if (!assistant) return null;
10484
+ const runtime = getLocalRuntime(assistant);
10485
+ if (!runtime) return null;
10486
+ return { assistant, localRuntime: runtime };
10487
+ }
10488
+ async function requireManagedAssistant(request, reply) {
10489
+ const assistantId = request.params.assistantId;
10490
+ if (!assistantId) {
10491
+ reply.status(400).send({ success: false, error: "assistantId is required" });
10492
+ return null;
10493
+ }
10494
+ const registered = await getLocalA2AAssistant(getTenantId18(request), assistantId);
10495
+ if (!registered) {
10496
+ reply.status(404).send({
10497
+ success: false,
10498
+ error: `Assistant ${assistantId} is not a local managed A2A assistant`
10499
+ });
10500
+ return null;
10501
+ }
10502
+ return { assistantId, localRuntime: registered.localRuntime };
10503
+ }
10504
+ async function getLocalA2AStatus(request, reply) {
10505
+ const tenantId = getTenantId18(request);
10506
+ const managed = await getLocalA2AAssistants(tenantId);
10507
+ const states = await Promise.all(
10508
+ managed.map(
10509
+ ({ assistant, localRuntime }) => localA2AService.getRegisteredState(assistant.id, localRuntime)
10510
+ )
10511
+ );
10512
+ reply.send({ success: true, data: { records: states, total: states.length } });
10513
+ }
10514
+ async function getLocalA2AProvider(request, reply) {
10515
+ const registered = await requireManagedAssistant(request, reply);
10516
+ if (!registered) return;
10517
+ const state = await localA2AService.getRegisteredState(
10518
+ registered.assistantId,
10519
+ registered.localRuntime
10520
+ );
10521
+ reply.send({ success: true, data: state });
10522
+ }
10523
+ async function startLocalA2AProvider(request, reply) {
10524
+ const registered = await requireManagedAssistant(request, reply);
10525
+ if (!registered) return;
10526
+ const state = await localA2AService.startProvider(
10527
+ registered.assistantId,
10528
+ registered.localRuntime
10529
+ );
10530
+ reply.send({ success: true, data: state });
10531
+ }
10532
+ async function stopLocalA2AProvider(request, reply) {
10533
+ const registered = await requireManagedAssistant(request, reply);
10534
+ if (!registered) return;
10535
+ const state = await localA2AService.stopProvider(registered.assistantId);
10536
+ reply.send({ success: true, data: state });
10537
+ }
10538
+ async function restartLocalA2AProvider(request, reply) {
10539
+ const registered = await requireManagedAssistant(request, reply);
10540
+ if (!registered) return;
10541
+ const state = await localA2AService.restartProvider(
10542
+ registered.assistantId,
10543
+ registered.localRuntime
10544
+ );
10545
+ reply.send({ success: true, data: state });
10546
+ }
10547
+
10548
+ // src/config/local-a2a-templates.ts
10549
+ var import_cli_path = require("@axiom-lattice/cli-a2a/cli-path");
10550
+ function buildCliA2ACommand(provider, agentName, agentDescription) {
10551
+ return {
10552
+ executable: process.execPath,
10553
+ args: [
10554
+ import_cli_path.cliA2AEntryPath,
10555
+ "--provider",
10556
+ provider,
10557
+ "--hostname",
10558
+ "localhost",
10559
+ "--advertise-host",
10560
+ "localhost",
10561
+ "--agent-name",
10562
+ agentName,
10563
+ "--agent-description",
10564
+ agentDescription,
10565
+ "--log-level",
10566
+ "info"
10567
+ ]
10568
+ };
10569
+ }
10570
+ var LOCAL_A2A_TEMPLATES = [
10571
+ {
10572
+ key: "claude",
10573
+ name: "Claude Code",
10574
+ command: buildCliA2ACommand(
10575
+ "claude",
10576
+ "Claude Code",
10577
+ "Local Claude Code wrapper exposed through A2A."
10578
+ )
10579
+ },
10580
+ {
10581
+ key: "codex",
10582
+ name: "Codex",
10583
+ command: buildCliA2ACommand(
10584
+ "codex",
10585
+ "Codex",
10586
+ "Local Codex wrapper exposed through A2A."
10587
+ )
10588
+ },
10589
+ {
10590
+ key: "opencode",
10591
+ name: "OpenCode",
10592
+ command: buildCliA2ACommand(
10593
+ "opencode",
10594
+ "OpenCode",
10595
+ "Local OpenCode wrapper exposed through A2A."
10596
+ )
10597
+ }
10598
+ ];
10599
+
10600
+ // src/controllers/local-a2a-templates.ts
10601
+ async function getLocalA2ATemplates(_request, reply) {
10602
+ reply.send({
10603
+ success: true,
10604
+ data: {
10605
+ records: LOCAL_A2A_TEMPLATES,
10606
+ total: LOCAL_A2A_TEMPLATES.length
10607
+ }
10608
+ });
10609
+ }
10610
+
10611
+ // src/routes/a2a-test-connection.ts
10612
+ var import_node_crypto = require("crypto");
10613
+ function describeTestConnectionError(error, agentCardUrl) {
10614
+ if (!(error instanceof Error)) {
10615
+ return `Failed to fetch agent card from ${agentCardUrl}: ${String(error)}`;
10616
+ }
10617
+ const cause = error.cause;
10618
+ const causeParts = [];
10619
+ if (cause instanceof Error) {
10620
+ causeParts.push(cause.message);
10621
+ const causeCode = cause.code;
10622
+ if (causeCode && !causeParts.some((part) => part.includes(causeCode))) {
10623
+ causeParts.push(causeCode);
10624
+ }
10625
+ } else if (typeof cause === "object" && cause !== null) {
10626
+ const code = "code" in cause ? String(cause.code) : "";
10627
+ const message = "message" in cause ? String(cause.message) : "";
10628
+ if (message) causeParts.push(message);
10629
+ if (code && !message.includes(code)) causeParts.push(code);
10630
+ }
10631
+ const detail = causeParts.length > 0 ? ` (${causeParts.join(", ")})` : "";
10632
+ return `Failed to fetch agent card from ${agentCardUrl}: ${error.message}${detail}`;
10633
+ }
10634
+ async function ensureLocalRuntimeForConnectionTest(localRuntime, agentCardUrl) {
10635
+ const commandValidation = validateLocalRuntimeCommand(localRuntime);
10636
+ if (!commandValidation.ok) {
10637
+ return { error: commandValidation.error };
10638
+ }
10639
+ const testRuntime = {
10640
+ ...localRuntime,
10641
+ agentCardUrl,
10642
+ command: {
10643
+ ...localRuntime.command,
10644
+ executable: localRuntime.command.executable.trim()
10645
+ }
10646
+ };
10647
+ const portValidation = validateLocalRuntimePort(testRuntime, agentCardUrl);
10648
+ if (!portValidation.ok) {
10649
+ return { error: portValidation.error };
10650
+ }
10651
+ const state = await localA2AService.startProvider(
10652
+ `test-${(0, import_node_crypto.randomUUID)()}`,
10653
+ testRuntime
10654
+ );
10655
+ if (state.status === "running") return { runtimeId: state.runtimeId };
10656
+ const detail = state.message ? ` ${state.message}` : "";
10657
+ localA2AService.removeState(state.runtimeId);
10658
+ return { error: `Local managed A2A server could not start for connection test.${detail}` };
10659
+ }
10660
+ async function readShortResponseBody(resp) {
10661
+ const body = await resp.text().catch(() => "");
10662
+ return body.length > 500 ? `${body.slice(0, 500)}...` : body;
10663
+ }
10664
+ function registerA2ATestConnectionRoute(app2) {
10665
+ app2.post("/api/a2a/test-connection", async (request, reply) => {
10666
+ const { agentCardUrl, apiKey, localRuntime } = request.body ?? {};
10667
+ const normalizedAgentCardUrl = normalizeA2AAgentCardUrl(
10668
+ agentCardUrl,
10669
+ localRuntime ? "http://" : "https://"
10670
+ );
10671
+ if (!normalizedAgentCardUrl) {
10672
+ return reply.status(400).send({ success: false, error: "agentCardUrl is required" });
10673
+ }
10674
+ let testRuntimeId = null;
10675
+ try {
10676
+ const headers = {};
10677
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
10678
+ if (localRuntime) {
10679
+ const localStartResult = await ensureLocalRuntimeForConnectionTest(localRuntime, normalizedAgentCardUrl);
10680
+ if ("error" in localStartResult) {
10681
+ return reply.send({ success: true, data: { reachable: false, error: localStartResult.error } });
10682
+ }
10683
+ testRuntimeId = localStartResult.runtimeId;
10684
+ }
10685
+ const resp = await fetch(normalizedAgentCardUrl, { headers, signal: AbortSignal.timeout(8e3) });
10686
+ if (!resp.ok) {
10687
+ const body = await readShortResponseBody(resp);
10688
+ const suffix = body ? `: ${body}` : "";
10689
+ return reply.send({ success: true, data: { reachable: false, error: `HTTP ${resp.status} ${resp.statusText}${suffix}` } });
10690
+ }
10691
+ const card = await resp.json();
10692
+ return reply.send({ success: true, data: { reachable: true, agentName: card.name, agentCard: card } });
10693
+ } catch (error) {
10694
+ return reply.send({
10695
+ success: true,
10696
+ data: {
10697
+ reachable: false,
10698
+ error: describeTestConnectionError(error, normalizedAgentCardUrl)
10699
+ }
10700
+ });
10701
+ } finally {
10702
+ if (testRuntimeId) {
10703
+ localA2AService.removeState(testRuntimeId);
10704
+ }
10705
+ }
10706
+ });
10707
+ }
10708
+
9794
10709
  // src/routes/index.ts
9795
10710
  var registerLatticeRoutes = (app2, channelDeps) => {
9796
10711
  registerA2ABridgeRoutes(app2);
9797
10712
  registerSTTRoutes(app2);
10713
+ app2.get("/api/local-a2a", getLocalA2AStatus);
10714
+ app2.get("/api/local-a2a/templates", getLocalA2ATemplates);
10715
+ app2.get("/api/local-a2a/:assistantId", getLocalA2AProvider);
10716
+ app2.post("/api/local-a2a/:assistantId/start", startLocalA2AProvider);
10717
+ app2.post("/api/local-a2a/:assistantId/stop", stopLocalA2AProvider);
10718
+ app2.post("/api/local-a2a/:assistantId/restart", restartLocalA2AProvider);
10719
+ registerA2ATestConnectionRoute(app2);
9798
10720
  app2.post("/api/runs", createRun);
9799
10721
  app2.post("/api/resume_stream", resumeStream);
9800
10722
  app2.post("/api/assistants/:assistantId/threads/:threadId/abort", abortRun);
@@ -10086,12 +11008,12 @@ function registerResourceRoutes(app2, controller) {
10086
11008
  }
10087
11009
 
10088
11010
  // src/export_registrations/index.ts
10089
- var import_core52 = require("@axiom-lattice/core");
11011
+ var import_core53 = require("@axiom-lattice/core");
10090
11012
 
10091
11013
  // src/export_registrations/skill.registration.ts
10092
- var import_core39 = require("@axiom-lattice/core");
11014
+ var import_core40 = require("@axiom-lattice/core");
10093
11015
  function getStore2() {
10094
- return (0, import_core39.getStoreLattice)("default", "skill").store;
11016
+ return (0, import_core40.getStoreLattice)("default", "skill").store;
10095
11017
  }
10096
11018
  var skillRegistration = {
10097
11019
  entityType: "skill",
@@ -10166,9 +11088,9 @@ var skillRegistration = {
10166
11088
  };
10167
11089
 
10168
11090
  // src/export_registrations/agent.registration.ts
10169
- var import_core40 = require("@axiom-lattice/core");
11091
+ var import_core41 = require("@axiom-lattice/core");
10170
11092
  function getStore3() {
10171
- return (0, import_core40.getStoreLattice)("default", "assistant").store;
11093
+ return (0, import_core41.getStoreLattice)("default", "assistant").store;
10172
11094
  }
10173
11095
  var agentRegistration = {
10174
11096
  entityType: "agent",
@@ -10235,9 +11157,9 @@ var agentRegistration = {
10235
11157
  };
10236
11158
 
10237
11159
  // src/export_registrations/binding.registration.ts
10238
- var import_core41 = require("@axiom-lattice/core");
11160
+ var import_core42 = require("@axiom-lattice/core");
10239
11161
  function getStore4() {
10240
- return (0, import_core41.getStoreLattice)("default", "channelBinding").store;
11162
+ return (0, import_core42.getStoreLattice)("default", "channelBinding").store;
10241
11163
  }
10242
11164
  var bindingRegistration = {
10243
11165
  entityType: "binding",
@@ -10325,9 +11247,9 @@ var bindingRegistration = {
10325
11247
  };
10326
11248
 
10327
11249
  // src/export_registrations/database-config.registration.ts
10328
- var import_core42 = require("@axiom-lattice/core");
11250
+ var import_core43 = require("@axiom-lattice/core");
10329
11251
  function getStore5() {
10330
- return (0, import_core42.getStoreLattice)("default", "database").store;
11252
+ return (0, import_core43.getStoreLattice)("default", "database").store;
10331
11253
  }
10332
11254
  var databaseConfigRegistration = {
10333
11255
  entityType: "database_config",
@@ -10407,9 +11329,9 @@ var databaseConfigRegistration = {
10407
11329
  };
10408
11330
 
10409
11331
  // src/export_registrations/metrics-config.registration.ts
10410
- var import_core43 = require("@axiom-lattice/core");
11332
+ var import_core44 = require("@axiom-lattice/core");
10411
11333
  function getStore6() {
10412
- return (0, import_core43.getStoreLattice)("default", "metrics").store;
11334
+ return (0, import_core44.getStoreLattice)("default", "metrics").store;
10413
11335
  }
10414
11336
  var metricsConfigRegistration = {
10415
11337
  entityType: "metrics_config",
@@ -10489,9 +11411,9 @@ var metricsConfigRegistration = {
10489
11411
  };
10490
11412
 
10491
11413
  // src/export_registrations/mcp-config.registration.ts
10492
- var import_core44 = require("@axiom-lattice/core");
11414
+ var import_core45 = require("@axiom-lattice/core");
10493
11415
  function getStore7() {
10494
- return (0, import_core44.getStoreLattice)("default", "mcp").store;
11416
+ return (0, import_core45.getStoreLattice)("default", "mcp").store;
10495
11417
  }
10496
11418
  var mcpConfigRegistration = {
10497
11419
  entityType: "mcp_config",
@@ -10574,9 +11496,9 @@ var mcpConfigRegistration = {
10574
11496
  };
10575
11497
 
10576
11498
  // src/export_registrations/connection.registration.ts
10577
- var import_core45 = require("@axiom-lattice/core");
11499
+ var import_core46 = require("@axiom-lattice/core");
10578
11500
  function getStore8() {
10579
- return (0, import_core45.getStoreLattice)("default", "connection").store;
11501
+ return (0, import_core46.getStoreLattice)("default", "connection").store;
10580
11502
  }
10581
11503
  async function listAllConnections(tenantId) {
10582
11504
  const store2 = getStore8();
@@ -10658,9 +11580,9 @@ var connectionRegistration = {
10658
11580
  };
10659
11581
 
10660
11582
  // src/export_registrations/collection.registration.ts
10661
- var import_core46 = require("@axiom-lattice/core");
11583
+ var import_core47 = require("@axiom-lattice/core");
10662
11584
  function getStore9() {
10663
- return (0, import_core46.getStoreLattice)("default", "collection").store;
11585
+ return (0, import_core47.getStoreLattice)("default", "collection").store;
10664
11586
  }
10665
11587
  var collectionRegistration = {
10666
11588
  entityType: "collection",
@@ -10731,9 +11653,9 @@ var collectionRegistration = {
10731
11653
  };
10732
11654
 
10733
11655
  // src/export_registrations/channel-installation.registration.ts
10734
- var import_core47 = require("@axiom-lattice/core");
11656
+ var import_core48 = require("@axiom-lattice/core");
10735
11657
  function getStore10() {
10736
- return (0, import_core47.getStoreLattice)("default", "channelInstallation").store;
11658
+ return (0, import_core48.getStoreLattice)("default", "channelInstallation").store;
10737
11659
  }
10738
11660
  var channelInstallationRegistration = {
10739
11661
  entityType: "channel_installation",
@@ -10808,9 +11730,9 @@ var channelInstallationRegistration = {
10808
11730
  };
10809
11731
 
10810
11732
  // src/export_registrations/menu.registration.ts
10811
- var import_core48 = require("@axiom-lattice/core");
11733
+ var import_core49 = require("@axiom-lattice/core");
10812
11734
  function getStore11() {
10813
- return (0, import_core48.getStoreLattice)("default", "menu").store;
11735
+ return (0, import_core49.getStoreLattice)("default", "menu").store;
10814
11736
  }
10815
11737
  var menuRegistration = {
10816
11738
  entityType: "menu",
@@ -10888,9 +11810,9 @@ var menuRegistration = {
10888
11810
  };
10889
11811
 
10890
11812
  // src/export_registrations/task.registration.ts
10891
- var import_core49 = require("@axiom-lattice/core");
11813
+ var import_core50 = require("@axiom-lattice/core");
10892
11814
  function getStore12() {
10893
- return (0, import_core49.getStoreLattice)("default", "task").store;
11815
+ return (0, import_core50.getStoreLattice)("default", "task").store;
10894
11816
  }
10895
11817
  var taskRegistration = {
10896
11818
  entityType: "task",
@@ -10976,9 +11898,9 @@ var taskRegistration = {
10976
11898
  };
10977
11899
 
10978
11900
  // src/export_registrations/a2a-api-key.registration.ts
10979
- var import_core50 = require("@axiom-lattice/core");
11901
+ var import_core51 = require("@axiom-lattice/core");
10980
11902
  function getStore13() {
10981
- return (0, import_core50.getStoreLattice)("default", "a2aApiKey").store;
11903
+ return (0, import_core51.getStoreLattice)("default", "a2aApiKey").store;
10982
11904
  }
10983
11905
  var a2aApiKeyRegistration = {
10984
11906
  entityType: "a2a_api_key",
@@ -11049,9 +11971,9 @@ var a2aApiKeyRegistration = {
11049
11971
  };
11050
11972
 
11051
11973
  // src/export_registrations/eval.registration.ts
11052
- var import_core51 = require("@axiom-lattice/core");
11974
+ var import_core52 = require("@axiom-lattice/core");
11053
11975
  function getStore14() {
11054
- return (0, import_core51.getStoreLattice)("default", "eval").store;
11976
+ return (0, import_core52.getStoreLattice)("default", "eval").store;
11055
11977
  }
11056
11978
  var evalRegistration = {
11057
11979
  entityType: "eval",
@@ -11377,7 +12299,7 @@ var evalRegistration = {
11377
12299
 
11378
12300
  // src/export_registrations/index.ts
11379
12301
  function registerAllBuiltinEntities() {
11380
- const registry = import_core52.ExportableEntityRegistry.getInstance();
12302
+ const registry = import_core53.ExportableEntityRegistry.getInstance();
11381
12303
  registry.register(skillRegistration);
11382
12304
  registry.register(agentRegistration);
11383
12305
  registry.register(bindingRegistration);
@@ -11394,7 +12316,7 @@ function registerAllBuiltinEntities() {
11394
12316
  }
11395
12317
 
11396
12318
  // src/router/MessageRouter.ts
11397
- var import_core53 = require("@axiom-lattice/core");
12319
+ var import_core54 = require("@axiom-lattice/core");
11398
12320
  var import_crypto10 = require("crypto");
11399
12321
  var BindingNotFoundError = class extends Error {
11400
12322
  constructor(message) {
@@ -11555,7 +12477,7 @@ var MessageRouter = class {
11555
12477
  channel: message.channel,
11556
12478
  adapterChannel: adapter.channel
11557
12479
  }, "Thread resolved by adapter strategy");
11558
- const threadStore = (0, import_core53.getStoreLattice)("default", "thread").store;
12480
+ const threadStore = (0, import_core54.getStoreLattice)("default", "thread").store;
11559
12481
  try {
11560
12482
  await threadStore.createThread(
11561
12483
  tenantId,
@@ -11592,7 +12514,7 @@ var MessageRouter = class {
11592
12514
  }
11593
12515
  }
11594
12516
  if (!threadId) {
11595
- const threadStore = (0, import_core53.getStoreLattice)("default", "thread").store;
12517
+ const threadStore = (0, import_core54.getStoreLattice)("default", "thread").store;
11596
12518
  const newThreadId = (0, import_crypto10.randomUUID)();
11597
12519
  console.log({
11598
12520
  event: "dispatch:thread:create",
@@ -11632,7 +12554,7 @@ var MessageRouter = class {
11632
12554
  senderId: message.sender.id,
11633
12555
  contentLength: message.content.text.length
11634
12556
  }, "Dispatching to agent");
11635
- const agent = import_core53.agentInstanceManager.getAgent({
12557
+ const agent = import_core54.agentInstanceManager.getAgent({
11636
12558
  tenant_id: tenantId,
11637
12559
  assistant_id: agentId,
11638
12560
  thread_id: threadId,
@@ -11877,7 +12799,7 @@ function createAuditLoggerMiddleware() {
11877
12799
  }
11878
12800
 
11879
12801
  // src/index.ts
11880
- var import_core57 = require("@axiom-lattice/core");
12802
+ var import_core58 = require("@axiom-lattice/core");
11881
12803
 
11882
12804
  // src/swagger.ts
11883
12805
  var import_swagger = __toESM(require("@fastify/swagger"));
@@ -11942,7 +12864,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
11942
12864
  };
11943
12865
 
11944
12866
  // src/services/agent_task_consumer.ts
11945
- var import_core54 = require("@axiom-lattice/core");
12867
+ var import_core55 = require("@axiom-lattice/core");
11946
12868
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
11947
12869
  const {
11948
12870
  assistant_id,
@@ -11960,17 +12882,17 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
11960
12882
  console.log(
11961
12883
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
11962
12884
  );
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);
12885
+ const agent = import_core55.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
12886
+ await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core55.QueueMode.STEER);
11965
12887
  if (callback_event) {
11966
12888
  agent.subscribeOnce("message:completed", (evt) => {
11967
- import_core54.eventBus.publish(callback_event, {
12889
+ import_core55.eventBus.publish(callback_event, {
11968
12890
  success: true,
11969
12891
  state: evt.state
11970
12892
  });
11971
12893
  if (main_thread_id && main_tenant_id) {
11972
12894
  try {
11973
- const mainAgent = import_core54.agentInstanceManager.getAgent({
12895
+ const mainAgent = import_core55.agentInstanceManager.getAgent({
11974
12896
  assistant_id: main_assistant_id ?? assistant_id,
11975
12897
  thread_id: main_thread_id,
11976
12898
  tenant_id: main_tenant_id,
@@ -12000,7 +12922,7 @@ ${summary}`
12000
12922
  }
12001
12923
  });
12002
12924
  agent.subscribeOnce("message:interrupted", (evt) => {
12003
- import_core54.eventBus.publish(callback_event, {
12925
+ import_core55.eventBus.publish(callback_event, {
12004
12926
  success: true,
12005
12927
  state: evt.state
12006
12928
  });
@@ -12019,11 +12941,11 @@ ${summary}`
12019
12941
  console.log(
12020
12942
  `\u5C06\u5728 ${delayMs}ms \u540E\u91CD\u8BD5\u4EFB\u52A1 (${nextRetryCount}/${maxRetries})`
12021
12943
  );
12022
- await new Promise((resolve) => setTimeout(resolve, delayMs));
12944
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
12023
12945
  return handleAgentTask(taskRequest, nextRetryCount);
12024
12946
  }
12025
12947
  if (callback_event) {
12026
- import_core54.eventBus.publish(callback_event, {
12948
+ import_core55.eventBus.publish(callback_event, {
12027
12949
  success: false,
12028
12950
  error: error instanceof Error ? error.message : String(error)
12029
12951
  });
@@ -12060,7 +12982,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
12060
12982
  * 初始化事件监听和队列轮询
12061
12983
  */
12062
12984
  initialize() {
12063
- import_core54.eventBus.subscribe(import_core54.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
12985
+ import_core55.eventBus.subscribe(import_core55.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
12064
12986
  this.startPollingQueue();
12065
12987
  console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
12066
12988
  }
@@ -12179,7 +13101,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
12179
13101
  handleAgentTask(taskRequest).catch((error) => {
12180
13102
  console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
12181
13103
  if (taskRequest.callback_event) {
12182
- import_core54.eventBus.publish(taskRequest.callback_event, {
13104
+ import_core55.eventBus.publish(taskRequest.callback_event, {
12183
13105
  success: false,
12184
13106
  error: error instanceof Error ? error.message : String(error)
12185
13107
  });
@@ -12194,8 +13116,8 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
12194
13116
  var AgentTaskConsumer = _AgentTaskConsumer;
12195
13117
 
12196
13118
  // src/index.ts
12197
- var import_core58 = require("@axiom-lattice/core");
12198
- var import_protocols5 = require("@axiom-lattice/protocols");
13119
+ var import_core59 = require("@axiom-lattice/core");
13120
+ var import_protocols7 = require("@axiom-lattice/protocols");
12199
13121
  var import_meta = {};
12200
13122
  process.on("unhandledRejection", (reason, promise) => {
12201
13123
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
@@ -12203,18 +13125,18 @@ process.on("unhandledRejection", (reason, promise) => {
12203
13125
  var DEFAULT_LOGGER_CONFIG = {
12204
13126
  name: "default",
12205
13127
  description: "Default logger for lattice-gateway service",
12206
- type: import_protocols5.LoggerType.PINO,
13128
+ type: import_protocols7.LoggerType.PINO,
12207
13129
  serviceName: "lattice/gateway",
12208
13130
  loggerName: "lattice/gateway"
12209
13131
  };
12210
13132
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
12211
13133
  var logger5 = loggerLattice.client;
12212
13134
  function initializeLogger(config) {
12213
- if (import_core58.loggerLatticeManager.hasLattice("default")) {
12214
- import_core58.loggerLatticeManager.removeLattice("default");
13135
+ if (import_core59.loggerLatticeManager.hasLattice("default")) {
13136
+ import_core59.loggerLatticeManager.removeLattice("default");
12215
13137
  }
12216
- (0, import_core58.registerLoggerLattice)("default", config);
12217
- return (0, import_core58.getLoggerLattice)("default");
13138
+ (0, import_core59.registerLoggerLattice)("default", config);
13139
+ return (0, import_core59.getLoggerLattice)("default");
12218
13140
  }
12219
13141
  var app = (0, import_fastify.default)({
12220
13142
  logger: false,
@@ -12325,7 +13247,7 @@ app.setErrorHandler((error, request, reply) => {
12325
13247
  });
12326
13248
  function getConfiguredSandboxProvider() {
12327
13249
  const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
12328
- return (0, import_core58.createSandboxProvider)({
13250
+ return (0, import_core59.createSandboxProvider)({
12329
13251
  type: sandboxProviderType,
12330
13252
  remoteBaseURL: process.env.SANDBOX_BASE_URL,
12331
13253
  microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
@@ -12341,7 +13263,7 @@ function getConfiguredSandboxProvider() {
12341
13263
  }
12342
13264
  async function restoreMcpConnections() {
12343
13265
  try {
12344
- const storeLattice = (0, import_core58.getStoreLattice)("default", "mcp");
13266
+ const storeLattice = (0, import_core59.getStoreLattice)("default", "mcp");
12345
13267
  const store2 = storeLattice.store;
12346
13268
  if (!store2) {
12347
13269
  logger5.info("MCP store not configured, skipping connection restoration");
@@ -12355,15 +13277,15 @@ async function restoreMcpConnections() {
12355
13277
  for (const config of connectedConfigs) {
12356
13278
  try {
12357
13279
  const connection = convertToConnection2(config.config);
12358
- import_core58.mcpManager.addServer(config.key, connection);
13280
+ import_core59.mcpManager.addServer(config.key, connection);
12359
13281
  } catch (err) {
12360
13282
  logger5.warn(`Failed to prepare MCP server "${config.key}" for restoration`, { error: err });
12361
13283
  }
12362
13284
  }
12363
- await import_core58.mcpManager.connect();
13285
+ await import_core59.mcpManager.connect();
12364
13286
  for (const config of connectedConfigs) {
12365
13287
  try {
12366
- await import_core58.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
13288
+ await import_core59.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
12367
13289
  logger5.info(`MCP server "${config.key}" restored with ${config.selectedTools.length} tools`);
12368
13290
  } catch (err) {
12369
13291
  logger5.warn(`Failed to register tools for MCP server "${config.key}"`, { error: err });
@@ -12396,7 +13318,7 @@ var start = async (config) => {
12396
13318
  const { getStoreLattice: getStore15 } = await import("@axiom-lattice/core");
12397
13319
  const bindingStore = getStore15("default", "channelBinding").store;
12398
13320
  const installationStore = getStore15("default", "channelInstallation").store;
12399
- (0, import_core57.setBindingRegistry)(bindingStore);
13321
+ (0, import_core58.setBindingRegistry)(bindingStore);
12400
13322
  const router = new MessageRouter({
12401
13323
  middlewares: [
12402
13324
  createDeduplicationMiddleware(),
@@ -12421,10 +13343,10 @@ var start = async (config) => {
12421
13343
  error: err instanceof Error ? err.message : String(err)
12422
13344
  });
12423
13345
  }
12424
- (0, import_core57.setEvalRunService)(evalRunner);
13346
+ (0, import_core58.setEvalRunService)(evalRunner);
12425
13347
  try {
12426
- const menuStore = (0, import_core58.getStoreLattice)("default", "menu").store;
12427
- (0, import_core57.setMenuRegistry)(menuStore);
13348
+ const menuStore = (0, import_core59.getStoreLattice)("default", "menu").store;
13349
+ (0, import_core58.setMenuRegistry)(menuStore);
12428
13350
  logger5.info("Menu registry initialized");
12429
13351
  } catch {
12430
13352
  }
@@ -12433,17 +13355,17 @@ var start = async (config) => {
12433
13355
  setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
12434
13356
  }
12435
13357
  registerLatticeRoutes(app, channelDeps);
12436
- if (!import_core58.sandboxLatticeManager.hasLattice("default")) {
12437
- import_core58.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
13358
+ if (!import_core59.sandboxLatticeManager.hasLattice("default")) {
13359
+ import_core59.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
12438
13360
  logger5.info("Registered sandbox manager from env configuration");
12439
13361
  }
12440
13362
  try {
12441
13363
  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();
13364
+ const sharedResourceStore = (0, import_core59.getStoreLattice)("default", "sharedResource").store;
13365
+ const cache = new import_core59.TokenCache();
12444
13366
  const resourceController = new ResourceController2({
12445
13367
  store: sharedResourceStore,
12446
- sandboxManager: import_core58.sandboxLatticeManager,
13368
+ sandboxManager: import_core59.sandboxLatticeManager,
12447
13369
  cache,
12448
13370
  logger: logger5
12449
13371
  });
@@ -12470,11 +13392,6 @@ var start = async (config) => {
12470
13392
  const target_port = config?.port || Number(process.env.PORT) || 4001;
12471
13393
  await app.listen({ port: target_port, host: "0.0.0.0" });
12472
13394
  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
13395
  const queueServiceConfig = config?.queueServiceConfig;
12479
13396
  if (queueServiceConfig) {
12480
13397
  setQueueServiceType(queueServiceConfig.type);
@@ -12484,7 +13401,7 @@ var start = async (config) => {
12484
13401
  }
12485
13402
  }
12486
13403
  if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
12487
- import_core58.agentInstanceManager.restore().then((stats) => {
13404
+ import_core59.agentInstanceManager.restore().then((stats) => {
12488
13405
  logger5.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
12489
13406
  }).catch((error) => {
12490
13407
  logger5.error("Agent recovery failed", { error });