@axiom-lattice/gateway 4.0.0 → 4.0.2
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/dist/{a2a-standard-U2XYG45L.mjs → a2a-standard-VYQCHMBV.mjs} +3 -10
- package/dist/a2a-standard-VYQCHMBV.mjs.map +1 -0
- package/dist/{chunk-IDOEIBCF.mjs → chunk-J4BK77PA.mjs} +13 -2
- package/dist/chunk-J4BK77PA.mjs.map +1 -0
- package/dist/index.js +1272 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1212 -93
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/dist/a2a-standard-U2XYG45L.mjs.map +0 -1
- package/dist/chunk-IDOEIBCF.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
PathOutsideRootError,
|
|
11
11
|
ProjectFileValidationError,
|
|
12
12
|
filesystemRootDirectory,
|
|
13
|
+
formatFileRefsSection,
|
|
13
14
|
isBinaryContentType,
|
|
14
15
|
isPathWithinRoot,
|
|
15
16
|
resolvePathWithinRoot,
|
|
16
17
|
saveProjectFile
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-J4BK77PA.mjs";
|
|
18
19
|
import {
|
|
19
20
|
getContentTypeFromFilename,
|
|
20
21
|
getFilenameFromPath
|
|
@@ -827,6 +828,90 @@ import {
|
|
|
827
828
|
agentInstanceManager as agentInstanceManager2
|
|
828
829
|
} from "@axiom-lattice/core";
|
|
829
830
|
import { MessageChunkTypes } from "@axiom-lattice/protocols";
|
|
831
|
+
|
|
832
|
+
// src/services/agents/ScopedAgentExecution.ts
|
|
833
|
+
async function executeScopedAgentMessage(agent, request, stopTypes, options) {
|
|
834
|
+
const { messageId } = options === void 0 ? await agent.addMessage(request) : await agent.addMessage(request, options.queueMode);
|
|
835
|
+
return { messageId, stream: agent.chunkStream(messageId, stopTypes) };
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/services/streaming/pipeAsyncIterableToSse.ts
|
|
839
|
+
async function pipeAsyncIterableToSse(raw, stream, options = {}) {
|
|
840
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
841
|
+
const project = options.project ?? ((value) => [value]);
|
|
842
|
+
const serialize = options.serialize ?? ((event) => `data: ${JSON.stringify(event)}
|
|
843
|
+
|
|
844
|
+
`);
|
|
845
|
+
const closeSources = options.closeSource && options.closeSource !== raw ? [raw, options.closeSource] : [raw];
|
|
846
|
+
let closed = raw.destroyed || raw.writableEnded;
|
|
847
|
+
let returned = false;
|
|
848
|
+
let resolveClosed;
|
|
849
|
+
const closedPromise = new Promise((resolve2) => {
|
|
850
|
+
resolveClosed = resolve2;
|
|
851
|
+
});
|
|
852
|
+
const returnConsumer = () => {
|
|
853
|
+
if (returned) return;
|
|
854
|
+
returned = true;
|
|
855
|
+
void Promise.resolve(iterator.return?.()).catch(() => void 0);
|
|
856
|
+
};
|
|
857
|
+
const onClose = () => {
|
|
858
|
+
closed = true;
|
|
859
|
+
returnConsumer();
|
|
860
|
+
resolveClosed?.();
|
|
861
|
+
};
|
|
862
|
+
for (const source of closeSources) {
|
|
863
|
+
source.on("close", onClose);
|
|
864
|
+
source.on("error", onClose);
|
|
865
|
+
}
|
|
866
|
+
try {
|
|
867
|
+
while (!closed) {
|
|
868
|
+
const next = await Promise.race([
|
|
869
|
+
iterator.next().then((result) => ({ kind: "next", result })),
|
|
870
|
+
closedPromise.then(() => ({ kind: "closed" }))
|
|
871
|
+
]);
|
|
872
|
+
if (next.kind === "closed" || next.result.done) break;
|
|
873
|
+
for (const event of project(next.result.value)) {
|
|
874
|
+
if (!await writeSse(raw, serialize(event), closedPromise)) {
|
|
875
|
+
closed = true;
|
|
876
|
+
returnConsumer();
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
} catch (error) {
|
|
882
|
+
if (!options.onError) throw error;
|
|
883
|
+
if (!closed && !raw.destroyed && !raw.writableEnded) {
|
|
884
|
+
for (const event of options.onError(error)) {
|
|
885
|
+
if (!await writeSse(raw, serialize(event), closedPromise)) break;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
returnConsumer();
|
|
889
|
+
} finally {
|
|
890
|
+
for (const source of closeSources) {
|
|
891
|
+
source.removeListener("close", onClose);
|
|
892
|
+
source.removeListener("error", onClose);
|
|
893
|
+
}
|
|
894
|
+
if (!closed && !raw.destroyed && !raw.writableEnded) raw.end();
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
async function writeSse(raw, value, closed) {
|
|
898
|
+
if (raw.destroyed || raw.writableEnded) return false;
|
|
899
|
+
if (raw.write(value)) return true;
|
|
900
|
+
return new Promise((resolve2) => {
|
|
901
|
+
let settled = false;
|
|
902
|
+
const onDrain = () => finish(!raw.destroyed && !raw.writableEnded);
|
|
903
|
+
const finish = (writable) => {
|
|
904
|
+
if (settled) return;
|
|
905
|
+
settled = true;
|
|
906
|
+
raw.removeListener("drain", onDrain);
|
|
907
|
+
resolve2(writable);
|
|
908
|
+
};
|
|
909
|
+
raw.once("drain", onDrain);
|
|
910
|
+
void closed.then(() => finish(false));
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/controllers/run.ts
|
|
830
915
|
function getUserId2(request) {
|
|
831
916
|
const authUser = request.user;
|
|
832
917
|
if (authUser?.id) return authUser.id;
|
|
@@ -893,20 +978,22 @@ var createRun = async (request, reply) => {
|
|
|
893
978
|
});
|
|
894
979
|
try {
|
|
895
980
|
const messageInput = message_id ? { ...input, id: message_id } : input;
|
|
896
|
-
const result = await agent
|
|
981
|
+
const result = await executeScopedAgentMessage(agent, {
|
|
897
982
|
input: messageInput,
|
|
898
983
|
command,
|
|
899
984
|
custom_run_config: mergedConfig
|
|
900
|
-
},
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
985
|
+
}, [MessageChunkTypes.MESSAGE_COMPLETED, MessageChunkTypes.INTERRUPT, MessageChunkTypes.MESSAGE_FAILED], {
|
|
986
|
+
queueMode: mode
|
|
987
|
+
});
|
|
988
|
+
await pipeAsyncIterableToSse(reply.raw, result.stream, {
|
|
989
|
+
onError: (error) => [{
|
|
990
|
+
type: "error",
|
|
991
|
+
data: {
|
|
992
|
+
id: v4(),
|
|
993
|
+
content: error instanceof Error ? error.message : "Stream processing error"
|
|
994
|
+
}
|
|
995
|
+
}]
|
|
996
|
+
});
|
|
910
997
|
} catch (error) {
|
|
911
998
|
const errorEvent = {
|
|
912
999
|
type: "error",
|
|
@@ -919,7 +1006,7 @@ var createRun = async (request, reply) => {
|
|
|
919
1006
|
|
|
920
1007
|
`);
|
|
921
1008
|
} finally {
|
|
922
|
-
reply.raw.end();
|
|
1009
|
+
if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end();
|
|
923
1010
|
}
|
|
924
1011
|
} else {
|
|
925
1012
|
const { message: msg, ...restInputNonStream } = input;
|
|
@@ -968,17 +1055,16 @@ var resumeStream = async (request, reply) => {
|
|
|
968
1055
|
workspace_id,
|
|
969
1056
|
project_id
|
|
970
1057
|
});
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1058
|
+
await pipeAsyncIterableToSse(reply.raw, agent.chunkStream(message_id, []), {
|
|
1059
|
+
closeSource: request.raw,
|
|
1060
|
+
onError: (error) => [{
|
|
1061
|
+
type: "error",
|
|
1062
|
+
data: {
|
|
1063
|
+
id: v4(),
|
|
1064
|
+
content: error instanceof Error ? error.message : "Resume stream processing error"
|
|
1065
|
+
}
|
|
1066
|
+
}]
|
|
975
1067
|
});
|
|
976
|
-
for await (const chunk of stream) {
|
|
977
|
-
if (closed || reply.raw.destroyed) break;
|
|
978
|
-
reply.raw.write(`data: ${JSON.stringify(chunk)}
|
|
979
|
-
|
|
980
|
-
`);
|
|
981
|
-
}
|
|
982
1068
|
} catch (error) {
|
|
983
1069
|
const errorEvent = {
|
|
984
1070
|
type: "error",
|
|
@@ -991,7 +1077,7 @@ var resumeStream = async (request, reply) => {
|
|
|
991
1077
|
|
|
992
1078
|
`);
|
|
993
1079
|
} finally {
|
|
994
|
-
reply.raw.end();
|
|
1080
|
+
if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end();
|
|
995
1081
|
}
|
|
996
1082
|
} catch (error) {
|
|
997
1083
|
reply.status(500).send({
|
|
@@ -3358,7 +3444,7 @@ async function abortWorkflowRun(request, reply) {
|
|
|
3358
3444
|
}
|
|
3359
3445
|
|
|
3360
3446
|
// src/controllers/personal-assistant.ts
|
|
3361
|
-
import { getStoreLattice as getStoreLattice5, PersonalAssistantConfig } from "@axiom-lattice/core";
|
|
3447
|
+
import { eventBus as eventBus2, getStoreLattice as getStoreLattice5, PersonalAssistantConfig } from "@axiom-lattice/core";
|
|
3362
3448
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3363
3449
|
function getWorkspaceId2(request) {
|
|
3364
3450
|
return request.headers["x-workspace-id"] || "default";
|
|
@@ -3455,6 +3541,7 @@ async function deletePersonalAssistant(request, reply) {
|
|
|
3455
3541
|
if (!deleted) {
|
|
3456
3542
|
return reply.status(500).send({ success: false, message: "Failed to delete personal assistant" });
|
|
3457
3543
|
}
|
|
3544
|
+
eventBus2.publish("assistant:deleted", { id: assistant.id, tenantId });
|
|
3458
3545
|
const threadStore = getStoreLattice5("default", "thread").store;
|
|
3459
3546
|
try {
|
|
3460
3547
|
const threads = await threadStore.getThreadsByAssistantId(tenantId, assistant.id);
|
|
@@ -6908,11 +6995,11 @@ async function createProject(request, reply) {
|
|
|
6908
6995
|
}
|
|
6909
6996
|
const serverCfg = data.targetServerConfig || {};
|
|
6910
6997
|
const judgeCfg = data.judgeModelConfig || {};
|
|
6911
|
-
const
|
|
6912
|
-
if (
|
|
6913
|
-
const { modelLatticeManager:
|
|
6914
|
-
if (!
|
|
6915
|
-
return reply.status(400).send({ success: false, message: `Judge model "${
|
|
6998
|
+
const modelKey2 = judgeCfg.modelKey || "";
|
|
6999
|
+
if (modelKey2) {
|
|
7000
|
+
const { modelLatticeManager: modelLatticeManager4 } = await import("@axiom-lattice/core");
|
|
7001
|
+
if (!modelLatticeManager4.hasLattice(modelKey2)) {
|
|
7002
|
+
return reply.status(400).send({ success: false, message: `Judge model "${modelKey2}" is not registered` });
|
|
6916
7003
|
}
|
|
6917
7004
|
}
|
|
6918
7005
|
const project = await store.createProject(tenantId, id, {
|
|
@@ -6920,7 +7007,7 @@ async function createProject(request, reply) {
|
|
|
6920
7007
|
description: data.description,
|
|
6921
7008
|
version: data.version,
|
|
6922
7009
|
judgeModelConfig: {
|
|
6923
|
-
modelKey,
|
|
7010
|
+
modelKey: modelKey2,
|
|
6924
7011
|
displayName: judgeCfg.displayName || ""
|
|
6925
7012
|
},
|
|
6926
7013
|
targetServerConfig: {
|
|
@@ -6957,8 +7044,8 @@ async function listProjects(request, reply) {
|
|
|
6957
7044
|
let projects = await store.getProjectsByTenant(tenantId);
|
|
6958
7045
|
if (projects.length === 0) {
|
|
6959
7046
|
try {
|
|
6960
|
-
const { modelLatticeManager:
|
|
6961
|
-
const models =
|
|
7047
|
+
const { modelLatticeManager: modelLatticeManager4 } = await import("@axiom-lattice/core");
|
|
7048
|
+
const models = modelLatticeManager4.getAllLattices();
|
|
6962
7049
|
const first = models[0];
|
|
6963
7050
|
const judgeModel = first ? { modelKey: first.key } : {};
|
|
6964
7051
|
const host = request.hostname || "localhost";
|
|
@@ -7024,15 +7111,15 @@ async function updateProject(request, reply) {
|
|
|
7024
7111
|
}
|
|
7025
7112
|
if (body.judgeModelConfig !== void 0) {
|
|
7026
7113
|
const judgeCfg = body.judgeModelConfig || {};
|
|
7027
|
-
const
|
|
7028
|
-
if (
|
|
7029
|
-
const { modelLatticeManager:
|
|
7030
|
-
if (!
|
|
7031
|
-
return reply.status(400).send({ success: false, message: `Judge model "${
|
|
7114
|
+
const modelKey2 = judgeCfg.modelKey || "";
|
|
7115
|
+
if (modelKey2) {
|
|
7116
|
+
const { modelLatticeManager: modelLatticeManager4 } = await import("@axiom-lattice/core");
|
|
7117
|
+
if (!modelLatticeManager4.hasLattice(modelKey2)) {
|
|
7118
|
+
return reply.status(400).send({ success: false, message: `Judge model "${modelKey2}" is not registered` });
|
|
7032
7119
|
}
|
|
7033
7120
|
}
|
|
7034
7121
|
updateData.judgeModelConfig = {
|
|
7035
|
-
modelKey,
|
|
7122
|
+
modelKey: modelKey2,
|
|
7036
7123
|
displayName: judgeCfg.displayName || ""
|
|
7037
7124
|
};
|
|
7038
7125
|
}
|
|
@@ -7895,6 +7982,8 @@ function extractUserFromAuthHeader(authHeader, secret) {
|
|
|
7895
7982
|
var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
|
|
7896
7983
|
function bypassesConsoleAuth(request) {
|
|
7897
7984
|
const path4 = request.url.split("?")[0];
|
|
7985
|
+
const method = request.method;
|
|
7986
|
+
if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path4) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path4) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path4) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path4) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path4) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path4)) return true;
|
|
7898
7987
|
if (request.method === "GET" && /^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path4)) {
|
|
7899
7988
|
return true;
|
|
7900
7989
|
}
|
|
@@ -7902,13 +7991,13 @@ function bypassesConsoleAuth(request) {
|
|
|
7902
7991
|
}
|
|
7903
7992
|
function registerConsoleAuthHook(app2, options) {
|
|
7904
7993
|
app2.addHook("preHandler", async (request, reply) => {
|
|
7905
|
-
if (bypassesConsoleAuth(request)) return;
|
|
7906
7994
|
const secret = options.tokenSecret ?? resolveConsoleTokenSecret(options.authRequired);
|
|
7907
7995
|
const user = extractUserFromAuthHeader(request.headers.authorization, secret);
|
|
7908
7996
|
if (user) {
|
|
7909
7997
|
request.user = user;
|
|
7910
|
-
return;
|
|
7911
7998
|
}
|
|
7999
|
+
if (bypassesConsoleAuth(request)) return;
|
|
8000
|
+
if (user) return;
|
|
7912
8001
|
if (!options.authRequired || request.method === "OPTIONS") return;
|
|
7913
8002
|
if (PUBLIC_ROUTES.some((route) => request.url === route)) return;
|
|
7914
8003
|
if (request.url.startsWith("/s/")) return;
|
|
@@ -8305,8 +8394,8 @@ function snapshotPlainObject(value) {
|
|
|
8305
8394
|
}
|
|
8306
8395
|
}
|
|
8307
8396
|
async function getInstallationStore() {
|
|
8308
|
-
const { getStoreLattice:
|
|
8309
|
-
const store =
|
|
8397
|
+
const { getStoreLattice: getStoreLattice28 } = await import("@axiom-lattice/core");
|
|
8398
|
+
const store = getStoreLattice28("default", "channelInstallation").store;
|
|
8310
8399
|
if (store) return store;
|
|
8311
8400
|
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
8312
8401
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -9002,7 +9091,7 @@ var registerSTTRoutes = (app2) => {
|
|
|
9002
9091
|
}
|
|
9003
9092
|
},
|
|
9004
9093
|
async (request, reply) => {
|
|
9005
|
-
const
|
|
9094
|
+
const modelKey2 = request.query.model || "default";
|
|
9006
9095
|
const data = await request.file({
|
|
9007
9096
|
limits: { fileSize: MAX_AUDIO_FILE_SIZE }
|
|
9008
9097
|
});
|
|
@@ -9020,9 +9109,9 @@ var registerSTTRoutes = (app2) => {
|
|
|
9020
9109
|
try {
|
|
9021
9110
|
let client;
|
|
9022
9111
|
try {
|
|
9023
|
-
client = getSTTClient(
|
|
9112
|
+
client = getSTTClient(modelKey2);
|
|
9024
9113
|
} catch {
|
|
9025
|
-
return reply.status(404).send({ error: `STT model "${
|
|
9114
|
+
return reply.status(404).send({ error: `STT model "${modelKey2}" not found` });
|
|
9026
9115
|
}
|
|
9027
9116
|
const result = await client.transcribe(buffer, format);
|
|
9028
9117
|
return reply.send({
|
|
@@ -9300,6 +9389,8 @@ function registerA2ATestConnectionRoute(app2) {
|
|
|
9300
9389
|
|
|
9301
9390
|
// src/controllers/tasks.ts
|
|
9302
9391
|
import {
|
|
9392
|
+
agentLatticeManager as agentLatticeManager4,
|
|
9393
|
+
ensureBuiltinAgentsForTenant,
|
|
9303
9394
|
getStoreLattice as getStoreLattice15
|
|
9304
9395
|
} from "@axiom-lattice/core";
|
|
9305
9396
|
var AGENT_CREATE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
@@ -9437,8 +9528,10 @@ async function createTask(request, reply) {
|
|
|
9437
9528
|
const ownerType = body.ownerType || "user";
|
|
9438
9529
|
const ownerId = body.ownerId || userId;
|
|
9439
9530
|
if (ownerType === "agent") {
|
|
9531
|
+
ensureBuiltinAgentsForTenant(tenantId);
|
|
9440
9532
|
const assistant = await getAssistantStore2().getAssistantById(tenantId, ownerId);
|
|
9441
|
-
|
|
9533
|
+
const configuredAgent = agentLatticeManager4.getAgentConfigWithTenant(tenantId, ownerId);
|
|
9534
|
+
if ((!assistant || assistant.tenantId !== tenantId) && !configuredAgent) {
|
|
9442
9535
|
return reply.status(404).send({
|
|
9443
9536
|
success: false,
|
|
9444
9537
|
code: "AGENT_OWNER_NOT_FOUND",
|
|
@@ -9586,8 +9679,1034 @@ function registerTaskRoutes(app2) {
|
|
|
9586
9679
|
app2.get("/api/tasks/:id/work-items", listTaskWorkItems);
|
|
9587
9680
|
}
|
|
9588
9681
|
|
|
9682
|
+
// src/routes/index.ts
|
|
9683
|
+
import { agentInstanceManager as agentInstanceManager6, agentLatticeManager as agentLatticeManager5, getStoreLattice as getStoreLattice16, modelLatticeManager as modelLatticeManager3 } from "@axiom-lattice/core";
|
|
9684
|
+
|
|
9685
|
+
// src/services/agent-web-app/AgentWebAppService.ts
|
|
9686
|
+
function createConfiguredAgentResolver(registry) {
|
|
9687
|
+
return {
|
|
9688
|
+
async exists(tenantId, assistantId) {
|
|
9689
|
+
await registry.hydrate(tenantId);
|
|
9690
|
+
return registry.has(tenantId, assistantId);
|
|
9691
|
+
}
|
|
9692
|
+
};
|
|
9693
|
+
}
|
|
9694
|
+
var AgentWebAppError = class extends Error {
|
|
9695
|
+
constructor(code, statusCode, message) {
|
|
9696
|
+
super(message);
|
|
9697
|
+
this.code = code;
|
|
9698
|
+
this.statusCode = statusCode;
|
|
9699
|
+
this.name = "AgentWebAppError";
|
|
9700
|
+
}
|
|
9701
|
+
};
|
|
9702
|
+
var AgentWebAppService = class {
|
|
9703
|
+
constructor(deps) {
|
|
9704
|
+
this.deps = deps;
|
|
9705
|
+
}
|
|
9706
|
+
async create(context, input) {
|
|
9707
|
+
await this.validateAssistant(context, input.assistantId);
|
|
9708
|
+
const normalized = await this.validateConfig(context.tenantId, input);
|
|
9709
|
+
return this.deps.webAppStore.create(context.tenantId, normalized);
|
|
9710
|
+
}
|
|
9711
|
+
async list(context, assistantId) {
|
|
9712
|
+
const records = await this.deps.webAppStore.list(context.tenantId, assistantId);
|
|
9713
|
+
const visible = [];
|
|
9714
|
+
for (const record of records) {
|
|
9715
|
+
if (await this.canAccessAssistant(context, record.assistantId)) visible.push(record);
|
|
9716
|
+
}
|
|
9717
|
+
return visible;
|
|
9718
|
+
}
|
|
9719
|
+
async get(context, webAppId) {
|
|
9720
|
+
return this.requireAuthorizedWebApp(context, webAppId);
|
|
9721
|
+
}
|
|
9722
|
+
async update(context, webAppId, patch) {
|
|
9723
|
+
const current = await this.requireAuthorizedWebApp(context, webAppId);
|
|
9724
|
+
const effective = {
|
|
9725
|
+
assistantId: current.assistantId,
|
|
9726
|
+
name: patch.name ?? current.name,
|
|
9727
|
+
description: Object.prototype.hasOwnProperty.call(patch, "description") ? patch.description : current.description,
|
|
9728
|
+
integration: current.integration,
|
|
9729
|
+
scope: { ...current.scope, ...patch.scope },
|
|
9730
|
+
features: { ...current.features, ...patch.features },
|
|
9731
|
+
appearance: { ...current.appearance, ...patch.appearance }
|
|
9732
|
+
};
|
|
9733
|
+
await this.validateAssistant(context, current.assistantId);
|
|
9734
|
+
const validated = await this.validateConfig(context.tenantId, effective);
|
|
9735
|
+
const update = {
|
|
9736
|
+
...patch.name !== void 0 ? { name: validated.name } : {},
|
|
9737
|
+
...Object.prototype.hasOwnProperty.call(patch, "description") ? { description: validated.description } : {},
|
|
9738
|
+
...patch.scope ? { scope: validated.scope } : {},
|
|
9739
|
+
...patch.features ? { features: validated.features } : {},
|
|
9740
|
+
...patch.appearance ? { appearance: validated.appearance } : {}
|
|
9741
|
+
};
|
|
9742
|
+
return this.writeSnapshot(context, current, update);
|
|
9743
|
+
}
|
|
9744
|
+
async enable(context, webAppId) {
|
|
9745
|
+
const current = await this.requireAuthorizedWebApp(context, webAppId);
|
|
9746
|
+
await this.validateConfig(context.tenantId, current);
|
|
9747
|
+
return this.writeSnapshot(context, current, { status: "active" });
|
|
9748
|
+
}
|
|
9749
|
+
async disable(context, webAppId) {
|
|
9750
|
+
const current = await this.requireAuthorizedWebApp(context, webAppId);
|
|
9751
|
+
return this.writeSnapshot(context, current, { status: "disabled" });
|
|
9752
|
+
}
|
|
9753
|
+
async delete(context, webAppId) {
|
|
9754
|
+
await this.requireAuthorizedWebApp(context, webAppId);
|
|
9755
|
+
if (!await this.deps.webAppStore.delete(context.tenantId, webAppId)) this.notFound();
|
|
9756
|
+
}
|
|
9757
|
+
async validateAssistant(context, assistantId) {
|
|
9758
|
+
const assistant = await this.deps.assistantStore.getAssistantById(context.tenantId, assistantId);
|
|
9759
|
+
if (assistant) {
|
|
9760
|
+
if (assistant.ownerUserId && assistant.ownerUserId !== context.userId) {
|
|
9761
|
+
throw new AgentWebAppError("ASSISTANT_FORBIDDEN", 403, "Assistant is not available to this user");
|
|
9762
|
+
}
|
|
9763
|
+
return;
|
|
9764
|
+
}
|
|
9765
|
+
if (await this.deps.configuredAgentResolver.exists(context.tenantId, assistantId)) return;
|
|
9766
|
+
throw new AgentWebAppError("ASSISTANT_NOT_FOUND", 404, "Assistant not found");
|
|
9767
|
+
}
|
|
9768
|
+
async validateConfig(tenantId, input) {
|
|
9769
|
+
const allowedProjectIds = [...new Set(input.scope.allowedProjectIds)];
|
|
9770
|
+
if (!allowedProjectIds.length || !allowedProjectIds.includes(input.scope.defaultProjectId)) {
|
|
9771
|
+
throw new AgentWebAppError("PROJECT_SCOPE_INVALID", 400, "Default project must be in the project allowlist");
|
|
9772
|
+
}
|
|
9773
|
+
if (!input.features.projectSelector && allowedProjectIds.length !== 1) {
|
|
9774
|
+
throw new AgentWebAppError("PROJECT_SCOPE_INVALID", 400, "Disabled project selection requires exactly one project");
|
|
9775
|
+
}
|
|
9776
|
+
for (const projectId of allowedProjectIds) {
|
|
9777
|
+
if (!await this.deps.projectStore.getProjectById(tenantId, projectId)) {
|
|
9778
|
+
throw new AgentWebAppError("PROJECT_NOT_FOUND", 404, "Project not found");
|
|
9779
|
+
}
|
|
9780
|
+
}
|
|
9781
|
+
const scope = this.validateModels(input.scope, input.features.modelSelector);
|
|
9782
|
+
return { ...input, scope: { ...scope, allowedProjectIds } };
|
|
9783
|
+
}
|
|
9784
|
+
validateModels(scope, modelSelector) {
|
|
9785
|
+
const allowedModelKeys = scope.allowedModelKeys ? [...new Set(scope.allowedModelKeys)] : void 0;
|
|
9786
|
+
if (!allowedModelKeys?.length) {
|
|
9787
|
+
if (scope.defaultModelKey || modelSelector) {
|
|
9788
|
+
throw new AgentWebAppError("MODEL_SCOPE_INVALID", 400, "Agent-default models cannot enable model selection or set a default");
|
|
9789
|
+
}
|
|
9790
|
+
return { ...scope, defaultModelKey: void 0, allowedModelKeys: void 0 };
|
|
9791
|
+
}
|
|
9792
|
+
if (!modelSelector && allowedModelKeys.length > 1) {
|
|
9793
|
+
throw new AgentWebAppError("MODEL_SCOPE_INVALID", 400, "Disabled model selection allows at most one model");
|
|
9794
|
+
}
|
|
9795
|
+
if (scope.defaultModelKey && !allowedModelKeys.includes(scope.defaultModelKey)) {
|
|
9796
|
+
throw new AgentWebAppError("MODEL_SCOPE_INVALID", 400, "Default model must be in the model allowlist");
|
|
9797
|
+
}
|
|
9798
|
+
for (const key of allowedModelKeys) {
|
|
9799
|
+
if (!this.deps.modelRegistry.has(key)) {
|
|
9800
|
+
throw new AgentWebAppError("MODEL_NOT_FOUND", 400, "Configured model is not registered");
|
|
9801
|
+
}
|
|
9802
|
+
}
|
|
9803
|
+
return { ...scope, allowedModelKeys };
|
|
9804
|
+
}
|
|
9805
|
+
async requireWebApp(tenantId, webAppId) {
|
|
9806
|
+
return await this.deps.webAppStore.getById(tenantId, webAppId) ?? this.notFound();
|
|
9807
|
+
}
|
|
9808
|
+
async requireAuthorizedWebApp(context, webAppId) {
|
|
9809
|
+
const webApp = await this.requireWebApp(context.tenantId, webAppId);
|
|
9810
|
+
if (!await this.canAccessAssistant(context, webApp.assistantId)) this.notFound();
|
|
9811
|
+
return webApp;
|
|
9812
|
+
}
|
|
9813
|
+
async canAccessAssistant(context, assistantId) {
|
|
9814
|
+
const assistant = await this.deps.assistantStore.getAssistantById(context.tenantId, assistantId);
|
|
9815
|
+
if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === context.userId;
|
|
9816
|
+
return this.deps.configuredAgentResolver.exists(context.tenantId, assistantId);
|
|
9817
|
+
}
|
|
9818
|
+
async writeSnapshot(context, current, patch) {
|
|
9819
|
+
const updated = await this.deps.webAppStore.update(
|
|
9820
|
+
context.tenantId,
|
|
9821
|
+
current.id,
|
|
9822
|
+
patch,
|
|
9823
|
+
{ expectedUpdatedAt: current.updatedAt }
|
|
9824
|
+
);
|
|
9825
|
+
if (updated) return updated;
|
|
9826
|
+
await this.requireAuthorizedWebApp(context, current.id);
|
|
9827
|
+
throw new AgentWebAppError("WEB_APP_CONFLICT", 409, "Agent Web App was modified concurrently");
|
|
9828
|
+
}
|
|
9829
|
+
notFound() {
|
|
9830
|
+
throw new AgentWebAppError("WEB_APP_NOT_FOUND", 404, "Agent Web App not found");
|
|
9831
|
+
}
|
|
9832
|
+
};
|
|
9833
|
+
|
|
9834
|
+
// src/routes/agent-web-apps.ts
|
|
9835
|
+
import { ZodError } from "zod";
|
|
9836
|
+
|
|
9837
|
+
// src/schemas/agent-web-app.ts
|
|
9838
|
+
import { z as z3 } from "zod";
|
|
9839
|
+
var externalId = z3.string().min(1).max(128).regex(/^[A-Za-z0-9._:@-]+$/);
|
|
9840
|
+
var modelKey = z3.string().min(1).max(128);
|
|
9841
|
+
var agentWebAppScopeSchema = z3.object({
|
|
9842
|
+
defaultProjectId: externalId,
|
|
9843
|
+
allowedProjectIds: z3.array(externalId).min(1).max(100),
|
|
9844
|
+
defaultModelKey: modelKey.optional(),
|
|
9845
|
+
allowedModelKeys: z3.array(modelKey).max(100).optional()
|
|
9846
|
+
}).strict();
|
|
9847
|
+
var agentWebAppFeaturesSchema = z3.object({
|
|
9848
|
+
projectSelector: z3.boolean(),
|
|
9849
|
+
modelSelector: z3.boolean(),
|
|
9850
|
+
threadManagement: z3.boolean(),
|
|
9851
|
+
attachments: z3.boolean(),
|
|
9852
|
+
hitl: z3.boolean(),
|
|
9853
|
+
genUI: z3.boolean()
|
|
9854
|
+
}).strict();
|
|
9855
|
+
var agentWebAppAppearanceSchema = z3.object({
|
|
9856
|
+
title: z3.string().max(120).optional(),
|
|
9857
|
+
welcomeMessage: z3.string().max(1e3).optional(),
|
|
9858
|
+
primaryColor: z3.string().regex(/^#[0-9A-Fa-f]{6}$/).optional()
|
|
9859
|
+
}).strict();
|
|
9860
|
+
var createAgentWebAppSchema = z3.object({
|
|
9861
|
+
assistantId: externalId,
|
|
9862
|
+
name: z3.string().min(1).max(120),
|
|
9863
|
+
description: z3.string().max(1e3).optional(),
|
|
9864
|
+
integration: z3.object({ type: z3.literal("react_sdk") }).strict(),
|
|
9865
|
+
scope: agentWebAppScopeSchema,
|
|
9866
|
+
features: agentWebAppFeaturesSchema,
|
|
9867
|
+
appearance: agentWebAppAppearanceSchema
|
|
9868
|
+
}).strict();
|
|
9869
|
+
var updateAgentWebAppSchema = z3.object({
|
|
9870
|
+
name: z3.string().min(1).max(120).optional(),
|
|
9871
|
+
description: z3.string().max(1e3).optional(),
|
|
9872
|
+
scope: agentWebAppScopeSchema.partial().strict().optional(),
|
|
9873
|
+
features: agentWebAppFeaturesSchema.partial().strict().optional(),
|
|
9874
|
+
appearance: agentWebAppAppearanceSchema.partial().strict().optional()
|
|
9875
|
+
}).strict().refine((value) => Object.keys(value).length > 0, "Patch must not be empty");
|
|
9876
|
+
var listAgentWebAppsQuerySchema = z3.object({
|
|
9877
|
+
assistantId: externalId.optional()
|
|
9878
|
+
}).strict();
|
|
9879
|
+
|
|
9880
|
+
// src/routes/agent-web-apps.ts
|
|
9881
|
+
function registerAgentWebAppRoutes(app2, deps) {
|
|
9882
|
+
app2.register(async (routes) => {
|
|
9883
|
+
routes.setErrorHandler((error, _request, reply) => normalizeError(reply, error));
|
|
9884
|
+
registerScopedRoutes(routes, deps.service);
|
|
9885
|
+
});
|
|
9886
|
+
}
|
|
9887
|
+
function registerScopedRoutes(app2, service2) {
|
|
9888
|
+
app2.post("/api/web-apps", async (request, reply) => handle(reply, async () => ({
|
|
9889
|
+
statusCode: 201,
|
|
9890
|
+
data: await service2.create(requireContext(request), createAgentWebAppSchema.parse(request.body))
|
|
9891
|
+
})));
|
|
9892
|
+
app2.get("/api/web-apps", async (request, reply) => handle(reply, async () => {
|
|
9893
|
+
const context = requireContext(request);
|
|
9894
|
+
const query = listAgentWebAppsQuerySchema.parse(request.query);
|
|
9895
|
+
const records = await service2.list(context, query.assistantId);
|
|
9896
|
+
return { data: { records, total: records.length } };
|
|
9897
|
+
}));
|
|
9898
|
+
app2.get("/api/web-apps/:webAppId", async (request, reply) => handle(reply, async () => ({ data: await service2.get(requireContext(request), request.params.webAppId) })));
|
|
9899
|
+
app2.patch("/api/web-apps/:webAppId", async (request, reply) => handle(reply, async () => ({
|
|
9900
|
+
data: await service2.update(
|
|
9901
|
+
requireContext(request),
|
|
9902
|
+
request.params.webAppId,
|
|
9903
|
+
updateAgentWebAppSchema.parse(request.body)
|
|
9904
|
+
)
|
|
9905
|
+
})));
|
|
9906
|
+
app2.post("/api/web-apps/:webAppId/enable", async (request, reply) => handle(reply, async () => ({ data: await service2.enable(requireContext(request), request.params.webAppId) })));
|
|
9907
|
+
app2.post("/api/web-apps/:webAppId/disable", async (request, reply) => handle(reply, async () => ({ data: await service2.disable(requireContext(request), request.params.webAppId) })));
|
|
9908
|
+
app2.delete("/api/web-apps/:webAppId", async (request, reply) => handle(reply, async () => {
|
|
9909
|
+
await service2.delete(requireContext(request), request.params.webAppId);
|
|
9910
|
+
return { data: { id: request.params.webAppId } };
|
|
9911
|
+
}));
|
|
9912
|
+
}
|
|
9913
|
+
function requireContext(request) {
|
|
9914
|
+
const user = request.user;
|
|
9915
|
+
const userId = user?.id ?? user?.userId;
|
|
9916
|
+
if (!userId || !user?.tenantId) {
|
|
9917
|
+
throw new AgentWebAppError("UNAUTHORIZED", 401, "Console session required");
|
|
9918
|
+
}
|
|
9919
|
+
return { tenantId: user.tenantId, userId };
|
|
9920
|
+
}
|
|
9921
|
+
async function handle(reply, operation) {
|
|
9922
|
+
try {
|
|
9923
|
+
const result = await operation();
|
|
9924
|
+
return reply.code(result.statusCode ?? 200).send({ success: true, data: result.data });
|
|
9925
|
+
} catch (error) {
|
|
9926
|
+
if (error instanceof ZodError) {
|
|
9927
|
+
return invalidRequest(reply, 400);
|
|
9928
|
+
}
|
|
9929
|
+
if (error instanceof AgentWebAppError) {
|
|
9930
|
+
return reply.code(error.statusCode).send({
|
|
9931
|
+
success: false,
|
|
9932
|
+
error: { code: error.code, message: error.message, retryable: false }
|
|
9933
|
+
});
|
|
9934
|
+
}
|
|
9935
|
+
throw error;
|
|
9936
|
+
}
|
|
9937
|
+
}
|
|
9938
|
+
function normalizeError(reply, error) {
|
|
9939
|
+
if (error instanceof AgentWebAppError) {
|
|
9940
|
+
return reply.code(error.statusCode).send({
|
|
9941
|
+
success: false,
|
|
9942
|
+
error: { code: error.code, message: error.message, retryable: false }
|
|
9943
|
+
});
|
|
9944
|
+
}
|
|
9945
|
+
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
9946
|
+
return invalidRequest(reply, error.statusCode);
|
|
9947
|
+
}
|
|
9948
|
+
return reply.code(500).send({
|
|
9949
|
+
success: false,
|
|
9950
|
+
error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false }
|
|
9951
|
+
});
|
|
9952
|
+
}
|
|
9953
|
+
function invalidRequest(reply, statusCode) {
|
|
9954
|
+
return reply.code(statusCode).send({
|
|
9955
|
+
success: false,
|
|
9956
|
+
error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false }
|
|
9957
|
+
});
|
|
9958
|
+
}
|
|
9959
|
+
|
|
9960
|
+
// src/routes/agent-web-app-runtime.ts
|
|
9961
|
+
import { z as z4, ZodError as ZodError2 } from "zod";
|
|
9962
|
+
|
|
9963
|
+
// src/services/agent-web-app/AgentWebAppRuntimeService.ts
|
|
9964
|
+
import { createHash, randomUUID as randomUUID9 } from "crypto";
|
|
9965
|
+
import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock } from "@axiom-lattice/protocols";
|
|
9966
|
+
function isRecord(value) {
|
|
9967
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9968
|
+
}
|
|
9969
|
+
var AgentWebAppRuntimeError = class extends Error {
|
|
9970
|
+
constructor(code, statusCode, message) {
|
|
9971
|
+
super(message);
|
|
9972
|
+
this.code = code;
|
|
9973
|
+
this.statusCode = statusCode;
|
|
9974
|
+
this.name = "AgentWebAppRuntimeError";
|
|
9975
|
+
}
|
|
9976
|
+
};
|
|
9977
|
+
var AgentWebAppRuntimeService = class {
|
|
9978
|
+
constructor(deps) {
|
|
9979
|
+
this.deps = deps;
|
|
9980
|
+
}
|
|
9981
|
+
async bootstrap(webAppId, userId, selection) {
|
|
9982
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
9983
|
+
const assistant = await this.deps.assistantResolver.resolve(
|
|
9984
|
+
runtime.webApp.tenantId,
|
|
9985
|
+
runtime.webApp.assistantId
|
|
9986
|
+
);
|
|
9987
|
+
if (!assistant) this.fail("WEB_APP_DISABLED", 403, "Agent Web App is unavailable");
|
|
9988
|
+
const publicProjectIds = runtime.webApp.features.projectSelector ? runtime.webApp.scope.allowedProjectIds : [runtime.webApp.scope.defaultProjectId];
|
|
9989
|
+
const projects = await Promise.all(publicProjectIds.map(async (id) => {
|
|
9990
|
+
const project = await this.deps.projectStore.getProjectById(runtime.webApp.tenantId, id);
|
|
9991
|
+
if (!project) this.fail("PROJECT_NOT_ALLOWED", 400, "Project is not available");
|
|
9992
|
+
return { id: project.id, name: project.name };
|
|
9993
|
+
}));
|
|
9994
|
+
const defaultModelKey = runtime.webApp.scope.defaultModelKey;
|
|
9995
|
+
if (defaultModelKey && !this.deps.modelRegistry.has(defaultModelKey)) {
|
|
9996
|
+
this.fail("MODEL_NOT_ALLOWED", 400, "Default model is not available");
|
|
9997
|
+
}
|
|
9998
|
+
const configuredPublicModelKeys = runtime.webApp.features.modelSelector ? runtime.webApp.scope.allowedModelKeys ?? [] : defaultModelKey ? [defaultModelKey] : [];
|
|
9999
|
+
const publicModelKeys = [...new Set(configuredPublicModelKeys)].filter((key) => this.deps.modelRegistry.has(key));
|
|
10000
|
+
const models = publicModelKeys.map((key) => ({
|
|
10001
|
+
key,
|
|
10002
|
+
label: this.deps.modelRegistry.label(key)
|
|
10003
|
+
}));
|
|
10004
|
+
const result = {
|
|
10005
|
+
webApp: {
|
|
10006
|
+
id: runtime.webApp.id,
|
|
10007
|
+
name: runtime.webApp.name,
|
|
10008
|
+
description: runtime.webApp.description,
|
|
10009
|
+
assistant,
|
|
10010
|
+
defaultProjectId: runtime.webApp.scope.defaultProjectId,
|
|
10011
|
+
defaultModelKey: runtime.webApp.scope.defaultModelKey,
|
|
10012
|
+
features: runtime.webApp.features,
|
|
10013
|
+
appearance: runtime.webApp.appearance,
|
|
10014
|
+
identityAssurance: "unverified"
|
|
10015
|
+
},
|
|
10016
|
+
projects,
|
|
10017
|
+
models
|
|
10018
|
+
};
|
|
10019
|
+
if (!runtime.webApp.features.threadManagement) {
|
|
10020
|
+
result.thread = await this.resolveImplicitThread(runtime, userId);
|
|
10021
|
+
}
|
|
10022
|
+
return result;
|
|
10023
|
+
}
|
|
10024
|
+
async listThreads(webAppId, userId, selection) {
|
|
10025
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
10026
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
10027
|
+
const threads = await this.ownedThreads(runtime, userId);
|
|
10028
|
+
return threads.sort(compareThreads).map(toRuntimeThread);
|
|
10029
|
+
}
|
|
10030
|
+
async createThread(webAppId, userId, input) {
|
|
10031
|
+
const runtime = await this.resolveRuntime(webAppId, input);
|
|
10032
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
10033
|
+
return this.createOwnedThread(runtime, userId, input.label);
|
|
10034
|
+
}
|
|
10035
|
+
/** Update a thread label; omitted or blank labels preserve the existing label. */
|
|
10036
|
+
async updateThread(webAppId, userId, threadId, input) {
|
|
10037
|
+
const runtime = await this.resolveRuntime(webAppId, input);
|
|
10038
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
10039
|
+
const thread = await this.requireOwnedThread(runtime, userId, threadId);
|
|
10040
|
+
const label = normalizeLabel(input.label);
|
|
10041
|
+
const updated = await this.deps.threadStore.updateThread(runtime.webApp.tenantId, thread.id, {
|
|
10042
|
+
metadata: this.metadata(runtime, userId, label)
|
|
10043
|
+
});
|
|
10044
|
+
if (!updated) this.threadNotFound();
|
|
10045
|
+
return toRuntimeThread(updated);
|
|
10046
|
+
}
|
|
10047
|
+
async deleteThread(webAppId, userId, threadId, selection) {
|
|
10048
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
10049
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
10050
|
+
await this.requireOwnedThread(runtime, userId, threadId);
|
|
10051
|
+
if (!await this.deps.threadStore.deleteThread(runtime.webApp.tenantId, threadId)) this.threadNotFound();
|
|
10052
|
+
}
|
|
10053
|
+
async getMessages(webAppId, userId, threadId, selection) {
|
|
10054
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
10055
|
+
await this.requireOwnedThread(runtime, userId, threadId);
|
|
10056
|
+
const messages = await this.deps.messageReader.getCurrentMessages({
|
|
10057
|
+
tenantId: runtime.webApp.tenantId,
|
|
10058
|
+
assistantId: runtime.webApp.assistantId,
|
|
10059
|
+
threadId,
|
|
10060
|
+
workspaceId: runtime.project.workspaceId,
|
|
10061
|
+
projectId: runtime.project.id
|
|
10062
|
+
});
|
|
10063
|
+
return messages.flatMap((message) => {
|
|
10064
|
+
if (!isRecord(message) || typeof message.id !== "string" || message.role !== "human" && message.role !== "ai") return [];
|
|
10065
|
+
const content = typeof message.content === "string" ? message.content : runtime.webApp.features.genUI && Array.isArray(message.content) ? message.content.map(parseAgentWebAppGenUIBlock).filter((block) => block !== void 0) : void 0;
|
|
10066
|
+
return [{ id: message.id, role: message.role, ...content && (typeof content === "string" || content.length > 0) ? { content } : {} }];
|
|
10067
|
+
});
|
|
10068
|
+
}
|
|
10069
|
+
async streamMessage(webAppId, userId, threadId, input) {
|
|
10070
|
+
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, input);
|
|
10071
|
+
const refs = this.resolveAttachmentRefs(runtime, userId, threadId, input.attachmentRefs ?? []);
|
|
10072
|
+
const agent = this.resolveAgent(runtime, threadId);
|
|
10073
|
+
const content = refs.length ? `${input.content}
|
|
10074
|
+
|
|
10075
|
+
${formatFileRefsSection(refs)}` : input.content;
|
|
10076
|
+
const execution = await this.execute(agent, runtime, { input: { message: content } });
|
|
10077
|
+
return { ...execution, allowInterrupts: runtime.webApp.features.hitl, allowGenUI: runtime.webApp.features.genUI };
|
|
10078
|
+
}
|
|
10079
|
+
async abort(webAppId, userId, threadId, selection) {
|
|
10080
|
+
const { agent } = await this.resolveOwnedAgent(webAppId, userId, threadId, selection);
|
|
10081
|
+
await agent.abort();
|
|
10082
|
+
}
|
|
10083
|
+
async uploadAttachment(webAppId, userId, threadId, selection, file) {
|
|
10084
|
+
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
|
|
10085
|
+
this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
|
|
10086
|
+
if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
|
|
10087
|
+
const userDigest = createHash("sha256").update(userId, "utf8").digest("hex");
|
|
10088
|
+
const contentDigest = createHash("sha256").update(file.bytes).digest("hex");
|
|
10089
|
+
const displayName = file.name;
|
|
10090
|
+
const physicalName = `${randomUUID9()}-${sanitizePhysicalFilename(file.name)}`;
|
|
10091
|
+
const saved = await this.deps.saveProjectFile({
|
|
10092
|
+
tenantId: runtime.webApp.tenantId,
|
|
10093
|
+
workspaceId: runtime.project.workspaceId,
|
|
10094
|
+
projectId: runtime.project.id,
|
|
10095
|
+
assistantId: runtime.webApp.assistantId,
|
|
10096
|
+
name: physicalName,
|
|
10097
|
+
mimeType: file.mimeType,
|
|
10098
|
+
bytes: file.bytes,
|
|
10099
|
+
path: `/project/web-apps/${runtime.webApp.id}/${userDigest}/${threadId}`
|
|
10100
|
+
});
|
|
10101
|
+
const ref = this.deps.attachmentRefs.sign({
|
|
10102
|
+
webAppId: runtime.webApp.id,
|
|
10103
|
+
userId,
|
|
10104
|
+
threadId,
|
|
10105
|
+
projectId: runtime.project.id,
|
|
10106
|
+
uri: saved.uri,
|
|
10107
|
+
name: displayName,
|
|
10108
|
+
mimeType: file.mimeType,
|
|
10109
|
+
size: saved.size,
|
|
10110
|
+
contentDigest
|
|
10111
|
+
});
|
|
10112
|
+
return { ref, name: displayName, mimeType: file.mimeType, size: saved.size };
|
|
10113
|
+
}
|
|
10114
|
+
async resumeInterrupt(webAppId, userId, threadId, interruptId, selection, input) {
|
|
10115
|
+
const { runtime, agent } = await this.resolveOwnedAgent(webAppId, userId, threadId, selection);
|
|
10116
|
+
this.requireNamedFeature(runtime.webApp.features.hitl, "Human review is disabled");
|
|
10117
|
+
const state = await agent.getCurrentState();
|
|
10118
|
+
if (!hasInterrupt(state, interruptId)) this.fail("INVALID_REQUEST", 400, "Interrupt not found");
|
|
10119
|
+
const execution = await this.execute(agent, runtime, {
|
|
10120
|
+
input: { message: input.message },
|
|
10121
|
+
command: { resume: input.response }
|
|
10122
|
+
});
|
|
10123
|
+
return { ...execution, allowInterrupts: true, allowGenUI: runtime.webApp.features.genUI };
|
|
10124
|
+
}
|
|
10125
|
+
execute(agent, runtime, request) {
|
|
10126
|
+
return executeScopedAgentMessage(agent, {
|
|
10127
|
+
...request,
|
|
10128
|
+
...runtime.modelKey ? { custom_run_config: { modelConfig: { modelKey: runtime.modelKey } } } : {}
|
|
10129
|
+
}, [MessageChunkTypes3.MESSAGE_COMPLETED, MessageChunkTypes3.INTERRUPT, MessageChunkTypes3.MESSAGE_FAILED]);
|
|
10130
|
+
}
|
|
10131
|
+
async resolveRuntime(webAppId, selection) {
|
|
10132
|
+
const webApp = await this.deps.webAppStore.findById(webAppId);
|
|
10133
|
+
if (!webApp) this.fail("WEB_APP_NOT_FOUND", 404, "Agent Web App not found");
|
|
10134
|
+
const previewAllowed = webApp.status === "draft" && selection.preview?.tenantId === webApp.tenantId && await this.deps.authorizePreview?.(webApp, selection.preview) === true;
|
|
10135
|
+
if (webApp.status !== "active" && !previewAllowed) this.fail("WEB_APP_DISABLED", 403, "Agent Web App is disabled");
|
|
10136
|
+
if (!webApp.features.projectSelector && selection.projectId !== void 0) {
|
|
10137
|
+
this.fail("PROJECT_SELECTOR_DISABLED", 400, "Project selection is disabled");
|
|
10138
|
+
}
|
|
10139
|
+
const projectId = selection.projectId ?? webApp.scope.defaultProjectId;
|
|
10140
|
+
if (!webApp.scope.allowedProjectIds.includes(projectId)) {
|
|
10141
|
+
this.fail("PROJECT_NOT_ALLOWED", 400, "Project is not allowed");
|
|
10142
|
+
}
|
|
10143
|
+
const project = await this.deps.projectStore.getProjectById(webApp.tenantId, projectId);
|
|
10144
|
+
if (!project) this.fail("PROJECT_NOT_ALLOWED", 400, "Project is not available");
|
|
10145
|
+
if (!webApp.features.modelSelector && selection.modelKey !== void 0) {
|
|
10146
|
+
this.fail("MODEL_NOT_ALLOWED", 400, "Model selection is disabled");
|
|
10147
|
+
}
|
|
10148
|
+
const modelKey2 = selection.modelKey ?? webApp.scope.defaultModelKey;
|
|
10149
|
+
const allowedModels = webApp.scope.allowedModelKeys;
|
|
10150
|
+
if (modelKey2 && (!allowedModels?.includes(modelKey2) || !this.deps.modelRegistry.has(modelKey2))) {
|
|
10151
|
+
this.fail("MODEL_NOT_ALLOWED", 400, "Model is not allowed");
|
|
10152
|
+
}
|
|
10153
|
+
return { webApp, project, modelKey: modelKey2 };
|
|
10154
|
+
}
|
|
10155
|
+
async resolveOwnedAgent(webAppId, userId, threadId, selection) {
|
|
10156
|
+
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
|
|
10157
|
+
return { runtime, agent: this.resolveAgent(runtime, threadId) };
|
|
10158
|
+
}
|
|
10159
|
+
async resolveOwnedRuntime(webAppId, userId, threadId, selection) {
|
|
10160
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
10161
|
+
await this.requireOwnedThread(runtime, userId, threadId);
|
|
10162
|
+
return runtime;
|
|
10163
|
+
}
|
|
10164
|
+
resolveAgent(runtime, threadId) {
|
|
10165
|
+
if (!this.deps.agentResolver) this.fail("INTERNAL_ERROR", 500, "Agent runtime is unavailable");
|
|
10166
|
+
return this.deps.agentResolver.getAgent({
|
|
10167
|
+
tenantId: runtime.webApp.tenantId,
|
|
10168
|
+
assistantId: runtime.webApp.assistantId,
|
|
10169
|
+
threadId,
|
|
10170
|
+
workspaceId: runtime.project.workspaceId,
|
|
10171
|
+
projectId: runtime.project.id,
|
|
10172
|
+
...runtime.modelKey ? { modelKey: runtime.modelKey } : {}
|
|
10173
|
+
});
|
|
10174
|
+
}
|
|
10175
|
+
resolveAttachmentRefs(runtime, userId, threadId, refs) {
|
|
10176
|
+
if (refs.length === 0) return [];
|
|
10177
|
+
this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
|
|
10178
|
+
if (!this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
|
|
10179
|
+
try {
|
|
10180
|
+
return refs.map((ref) => ({
|
|
10181
|
+
...this.deps.attachmentRefs.verify(ref, {
|
|
10182
|
+
webAppId: runtime.webApp.id,
|
|
10183
|
+
userId,
|
|
10184
|
+
threadId,
|
|
10185
|
+
projectId: runtime.project.id
|
|
10186
|
+
}),
|
|
10187
|
+
addedBy: "user"
|
|
10188
|
+
}));
|
|
10189
|
+
} catch {
|
|
10190
|
+
return this.fail("INVALID_REQUEST", 400, "Invalid attachment reference");
|
|
10191
|
+
}
|
|
10192
|
+
}
|
|
10193
|
+
async ownedThreads(runtime, userId) {
|
|
10194
|
+
return this.deps.threadStore.getThreadsByAssistantId(
|
|
10195
|
+
runtime.webApp.tenantId,
|
|
10196
|
+
runtime.webApp.assistantId,
|
|
10197
|
+
{
|
|
10198
|
+
source: "web_app",
|
|
10199
|
+
webAppId: runtime.webApp.id,
|
|
10200
|
+
userId,
|
|
10201
|
+
projectId: runtime.project.id
|
|
10202
|
+
}
|
|
10203
|
+
);
|
|
10204
|
+
}
|
|
10205
|
+
async requireOwnedThread(runtime, userId, threadId) {
|
|
10206
|
+
const thread = await this.deps.threadStore.getThreadById(runtime.webApp.tenantId, threadId);
|
|
10207
|
+
const metadata = thread?.metadata;
|
|
10208
|
+
if (!thread || thread.assistantId !== runtime.webApp.assistantId || metadata?.source !== "web_app" || metadata.webAppId !== runtime.webApp.id || metadata.userId !== userId || metadata.projectId !== runtime.project.id) this.threadNotFound();
|
|
10209
|
+
return thread;
|
|
10210
|
+
}
|
|
10211
|
+
async resolveImplicitThread(runtime, userId) {
|
|
10212
|
+
const latest = (await this.ownedThreads(runtime, userId)).sort(compareThreads)[0];
|
|
10213
|
+
if (latest) return toRuntimeThread(latest);
|
|
10214
|
+
const threadId = implicitThreadId(runtime, userId);
|
|
10215
|
+
const existing = await this.deps.threadStore.getThreadById(
|
|
10216
|
+
runtime.webApp.tenantId,
|
|
10217
|
+
threadId
|
|
10218
|
+
);
|
|
10219
|
+
if (existing) return toRuntimeThread(this.requireImplicitOwnership(runtime, userId, existing));
|
|
10220
|
+
await this.deps.threadStore.createThread(
|
|
10221
|
+
runtime.webApp.tenantId,
|
|
10222
|
+
runtime.webApp.assistantId,
|
|
10223
|
+
threadId,
|
|
10224
|
+
{ metadata: this.metadata(runtime, userId) }
|
|
10225
|
+
);
|
|
10226
|
+
const persisted = await this.deps.threadStore.getThreadById(
|
|
10227
|
+
runtime.webApp.tenantId,
|
|
10228
|
+
threadId
|
|
10229
|
+
);
|
|
10230
|
+
if (!persisted) this.fail("INTERNAL_ERROR", 500, "Implicit thread could not be resolved");
|
|
10231
|
+
return toRuntimeThread(this.requireImplicitOwnership(runtime, userId, persisted));
|
|
10232
|
+
}
|
|
10233
|
+
requireImplicitOwnership(runtime, userId, thread) {
|
|
10234
|
+
const metadata = thread.metadata;
|
|
10235
|
+
if (thread.assistantId !== runtime.webApp.assistantId || metadata?.source !== "web_app" || metadata.webAppId !== runtime.webApp.id || metadata.userId !== userId || metadata.projectId !== runtime.project.id) this.fail("INTERNAL_ERROR", 500, "Implicit thread identity conflict");
|
|
10236
|
+
return thread;
|
|
10237
|
+
}
|
|
10238
|
+
async createOwnedThread(runtime, userId, rawLabel) {
|
|
10239
|
+
const thread = await this.deps.threadStore.createThread(
|
|
10240
|
+
runtime.webApp.tenantId,
|
|
10241
|
+
runtime.webApp.assistantId,
|
|
10242
|
+
randomUUID9(),
|
|
10243
|
+
{ metadata: this.metadata(runtime, userId, normalizeLabel(rawLabel)) }
|
|
10244
|
+
);
|
|
10245
|
+
return toRuntimeThread(thread);
|
|
10246
|
+
}
|
|
10247
|
+
metadata(runtime, userId, label) {
|
|
10248
|
+
return {
|
|
10249
|
+
source: "web_app",
|
|
10250
|
+
webAppId: runtime.webApp.id,
|
|
10251
|
+
userId,
|
|
10252
|
+
projectId: runtime.project.id,
|
|
10253
|
+
...label ? { label } : {}
|
|
10254
|
+
};
|
|
10255
|
+
}
|
|
10256
|
+
requireFeature(enabled) {
|
|
10257
|
+
if (!enabled) this.fail("FEATURE_DISABLED", 403, "Thread management is disabled");
|
|
10258
|
+
}
|
|
10259
|
+
requireNamedFeature(enabled, message) {
|
|
10260
|
+
if (!enabled) this.fail("FEATURE_DISABLED", 403, message);
|
|
10261
|
+
}
|
|
10262
|
+
threadNotFound() {
|
|
10263
|
+
return this.fail("THREAD_NOT_FOUND", 404, "Thread not found");
|
|
10264
|
+
}
|
|
10265
|
+
fail(code, statusCode, message) {
|
|
10266
|
+
throw new AgentWebAppRuntimeError(code, statusCode, message);
|
|
10267
|
+
}
|
|
10268
|
+
};
|
|
10269
|
+
function hasInterrupt(state, interruptId) {
|
|
10270
|
+
if (typeof state !== "object" || state === null || !("tasks" in state) || !Array.isArray(state.tasks)) return false;
|
|
10271
|
+
return state.tasks.some((task) => {
|
|
10272
|
+
if (typeof task !== "object" || task === null || !("interrupts" in task) || !Array.isArray(task.interrupts)) return false;
|
|
10273
|
+
return task.interrupts.some((interrupt) => typeof interrupt === "object" && interrupt !== null && "id" in interrupt && interrupt.id === interruptId);
|
|
10274
|
+
});
|
|
10275
|
+
}
|
|
10276
|
+
function sanitizePhysicalFilename(name) {
|
|
10277
|
+
const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^\.+/, "");
|
|
10278
|
+
return sanitized || "file";
|
|
10279
|
+
}
|
|
10280
|
+
function normalizeLabel(label) {
|
|
10281
|
+
const normalized = label?.trim();
|
|
10282
|
+
return normalized || void 0;
|
|
10283
|
+
}
|
|
10284
|
+
function implicitThreadId(runtime, userId) {
|
|
10285
|
+
const canonicalTuple = JSON.stringify([
|
|
10286
|
+
"axiom.agent-web-app.implicit-thread",
|
|
10287
|
+
1,
|
|
10288
|
+
runtime.webApp.tenantId,
|
|
10289
|
+
runtime.webApp.assistantId,
|
|
10290
|
+
runtime.webApp.id,
|
|
10291
|
+
userId,
|
|
10292
|
+
runtime.project.id
|
|
10293
|
+
]);
|
|
10294
|
+
const bytes = createHash("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
|
|
10295
|
+
bytes[6] = bytes[6] & 15 | 128;
|
|
10296
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
10297
|
+
const hex = bytes.toString("hex");
|
|
10298
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
10299
|
+
}
|
|
10300
|
+
function compareThreads(left, right) {
|
|
10301
|
+
return right.updatedAt.getTime() - left.updatedAt.getTime() || right.id.localeCompare(left.id);
|
|
10302
|
+
}
|
|
10303
|
+
function toRuntimeThread(thread) {
|
|
10304
|
+
return {
|
|
10305
|
+
id: thread.id,
|
|
10306
|
+
projectId: String(thread.metadata?.projectId),
|
|
10307
|
+
...typeof thread.metadata?.label === "string" ? { label: thread.metadata.label } : {},
|
|
10308
|
+
createdAt: thread.createdAt,
|
|
10309
|
+
updatedAt: thread.updatedAt
|
|
10310
|
+
};
|
|
10311
|
+
}
|
|
10312
|
+
|
|
10313
|
+
// src/services/agent-web-app/AgentWebAppStreamProjector.ts
|
|
10314
|
+
import {
|
|
10315
|
+
MessageChunkTypes as MessageChunkTypes4,
|
|
10316
|
+
parseAgentWebAppGenUIBlock as parseAgentWebAppGenUIBlock2
|
|
10317
|
+
} from "@axiom-lattice/protocols";
|
|
10318
|
+
function createAgentWebAppStreamProjectionContext(options = {}) {
|
|
10319
|
+
return { startedToolIds: /* @__PURE__ */ new Set(), allowInterrupts: options.allowInterrupts ?? true, allowGenUI: options.allowGenUI ?? false };
|
|
10320
|
+
}
|
|
10321
|
+
function projectAgentWebAppChunk(chunk, context) {
|
|
10322
|
+
if (chunk.type === MessageChunkTypes4.AI) {
|
|
10323
|
+
const events = [];
|
|
10324
|
+
const startedToolIds = context?.startedToolIds ?? /* @__PURE__ */ new Set();
|
|
10325
|
+
if (typeof chunk.data.content === "string" && chunk.data.content) events.push({ type: "message.delta", text: chunk.data.content });
|
|
10326
|
+
if (context?.allowGenUI && Array.isArray(chunk.data.content)) {
|
|
10327
|
+
for (const value of chunk.data.content) {
|
|
10328
|
+
const block = parseAgentWebAppGenUIBlock2(value);
|
|
10329
|
+
if (block) events.push({ type: "genui.render", block });
|
|
10330
|
+
}
|
|
10331
|
+
}
|
|
10332
|
+
for (const call of chunk.data.tool_call_chunks ?? []) {
|
|
10333
|
+
if (call.id && call.name && !startedToolIds.has(call.id)) {
|
|
10334
|
+
startedToolIds.add(call.id);
|
|
10335
|
+
events.push({ type: "tool.started", id: call.id, name: call.name });
|
|
10336
|
+
}
|
|
10337
|
+
}
|
|
10338
|
+
for (const call of chunk.data.tool_calls ?? []) {
|
|
10339
|
+
if (!startedToolIds.has(call.id)) {
|
|
10340
|
+
startedToolIds.add(call.id);
|
|
10341
|
+
events.push({ type: "tool.started", id: call.id, name: call.name });
|
|
10342
|
+
}
|
|
10343
|
+
}
|
|
10344
|
+
return events;
|
|
10345
|
+
}
|
|
10346
|
+
if (chunk.type === MessageChunkTypes4.TOOL && chunk.data.tool_call_id) {
|
|
10347
|
+
return [{ type: "tool.completed", id: chunk.data.tool_call_id }];
|
|
10348
|
+
}
|
|
10349
|
+
if (chunk.type === MessageChunkTypes4.INTERRUPT) {
|
|
10350
|
+
if (context && !context.allowInterrupts) return [{ type: "stream.completed" }];
|
|
10351
|
+
const interrupt = publicInterrupt(chunk);
|
|
10352
|
+
return interrupt ? [{ type: "interrupt.created", interrupt }, { type: "stream.completed" }] : [{ type: "stream.completed" }];
|
|
10353
|
+
}
|
|
10354
|
+
if (chunk.type === MessageChunkTypes4.MESSAGE_COMPLETED) {
|
|
10355
|
+
return [
|
|
10356
|
+
{ type: "message.completed", messageId: chunk.data.id },
|
|
10357
|
+
{ type: "stream.completed" }
|
|
10358
|
+
];
|
|
10359
|
+
}
|
|
10360
|
+
if (chunk.type === MessageChunkTypes4.MESSAGE_FAILED) {
|
|
10361
|
+
return [
|
|
10362
|
+
{ type: "error", error: { code: "STREAM_FAILED", message: "Stream failed", retryable: true } },
|
|
10363
|
+
{ type: "stream.completed" }
|
|
10364
|
+
];
|
|
10365
|
+
}
|
|
10366
|
+
return [];
|
|
10367
|
+
}
|
|
10368
|
+
function publicInterrupt(chunk) {
|
|
10369
|
+
const value = parseInterruptContent(chunk.data.content);
|
|
10370
|
+
if (!value) return void 0;
|
|
10371
|
+
const topLevelId = chunk.id;
|
|
10372
|
+
const id = typeof topLevelId === "string" ? topLevelId : chunk.data.id;
|
|
10373
|
+
if (!id) return void 0;
|
|
10374
|
+
return {
|
|
10375
|
+
id,
|
|
10376
|
+
type: typeof value.type === "string" ? value.type : "input",
|
|
10377
|
+
prompt: typeof value.prompt === "string" ? value.prompt : typeof value.message === "string" ? value.message : "Input required",
|
|
10378
|
+
...isRecord2(value.data) ? { data: value.data } : {}
|
|
10379
|
+
};
|
|
10380
|
+
}
|
|
10381
|
+
function parseInterruptContent(content) {
|
|
10382
|
+
if (!content) return {};
|
|
10383
|
+
if (isRecord2(content)) return content;
|
|
10384
|
+
if (typeof content !== "string") return void 0;
|
|
10385
|
+
try {
|
|
10386
|
+
const parsed = JSON.parse(content);
|
|
10387
|
+
return isRecord2(parsed) ? parsed : { prompt: content };
|
|
10388
|
+
} catch {
|
|
10389
|
+
return { prompt: content };
|
|
10390
|
+
}
|
|
10391
|
+
}
|
|
10392
|
+
function isRecord2(value) {
|
|
10393
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10394
|
+
}
|
|
10395
|
+
|
|
10396
|
+
// src/routes/agent-web-app-runtime.ts
|
|
10397
|
+
var idSchema = z4.string().min(1).max(128);
|
|
10398
|
+
var paramsSchema = z4.object({ webAppId: idSchema }).strict();
|
|
10399
|
+
var threadParamsSchema = z4.object({ webAppId: idSchema, threadId: idSchema }).strict();
|
|
10400
|
+
var interruptParamsSchema = z4.object({ webAppId: idSchema, threadId: idSchema, interruptId: idSchema }).strict();
|
|
10401
|
+
var bootstrapQuerySchema = z4.object({ projectId: idSchema.optional(), modelKey: idSchema.optional() }).strict();
|
|
10402
|
+
var projectQuerySchema = z4.object({ projectId: idSchema.optional() }).strict();
|
|
10403
|
+
var threadBodySchema = z4.object({ projectId: idSchema.optional(), label: z4.string().max(128).optional() }).strict();
|
|
10404
|
+
var streamBodySchema = z4.object({
|
|
10405
|
+
content: z4.string().trim().min(1).max(32768),
|
|
10406
|
+
projectId: idSchema.optional(),
|
|
10407
|
+
modelKey: idSchema.optional(),
|
|
10408
|
+
attachmentRefs: z4.array(z4.string().min(1).max(8192)).max(10).optional()
|
|
10409
|
+
}).strict();
|
|
10410
|
+
var resumeBodySchema = z4.object({
|
|
10411
|
+
projectId: idSchema.optional(),
|
|
10412
|
+
modelKey: idSchema.optional(),
|
|
10413
|
+
message: z4.string().trim().min(1).max(4096),
|
|
10414
|
+
response: z4.union([
|
|
10415
|
+
z4.boolean(),
|
|
10416
|
+
z4.string().max(4096),
|
|
10417
|
+
z4.object({ approved: z4.boolean(), feedback: z4.string().max(4096).optional() }).strict()
|
|
10418
|
+
])
|
|
10419
|
+
}).strict();
|
|
10420
|
+
var MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
10421
|
+
function createExternalHeaderIdentityResolver() {
|
|
10422
|
+
return { resolve(request) {
|
|
10423
|
+
const raw = request.headers["x-axiom-external-user-id"];
|
|
10424
|
+
if (raw === void 0) throw new AgentWebAppRuntimeError("USER_ID_REQUIRED", 401, "External user id is required");
|
|
10425
|
+
if (Array.isArray(raw) || typeof raw !== "string") throw new AgentWebAppRuntimeError("INVALID_USER_ID", 400, "External user id is invalid");
|
|
10426
|
+
const userId = raw.trim();
|
|
10427
|
+
if (!/^[A-Za-z0-9._:@-]+$/.test(userId) || userId.length > 128) throw new AgentWebAppRuntimeError("INVALID_USER_ID", 400, "External user id is invalid");
|
|
10428
|
+
return { userId, assurance: "unverified" };
|
|
10429
|
+
} };
|
|
10430
|
+
}
|
|
10431
|
+
function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
10432
|
+
const identities = deps.identityResolver ?? createExternalHeaderIdentityResolver();
|
|
10433
|
+
app2.register(async (routes) => {
|
|
10434
|
+
routes.setErrorHandler((error, _request, reply) => normalizeError2(reply, error));
|
|
10435
|
+
routes.get("/api/web-apps/:webAppId/runtime/bootstrap", async (request, reply) => handle2(reply, async () => {
|
|
10436
|
+
const { webAppId } = paramsSchema.parse(request.params);
|
|
10437
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10438
|
+
return { data: await deps.service.bootstrap(webAppId, identity.userId, withPreview(bootstrapQuerySchema.parse(request.query), identity.preview)) };
|
|
10439
|
+
}));
|
|
10440
|
+
routes.get("/api/web-apps/:webAppId/runtime/threads", async (request, reply) => handle2(reply, async () => {
|
|
10441
|
+
const { webAppId } = paramsSchema.parse(request.params);
|
|
10442
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10443
|
+
const records = await deps.service.listThreads(webAppId, identity.userId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
10444
|
+
return { data: { records, total: records.length } };
|
|
10445
|
+
}));
|
|
10446
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads", async (request, reply) => handle2(reply, async () => {
|
|
10447
|
+
const { webAppId } = paramsSchema.parse(request.params);
|
|
10448
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10449
|
+
return { statusCode: 201, data: await deps.service.createThread(webAppId, identity.userId, withPreview(threadBodySchema.parse(request.body), identity.preview)) };
|
|
10450
|
+
}));
|
|
10451
|
+
routes.patch("/api/web-apps/:webAppId/runtime/threads/:threadId", async (request, reply) => handle2(reply, async () => {
|
|
10452
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
10453
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10454
|
+
return { data: await deps.service.updateThread(webAppId, identity.userId, threadId, withPreview(threadBodySchema.parse(request.body), identity.preview)) };
|
|
10455
|
+
}));
|
|
10456
|
+
routes.delete("/api/web-apps/:webAppId/runtime/threads/:threadId", async (request, reply) => handle2(reply, async () => {
|
|
10457
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
10458
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10459
|
+
await deps.service.deleteThread(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
10460
|
+
return { data: { id: threadId } };
|
|
10461
|
+
}));
|
|
10462
|
+
routes.get("/api/web-apps/:webAppId/runtime/threads/:threadId/messages", async (request, reply) => handle2(reply, async () => {
|
|
10463
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
10464
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10465
|
+
const messages = await deps.service.getMessages(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
10466
|
+
return { data: { messages } };
|
|
10467
|
+
}));
|
|
10468
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/messages/stream", async (request, reply) => {
|
|
10469
|
+
try {
|
|
10470
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
10471
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10472
|
+
const input = streamBodySchema.parse(request.body);
|
|
10473
|
+
const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
|
|
10474
|
+
beginSse(reply);
|
|
10475
|
+
await pipeAgentWebAppStream(reply.raw, result.stream, result);
|
|
10476
|
+
} catch (error) {
|
|
10477
|
+
return normalizeError2(reply, error);
|
|
10478
|
+
}
|
|
10479
|
+
});
|
|
10480
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/abort", async (request, reply) => handle2(reply, async () => {
|
|
10481
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
10482
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10483
|
+
await deps.service.abort(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
10484
|
+
return { data: { aborted: true } };
|
|
10485
|
+
}));
|
|
10486
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/attachments", async (request, reply) => handle2(reply, async () => {
|
|
10487
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
10488
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10489
|
+
if (!request.isMultipart()) throw new ZodError2([]);
|
|
10490
|
+
const part = await request.file({ limits: { files: 1, fileSize: MAX_ATTACHMENT_BYTES } });
|
|
10491
|
+
if (!part || !part.filename || part.filename.length > 255) throw new ZodError2([]);
|
|
10492
|
+
const bytes = await part.toBuffer();
|
|
10493
|
+
if (bytes.length === 0 || part.file.truncated) throw new ZodError2([]);
|
|
10494
|
+
const data = await deps.service.uploadAttachment(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview), {
|
|
10495
|
+
name: part.filename,
|
|
10496
|
+
mimeType: part.mimetype || "application/octet-stream",
|
|
10497
|
+
bytes
|
|
10498
|
+
});
|
|
10499
|
+
return { statusCode: 201, data };
|
|
10500
|
+
}));
|
|
10501
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/interrupts/:interruptId/resume", async (request, reply) => {
|
|
10502
|
+
try {
|
|
10503
|
+
const { webAppId, threadId, interruptId } = interruptParamsSchema.parse(request.params);
|
|
10504
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
10505
|
+
const { projectId, modelKey: modelKey2, message, response } = resumeBodySchema.parse(request.body);
|
|
10506
|
+
const result = await deps.service.resumeInterrupt(webAppId, identity.userId, threadId, interruptId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview), { message, response });
|
|
10507
|
+
beginSse(reply);
|
|
10508
|
+
await pipeAgentWebAppStream(reply.raw, result.stream, result);
|
|
10509
|
+
} catch (error) {
|
|
10510
|
+
return normalizeError2(reply, error);
|
|
10511
|
+
}
|
|
10512
|
+
});
|
|
10513
|
+
});
|
|
10514
|
+
}
|
|
10515
|
+
function resolveRuntimeIdentity(request, webAppId, identities) {
|
|
10516
|
+
if (request.headers["x-axiom-web-app-preview"] !== "true") return identities.resolve(request);
|
|
10517
|
+
const user = request.user;
|
|
10518
|
+
if (!user?.id || !user.tenantId) throw new AgentWebAppRuntimeError("USER_ID_REQUIRED", 401, "Authenticated Console preview is required");
|
|
10519
|
+
return { userId: `console-preview:${user.id}:${webAppId}`, preview: { tenantId: user.tenantId, userId: user.id } };
|
|
10520
|
+
}
|
|
10521
|
+
function withPreview(selection, preview) {
|
|
10522
|
+
return { ...selection, ...preview ? { preview } : {} };
|
|
10523
|
+
}
|
|
10524
|
+
async function pipeAgentWebAppStream(raw, stream, options = {}) {
|
|
10525
|
+
const projection = createAgentWebAppStreamProjectionContext(options);
|
|
10526
|
+
await pipeAsyncIterableToSse(raw, stream, {
|
|
10527
|
+
project: (chunk) => projectAgentWebAppChunk(chunk, projection),
|
|
10528
|
+
onError: () => [
|
|
10529
|
+
{ type: "error", error: { code: "STREAM_FAILED", message: "Stream failed", retryable: true } },
|
|
10530
|
+
{ type: "stream.completed" }
|
|
10531
|
+
]
|
|
10532
|
+
});
|
|
10533
|
+
}
|
|
10534
|
+
function beginSse(reply) {
|
|
10535
|
+
reply.hijack();
|
|
10536
|
+
reply.raw.writeHead(200, {
|
|
10537
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
10538
|
+
"Cache-Control": "no-cache, no-transform",
|
|
10539
|
+
Connection: "keep-alive",
|
|
10540
|
+
"X-Accel-Buffering": "no"
|
|
10541
|
+
});
|
|
10542
|
+
}
|
|
10543
|
+
async function handle2(reply, operation) {
|
|
10544
|
+
try {
|
|
10545
|
+
const result = await operation();
|
|
10546
|
+
return reply.code(result.statusCode ?? 200).send({ success: true, data: result.data });
|
|
10547
|
+
} catch (error) {
|
|
10548
|
+
return normalizeError2(reply, error);
|
|
10549
|
+
}
|
|
10550
|
+
}
|
|
10551
|
+
function normalizeError2(reply, error) {
|
|
10552
|
+
if (error instanceof AgentWebAppRuntimeError) return reply.code(error.statusCode).send({ success: false, error: { code: error.code, message: error.message, retryable: false } });
|
|
10553
|
+
if (error instanceof ZodError2) return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
10554
|
+
if (isMultipartRequestError(error)) return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
10555
|
+
return reply.code(500).send({ success: false, error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false } });
|
|
10556
|
+
}
|
|
10557
|
+
function isMultipartRequestError(error) {
|
|
10558
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && error.code.startsWith("FST_");
|
|
10559
|
+
}
|
|
10560
|
+
|
|
10561
|
+
// src/services/agent-web-app/AgentWebAppAttachmentRef.ts
|
|
10562
|
+
import { createCipheriv, createDecipheriv, createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
|
|
10563
|
+
var TTL_MS = 15 * 6e4;
|
|
10564
|
+
function resolveAgentWebAppAttachmentSecret(consoleSecret) {
|
|
10565
|
+
return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
|
|
10566
|
+
}
|
|
10567
|
+
var AgentWebAppAttachmentRefService = class {
|
|
10568
|
+
constructor(secret, now = Date.now) {
|
|
10569
|
+
this.now = now;
|
|
10570
|
+
if (!secret) throw new Error("Attachment signing secret is required");
|
|
10571
|
+
this.key = createHash2("sha256").update(secret, "utf8").digest();
|
|
10572
|
+
}
|
|
10573
|
+
sign(input) {
|
|
10574
|
+
const payload = {
|
|
10575
|
+
webAppId: input.webAppId,
|
|
10576
|
+
userDigest: digestUser(input.userId),
|
|
10577
|
+
threadId: input.threadId,
|
|
10578
|
+
projectId: input.projectId,
|
|
10579
|
+
uri: input.uri,
|
|
10580
|
+
name: input.name,
|
|
10581
|
+
mimeType: input.mimeType,
|
|
10582
|
+
size: input.size,
|
|
10583
|
+
contentDigest: input.contentDigest,
|
|
10584
|
+
exp: this.now() + TTL_MS
|
|
10585
|
+
};
|
|
10586
|
+
const iv = randomBytes2(12);
|
|
10587
|
+
const cipher = createCipheriv("aes-256-gcm", this.key, iv);
|
|
10588
|
+
const ciphertext = Buffer.concat([
|
|
10589
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
10590
|
+
cipher.final()
|
|
10591
|
+
]);
|
|
10592
|
+
const tag = cipher.getAuthTag();
|
|
10593
|
+
return `v1.${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
10594
|
+
}
|
|
10595
|
+
verify(ref, scope) {
|
|
10596
|
+
const [version, encodedIv, encodedCiphertext, encodedTag, extra] = ref.split(".");
|
|
10597
|
+
if (version !== "v1" || !encodedIv || !encodedCiphertext || !encodedTag || extra !== void 0 || !/^[A-Za-z0-9_-]{16}$/.test(encodedIv) || !/^[A-Za-z0-9_-]+$/.test(encodedCiphertext) || !/^[A-Za-z0-9_-]{22}$/.test(encodedTag)) this.invalid();
|
|
10598
|
+
try {
|
|
10599
|
+
const iv = decodeCanonical(encodedIv, 12);
|
|
10600
|
+
const ciphertext = decodeCanonical(encodedCiphertext);
|
|
10601
|
+
const tag = decodeCanonical(encodedTag, 16);
|
|
10602
|
+
const decipher = createDecipheriv("aes-256-gcm", this.key, iv);
|
|
10603
|
+
decipher.setAuthTag(tag);
|
|
10604
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
10605
|
+
const value = JSON.parse(plaintext.toString("utf8"));
|
|
10606
|
+
if (!isAttachmentPayload(value)) this.invalid();
|
|
10607
|
+
if (value.exp <= this.now()) throw new Error("Attachment reference expired");
|
|
10608
|
+
if (value.webAppId !== scope.webAppId || value.userDigest !== digestUser(scope.userId) || value.threadId !== scope.threadId || value.projectId !== scope.projectId) this.invalid();
|
|
10609
|
+
return {
|
|
10610
|
+
uri: value.uri,
|
|
10611
|
+
name: value.name,
|
|
10612
|
+
mimeType: value.mimeType,
|
|
10613
|
+
size: value.size,
|
|
10614
|
+
contentDigest: value.contentDigest
|
|
10615
|
+
};
|
|
10616
|
+
} catch (error) {
|
|
10617
|
+
if (error instanceof Error && error.message === "Attachment reference expired") throw error;
|
|
10618
|
+
this.invalid();
|
|
10619
|
+
}
|
|
10620
|
+
}
|
|
10621
|
+
invalid() {
|
|
10622
|
+
throw new Error("Invalid attachment reference");
|
|
10623
|
+
}
|
|
10624
|
+
};
|
|
10625
|
+
function decodeCanonical(value, expectedBytes) {
|
|
10626
|
+
const decoded = Buffer.from(value, "base64url");
|
|
10627
|
+
if (decoded.toString("base64url") !== value || expectedBytes !== void 0 && decoded.length !== expectedBytes) {
|
|
10628
|
+
throw new Error("Invalid attachment reference");
|
|
10629
|
+
}
|
|
10630
|
+
return decoded;
|
|
10631
|
+
}
|
|
10632
|
+
function digestUser(userId) {
|
|
10633
|
+
return createHash2("sha256").update(userId, "utf8").digest("hex");
|
|
10634
|
+
}
|
|
10635
|
+
function isAttachmentPayload(value) {
|
|
10636
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
10637
|
+
const record = value;
|
|
10638
|
+
if (Object.keys(record).sort().join(",") !== "contentDigest,exp,mimeType,name,projectId,size,threadId,uri,userDigest,webAppId") return false;
|
|
10639
|
+
return typeof record.webAppId === "string" && typeof record.userDigest === "string" && /^[a-f0-9]{64}$/.test(record.userDigest) && typeof record.threadId === "string" && typeof record.projectId === "string" && typeof record.uri === "string" && record.uri.startsWith("/") && typeof record.name === "string" && record.name.length > 0 && typeof record.mimeType === "string" && record.mimeType.length > 0 && typeof record.size === "number" && Number.isSafeInteger(record.size) && record.size >= 0 && typeof record.contentDigest === "string" && /^[a-f0-9]{64}$/.test(record.contentDigest) && typeof record.exp === "number" && Number.isSafeInteger(record.exp);
|
|
10640
|
+
}
|
|
10641
|
+
|
|
9589
10642
|
// src/routes/index.ts
|
|
9590
10643
|
var registerLatticeRoutes = (app2, channelDeps) => {
|
|
10644
|
+
const webAppStore = getStoreLattice16("default", "agentWebApp").store;
|
|
10645
|
+
const assistantStore = getStoreLattice16("default", "assistant").store;
|
|
10646
|
+
const projectStore = getStoreLattice16("default", "project").store;
|
|
10647
|
+
registerAgentWebAppRoutes(app2, {
|
|
10648
|
+
service: new AgentWebAppService({
|
|
10649
|
+
webAppStore,
|
|
10650
|
+
assistantStore,
|
|
10651
|
+
projectStore,
|
|
10652
|
+
modelRegistry: { has: (key) => modelLatticeManager3.hasLattice(key) },
|
|
10653
|
+
configuredAgentResolver: createConfiguredAgentResolver({
|
|
10654
|
+
hydrate: (tenantId) => agentLatticeManager5.initializeStoredAssistantsForTenant(tenantId),
|
|
10655
|
+
has: (tenantId, assistantId) => agentLatticeManager5.hasAgentLatticeWithTenant(tenantId, assistantId)
|
|
10656
|
+
})
|
|
10657
|
+
})
|
|
10658
|
+
});
|
|
10659
|
+
registerAgentWebAppRuntimeRoutes(app2, {
|
|
10660
|
+
service: new AgentWebAppRuntimeService({
|
|
10661
|
+
webAppStore,
|
|
10662
|
+
threadStore: getStoreLattice16("default", "thread").store,
|
|
10663
|
+
projectStore,
|
|
10664
|
+
assistantResolver: {
|
|
10665
|
+
resolve: async (tenantId, assistantId) => {
|
|
10666
|
+
const stored = await assistantStore.getAssistantById(tenantId, assistantId);
|
|
10667
|
+
if (stored) return stored;
|
|
10668
|
+
await agentLatticeManager5.initializeStoredAssistantsForTenant(tenantId);
|
|
10669
|
+
const config = agentLatticeManager5.getAgentConfigWithTenant(tenantId, assistantId);
|
|
10670
|
+
return config ? { id: assistantId, name: config.name, description: config.description } : null;
|
|
10671
|
+
}
|
|
10672
|
+
},
|
|
10673
|
+
modelRegistry: {
|
|
10674
|
+
has: (key) => modelLatticeManager3.hasLattice(key),
|
|
10675
|
+
label: (key) => key
|
|
10676
|
+
},
|
|
10677
|
+
messageReader: {
|
|
10678
|
+
getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => agentInstanceManager6.getAgent({
|
|
10679
|
+
tenant_id: tenantId,
|
|
10680
|
+
assistant_id: assistantId,
|
|
10681
|
+
thread_id: threadId,
|
|
10682
|
+
workspace_id: workspaceId,
|
|
10683
|
+
project_id: projectId
|
|
10684
|
+
}).getCurrentMessages()
|
|
10685
|
+
},
|
|
10686
|
+
agentResolver: {
|
|
10687
|
+
getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => agentInstanceManager6.getAgent({
|
|
10688
|
+
tenant_id: tenantId,
|
|
10689
|
+
assistant_id: assistantId,
|
|
10690
|
+
thread_id: threadId,
|
|
10691
|
+
workspace_id: workspaceId,
|
|
10692
|
+
project_id: projectId
|
|
10693
|
+
})
|
|
10694
|
+
},
|
|
10695
|
+
attachmentRefs: new AgentWebAppAttachmentRefService(
|
|
10696
|
+
resolveAgentWebAppAttachmentSecret(
|
|
10697
|
+
resolveConsoleTokenSecret(process.env.AUTH_REQUIRED === "true")
|
|
10698
|
+
)
|
|
10699
|
+
),
|
|
10700
|
+
saveProjectFile,
|
|
10701
|
+
authorizePreview: async (webApp, preview) => {
|
|
10702
|
+
if (webApp.tenantId !== preview.tenantId) return false;
|
|
10703
|
+
const assistant = await assistantStore.getAssistantById(preview.tenantId, webApp.assistantId);
|
|
10704
|
+
if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === preview.userId;
|
|
10705
|
+
await agentLatticeManager5.initializeStoredAssistantsForTenant(preview.tenantId);
|
|
10706
|
+
return agentLatticeManager5.hasAgentLatticeWithTenant(preview.tenantId, webApp.assistantId);
|
|
10707
|
+
}
|
|
10708
|
+
})
|
|
10709
|
+
});
|
|
9591
10710
|
registerA2ABridgeRoutes(app2);
|
|
9592
10711
|
registerSTTRoutes(app2);
|
|
9593
10712
|
app2.get("/api/local-a2a", getLocalA2AStatus);
|
|
@@ -10031,9 +11150,9 @@ var skillRegistration = {
|
|
|
10031
11150
|
};
|
|
10032
11151
|
|
|
10033
11152
|
// src/export_registrations/agent.registration.ts
|
|
10034
|
-
import { getStoreLattice as
|
|
11153
|
+
import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
|
|
10035
11154
|
function getStore3() {
|
|
10036
|
-
return
|
|
11155
|
+
return getStoreLattice17("default", "assistant").store;
|
|
10037
11156
|
}
|
|
10038
11157
|
var agentRegistration = {
|
|
10039
11158
|
entityType: "agent",
|
|
@@ -10113,9 +11232,9 @@ var agentRegistration = {
|
|
|
10113
11232
|
};
|
|
10114
11233
|
|
|
10115
11234
|
// src/export_registrations/database-config.registration.ts
|
|
10116
|
-
import { getStoreLattice as
|
|
11235
|
+
import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
|
|
10117
11236
|
function getStore4() {
|
|
10118
|
-
return
|
|
11237
|
+
return getStoreLattice18("default", "database").store;
|
|
10119
11238
|
}
|
|
10120
11239
|
var databaseConfigRegistration = {
|
|
10121
11240
|
entityType: "database_config",
|
|
@@ -10195,9 +11314,9 @@ var databaseConfigRegistration = {
|
|
|
10195
11314
|
};
|
|
10196
11315
|
|
|
10197
11316
|
// src/export_registrations/metrics-config.registration.ts
|
|
10198
|
-
import { getStoreLattice as
|
|
11317
|
+
import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
|
|
10199
11318
|
function getStore5() {
|
|
10200
|
-
return
|
|
11319
|
+
return getStoreLattice19("default", "metrics").store;
|
|
10201
11320
|
}
|
|
10202
11321
|
var metricsConfigRegistration = {
|
|
10203
11322
|
entityType: "metrics_config",
|
|
@@ -10277,9 +11396,9 @@ var metricsConfigRegistration = {
|
|
|
10277
11396
|
};
|
|
10278
11397
|
|
|
10279
11398
|
// src/export_registrations/mcp-config.registration.ts
|
|
10280
|
-
import { getStoreLattice as
|
|
11399
|
+
import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
|
|
10281
11400
|
function getStore6() {
|
|
10282
|
-
return
|
|
11401
|
+
return getStoreLattice20("default", "mcp").store;
|
|
10283
11402
|
}
|
|
10284
11403
|
var mcpConfigRegistration = {
|
|
10285
11404
|
entityType: "mcp_config",
|
|
@@ -10362,9 +11481,9 @@ var mcpConfigRegistration = {
|
|
|
10362
11481
|
};
|
|
10363
11482
|
|
|
10364
11483
|
// src/export_registrations/connection.registration.ts
|
|
10365
|
-
import { getStoreLattice as
|
|
11484
|
+
import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
|
|
10366
11485
|
function getStore7() {
|
|
10367
|
-
return
|
|
11486
|
+
return getStoreLattice21("default", "connection").store;
|
|
10368
11487
|
}
|
|
10369
11488
|
async function listAllConnections(tenantId) {
|
|
10370
11489
|
const store = getStore7();
|
|
@@ -10446,9 +11565,9 @@ var connectionRegistration = {
|
|
|
10446
11565
|
};
|
|
10447
11566
|
|
|
10448
11567
|
// src/export_registrations/collection.registration.ts
|
|
10449
|
-
import { getStoreLattice as
|
|
11568
|
+
import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
|
|
10450
11569
|
function getStore8() {
|
|
10451
|
-
return
|
|
11570
|
+
return getStoreLattice22("default", "collection").store;
|
|
10452
11571
|
}
|
|
10453
11572
|
var collectionRegistration = {
|
|
10454
11573
|
entityType: "collection",
|
|
@@ -10519,11 +11638,11 @@ var collectionRegistration = {
|
|
|
10519
11638
|
};
|
|
10520
11639
|
|
|
10521
11640
|
// src/export_registrations/channel-installation.registration.ts
|
|
10522
|
-
import { getStoreLattice as
|
|
10523
|
-
import { randomUUID as
|
|
11641
|
+
import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
|
|
11642
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
10524
11643
|
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
10525
11644
|
function getStore9() {
|
|
10526
|
-
return
|
|
11645
|
+
return getStoreLattice23("default", "channelInstallation").store;
|
|
10527
11646
|
}
|
|
10528
11647
|
var channelInstallationRegistration = {
|
|
10529
11648
|
entityType: "channel_installation",
|
|
@@ -10610,7 +11729,7 @@ var channelInstallationRegistration = {
|
|
|
10610
11729
|
}
|
|
10611
11730
|
id = sourceId;
|
|
10612
11731
|
} else {
|
|
10613
|
-
id =
|
|
11732
|
+
id = randomUUID10();
|
|
10614
11733
|
}
|
|
10615
11734
|
await store.createInstallation(tenantId, id, {
|
|
10616
11735
|
channel: data.channel,
|
|
@@ -10622,9 +11741,9 @@ var channelInstallationRegistration = {
|
|
|
10622
11741
|
};
|
|
10623
11742
|
|
|
10624
11743
|
// src/export_registrations/menu.registration.ts
|
|
10625
|
-
import { getStoreLattice as
|
|
11744
|
+
import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
|
|
10626
11745
|
function getStore10() {
|
|
10627
|
-
return
|
|
11746
|
+
return getStoreLattice24("default", "menu").store;
|
|
10628
11747
|
}
|
|
10629
11748
|
var menuRegistration = {
|
|
10630
11749
|
entityType: "menu",
|
|
@@ -10702,9 +11821,9 @@ var menuRegistration = {
|
|
|
10702
11821
|
};
|
|
10703
11822
|
|
|
10704
11823
|
// src/export_registrations/eval.registration.ts
|
|
10705
|
-
import { getStoreLattice as
|
|
11824
|
+
import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
|
|
10706
11825
|
function getStore11() {
|
|
10707
|
-
return
|
|
11826
|
+
return getStoreLattice25("default", "eval").store;
|
|
10708
11827
|
}
|
|
10709
11828
|
var evalRegistration = {
|
|
10710
11829
|
entityType: "eval",
|
|
@@ -11045,10 +12164,10 @@ function registerAllBuiltinEntities() {
|
|
|
11045
12164
|
|
|
11046
12165
|
// src/router/MessageRouter.ts
|
|
11047
12166
|
import {
|
|
11048
|
-
getStoreLattice as
|
|
11049
|
-
agentInstanceManager as
|
|
12167
|
+
getStoreLattice as getStoreLattice26,
|
|
12168
|
+
agentInstanceManager as agentInstanceManager7
|
|
11050
12169
|
} from "@axiom-lattice/core";
|
|
11051
|
-
import { randomUUID as
|
|
12170
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
11052
12171
|
var BindingNotFoundError = class extends Error {
|
|
11053
12172
|
constructor(message) {
|
|
11054
12173
|
super(message);
|
|
@@ -11217,7 +12336,7 @@ var MessageRouter = class {
|
|
|
11217
12336
|
channel: message.channel,
|
|
11218
12337
|
adapterChannel: adapter.channel
|
|
11219
12338
|
}, "Thread resolved by adapter strategy");
|
|
11220
|
-
const threadStore =
|
|
12339
|
+
const threadStore = getStoreLattice26("default", "thread").store;
|
|
11221
12340
|
try {
|
|
11222
12341
|
await threadStore.createThread(
|
|
11223
12342
|
tenantId,
|
|
@@ -11254,8 +12373,8 @@ var MessageRouter = class {
|
|
|
11254
12373
|
}
|
|
11255
12374
|
}
|
|
11256
12375
|
if (!threadId) {
|
|
11257
|
-
const threadStore =
|
|
11258
|
-
const newThreadId =
|
|
12376
|
+
const threadStore = getStoreLattice26("default", "thread").store;
|
|
12377
|
+
const newThreadId = randomUUID11();
|
|
11259
12378
|
console.log({
|
|
11260
12379
|
event: "dispatch:thread:create",
|
|
11261
12380
|
agentId,
|
|
@@ -11294,7 +12413,7 @@ var MessageRouter = class {
|
|
|
11294
12413
|
senderId: message.sender.id,
|
|
11295
12414
|
contentLength: message.content.text.length
|
|
11296
12415
|
}, "Dispatching to agent");
|
|
11297
|
-
const agent =
|
|
12416
|
+
const agent = agentInstanceManager7.getAgent({
|
|
11298
12417
|
tenant_id: tenantId,
|
|
11299
12418
|
assistant_id: agentId,
|
|
11300
12419
|
thread_id: threadId,
|
|
@@ -11598,7 +12717,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
11598
12717
|
};
|
|
11599
12718
|
|
|
11600
12719
|
// src/services/agent_task_consumer.ts
|
|
11601
|
-
import { eventBus as
|
|
12720
|
+
import { eventBus as eventBus3, AGENT_TASK_EVENT, agentInstanceManager as agentInstanceManager8, QueueMode as QueueMode2 } from "@axiom-lattice/core";
|
|
11602
12721
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
11603
12722
|
const {
|
|
11604
12723
|
assistant_id,
|
|
@@ -11616,17 +12735,17 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
11616
12735
|
console.log(
|
|
11617
12736
|
`\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
|
|
11618
12737
|
);
|
|
11619
|
-
const agent =
|
|
12738
|
+
const agent = agentInstanceManager8.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
|
|
11620
12739
|
await agent.addMessage({ input, command, custom_run_config: runConfig }, QueueMode2.STEER);
|
|
11621
12740
|
if (callback_event) {
|
|
11622
12741
|
agent.subscribeOnce("message:completed", (evt) => {
|
|
11623
|
-
|
|
12742
|
+
eventBus3.publish(callback_event, {
|
|
11624
12743
|
success: true,
|
|
11625
12744
|
state: evt.state
|
|
11626
12745
|
});
|
|
11627
12746
|
if (main_thread_id && main_tenant_id) {
|
|
11628
12747
|
try {
|
|
11629
|
-
const mainAgent =
|
|
12748
|
+
const mainAgent = agentInstanceManager8.getAgent({
|
|
11630
12749
|
assistant_id: main_assistant_id ?? assistant_id,
|
|
11631
12750
|
thread_id: main_thread_id,
|
|
11632
12751
|
tenant_id: main_tenant_id,
|
|
@@ -11656,7 +12775,7 @@ ${summary}`
|
|
|
11656
12775
|
}
|
|
11657
12776
|
});
|
|
11658
12777
|
agent.subscribeOnce("message:interrupted", (evt) => {
|
|
11659
|
-
|
|
12778
|
+
eventBus3.publish(callback_event, {
|
|
11660
12779
|
success: true,
|
|
11661
12780
|
state: evt.state
|
|
11662
12781
|
});
|
|
@@ -11679,7 +12798,7 @@ ${summary}`
|
|
|
11679
12798
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
11680
12799
|
}
|
|
11681
12800
|
if (callback_event) {
|
|
11682
|
-
|
|
12801
|
+
eventBus3.publish(callback_event, {
|
|
11683
12802
|
success: false,
|
|
11684
12803
|
error: error instanceof Error ? error.message : String(error)
|
|
11685
12804
|
});
|
|
@@ -11716,7 +12835,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
11716
12835
|
* 初始化事件监听和队列轮询
|
|
11717
12836
|
*/
|
|
11718
12837
|
initialize() {
|
|
11719
|
-
|
|
12838
|
+
eventBus3.subscribe(AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
|
|
11720
12839
|
this.startPollingQueue();
|
|
11721
12840
|
console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
|
|
11722
12841
|
}
|
|
@@ -11835,7 +12954,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
11835
12954
|
handleAgentTask(taskRequest).catch((error) => {
|
|
11836
12955
|
console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
|
|
11837
12956
|
if (taskRequest.callback_event) {
|
|
11838
|
-
|
|
12957
|
+
eventBus3.publish(taskRequest.callback_event, {
|
|
11839
12958
|
success: false,
|
|
11840
12959
|
error: error instanceof Error ? error.message : String(error)
|
|
11841
12960
|
});
|
|
@@ -11855,8 +12974,8 @@ import {
|
|
|
11855
12974
|
getLoggerLattice,
|
|
11856
12975
|
loggerLatticeManager,
|
|
11857
12976
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
11858
|
-
getStoreLattice as
|
|
11859
|
-
agentInstanceManager as
|
|
12977
|
+
getStoreLattice as getStoreLattice27,
|
|
12978
|
+
agentInstanceManager as agentInstanceManager9,
|
|
11860
12979
|
createSandboxProvider,
|
|
11861
12980
|
TokenCache,
|
|
11862
12981
|
mcpManager
|
|
@@ -11990,7 +13109,7 @@ function getConfiguredSandboxProvider() {
|
|
|
11990
13109
|
}
|
|
11991
13110
|
async function restoreMcpConnections() {
|
|
11992
13111
|
try {
|
|
11993
|
-
const storeLattice =
|
|
13112
|
+
const storeLattice = getStoreLattice27("default", "mcp");
|
|
11994
13113
|
const store = storeLattice.store;
|
|
11995
13114
|
if (!store) {
|
|
11996
13115
|
logger4.info("MCP store not configured, skipping connection restoration");
|
|
@@ -12064,7 +13183,7 @@ var start = async (config) => {
|
|
|
12064
13183
|
}
|
|
12065
13184
|
setEvalRunService(evalRunner);
|
|
12066
13185
|
try {
|
|
12067
|
-
const menuStore =
|
|
13186
|
+
const menuStore = getStoreLattice27("default", "menu").store;
|
|
12068
13187
|
setMenuRegistry(menuStore);
|
|
12069
13188
|
logger4.info("Menu registry initialized");
|
|
12070
13189
|
} catch {
|
|
@@ -12076,12 +13195,12 @@ var start = async (config) => {
|
|
|
12076
13195
|
registerLatticeRoutes(app, channelDeps);
|
|
12077
13196
|
try {
|
|
12078
13197
|
const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-DK5X6VIL.mjs");
|
|
12079
|
-
const { registerA2AStandardRoutes } = await import("./a2a-standard-
|
|
13198
|
+
const { registerA2AStandardRoutes } = await import("./a2a-standard-VYQCHMBV.mjs");
|
|
12080
13199
|
const a2a = await import("./a2a-7N3WDAOP.mjs");
|
|
12081
|
-
const a2aKeyStore =
|
|
12082
|
-
const assistantStore =
|
|
12083
|
-
const projectStore =
|
|
12084
|
-
const taskStore =
|
|
13200
|
+
const a2aKeyStore = getStoreLattice27("default", "a2aApiKey").store;
|
|
13201
|
+
const assistantStore = getStoreLattice27("default", "assistant").store;
|
|
13202
|
+
const projectStore = getStoreLattice27("default", "project").store;
|
|
13203
|
+
const taskStore = getStoreLattice27("default", "task").store;
|
|
12085
13204
|
const authService = new A2AAuthService({
|
|
12086
13205
|
keyStore: a2aKeyStore,
|
|
12087
13206
|
assistantStore,
|
|
@@ -12111,7 +13230,7 @@ var start = async (config) => {
|
|
|
12111
13230
|
}
|
|
12112
13231
|
try {
|
|
12113
13232
|
const { ResourceController } = await import("./resources-VA7LSDKN.mjs");
|
|
12114
|
-
const sharedResourceStore =
|
|
13233
|
+
const sharedResourceStore = getStoreLattice27("default", "sharedResource").store;
|
|
12115
13234
|
const cache = new TokenCache();
|
|
12116
13235
|
const resourceController = new ResourceController({
|
|
12117
13236
|
store: sharedResourceStore,
|
|
@@ -12151,7 +13270,7 @@ var start = async (config) => {
|
|
|
12151
13270
|
}
|
|
12152
13271
|
}
|
|
12153
13272
|
if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
|
|
12154
|
-
|
|
13273
|
+
agentInstanceManager9.restore().then((stats) => {
|
|
12155
13274
|
logger4.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
|
|
12156
13275
|
}).catch((error) => {
|
|
12157
13276
|
logger4.error("Agent recovery failed", { error });
|