@axiom-lattice/gateway 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{OrphanRecoveryRuntime-MQDEJX3M.mjs → OrphanRecoveryRuntime-BEFILJMU.mjs} +2 -2
- package/dist/{chunk-2POW27EI.mjs → chunk-7CG57LEF.mjs} +2 -2
- package/dist/{chunk-WKNZ7GBC.mjs → chunk-EXPERMZR.mjs} +1 -1
- package/dist/chunk-EXPERMZR.mjs.map +1 -0
- package/dist/{chunk-52SUWMOX.mjs → chunk-JUA3VJE7.mjs} +3 -2
- package/dist/chunk-JUA3VJE7.mjs.map +1 -0
- package/dist/{chunk-TJAUXMYJ.mjs → chunk-URXZPI2F.mjs} +8 -1
- package/dist/chunk-URXZPI2F.mjs.map +1 -0
- package/dist/{chunk-KDQKTU3N.mjs → chunk-ZBTG3T7K.mjs} +44 -8
- package/dist/chunk-ZBTG3T7K.mjs.map +1 -0
- package/dist/embed/{embed-page-AgentDetailSideApp-2C2ZR62Q.js → embed-page-AgentDetailSideApp-NTLAFO5C.js} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-GG4ONSTS.js → embed-page-AgentGovernanceWorkbench-QPVBOMLH.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-WMYTVUUC.js → embed-page-MessageGraph-JAJGMYKH.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-7XCMDMH6.js → embed-page-ReactChart-WUG6TEAZ.js} +1 -1
- package/dist/embed/{embed-page-SkillFlow-XMMHEHQL.js → embed-page-SkillFlow-TSBZ4V2P.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-4ZJ4Q4CL.js → embed-page-TasksSideApp-TJI22YGD.js} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-H5OOEN3K.js → embed-page-TeamWorkspace-OJJLN5VI.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-QMXWERVC.js → embed-page-TopologyRuntimeView-ZFU4N6TV.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-L2OEGCA5.js → embed-page-WorkflowAutomationView-ALXV7VQQ.js} +1 -1
- package/dist/embed/{embed-page-chunk-CZE4IEB3.js → embed-page-chunk-DZQBVJ7Z.js} +1 -1
- package/dist/embed/{embed-page-chunk-N2ISCAS7.js → embed-page-chunk-IPIJ2SG6.js} +1 -1
- package/dist/embed/{embed-page-chunk-SUMYB6ZA.js → embed-page-chunk-N22EGQAJ.js} +1 -1
- package/dist/embed/{embed-page-chunk-OHDEKFTU.js → embed-page-chunk-O72XBKQH.js} +2 -2
- package/dist/embed/{embed-page-chunk-XIXSRYJY.js → embed-page-chunk-QMVIL4BJ.js} +190 -185
- package/dist/embed/{embed-page-chunk-2G54EIW2.js → embed-page-chunk-UT3WYBZH.js} +1 -1
- package/dist/embed/embed-page-chunk-YPNSOZYW.js +8 -0
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.d.mts +146 -17
- package/dist/index.d.ts +146 -17
- package/dist/index.js +1190 -328
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +649 -97
- package/dist/index.mjs.map +1 -1
- package/dist/{open-STFMNRO5.mjs → open-UVKMONVE.mjs} +5 -4
- package/dist/open-UVKMONVE.mjs.map +1 -0
- package/dist/{open-files-NPNQQ6TY.mjs → open-files-X3HXVEEM.mjs} +4 -4
- package/dist/{open-session-IHDGAXL3.mjs → open-session-2Y7XJJW2.mjs} +3 -3
- package/dist/{resources-DXPXAXEZ.mjs → resources-2L3KSUUU.mjs} +2 -2
- package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs +275 -0
- package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs.map +1 -0
- package/dist/{sandboxProvider-I3P66VXT.mjs → sandboxProvider-DUYWHYJN.mjs} +2 -2
- package/package.json +14 -8
- package/dist/chunk-52SUWMOX.mjs.map +0 -1
- package/dist/chunk-KDQKTU3N.mjs.map +0 -1
- package/dist/chunk-TJAUXMYJ.mjs.map +0 -1
- package/dist/chunk-WKNZ7GBC.mjs.map +0 -1
- package/dist/embed/embed-page-chunk-HEMNMC4X.js +0 -7
- package/dist/open-STFMNRO5.mjs.map +0 -1
- /package/dist/{OrphanRecoveryRuntime-MQDEJX3M.mjs.map → OrphanRecoveryRuntime-BEFILJMU.mjs.map} +0 -0
- /package/dist/{chunk-2POW27EI.mjs.map → chunk-7CG57LEF.mjs.map} +0 -0
- /package/dist/{open-files-NPNQQ6TY.mjs.map → open-files-X3HXVEEM.mjs.map} +0 -0
- /package/dist/{open-session-IHDGAXL3.mjs.map → open-session-2Y7XJJW2.mjs.map} +0 -0
- /package/dist/{resources-DXPXAXEZ.mjs.map → resources-2L3KSUUU.mjs.map} +0 -0
- /package/dist/{sandboxProvider-I3P66VXT.mjs.map → sandboxProvider-DUYWHYJN.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -9,13 +9,14 @@ import {
|
|
|
9
9
|
} from "./chunk-73QZQQOT.mjs";
|
|
10
10
|
import {
|
|
11
11
|
buildPreviewTokenScript
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-7CG57LEF.mjs";
|
|
13
|
+
import "./chunk-EXPERMZR.mjs";
|
|
14
14
|
import {
|
|
15
15
|
CollectionService
|
|
16
16
|
} from "./chunk-PWHUWFXL.mjs";
|
|
17
17
|
import {
|
|
18
18
|
createOrphanRecoveryRuntime,
|
|
19
|
+
createTaskRunLiveness,
|
|
19
20
|
isProjectLifecycleTask,
|
|
20
21
|
isRosterExemptProject,
|
|
21
22
|
recoverTask,
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
resolveTaskOwnerMembership,
|
|
24
25
|
resolveTopCaller,
|
|
25
26
|
runOrphanRecovery
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-ZBTG3T7K.mjs";
|
|
27
28
|
import {
|
|
28
29
|
createRecoveryCallerDeps
|
|
29
30
|
} from "./chunk-NOXGKYUV.mjs";
|
|
@@ -858,9 +859,11 @@ async function getAssistantStats(request, reply) {
|
|
|
858
859
|
// src/controllers/run.ts
|
|
859
860
|
import { v4 } from "uuid";
|
|
860
861
|
import {
|
|
861
|
-
agentInstanceManager as agentInstanceManager2
|
|
862
|
+
agentInstanceManager as agentInstanceManager2,
|
|
863
|
+
getReplicaId,
|
|
864
|
+
getThreadRunOwner
|
|
862
865
|
} from "@axiom-lattice/core";
|
|
863
|
-
import { MessageChunkTypes } from "@axiom-lattice/protocols";
|
|
866
|
+
import { MessageChunkTypes, AGENT_AFFINITY_HEADER, AFFINITY_ECHO_HEADER, affinityEchoHeaders, REPLICA_HEADER, AFFINITY_MISS_CODE, buildAffinityMissChunk } from "@axiom-lattice/protocols";
|
|
864
867
|
|
|
865
868
|
// src/utils/agentScope.ts
|
|
866
869
|
function headerValue(value) {
|
|
@@ -960,6 +963,24 @@ async function writeSse(raw, value, closed) {
|
|
|
960
963
|
}
|
|
961
964
|
|
|
962
965
|
// src/controllers/run.ts
|
|
966
|
+
var replicaHeader = () => ({ [REPLICA_HEADER]: getReplicaId() });
|
|
967
|
+
var exposeHeaders = `${REPLICA_HEADER}, ${AFFINITY_ECHO_HEADER}`;
|
|
968
|
+
function applyAffinityEcho(request, reply) {
|
|
969
|
+
for (const [name, value] of Object.entries(affinityEchoHeaders(request.headers[AGENT_AFFINITY_HEADER]))) {
|
|
970
|
+
reply.header(name, value);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
var AFFINITY_GRACE_MS = Number.isFinite(Number(process.env.AXIOM_AFFINITY_GRACE_MS)) ? Number(process.env.AXIOM_AFFINITY_GRACE_MS) : 500;
|
|
974
|
+
async function peerOwnsThread(input) {
|
|
975
|
+
if (!process.env.AXIOM_REPLICA_ID) return null;
|
|
976
|
+
if (!input.tenant_id || !input.assistant_id || !input.thread_id) return null;
|
|
977
|
+
const owner = await getThreadRunOwner({
|
|
978
|
+
tenantId: input.tenant_id,
|
|
979
|
+
assistantId: input.assistant_id,
|
|
980
|
+
threadId: input.thread_id
|
|
981
|
+
});
|
|
982
|
+
return owner && owner !== getReplicaId() ? owner : null;
|
|
983
|
+
}
|
|
963
984
|
function getUserId2(request) {
|
|
964
985
|
const authUser = request.user;
|
|
965
986
|
if (authUser?.id) return authUser.id;
|
|
@@ -986,6 +1007,7 @@ var createRun = async (request, reply) => {
|
|
|
986
1007
|
...user_id && { user_id }
|
|
987
1008
|
};
|
|
988
1009
|
if (!assistant_id) {
|
|
1010
|
+
applyAffinityEcho(request, reply);
|
|
989
1011
|
reply.status(400).send({
|
|
990
1012
|
success: false,
|
|
991
1013
|
error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
|
|
@@ -1007,6 +1029,8 @@ var createRun = async (request, reply) => {
|
|
|
1007
1029
|
command,
|
|
1008
1030
|
custom_run_config: mergedConfig
|
|
1009
1031
|
}, mode);
|
|
1032
|
+
reply.header(REPLICA_HEADER, getReplicaId());
|
|
1033
|
+
applyAffinityEcho(request, reply);
|
|
1010
1034
|
reply.status(202).send({
|
|
1011
1035
|
success: true,
|
|
1012
1036
|
messageId: result.messageId,
|
|
@@ -1020,7 +1044,10 @@ var createRun = async (request, reply) => {
|
|
|
1020
1044
|
"Content-Type": "text/event-stream",
|
|
1021
1045
|
"Cache-Control": "no-cache",
|
|
1022
1046
|
Connection: "keep-alive",
|
|
1023
|
-
"Access-Control-Allow-Origin": "*"
|
|
1047
|
+
"Access-Control-Allow-Origin": "*",
|
|
1048
|
+
"Access-Control-Expose-Headers": exposeHeaders,
|
|
1049
|
+
...replicaHeader(),
|
|
1050
|
+
...affinityEchoHeaders(request.headers[AGENT_AFFINITY_HEADER])
|
|
1024
1051
|
});
|
|
1025
1052
|
try {
|
|
1026
1053
|
const messageInput = message_id ? { ...input, id: message_id } : input;
|
|
@@ -1031,6 +1058,18 @@ var createRun = async (request, reply) => {
|
|
|
1031
1058
|
}, [MessageChunkTypes.MESSAGE_COMPLETED, MessageChunkTypes.INTERRUPT, MessageChunkTypes.MESSAGE_FAILED], {
|
|
1032
1059
|
queueMode: mode
|
|
1033
1060
|
});
|
|
1061
|
+
let peer = await peerOwnsThread({ tenant_id, assistant_id, thread_id });
|
|
1062
|
+
if (peer) {
|
|
1063
|
+
await new Promise((resolve3) => setTimeout(resolve3, AFFINITY_GRACE_MS));
|
|
1064
|
+
peer = await peerOwnsThread({ tenant_id, assistant_id, thread_id });
|
|
1065
|
+
}
|
|
1066
|
+
if (peer) {
|
|
1067
|
+
reply.raw.write(`data: ${JSON.stringify(buildAffinityMissChunk(peer, thread_id))}
|
|
1068
|
+
|
|
1069
|
+
`);
|
|
1070
|
+
reply.raw.end();
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1034
1073
|
await pipeAsyncIterableToSse(reply.raw, result.stream, {
|
|
1035
1074
|
onError: (error) => [{
|
|
1036
1075
|
type: "error",
|
|
@@ -1062,12 +1101,15 @@ var createRun = async (request, reply) => {
|
|
|
1062
1101
|
command,
|
|
1063
1102
|
custom_run_config: mergedConfig
|
|
1064
1103
|
});
|
|
1104
|
+
reply.header(REPLICA_HEADER, getReplicaId());
|
|
1105
|
+
applyAffinityEcho(request, reply);
|
|
1065
1106
|
reply.status(200).send({
|
|
1066
1107
|
success: true,
|
|
1067
1108
|
...result
|
|
1068
1109
|
});
|
|
1069
1110
|
}
|
|
1070
1111
|
} catch (error) {
|
|
1112
|
+
applyAffinityEcho(request, reply);
|
|
1071
1113
|
reply.status(500).send({
|
|
1072
1114
|
success: false,
|
|
1073
1115
|
error: `\u521B\u5EFA\u8FD0\u884C\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
|
|
@@ -1090,9 +1132,20 @@ var resumeStream = async (request, reply) => {
|
|
|
1090
1132
|
"Content-Type": "text/event-stream",
|
|
1091
1133
|
"Cache-Control": "no-cache",
|
|
1092
1134
|
Connection: "keep-alive",
|
|
1093
|
-
"Access-Control-Allow-Origin": "*"
|
|
1135
|
+
"Access-Control-Allow-Origin": "*",
|
|
1136
|
+
"Access-Control-Expose-Headers": exposeHeaders,
|
|
1137
|
+
...replicaHeader(),
|
|
1138
|
+
...affinityEchoHeaders(request.headers[AGENT_AFFINITY_HEADER])
|
|
1094
1139
|
});
|
|
1095
1140
|
try {
|
|
1141
|
+
const peer = await peerOwnsThread({ tenant_id, assistant_id, thread_id });
|
|
1142
|
+
if (peer) {
|
|
1143
|
+
reply.raw.write(`data: ${JSON.stringify(buildAffinityMissChunk(peer, thread_id))}
|
|
1144
|
+
|
|
1145
|
+
`);
|
|
1146
|
+
reply.raw.end();
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1096
1149
|
const agent = agentInstanceManager2.getAgent({
|
|
1097
1150
|
assistant_id,
|
|
1098
1151
|
thread_id,
|
|
@@ -1143,12 +1196,22 @@ var abortRun = async (request, reply) => {
|
|
|
1143
1196
|
workspace_id,
|
|
1144
1197
|
project_id
|
|
1145
1198
|
});
|
|
1199
|
+
const peer = await peerOwnsThread({ tenant_id, assistant_id: assistantId, thread_id: threadId });
|
|
1200
|
+
if (peer) {
|
|
1201
|
+
reply.header(REPLICA_HEADER, getReplicaId());
|
|
1202
|
+
applyAffinityEcho(request, reply);
|
|
1203
|
+
reply.status(409).send({ success: false, code: AFFINITY_MISS_CODE, owner: peer, threadId });
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1146
1206
|
await agent.abort();
|
|
1207
|
+
reply.header(REPLICA_HEADER, getReplicaId());
|
|
1208
|
+
applyAffinityEcho(request, reply);
|
|
1147
1209
|
reply.status(200).send({
|
|
1148
1210
|
success: true,
|
|
1149
1211
|
message: "Agent aborted"
|
|
1150
1212
|
});
|
|
1151
1213
|
} catch (error) {
|
|
1214
|
+
applyAffinityEcho(request, reply);
|
|
1152
1215
|
reply.status(500).send({
|
|
1153
1216
|
success: false,
|
|
1154
1217
|
error: `Abort failed: ${error.message}`
|
|
@@ -1166,13 +1229,17 @@ var recoverRun = async (request, reply) => {
|
|
|
1166
1229
|
workspace_id,
|
|
1167
1230
|
project_id
|
|
1168
1231
|
});
|
|
1169
|
-
const { status } = await agent.resumeTask();
|
|
1232
|
+
const { status, leaseContended } = await agent.resumeTask();
|
|
1233
|
+
reply.header(REPLICA_HEADER, getReplicaId());
|
|
1234
|
+
applyAffinityEcho(request, reply);
|
|
1170
1235
|
reply.status(200).send({
|
|
1171
1236
|
success: true,
|
|
1172
1237
|
threadId,
|
|
1173
|
-
status
|
|
1238
|
+
status,
|
|
1239
|
+
...leaseContended ? { code: AFFINITY_MISS_CODE } : {}
|
|
1174
1240
|
});
|
|
1175
1241
|
} catch (error) {
|
|
1242
|
+
applyAffinityEcho(request, reply);
|
|
1176
1243
|
reply.status(500).send({
|
|
1177
1244
|
success: false,
|
|
1178
1245
|
error: `Recover failed: ${error.message}`
|
|
@@ -7750,11 +7817,49 @@ function connectionRoutes(fastify2) {
|
|
|
7750
7817
|
// src/controllers/mcp-configs.ts
|
|
7751
7818
|
import {
|
|
7752
7819
|
getStoreLattice as getStoreLattice9,
|
|
7820
|
+
getReplicaId as getReplicaId2,
|
|
7753
7821
|
mcpManager,
|
|
7754
7822
|
McpLatticeManager,
|
|
7755
7823
|
convertToConnection
|
|
7756
7824
|
} from "@axiom-lattice/core";
|
|
7757
7825
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
7826
|
+
|
|
7827
|
+
// src/services/mcpConfigSync.ts
|
|
7828
|
+
var MCP_CONFIG_CHANGED_TOPIC = "mcp-config-changed";
|
|
7829
|
+
async function publishMcpConfigChanged(outbox, owner, tenantId, serverKey) {
|
|
7830
|
+
if (!tenantId || !serverKey) return;
|
|
7831
|
+
try {
|
|
7832
|
+
await outbox.append({
|
|
7833
|
+
topic: MCP_CONFIG_CHANGED_TOPIC,
|
|
7834
|
+
payload: { tenantId, serverKey },
|
|
7835
|
+
originReplicaId: owner
|
|
7836
|
+
});
|
|
7837
|
+
} catch {
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7840
|
+
async function reconcileMcpConfigChange(payload, deps) {
|
|
7841
|
+
const tenantId = payload?.tenantId;
|
|
7842
|
+
const serverKey = payload?.serverKey;
|
|
7843
|
+
if (typeof tenantId !== "string" || tenantId.length === 0) return;
|
|
7844
|
+
if (typeof serverKey !== "string" || serverKey.length === 0) return;
|
|
7845
|
+
const config = await deps.store.getConfigByKey(tenantId, serverKey);
|
|
7846
|
+
if (!config || config.status !== "connected") {
|
|
7847
|
+
if (deps.manager.hasServer(tenantId, serverKey)) {
|
|
7848
|
+
await deps.manager.removeServer(tenantId, serverKey);
|
|
7849
|
+
}
|
|
7850
|
+
return;
|
|
7851
|
+
}
|
|
7852
|
+
const connection = deps.convertToConnection(config.config);
|
|
7853
|
+
deps.manager.registerServer(tenantId, serverKey, connection, {
|
|
7854
|
+
timeoutMs: config.config.timeout,
|
|
7855
|
+
maxAttempts: config.config.retryAttempts,
|
|
7856
|
+
selectedTools: config.selectedTools
|
|
7857
|
+
});
|
|
7858
|
+
await deps.manager.ensureConnected(tenantId, serverKey);
|
|
7859
|
+
await deps.manager.registerToolsToToolLattice(tenantId, serverKey, config.selectedTools);
|
|
7860
|
+
}
|
|
7861
|
+
|
|
7862
|
+
// src/controllers/mcp-configs.ts
|
|
7758
7863
|
function getTenantId10(request) {
|
|
7759
7864
|
const userTenantId = request.user?.tenantId;
|
|
7760
7865
|
if (userTenantId) {
|
|
@@ -7762,6 +7867,14 @@ function getTenantId10(request) {
|
|
|
7762
7867
|
}
|
|
7763
7868
|
return request.headers["x-tenant-id"] || "default";
|
|
7764
7869
|
}
|
|
7870
|
+
function broadcastMcpConfigChanged(tenantId, serverKey) {
|
|
7871
|
+
if (!tenantId || !serverKey) return;
|
|
7872
|
+
try {
|
|
7873
|
+
const outbox = getStoreLattice9("default", "outbox").store;
|
|
7874
|
+
void publishMcpConfigChanged(outbox, getReplicaId2(), tenantId, serverKey);
|
|
7875
|
+
} catch {
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7765
7878
|
async function getMcpServerConfigList(request, reply) {
|
|
7766
7879
|
const tenantId = getTenantId10(request);
|
|
7767
7880
|
try {
|
|
@@ -7839,6 +7952,7 @@ async function createMcpServerConfig(request, reply) {
|
|
|
7839
7952
|
await store.updateConfig(tenantId, id3, { status: "error" });
|
|
7840
7953
|
config.status = "error";
|
|
7841
7954
|
}
|
|
7955
|
+
broadcastMcpConfigChanged(tenantId, config.key);
|
|
7842
7956
|
reply.code(201);
|
|
7843
7957
|
return {
|
|
7844
7958
|
success: true,
|
|
@@ -7868,7 +7982,9 @@ async function updateMcpServerConfig(request, reply) {
|
|
|
7868
7982
|
message: "MCP server configuration not found"
|
|
7869
7983
|
};
|
|
7870
7984
|
}
|
|
7871
|
-
const
|
|
7985
|
+
const configChanged = updates.config !== void 0;
|
|
7986
|
+
const toolsChanged = updates.selectedTools !== void 0;
|
|
7987
|
+
const statusChanged = updates.status !== void 0;
|
|
7872
7988
|
const updated = await store.updateConfig(tenantId, existing.id, updates);
|
|
7873
7989
|
if (!updated) {
|
|
7874
7990
|
return {
|
|
@@ -7876,7 +7992,7 @@ async function updateMcpServerConfig(request, reply) {
|
|
|
7876
7992
|
message: "Failed to update MCP server configuration"
|
|
7877
7993
|
};
|
|
7878
7994
|
}
|
|
7879
|
-
if (
|
|
7995
|
+
if (configChanged) {
|
|
7880
7996
|
try {
|
|
7881
7997
|
const connection = convertToConnection(updated.config);
|
|
7882
7998
|
mcpManager.registerServer(tenantId, key, connection, {
|
|
@@ -7892,6 +8008,22 @@ async function updateMcpServerConfig(request, reply) {
|
|
|
7892
8008
|
await store.updateConfig(tenantId, existing.id, { status: "error" });
|
|
7893
8009
|
updated.status = "error";
|
|
7894
8010
|
}
|
|
8011
|
+
} else if (toolsChanged && mcpManager.hasServer(tenantId, key)) {
|
|
8012
|
+
try {
|
|
8013
|
+
await mcpManager.registerToolsToToolLattice(tenantId, key, updated.selectedTools);
|
|
8014
|
+
} catch (error) {
|
|
8015
|
+
console.warn("Failed to re-register MCP tools:", error);
|
|
8016
|
+
}
|
|
8017
|
+
}
|
|
8018
|
+
if (statusChanged && updated.status !== "connected" && mcpManager.hasServer(tenantId, key)) {
|
|
8019
|
+
try {
|
|
8020
|
+
await mcpManager.removeServer(tenantId, key);
|
|
8021
|
+
} catch (error) {
|
|
8022
|
+
console.warn("Failed to remove MCP server:", error);
|
|
8023
|
+
}
|
|
8024
|
+
}
|
|
8025
|
+
if (configChanged || toolsChanged || statusChanged) {
|
|
8026
|
+
broadcastMcpConfigChanged(tenantId, key);
|
|
7895
8027
|
}
|
|
7896
8028
|
return {
|
|
7897
8029
|
success: true,
|
|
@@ -7941,6 +8073,7 @@ async function deleteMcpServerConfig(request, reply) {
|
|
|
7941
8073
|
message: "Failed to delete MCP server configuration"
|
|
7942
8074
|
};
|
|
7943
8075
|
}
|
|
8076
|
+
broadcastMcpConfigChanged(tenantId, configKey);
|
|
7944
8077
|
return {
|
|
7945
8078
|
success: true,
|
|
7946
8079
|
message: "MCP server configuration deleted successfully"
|
|
@@ -8015,12 +8148,17 @@ async function listMcpServerTools(request, reply) {
|
|
|
8015
8148
|
message: "MCP server configuration not found"
|
|
8016
8149
|
};
|
|
8017
8150
|
}
|
|
8018
|
-
|
|
8151
|
+
const wasConnected = mcpManager.isServerConnected(tenantId, key);
|
|
8152
|
+
if (!wasConnected) {
|
|
8019
8153
|
await connectAndRegisterTools(config);
|
|
8020
8154
|
}
|
|
8021
|
-
|
|
8155
|
+
const statusStale = config.status !== "connected";
|
|
8156
|
+
if (statusStale) {
|
|
8022
8157
|
await store.updateConfig(tenantId, config.id, { status: "connected" });
|
|
8023
8158
|
}
|
|
8159
|
+
if (!wasConnected || statusStale) {
|
|
8160
|
+
broadcastMcpConfigChanged(tenantId, key);
|
|
8161
|
+
}
|
|
8024
8162
|
const tools = await mcpManager.getServerTools(tenantId, key);
|
|
8025
8163
|
return {
|
|
8026
8164
|
success: true,
|
|
@@ -8055,6 +8193,7 @@ async function connectMcpServer(request, reply) {
|
|
|
8055
8193
|
const updated = await store.updateConfig(tenantId, config.id, {
|
|
8056
8194
|
status: "connected"
|
|
8057
8195
|
});
|
|
8196
|
+
broadcastMcpConfigChanged(tenantId, key);
|
|
8058
8197
|
return {
|
|
8059
8198
|
success: true,
|
|
8060
8199
|
message: "MCP server connected successfully",
|
|
@@ -8068,6 +8207,7 @@ async function connectMcpServer(request, reply) {
|
|
|
8068
8207
|
if (config) {
|
|
8069
8208
|
await store.updateConfig(tenantId, config.id, { status: "error" });
|
|
8070
8209
|
}
|
|
8210
|
+
broadcastMcpConfigChanged(tenantId, key);
|
|
8071
8211
|
return {
|
|
8072
8212
|
success: false,
|
|
8073
8213
|
message: `Failed to connect MCP server: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
@@ -8094,6 +8234,7 @@ async function disconnectMcpServer(request, reply) {
|
|
|
8094
8234
|
const updated = await store.updateConfig(tenantId, config.id, {
|
|
8095
8235
|
status: "disconnected"
|
|
8096
8236
|
});
|
|
8237
|
+
broadcastMcpConfigChanged(tenantId, key);
|
|
8097
8238
|
return {
|
|
8098
8239
|
success: true,
|
|
8099
8240
|
message: "MCP server disconnected successfully",
|
|
@@ -10619,8 +10760,8 @@ function snapshotPlainObject(value) {
|
|
|
10619
10760
|
}
|
|
10620
10761
|
}
|
|
10621
10762
|
async function getInstallationStore() {
|
|
10622
|
-
const { getStoreLattice:
|
|
10623
|
-
const store =
|
|
10763
|
+
const { getStoreLattice: getStoreLattice30 } = await import("@axiom-lattice/core");
|
|
10764
|
+
const store = getStoreLattice30("default", "channelInstallation").store;
|
|
10624
10765
|
if (store) return store;
|
|
10625
10766
|
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
10626
10767
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -12369,6 +12510,10 @@ var ProjectTaskExecutionService = class {
|
|
|
12369
12510
|
input.inputMessageId,
|
|
12370
12511
|
true
|
|
12371
12512
|
);
|
|
12513
|
+
if (resumed.leaseContended) {
|
|
12514
|
+
settlement.cleanup();
|
|
12515
|
+
return { kind: "deferred", task: input.task, thread: input.thread, messageId: input.inputMessageId };
|
|
12516
|
+
}
|
|
12372
12517
|
if (resumed.status === ThreadStatus3.INTERRUPTED && settlement.tryClaim()) {
|
|
12373
12518
|
settlement.cleanup();
|
|
12374
12519
|
const outcome = await this.settleInterruptedEvent(input.task, input.thread, input.inputMessageId);
|
|
@@ -14056,7 +14201,8 @@ import { z as z5, ZodError as ZodError2 } from "zod";
|
|
|
14056
14201
|
|
|
14057
14202
|
// src/services/agent-web-app/AgentWebAppRuntimeService.ts
|
|
14058
14203
|
import { createHash as createHash4, randomUUID as randomUUID14 } from "crypto";
|
|
14059
|
-
import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock } from "@axiom-lattice/protocols";
|
|
14204
|
+
import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock, buildAffinityMissChunk as buildAffinityMissChunk2 } from "@axiom-lattice/protocols";
|
|
14205
|
+
import { getReplicaId as getReplicaId3, getThreadRunOwner as getThreadRunOwner2 } from "@axiom-lattice/core";
|
|
14060
14206
|
function isRecord2(value) {
|
|
14061
14207
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14062
14208
|
}
|
|
@@ -14218,6 +14364,20 @@ var AgentWebAppRuntimeService = class {
|
|
|
14218
14364
|
async resumeStream(webAppId, userId, threadId, messageId, selection) {
|
|
14219
14365
|
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
14220
14366
|
await this.requireOwnedThread(runtime, userId, threadId);
|
|
14367
|
+
if (process.env.AXIOM_REPLICA_ID) {
|
|
14368
|
+
const owner = await getThreadRunOwner2({
|
|
14369
|
+
tenantId: runtime.webApp.tenantId,
|
|
14370
|
+
assistantId: runtime.webApp.assistantId,
|
|
14371
|
+
threadId
|
|
14372
|
+
});
|
|
14373
|
+
if (owner && owner !== getReplicaId3()) {
|
|
14374
|
+
return {
|
|
14375
|
+
stream: (async function* () {
|
|
14376
|
+
yield buildAffinityMissChunk2(owner, threadId);
|
|
14377
|
+
})()
|
|
14378
|
+
};
|
|
14379
|
+
}
|
|
14380
|
+
}
|
|
14221
14381
|
const agent = this.resolveAgent(runtime, threadId);
|
|
14222
14382
|
return { stream: agent.chunkStream(messageId, []) };
|
|
14223
14383
|
}
|
|
@@ -14562,7 +14722,8 @@ async function verifyWebAppToken(token, issuer, options = {}) {
|
|
|
14562
14722
|
}
|
|
14563
14723
|
|
|
14564
14724
|
// src/routes/agent-web-app-runtime.ts
|
|
14565
|
-
import { createAgentWebAppStreamProjectionContext, projectAgentWebAppChunk } from "@axiom-lattice/protocols";
|
|
14725
|
+
import { createAgentWebAppStreamProjectionContext, projectAgentWebAppChunk, AGENT_AFFINITY_HEADER as AGENT_AFFINITY_HEADER2, AFFINITY_ECHO_HEADER as AFFINITY_ECHO_HEADER2, affinityEchoHeaders as affinityEchoHeaders2, REPLICA_HEADER as REPLICA_HEADER2 } from "@axiom-lattice/protocols";
|
|
14726
|
+
import { getReplicaId as getReplicaId4 } from "@axiom-lattice/core";
|
|
14566
14727
|
var idSchema = z5.string().min(1).max(128);
|
|
14567
14728
|
var paramsSchema = z5.object({ webAppId: idSchema }).strict();
|
|
14568
14729
|
var threadParamsSchema = z5.object({ webAppId: idSchema, threadId: idSchema }).strict();
|
|
@@ -14711,7 +14872,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
|
14711
14872
|
const identity = await resolveRuntimeIdentity(request, webAppId, identities);
|
|
14712
14873
|
const input = streamBodySchema.parse(request.body);
|
|
14713
14874
|
const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
|
|
14714
|
-
beginSse(reply);
|
|
14875
|
+
beginSse(reply, request);
|
|
14715
14876
|
await pipeNativeWebAppStream(reply.raw, result.stream);
|
|
14716
14877
|
} catch (error) {
|
|
14717
14878
|
return normalizeError2(reply, error);
|
|
@@ -14723,7 +14884,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
|
14723
14884
|
const identity = await resolveRuntimeIdentity(request, webAppId, identities);
|
|
14724
14885
|
const input = streamBodySchema.parse(request.body);
|
|
14725
14886
|
const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
|
|
14726
|
-
beginSse(reply);
|
|
14887
|
+
beginSse(reply, request);
|
|
14727
14888
|
await pipeAgentWebAppStream(reply.raw, result.stream, result);
|
|
14728
14889
|
} catch (error) {
|
|
14729
14890
|
return normalizeError2(reply, error);
|
|
@@ -14735,7 +14896,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
|
14735
14896
|
const identity = await resolveRuntimeIdentity(request, webAppId, identities);
|
|
14736
14897
|
const { messageId, projectId, modelKey: modelKey2 } = resumeSchema.parse(request.body);
|
|
14737
14898
|
const result = await deps.service.resumeStream(webAppId, identity.userId, threadId, messageId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview));
|
|
14738
|
-
beginSse(reply);
|
|
14899
|
+
beginSse(reply, request);
|
|
14739
14900
|
await pipeNativeWebAppStream(reply.raw, result.stream);
|
|
14740
14901
|
} catch (error) {
|
|
14741
14902
|
return normalizeError2(reply, error);
|
|
@@ -14768,7 +14929,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
|
14768
14929
|
const identity = await resolveRuntimeIdentity(request, webAppId, identities);
|
|
14769
14930
|
const { projectId, modelKey: modelKey2, message, response } = resumeBodySchema.parse(request.body);
|
|
14770
14931
|
const result = await deps.service.resumeInterrupt(webAppId, identity.userId, threadId, interruptId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview), { message, response });
|
|
14771
|
-
beginSse(reply);
|
|
14932
|
+
beginSse(reply, request);
|
|
14772
14933
|
await pipeAgentWebAppStream(reply.raw, result.stream, result);
|
|
14773
14934
|
} catch (error) {
|
|
14774
14935
|
return normalizeError2(reply, error);
|
|
@@ -14996,6 +15157,10 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
|
14996
15157
|
if (!threadId) throw new AgentWebAppRuntimeError("INVALID_REQUEST", 400, "thread_id is required");
|
|
14997
15158
|
const content = body.messages.map((message) => message.content).join("\n");
|
|
14998
15159
|
const { messageId } = await deps.service.enqueueMessage(webAppId, identity.userId, threadId, content, { projectId: body.projectId, modelKey: body.modelKey });
|
|
15160
|
+
reply.header(REPLICA_HEADER2, getReplicaId4());
|
|
15161
|
+
for (const [name, value] of Object.entries(affinityEchoHeaders2(request.headers[AGENT_AFFINITY_HEADER2]))) {
|
|
15162
|
+
reply.header(name, value);
|
|
15163
|
+
}
|
|
14999
15164
|
return reply.code(202).send({ success: true, messageId, queued: true });
|
|
15000
15165
|
} catch (error) {
|
|
15001
15166
|
return normalizeError2(reply, error);
|
|
@@ -15016,7 +15181,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
|
15016
15181
|
const identity = await resolveRuntimeIdentity(request, webAppId, identities);
|
|
15017
15182
|
const body = resumeStreamSchema2.parse(request.body ?? {});
|
|
15018
15183
|
const result = await deps.service.resumeStream(webAppId, identity.userId, body.thread_id, body.message_id, { projectId: body.projectId, modelKey: body.modelKey });
|
|
15019
|
-
beginSse(reply);
|
|
15184
|
+
beginSse(reply, request);
|
|
15020
15185
|
await pipeNativeWebAppStream(reply.raw, result.stream);
|
|
15021
15186
|
} catch (error) {
|
|
15022
15187
|
return normalizeError2(reply, error);
|
|
@@ -15069,14 +15234,17 @@ async function pipeNativeWebAppStream(raw, stream) {
|
|
|
15069
15234
|
}]
|
|
15070
15235
|
});
|
|
15071
15236
|
}
|
|
15072
|
-
function beginSse(reply) {
|
|
15237
|
+
function beginSse(reply, request) {
|
|
15073
15238
|
reply.hijack();
|
|
15074
15239
|
reply.raw.writeHead(200, {
|
|
15075
15240
|
"Content-Type": "text/event-stream; charset=utf-8",
|
|
15076
15241
|
"Cache-Control": "no-cache, no-transform",
|
|
15077
15242
|
Connection: "keep-alive",
|
|
15078
15243
|
"X-Accel-Buffering": "no",
|
|
15079
|
-
"Access-Control-Allow-Origin": "*"
|
|
15244
|
+
"Access-Control-Allow-Origin": "*",
|
|
15245
|
+
"Access-Control-Expose-Headers": `${REPLICA_HEADER2}, ${AFFINITY_ECHO_HEADER2}`,
|
|
15246
|
+
[REPLICA_HEADER2]: getReplicaId4(),
|
|
15247
|
+
...affinityEchoHeaders2(request.headers[AGENT_AFFINITY_HEADER2])
|
|
15080
15248
|
});
|
|
15081
15249
|
}
|
|
15082
15250
|
async function handle2(reply, operation) {
|
|
@@ -15676,6 +15844,17 @@ var ModelProviderServiceError = class extends Error {
|
|
|
15676
15844
|
this.name = "ModelProviderServiceError";
|
|
15677
15845
|
}
|
|
15678
15846
|
};
|
|
15847
|
+
var UPSTREAM_ERROR_CODES = {
|
|
15848
|
+
UPSTREAM_UNREACHABLE: true,
|
|
15849
|
+
UPSTREAM_AUTH_FAILED: true,
|
|
15850
|
+
UPSTREAM_REDIRECT_BLOCKED: true,
|
|
15851
|
+
UPSTREAM_INVALID_RESPONSE: true,
|
|
15852
|
+
UPSTREAM_RATE_LIMITED: true,
|
|
15853
|
+
UPSTREAM_TIMEOUT: true
|
|
15854
|
+
};
|
|
15855
|
+
function isUpstreamErrorCode(code) {
|
|
15856
|
+
return Object.prototype.hasOwnProperty.call(UPSTREAM_ERROR_CODES, code);
|
|
15857
|
+
}
|
|
15679
15858
|
|
|
15680
15859
|
// src/services/model-provider/discovery.ts
|
|
15681
15860
|
var MAX_DISCOVERED_MODELS = 1e3;
|
|
@@ -15951,6 +16130,33 @@ var ModelProviderService = class {
|
|
|
15951
16130
|
models
|
|
15952
16131
|
};
|
|
15953
16132
|
}
|
|
16133
|
+
/**
|
|
16134
|
+
* Hydrates the runtime model registry from the persisted provider catalog.
|
|
16135
|
+
*
|
|
16136
|
+
* @returns The model keys registered in this process.
|
|
16137
|
+
* @remarks Registers the Phase-1 (default tenant) enabled provider models so a
|
|
16138
|
+
* replica that did not perform discovery — or that restarted — can execute
|
|
16139
|
+
* them. Best-effort; the caller logs failures.
|
|
16140
|
+
*/
|
|
16141
|
+
async hydrateRegistry() {
|
|
16142
|
+
const tenantId = PHASE1_REGISTRY_TENANT;
|
|
16143
|
+
if (!this.shouldRegister(tenantId)) {
|
|
16144
|
+
return [];
|
|
16145
|
+
}
|
|
16146
|
+
const providers = await this.deps.providerStore.listProviders(tenantId);
|
|
16147
|
+
const registered = [];
|
|
16148
|
+
for (const provider of providers) {
|
|
16149
|
+
if (!provider.enabled) {
|
|
16150
|
+
continue;
|
|
16151
|
+
}
|
|
16152
|
+
const models = await this.deps.providerStore.listModels(
|
|
16153
|
+
tenantId,
|
|
16154
|
+
provider.id
|
|
16155
|
+
);
|
|
16156
|
+
registered.push(...this.syncRegistry(tenantId, provider, models));
|
|
16157
|
+
}
|
|
16158
|
+
return registered;
|
|
16159
|
+
}
|
|
15954
16160
|
/**
|
|
15955
16161
|
* Create a provider configuration. Does not run discovery.
|
|
15956
16162
|
*/
|
|
@@ -16190,7 +16396,7 @@ var ModelProviderService = class {
|
|
|
16190
16396
|
models
|
|
16191
16397
|
};
|
|
16192
16398
|
} catch (error) {
|
|
16193
|
-
if (error instanceof ModelProviderServiceError) {
|
|
16399
|
+
if (error instanceof ModelProviderServiceError && isUpstreamErrorCode(error.code)) {
|
|
16194
16400
|
return {
|
|
16195
16401
|
reachable: false,
|
|
16196
16402
|
latencyMs: Date.now() - startedAt,
|
|
@@ -16606,6 +16812,10 @@ var ModelProviderController = class {
|
|
|
16606
16812
|
return sendError2(reply, error);
|
|
16607
16813
|
}
|
|
16608
16814
|
}
|
|
16815
|
+
/** Hydrates the runtime model registry from persisted providers (startup). */
|
|
16816
|
+
async hydrateRegistry() {
|
|
16817
|
+
return this.service.hydrateRegistry();
|
|
16818
|
+
}
|
|
16609
16819
|
/** GET /api/model-providers */
|
|
16610
16820
|
async listProviders(request, reply) {
|
|
16611
16821
|
try {
|
|
@@ -19174,6 +19384,8 @@ function createRoomMessageRetentionJob(input) {
|
|
|
19174
19384
|
|
|
19175
19385
|
// src/services/collaboration/ProjectTaskExecutionMutex.ts
|
|
19176
19386
|
import { snapshotExactRecord as snapshotExactRecord8 } from "@axiom-lattice/protocols";
|
|
19387
|
+
import { getCoordinationLeaseStore, getReplicaId as getReplicaId5, withLease } from "@axiom-lattice/core";
|
|
19388
|
+
var MACHINE_LEASE_OPTIONS = { ttlMs: 6e4, acquireTimeoutMs: 9e4, retryDelayMs: 100 };
|
|
19177
19389
|
function machineKey(scope) {
|
|
19178
19390
|
const snapshot = snapshotExactRecord8(scope, ["tenantId", "assistantId", "projectId"]);
|
|
19179
19391
|
if (!snapshot) throw new Error("Invalid Project task machine scope");
|
|
@@ -19197,9 +19409,12 @@ var ProjectTaskExecutionMutex = class {
|
|
|
19197
19409
|
*/
|
|
19198
19410
|
async runExclusive(scope, operation, signal) {
|
|
19199
19411
|
const key = machineKey(scope);
|
|
19412
|
+
const leaseKey = `project-task-machine:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.assistantId)}:${encodeURIComponent(scope.projectId)}`;
|
|
19200
19413
|
await this.acquire(key, signal);
|
|
19201
19414
|
try {
|
|
19202
|
-
|
|
19415
|
+
const store = getCoordinationLeaseStore();
|
|
19416
|
+
if (!store) return await operation();
|
|
19417
|
+
return await withLease(store, leaseKey, getReplicaId5(), operation, MACHINE_LEASE_OPTIONS);
|
|
19203
19418
|
} finally {
|
|
19204
19419
|
this.release(key);
|
|
19205
19420
|
}
|
|
@@ -19258,6 +19473,20 @@ import {
|
|
|
19258
19473
|
import {
|
|
19259
19474
|
snapshotExactRecord as snapshotExactRecord9
|
|
19260
19475
|
} from "@axiom-lattice/protocols";
|
|
19476
|
+
|
|
19477
|
+
// src/services/collaboration/projectTaskDispatchMarker.ts
|
|
19478
|
+
var EXECUTION_REQUESTED_AT_KEY = "execution_requested_at";
|
|
19479
|
+
function hasExecutionRequest(task) {
|
|
19480
|
+
const value = task.context?.[EXECUTION_REQUESTED_AT_KEY];
|
|
19481
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
19482
|
+
const parsed = new Date(value);
|
|
19483
|
+
return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
|
|
19484
|
+
}
|
|
19485
|
+
function withExecutionRequest(context, nowIso) {
|
|
19486
|
+
return { ...context ?? {}, [EXECUTION_REQUESTED_AT_KEY]: nowIso };
|
|
19487
|
+
}
|
|
19488
|
+
|
|
19489
|
+
// src/services/collaboration/ProjectTaskObserver.ts
|
|
19261
19490
|
var SYSTEM_ACTOR2 = "system";
|
|
19262
19491
|
var TERMINAL_ACTIONS = ["completed", "failed", "interrupted", "cancelled"];
|
|
19263
19492
|
var ProjectTaskObserver = class {
|
|
@@ -19461,7 +19690,7 @@ var ProjectTaskObserver = class {
|
|
|
19461
19690
|
return;
|
|
19462
19691
|
}
|
|
19463
19692
|
}
|
|
19464
|
-
if (intent === "dispatch" && await this.isReady(task) && !signal?.aborted && !this.disposed) {
|
|
19693
|
+
if ((intent === "dispatch" || hasExecutionRequest(task)) && await this.isReady(task) && !signal?.aborted && !this.disposed) {
|
|
19465
19694
|
await this.runStart(task, signal);
|
|
19466
19695
|
}
|
|
19467
19696
|
return;
|
|
@@ -19578,7 +19807,7 @@ var ProjectTaskObserver = class {
|
|
|
19578
19807
|
}, signal);
|
|
19579
19808
|
}
|
|
19580
19809
|
async mapOutcome(outcome) {
|
|
19581
|
-
if (outcome.kind === "aborted") return;
|
|
19810
|
+
if (outcome.kind === "aborted" || outcome.kind === "deferred") return;
|
|
19582
19811
|
const current = snapshotTaskItem(await this.deps.tasks.getById(outcome.task.tenantId, outcome.task.id));
|
|
19583
19812
|
if (!current || current.ownerId !== outcome.thread.assistantId || current.context?.thread_id !== outcome.thread.id) return;
|
|
19584
19813
|
if (isTerminal(current.status) || current.status === "review") return;
|
|
@@ -20510,6 +20739,7 @@ var DEFAULT_MAX_EVENT_AGE_MS = 10 * 60 * 1e3;
|
|
|
20510
20739
|
var DEFAULT_MAX_ROOMS = 1e3;
|
|
20511
20740
|
var DEFAULT_MAX_GLOBAL_EVENTS = 1e5;
|
|
20512
20741
|
var DEFAULT_MAX_GLOBAL_BYTES = 64 * 1024 * 1024;
|
|
20742
|
+
var DEFAULT_SOURCE_KEY_LIMIT = 2e3;
|
|
20513
20743
|
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}$/;
|
|
20514
20744
|
var ProjectRoomBrokerResourceCapacityError = class extends Error {
|
|
20515
20745
|
constructor(resource) {
|
|
@@ -20523,6 +20753,7 @@ var ProjectRoomEventBroker = class {
|
|
|
20523
20753
|
constructor(options = {}) {
|
|
20524
20754
|
this.rooms = /* @__PURE__ */ new Map();
|
|
20525
20755
|
this.sequenceScopes = /* @__PURE__ */ new Map();
|
|
20756
|
+
this.seenSourceKeys = /* @__PURE__ */ new Map();
|
|
20526
20757
|
this.eventCount = 0;
|
|
20527
20758
|
this.byteCount = 0;
|
|
20528
20759
|
this.subscriberCount = 0;
|
|
@@ -20550,6 +20781,10 @@ var ProjectRoomEventBroker = class {
|
|
|
20550
20781
|
options.maxGlobalBytes ?? DEFAULT_MAX_GLOBAL_BYTES,
|
|
20551
20782
|
"maxGlobalBytes"
|
|
20552
20783
|
);
|
|
20784
|
+
this.sourceKeyLimit = positiveSafeInteger(
|
|
20785
|
+
options.sourceKeyLimit ?? DEFAULT_SOURCE_KEY_LIMIT,
|
|
20786
|
+
"sourceKeyLimit"
|
|
20787
|
+
);
|
|
20553
20788
|
this.sequence = nonnegativeSafeInteger(options.initialSequence ?? 0, "initialSequence");
|
|
20554
20789
|
this.log = options.log ?? { warn: () => void 0 };
|
|
20555
20790
|
}
|
|
@@ -20561,8 +20796,22 @@ var ProjectRoomEventBroker = class {
|
|
|
20561
20796
|
get retainedBytes() {
|
|
20562
20797
|
return this.byteCount;
|
|
20563
20798
|
}
|
|
20564
|
-
/**
|
|
20565
|
-
|
|
20799
|
+
/**
|
|
20800
|
+
* Installs (or clears) a hook invoked after a locally published event is stored
|
|
20801
|
+
* and queued to local subscribers. Used to mirror events to other replicas via
|
|
20802
|
+
* a shared transport. Best-effort: a throwing hook never affects `publish`.
|
|
20803
|
+
*/
|
|
20804
|
+
setBroadcastHook(hook) {
|
|
20805
|
+
this.broadcast = hook;
|
|
20806
|
+
}
|
|
20807
|
+
/**
|
|
20808
|
+
* Atomically stores an immutable event before queueing detached listener callbacks.
|
|
20809
|
+
*
|
|
20810
|
+
* @param sourceKey Optional caller-owned fact key; when present it is retained
|
|
20811
|
+
* for bounded cross-replica duplicate suppression so a later foreign copy of the
|
|
20812
|
+
* same fact (or a local publish) is dropped by {@link ingestForeignEvent}.
|
|
20813
|
+
*/
|
|
20814
|
+
publish(scope, draft, sourceKey) {
|
|
20566
20815
|
this.assertOpen();
|
|
20567
20816
|
const canonicalScope = snapshotScope(scope);
|
|
20568
20817
|
const key = scopeKey2(canonicalScope);
|
|
@@ -20597,9 +20846,52 @@ var ProjectRoomEventBroker = class {
|
|
|
20597
20846
|
this.sequence = nextSequence;
|
|
20598
20847
|
this.eventCount += 1;
|
|
20599
20848
|
this.byteCount += bytes;
|
|
20849
|
+
if (sourceKey !== void 0) this.rememberSourceKey(sourceKey);
|
|
20600
20850
|
for (const subscription of [...room.subscriptions]) this.queueCallback(subscription, serialized);
|
|
20851
|
+
if (this.broadcast) {
|
|
20852
|
+
try {
|
|
20853
|
+
this.broadcast(canonicalScope, serialized, sourceKey);
|
|
20854
|
+
} catch {
|
|
20855
|
+
}
|
|
20856
|
+
}
|
|
20601
20857
|
return parseScopedEvent(serialized);
|
|
20602
20858
|
}
|
|
20859
|
+
/**
|
|
20860
|
+
* Delivers one event published by ANOTHER replica to this process's local
|
|
20861
|
+
* subscribers for its room, without adding it to the local replay ring or
|
|
20862
|
+
* consuming a local sequence (its id carries the origin replica's epoch).
|
|
20863
|
+
*
|
|
20864
|
+
* @param serializedEvent A serialized scoped business event produced by a peer.
|
|
20865
|
+
* @param sourceKey Optional caller-owned fact key shared by every replica's copy
|
|
20866
|
+
* of the same committed fact. When it was already seen (locally published or
|
|
20867
|
+
* ingested from a peer) the event is dropped to avoid N× duplicate delivery.
|
|
20868
|
+
* @returns True when it was queued to at least one local subscription.
|
|
20869
|
+
* @remarks Source-key deduplication is process-local and bounded, so it caps
|
|
20870
|
+
* duplicate fan-out within the retention window without replacing REST, which
|
|
20871
|
+
* remains the authority. A malformed payload or a room with no local
|
|
20872
|
+
* subscribers is ignored.
|
|
20873
|
+
*/
|
|
20874
|
+
ingestForeignEvent(serializedEvent, sourceKey) {
|
|
20875
|
+
this.assertOpen();
|
|
20876
|
+
if (sourceKey !== void 0 && this.seenSourceKeys.has(sourceKey)) return false;
|
|
20877
|
+
let key;
|
|
20878
|
+
try {
|
|
20879
|
+
const event = parseScopedEvent(serializedEvent);
|
|
20880
|
+
key = scopeKey2(snapshotScope(event.scope));
|
|
20881
|
+
} catch {
|
|
20882
|
+
return false;
|
|
20883
|
+
}
|
|
20884
|
+
if (sourceKey !== void 0) this.rememberSourceKey(sourceKey);
|
|
20885
|
+
const room = this.rooms.get(key);
|
|
20886
|
+
if (!room || room.subscriptions.size === 0) return false;
|
|
20887
|
+
this.touch(room);
|
|
20888
|
+
let queued = false;
|
|
20889
|
+
for (const subscription of [...room.subscriptions]) {
|
|
20890
|
+
this.queueCallback(subscription, serializedEvent);
|
|
20891
|
+
queued = true;
|
|
20892
|
+
}
|
|
20893
|
+
return queued;
|
|
20894
|
+
}
|
|
20603
20895
|
/** Atomically resolves replay and registers a distinct subscription for live events. */
|
|
20604
20896
|
subscribe(scope, afterEventId, listener) {
|
|
20605
20897
|
this.assertOpen();
|
|
@@ -20639,6 +20931,7 @@ var ProjectRoomEventBroker = class {
|
|
|
20639
20931
|
}
|
|
20640
20932
|
this.rooms.clear();
|
|
20641
20933
|
this.sequenceScopes.clear();
|
|
20934
|
+
this.seenSourceKeys.clear();
|
|
20642
20935
|
this.eventCount = 0;
|
|
20643
20936
|
this.byteCount = 0;
|
|
20644
20937
|
this.subscriberCount = 0;
|
|
@@ -20773,6 +21066,14 @@ var ProjectRoomEventBroker = class {
|
|
|
20773
21066
|
this.usageClock += 1;
|
|
20774
21067
|
room.lastUsed = this.usageClock;
|
|
20775
21068
|
}
|
|
21069
|
+
rememberSourceKey(key) {
|
|
21070
|
+
this.seenSourceKeys.set(key, true);
|
|
21071
|
+
while (this.seenSourceKeys.size > this.sourceKeyLimit) {
|
|
21072
|
+
const oldest = this.seenSourceKeys.keys().next().value;
|
|
21073
|
+
if (oldest === void 0) break;
|
|
21074
|
+
this.seenSourceKeys.delete(oldest);
|
|
21075
|
+
}
|
|
21076
|
+
}
|
|
20776
21077
|
};
|
|
20777
21078
|
function positiveSafeInteger(value, name) {
|
|
20778
21079
|
if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError(`${name} must be a positive safe integer`);
|
|
@@ -20970,7 +21271,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
20970
21271
|
if (!event) throw new Error("Invalid Project Room realtime projection");
|
|
20971
21272
|
const dedupeKey = createHash9("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
|
|
20972
21273
|
if (this.sourceKeys.has(dedupeKey)) return;
|
|
20973
|
-
this.deps.broker.publish(event.scope, event.draft);
|
|
21274
|
+
this.deps.broker.publish(event.scope, event.draft, event.sourceKey);
|
|
20974
21275
|
this.sourceKeys.set(dedupeKey, true);
|
|
20975
21276
|
while (this.sourceKeys.size > this.dedupeLimit) {
|
|
20976
21277
|
const oldest = this.sourceKeys.keys().next().value;
|
|
@@ -22374,7 +22675,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
22374
22675
|
bundleStore: getStoreLattice17("default", "capabilityBundle").store,
|
|
22375
22676
|
projectStore
|
|
22376
22677
|
}, { authRequired: process.env.AUTH_REQUIRED === "true" }));
|
|
22377
|
-
|
|
22678
|
+
const modelProviderController = new ModelProviderController({
|
|
22378
22679
|
providerStore: getStoreLattice17("default", "modelProvider").store,
|
|
22379
22680
|
userTenantLinkStore,
|
|
22380
22681
|
registry: {
|
|
@@ -22383,7 +22684,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
22383
22684
|
remove: (tenantId, modelKey2) => modelLatticeManager3.removeLatticeWithTenant(tenantId, modelKey2),
|
|
22384
22685
|
listRegistered: (tenantId) => modelLatticeManager3.getLatticeKeysByTenant(tenantId)
|
|
22385
22686
|
}
|
|
22386
|
-
}, { authRequired: process.env.AUTH_REQUIRED === "true" })
|
|
22687
|
+
}, { authRequired: process.env.AUTH_REQUIRED === "true" });
|
|
22688
|
+
registerModelProviderRoutes(app2, modelProviderController);
|
|
22689
|
+
void modelProviderController.hydrateRegistry().catch(() => void 0);
|
|
22387
22690
|
registerAgentWebAppRoutes(app2, {
|
|
22388
22691
|
service: new AgentWebAppService({
|
|
22389
22692
|
webAppStore,
|
|
@@ -22559,13 +22862,17 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
22559
22862
|
}) : void 0;
|
|
22560
22863
|
const admitted = transferred ?? task;
|
|
22561
22864
|
if (!admitted.workspaceId || !admitted.projectId) return;
|
|
22865
|
+
const markerUpdated = await taskStore.update(admitted.tenantId, admitted.id, {
|
|
22866
|
+
context: withExecutionRequest(admitted.context, (/* @__PURE__ */ new Date()).toISOString())
|
|
22867
|
+
}).catch(() => null);
|
|
22868
|
+
const eventTask = markerUpdated ?? admitted;
|
|
22562
22869
|
void projectTaskAdmissionNotifier.publish({
|
|
22563
|
-
tenantId:
|
|
22564
|
-
workspaceId:
|
|
22565
|
-
projectId:
|
|
22566
|
-
taskId:
|
|
22567
|
-
ownerId:
|
|
22568
|
-
updatedAt:
|
|
22870
|
+
tenantId: eventTask.tenantId,
|
|
22871
|
+
workspaceId: eventTask.workspaceId,
|
|
22872
|
+
projectId: eventTask.projectId,
|
|
22873
|
+
taskId: eventTask.id,
|
|
22874
|
+
ownerId: eventTask.ownerId,
|
|
22875
|
+
updatedAt: eventTask.updatedAt.toISOString()
|
|
22569
22876
|
});
|
|
22570
22877
|
}
|
|
22571
22878
|
});
|
|
@@ -24800,13 +25107,25 @@ import {
|
|
|
24800
25107
|
getLoggerLattice,
|
|
24801
25108
|
loggerLatticeManager,
|
|
24802
25109
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
24803
|
-
getStoreLattice as
|
|
25110
|
+
getStoreLattice as getStoreLattice29,
|
|
24804
25111
|
agentInstanceManager as agentInstanceManager9,
|
|
24805
25112
|
createSandboxProvider,
|
|
24806
25113
|
TokenCache,
|
|
24807
25114
|
mcpManager as mcpManager3,
|
|
24808
25115
|
convertToConnection as convertToConnection2,
|
|
24809
|
-
createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2
|
|
25116
|
+
createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2,
|
|
25117
|
+
runAsLeader,
|
|
25118
|
+
configureCoordinationLeaseStore,
|
|
25119
|
+
getReplicaId as getReplicaId6,
|
|
25120
|
+
taskLifecycleNotifier as taskLifecycleNotifier3,
|
|
25121
|
+
projectTaskAdmissionNotifier as projectTaskAdmissionNotifier2,
|
|
25122
|
+
scheduleLatticeManager as scheduleLatticeManager2,
|
|
25123
|
+
OutboxPoller,
|
|
25124
|
+
eventBus as eventBus4,
|
|
25125
|
+
agentLatticeManager as agentLatticeManager5,
|
|
25126
|
+
configurePluginCatalogSync,
|
|
25127
|
+
PLUGIN_CATALOG_CHANGED_TOPIC,
|
|
25128
|
+
getSandboxPluginCatalog as getSandboxPluginCatalog4
|
|
24810
25129
|
} from "@axiom-lattice/core";
|
|
24811
25130
|
|
|
24812
25131
|
// src/channels/ChannelInfrastructure.ts
|
|
@@ -25475,9 +25794,167 @@ async function prepareProjectTaskRuntime(runtime, log2) {
|
|
|
25475
25794
|
}
|
|
25476
25795
|
}
|
|
25477
25796
|
|
|
25797
|
+
// src/services/multiReplicaReadiness.ts
|
|
25798
|
+
import { getStoreLattice as getStoreLattice28 } from "@axiom-lattice/core";
|
|
25799
|
+
var SHARED_STORE_KINDS = ["lease", "outbox", "threadMessageQueue", "task", "taskWorkItem"];
|
|
25800
|
+
function storeClassName(kind) {
|
|
25801
|
+
try {
|
|
25802
|
+
return getStoreLattice28("default", kind).store.constructor?.name;
|
|
25803
|
+
} catch {
|
|
25804
|
+
return void 0;
|
|
25805
|
+
}
|
|
25806
|
+
}
|
|
25807
|
+
function warnMultiReplicaReadiness(logger7) {
|
|
25808
|
+
if (typeof process.env.AXIOM_REPLICA_ID !== "string" || process.env.AXIOM_REPLICA_ID.trim() === "") return;
|
|
25809
|
+
for (const kind of SHARED_STORE_KINDS) {
|
|
25810
|
+
const name = storeClassName(kind);
|
|
25811
|
+
if (name === void 0 || !name.startsWith("InMemory")) continue;
|
|
25812
|
+
logger7.warn(`Multi-replica: store '${kind}' is ${name} (not shared); configure a shared store`, {
|
|
25813
|
+
phase: "multi_replica_readiness",
|
|
25814
|
+
store: kind
|
|
25815
|
+
});
|
|
25816
|
+
}
|
|
25817
|
+
if (!process.env.AUTH_TOKEN_SECRET?.trim() && !process.env.JWT_SECRET?.trim()) {
|
|
25818
|
+
logger7.warn(
|
|
25819
|
+
"Multi-replica: AUTH_TOKEN_SECRET/JWT_SECRET unset; console sessions are signed per-process and will not verify across replicas",
|
|
25820
|
+
{ phase: "multi_replica_readiness", env: "AUTH_TOKEN_SECRET" }
|
|
25821
|
+
);
|
|
25822
|
+
}
|
|
25823
|
+
if (!process.env.LATTICE_ENCRYPTION_KEY?.trim()) {
|
|
25824
|
+
logger7.warn(
|
|
25825
|
+
"Multi-replica: LATTICE_ENCRYPTION_KEY unset; stored credentials use the shared dev default and may not decrypt across differing keys",
|
|
25826
|
+
{ phase: "multi_replica_readiness", env: "LATTICE_ENCRYPTION_KEY" }
|
|
25827
|
+
);
|
|
25828
|
+
}
|
|
25829
|
+
logger7.warn(
|
|
25830
|
+
"Multi-replica: agent run SSE and resume_stream are process-local; route a thread's follow-up runs to the replica streaming it (sticky routing) or share the stream/queue state",
|
|
25831
|
+
{ phase: "multi_replica_readiness", feature: "agent-run-sse" }
|
|
25832
|
+
);
|
|
25833
|
+
}
|
|
25834
|
+
|
|
25835
|
+
// src/services/OutboxRetentionJob.ts
|
|
25836
|
+
var HOUR_MS = 60 * 60 * 1e3;
|
|
25837
|
+
var DEFAULT_INTERVAL_MS2 = 6 * HOUR_MS;
|
|
25838
|
+
var DEFAULT_INITIAL_DELAY_MS2 = 6e4;
|
|
25839
|
+
var AXIOM_OUTBOX_RETENTION_HOURS_ENV = "AXIOM_OUTBOX_RETENTION_HOURS";
|
|
25840
|
+
var DEFAULT_RETENTION_HOURS = 24;
|
|
25841
|
+
function parseRetentionHours(value) {
|
|
25842
|
+
if (value === void 0 || value === null) return DEFAULT_RETENTION_HOURS;
|
|
25843
|
+
if (typeof value !== "string") return void 0;
|
|
25844
|
+
const trimmed = value.trim();
|
|
25845
|
+
if (trimmed.length === 0) return DEFAULT_RETENTION_HOURS;
|
|
25846
|
+
const parsed = Number(trimmed);
|
|
25847
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
25848
|
+
}
|
|
25849
|
+
var OutboxRetentionJob = class {
|
|
25850
|
+
/** @param deps Outbox slice, logger, and optional retention/interval bounds. */
|
|
25851
|
+
constructor(deps) {
|
|
25852
|
+
this.deps = deps;
|
|
25853
|
+
this.running = false;
|
|
25854
|
+
this.stopped = false;
|
|
25855
|
+
}
|
|
25856
|
+
/** Schedules one delayed sweep plus the recurring interval (both unref'd). */
|
|
25857
|
+
start() {
|
|
25858
|
+
if (this.stopped || this.deps.retentionMs === void 0) return;
|
|
25859
|
+
if (this.initial !== void 0 || this.interval !== void 0) return;
|
|
25860
|
+
this.initial = setTimeout(() => {
|
|
25861
|
+
this.initial = void 0;
|
|
25862
|
+
void this.runOnce();
|
|
25863
|
+
}, this.deps.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS2);
|
|
25864
|
+
this.initial.unref?.();
|
|
25865
|
+
this.interval = setInterval(() => void this.runOnce(), this.deps.intervalMs ?? DEFAULT_INTERVAL_MS2);
|
|
25866
|
+
this.interval.unref?.();
|
|
25867
|
+
}
|
|
25868
|
+
/**
|
|
25869
|
+
* Runs one age-based prune.
|
|
25870
|
+
*
|
|
25871
|
+
* @returns The number of rows removed (0 when disabled, overlapping, or on failure).
|
|
25872
|
+
*/
|
|
25873
|
+
async runOnce() {
|
|
25874
|
+
const retentionMs = this.deps.retentionMs;
|
|
25875
|
+
if (this.stopped || retentionMs === void 0 || this.running) return 0;
|
|
25876
|
+
this.running = true;
|
|
25877
|
+
try {
|
|
25878
|
+
const removed = await this.deps.outbox.pruneOlderThan(retentionMs);
|
|
25879
|
+
if (removed > 0) this.deps.log.info("outbox pruned", { removed });
|
|
25880
|
+
return removed;
|
|
25881
|
+
} catch (error) {
|
|
25882
|
+
this.deps.log.warn("outbox prune failed", {
|
|
25883
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25884
|
+
});
|
|
25885
|
+
return 0;
|
|
25886
|
+
} finally {
|
|
25887
|
+
this.running = false;
|
|
25888
|
+
}
|
|
25889
|
+
}
|
|
25890
|
+
/** Stops the timers; an in-flight sweep finishes on its own. */
|
|
25891
|
+
stop() {
|
|
25892
|
+
this.stopped = true;
|
|
25893
|
+
if (this.initial !== void 0) {
|
|
25894
|
+
clearTimeout(this.initial);
|
|
25895
|
+
this.initial = void 0;
|
|
25896
|
+
}
|
|
25897
|
+
if (this.interval !== void 0) {
|
|
25898
|
+
clearInterval(this.interval);
|
|
25899
|
+
this.interval = void 0;
|
|
25900
|
+
}
|
|
25901
|
+
}
|
|
25902
|
+
};
|
|
25903
|
+
function createOutboxRetentionJob(deps) {
|
|
25904
|
+
const retentionMs = deps.retentionMs ?? resolveRetentionMs(process.env[AXIOM_OUTBOX_RETENTION_HOURS_ENV]);
|
|
25905
|
+
return new OutboxRetentionJob({ ...deps, retentionMs });
|
|
25906
|
+
}
|
|
25907
|
+
function resolveRetentionMs(value) {
|
|
25908
|
+
const hours = parseRetentionHours(value);
|
|
25909
|
+
return hours === void 0 ? void 0 : hours * HOUR_MS;
|
|
25910
|
+
}
|
|
25911
|
+
|
|
25912
|
+
// src/services/collaboration/roomEventSync.ts
|
|
25913
|
+
var ROOM_EVENT_TOPIC = "room-event";
|
|
25914
|
+
async function publishRoomEvent(outbox, owner, scope, serialized, sourceKey) {
|
|
25915
|
+
try {
|
|
25916
|
+
await outbox.append({
|
|
25917
|
+
topic: ROOM_EVENT_TOPIC,
|
|
25918
|
+
payload: { scope, serialized, sourceKey },
|
|
25919
|
+
originReplicaId: owner
|
|
25920
|
+
});
|
|
25921
|
+
} catch {
|
|
25922
|
+
}
|
|
25923
|
+
}
|
|
25924
|
+
function ingestRoomEvent(payload, broker) {
|
|
25925
|
+
const record = payload;
|
|
25926
|
+
const serialized = record?.serialized;
|
|
25927
|
+
if (typeof serialized !== "string" || serialized.length === 0) return false;
|
|
25928
|
+
const sourceKey = record?.sourceKey;
|
|
25929
|
+
if (typeof sourceKey === "string" && sourceKey.length > 0) {
|
|
25930
|
+
return broker.ingestForeignEvent(serialized, sourceKey);
|
|
25931
|
+
}
|
|
25932
|
+
return broker.ingestForeignEvent(serialized);
|
|
25933
|
+
}
|
|
25934
|
+
|
|
25935
|
+
// src/services/assistantRegistrySync.ts
|
|
25936
|
+
var ASSISTANT_CHANGE_EVENTS = ["assistant:created", "assistant:updated", "assistant:deleted"];
|
|
25937
|
+
var ASSISTANT_CHANGED_TOPIC = "assistant-changed";
|
|
25938
|
+
function bridgeAssistantEvents(subscribe, outbox, owner) {
|
|
25939
|
+
for (const event of ASSISTANT_CHANGE_EVENTS) {
|
|
25940
|
+
subscribe(event, (data) => {
|
|
25941
|
+
const tenantId = data?.tenantId;
|
|
25942
|
+
if (typeof tenantId !== "string" || tenantId.length === 0) return;
|
|
25943
|
+
void outbox.append({ topic: ASSISTANT_CHANGED_TOPIC, payload: { tenantId }, originReplicaId: owner }).catch(() => void 0);
|
|
25944
|
+
});
|
|
25945
|
+
}
|
|
25946
|
+
}
|
|
25947
|
+
async function handleAssistantChanged(payload, reload) {
|
|
25948
|
+
const tenantId = payload?.tenantId;
|
|
25949
|
+
if (typeof tenantId !== "string" || tenantId.length === 0) return;
|
|
25950
|
+
await reload(tenantId);
|
|
25951
|
+
}
|
|
25952
|
+
|
|
25478
25953
|
// src/index.ts
|
|
25479
25954
|
import {
|
|
25480
|
-
LoggerType
|
|
25955
|
+
LoggerType,
|
|
25956
|
+
REPLICA_HEADER as REPLICA_HEADER3,
|
|
25957
|
+
AFFINITY_ECHO_HEADER as AFFINITY_ECHO_HEADER3
|
|
25481
25958
|
} from "@axiom-lattice/protocols";
|
|
25482
25959
|
process.on("unhandledRejection", (reason, promise) => {
|
|
25483
25960
|
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
|
|
@@ -25557,7 +26034,7 @@ app.register(cors, {
|
|
|
25557
26034
|
origin: true,
|
|
25558
26035
|
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
|
25559
26036
|
allowedHeaders: "*",
|
|
25560
|
-
exposedHeaders: ["Content-Type", "Content-Disposition"],
|
|
26037
|
+
exposedHeaders: ["Content-Type", "Content-Disposition", REPLICA_HEADER3, AFFINITY_ECHO_HEADER3],
|
|
25561
26038
|
credentials: true
|
|
25562
26039
|
});
|
|
25563
26040
|
app.register(sensible);
|
|
@@ -25609,7 +26086,7 @@ function getConfiguredSandboxProvider() {
|
|
|
25609
26086
|
}
|
|
25610
26087
|
async function restoreMcpConnections() {
|
|
25611
26088
|
try {
|
|
25612
|
-
const storeLattice =
|
|
26089
|
+
const storeLattice = getStoreLattice29("default", "mcp");
|
|
25613
26090
|
const store = storeLattice.store;
|
|
25614
26091
|
if (!store) {
|
|
25615
26092
|
logger6.info("MCP store not configured, skipping connection restoration");
|
|
@@ -25620,25 +26097,16 @@ async function restoreMcpConnections() {
|
|
|
25620
26097
|
);
|
|
25621
26098
|
if (configs.length === 0) return;
|
|
25622
26099
|
logger6.info(`Restoring ${configs.length} MCP server connections...`);
|
|
25623
|
-
|
|
25624
|
-
|
|
25625
|
-
|
|
25626
|
-
|
|
25627
|
-
|
|
25628
|
-
maxAttempts: config.config.retryAttempts,
|
|
25629
|
-
selectedTools: config.selectedTools
|
|
25630
|
-
});
|
|
25631
|
-
} catch (err) {
|
|
25632
|
-
logger6.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
|
|
25633
|
-
}
|
|
25634
|
-
}
|
|
25635
|
-
await mcpManager3.connectAll();
|
|
26100
|
+
const deps = {
|
|
26101
|
+
store,
|
|
26102
|
+
manager: mcpManager3,
|
|
26103
|
+
convertToConnection: convertToConnection2
|
|
26104
|
+
};
|
|
25636
26105
|
const results = await Promise.allSettled(
|
|
25637
26106
|
configs.map(
|
|
25638
|
-
(config) =>
|
|
25639
|
-
config.tenantId,
|
|
25640
|
-
|
|
25641
|
-
config.selectedTools
|
|
26107
|
+
(config) => reconcileMcpConfigChange(
|
|
26108
|
+
{ tenantId: config.tenantId, serverKey: config.key },
|
|
26109
|
+
deps
|
|
25642
26110
|
)
|
|
25643
26111
|
)
|
|
25644
26112
|
);
|
|
@@ -25646,7 +26114,7 @@ async function restoreMcpConnections() {
|
|
|
25646
26114
|
if (result.status === "rejected") {
|
|
25647
26115
|
const config = configs[index];
|
|
25648
26116
|
logger6.warn(
|
|
25649
|
-
`Failed to
|
|
26117
|
+
`Failed to restore MCP server "${config.tenantId}/${config.key}"`,
|
|
25650
26118
|
{ error: result.reason }
|
|
25651
26119
|
);
|
|
25652
26120
|
}
|
|
@@ -25673,9 +26141,9 @@ var start = async (config) => {
|
|
|
25673
26141
|
let projectRoomRealtimeRuntime;
|
|
25674
26142
|
if (projectRoomRealtimeMode === "single-gateway") {
|
|
25675
26143
|
projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
|
|
25676
|
-
tasks:
|
|
25677
|
-
rooms:
|
|
25678
|
-
memberships:
|
|
26144
|
+
tasks: getStoreLattice29("default", "task").store,
|
|
26145
|
+
rooms: getStoreLattice29("default", "projectRoom").store,
|
|
26146
|
+
memberships: getStoreLattice29("default", "projectBotMembership").store,
|
|
25679
26147
|
log: { warn: (message, context) => logger6.warn(message, context) }
|
|
25680
26148
|
});
|
|
25681
26149
|
}
|
|
@@ -25718,7 +26186,7 @@ var start = async (config) => {
|
|
|
25718
26186
|
}
|
|
25719
26187
|
setEvalRunService(evalRunner);
|
|
25720
26188
|
try {
|
|
25721
|
-
const menuStore =
|
|
26189
|
+
const menuStore = getStoreLattice29("default", "menu").store;
|
|
25722
26190
|
setMenuRegistry(menuStore);
|
|
25723
26191
|
logger6.info("Menu registry initialized");
|
|
25724
26192
|
} catch {
|
|
@@ -25736,10 +26204,10 @@ var start = async (config) => {
|
|
|
25736
26204
|
const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-7UGQ7GMW.mjs");
|
|
25737
26205
|
const { registerA2AStandardRoutes } = await import("./a2a-standard-PBYPNHOA.mjs");
|
|
25738
26206
|
const a2a = await import("./a2a-FGYX2HH3.mjs");
|
|
25739
|
-
const a2aKeyStore =
|
|
25740
|
-
const assistantStore =
|
|
25741
|
-
const projectStore =
|
|
25742
|
-
const taskStore =
|
|
26207
|
+
const a2aKeyStore = getStoreLattice29("default", "a2aApiKey").store;
|
|
26208
|
+
const assistantStore = getStoreLattice29("default", "assistant").store;
|
|
26209
|
+
const projectStore = getStoreLattice29("default", "project").store;
|
|
26210
|
+
const taskStore = getStoreLattice29("default", "task").store;
|
|
25743
26211
|
const authService = new A2AAuthService({
|
|
25744
26212
|
keyStore: a2aKeyStore,
|
|
25745
26213
|
assistantStore,
|
|
@@ -25764,22 +26232,22 @@ var start = async (config) => {
|
|
|
25764
26232
|
});
|
|
25765
26233
|
}
|
|
25766
26234
|
try {
|
|
25767
|
-
const { registerOpenRoutes } = await import("./open-
|
|
25768
|
-
const { registerOpenSessionRoutes } = await import("./open-session-
|
|
26235
|
+
const { registerOpenRoutes } = await import("./open-UVKMONVE.mjs");
|
|
26236
|
+
const { registerOpenSessionRoutes } = await import("./open-session-2Y7XJJW2.mjs");
|
|
25769
26237
|
const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
|
|
25770
|
-
const { registerOpenFilesRoutes } = await import("./open-files-
|
|
26238
|
+
const { registerOpenFilesRoutes } = await import("./open-files-X3HXVEEM.mjs");
|
|
25771
26239
|
const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
|
|
25772
26240
|
const { registerOpenExposureRoutes } = await import("./open-exposure-VU227IO3.mjs");
|
|
25773
26241
|
const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
|
|
25774
26242
|
const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
|
|
25775
|
-
const { createOpenSandboxProvider } = await import("./sandboxProvider-
|
|
26243
|
+
const { createOpenSandboxProvider } = await import("./sandboxProvider-DUYWHYJN.mjs");
|
|
25776
26244
|
const { createAgentProvider } = await import("./agentProvider-X4FVQPKW.mjs");
|
|
25777
26245
|
const { registerExposedPluginTools } = await import("./registerExposedPlugins-6GCOE64C.mjs");
|
|
25778
26246
|
const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
|
|
25779
26247
|
openLattice.register(createKbProvider(new CollectionService2()));
|
|
25780
26248
|
openLattice.register(createOpenSandboxProvider());
|
|
25781
|
-
const threadStore =
|
|
25782
|
-
const assistantStore =
|
|
26249
|
+
const threadStore = getStoreLattice29("default", "thread").store;
|
|
26250
|
+
const assistantStore = getStoreLattice29("default", "assistant").store;
|
|
25783
26251
|
openLattice.register(
|
|
25784
26252
|
createAgentProvider({
|
|
25785
26253
|
threadStore,
|
|
@@ -25802,13 +26270,24 @@ var start = async (config) => {
|
|
|
25802
26270
|
error: err instanceof Error ? err.message : String(err)
|
|
25803
26271
|
});
|
|
25804
26272
|
}
|
|
26273
|
+
try {
|
|
26274
|
+
const { registerGatewaySandboxOpenCredentialProvider } = await import("./sandboxOpenCredentialProvider-USIU7Z7H.mjs");
|
|
26275
|
+
registerGatewaySandboxOpenCredentialProvider((error) => {
|
|
26276
|
+
logger6.warn("sandbox open-credential provider failed", { error });
|
|
26277
|
+
});
|
|
26278
|
+
logger6.info("Sandbox open-credential provider registered");
|
|
26279
|
+
} catch (err) {
|
|
26280
|
+
logger6.warn("Sandbox open-credential provider unavailable", {
|
|
26281
|
+
error: err instanceof Error ? err.message : String(err)
|
|
26282
|
+
});
|
|
26283
|
+
}
|
|
25805
26284
|
if (!sandboxLatticeManager2.hasLattice("default")) {
|
|
25806
26285
|
sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
|
|
25807
26286
|
logger6.info("Registered sandbox manager from env configuration");
|
|
25808
26287
|
}
|
|
25809
26288
|
try {
|
|
25810
|
-
const { ResourceController } = await import("./resources-
|
|
25811
|
-
const sharedResourceStore =
|
|
26289
|
+
const { ResourceController } = await import("./resources-2L3KSUUU.mjs");
|
|
26290
|
+
const sharedResourceStore = getStoreLattice29("default", "sharedResource").store;
|
|
25812
26291
|
const cache = new TokenCache();
|
|
25813
26292
|
const resourceController = new ResourceController({
|
|
25814
26293
|
store: sharedResourceStore,
|
|
@@ -25837,30 +26316,98 @@ var start = async (config) => {
|
|
|
25837
26316
|
}
|
|
25838
26317
|
}
|
|
25839
26318
|
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
25840
|
-
|
|
25841
|
-
|
|
26319
|
+
configureCoordinationLeaseStore(getStoreLattice29("default", "lease").store);
|
|
26320
|
+
const outboxStore = getStoreLattice29("default", "outbox").store;
|
|
26321
|
+
taskLifecycleNotifier3.setOutbox(outboxStore);
|
|
26322
|
+
projectTaskAdmissionNotifier2.setOutbox(outboxStore);
|
|
26323
|
+
configurePluginCatalogSync({ outbox: outboxStore, owner: getReplicaId6() });
|
|
26324
|
+
bridgeAssistantEvents(
|
|
26325
|
+
(event, cb) => eventBus4.subscribe(event, cb),
|
|
26326
|
+
outboxStore,
|
|
26327
|
+
getReplicaId6()
|
|
26328
|
+
);
|
|
26329
|
+
if (projectRoomRealtimeRuntime) {
|
|
26330
|
+
projectRoomRealtimeRuntime.broker.setBroadcastHook((scope, serialized, sourceKey) => {
|
|
26331
|
+
void publishRoomEvent(outboxStore, getReplicaId6(), scope, serialized, sourceKey);
|
|
26332
|
+
});
|
|
25842
26333
|
}
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
26334
|
+
const outboxPoller = new OutboxPoller({
|
|
26335
|
+
store: outboxStore,
|
|
26336
|
+
owner: getReplicaId6(),
|
|
26337
|
+
handlers: {
|
|
26338
|
+
"task-lifecycle": (payload) => taskLifecycleNotifier3.deliverFromOutbox(payload),
|
|
26339
|
+
"project-task-admission": (payload) => projectTaskAdmissionNotifier2.deliverFromOutbox(payload),
|
|
26340
|
+
[ROOM_EVENT_TOPIC]: async (payload) => {
|
|
26341
|
+
if (projectRoomRealtimeRuntime) ingestRoomEvent(payload, projectRoomRealtimeRuntime.broker);
|
|
26342
|
+
},
|
|
26343
|
+
"assistant-changed": (payload) => handleAssistantChanged(payload, (tenantId) => agentLatticeManager5.initializeStoredAssistantsForTenant(tenantId)),
|
|
26344
|
+
[MCP_CONFIG_CHANGED_TOPIC]: (payload) => reconcileMcpConfigChange(payload, {
|
|
26345
|
+
store: getStoreLattice29("default", "mcp").store,
|
|
26346
|
+
manager: mcpManager3,
|
|
26347
|
+
convertToConnection: convertToConnection2
|
|
26348
|
+
}),
|
|
26349
|
+
[PLUGIN_CATALOG_CHANGED_TOPIC]: async (payload) => {
|
|
26350
|
+
const tenantId = payload?.tenantId;
|
|
26351
|
+
if (typeof tenantId === "string" && tenantId.length > 0) getSandboxPluginCatalog4().invalidate(tenantId);
|
|
26352
|
+
}
|
|
26353
|
+
},
|
|
26354
|
+
log: logger6
|
|
26355
|
+
});
|
|
26356
|
+
try {
|
|
26357
|
+
await outboxPoller.start();
|
|
26358
|
+
} catch (err) {
|
|
26359
|
+
logger6.warn("Outbox poller failed to start", { error: err instanceof Error ? err.message : String(err) });
|
|
26360
|
+
}
|
|
26361
|
+
const leaseStore = getStoreLattice29("default", "lease").store;
|
|
26362
|
+
if (leaseStore.constructor?.name === "InMemoryLeaseStore") {
|
|
26363
|
+
logger6.warn(
|
|
26364
|
+
"Startup recovery leader gate uses a non-distributed lease store (per-process only); configure a shared 'lease' store for multi-replica deployments",
|
|
26365
|
+
{ phase: "startup_recovery" }
|
|
26366
|
+
);
|
|
26367
|
+
}
|
|
26368
|
+
warnMultiReplicaReadiness(logger6);
|
|
26369
|
+
const startup = await runAsLeader(leaseStore, "startup-recovery", async () => {
|
|
26370
|
+
if (routes.projectTaskRuntime) {
|
|
26371
|
+
await prepareProjectTaskRuntime(routes.projectTaskRuntime, logger6);
|
|
25860
26372
|
}
|
|
26373
|
+
if (process.env.AXIOM_TASK_ORPHAN_RECOVERY === "true") {
|
|
26374
|
+
try {
|
|
26375
|
+
const { createOrphanRecoveryRuntime: createOrphanRecoveryRuntime2 } = await import("./OrphanRecoveryRuntime-BEFILJMU.mjs");
|
|
26376
|
+
const { createRecoveryCallerDeps: createRecoveryCallerDeps2 } = await import("./RecoveryCallerDeps-6SJUYJ5S.mjs");
|
|
26377
|
+
const runtime = createOrphanRecoveryRuntime2({
|
|
26378
|
+
taskStore: getStoreLattice29("default", "task").store,
|
|
26379
|
+
workItemStore: getStoreLattice29("default", "taskWorkItem").store,
|
|
26380
|
+
tenantStore: getStoreLattice29("default", "tenant").store,
|
|
26381
|
+
...createRecoveryCallerDeps2(),
|
|
26382
|
+
log: logger6
|
|
26383
|
+
});
|
|
26384
|
+
await runtime.recoverAll();
|
|
26385
|
+
} catch (err) {
|
|
26386
|
+
logger6.warn("Task orphan recovery failed", { error: err instanceof Error ? err.message : String(err) });
|
|
26387
|
+
}
|
|
26388
|
+
}
|
|
26389
|
+
if (scheduleLatticeManager2.hasLattice("default")) {
|
|
26390
|
+
try {
|
|
26391
|
+
const restored = await scheduleLatticeManager2.getScheduleLattice("default").restore();
|
|
26392
|
+
logger6.info("Scheduled tasks restored", { phase: "startup_recovery", restored });
|
|
26393
|
+
} catch (err) {
|
|
26394
|
+
logger6.warn("Scheduled task restore failed", {
|
|
26395
|
+
phase: "startup_recovery",
|
|
26396
|
+
error: err instanceof Error ? err.message : String(err)
|
|
26397
|
+
});
|
|
26398
|
+
}
|
|
26399
|
+
}
|
|
26400
|
+
}, { hold: true, ttlMs: 5 * 6e4 });
|
|
26401
|
+
if (!startup.ran) {
|
|
26402
|
+
logger6.info("Startup recovery skipped (another replica leads)", { phase: "startup_recovery" });
|
|
25861
26403
|
}
|
|
25862
26404
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
25863
26405
|
logger6.info(`Lattice Gateway is running on port: ${target_port}`);
|
|
26406
|
+
const outboxRetention = createOutboxRetentionJob({
|
|
26407
|
+
outbox: outboxStore,
|
|
26408
|
+
log: logger6
|
|
26409
|
+
});
|
|
26410
|
+
outboxRetention.start();
|
|
25864
26411
|
const queueServiceConfig = config?.queueServiceConfig;
|
|
25865
26412
|
if (queueServiceConfig) {
|
|
25866
26413
|
setQueueServiceType(queueServiceConfig.type);
|
|
@@ -25886,8 +26433,13 @@ var LatticeGateway = {
|
|
|
25886
26433
|
};
|
|
25887
26434
|
export {
|
|
25888
26435
|
LatticeGateway,
|
|
26436
|
+
ProjectRoomEventBroker,
|
|
26437
|
+
ROOM_EVENT_TOPIC,
|
|
25889
26438
|
createOrphanRecoveryRuntime,
|
|
25890
26439
|
createRecoveryCallerDeps,
|
|
26440
|
+
createTaskRunLiveness,
|
|
26441
|
+
ingestRoomEvent,
|
|
26442
|
+
publishRoomEvent,
|
|
25891
26443
|
recoverTask,
|
|
25892
26444
|
recoverTopCaller,
|
|
25893
26445
|
resolveTopCaller,
|