@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.
- package/.turbo/turbo-build.log +12 -12
- package/CHANGELOG.md +38 -0
- package/dist/{a2a-ERG5RMUW.mjs → a2a-QMHEH3ZT.mjs} +77 -29
- package/dist/a2a-QMHEH3ZT.mjs.map +1 -0
- package/dist/index.js +1098 -181
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +967 -96
- 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/eval.ts +6 -2
- 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__/eval-runner.test.ts +131 -0
- package/src/services/__tests__/local-a2a.test.ts +124 -0
- package/src/services/eval-runner.ts +50 -3
- package/src/services/local-a2a.ts +354 -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.mjs
CHANGED
|
@@ -12,19 +12,387 @@ import {
|
|
|
12
12
|
} from "./chunk-LHQY46YB.mjs";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
|
+
import "dotenv/config";
|
|
15
16
|
import fastify from "fastify";
|
|
16
17
|
import cors from "@fastify/cors";
|
|
17
18
|
import multipart from "@fastify/multipart";
|
|
18
19
|
import sensible from "@fastify/sensible";
|
|
19
20
|
import websocket from "@fastify/websocket";
|
|
20
21
|
import staticPlugin from "@fastify/static";
|
|
21
|
-
import
|
|
22
|
+
import path3 from "path";
|
|
22
23
|
import { fileURLToPath } from "url";
|
|
23
24
|
|
|
24
25
|
// src/controllers/assistant.ts
|
|
25
26
|
import { agentInstanceManager, getStoreLattice } from "@axiom-lattice/core";
|
|
27
|
+
import { AgentType } from "@axiom-lattice/protocols";
|
|
26
28
|
import { randomUUID } from "crypto";
|
|
27
29
|
import { agentLatticeManager, eventBus } from "@axiom-lattice/core";
|
|
30
|
+
|
|
31
|
+
// src/services/local-a2a.ts
|
|
32
|
+
import { spawn } from "child_process";
|
|
33
|
+
import { existsSync, statSync } from "fs";
|
|
34
|
+
import { dirname, join, parse, resolve } from "path";
|
|
35
|
+
import { setTimeout as delay } from "timers/promises";
|
|
36
|
+
var STDERR_TAIL_LIMIT = 4096;
|
|
37
|
+
function readBooleanEnv(name, defaultValue, env = process.env) {
|
|
38
|
+
const value = env[name];
|
|
39
|
+
if (value === void 0) return defaultValue;
|
|
40
|
+
return value === "1" || value.toLowerCase() === "true";
|
|
41
|
+
}
|
|
42
|
+
function findWorkspaceRoot(startDir) {
|
|
43
|
+
let current = resolve(startDir);
|
|
44
|
+
const { root } = parse(current);
|
|
45
|
+
while (current !== root) {
|
|
46
|
+
if (existsSync(join(current, "pnpm-workspace.yaml"))) {
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
const parent = dirname(current);
|
|
50
|
+
if (parent === current) return null;
|
|
51
|
+
current = parent;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function resolveLocalA2AWorkingDirectory(options) {
|
|
56
|
+
if (options.envWorkdir) return options.envWorkdir;
|
|
57
|
+
if (options.commandCwd) return options.commandCwd;
|
|
58
|
+
return findWorkspaceRoot(options.currentCwd) || options.currentCwd;
|
|
59
|
+
}
|
|
60
|
+
function validateWorkingDirectory(cwd) {
|
|
61
|
+
try {
|
|
62
|
+
const stat2 = statSync(cwd);
|
|
63
|
+
if (!stat2.isDirectory()) {
|
|
64
|
+
return `Working directory is not a directory: ${cwd}`;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return `Working directory is not accessible: ${cwd} (${message})`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function appendStderrTail(current, chunk) {
|
|
73
|
+
const next = `${current}${chunk}`;
|
|
74
|
+
if (next.length <= STDERR_TAIL_LIMIT) return next;
|
|
75
|
+
return next.slice(-STDERR_TAIL_LIMIT);
|
|
76
|
+
}
|
|
77
|
+
function formatExitMessage(exitReason, stderrTail) {
|
|
78
|
+
const stderr = stderrTail.trim();
|
|
79
|
+
return stderr ? `Exited before becoming ready (${exitReason}): ${stderr}` : `Exited before becoming ready (${exitReason})`;
|
|
80
|
+
}
|
|
81
|
+
async function probeReady(agentCardUrl, healthUrl, timeoutMs) {
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
84
|
+
try {
|
|
85
|
+
if (healthUrl) {
|
|
86
|
+
const resp = await fetch(healthUrl, { signal: controller.signal });
|
|
87
|
+
if (!resp.ok) return false;
|
|
88
|
+
}
|
|
89
|
+
const cardResp = await fetch(agentCardUrl, { signal: controller.signal });
|
|
90
|
+
return cardResp.ok;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
} finally {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var _LocalA2AService = class _LocalA2AService {
|
|
98
|
+
constructor() {
|
|
99
|
+
this.states = /* @__PURE__ */ new Map();
|
|
100
|
+
this.autoStartEnabled = readBooleanEnv("LOCAL_A2A_AUTO_START", true);
|
|
101
|
+
}
|
|
102
|
+
static getInstance() {
|
|
103
|
+
if (!_LocalA2AService.instance) {
|
|
104
|
+
_LocalA2AService.instance = new _LocalA2AService();
|
|
105
|
+
}
|
|
106
|
+
return _LocalA2AService.instance;
|
|
107
|
+
}
|
|
108
|
+
getOrCreateState(runtimeId, localRuntime) {
|
|
109
|
+
let state = this.states.get(runtimeId);
|
|
110
|
+
if (!state) {
|
|
111
|
+
state = {
|
|
112
|
+
runtimeId,
|
|
113
|
+
label: localRuntime.label || runtimeId,
|
|
114
|
+
status: this.autoStartEnabled ? "stopped" : "disabled",
|
|
115
|
+
enabled: this.autoStartEnabled,
|
|
116
|
+
assistantId: runtimeId,
|
|
117
|
+
agentCardUrl: localRuntime.agentCardUrl,
|
|
118
|
+
healthUrl: localRuntime.healthUrl,
|
|
119
|
+
localRuntime
|
|
120
|
+
};
|
|
121
|
+
this.states.set(runtimeId, state);
|
|
122
|
+
} else {
|
|
123
|
+
state.localRuntime = localRuntime;
|
|
124
|
+
state.label = localRuntime.label || state.label;
|
|
125
|
+
state.agentCardUrl = localRuntime.agentCardUrl;
|
|
126
|
+
state.healthUrl = localRuntime.healthUrl;
|
|
127
|
+
}
|
|
128
|
+
return state;
|
|
129
|
+
}
|
|
130
|
+
async getRegisteredState(runtimeId, localRuntime) {
|
|
131
|
+
const state = this.getOrCreateState(runtimeId, localRuntime);
|
|
132
|
+
if (state.process && state.status === "running") {
|
|
133
|
+
const alive = await probeReady(
|
|
134
|
+
localRuntime.agentCardUrl,
|
|
135
|
+
localRuntime.healthUrl,
|
|
136
|
+
2e3
|
|
137
|
+
);
|
|
138
|
+
if (!alive) {
|
|
139
|
+
state.process = void 0;
|
|
140
|
+
state.pid = void 0;
|
|
141
|
+
state.status = "stopped";
|
|
142
|
+
state.message = "Process exited unexpectedly";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return this.snapshotState(state);
|
|
146
|
+
}
|
|
147
|
+
async startProvider(runtimeId, localRuntime) {
|
|
148
|
+
const state = this.getOrCreateState(runtimeId, localRuntime);
|
|
149
|
+
if (!state.enabled) {
|
|
150
|
+
state.status = "disabled";
|
|
151
|
+
state.message = "Disabled by configuration";
|
|
152
|
+
return this.snapshotState(state);
|
|
153
|
+
}
|
|
154
|
+
if (state.process && state.status === "running") {
|
|
155
|
+
return this.snapshotState(state);
|
|
156
|
+
}
|
|
157
|
+
state.enabled = true;
|
|
158
|
+
const { executable, args, cwd, env: cmdEnv } = localRuntime.command;
|
|
159
|
+
const resolvedCwd = resolveLocalA2AWorkingDirectory({
|
|
160
|
+
envWorkdir: process.env.LOCAL_A2A_WORKDIR || void 0,
|
|
161
|
+
commandCwd: cwd,
|
|
162
|
+
currentCwd: process.cwd()
|
|
163
|
+
});
|
|
164
|
+
const cwdError = validateWorkingDirectory(resolvedCwd);
|
|
165
|
+
if (cwdError) {
|
|
166
|
+
state.process = void 0;
|
|
167
|
+
state.pid = void 0;
|
|
168
|
+
state.status = "failed";
|
|
169
|
+
state.message = cwdError;
|
|
170
|
+
return this.snapshotState(state);
|
|
171
|
+
}
|
|
172
|
+
state.status = "starting";
|
|
173
|
+
state.message = "Starting";
|
|
174
|
+
let stderrTail = "";
|
|
175
|
+
const childEnv = cmdEnv ? { ...process.env, ...cmdEnv } : process.env;
|
|
176
|
+
const child = spawn(executable, args, {
|
|
177
|
+
cwd: resolvedCwd,
|
|
178
|
+
env: childEnv,
|
|
179
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
180
|
+
shell: false
|
|
181
|
+
});
|
|
182
|
+
state.process = child;
|
|
183
|
+
state.pid = child.pid ?? void 0;
|
|
184
|
+
child.once("error", (error) => {
|
|
185
|
+
const current = this.states.get(runtimeId);
|
|
186
|
+
if (!current) return;
|
|
187
|
+
current.process = void 0;
|
|
188
|
+
current.pid = void 0;
|
|
189
|
+
current.status = "failed";
|
|
190
|
+
const stderr = stderrTail.trim();
|
|
191
|
+
current.message = stderr ? `Failed to start: ${error.message}: ${stderr}` : `Failed to start: ${error.message}`;
|
|
192
|
+
});
|
|
193
|
+
child.stdout.on("data", (chunk) => {
|
|
194
|
+
const text = chunk.toString("utf8").trim();
|
|
195
|
+
if (text) console.log(`[local-a2a:${runtimeId}] ${text}`);
|
|
196
|
+
});
|
|
197
|
+
child.stderr.on("data", (chunk) => {
|
|
198
|
+
const rawText = chunk.toString("utf8");
|
|
199
|
+
stderrTail = appendStderrTail(stderrTail, rawText);
|
|
200
|
+
const text = rawText.trim();
|
|
201
|
+
if (text) console.error(`[local-a2a:${runtimeId}] ${text}`);
|
|
202
|
+
});
|
|
203
|
+
child.on("close", (code, signal) => {
|
|
204
|
+
const current = this.states.get(runtimeId);
|
|
205
|
+
if (!current) return;
|
|
206
|
+
current.process = void 0;
|
|
207
|
+
current.pid = void 0;
|
|
208
|
+
const exitReason = signal ?? code ?? "unknown";
|
|
209
|
+
if (current.status === "running") {
|
|
210
|
+
current.status = "stopped";
|
|
211
|
+
current.message = `Stopped (${exitReason})`;
|
|
212
|
+
} else if (current.status === "starting") {
|
|
213
|
+
current.status = "failed";
|
|
214
|
+
current.message = formatExitMessage(exitReason, stderrTail);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
const ready = await this.waitUntilReady(localRuntime, state, 15e3);
|
|
218
|
+
if (!ready) {
|
|
219
|
+
if (state.process) {
|
|
220
|
+
state.process.kill("SIGTERM");
|
|
221
|
+
}
|
|
222
|
+
state.process = void 0;
|
|
223
|
+
state.pid = void 0;
|
|
224
|
+
if (state.status !== "failed") {
|
|
225
|
+
state.status = "failed";
|
|
226
|
+
state.message = "Server did not become ready";
|
|
227
|
+
}
|
|
228
|
+
return this.snapshotState(state);
|
|
229
|
+
}
|
|
230
|
+
state.status = "running";
|
|
231
|
+
state.message = "Running";
|
|
232
|
+
state.lastStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
233
|
+
return this.snapshotState(state);
|
|
234
|
+
}
|
|
235
|
+
async stopProvider(runtimeId) {
|
|
236
|
+
const state = this.states.get(runtimeId);
|
|
237
|
+
if (!state) {
|
|
238
|
+
return this.emptyState(runtimeId);
|
|
239
|
+
}
|
|
240
|
+
const child = state.process;
|
|
241
|
+
if (child) {
|
|
242
|
+
child.kill("SIGTERM");
|
|
243
|
+
await delay(750);
|
|
244
|
+
if (child.exitCode === null) {
|
|
245
|
+
child.kill("SIGKILL");
|
|
246
|
+
}
|
|
247
|
+
state.process = void 0;
|
|
248
|
+
}
|
|
249
|
+
state.pid = void 0;
|
|
250
|
+
state.status = "stopped";
|
|
251
|
+
state.message = "Stopped";
|
|
252
|
+
state.lastStoppedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
253
|
+
return this.snapshotState(state);
|
|
254
|
+
}
|
|
255
|
+
async restartProvider(runtimeId, localRuntime) {
|
|
256
|
+
await this.stopProvider(runtimeId);
|
|
257
|
+
return this.startProvider(runtimeId, localRuntime);
|
|
258
|
+
}
|
|
259
|
+
removeState(runtimeId) {
|
|
260
|
+
const state = this.states.get(runtimeId);
|
|
261
|
+
if (state?.process) {
|
|
262
|
+
state.process.kill("SIGTERM");
|
|
263
|
+
state.process = void 0;
|
|
264
|
+
}
|
|
265
|
+
this.states.delete(runtimeId);
|
|
266
|
+
}
|
|
267
|
+
async waitUntilReady(localRuntime, state, timeoutMs) {
|
|
268
|
+
const start2 = Date.now();
|
|
269
|
+
while (Date.now() - start2 < timeoutMs) {
|
|
270
|
+
if (state.status === "failed") return false;
|
|
271
|
+
if (await probeReady(localRuntime.agentCardUrl, localRuntime.healthUrl, 1500)) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
await delay(500);
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
emptyState(runtimeId) {
|
|
279
|
+
return {
|
|
280
|
+
runtimeId,
|
|
281
|
+
label: runtimeId,
|
|
282
|
+
status: "stopped",
|
|
283
|
+
enabled: false
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
snapshotState(state) {
|
|
287
|
+
return {
|
|
288
|
+
runtimeId: state.runtimeId,
|
|
289
|
+
label: state.label,
|
|
290
|
+
status: state.status,
|
|
291
|
+
enabled: state.enabled,
|
|
292
|
+
assistantId: state.assistantId,
|
|
293
|
+
agentCardUrl: state.agentCardUrl,
|
|
294
|
+
healthUrl: state.healthUrl,
|
|
295
|
+
pid: state.pid,
|
|
296
|
+
message: state.message,
|
|
297
|
+
lastStartedAt: state.lastStartedAt,
|
|
298
|
+
lastStoppedAt: state.lastStoppedAt
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
_LocalA2AService.instance = null;
|
|
303
|
+
var LocalA2AService = _LocalA2AService;
|
|
304
|
+
var localA2AService = LocalA2AService.getInstance();
|
|
305
|
+
|
|
306
|
+
// src/utils/a2a-url.ts
|
|
307
|
+
var AGENT_CARD_PATH = "/.well-known/agent-card.json";
|
|
308
|
+
function normalizeA2AAgentCardUrl(value, defaultProtocol = "https://") {
|
|
309
|
+
const raw = value?.trim() ?? "";
|
|
310
|
+
if (!raw) return "";
|
|
311
|
+
const withProtocol = /^https?:\/\//i.test(raw) ? raw : `${defaultProtocol}${raw.replace(/^\/+/, "")}`;
|
|
312
|
+
const trimmed = withProtocol.replace(/\/+$/, "");
|
|
313
|
+
if (trimmed.endsWith(AGENT_CARD_PATH)) {
|
|
314
|
+
return trimmed;
|
|
315
|
+
}
|
|
316
|
+
return `${trimmed}${AGENT_CARD_PATH}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/utils/local-a2a-runtime.ts
|
|
320
|
+
function validateLocalRuntimeCommand(localRuntime) {
|
|
321
|
+
const runtime = localRuntime;
|
|
322
|
+
const executable = runtime.command?.executable;
|
|
323
|
+
if (typeof executable !== "string" || executable.trim().length === 0) {
|
|
324
|
+
return {
|
|
325
|
+
ok: false,
|
|
326
|
+
error: "Local managed A2A command executable is required"
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const args = runtime.command?.args;
|
|
330
|
+
if (!Array.isArray(args) || !args.every((arg) => typeof arg === "string")) {
|
|
331
|
+
return {
|
|
332
|
+
ok: false,
|
|
333
|
+
error: "Local managed A2A command args must be an array of strings"
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return { ok: true };
|
|
337
|
+
}
|
|
338
|
+
function getAgentCardPort(agentCardUrl) {
|
|
339
|
+
let url;
|
|
340
|
+
try {
|
|
341
|
+
url = new URL(agentCardUrl);
|
|
342
|
+
} catch {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
if (url.port) {
|
|
346
|
+
const port = Number(url.port);
|
|
347
|
+
return Number.isInteger(port) ? port : null;
|
|
348
|
+
}
|
|
349
|
+
if (url.protocol === "http:") return 80;
|
|
350
|
+
if (url.protocol === "https:") return 443;
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
function parseCommandPort(args) {
|
|
354
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
355
|
+
const arg = args[index];
|
|
356
|
+
const match = /^(--port|-p)(?:[=\s]+(\d+))?$/.exec(arg);
|
|
357
|
+
if (!match) continue;
|
|
358
|
+
const inlinePort = match[2];
|
|
359
|
+
if (inlinePort) return Number(inlinePort);
|
|
360
|
+
const nextArg = args[index + 1];
|
|
361
|
+
if (nextArg && /^\d+$/.test(nextArg)) {
|
|
362
|
+
return Number(nextArg);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
function isLoopbackHost(hostname) {
|
|
368
|
+
const normalized = hostname.toLowerCase();
|
|
369
|
+
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
|
|
370
|
+
}
|
|
371
|
+
function validateLocalRuntimePort(localRuntime, agentCardUrl) {
|
|
372
|
+
let url;
|
|
373
|
+
try {
|
|
374
|
+
url = new URL(agentCardUrl);
|
|
375
|
+
} catch {
|
|
376
|
+
return { ok: true };
|
|
377
|
+
}
|
|
378
|
+
if (!isLoopbackHost(url.hostname) || !url.port) {
|
|
379
|
+
return { ok: true };
|
|
380
|
+
}
|
|
381
|
+
const commandPort = parseCommandPort(localRuntime.command.args);
|
|
382
|
+
if (commandPort === null) {
|
|
383
|
+
return { ok: true };
|
|
384
|
+
}
|
|
385
|
+
const agentCardPort = Number(url.port);
|
|
386
|
+
if (commandPort === agentCardPort) {
|
|
387
|
+
return { ok: true };
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
ok: false,
|
|
391
|
+
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.`
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/controllers/assistant.ts
|
|
28
396
|
function getTenantId(request) {
|
|
29
397
|
const userTenantId = request.user?.tenantId;
|
|
30
398
|
if (userTenantId) {
|
|
@@ -154,6 +522,79 @@ async function createAssistant(request, reply) {
|
|
|
154
522
|
});
|
|
155
523
|
}
|
|
156
524
|
const id = data.id ?? randomUUID();
|
|
525
|
+
if (data.graphDefinition && data.graphDefinition.type === AgentType.A2A_REMOTE) {
|
|
526
|
+
const a2aConfig = data.graphDefinition;
|
|
527
|
+
const warnings = [];
|
|
528
|
+
const isLocalManaged = !!a2aConfig.localRuntime;
|
|
529
|
+
a2aConfig.agentCardUrl = normalizeA2AAgentCardUrl(
|
|
530
|
+
a2aConfig.agentCardUrl,
|
|
531
|
+
isLocalManaged ? "http://" : "https://"
|
|
532
|
+
);
|
|
533
|
+
if (a2aConfig.localRuntime) {
|
|
534
|
+
const commandValidation = validateLocalRuntimeCommand(
|
|
535
|
+
a2aConfig.localRuntime
|
|
536
|
+
);
|
|
537
|
+
if (!commandValidation.ok) {
|
|
538
|
+
return reply.status(400).send({
|
|
539
|
+
success: false,
|
|
540
|
+
message: commandValidation.error
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
a2aConfig.localRuntime.command.executable = a2aConfig.localRuntime.command.executable.trim();
|
|
544
|
+
a2aConfig.localRuntime.agentCardUrl = normalizeA2AAgentCardUrl(
|
|
545
|
+
a2aConfig.localRuntime.agentCardUrl || a2aConfig.agentCardUrl,
|
|
546
|
+
"http://"
|
|
547
|
+
);
|
|
548
|
+
a2aConfig.agentCardUrl = a2aConfig.localRuntime.agentCardUrl;
|
|
549
|
+
const portValidation = validateLocalRuntimePort(
|
|
550
|
+
a2aConfig.localRuntime,
|
|
551
|
+
a2aConfig.agentCardUrl
|
|
552
|
+
);
|
|
553
|
+
if (!portValidation.ok) {
|
|
554
|
+
return reply.status(400).send({
|
|
555
|
+
success: false,
|
|
556
|
+
message: portValidation.error
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (a2aConfig.localRuntime) {
|
|
561
|
+
const existingAssistants = await getStoreLattice("default", "assistant").store.getAllAssistants(tenantId);
|
|
562
|
+
const targetPort = getAgentCardPort(a2aConfig.localRuntime.agentCardUrl);
|
|
563
|
+
const duplicate = existingAssistants.find((a) => {
|
|
564
|
+
if (a.id === id) return false;
|
|
565
|
+
const gd = a.graphDefinition && typeof a.graphDefinition === "object" ? a.graphDefinition : void 0;
|
|
566
|
+
if (!gd || gd.type !== AgentType.A2A_REMOTE) return false;
|
|
567
|
+
const lr = gd.localRuntime;
|
|
568
|
+
if (!lr) return false;
|
|
569
|
+
const existingAgentCardUrl = normalizeA2AAgentCardUrl(lr.agentCardUrl, "http://");
|
|
570
|
+
const existingPort = getAgentCardPort(existingAgentCardUrl);
|
|
571
|
+
if (targetPort !== null && existingPort !== null) {
|
|
572
|
+
return existingPort === targetPort;
|
|
573
|
+
}
|
|
574
|
+
return existingAgentCardUrl === a2aConfig.localRuntime.agentCardUrl;
|
|
575
|
+
});
|
|
576
|
+
if (duplicate) {
|
|
577
|
+
const conflictDetail = targetPort === null ? `agentCardUrl "${a2aConfig.localRuntime.agentCardUrl}"` : `port ${targetPort}`;
|
|
578
|
+
return reply.status(409).send({
|
|
579
|
+
success: false,
|
|
580
|
+
message: `A local managed A2A assistant using ${conflictDetail} already exists: "${duplicate.name}". Each local managed A2A assistant needs a unique port.`
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const resp = await fetch(a2aConfig.agentCardUrl, {
|
|
586
|
+
signal: AbortSignal.timeout(5e3)
|
|
587
|
+
});
|
|
588
|
+
if (!resp.ok) {
|
|
589
|
+
warnings.push(`agentCardUrl returned HTTP ${resp.status}`);
|
|
590
|
+
}
|
|
591
|
+
} catch {
|
|
592
|
+
warnings.push(`agentCardUrl is not reachable: ${a2aConfig.agentCardUrl}`);
|
|
593
|
+
}
|
|
594
|
+
if (warnings.length > 0) {
|
|
595
|
+
console.warn(`[createAssistant] A2A warnings for ${a2aConfig.agentCardUrl}:`, warnings);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
157
598
|
return upsertAssistant(tenantId, id, data, reply, true);
|
|
158
599
|
}
|
|
159
600
|
async function updateAssistant(request, reply) {
|
|
@@ -167,11 +608,28 @@ async function deleteAssistant(request, reply) {
|
|
|
167
608
|
const { id } = request.params;
|
|
168
609
|
const storeLattice = getStoreLattice("default", "assistant");
|
|
169
610
|
const assistantStore = storeLattice.store;
|
|
611
|
+
const storedAssistant = await assistantStore.getAssistantById(tenantId, id);
|
|
612
|
+
const graphDefinition = storedAssistant?.graphDefinition;
|
|
613
|
+
const hasLocalRuntime = graphDefinition && typeof graphDefinition === "object" ? !!graphDefinition.localRuntime : false;
|
|
614
|
+
if (storedAssistant && hasLocalRuntime) {
|
|
615
|
+
localA2AService.removeState(id);
|
|
616
|
+
const deleted2 = await assistantStore.deleteAssistant(tenantId, id);
|
|
617
|
+
if (!deleted2) {
|
|
618
|
+
return reply.status(500).send({
|
|
619
|
+
success: false,
|
|
620
|
+
message: "Failed to delete assistant"
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
eventBus.publish("assistant:deleted", { id, tenantId });
|
|
624
|
+
return {
|
|
625
|
+
success: true,
|
|
626
|
+
message: "Deleted local A2A assistant"
|
|
627
|
+
};
|
|
628
|
+
}
|
|
170
629
|
const agentConfig = await agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
|
|
171
630
|
const isCodeConfigured = !!agentConfig;
|
|
172
631
|
if (isCodeConfigured) {
|
|
173
|
-
|
|
174
|
-
if (!exists2) {
|
|
632
|
+
if (!storedAssistant) {
|
|
175
633
|
return reply.status(404).send({
|
|
176
634
|
success: false,
|
|
177
635
|
message: "Assistant not found (code-configured assistants cannot be deleted from code, only from store)"
|
|
@@ -184,8 +642,7 @@ async function deleteAssistant(request, reply) {
|
|
|
184
642
|
message: "Deleted assistant from store (code-configured registration remains)"
|
|
185
643
|
};
|
|
186
644
|
}
|
|
187
|
-
|
|
188
|
-
if (!exists) {
|
|
645
|
+
if (!storedAssistant) {
|
|
189
646
|
return reply.status(404).send({
|
|
190
647
|
success: false,
|
|
191
648
|
message: "Assistant not found"
|
|
@@ -319,7 +776,7 @@ var createRun = async (request, reply) => {
|
|
|
319
776
|
|
|
320
777
|
`);
|
|
321
778
|
if (!success) {
|
|
322
|
-
await new Promise((
|
|
779
|
+
await new Promise((resolve3) => reply.raw.once("drain", resolve3));
|
|
323
780
|
}
|
|
324
781
|
}
|
|
325
782
|
} catch (error) {
|
|
@@ -2911,15 +3368,14 @@ function getStore() {
|
|
|
2911
3368
|
async function listTasks(request, reply) {
|
|
2912
3369
|
try {
|
|
2913
3370
|
const tenantId = getTenantId9(request);
|
|
2914
|
-
const userId = getUserId4(request);
|
|
2915
3371
|
const query = request.query;
|
|
2916
3372
|
const store = getStore();
|
|
2917
3373
|
const tasks = await store.list({
|
|
2918
3374
|
tenantId,
|
|
2919
|
-
ownerId: query.ownerId || userId,
|
|
2920
3375
|
status: query.status,
|
|
2921
3376
|
priority: query.priority,
|
|
2922
|
-
|
|
3377
|
+
...query.ownerId && { ownerId: query.ownerId },
|
|
3378
|
+
...query.ownerType && { ownerType: query.ownerType },
|
|
2923
3379
|
metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
|
|
2924
3380
|
projectId: query.projectId,
|
|
2925
3381
|
limit: query.limit ? parseInt(query.limit) : void 0,
|
|
@@ -4054,12 +4510,41 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
4054
4510
|
}
|
|
4055
4511
|
|
|
4056
4512
|
// src/controllers/workspace.ts
|
|
4057
|
-
import * as
|
|
4058
|
-
import * as
|
|
4513
|
+
import * as fs2 from "fs/promises";
|
|
4514
|
+
import * as path2 from "path";
|
|
4515
|
+
import * as os from "os";
|
|
4059
4516
|
import { getStoreLattice as getStoreLattice7 } from "@axiom-lattice/core";
|
|
4060
4517
|
import { SandboxFilesystem, FilesystemBackend } from "@axiom-lattice/core";
|
|
4061
4518
|
import { getSandBoxManager as getSandBoxManager3 } from "@axiom-lattice/core";
|
|
4062
4519
|
import { v4 as uuidv42 } from "uuid";
|
|
4520
|
+
|
|
4521
|
+
// src/utils/path-containment.ts
|
|
4522
|
+
import * as fs from "fs/promises";
|
|
4523
|
+
import * as path from "path";
|
|
4524
|
+
var PathOutsideRootError = class extends Error {
|
|
4525
|
+
constructor(rootPath, targetPath) {
|
|
4526
|
+
super(`Path is outside the allowed root: ${targetPath}`);
|
|
4527
|
+
this.rootPath = rootPath;
|
|
4528
|
+
this.targetPath = targetPath;
|
|
4529
|
+
this.name = "PathOutsideRootError";
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4532
|
+
function isPathWithinRoot(rootPath, targetPath) {
|
|
4533
|
+
const relative2 = path.relative(rootPath, targetPath);
|
|
4534
|
+
return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
|
|
4535
|
+
}
|
|
4536
|
+
async function resolvePathWithinRoot(rootPath, targetPath) {
|
|
4537
|
+
const [resolvedRoot, resolvedTarget] = await Promise.all([
|
|
4538
|
+
fs.realpath(rootPath),
|
|
4539
|
+
fs.realpath(targetPath)
|
|
4540
|
+
]);
|
|
4541
|
+
if (!isPathWithinRoot(resolvedRoot, resolvedTarget)) {
|
|
4542
|
+
throw new PathOutsideRootError(resolvedRoot, resolvedTarget);
|
|
4543
|
+
}
|
|
4544
|
+
return { rootPath: resolvedRoot, targetPath: resolvedTarget };
|
|
4545
|
+
}
|
|
4546
|
+
|
|
4547
|
+
// src/controllers/workspace.ts
|
|
4063
4548
|
var WorkspaceController = class {
|
|
4064
4549
|
constructor() {
|
|
4065
4550
|
this.workspaceStore = getStoreLattice7("default", "workspace").store;
|
|
@@ -4220,16 +4705,23 @@ var WorkspaceController = class {
|
|
|
4220
4705
|
}),
|
|
4221
4706
|
workspace
|
|
4222
4707
|
};
|
|
4223
|
-
} else {
|
|
4224
|
-
console.log(`[getBackend] using FilesystemBackend rootDir=/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`);
|
|
4225
|
-
return {
|
|
4226
|
-
backend: new FilesystemBackend({
|
|
4227
|
-
rootDir: `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`,
|
|
4228
|
-
virtualMode: true
|
|
4229
|
-
}),
|
|
4230
|
-
workspace
|
|
4231
|
-
};
|
|
4232
4708
|
}
|
|
4709
|
+
const rootDir = await this.getFilesystemRootDirectory(
|
|
4710
|
+
tenantId,
|
|
4711
|
+
workspaceId,
|
|
4712
|
+
projectId
|
|
4713
|
+
);
|
|
4714
|
+
console.log(`[getBackend] using FilesystemBackend rootDir=${rootDir}`);
|
|
4715
|
+
return {
|
|
4716
|
+
backend: new FilesystemBackend({
|
|
4717
|
+
rootDir,
|
|
4718
|
+
virtualMode: true
|
|
4719
|
+
}),
|
|
4720
|
+
workspace
|
|
4721
|
+
};
|
|
4722
|
+
}
|
|
4723
|
+
async getFilesystemRootDirectory(tenantId, workspaceId, projectId) {
|
|
4724
|
+
return `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
|
|
4233
4725
|
}
|
|
4234
4726
|
isBinaryContentType(filename) {
|
|
4235
4727
|
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
@@ -4418,21 +4910,21 @@ var WorkspaceController = class {
|
|
|
4418
4910
|
async listPath(request) {
|
|
4419
4911
|
const tenantId = this.getTenantId(request);
|
|
4420
4912
|
const { workspaceId, projectId } = request.params;
|
|
4421
|
-
const
|
|
4913
|
+
const path4 = request.query.path || "/";
|
|
4422
4914
|
const assistantId = request.query.assistantId;
|
|
4423
|
-
console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${
|
|
4424
|
-
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId,
|
|
4425
|
-
console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${
|
|
4426
|
-
const files = await backend.lsInfo(
|
|
4915
|
+
console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${path4} assistantId=${assistantId}`);
|
|
4916
|
+
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path4);
|
|
4917
|
+
console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${path4})`);
|
|
4918
|
+
const files = await backend.lsInfo(path4);
|
|
4427
4919
|
console.log(`[listPath] result count=${files.length}`);
|
|
4428
4920
|
return { success: true, data: files };
|
|
4429
4921
|
}
|
|
4430
4922
|
async readFile(request) {
|
|
4431
4923
|
const tenantId = this.getTenantId(request);
|
|
4432
4924
|
const { workspaceId, projectId } = request.params;
|
|
4433
|
-
const { path:
|
|
4434
|
-
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId,
|
|
4435
|
-
const content = await backend.read(
|
|
4925
|
+
const { path: path4, offset = 0, limit = 1e3, assistantId } = request.query;
|
|
4926
|
+
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path4);
|
|
4927
|
+
const content = await backend.read(path4, Number(offset), Number(limit));
|
|
4436
4928
|
return { success: true, data: { content, offset, limit } };
|
|
4437
4929
|
}
|
|
4438
4930
|
/**
|
|
@@ -4444,7 +4936,7 @@ var WorkspaceController = class {
|
|
|
4444
4936
|
* - `path`: optional directory path relative to project root (e.g., "docs", "src/utils")
|
|
4445
4937
|
* - `assistantId`: optional query parameter for sandbox identity matching
|
|
4446
4938
|
* For sandbox storage, delegates to the sandbox upload API.
|
|
4447
|
-
* For filesystem storage, writes
|
|
4939
|
+
* For filesystem storage, writes under the same project root used by the file backend.
|
|
4448
4940
|
*/
|
|
4449
4941
|
async uploadFile(request, reply) {
|
|
4450
4942
|
const tenantId = this.getTenantId(request);
|
|
@@ -4478,7 +4970,7 @@ var WorkspaceController = class {
|
|
|
4478
4970
|
workspaceId,
|
|
4479
4971
|
projectId
|
|
4480
4972
|
};
|
|
4481
|
-
const realPath = pathValue ?
|
|
4973
|
+
const realPath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
|
|
4482
4974
|
const isBinary = this.isBinaryContentType(filename);
|
|
4483
4975
|
const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, realPath);
|
|
4484
4976
|
if (volumeBackend && !isBinary) {
|
|
@@ -4489,7 +4981,7 @@ var WorkspaceController = class {
|
|
|
4489
4981
|
const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
|
|
4490
4982
|
await sandbox.file.uploadFile({ file: realPath, data: buffer });
|
|
4491
4983
|
}
|
|
4492
|
-
const relativePath = pathValue ?
|
|
4984
|
+
const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
|
|
4493
4985
|
const result2 = {
|
|
4494
4986
|
path: relativePath,
|
|
4495
4987
|
name: filename,
|
|
@@ -4499,14 +4991,43 @@ var WorkspaceController = class {
|
|
|
4499
4991
|
};
|
|
4500
4992
|
return reply.status(200).send({ success: true, data: result2 });
|
|
4501
4993
|
}
|
|
4502
|
-
const rootDir =
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4994
|
+
const rootDir = await this.getFilesystemRootDirectory(
|
|
4995
|
+
tenantId,
|
|
4996
|
+
workspaceId,
|
|
4997
|
+
projectId
|
|
4998
|
+
);
|
|
4999
|
+
await fs2.mkdir(rootDir, { recursive: true });
|
|
5000
|
+
const relativeDirectory = pathValue?.replace(/^[/\\]+/, "") || ".";
|
|
5001
|
+
const targetDir = path2.resolve(rootDir, relativeDirectory);
|
|
5002
|
+
if (!isPathWithinRoot(path2.resolve(rootDir), targetDir)) {
|
|
5003
|
+
return reply.status(400).send({
|
|
5004
|
+
success: false,
|
|
5005
|
+
error: "Upload path must stay within the project directory"
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
5008
|
+
await fs2.mkdir(targetDir, { recursive: true });
|
|
5009
|
+
const [resolvedRoot, resolvedTargetDir] = await Promise.all([
|
|
5010
|
+
fs2.realpath(rootDir),
|
|
5011
|
+
fs2.realpath(targetDir)
|
|
5012
|
+
]);
|
|
5013
|
+
if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {
|
|
5014
|
+
return reply.status(400).send({
|
|
5015
|
+
success: false,
|
|
5016
|
+
error: "Upload path must stay within the project directory"
|
|
5017
|
+
});
|
|
5018
|
+
}
|
|
5019
|
+
const safeFilename = path2.basename(filename);
|
|
5020
|
+
if (safeFilename !== filename) {
|
|
5021
|
+
return reply.status(400).send({
|
|
5022
|
+
success: false,
|
|
5023
|
+
error: "Invalid filename"
|
|
5024
|
+
});
|
|
5025
|
+
}
|
|
5026
|
+
const targetPath = path2.join(resolvedTargetDir, safeFilename);
|
|
5027
|
+
await fs2.writeFile(targetPath, buffer);
|
|
5028
|
+
const stat2 = await fs2.stat(targetPath);
|
|
4508
5029
|
const result = {
|
|
4509
|
-
path:
|
|
5030
|
+
path: path2.posix.join("/", relativeDirectory, safeFilename),
|
|
4510
5031
|
name: filename,
|
|
4511
5032
|
is_dir: false,
|
|
4512
5033
|
size: stat2.size,
|
|
@@ -4514,9 +5035,63 @@ var WorkspaceController = class {
|
|
|
4514
5035
|
};
|
|
4515
5036
|
return reply.status(200).send({ success: true, data: result });
|
|
4516
5037
|
} catch (error) {
|
|
5038
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4517
5039
|
return reply.status(500).send({
|
|
4518
5040
|
success: false,
|
|
4519
|
-
error: `Upload handler error: ${
|
|
5041
|
+
error: `Upload handler error: ${message}`
|
|
5042
|
+
});
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
5045
|
+
/**
|
|
5046
|
+
* Browse local filesystem directories for the folder picker.
|
|
5047
|
+
*
|
|
5048
|
+
* Security constraints:
|
|
5049
|
+
* - Restricted to os.homedir()
|
|
5050
|
+
* - Blacklists system directories
|
|
5051
|
+
* - Returns only directory names, no file contents or hidden files
|
|
5052
|
+
*/
|
|
5053
|
+
async browseFilesystem(request, reply) {
|
|
5054
|
+
const homeDir = os.homedir();
|
|
5055
|
+
const requestedPath = request.query.path || homeDir;
|
|
5056
|
+
const SYSTEM_BLACKLIST = ["/etc", "/sys", "/proc", "/var", "/tmp", "/dev"];
|
|
5057
|
+
try {
|
|
5058
|
+
const resolved = await resolvePathWithinRoot(homeDir, requestedPath);
|
|
5059
|
+
const normalized = resolved.targetPath;
|
|
5060
|
+
for (const blacklisted of SYSTEM_BLACKLIST) {
|
|
5061
|
+
if (isPathWithinRoot(blacklisted, normalized)) {
|
|
5062
|
+
return reply.status(403).send({
|
|
5063
|
+
success: false,
|
|
5064
|
+
error: `Cannot browse system directory: ${blacklisted}`
|
|
5065
|
+
});
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
const entries = await fs2.readdir(normalized, { withFileTypes: true });
|
|
5069
|
+
const dirEntries = entries.filter((entry) => !entry.name.startsWith(".") && entry.isDirectory()).map((entry) => ({
|
|
5070
|
+
name: entry.name,
|
|
5071
|
+
path: path2.join(normalized, entry.name),
|
|
5072
|
+
isDirectory: true
|
|
5073
|
+
}));
|
|
5074
|
+
const parentPath = path2.dirname(normalized);
|
|
5075
|
+
const hasParent = parentPath !== normalized && isPathWithinRoot(resolved.rootPath, parentPath);
|
|
5076
|
+
return {
|
|
5077
|
+
success: true,
|
|
5078
|
+
data: {
|
|
5079
|
+
path: normalized,
|
|
5080
|
+
parentPath: hasParent ? parentPath : void 0,
|
|
5081
|
+
entries: dirEntries
|
|
5082
|
+
}
|
|
5083
|
+
};
|
|
5084
|
+
} catch (error) {
|
|
5085
|
+
if (error instanceof PathOutsideRootError) {
|
|
5086
|
+
return reply.status(403).send({
|
|
5087
|
+
success: false,
|
|
5088
|
+
error: "Browse restricted to home directory"
|
|
5089
|
+
});
|
|
5090
|
+
}
|
|
5091
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5092
|
+
return reply.status(400).send({
|
|
5093
|
+
success: false,
|
|
5094
|
+
error: `Cannot read directory: ${message}`
|
|
4520
5095
|
});
|
|
4521
5096
|
}
|
|
4522
5097
|
}
|
|
@@ -4577,6 +5152,10 @@ function registerWorkspaceRoutes(app2) {
|
|
|
4577
5152
|
"/api/workspaces/:workspaceId/projects/:projectId/viewfile",
|
|
4578
5153
|
controller.viewFile.bind(controller)
|
|
4579
5154
|
);
|
|
5155
|
+
app2.get(
|
|
5156
|
+
"/api/filesystem/browse",
|
|
5157
|
+
controller.browseFilesystem.bind(controller)
|
|
5158
|
+
);
|
|
4580
5159
|
}
|
|
4581
5160
|
|
|
4582
5161
|
// src/controllers/database-configs.ts
|
|
@@ -4796,7 +5375,7 @@ async function testDatabaseConnection(request, reply) {
|
|
|
4796
5375
|
await db.connect();
|
|
4797
5376
|
await db.listTables();
|
|
4798
5377
|
const latency = Date.now() - startTime;
|
|
4799
|
-
await new Promise((
|
|
5378
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
4800
5379
|
await db.disconnect();
|
|
4801
5380
|
await sqlDatabaseManager.removeDatabase(tenantId, testKey);
|
|
4802
5381
|
return {
|
|
@@ -5630,11 +6209,12 @@ var EvalRunner = class {
|
|
|
5630
6209
|
constructor() {
|
|
5631
6210
|
this.runs = /* @__PURE__ */ new Map();
|
|
5632
6211
|
this.eventEmitter = new EventEmitter();
|
|
6212
|
+
this.processStartedAt = /* @__PURE__ */ new Date();
|
|
5633
6213
|
}
|
|
5634
6214
|
getEventEmitter() {
|
|
5635
6215
|
return this.eventEmitter;
|
|
5636
6216
|
}
|
|
5637
|
-
async startRun(tenantId, projectId) {
|
|
6217
|
+
async startRun(tenantId, projectId, suiteIds) {
|
|
5638
6218
|
const store = this.getEvalStore();
|
|
5639
6219
|
const project = await store.getProjectById(tenantId, projectId);
|
|
5640
6220
|
if (!project) throw new Error("Project not found");
|
|
@@ -5644,10 +6224,27 @@ var EvalRunner = class {
|
|
|
5644
6224
|
}
|
|
5645
6225
|
}
|
|
5646
6226
|
const existingRuns = await store.getRunsByTenant(tenantId, { projectId, status: "running" });
|
|
5647
|
-
|
|
6227
|
+
for (const run of existingRuns) {
|
|
6228
|
+
const orphaned = !this.runs.has(run.id) && run.createdAt < this.processStartedAt;
|
|
6229
|
+
if (orphaned) {
|
|
6230
|
+
await store.updateRunStatus(tenantId, run.id, {
|
|
6231
|
+
status: "failed",
|
|
6232
|
+
error: "Gateway restarted \u2014 run orphaned",
|
|
6233
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
6234
|
+
});
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
const stillRunning = await store.getRunsByTenant(tenantId, { projectId, status: "running" });
|
|
6238
|
+
if (stillRunning.length > 0) {
|
|
5648
6239
|
throw new Error("A run is already in progress for this project");
|
|
5649
6240
|
}
|
|
5650
|
-
|
|
6241
|
+
let suites = await store.getSuitesByProject(tenantId, projectId);
|
|
6242
|
+
if (suiteIds && suiteIds.length > 0) {
|
|
6243
|
+
suites = suites.filter((s) => suiteIds.includes(s.id));
|
|
6244
|
+
if (suites.length === 0) {
|
|
6245
|
+
throw new Error("No suites match the requested suiteIds filter");
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
5651
6248
|
const evalSuites = [];
|
|
5652
6249
|
let totalCases = 0;
|
|
5653
6250
|
for (const suite of suites) {
|
|
@@ -5690,6 +6287,9 @@ var EvalRunner = class {
|
|
|
5690
6287
|
const runId = uuidv43();
|
|
5691
6288
|
const concurrency = Math.max(1, Math.floor(project.concurrency) || 3);
|
|
5692
6289
|
await store.createRun(tenantId, projectId, runId, { totalCases, concurrency });
|
|
6290
|
+
const tsc = project.targetServerConfig ?? {};
|
|
6291
|
+
const workspaceId = tsc.workspace_id || "";
|
|
6292
|
+
const projectIdFromConfig = tsc.project_id || "";
|
|
5693
6293
|
const projectConfig = {
|
|
5694
6294
|
projectName: project.name,
|
|
5695
6295
|
version: project.version,
|
|
@@ -5697,7 +6297,9 @@ var EvalRunner = class {
|
|
|
5697
6297
|
suites: evalSuites,
|
|
5698
6298
|
judge_agent_config: { modelKey: resolvedJudgeModelKey },
|
|
5699
6299
|
lattice_server_config: {
|
|
5700
|
-
tenant_id: tenantId
|
|
6300
|
+
tenant_id: tenantId,
|
|
6301
|
+
workspace_id: workspaceId,
|
|
6302
|
+
project_id: projectIdFromConfig
|
|
5701
6303
|
},
|
|
5702
6304
|
concurrency
|
|
5703
6305
|
};
|
|
@@ -5741,6 +6343,15 @@ var EvalRunner = class {
|
|
|
5741
6343
|
const runPromise = (async () => {
|
|
5742
6344
|
try {
|
|
5743
6345
|
const evalProject = new LatticeEvalProject(projectConfig, onCaseComplete);
|
|
6346
|
+
const calibration = await evalProject.calibrateJudge();
|
|
6347
|
+
if (!calibration.ok) {
|
|
6348
|
+
await store.updateRunStatus(tenantId, runId, {
|
|
6349
|
+
status: "failed",
|
|
6350
|
+
error: `Judge calibration failed: ${calibration.reason}`,
|
|
6351
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
6352
|
+
});
|
|
6353
|
+
throw new Error(`Judge calibration failed: ${calibration.reason}`);
|
|
6354
|
+
}
|
|
5744
6355
|
const { report } = await evalProject.runAllSuitesBatch(concurrency, abortController.signal);
|
|
5745
6356
|
if (!abortController.signal.aborted) {
|
|
5746
6357
|
const completedCount = stats.passed + stats.failed;
|
|
@@ -6128,11 +6739,12 @@ function registerEvalRoutes(app2) {
|
|
|
6128
6739
|
try {
|
|
6129
6740
|
const tenantId = getTenantId13(request);
|
|
6130
6741
|
const { pid } = request.params;
|
|
6131
|
-
const
|
|
6742
|
+
const body = request.body ?? {};
|
|
6743
|
+
const runId = await evalRunner.startRun(tenantId, pid, body?.suiteIds);
|
|
6132
6744
|
reply.status(202).send({ success: true, message: "Run started", data: { run_id: runId } });
|
|
6133
6745
|
} catch (err) {
|
|
6134
6746
|
const msg = err.message;
|
|
6135
|
-
const code = msg === "Project not found" ? 404 : msg.includes("already in progress") ? 409 : 500;
|
|
6747
|
+
const code = msg === "Project not found" ? 404 : msg.includes("already in progress") ? 409 : msg.includes("No suites match") ? 400 : 500;
|
|
6136
6748
|
reply.status(code).send({ success: false, message: msg });
|
|
6137
6749
|
}
|
|
6138
6750
|
});
|
|
@@ -7109,8 +7721,8 @@ function getTenantId14(request) {
|
|
|
7109
7721
|
return request.headers["x-tenant-id"] || "default";
|
|
7110
7722
|
}
|
|
7111
7723
|
async function getInstallationStore() {
|
|
7112
|
-
const { getStoreLattice:
|
|
7113
|
-
const store =
|
|
7724
|
+
const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
|
|
7725
|
+
const store = getStoreLattice31("default", "channelInstallation").store;
|
|
7114
7726
|
if (store) return store;
|
|
7115
7727
|
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
7116
7728
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -7591,8 +8203,8 @@ var log = {
|
|
|
7591
8203
|
};
|
|
7592
8204
|
var connections = /* @__PURE__ */ new Map();
|
|
7593
8205
|
var pending = /* @__PURE__ */ new Map();
|
|
7594
|
-
function getAgentIdFromPath(
|
|
7595
|
-
const match =
|
|
8206
|
+
function getAgentIdFromPath(path4) {
|
|
8207
|
+
const match = path4.match(/^\/api\/a2a\/bridge\/proxy\/([^/]+)/);
|
|
7596
8208
|
return match ? match[1] : null;
|
|
7597
8209
|
}
|
|
7598
8210
|
function handleBridgeConnection(socket, _request) {
|
|
@@ -7700,12 +8312,12 @@ async function handleBridgeProxy(request, reply) {
|
|
|
7700
8312
|
};
|
|
7701
8313
|
const timeout = 3e5;
|
|
7702
8314
|
try {
|
|
7703
|
-
const response = await new Promise((
|
|
8315
|
+
const response = await new Promise((resolve3, reject) => {
|
|
7704
8316
|
const timer = setTimeout(() => {
|
|
7705
8317
|
pending.delete(requestId);
|
|
7706
8318
|
reject(new Error(`Bridge request timeout after ${timeout}ms`));
|
|
7707
8319
|
}, timeout);
|
|
7708
|
-
pending.set(requestId, { resolve, reject, timer, agentId });
|
|
8320
|
+
pending.set(requestId, { resolve: resolve3, reject, timer, agentId });
|
|
7709
8321
|
try {
|
|
7710
8322
|
if (conn.ws.readyState !== conn.ws.OPEN) {
|
|
7711
8323
|
pending.delete(requestId);
|
|
@@ -7828,10 +8440,274 @@ var registerSTTRoutes = (app2) => {
|
|
|
7828
8440
|
);
|
|
7829
8441
|
};
|
|
7830
8442
|
|
|
8443
|
+
// src/controllers/local-a2a.ts
|
|
8444
|
+
import { getStoreLattice as getStoreLattice15 } from "@axiom-lattice/core";
|
|
8445
|
+
import {
|
|
8446
|
+
AgentType as AgentType2
|
|
8447
|
+
} from "@axiom-lattice/protocols";
|
|
8448
|
+
function getTenantId17(request) {
|
|
8449
|
+
const userTenantId = request.user?.tenantId;
|
|
8450
|
+
if (userTenantId) return userTenantId;
|
|
8451
|
+
return request.headers["x-tenant-id"] || "default";
|
|
8452
|
+
}
|
|
8453
|
+
function getLocalRuntime(assistant) {
|
|
8454
|
+
const graphDefinition = assistant.graphDefinition;
|
|
8455
|
+
if (!graphDefinition || typeof graphDefinition !== "object") return void 0;
|
|
8456
|
+
if (graphDefinition.type !== AgentType2.A2A_REMOTE) return void 0;
|
|
8457
|
+
return graphDefinition.localRuntime;
|
|
8458
|
+
}
|
|
8459
|
+
async function getLocalA2AAssistants(tenantId) {
|
|
8460
|
+
const storeLattice = getStoreLattice15("default", "assistant");
|
|
8461
|
+
const assistants = await storeLattice.store.getAllAssistants(tenantId);
|
|
8462
|
+
const results = [];
|
|
8463
|
+
for (const assistant of assistants) {
|
|
8464
|
+
const runtime = getLocalRuntime(assistant);
|
|
8465
|
+
if (runtime) {
|
|
8466
|
+
results.push({ assistant, localRuntime: runtime });
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
return results;
|
|
8470
|
+
}
|
|
8471
|
+
async function getLocalA2AAssistant(tenantId, assistantId) {
|
|
8472
|
+
const storeLattice = getStoreLattice15("default", "assistant");
|
|
8473
|
+
const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
|
|
8474
|
+
if (!assistant) return null;
|
|
8475
|
+
const runtime = getLocalRuntime(assistant);
|
|
8476
|
+
if (!runtime) return null;
|
|
8477
|
+
return { assistant, localRuntime: runtime };
|
|
8478
|
+
}
|
|
8479
|
+
async function requireManagedAssistant(request, reply) {
|
|
8480
|
+
const assistantId = request.params.assistantId;
|
|
8481
|
+
if (!assistantId) {
|
|
8482
|
+
reply.status(400).send({ success: false, error: "assistantId is required" });
|
|
8483
|
+
return null;
|
|
8484
|
+
}
|
|
8485
|
+
const registered = await getLocalA2AAssistant(getTenantId17(request), assistantId);
|
|
8486
|
+
if (!registered) {
|
|
8487
|
+
reply.status(404).send({
|
|
8488
|
+
success: false,
|
|
8489
|
+
error: `Assistant ${assistantId} is not a local managed A2A assistant`
|
|
8490
|
+
});
|
|
8491
|
+
return null;
|
|
8492
|
+
}
|
|
8493
|
+
return { assistantId, localRuntime: registered.localRuntime };
|
|
8494
|
+
}
|
|
8495
|
+
async function getLocalA2AStatus(request, reply) {
|
|
8496
|
+
const tenantId = getTenantId17(request);
|
|
8497
|
+
const managed = await getLocalA2AAssistants(tenantId);
|
|
8498
|
+
const states = await Promise.all(
|
|
8499
|
+
managed.map(
|
|
8500
|
+
({ assistant, localRuntime }) => localA2AService.getRegisteredState(assistant.id, localRuntime)
|
|
8501
|
+
)
|
|
8502
|
+
);
|
|
8503
|
+
reply.send({ success: true, data: { records: states, total: states.length } });
|
|
8504
|
+
}
|
|
8505
|
+
async function getLocalA2AProvider(request, reply) {
|
|
8506
|
+
const registered = await requireManagedAssistant(request, reply);
|
|
8507
|
+
if (!registered) return;
|
|
8508
|
+
const state = await localA2AService.getRegisteredState(
|
|
8509
|
+
registered.assistantId,
|
|
8510
|
+
registered.localRuntime
|
|
8511
|
+
);
|
|
8512
|
+
reply.send({ success: true, data: state });
|
|
8513
|
+
}
|
|
8514
|
+
async function startLocalA2AProvider(request, reply) {
|
|
8515
|
+
const registered = await requireManagedAssistant(request, reply);
|
|
8516
|
+
if (!registered) return;
|
|
8517
|
+
const state = await localA2AService.startProvider(
|
|
8518
|
+
registered.assistantId,
|
|
8519
|
+
registered.localRuntime
|
|
8520
|
+
);
|
|
8521
|
+
reply.send({ success: true, data: state });
|
|
8522
|
+
}
|
|
8523
|
+
async function stopLocalA2AProvider(request, reply) {
|
|
8524
|
+
const registered = await requireManagedAssistant(request, reply);
|
|
8525
|
+
if (!registered) return;
|
|
8526
|
+
const state = await localA2AService.stopProvider(registered.assistantId);
|
|
8527
|
+
reply.send({ success: true, data: state });
|
|
8528
|
+
}
|
|
8529
|
+
async function restartLocalA2AProvider(request, reply) {
|
|
8530
|
+
const registered = await requireManagedAssistant(request, reply);
|
|
8531
|
+
if (!registered) return;
|
|
8532
|
+
const state = await localA2AService.restartProvider(
|
|
8533
|
+
registered.assistantId,
|
|
8534
|
+
registered.localRuntime
|
|
8535
|
+
);
|
|
8536
|
+
reply.send({ success: true, data: state });
|
|
8537
|
+
}
|
|
8538
|
+
|
|
8539
|
+
// src/config/local-a2a-templates.ts
|
|
8540
|
+
import { cliA2AEntryPath } from "@axiom-lattice/cli-a2a/cli-path";
|
|
8541
|
+
function buildCliA2ACommand(provider, agentName, agentDescription) {
|
|
8542
|
+
return {
|
|
8543
|
+
executable: process.execPath,
|
|
8544
|
+
args: [
|
|
8545
|
+
cliA2AEntryPath,
|
|
8546
|
+
"--provider",
|
|
8547
|
+
provider,
|
|
8548
|
+
"--hostname",
|
|
8549
|
+
"localhost",
|
|
8550
|
+
"--advertise-host",
|
|
8551
|
+
"localhost",
|
|
8552
|
+
"--agent-name",
|
|
8553
|
+
agentName,
|
|
8554
|
+
"--agent-description",
|
|
8555
|
+
agentDescription,
|
|
8556
|
+
"--log-level",
|
|
8557
|
+
"info"
|
|
8558
|
+
]
|
|
8559
|
+
};
|
|
8560
|
+
}
|
|
8561
|
+
var LOCAL_A2A_TEMPLATES = [
|
|
8562
|
+
{
|
|
8563
|
+
key: "claude",
|
|
8564
|
+
name: "Claude Code",
|
|
8565
|
+
command: buildCliA2ACommand(
|
|
8566
|
+
"claude",
|
|
8567
|
+
"Claude Code",
|
|
8568
|
+
"Local Claude Code wrapper exposed through A2A."
|
|
8569
|
+
)
|
|
8570
|
+
},
|
|
8571
|
+
{
|
|
8572
|
+
key: "codex",
|
|
8573
|
+
name: "Codex",
|
|
8574
|
+
command: buildCliA2ACommand(
|
|
8575
|
+
"codex",
|
|
8576
|
+
"Codex",
|
|
8577
|
+
"Local Codex wrapper exposed through A2A."
|
|
8578
|
+
)
|
|
8579
|
+
},
|
|
8580
|
+
{
|
|
8581
|
+
key: "opencode",
|
|
8582
|
+
name: "OpenCode",
|
|
8583
|
+
command: buildCliA2ACommand(
|
|
8584
|
+
"opencode",
|
|
8585
|
+
"OpenCode",
|
|
8586
|
+
"Local OpenCode wrapper exposed through A2A."
|
|
8587
|
+
)
|
|
8588
|
+
}
|
|
8589
|
+
];
|
|
8590
|
+
|
|
8591
|
+
// src/controllers/local-a2a-templates.ts
|
|
8592
|
+
async function getLocalA2ATemplates(_request, reply) {
|
|
8593
|
+
reply.send({
|
|
8594
|
+
success: true,
|
|
8595
|
+
data: {
|
|
8596
|
+
records: LOCAL_A2A_TEMPLATES,
|
|
8597
|
+
total: LOCAL_A2A_TEMPLATES.length
|
|
8598
|
+
}
|
|
8599
|
+
});
|
|
8600
|
+
}
|
|
8601
|
+
|
|
8602
|
+
// src/routes/a2a-test-connection.ts
|
|
8603
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
8604
|
+
function describeTestConnectionError(error, agentCardUrl) {
|
|
8605
|
+
if (!(error instanceof Error)) {
|
|
8606
|
+
return `Failed to fetch agent card from ${agentCardUrl}: ${String(error)}`;
|
|
8607
|
+
}
|
|
8608
|
+
const cause = error.cause;
|
|
8609
|
+
const causeParts = [];
|
|
8610
|
+
if (cause instanceof Error) {
|
|
8611
|
+
causeParts.push(cause.message);
|
|
8612
|
+
const causeCode = cause.code;
|
|
8613
|
+
if (causeCode && !causeParts.some((part) => part.includes(causeCode))) {
|
|
8614
|
+
causeParts.push(causeCode);
|
|
8615
|
+
}
|
|
8616
|
+
} else if (typeof cause === "object" && cause !== null) {
|
|
8617
|
+
const code = "code" in cause ? String(cause.code) : "";
|
|
8618
|
+
const message = "message" in cause ? String(cause.message) : "";
|
|
8619
|
+
if (message) causeParts.push(message);
|
|
8620
|
+
if (code && !message.includes(code)) causeParts.push(code);
|
|
8621
|
+
}
|
|
8622
|
+
const detail = causeParts.length > 0 ? ` (${causeParts.join(", ")})` : "";
|
|
8623
|
+
return `Failed to fetch agent card from ${agentCardUrl}: ${error.message}${detail}`;
|
|
8624
|
+
}
|
|
8625
|
+
async function ensureLocalRuntimeForConnectionTest(localRuntime, agentCardUrl) {
|
|
8626
|
+
const commandValidation = validateLocalRuntimeCommand(localRuntime);
|
|
8627
|
+
if (!commandValidation.ok) {
|
|
8628
|
+
return { error: commandValidation.error };
|
|
8629
|
+
}
|
|
8630
|
+
const testRuntime = {
|
|
8631
|
+
...localRuntime,
|
|
8632
|
+
agentCardUrl,
|
|
8633
|
+
command: {
|
|
8634
|
+
...localRuntime.command,
|
|
8635
|
+
executable: localRuntime.command.executable.trim()
|
|
8636
|
+
}
|
|
8637
|
+
};
|
|
8638
|
+
const portValidation = validateLocalRuntimePort(testRuntime, agentCardUrl);
|
|
8639
|
+
if (!portValidation.ok) {
|
|
8640
|
+
return { error: portValidation.error };
|
|
8641
|
+
}
|
|
8642
|
+
const state = await localA2AService.startProvider(
|
|
8643
|
+
`test-${randomUUID8()}`,
|
|
8644
|
+
testRuntime
|
|
8645
|
+
);
|
|
8646
|
+
if (state.status === "running") return { runtimeId: state.runtimeId };
|
|
8647
|
+
const detail = state.message ? ` ${state.message}` : "";
|
|
8648
|
+
localA2AService.removeState(state.runtimeId);
|
|
8649
|
+
return { error: `Local managed A2A server could not start for connection test.${detail}` };
|
|
8650
|
+
}
|
|
8651
|
+
async function readShortResponseBody(resp) {
|
|
8652
|
+
const body = await resp.text().catch(() => "");
|
|
8653
|
+
return body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
8654
|
+
}
|
|
8655
|
+
function registerA2ATestConnectionRoute(app2) {
|
|
8656
|
+
app2.post("/api/a2a/test-connection", async (request, reply) => {
|
|
8657
|
+
const { agentCardUrl, apiKey, localRuntime } = request.body ?? {};
|
|
8658
|
+
const normalizedAgentCardUrl = normalizeA2AAgentCardUrl(
|
|
8659
|
+
agentCardUrl,
|
|
8660
|
+
localRuntime ? "http://" : "https://"
|
|
8661
|
+
);
|
|
8662
|
+
if (!normalizedAgentCardUrl) {
|
|
8663
|
+
return reply.status(400).send({ success: false, error: "agentCardUrl is required" });
|
|
8664
|
+
}
|
|
8665
|
+
let testRuntimeId = null;
|
|
8666
|
+
try {
|
|
8667
|
+
const headers = {};
|
|
8668
|
+
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
8669
|
+
if (localRuntime) {
|
|
8670
|
+
const localStartResult = await ensureLocalRuntimeForConnectionTest(localRuntime, normalizedAgentCardUrl);
|
|
8671
|
+
if ("error" in localStartResult) {
|
|
8672
|
+
return reply.send({ success: true, data: { reachable: false, error: localStartResult.error } });
|
|
8673
|
+
}
|
|
8674
|
+
testRuntimeId = localStartResult.runtimeId;
|
|
8675
|
+
}
|
|
8676
|
+
const resp = await fetch(normalizedAgentCardUrl, { headers, signal: AbortSignal.timeout(8e3) });
|
|
8677
|
+
if (!resp.ok) {
|
|
8678
|
+
const body = await readShortResponseBody(resp);
|
|
8679
|
+
const suffix = body ? `: ${body}` : "";
|
|
8680
|
+
return reply.send({ success: true, data: { reachable: false, error: `HTTP ${resp.status} ${resp.statusText}${suffix}` } });
|
|
8681
|
+
}
|
|
8682
|
+
const card = await resp.json();
|
|
8683
|
+
return reply.send({ success: true, data: { reachable: true, agentName: card.name, agentCard: card } });
|
|
8684
|
+
} catch (error) {
|
|
8685
|
+
return reply.send({
|
|
8686
|
+
success: true,
|
|
8687
|
+
data: {
|
|
8688
|
+
reachable: false,
|
|
8689
|
+
error: describeTestConnectionError(error, normalizedAgentCardUrl)
|
|
8690
|
+
}
|
|
8691
|
+
});
|
|
8692
|
+
} finally {
|
|
8693
|
+
if (testRuntimeId) {
|
|
8694
|
+
localA2AService.removeState(testRuntimeId);
|
|
8695
|
+
}
|
|
8696
|
+
}
|
|
8697
|
+
});
|
|
8698
|
+
}
|
|
8699
|
+
|
|
7831
8700
|
// src/routes/index.ts
|
|
7832
8701
|
var registerLatticeRoutes = (app2, channelDeps) => {
|
|
7833
8702
|
registerA2ABridgeRoutes(app2);
|
|
7834
8703
|
registerSTTRoutes(app2);
|
|
8704
|
+
app2.get("/api/local-a2a", getLocalA2AStatus);
|
|
8705
|
+
app2.get("/api/local-a2a/templates", getLocalA2ATemplates);
|
|
8706
|
+
app2.get("/api/local-a2a/:assistantId", getLocalA2AProvider);
|
|
8707
|
+
app2.post("/api/local-a2a/:assistantId/start", startLocalA2AProvider);
|
|
8708
|
+
app2.post("/api/local-a2a/:assistantId/stop", stopLocalA2AProvider);
|
|
8709
|
+
app2.post("/api/local-a2a/:assistantId/restart", restartLocalA2AProvider);
|
|
8710
|
+
registerA2ATestConnectionRoute(app2);
|
|
7835
8711
|
app2.post("/api/runs", createRun);
|
|
7836
8712
|
app2.post("/api/resume_stream", resumeStream);
|
|
7837
8713
|
app2.post("/api/assistants/:assistantId/threads/:threadId/abort", abortRun);
|
|
@@ -8126,9 +9002,9 @@ function registerResourceRoutes(app2, controller) {
|
|
|
8126
9002
|
import { ExportableEntityRegistry as ExportableEntityRegistry3 } from "@axiom-lattice/core";
|
|
8127
9003
|
|
|
8128
9004
|
// src/export_registrations/skill.registration.ts
|
|
8129
|
-
import { getStoreLattice as
|
|
9005
|
+
import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
|
|
8130
9006
|
function getStore2() {
|
|
8131
|
-
return
|
|
9007
|
+
return getStoreLattice16("default", "skill").store;
|
|
8132
9008
|
}
|
|
8133
9009
|
var skillRegistration = {
|
|
8134
9010
|
entityType: "skill",
|
|
@@ -8203,9 +9079,9 @@ var skillRegistration = {
|
|
|
8203
9079
|
};
|
|
8204
9080
|
|
|
8205
9081
|
// src/export_registrations/agent.registration.ts
|
|
8206
|
-
import { getStoreLattice as
|
|
9082
|
+
import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
|
|
8207
9083
|
function getStore3() {
|
|
8208
|
-
return
|
|
9084
|
+
return getStoreLattice17("default", "assistant").store;
|
|
8209
9085
|
}
|
|
8210
9086
|
var agentRegistration = {
|
|
8211
9087
|
entityType: "agent",
|
|
@@ -8272,9 +9148,9 @@ var agentRegistration = {
|
|
|
8272
9148
|
};
|
|
8273
9149
|
|
|
8274
9150
|
// src/export_registrations/binding.registration.ts
|
|
8275
|
-
import { getStoreLattice as
|
|
9151
|
+
import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
|
|
8276
9152
|
function getStore4() {
|
|
8277
|
-
return
|
|
9153
|
+
return getStoreLattice18("default", "channelBinding").store;
|
|
8278
9154
|
}
|
|
8279
9155
|
var bindingRegistration = {
|
|
8280
9156
|
entityType: "binding",
|
|
@@ -8362,9 +9238,9 @@ var bindingRegistration = {
|
|
|
8362
9238
|
};
|
|
8363
9239
|
|
|
8364
9240
|
// src/export_registrations/database-config.registration.ts
|
|
8365
|
-
import { getStoreLattice as
|
|
9241
|
+
import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
|
|
8366
9242
|
function getStore5() {
|
|
8367
|
-
return
|
|
9243
|
+
return getStoreLattice19("default", "database").store;
|
|
8368
9244
|
}
|
|
8369
9245
|
var databaseConfigRegistration = {
|
|
8370
9246
|
entityType: "database_config",
|
|
@@ -8444,9 +9320,9 @@ var databaseConfigRegistration = {
|
|
|
8444
9320
|
};
|
|
8445
9321
|
|
|
8446
9322
|
// src/export_registrations/metrics-config.registration.ts
|
|
8447
|
-
import { getStoreLattice as
|
|
9323
|
+
import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
|
|
8448
9324
|
function getStore6() {
|
|
8449
|
-
return
|
|
9325
|
+
return getStoreLattice20("default", "metrics").store;
|
|
8450
9326
|
}
|
|
8451
9327
|
var metricsConfigRegistration = {
|
|
8452
9328
|
entityType: "metrics_config",
|
|
@@ -8526,9 +9402,9 @@ var metricsConfigRegistration = {
|
|
|
8526
9402
|
};
|
|
8527
9403
|
|
|
8528
9404
|
// src/export_registrations/mcp-config.registration.ts
|
|
8529
|
-
import { getStoreLattice as
|
|
9405
|
+
import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
|
|
8530
9406
|
function getStore7() {
|
|
8531
|
-
return
|
|
9407
|
+
return getStoreLattice21("default", "mcp").store;
|
|
8532
9408
|
}
|
|
8533
9409
|
var mcpConfigRegistration = {
|
|
8534
9410
|
entityType: "mcp_config",
|
|
@@ -8611,9 +9487,9 @@ var mcpConfigRegistration = {
|
|
|
8611
9487
|
};
|
|
8612
9488
|
|
|
8613
9489
|
// src/export_registrations/connection.registration.ts
|
|
8614
|
-
import { getStoreLattice as
|
|
9490
|
+
import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
|
|
8615
9491
|
function getStore8() {
|
|
8616
|
-
return
|
|
9492
|
+
return getStoreLattice22("default", "connection").store;
|
|
8617
9493
|
}
|
|
8618
9494
|
async function listAllConnections(tenantId) {
|
|
8619
9495
|
const store = getStore8();
|
|
@@ -8695,9 +9571,9 @@ var connectionRegistration = {
|
|
|
8695
9571
|
};
|
|
8696
9572
|
|
|
8697
9573
|
// src/export_registrations/collection.registration.ts
|
|
8698
|
-
import { getStoreLattice as
|
|
9574
|
+
import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
|
|
8699
9575
|
function getStore9() {
|
|
8700
|
-
return
|
|
9576
|
+
return getStoreLattice23("default", "collection").store;
|
|
8701
9577
|
}
|
|
8702
9578
|
var collectionRegistration = {
|
|
8703
9579
|
entityType: "collection",
|
|
@@ -8768,9 +9644,9 @@ var collectionRegistration = {
|
|
|
8768
9644
|
};
|
|
8769
9645
|
|
|
8770
9646
|
// src/export_registrations/channel-installation.registration.ts
|
|
8771
|
-
import { getStoreLattice as
|
|
9647
|
+
import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
|
|
8772
9648
|
function getStore10() {
|
|
8773
|
-
return
|
|
9649
|
+
return getStoreLattice24("default", "channelInstallation").store;
|
|
8774
9650
|
}
|
|
8775
9651
|
var channelInstallationRegistration = {
|
|
8776
9652
|
entityType: "channel_installation",
|
|
@@ -8845,9 +9721,9 @@ var channelInstallationRegistration = {
|
|
|
8845
9721
|
};
|
|
8846
9722
|
|
|
8847
9723
|
// src/export_registrations/menu.registration.ts
|
|
8848
|
-
import { getStoreLattice as
|
|
9724
|
+
import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
|
|
8849
9725
|
function getStore11() {
|
|
8850
|
-
return
|
|
9726
|
+
return getStoreLattice25("default", "menu").store;
|
|
8851
9727
|
}
|
|
8852
9728
|
var menuRegistration = {
|
|
8853
9729
|
entityType: "menu",
|
|
@@ -8925,9 +9801,9 @@ var menuRegistration = {
|
|
|
8925
9801
|
};
|
|
8926
9802
|
|
|
8927
9803
|
// src/export_registrations/task.registration.ts
|
|
8928
|
-
import { getStoreLattice as
|
|
9804
|
+
import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
|
|
8929
9805
|
function getStore12() {
|
|
8930
|
-
return
|
|
9806
|
+
return getStoreLattice26("default", "task").store;
|
|
8931
9807
|
}
|
|
8932
9808
|
var taskRegistration = {
|
|
8933
9809
|
entityType: "task",
|
|
@@ -9013,9 +9889,9 @@ var taskRegistration = {
|
|
|
9013
9889
|
};
|
|
9014
9890
|
|
|
9015
9891
|
// src/export_registrations/a2a-api-key.registration.ts
|
|
9016
|
-
import { getStoreLattice as
|
|
9892
|
+
import { getStoreLattice as getStoreLattice27 } from "@axiom-lattice/core";
|
|
9017
9893
|
function getStore13() {
|
|
9018
|
-
return
|
|
9894
|
+
return getStoreLattice27("default", "a2aApiKey").store;
|
|
9019
9895
|
}
|
|
9020
9896
|
var a2aApiKeyRegistration = {
|
|
9021
9897
|
entityType: "a2a_api_key",
|
|
@@ -9086,9 +9962,9 @@ var a2aApiKeyRegistration = {
|
|
|
9086
9962
|
};
|
|
9087
9963
|
|
|
9088
9964
|
// src/export_registrations/eval.registration.ts
|
|
9089
|
-
import { getStoreLattice as
|
|
9965
|
+
import { getStoreLattice as getStoreLattice28 } from "@axiom-lattice/core";
|
|
9090
9966
|
function getStore14() {
|
|
9091
|
-
return
|
|
9967
|
+
return getStoreLattice28("default", "eval").store;
|
|
9092
9968
|
}
|
|
9093
9969
|
var evalRegistration = {
|
|
9094
9970
|
entityType: "eval",
|
|
@@ -9432,10 +10308,10 @@ function registerAllBuiltinEntities() {
|
|
|
9432
10308
|
|
|
9433
10309
|
// src/router/MessageRouter.ts
|
|
9434
10310
|
import {
|
|
9435
|
-
getStoreLattice as
|
|
10311
|
+
getStoreLattice as getStoreLattice29,
|
|
9436
10312
|
agentInstanceManager as agentInstanceManager6
|
|
9437
10313
|
} from "@axiom-lattice/core";
|
|
9438
|
-
import { randomUUID as
|
|
10314
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
9439
10315
|
var BindingNotFoundError = class extends Error {
|
|
9440
10316
|
constructor(message) {
|
|
9441
10317
|
super(message);
|
|
@@ -9595,7 +10471,7 @@ var MessageRouter = class {
|
|
|
9595
10471
|
channel: message.channel,
|
|
9596
10472
|
adapterChannel: adapter.channel
|
|
9597
10473
|
}, "Thread resolved by adapter strategy");
|
|
9598
|
-
const threadStore =
|
|
10474
|
+
const threadStore = getStoreLattice29("default", "thread").store;
|
|
9599
10475
|
try {
|
|
9600
10476
|
await threadStore.createThread(
|
|
9601
10477
|
tenantId,
|
|
@@ -9632,8 +10508,8 @@ var MessageRouter = class {
|
|
|
9632
10508
|
}
|
|
9633
10509
|
}
|
|
9634
10510
|
if (!threadId) {
|
|
9635
|
-
const threadStore =
|
|
9636
|
-
const newThreadId =
|
|
10511
|
+
const threadStore = getStoreLattice29("default", "thread").store;
|
|
10512
|
+
const newThreadId = randomUUID9();
|
|
9637
10513
|
console.log({
|
|
9638
10514
|
event: "dispatch:thread:create",
|
|
9639
10515
|
agentId,
|
|
@@ -10058,7 +10934,7 @@ ${summary}`
|
|
|
10058
10934
|
console.log(
|
|
10059
10935
|
`\u5C06\u5728 ${delayMs}ms \u540E\u91CD\u8BD5\u4EFB\u52A1 (${nextRetryCount}/${maxRetries})`
|
|
10060
10936
|
);
|
|
10061
|
-
await new Promise((
|
|
10937
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
10062
10938
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
10063
10939
|
}
|
|
10064
10940
|
if (callback_event) {
|
|
@@ -10238,7 +11114,7 @@ import {
|
|
|
10238
11114
|
getLoggerLattice,
|
|
10239
11115
|
loggerLatticeManager,
|
|
10240
11116
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
10241
|
-
getStoreLattice as
|
|
11117
|
+
getStoreLattice as getStoreLattice30,
|
|
10242
11118
|
agentInstanceManager as agentInstanceManager8,
|
|
10243
11119
|
createSandboxProvider,
|
|
10244
11120
|
TokenCache,
|
|
@@ -10348,9 +11224,9 @@ app.register(multipart, {
|
|
|
10348
11224
|
});
|
|
10349
11225
|
app.register(websocket);
|
|
10350
11226
|
var __filename = fileURLToPath(import.meta.url);
|
|
10351
|
-
var __dirname =
|
|
11227
|
+
var __dirname = path3.dirname(__filename);
|
|
10352
11228
|
app.register(staticPlugin, {
|
|
10353
|
-
root:
|
|
11229
|
+
root: path3.join(__dirname, "../public"),
|
|
10354
11230
|
prefix: "/"
|
|
10355
11231
|
});
|
|
10356
11232
|
app.setErrorHandler((error, request, reply) => {
|
|
@@ -10391,7 +11267,7 @@ function getConfiguredSandboxProvider() {
|
|
|
10391
11267
|
}
|
|
10392
11268
|
async function restoreMcpConnections() {
|
|
10393
11269
|
try {
|
|
10394
|
-
const storeLattice =
|
|
11270
|
+
const storeLattice = getStoreLattice30("default", "mcp");
|
|
10395
11271
|
const store = storeLattice.store;
|
|
10396
11272
|
if (!store) {
|
|
10397
11273
|
logger4.info("MCP store not configured, skipping connection restoration");
|
|
@@ -10460,7 +11336,7 @@ var start = async (config) => {
|
|
|
10460
11336
|
channelDeps = { router, installationStore };
|
|
10461
11337
|
try {
|
|
10462
11338
|
const a2aKeyStore = getStore15("default", "a2aApiKey").store;
|
|
10463
|
-
const a2a = await import("./a2a-
|
|
11339
|
+
const a2a = await import("./a2a-QMHEH3ZT.mjs");
|
|
10464
11340
|
a2a.setA2AKeyStore(a2aKeyStore);
|
|
10465
11341
|
await a2a.refreshStoreKeyMap();
|
|
10466
11342
|
logger4.info("A2A key store initialized");
|
|
@@ -10473,7 +11349,7 @@ var start = async (config) => {
|
|
|
10473
11349
|
}
|
|
10474
11350
|
setEvalRunService(evalRunner);
|
|
10475
11351
|
try {
|
|
10476
|
-
const menuStore =
|
|
11352
|
+
const menuStore = getStoreLattice30("default", "menu").store;
|
|
10477
11353
|
setMenuRegistry(menuStore);
|
|
10478
11354
|
logger4.info("Menu registry initialized");
|
|
10479
11355
|
} catch {
|
|
@@ -10489,7 +11365,7 @@ var start = async (config) => {
|
|
|
10489
11365
|
}
|
|
10490
11366
|
try {
|
|
10491
11367
|
const { ResourceController } = await import("./resources-VA7LSDKN.mjs");
|
|
10492
|
-
const sharedResourceStore =
|
|
11368
|
+
const sharedResourceStore = getStoreLattice30("default", "sharedResource").store;
|
|
10493
11369
|
const cache = new TokenCache();
|
|
10494
11370
|
const resourceController = new ResourceController({
|
|
10495
11371
|
store: sharedResourceStore,
|
|
@@ -10520,11 +11396,6 @@ var start = async (config) => {
|
|
|
10520
11396
|
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
10521
11397
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
10522
11398
|
logger4.info(`Lattice Gateway is running on port: ${target_port}`);
|
|
10523
|
-
try {
|
|
10524
|
-
logger4.info("AgentLifecycleManager initialized");
|
|
10525
|
-
} catch (error) {
|
|
10526
|
-
logger4.warn("Failed to initialize AgentLifecycleManager", { error });
|
|
10527
|
-
}
|
|
10528
11399
|
const queueServiceConfig = config?.queueServiceConfig;
|
|
10529
11400
|
if (queueServiceConfig) {
|
|
10530
11401
|
setQueueServiceType(queueServiceConfig.type);
|