@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.
- package/.turbo/turbo-build.log +12 -12
- package/CHANGELOG.md +26 -0
- package/dist/{a2a-ERG5RMUW.mjs → a2a-QMHEH3ZT.mjs} +77 -29
- package/dist/a2a-QMHEH3ZT.mjs.map +1 -0
- package/dist/index.js +1064 -176
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +933 -91
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
- package/src/__tests__/a2a.test.ts +82 -0
- package/src/__tests__/workspace.test.ts +13 -3
- package/src/config/local-a2a-templates.ts +59 -0
- package/src/controllers/__tests__/assistant-local-a2a.test.ts +147 -0
- package/src/controllers/__tests__/local-a2a-templates.test.ts +112 -0
- package/src/controllers/assistant.ts +126 -4
- package/src/controllers/local-a2a-templates.ts +18 -0
- package/src/controllers/local-a2a.ts +149 -0
- package/src/controllers/tasks.ts +2 -3
- package/src/controllers/workspace.ts +144 -17
- package/src/index.ts +1 -9
- package/src/routes/__tests__/a2a-test-connection.test.ts +177 -0
- package/src/routes/a2a-test-connection.ts +126 -0
- package/src/routes/a2a.ts +100 -28
- package/src/routes/index.ts +23 -0
- package/src/services/__tests__/local-a2a.test.ts +124 -0
- package/src/services/eval-runner.ts +6 -0
- package/src/services/local-a2a.ts +352 -0
- package/src/utils/__tests__/local-a2a-runtime.test.ts +77 -0
- package/src/utils/__tests__/path-containment.test.ts +35 -0
- package/src/utils/a2a-url.ts +20 -0
- package/src/utils/local-a2a-runtime.ts +107 -0
- package/src/utils/path-containment.ts +38 -0
- 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(
|
|
39
|
-
const segments =
|
|
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(
|
|
707
|
-
const url = `${getBaseUrl()}${
|
|
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 ${
|
|
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(
|
|
716
|
-
const url = `${getBaseUrl()}${
|
|
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 ${
|
|
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((
|
|
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 ===
|
|
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
|
-
|
|
1197
|
+
interrupted = true;
|
|
1198
|
+
break;
|
|
1196
1199
|
}
|
|
1197
|
-
if (chunkType ===
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
1506
|
+
import_protocols6.MessageChunkTypes.MESSAGE_COMPLETED
|
|
1463
1507
|
]);
|
|
1464
|
-
await streamChunksAsA2A(stream, taskId, sendEvent);
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
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,
|
|
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
|
-
|
|
1623
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
1866
|
-
return /\.[a-zA-Z0-9]+$/.test(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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, [
|
|
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((
|
|
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
|
|
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" ?
|
|
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
|
|
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
|
-
[
|
|
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
|
-
|
|
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
|
|
6025
|
-
var
|
|
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
|
|
6928
|
+
const path4 = request.query.path || "/";
|
|
6390
6929
|
const assistantId = request.query.assistantId;
|
|
6391
|
-
console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${
|
|
6392
|
-
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId,
|
|
6393
|
-
console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${
|
|
6394
|
-
const files = await backend.lsInfo(
|
|
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:
|
|
6402
|
-
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId,
|
|
6403
|
-
const content = await backend.read(
|
|
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
|
|
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 ?
|
|
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 ?
|
|
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 =
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
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:
|
|
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: ${
|
|
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((
|
|
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:
|
|
9076
|
-
const store2 =
|
|
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(
|
|
9558
|
-
const match =
|
|
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((
|
|
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
|
|
10982
|
+
var import_core53 = require("@axiom-lattice/core");
|
|
10090
10983
|
|
|
10091
10984
|
// src/export_registrations/skill.registration.ts
|
|
10092
|
-
var
|
|
10985
|
+
var import_core40 = require("@axiom-lattice/core");
|
|
10093
10986
|
function getStore2() {
|
|
10094
|
-
return (0,
|
|
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
|
|
11062
|
+
var import_core41 = require("@axiom-lattice/core");
|
|
10170
11063
|
function getStore3() {
|
|
10171
|
-
return (0,
|
|
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
|
|
11131
|
+
var import_core42 = require("@axiom-lattice/core");
|
|
10239
11132
|
function getStore4() {
|
|
10240
|
-
return (0,
|
|
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
|
|
11221
|
+
var import_core43 = require("@axiom-lattice/core");
|
|
10329
11222
|
function getStore5() {
|
|
10330
|
-
return (0,
|
|
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
|
|
11303
|
+
var import_core44 = require("@axiom-lattice/core");
|
|
10411
11304
|
function getStore6() {
|
|
10412
|
-
return (0,
|
|
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
|
|
11385
|
+
var import_core45 = require("@axiom-lattice/core");
|
|
10493
11386
|
function getStore7() {
|
|
10494
|
-
return (0,
|
|
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
|
|
11470
|
+
var import_core46 = require("@axiom-lattice/core");
|
|
10578
11471
|
function getStore8() {
|
|
10579
|
-
return (0,
|
|
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
|
|
11554
|
+
var import_core47 = require("@axiom-lattice/core");
|
|
10662
11555
|
function getStore9() {
|
|
10663
|
-
return (0,
|
|
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
|
|
11627
|
+
var import_core48 = require("@axiom-lattice/core");
|
|
10735
11628
|
function getStore10() {
|
|
10736
|
-
return (0,
|
|
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
|
|
11704
|
+
var import_core49 = require("@axiom-lattice/core");
|
|
10812
11705
|
function getStore11() {
|
|
10813
|
-
return (0,
|
|
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
|
|
11784
|
+
var import_core50 = require("@axiom-lattice/core");
|
|
10892
11785
|
function getStore12() {
|
|
10893
|
-
return (0,
|
|
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
|
|
11872
|
+
var import_core51 = require("@axiom-lattice/core");
|
|
10980
11873
|
function getStore13() {
|
|
10981
|
-
return (0,
|
|
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
|
|
11945
|
+
var import_core52 = require("@axiom-lattice/core");
|
|
11053
11946
|
function getStore14() {
|
|
11054
|
-
return (0,
|
|
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 =
|
|
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
|
|
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,
|
|
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,
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
11964
|
-
await agent.addMessage({ input, command, custom_run_config: runConfig },
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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((
|
|
12915
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
12023
12916
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
12024
12917
|
}
|
|
12025
12918
|
if (callback_event) {
|
|
12026
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
12198
|
-
var
|
|
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:
|
|
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 (
|
|
12214
|
-
|
|
13106
|
+
if (import_core59.loggerLatticeManager.hasLattice("default")) {
|
|
13107
|
+
import_core59.loggerLatticeManager.removeLattice("default");
|
|
12215
13108
|
}
|
|
12216
|
-
(0,
|
|
12217
|
-
return (0,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
13256
|
+
await import_core59.mcpManager.connect();
|
|
12364
13257
|
for (const config of connectedConfigs) {
|
|
12365
13258
|
try {
|
|
12366
|
-
await
|
|
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,
|
|
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,
|
|
13317
|
+
(0, import_core58.setEvalRunService)(evalRunner);
|
|
12425
13318
|
try {
|
|
12426
|
-
const menuStore = (0,
|
|
12427
|
-
(0,
|
|
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 (!
|
|
12437
|
-
|
|
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,
|
|
12443
|
-
const cache = new
|
|
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:
|
|
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
|
-
|
|
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 });
|