@axiom-lattice/gateway 4.5.0 → 4.6.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/embed/{embed-page-AgentDetailSideApp-I4ZT3MQQ.js → embed-page-AgentDetailSideApp-7NYLPLAX.js} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-HVFB6EPY.js → embed-page-AgentGovernanceWorkbench-LNGNSI7X.js} +2 -2
- package/dist/embed/{embed-page-MessageGraph-CZQROEO7.js → embed-page-MessageGraph-E6DZDT65.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-WMWBSL54.js → embed-page-ReactChart-3VE7F5VJ.js} +1 -1
- package/dist/embed/{embed-page-SkillFlow-5JLJ5R57.js → embed-page-SkillFlow-XMMHEHQL.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-5BYTO4KT.js → embed-page-TasksSideApp-QM5TZ7EG.js} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-5KN7ZZWQ.js → embed-page-TeamWorkspace-E7RFY3ZY.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-S6XAQHDF.js → embed-page-TopologyRuntimeView-DLPDUB7Z.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-E6G446HW.js → embed-page-WorkflowAutomationView-FKSVOHHB.js} +1 -1
- package/dist/embed/embed-page-chunk-EXCJ42IB.js +2 -0
- package/dist/embed/{embed-page-chunk-MUE3GWJV.js → embed-page-chunk-H6TK3FAR.js} +1 -1
- package/dist/embed/{embed-page-chunk-QU52HUXW.js → embed-page-chunk-HEMNMC4X.js} +4 -4
- package/dist/embed/{embed-page-chunk-2JOGNXZT.js → embed-page-chunk-I7PWCKAG.js} +9 -9
- package/dist/embed/{embed-page-chunk-KDWA6VOG.js → embed-page-chunk-KN7D66KL.js} +1 -1
- package/dist/embed/{embed-page-chunk-7GAD6Q52.js → embed-page-chunk-U276WBDI.js} +2078 -2078
- package/dist/embed/{embed-page-chunk-YQ3BA27D.js → embed-page-chunk-WWTU55RX.js} +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.js +1830 -644
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1335 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/dist/embed/embed-page-chunk-DSQ4DIQY.js +0 -2
package/dist/index.js
CHANGED
|
@@ -956,18 +956,18 @@ function extractText(msg) {
|
|
|
956
956
|
}
|
|
957
957
|
return null;
|
|
958
958
|
}
|
|
959
|
-
var
|
|
959
|
+
var import_zod18, logger6, wechatConfigSchema, MAX_RECONNECT_DELAY_MS, BASE_RECONNECT_DELAY_MS, HEARTBEAT_INTERVAL_MS, MSG_TYPE_USER, MSG_ITEM_TEXT, activeConnections2, seenClientIds, wechatChannelAdapter;
|
|
960
960
|
var init_WechatChannelAdapter = __esm({
|
|
961
961
|
"src/channels/wechat/WechatChannelAdapter.ts"() {
|
|
962
962
|
"use strict";
|
|
963
|
-
|
|
963
|
+
import_zod18 = require("zod");
|
|
964
964
|
init_wechat_client();
|
|
965
965
|
init_context_store();
|
|
966
966
|
init_Logger();
|
|
967
|
-
|
|
968
|
-
wechatConfigSchema =
|
|
969
|
-
botToken:
|
|
970
|
-
uin:
|
|
967
|
+
logger6 = new Logger({ serviceName: "lattice/gateway/wechat" });
|
|
968
|
+
wechatConfigSchema = import_zod18.z.object({
|
|
969
|
+
botToken: import_zod18.z.string(),
|
|
970
|
+
uin: import_zod18.z.string().optional()
|
|
971
971
|
});
|
|
972
972
|
MAX_RECONNECT_DELAY_MS = 3e4;
|
|
973
973
|
BASE_RECONNECT_DELAY_MS = 1e3;
|
|
@@ -1015,7 +1015,7 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1015
1015
|
const senderId = replyTarget.rawTarget.senderId;
|
|
1016
1016
|
const contextToken = getContextToken(installation.id, senderId);
|
|
1017
1017
|
if (!contextToken) {
|
|
1018
|
-
|
|
1018
|
+
logger6.warn("WeChat context token expired, cannot send reply", {
|
|
1019
1019
|
installationId: installation.id,
|
|
1020
1020
|
senderId
|
|
1021
1021
|
});
|
|
@@ -1033,14 +1033,14 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1033
1033
|
async connect(installation, deps) {
|
|
1034
1034
|
const { id: installationId, tenantId, config } = installation;
|
|
1035
1035
|
if (!config.botToken) {
|
|
1036
|
-
|
|
1036
|
+
logger6.warn("WeChat installation missing botToken, skipping", { installationId });
|
|
1037
1037
|
return;
|
|
1038
1038
|
}
|
|
1039
1039
|
if (activeConnections2.has(installationId)) {
|
|
1040
|
-
|
|
1040
|
+
logger6.warn("WeChat polling already running for installation, skipping", { installationId });
|
|
1041
1041
|
return;
|
|
1042
1042
|
}
|
|
1043
|
-
|
|
1043
|
+
logger6.info("WeChat polling starting", { installationId, tenantId });
|
|
1044
1044
|
const abortController = new AbortController();
|
|
1045
1045
|
const heartbeatTimer = setInterval(() => {
|
|
1046
1046
|
const state2 = activeConnections2.get(installationId);
|
|
@@ -1050,7 +1050,7 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1050
1050
|
}
|
|
1051
1051
|
const elapsed = Date.now() - state2.lastActivity;
|
|
1052
1052
|
if (elapsed > HEARTBEAT_INTERVAL_MS * 2) {
|
|
1053
|
-
|
|
1053
|
+
logger6.error("WeChat polling heartbeat lost \u2014 no activity", {
|
|
1054
1054
|
installationId,
|
|
1055
1055
|
elapsedMs: elapsed
|
|
1056
1056
|
});
|
|
@@ -1069,7 +1069,7 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1069
1069
|
let reconnectDelay = BASE_RECONNECT_DELAY_MS;
|
|
1070
1070
|
const scheduleNextPoll = () => {
|
|
1071
1071
|
poll().catch((err) => {
|
|
1072
|
-
|
|
1072
|
+
logger6.error("WeChat poll iteration crashed", {
|
|
1073
1073
|
installationId,
|
|
1074
1074
|
error: err instanceof Error ? err.message : String(err)
|
|
1075
1075
|
});
|
|
@@ -1082,7 +1082,7 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1082
1082
|
const poll = async () => {
|
|
1083
1083
|
const currentState = activeConnections2.get(installationId);
|
|
1084
1084
|
if (!currentState || currentState.abortController.signal.aborted) {
|
|
1085
|
-
|
|
1085
|
+
logger6.info("WeChat polling aborted", { installationId });
|
|
1086
1086
|
return;
|
|
1087
1087
|
}
|
|
1088
1088
|
try {
|
|
@@ -1095,7 +1095,7 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1095
1095
|
reconnectDelay = BASE_RECONNECT_DELAY_MS;
|
|
1096
1096
|
currentState.lastActivity = Date.now();
|
|
1097
1097
|
if (result.msgs.length > 0) {
|
|
1098
|
-
|
|
1098
|
+
logger6.info("WeChat poll received messages", {
|
|
1099
1099
|
installationId,
|
|
1100
1100
|
count: result.msgs.length
|
|
1101
1101
|
});
|
|
@@ -1112,7 +1112,7 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1112
1112
|
if (inbound && router) {
|
|
1113
1113
|
const dispatchResult = await router.dispatch(inbound);
|
|
1114
1114
|
if (!dispatchResult.success) {
|
|
1115
|
-
|
|
1115
|
+
logger6.warn("WeChat dispatch failed", {
|
|
1116
1116
|
installationId,
|
|
1117
1117
|
error: dispatchResult.error?.message
|
|
1118
1118
|
});
|
|
@@ -1121,10 +1121,10 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1121
1121
|
}
|
|
1122
1122
|
} catch (err) {
|
|
1123
1123
|
if (err instanceof Error && err.name === "AbortError") {
|
|
1124
|
-
|
|
1124
|
+
logger6.info("WeChat poll aborted by signal", { installationId });
|
|
1125
1125
|
return;
|
|
1126
1126
|
}
|
|
1127
|
-
|
|
1127
|
+
logger6.error("WeChat poll error", {
|
|
1128
1128
|
installationId,
|
|
1129
1129
|
error: err instanceof Error ? err.message : String(err)
|
|
1130
1130
|
});
|
|
@@ -1134,19 +1134,19 @@ var init_WechatChannelAdapter = __esm({
|
|
|
1134
1134
|
scheduleNextPoll();
|
|
1135
1135
|
};
|
|
1136
1136
|
scheduleNextPoll();
|
|
1137
|
-
|
|
1137
|
+
logger6.info("WeChat polling started", { installationId });
|
|
1138
1138
|
},
|
|
1139
1139
|
async disconnect(installationId) {
|
|
1140
1140
|
const state = activeConnections2.get(installationId);
|
|
1141
1141
|
if (!state) {
|
|
1142
|
-
|
|
1142
|
+
logger6.warn("WeChat polling not running, nothing to disconnect", { installationId });
|
|
1143
1143
|
return;
|
|
1144
1144
|
}
|
|
1145
|
-
|
|
1145
|
+
logger6.info("WeChat polling disconnecting", { installationId });
|
|
1146
1146
|
state.abortController.abort();
|
|
1147
1147
|
clearInterval(state.heartbeatTimer);
|
|
1148
1148
|
activeConnections2.delete(installationId);
|
|
1149
|
-
|
|
1149
|
+
logger6.info("WeChat polling disconnected", { installationId });
|
|
1150
1150
|
}
|
|
1151
1151
|
};
|
|
1152
1152
|
}
|
|
@@ -1179,11 +1179,11 @@ function devModeDefaults() {
|
|
|
1179
1179
|
const projectId = process.env[DEFAULT_PROJECT_ENV] || void 0;
|
|
1180
1180
|
return { tenantId, projectId };
|
|
1181
1181
|
}
|
|
1182
|
-
var
|
|
1182
|
+
var import_protocols33, A2AAuthError, DEFAULT_TENANT_ENV, DEFAULT_TENANT_ID, DEFAULT_PROJECT_ENV, BEARER_SCHEME_RE, A2AAuthService;
|
|
1183
1183
|
var init_A2AAuthService = __esm({
|
|
1184
1184
|
"src/services/a2a/A2AAuthService.ts"() {
|
|
1185
1185
|
"use strict";
|
|
1186
|
-
|
|
1186
|
+
import_protocols33 = require("@axiom-lattice/protocols");
|
|
1187
1187
|
A2AAuthError = class extends Error {
|
|
1188
1188
|
constructor(message, statusCode) {
|
|
1189
1189
|
super(message);
|
|
@@ -1238,7 +1238,7 @@ var init_A2AAuthService = __esm({
|
|
|
1238
1238
|
const entry = merged.get(token);
|
|
1239
1239
|
if (!entry) return { authenticated: false };
|
|
1240
1240
|
const hasExplicitGrants = Array.isArray(entry.grants) && entry.grants.length > 0;
|
|
1241
|
-
const agentGrant = (0,
|
|
1241
|
+
const agentGrant = (0, import_protocols33.effectiveGrants)(entry).find((g) => g.domain === "agent");
|
|
1242
1242
|
const assistantIds = hasExplicitGrants ? agentGrant?.items ?? [] : entry.assistantIds;
|
|
1243
1243
|
return {
|
|
1244
1244
|
authenticated: true,
|
|
@@ -1453,7 +1453,7 @@ function extractFailureReason(task) {
|
|
|
1453
1453
|
return extractText2(task.status.message.parts);
|
|
1454
1454
|
}
|
|
1455
1455
|
function observationEventKey(task, result, failureReason) {
|
|
1456
|
-
const digest = (0,
|
|
1456
|
+
const digest = (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify({
|
|
1457
1457
|
state: task.status.state,
|
|
1458
1458
|
timestamp: task.status.timestamp,
|
|
1459
1459
|
message: task.status.message,
|
|
@@ -1463,7 +1463,7 @@ function observationEventKey(task, result, failureReason) {
|
|
|
1463
1463
|
return `a2a-status-observation:${task.id}:${digest}`;
|
|
1464
1464
|
}
|
|
1465
1465
|
function snapshotEventKey(task) {
|
|
1466
|
-
const digest = (0,
|
|
1466
|
+
const digest = (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify(task)).digest("hex");
|
|
1467
1467
|
return `a2a-task-snapshot:${task.id}:${digest}`;
|
|
1468
1468
|
}
|
|
1469
1469
|
function isGovernedObservation(state) {
|
|
@@ -1475,13 +1475,13 @@ function isA2ATaskSnapshot(value) {
|
|
|
1475
1475
|
function snapshotSequence(value) {
|
|
1476
1476
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
1477
1477
|
}
|
|
1478
|
-
var
|
|
1478
|
+
var import_node_crypto28, import_node_util, import_core66, TASK_STATUS_TO_A2A_STATE, TITLE_MAX_LENGTH, A2ATaskScopeConflictError, A2ATaskStoreAdapter;
|
|
1479
1479
|
var init_A2ATaskStoreAdapter = __esm({
|
|
1480
1480
|
"src/services/a2a/A2ATaskStoreAdapter.ts"() {
|
|
1481
1481
|
"use strict";
|
|
1482
|
-
|
|
1482
|
+
import_node_crypto28 = require("crypto");
|
|
1483
1483
|
import_node_util = require("util");
|
|
1484
|
-
|
|
1484
|
+
import_core66 = require("@axiom-lattice/core");
|
|
1485
1485
|
TASK_STATUS_TO_A2A_STATE = {
|
|
1486
1486
|
pending: "submitted",
|
|
1487
1487
|
in_progress: "working",
|
|
@@ -1507,7 +1507,7 @@ var init_A2ATaskStoreAdapter = __esm({
|
|
|
1507
1507
|
this.workItemStore = options.workItemStore;
|
|
1508
1508
|
this.lifecycle = options.lifecycle;
|
|
1509
1509
|
this.scope = options.scope;
|
|
1510
|
-
this.runBridge = (0,
|
|
1510
|
+
this.runBridge = (0, import_core66.createTaskRunBridge)({
|
|
1511
1511
|
lifecycle: this.lifecycle,
|
|
1512
1512
|
taskStore: this.taskStore,
|
|
1513
1513
|
workItemStore: this.workItemStore
|
|
@@ -1858,13 +1858,13 @@ function assistantHasGovernedTask(assistant) {
|
|
|
1858
1858
|
const allowedTools = taskMiddleware?.allowedTools;
|
|
1859
1859
|
return !!taskMiddleware && (allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task")));
|
|
1860
1860
|
}
|
|
1861
|
-
var import_uuid11,
|
|
1861
|
+
var import_uuid11, import_core67, import_protocols34, _AxiomAgentExecutor, AxiomAgentExecutor;
|
|
1862
1862
|
var init_AxiomAgentExecutor = __esm({
|
|
1863
1863
|
"src/services/a2a/AxiomAgentExecutor.ts"() {
|
|
1864
1864
|
"use strict";
|
|
1865
1865
|
import_uuid11 = require("uuid");
|
|
1866
|
-
|
|
1867
|
-
|
|
1866
|
+
import_core67 = require("@axiom-lattice/core");
|
|
1867
|
+
import_protocols34 = require("@axiom-lattice/protocols");
|
|
1868
1868
|
init_a2aFileRefs();
|
|
1869
1869
|
init_task_file_refs();
|
|
1870
1870
|
_AxiomAgentExecutor = class _AxiomAgentExecutor {
|
|
@@ -1945,7 +1945,7 @@ var init_AxiomAgentExecutor = __esm({
|
|
|
1945
1945
|
await this.taskStore.save(initialTask);
|
|
1946
1946
|
bus.publish(initialTask);
|
|
1947
1947
|
try {
|
|
1948
|
-
const agent =
|
|
1948
|
+
const agent = import_core67.agentInstanceManager.getAgent(this.agentParams(threadId));
|
|
1949
1949
|
const requestText = inputFiles.length > 0 ? `${text}
|
|
1950
1950
|
|
|
1951
1951
|
${formatFileRefsSection(inputFiles)}` : text;
|
|
@@ -1963,7 +1963,7 @@ ${formatFileRefsSection(inputFiles)}` : text;
|
|
|
1963
1963
|
custom_run_config: { taskId: ctx.taskId, execution: { taskId: ctx.taskId } }
|
|
1964
1964
|
});
|
|
1965
1965
|
const stream = agent.chunkStream(messageId, [
|
|
1966
|
-
|
|
1966
|
+
import_protocols34.MessageChunkTypes.MESSAGE_COMPLETED
|
|
1967
1967
|
]);
|
|
1968
1968
|
let outputText = "";
|
|
1969
1969
|
let interrupted = false;
|
|
@@ -1971,14 +1971,14 @@ ${formatFileRefsSection(inputFiles)}` : text;
|
|
|
1971
1971
|
if (execution.canceled) return;
|
|
1972
1972
|
const chunkType = chunk.type;
|
|
1973
1973
|
const chunkText = chunk.data?.content ?? "";
|
|
1974
|
-
if (chunkType ===
|
|
1974
|
+
if (chunkType === import_protocols34.MessageChunkTypes.INTERRUPT) {
|
|
1975
1975
|
interrupted = true;
|
|
1976
1976
|
break;
|
|
1977
1977
|
}
|
|
1978
|
-
if (chunkType ===
|
|
1978
|
+
if (chunkType === import_protocols34.MessageChunkTypes.AI && chunkText) {
|
|
1979
1979
|
outputText += chunkText;
|
|
1980
1980
|
}
|
|
1981
|
-
if (chunkType ===
|
|
1981
|
+
if (chunkType === import_protocols34.MessageChunkTypes.AI || chunkType === import_protocols34.MessageChunkTypes.TOOL) {
|
|
1982
1982
|
if (chunkText) {
|
|
1983
1983
|
bus.publish({
|
|
1984
1984
|
kind: "status-update",
|
|
@@ -2140,7 +2140,7 @@ ${formatFileRefsSection(inputFiles)}` : text;
|
|
|
2140
2140
|
const threadId = task ? asString2(task.metadata?.threadId) : void 0;
|
|
2141
2141
|
if (threadId) {
|
|
2142
2142
|
try {
|
|
2143
|
-
const agent =
|
|
2143
|
+
const agent = import_core67.agentInstanceManager.getAgent(this.agentParams(threadId));
|
|
2144
2144
|
await agent.abort();
|
|
2145
2145
|
} catch (err) {
|
|
2146
2146
|
console.warn({
|
|
@@ -2494,10 +2494,10 @@ function isAsyncGenerator(value) {
|
|
|
2494
2494
|
}
|
|
2495
2495
|
function registerA2AStandardRoutes(app2, deps) {
|
|
2496
2496
|
const authService = deps.authService;
|
|
2497
|
-
const resolveTasks = deps.getTaskStoreLattice ?? (() => (0,
|
|
2498
|
-
const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => (0,
|
|
2499
|
-
const getAssistantStore2 = deps.getAssistantStore ?? (() => (0,
|
|
2500
|
-
const getProjectStore2 = deps.getProjectStore ?? (() => (0,
|
|
2497
|
+
const resolveTasks = deps.getTaskStoreLattice ?? (() => (0, import_core68.getStoreLattice)("default", "task").store);
|
|
2498
|
+
const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => (0, import_core68.getStoreLattice)("default", "taskWorkItem").store);
|
|
2499
|
+
const getAssistantStore2 = deps.getAssistantStore ?? (() => (0, import_core68.getStoreLattice)("default", "assistant").store);
|
|
2500
|
+
const getProjectStore2 = deps.getProjectStore ?? (() => (0, import_core68.getStoreLattice)("default", "project").store);
|
|
2501
2501
|
const runtimeRegistry = deps.runtimeRegistry ?? new A2ARuntimeRegistry();
|
|
2502
2502
|
app2.addHook("onClose", async () => {
|
|
2503
2503
|
runtimeRegistry.close();
|
|
@@ -2580,7 +2580,7 @@ function registerA2AStandardRoutes(app2, deps) {
|
|
|
2580
2580
|
const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
|
|
2581
2581
|
taskStore: internalTasks,
|
|
2582
2582
|
workItemStore: workItems,
|
|
2583
|
-
lifecycle: (0,
|
|
2583
|
+
lifecycle: (0, import_core68.createTaskLifecycleService)({
|
|
2584
2584
|
taskStore: internalTasks,
|
|
2585
2585
|
workItemStore: workItems
|
|
2586
2586
|
}),
|
|
@@ -2699,7 +2699,7 @@ data: ${JSON.stringify(errorResponse)}
|
|
|
2699
2699
|
const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
|
|
2700
2700
|
taskStore: internalTasks,
|
|
2701
2701
|
workItemStore: workItems,
|
|
2702
|
-
lifecycle: (0,
|
|
2702
|
+
lifecycle: (0, import_core68.createTaskLifecycleService)({
|
|
2703
2703
|
taskStore: internalTasks,
|
|
2704
2704
|
workItemStore: workItems
|
|
2705
2705
|
}),
|
|
@@ -2744,13 +2744,13 @@ data: ${JSON.stringify(data)}
|
|
|
2744
2744
|
}
|
|
2745
2745
|
);
|
|
2746
2746
|
}
|
|
2747
|
-
var import_uuid12, import_server2,
|
|
2747
|
+
var import_uuid12, import_server2, import_core68, SSE_HEADERS;
|
|
2748
2748
|
var init_a2a_standard = __esm({
|
|
2749
2749
|
"src/routes/a2a-standard.ts"() {
|
|
2750
2750
|
"use strict";
|
|
2751
2751
|
import_uuid12 = require("uuid");
|
|
2752
2752
|
import_server2 = require("@a2a-js/sdk/server");
|
|
2753
|
-
|
|
2753
|
+
import_core68 = require("@axiom-lattice/core");
|
|
2754
2754
|
init_A2AAuthService();
|
|
2755
2755
|
init_A2AAgentCardBuilder();
|
|
2756
2756
|
init_A2ATaskStoreAdapter();
|
|
@@ -2958,11 +2958,11 @@ var init_a2a = __esm({
|
|
|
2958
2958
|
});
|
|
2959
2959
|
|
|
2960
2960
|
// src/services/open/openAuthService.ts
|
|
2961
|
-
var
|
|
2961
|
+
var import_protocols35, OpenAuthService;
|
|
2962
2962
|
var init_openAuthService = __esm({
|
|
2963
2963
|
"src/services/open/openAuthService.ts"() {
|
|
2964
2964
|
"use strict";
|
|
2965
|
-
|
|
2965
|
+
import_protocols35 = require("@axiom-lattice/protocols");
|
|
2966
2966
|
OpenAuthService = class {
|
|
2967
2967
|
constructor(keyStore) {
|
|
2968
2968
|
this.keyStore = keyStore;
|
|
@@ -2976,7 +2976,7 @@ var init_openAuthService = __esm({
|
|
|
2976
2976
|
kind: "api_key",
|
|
2977
2977
|
tenantId: record.tenantId,
|
|
2978
2978
|
projectId: record.projectId,
|
|
2979
|
-
grants: (0,
|
|
2979
|
+
grants: (0, import_protocols35.effectiveGrants)(record)
|
|
2980
2980
|
};
|
|
2981
2981
|
}
|
|
2982
2982
|
};
|
|
@@ -2984,11 +2984,11 @@ var init_openAuthService = __esm({
|
|
|
2984
2984
|
});
|
|
2985
2985
|
|
|
2986
2986
|
// src/services/open/openCredentialService.ts
|
|
2987
|
-
var
|
|
2987
|
+
var import_protocols36, OpenCredentialService;
|
|
2988
2988
|
var init_openCredentialService = __esm({
|
|
2989
2989
|
"src/services/open/openCredentialService.ts"() {
|
|
2990
2990
|
"use strict";
|
|
2991
|
-
|
|
2991
|
+
import_protocols36 = require("@axiom-lattice/protocols");
|
|
2992
2992
|
init_openToken();
|
|
2993
2993
|
init_openAuthService();
|
|
2994
2994
|
OpenCredentialService = class {
|
|
@@ -3147,11 +3147,11 @@ function grantCovers(grants, domain, action) {
|
|
|
3147
3147
|
(g) => g.domain === domain && (!g.items || g.items.length === 0 || g.items.includes(action))
|
|
3148
3148
|
);
|
|
3149
3149
|
}
|
|
3150
|
-
var
|
|
3150
|
+
var import_zod19, GrantDeniedError, ValidationError, ActionNotFoundError, OpenLattice, openLattice;
|
|
3151
3151
|
var init_openLattice = __esm({
|
|
3152
3152
|
"src/services/open/openLattice.ts"() {
|
|
3153
3153
|
"use strict";
|
|
3154
|
-
|
|
3154
|
+
import_zod19 = require("zod");
|
|
3155
3155
|
GrantDeniedError = class extends Error {
|
|
3156
3156
|
constructor(toolName) {
|
|
3157
3157
|
super(`GRANT_DENIED: no grant covers ${toolName}`);
|
|
@@ -3291,7 +3291,7 @@ var init_openLattice = __esm({
|
|
|
3291
3291
|
throw new GrantDeniedError(toolName);
|
|
3292
3292
|
}
|
|
3293
3293
|
const resolvedSchema = resolved.action.schema;
|
|
3294
|
-
const parsed = "safeParse" in resolvedSchema ? resolvedSchema.safeParse(args) :
|
|
3294
|
+
const parsed = "safeParse" in resolvedSchema ? resolvedSchema.safeParse(args) : import_zod19.z.object(resolvedSchema).safeParse(args);
|
|
3295
3295
|
if (!parsed.success) {
|
|
3296
3296
|
throw new ValidationError(parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "));
|
|
3297
3297
|
}
|
|
@@ -3424,7 +3424,7 @@ var init_openMcpServer = __esm({
|
|
|
3424
3424
|
|
|
3425
3425
|
// src/services/open/pluginExposeAdapter.ts
|
|
3426
3426
|
function schemaOf(tool) {
|
|
3427
|
-
return tool.lc_kwargs?.schema ?? tool.schema ??
|
|
3427
|
+
return tool.lc_kwargs?.schema ?? tool.schema ?? import_zod20.z.object({}).passthrough();
|
|
3428
3428
|
}
|
|
3429
3429
|
function createPluginProvider(domain, tools, options = {}) {
|
|
3430
3430
|
function toAction(info) {
|
|
@@ -3472,11 +3472,11 @@ function createPluginProvider(domain, tools, options = {}) {
|
|
|
3472
3472
|
}
|
|
3473
3473
|
};
|
|
3474
3474
|
}
|
|
3475
|
-
var
|
|
3475
|
+
var import_zod20;
|
|
3476
3476
|
var init_pluginExposeAdapter = __esm({
|
|
3477
3477
|
"src/services/open/pluginExposeAdapter.ts"() {
|
|
3478
3478
|
"use strict";
|
|
3479
|
-
|
|
3479
|
+
import_zod20 = require("zod");
|
|
3480
3480
|
}
|
|
3481
3481
|
});
|
|
3482
3482
|
|
|
@@ -3501,8 +3501,8 @@ function exposedDeclarations(plugin) {
|
|
|
3501
3501
|
return (plugin.meta.tools ?? []).filter((t2) => t2.expose);
|
|
3502
3502
|
}
|
|
3503
3503
|
async function registerExposedPluginTools() {
|
|
3504
|
-
for (const key of
|
|
3505
|
-
const plugin =
|
|
3504
|
+
for (const key of import_core69.PluginRegistry.list()) {
|
|
3505
|
+
const plugin = import_core69.PluginRegistry.get(key);
|
|
3506
3506
|
if (!plugin?.middleware) continue;
|
|
3507
3507
|
const exposed = exposedDeclarations(plugin);
|
|
3508
3508
|
if (exposed.length === 0) continue;
|
|
@@ -3537,8 +3537,8 @@ async function registerExposedPluginTools() {
|
|
|
3537
3537
|
}
|
|
3538
3538
|
function collectExposedPluginUiResources(grants) {
|
|
3539
3539
|
const resources = /* @__PURE__ */ new Map();
|
|
3540
|
-
for (const type of
|
|
3541
|
-
const plugin =
|
|
3540
|
+
for (const type of import_core69.PluginRegistry.list()) {
|
|
3541
|
+
const plugin = import_core69.PluginRegistry.get(type);
|
|
3542
3542
|
if (!plugin?.middleware) continue;
|
|
3543
3543
|
const exposedNames = new Set(exposedDeclarations(plugin).map((d) => d.name));
|
|
3544
3544
|
if (exposedNames.size === 0) continue;
|
|
@@ -3562,11 +3562,11 @@ function collectExposedPluginUiResources(grants) {
|
|
|
3562
3562
|
}
|
|
3563
3563
|
return resources;
|
|
3564
3564
|
}
|
|
3565
|
-
var
|
|
3565
|
+
var import_core69;
|
|
3566
3566
|
var init_registerExposedPlugins = __esm({
|
|
3567
3567
|
"src/services/open/registerExposedPlugins.ts"() {
|
|
3568
3568
|
"use strict";
|
|
3569
|
-
|
|
3569
|
+
import_core69 = require("@axiom-lattice/core");
|
|
3570
3570
|
init_openLattice();
|
|
3571
3571
|
init_pluginExposeAdapter();
|
|
3572
3572
|
}
|
|
@@ -3581,14 +3581,14 @@ function toText(result) {
|
|
|
3581
3581
|
}
|
|
3582
3582
|
function defaultCatalog() {
|
|
3583
3583
|
return {
|
|
3584
|
-
getValidatedManifest: (tenantId, type) => (0,
|
|
3585
|
-
getExecutor: () => (0,
|
|
3586
|
-
readPluginFile: (tenantId, type, relPath) => (0,
|
|
3584
|
+
getValidatedManifest: (tenantId, type) => (0, import_core70.getSandboxPluginCatalog)().getValidatedManifest(tenantId, type),
|
|
3585
|
+
getExecutor: () => (0, import_core70.getSandboxPluginCatalog)().getExecutor(),
|
|
3586
|
+
readPluginFile: (tenantId, type, relPath) => (0, import_core70.getSandboxPluginCatalog)().readPluginFile(tenantId, type, relPath)
|
|
3587
3587
|
};
|
|
3588
3588
|
}
|
|
3589
3589
|
async function defaultResolveConnections(pluginType, ctx) {
|
|
3590
3590
|
try {
|
|
3591
|
-
return await (0,
|
|
3591
|
+
return await (0, import_core70.resolveSandboxPluginConnections)(
|
|
3592
3592
|
pluginType,
|
|
3593
3593
|
{ connectAll: true },
|
|
3594
3594
|
{
|
|
@@ -3617,7 +3617,7 @@ function mergeOpenCatalog(globalCatalog, tenantTools) {
|
|
|
3617
3617
|
async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
|
|
3618
3618
|
const catalog = deps.catalog ?? defaultCatalog();
|
|
3619
3619
|
const listPlugins = deps.listPlugins ?? (async (tenant2) => {
|
|
3620
|
-
const snapshot = await (0,
|
|
3620
|
+
const snapshot = await (0, import_core70.getSandboxPluginCatalog)().ensureLoaded(tenant2);
|
|
3621
3621
|
return { plugins: snapshot.plugins, diagnostics: snapshot.diagnostics };
|
|
3622
3622
|
});
|
|
3623
3623
|
const resolveConnections = deps.resolveConnections ?? defaultResolveConnections;
|
|
@@ -3659,7 +3659,7 @@ async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
|
|
|
3659
3659
|
domain,
|
|
3660
3660
|
action: tool.name,
|
|
3661
3661
|
description: tool.description,
|
|
3662
|
-
schema: (0,
|
|
3662
|
+
schema: (0, import_core70.jsonSchemaToZod)(tool.schema),
|
|
3663
3663
|
annotations: {
|
|
3664
3664
|
readOnlyHint: tool.readOnly ?? false,
|
|
3665
3665
|
destructiveHint: tool.destructive ?? false
|
|
@@ -3710,11 +3710,11 @@ async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
|
|
|
3710
3710
|
}
|
|
3711
3711
|
};
|
|
3712
3712
|
}
|
|
3713
|
-
var
|
|
3713
|
+
var import_core70;
|
|
3714
3714
|
var init_tenantOpenPlugins = __esm({
|
|
3715
3715
|
"src/services/open/tenantOpenPlugins.ts"() {
|
|
3716
3716
|
"use strict";
|
|
3717
|
-
|
|
3717
|
+
import_core70 = require("@axiom-lattice/core");
|
|
3718
3718
|
init_openLattice();
|
|
3719
3719
|
init_pluginToolExecution();
|
|
3720
3720
|
}
|
|
@@ -3751,13 +3751,13 @@ function emptyTenantPlugins() {
|
|
|
3751
3751
|
}
|
|
3752
3752
|
function registerOpenRoutes(app2, deps = {}) {
|
|
3753
3753
|
const openAuth = deps.openAuth ?? new OpenCredentialService(
|
|
3754
|
-
(0,
|
|
3755
|
-
(0,
|
|
3754
|
+
(0, import_core71.getStoreLattice)("default", "a2aApiKey").store,
|
|
3755
|
+
(0, import_core71.getStoreLattice)("default", "project").store
|
|
3756
3756
|
);
|
|
3757
3757
|
const grantsSource = deps.grantsSource ?? ((ctx) => ctx.grants);
|
|
3758
3758
|
const audit = deps.audit ?? (() => {
|
|
3759
3759
|
try {
|
|
3760
|
-
const store2 = (0,
|
|
3760
|
+
const store2 = (0, import_core71.getStoreLattice)("default", "openAudit").store;
|
|
3761
3761
|
return new OpenAuditService(store2);
|
|
3762
3762
|
} catch {
|
|
3763
3763
|
return void 0;
|
|
@@ -3911,12 +3911,12 @@ function registerOpenRoutes(app2, deps = {}) {
|
|
|
3911
3911
|
}
|
|
3912
3912
|
);
|
|
3913
3913
|
}
|
|
3914
|
-
var import_types,
|
|
3914
|
+
var import_types, import_core71, RPC_ERROR;
|
|
3915
3915
|
var init_open = __esm({
|
|
3916
3916
|
"src/routes/open.ts"() {
|
|
3917
3917
|
"use strict";
|
|
3918
3918
|
import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
3919
|
-
|
|
3919
|
+
import_core71 = require("@axiom-lattice/core");
|
|
3920
3920
|
init_openCredentialService();
|
|
3921
3921
|
init_openAuditService();
|
|
3922
3922
|
init_openRateLimiter();
|
|
@@ -3949,7 +3949,7 @@ function registerOpenSessionRoutes(app2) {
|
|
|
3949
3949
|
if (typeof shareToken !== "string" || shareToken.length === 0) {
|
|
3950
3950
|
return reply.status(400).send({ error: "SHARE_TOKEN_REQUIRED" });
|
|
3951
3951
|
}
|
|
3952
|
-
const store2 = (0,
|
|
3952
|
+
const store2 = (0, import_core72.getStoreLattice)("default", "sharedResource").store;
|
|
3953
3953
|
const record = await store2.findByToken(shareToken);
|
|
3954
3954
|
if (!record || record.revoked) {
|
|
3955
3955
|
return reply.status(404).send({ error: "SHARE_NOT_FOUND" });
|
|
@@ -3974,11 +3974,11 @@ function registerOpenSessionRoutes(app2) {
|
|
|
3974
3974
|
}
|
|
3975
3975
|
);
|
|
3976
3976
|
}
|
|
3977
|
-
var
|
|
3977
|
+
var import_core72;
|
|
3978
3978
|
var init_open_session = __esm({
|
|
3979
3979
|
"src/routes/open-session.ts"() {
|
|
3980
3980
|
"use strict";
|
|
3981
|
-
|
|
3981
|
+
import_core72 = require("@axiom-lattice/core");
|
|
3982
3982
|
init_openToken();
|
|
3983
3983
|
init_previewToken();
|
|
3984
3984
|
init_openLattice();
|
|
@@ -3987,13 +3987,13 @@ var init_open_session = __esm({
|
|
|
3987
3987
|
|
|
3988
3988
|
// src/services/open/openCatalogService.ts
|
|
3989
3989
|
async function defaultListTenantPlugins(tenantId) {
|
|
3990
|
-
return (await (0,
|
|
3990
|
+
return (await (0, import_core73.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
|
|
3991
3991
|
}
|
|
3992
|
-
var
|
|
3992
|
+
var import_core73, OpenCatalogService;
|
|
3993
3993
|
var init_openCatalogService = __esm({
|
|
3994
3994
|
"src/services/open/openCatalogService.ts"() {
|
|
3995
3995
|
"use strict";
|
|
3996
|
-
|
|
3996
|
+
import_core73 = require("@axiom-lattice/core");
|
|
3997
3997
|
init_openLattice();
|
|
3998
3998
|
OpenCatalogService = class {
|
|
3999
3999
|
constructor(assistantStore, lattice = openLattice, listTenantPlugins2 = defaultListTenantPlugins) {
|
|
@@ -4068,17 +4068,17 @@ function registerOpenCatalogRoutes(app2) {
|
|
|
4068
4068
|
if (!tenantId) {
|
|
4069
4069
|
return reply.status(401).send({ success: false, error: "Unauthorized" });
|
|
4070
4070
|
}
|
|
4071
|
-
const assistantStore = (0,
|
|
4071
|
+
const assistantStore = (0, import_core74.getStoreLattice)("default", "assistant").store;
|
|
4072
4072
|
const service2 = new OpenCatalogService(assistantStore);
|
|
4073
4073
|
const catalog = await service2.buildForTenant(tenantId);
|
|
4074
4074
|
return reply.status(200).send({ success: true, data: catalog });
|
|
4075
4075
|
});
|
|
4076
4076
|
}
|
|
4077
|
-
var
|
|
4077
|
+
var import_core74;
|
|
4078
4078
|
var init_open_catalog = __esm({
|
|
4079
4079
|
"src/routes/open-catalog.ts"() {
|
|
4080
4080
|
"use strict";
|
|
4081
|
-
|
|
4081
|
+
import_core74 = require("@axiom-lattice/core");
|
|
4082
4082
|
init_openCatalogService();
|
|
4083
4083
|
}
|
|
4084
4084
|
});
|
|
@@ -4090,7 +4090,7 @@ __export(sandboxProvider_exports, {
|
|
|
4090
4090
|
resolveContextSandbox: () => resolveContextSandbox
|
|
4091
4091
|
});
|
|
4092
4092
|
async function resolveContextSandbox(ctx) {
|
|
4093
|
-
return
|
|
4093
|
+
return import_core75.sandboxLatticeManager.getSandboxFromConfig({
|
|
4094
4094
|
assistant_id: "",
|
|
4095
4095
|
thread_id: "",
|
|
4096
4096
|
tenantId: ctx.tenantId,
|
|
@@ -4109,21 +4109,21 @@ function createOpenSandboxProvider() {
|
|
|
4109
4109
|
name: "files_read",
|
|
4110
4110
|
label: "Read files",
|
|
4111
4111
|
description: "Read a file from the project sandbox.",
|
|
4112
|
-
schema: { path:
|
|
4112
|
+
schema: { path: import_zod21.z.string().min(1) },
|
|
4113
4113
|
annotations: { readOnlyHint: true, destructiveHint: false }
|
|
4114
4114
|
},
|
|
4115
4115
|
{
|
|
4116
4116
|
name: "files_list",
|
|
4117
4117
|
label: "List files",
|
|
4118
4118
|
description: "List files under a directory in the project sandbox.",
|
|
4119
|
-
schema: { path:
|
|
4119
|
+
schema: { path: import_zod21.z.string(), recursive: import_zod21.z.boolean().optional() },
|
|
4120
4120
|
annotations: { readOnlyHint: true, destructiveHint: false }
|
|
4121
4121
|
},
|
|
4122
4122
|
{
|
|
4123
4123
|
name: "files_write",
|
|
4124
4124
|
label: "Write files",
|
|
4125
4125
|
description: "Write content to a file in the project sandbox.",
|
|
4126
|
-
schema: { path:
|
|
4126
|
+
schema: { path: import_zod21.z.string().min(1), content: import_zod21.z.string() },
|
|
4127
4127
|
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
4128
4128
|
},
|
|
4129
4129
|
{
|
|
@@ -4131,9 +4131,9 @@ function createOpenSandboxProvider() {
|
|
|
4131
4131
|
label: "Run shell commands",
|
|
4132
4132
|
description: "Execute a shell command in the project sandbox. Returns stdout (up to 64KB) and exit code.",
|
|
4133
4133
|
schema: {
|
|
4134
|
-
command:
|
|
4135
|
-
exec_dir:
|
|
4136
|
-
timeout:
|
|
4134
|
+
command: import_zod21.z.string().min(1),
|
|
4135
|
+
exec_dir: import_zod21.z.string().optional(),
|
|
4136
|
+
timeout: import_zod21.z.number().int().min(1e3).optional()
|
|
4137
4137
|
},
|
|
4138
4138
|
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
4139
4139
|
}
|
|
@@ -4188,12 +4188,12 @@ ${truncated}`
|
|
|
4188
4188
|
}
|
|
4189
4189
|
};
|
|
4190
4190
|
}
|
|
4191
|
-
var
|
|
4191
|
+
var import_zod21, import_core75, MAX_OUTPUT_BYTES;
|
|
4192
4192
|
var init_sandboxProvider = __esm({
|
|
4193
4193
|
"src/services/open/providers/sandboxProvider.ts"() {
|
|
4194
4194
|
"use strict";
|
|
4195
|
-
|
|
4196
|
-
|
|
4195
|
+
import_zod21 = require("zod");
|
|
4196
|
+
import_core75 = require("@axiom-lattice/core");
|
|
4197
4197
|
MAX_OUTPUT_BYTES = 64e3;
|
|
4198
4198
|
}
|
|
4199
4199
|
});
|
|
@@ -4215,12 +4215,12 @@ function safePath(raw) {
|
|
|
4215
4215
|
}
|
|
4216
4216
|
function registerOpenFilesRoutes(app2, deps = {}) {
|
|
4217
4217
|
const openAuth = deps.openAuth ?? new OpenCredentialService(
|
|
4218
|
-
(0,
|
|
4219
|
-
(0,
|
|
4218
|
+
(0, import_core76.getStoreLattice)("default", "a2aApiKey").store,
|
|
4219
|
+
(0, import_core76.getStoreLattice)("default", "project").store
|
|
4220
4220
|
);
|
|
4221
4221
|
const audit = deps.audit ?? (() => {
|
|
4222
4222
|
try {
|
|
4223
|
-
const store2 = (0,
|
|
4223
|
+
const store2 = (0, import_core76.getStoreLattice)("default", "openAudit").store;
|
|
4224
4224
|
return new OpenAuditService(store2);
|
|
4225
4225
|
} catch {
|
|
4226
4226
|
return void 0;
|
|
@@ -4330,11 +4330,11 @@ function registerOpenFilesRoutes(app2, deps = {}) {
|
|
|
4330
4330
|
}
|
|
4331
4331
|
});
|
|
4332
4332
|
}
|
|
4333
|
-
var
|
|
4333
|
+
var import_core76;
|
|
4334
4334
|
var init_open_files = __esm({
|
|
4335
4335
|
"src/routes/open-files.ts"() {
|
|
4336
4336
|
"use strict";
|
|
4337
|
-
|
|
4337
|
+
import_core76 = require("@axiom-lattice/core");
|
|
4338
4338
|
init_openCredentialService();
|
|
4339
4339
|
init_openLattice();
|
|
4340
4340
|
init_sandboxProvider();
|
|
@@ -4356,7 +4356,7 @@ function registerOpenAuditRoutes(app2) {
|
|
|
4356
4356
|
return reply.status(401).send({ success: false, error: "Unauthorized" });
|
|
4357
4357
|
}
|
|
4358
4358
|
const status = request.query.status === "ok" || request.query.status === "error" ? request.query.status : void 0;
|
|
4359
|
-
const store2 = (0,
|
|
4359
|
+
const store2 = (0, import_core77.getStoreLattice)("default", "openAudit").store;
|
|
4360
4360
|
const records = await store2.query({
|
|
4361
4361
|
tenantId,
|
|
4362
4362
|
credentialId: request.query.credentialId,
|
|
@@ -4371,11 +4371,11 @@ function registerOpenAuditRoutes(app2) {
|
|
|
4371
4371
|
}
|
|
4372
4372
|
);
|
|
4373
4373
|
}
|
|
4374
|
-
var
|
|
4374
|
+
var import_core77;
|
|
4375
4375
|
var init_open_audit = __esm({
|
|
4376
4376
|
"src/routes/open-audit.ts"() {
|
|
4377
4377
|
"use strict";
|
|
4378
|
-
|
|
4378
|
+
import_core77 = require("@axiom-lattice/core");
|
|
4379
4379
|
}
|
|
4380
4380
|
});
|
|
4381
4381
|
|
|
@@ -4392,12 +4392,12 @@ function registerOpenExposureRoutes(app2, deps = {}) {
|
|
|
4392
4392
|
if (!tenantId) {
|
|
4393
4393
|
return reply.status(401).send({ success: false, error: "Unauthorized" });
|
|
4394
4394
|
}
|
|
4395
|
-
const keyStore = (0,
|
|
4395
|
+
const keyStore = (0, import_core78.getStoreLattice)("default", "a2aApiKey").store;
|
|
4396
4396
|
const record = await keyStore.findById(request.params.id);
|
|
4397
4397
|
if (!record || record.tenantId !== tenantId) {
|
|
4398
4398
|
return reply.status(404).send({ success: false, error: "Key not found" });
|
|
4399
4399
|
}
|
|
4400
|
-
const grants = (0,
|
|
4400
|
+
const grants = (0, import_protocols37.effectiveGrants)(record);
|
|
4401
4401
|
const forwardedHost = request.headers["x-forwarded-host"];
|
|
4402
4402
|
const host = (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ?? (Array.isArray(request.headers.host) ? request.headers.host[0] : request.headers.host) ?? request.hostname;
|
|
4403
4403
|
const baseUrl = `${request.protocol}://${host}`;
|
|
@@ -4426,7 +4426,7 @@ function registerOpenExposureRoutes(app2, deps = {}) {
|
|
|
4426
4426
|
hasUi: Boolean(t2.ui)
|
|
4427
4427
|
}));
|
|
4428
4428
|
const agentGrant = grants.find((g) => g.domain === "agent");
|
|
4429
|
-
const assistantStore = (0,
|
|
4429
|
+
const assistantStore = (0, import_core78.getStoreLattice)("default", "assistant").store;
|
|
4430
4430
|
const agents = [];
|
|
4431
4431
|
for (const assistantId of agentGrant?.items ?? []) {
|
|
4432
4432
|
const assistant = await assistantStore.getAssistantById(
|
|
@@ -4461,12 +4461,12 @@ function registerOpenExposureRoutes(app2, deps = {}) {
|
|
|
4461
4461
|
}
|
|
4462
4462
|
);
|
|
4463
4463
|
}
|
|
4464
|
-
var
|
|
4464
|
+
var import_core78, import_protocols37;
|
|
4465
4465
|
var init_open_exposure = __esm({
|
|
4466
4466
|
"src/routes/open-exposure.ts"() {
|
|
4467
4467
|
"use strict";
|
|
4468
|
-
|
|
4469
|
-
|
|
4468
|
+
import_core78 = require("@axiom-lattice/core");
|
|
4469
|
+
import_protocols37 = require("@axiom-lattice/protocols");
|
|
4470
4470
|
init_openLattice();
|
|
4471
4471
|
init_tenantOpenPlugins();
|
|
4472
4472
|
init_A2AAgentCardBuilder();
|
|
@@ -4490,9 +4490,9 @@ function createKbProvider(collectionService3) {
|
|
|
4490
4490
|
label: "Search collections",
|
|
4491
4491
|
description: "Search a knowledge base collection. Returns matched entries with content, metadata and similarity score.",
|
|
4492
4492
|
schema: {
|
|
4493
|
-
collection:
|
|
4494
|
-
query:
|
|
4495
|
-
limit:
|
|
4493
|
+
collection: import_zod22.z.string().min(1),
|
|
4494
|
+
query: import_zod22.z.string().min(1),
|
|
4495
|
+
limit: import_zod22.z.number().int().min(1).max(50).optional()
|
|
4496
4496
|
},
|
|
4497
4497
|
annotations: { readOnlyHint: true, destructiveHint: false }
|
|
4498
4498
|
}
|
|
@@ -4520,11 +4520,11 @@ function createKbProvider(collectionService3) {
|
|
|
4520
4520
|
}
|
|
4521
4521
|
};
|
|
4522
4522
|
}
|
|
4523
|
-
var
|
|
4523
|
+
var import_zod22;
|
|
4524
4524
|
var init_kbProvider = __esm({
|
|
4525
4525
|
"src/services/open/providers/kbProvider.ts"() {
|
|
4526
4526
|
"use strict";
|
|
4527
|
-
|
|
4527
|
+
import_zod22 = require("zod");
|
|
4528
4528
|
}
|
|
4529
4529
|
});
|
|
4530
4530
|
|
|
@@ -4562,7 +4562,7 @@ function createAgentProvider(deps = {}) {
|
|
|
4562
4562
|
toolName: `invoke_${assistantId}`,
|
|
4563
4563
|
label: name,
|
|
4564
4564
|
description: `Invoke the "${name}" agent${description ? ` \u2014 ${description}` : ""}. Returns its final reply.`,
|
|
4565
|
-
schema: { message:
|
|
4565
|
+
schema: { message: import_zod23.z.string().min(1) },
|
|
4566
4566
|
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
4567
4567
|
};
|
|
4568
4568
|
})
|
|
@@ -4585,7 +4585,7 @@ function createAgentProvider(deps = {}) {
|
|
|
4585
4585
|
} catch {
|
|
4586
4586
|
}
|
|
4587
4587
|
}
|
|
4588
|
-
const agent =
|
|
4588
|
+
const agent = import_core79.agentInstanceManager.getAgent({
|
|
4589
4589
|
tenant_id: ctx.tenantId,
|
|
4590
4590
|
assistant_id: assistantId,
|
|
4591
4591
|
thread_id: threadId,
|
|
@@ -4599,8 +4599,8 @@ function createAgentProvider(deps = {}) {
|
|
|
4599
4599
|
const stream = agent.chunkStream(messageId, [...STOP_TYPES]);
|
|
4600
4600
|
for await (const chunk of stream) {
|
|
4601
4601
|
const text = chunk.data?.content;
|
|
4602
|
-
if (chunk.type ===
|
|
4603
|
-
if (chunk.type ===
|
|
4602
|
+
if (chunk.type === import_protocols38.MessageChunkTypes.INTERRUPT) break;
|
|
4603
|
+
if (chunk.type === import_protocols38.MessageChunkTypes.AI && text) {
|
|
4604
4604
|
parts.push(text);
|
|
4605
4605
|
}
|
|
4606
4606
|
}
|
|
@@ -4610,14 +4610,14 @@ function createAgentProvider(deps = {}) {
|
|
|
4610
4610
|
}
|
|
4611
4611
|
};
|
|
4612
4612
|
}
|
|
4613
|
-
var
|
|
4613
|
+
var import_zod23, import_core79, import_protocols38, STOP_TYPES;
|
|
4614
4614
|
var init_agentProvider = __esm({
|
|
4615
4615
|
"src/services/open/providers/agentProvider.ts"() {
|
|
4616
4616
|
"use strict";
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
STOP_TYPES = [
|
|
4617
|
+
import_zod23 = require("zod");
|
|
4618
|
+
import_core79 = require("@axiom-lattice/core");
|
|
4619
|
+
import_protocols38 = require("@axiom-lattice/protocols");
|
|
4620
|
+
STOP_TYPES = [import_protocols38.MessageChunkTypes.MESSAGE_COMPLETED];
|
|
4621
4621
|
}
|
|
4622
4622
|
});
|
|
4623
4623
|
|
|
@@ -4647,12 +4647,12 @@ var resources_exports = {};
|
|
|
4647
4647
|
__export(resources_exports, {
|
|
4648
4648
|
ResourceController: () => ResourceController
|
|
4649
4649
|
});
|
|
4650
|
-
var import_bcryptjs,
|
|
4650
|
+
var import_bcryptjs, import_core80, ResourceController;
|
|
4651
4651
|
var init_resources = __esm({
|
|
4652
4652
|
"src/controllers/resources.ts"() {
|
|
4653
4653
|
"use strict";
|
|
4654
4654
|
import_bcryptjs = __toESM(require("bcryptjs"));
|
|
4655
|
-
|
|
4655
|
+
import_core80 = require("@axiom-lattice/core");
|
|
4656
4656
|
init_mime();
|
|
4657
4657
|
init_http_range();
|
|
4658
4658
|
init_sandboxGrants();
|
|
@@ -4669,14 +4669,14 @@ var init_resources = __esm({
|
|
|
4669
4669
|
if (!workspaceId || !projectId) {
|
|
4670
4670
|
return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
|
|
4671
4671
|
}
|
|
4672
|
-
const payload = (0,
|
|
4672
|
+
const payload = (0, import_core80.createSharePayload)(
|
|
4673
4673
|
tenantId,
|
|
4674
4674
|
workspaceId,
|
|
4675
4675
|
projectId,
|
|
4676
4676
|
userId,
|
|
4677
4677
|
body
|
|
4678
4678
|
);
|
|
4679
|
-
const token = (0,
|
|
4679
|
+
const token = (0, import_core80.generateToken)();
|
|
4680
4680
|
try {
|
|
4681
4681
|
await this.deps.store.create({ ...payload, token });
|
|
4682
4682
|
this.deps.logger.info(
|
|
@@ -4828,7 +4828,7 @@ var init_resources = __esm({
|
|
|
4828
4828
|
resourcePath: subPath
|
|
4829
4829
|
}, subPath, ext, request, reply);
|
|
4830
4830
|
}
|
|
4831
|
-
const volume = (0,
|
|
4831
|
+
const volume = (0, import_core80.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
|
|
4832
4832
|
return this._resolveAndServe(
|
|
4833
4833
|
volume,
|
|
4834
4834
|
subPath,
|
|
@@ -5052,8 +5052,8 @@ var import_static = __toESM(require("@fastify/static"));
|
|
|
5052
5052
|
var import_path = __toESM(require("path"));
|
|
5053
5053
|
|
|
5054
5054
|
// src/routes/index.ts
|
|
5055
|
-
var
|
|
5056
|
-
var
|
|
5055
|
+
var import_node_crypto26 = require("crypto");
|
|
5056
|
+
var import_protocols30 = require("@axiom-lattice/protocols");
|
|
5057
5057
|
|
|
5058
5058
|
// src/controllers/assistant.ts
|
|
5059
5059
|
var import_core = require("@axiom-lattice/core");
|
|
@@ -6910,9 +6910,9 @@ async function getConfig(request, reply) {
|
|
|
6910
6910
|
var import_core9 = require("@axiom-lattice/core");
|
|
6911
6911
|
async function getModels(request, reply) {
|
|
6912
6912
|
try {
|
|
6913
|
-
const allLattices = import_core9.modelLatticeManager.
|
|
6913
|
+
const allLattices = import_core9.modelLatticeManager.getAllLatticesByTenant("default");
|
|
6914
6914
|
const models = allLattices.map((lattice) => {
|
|
6915
|
-
const config = lattice.client.
|
|
6915
|
+
const config = lattice.client.getConfig();
|
|
6916
6916
|
const displayName = config.displayName || lattice.key.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
6917
6917
|
return {
|
|
6918
6918
|
key: lattice.key,
|
|
@@ -6920,7 +6920,13 @@ async function getModels(request, reply) {
|
|
|
6920
6920
|
provider: config.provider || "openai",
|
|
6921
6921
|
displayName,
|
|
6922
6922
|
streaming: config.streaming || false,
|
|
6923
|
-
|
|
6923
|
+
// The key itself is NOT returned. `hasApiKey` is what a client needs to
|
|
6924
|
+
// render "configured — leave blank to keep"; the value was previously
|
|
6925
|
+
// sent in cleartext to every caller of this endpoint, which put provider
|
|
6926
|
+
// credentials in any browser that loaded the model settings. Clients
|
|
6927
|
+
// that edit a model therefore leave the key field empty, which
|
|
6928
|
+
// `updateModels` reads as "keep the existing one".
|
|
6929
|
+
hasApiKey: Boolean(config.apiKey),
|
|
6924
6930
|
baseURL: config.baseURL || "",
|
|
6925
6931
|
maxTokens: config.maxTokens,
|
|
6926
6932
|
temperature: config.temperature,
|
|
@@ -6962,6 +6968,8 @@ async function updateModels(request, reply) {
|
|
|
6962
6968
|
continue;
|
|
6963
6969
|
}
|
|
6964
6970
|
try {
|
|
6971
|
+
const existingConfig = import_core9.modelLatticeManager.hasLattice(modelConfig.key) ? import_core9.modelLatticeManager.getModelLattice(modelConfig.key).client.getConfig() : void 0;
|
|
6972
|
+
const effectiveApiKey = modelConfig.clearApiKey ? void 0 : modelConfig.apiKey || existingConfig?.apiKey;
|
|
6965
6973
|
if (import_core9.modelLatticeManager.hasLattice(modelConfig.key)) {
|
|
6966
6974
|
import_core9.modelLatticeManager.removeLattice(modelConfig.key);
|
|
6967
6975
|
}
|
|
@@ -6970,7 +6978,7 @@ async function updateModels(request, reply) {
|
|
|
6970
6978
|
model: modelConfig.model,
|
|
6971
6979
|
displayName: modelConfig.displayName,
|
|
6972
6980
|
streaming: modelConfig.streaming ?? false,
|
|
6973
|
-
apiKey:
|
|
6981
|
+
apiKey: effectiveApiKey,
|
|
6974
6982
|
baseURL: modelConfig.baseURL,
|
|
6975
6983
|
maxTokens: modelConfig.maxTokens,
|
|
6976
6984
|
temperature: modelConfig.temperature,
|
|
@@ -16712,7 +16720,7 @@ function registerTaskRoutes(app2, controller) {
|
|
|
16712
16720
|
}
|
|
16713
16721
|
|
|
16714
16722
|
// src/routes/index.ts
|
|
16715
|
-
var
|
|
16723
|
+
var import_core51 = require("@axiom-lattice/core");
|
|
16716
16724
|
|
|
16717
16725
|
// src/controllers/tasks.ts
|
|
16718
16726
|
var import_protocols13 = require("@axiom-lattice/protocols");
|
|
@@ -20612,177 +20620,1298 @@ function registerCapabilityBundleRoutes(app2, controller) {
|
|
|
20612
20620
|
app2.post("/api/projects/:projectId/capability-preview", controller.postProjectPreview.bind(controller));
|
|
20613
20621
|
}
|
|
20614
20622
|
|
|
20615
|
-
// src/
|
|
20623
|
+
// src/controllers/model-providers.ts
|
|
20624
|
+
var import_zod11 = require("zod");
|
|
20625
|
+
|
|
20626
|
+
// src/services/model-provider/ModelProviderService.ts
|
|
20616
20627
|
var import_node_crypto14 = require("crypto");
|
|
20617
|
-
var
|
|
20618
|
-
|
|
20619
|
-
constructor(deps) {
|
|
20620
|
-
this.deps = deps;
|
|
20621
|
-
this.idFactory = deps.idFactory ?? import_node_crypto14.randomUUID;
|
|
20622
|
-
}
|
|
20623
|
-
/**
|
|
20624
|
-
* Authorizes read access and ensures the project's main room.
|
|
20625
|
-
*
|
|
20626
|
-
* @remarks
|
|
20627
|
-
* This method is safe to call while holding the process-wide Project
|
|
20628
|
-
* mutation mutex: it never re-enters it. Coordinator backfill is a separate
|
|
20629
|
-
* entry point ({@link ensureMainRoomWithCoordinator}) because it acquires
|
|
20630
|
-
* that mutex internally.
|
|
20631
|
-
*/
|
|
20632
|
-
async ensureMainRoom(input) {
|
|
20633
|
-
const { room } = await this.authorizedRoom(input);
|
|
20634
|
-
return room;
|
|
20635
|
-
}
|
|
20636
|
-
/**
|
|
20637
|
-
* Authorizes read access, ensures the main room, and lazily backfills the
|
|
20638
|
-
* built-in coordinator for business projects.
|
|
20639
|
-
*
|
|
20640
|
-
* @remarks
|
|
20641
|
-
* Only call this outside the Project mutation mutex (room open / bootstrap
|
|
20642
|
-
* paths); calling it under the mutex deadlocks on the coordinator ensurer.
|
|
20643
|
-
*/
|
|
20644
|
-
async ensureMainRoomWithCoordinator(input) {
|
|
20645
|
-
const { project, room } = await this.authorizedRoom(input);
|
|
20646
|
-
if ((project.kind === "business" || project.kind === "public") && this.deps.ensureCoordinator) {
|
|
20647
|
-
try {
|
|
20648
|
-
await this.deps.ensureCoordinator({
|
|
20649
|
-
tenantId: input.tenantId,
|
|
20650
|
-
workspaceId: project.workspaceId,
|
|
20651
|
-
projectId: project.id,
|
|
20652
|
-
roomId: room.id
|
|
20653
|
-
});
|
|
20654
|
-
} catch (error) {
|
|
20655
|
-
this.deps.log?.warn("project-room:coordinator-ensure-failed", {
|
|
20656
|
-
tenantId: input.tenantId,
|
|
20657
|
-
projectId: project.id,
|
|
20658
|
-
errorName: error instanceof Error ? error.name : "UnknownError"
|
|
20659
|
-
});
|
|
20660
|
-
}
|
|
20661
|
-
}
|
|
20662
|
-
return room;
|
|
20663
|
-
}
|
|
20664
|
-
async authorizedRoom(input) {
|
|
20665
|
-
const { project } = await this.deps.accessService.requireProjectAccess({
|
|
20666
|
-
...input,
|
|
20667
|
-
capability: "read"
|
|
20668
|
-
});
|
|
20669
|
-
const room = await this.deps.roomStore.ensureMainRoom({
|
|
20670
|
-
id: this.idFactory(),
|
|
20671
|
-
tenantId: input.tenantId,
|
|
20672
|
-
workspaceId: project.workspaceId,
|
|
20673
|
-
projectId: project.id,
|
|
20674
|
-
name: project.name
|
|
20675
|
-
});
|
|
20676
|
-
return { project, room };
|
|
20677
|
-
}
|
|
20678
|
-
};
|
|
20628
|
+
var import_protocols16 = require("@axiom-lattice/protocols");
|
|
20629
|
+
var import_core47 = require("@axiom-lattice/core");
|
|
20679
20630
|
|
|
20680
|
-
// src/services/
|
|
20681
|
-
var
|
|
20682
|
-
|
|
20683
|
-
constructor(code, statusCode, message) {
|
|
20631
|
+
// src/services/model-provider/errors.ts
|
|
20632
|
+
var ModelProviderServiceError = class extends Error {
|
|
20633
|
+
/** Creates a model provider service error. */
|
|
20634
|
+
constructor(code, statusCode, message, retryable = false) {
|
|
20684
20635
|
super(message);
|
|
20685
20636
|
this.code = code;
|
|
20686
20637
|
this.statusCode = statusCode;
|
|
20687
|
-
this.
|
|
20638
|
+
this.retryable = retryable;
|
|
20639
|
+
this.name = "ModelProviderServiceError";
|
|
20688
20640
|
}
|
|
20689
20641
|
};
|
|
20690
|
-
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
|
|
20694
|
-
var
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
const membership = await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, actor3.userId);
|
|
20703
|
-
if (!membership || membership.status !== "active") this.accessDenied();
|
|
20704
|
-
return this.deps.membershipStore.list(actor3.tenantId, actor3.projectId);
|
|
20705
|
-
}
|
|
20706
|
-
async add(actor3, input) {
|
|
20707
|
-
return this.withProjectLock(actor3, async () => {
|
|
20708
|
-
const access = await this.authorize(actor3);
|
|
20709
|
-
if (input.role === "owner" && access.role !== "owner") this.accessDenied();
|
|
20710
|
-
if (!await this.deps.userTenantLinkStore.getLink(input.userId, actor3.tenantId)) {
|
|
20711
|
-
throw new ProjectMembershipServiceError("TARGET_USER_NOT_IN_TENANT", 400, "Target user is not in the tenant");
|
|
20712
|
-
}
|
|
20713
|
-
if (await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, input.userId)) {
|
|
20714
|
-
throw new ProjectMembershipServiceError("MEMBERSHIP_EXISTS", 409, "Project membership already exists");
|
|
20715
|
-
}
|
|
20716
|
-
try {
|
|
20717
|
-
const created = await this.deps.membershipStore.create({
|
|
20718
|
-
id: this.idFactory(),
|
|
20719
|
-
tenantId: actor3.tenantId,
|
|
20720
|
-
projectId: actor3.projectId,
|
|
20721
|
-
userId: input.userId,
|
|
20722
|
-
role: input.role,
|
|
20723
|
-
status: "active"
|
|
20724
|
-
});
|
|
20725
|
-
this.publish(created, "added", `membership:${created.id}:add:${created.joinedAt.toISOString()}`);
|
|
20726
|
-
return created;
|
|
20727
|
-
} catch (error) {
|
|
20728
|
-
if (error instanceof Error && error.name === "DuplicateProjectMembershipError") {
|
|
20729
|
-
throw new ProjectMembershipServiceError("MEMBERSHIP_EXISTS", 409, "Project membership already exists");
|
|
20730
|
-
}
|
|
20731
|
-
if (isMembershipConflict(error)) {
|
|
20732
|
-
throw new ProjectMembershipServiceError("MEMBERSHIP_CONFLICT", 409, "Project membership could not be created");
|
|
20733
|
-
}
|
|
20734
|
-
throw error;
|
|
20735
|
-
}
|
|
20736
|
-
});
|
|
20642
|
+
|
|
20643
|
+
// src/services/model-provider/discovery.ts
|
|
20644
|
+
var MAX_DISCOVERED_MODELS = 1e3;
|
|
20645
|
+
var MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
20646
|
+
var DEFAULT_DISCOVERY_TIMEOUT_MS = 8e3;
|
|
20647
|
+
function resolveDiscoveryTimeoutMs(raw = process.env.LATTICE_MODEL_DISCOVERY_TIMEOUT_MS) {
|
|
20648
|
+
if (!raw) {
|
|
20649
|
+
return DEFAULT_DISCOVERY_TIMEOUT_MS;
|
|
20650
|
+
}
|
|
20651
|
+
const parsed = Number.parseInt(raw, 10);
|
|
20652
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
20653
|
+
return DEFAULT_DISCOVERY_TIMEOUT_MS;
|
|
20737
20654
|
}
|
|
20738
|
-
|
|
20739
|
-
|
|
20740
|
-
|
|
20741
|
-
|
|
20742
|
-
|
|
20743
|
-
|
|
20744
|
-
|
|
20745
|
-
|
|
20746
|
-
|
|
20747
|
-
|
|
20655
|
+
return parsed;
|
|
20656
|
+
}
|
|
20657
|
+
function buildModelsEndpoint(baseURL) {
|
|
20658
|
+
const url = new URL(baseURL);
|
|
20659
|
+
const trimmed = url.pathname.replace(/\/+$/, "");
|
|
20660
|
+
url.pathname = trimmed.endsWith("/v1") ? `${trimmed}/models` : `${trimmed}/v1/models`;
|
|
20661
|
+
url.search = "";
|
|
20662
|
+
url.hash = "";
|
|
20663
|
+
return url.toString();
|
|
20664
|
+
}
|
|
20665
|
+
function parseModelsPayload(payload) {
|
|
20666
|
+
const entries = Array.isArray(payload) ? payload : typeof payload === "object" && payload !== null ? payload.data : void 0;
|
|
20667
|
+
if (!Array.isArray(entries)) {
|
|
20668
|
+
throw new ModelProviderServiceError(
|
|
20669
|
+
"UPSTREAM_INVALID_RESPONSE",
|
|
20670
|
+
502,
|
|
20671
|
+
"Upstream did not return a model list"
|
|
20672
|
+
);
|
|
20748
20673
|
}
|
|
20749
|
-
|
|
20750
|
-
|
|
20751
|
-
|
|
20752
|
-
|
|
20753
|
-
|
|
20754
|
-
|
|
20755
|
-
|
|
20756
|
-
|
|
20757
|
-
|
|
20674
|
+
const models = [];
|
|
20675
|
+
for (const entry of entries.slice(0, MAX_DISCOVERED_MODELS)) {
|
|
20676
|
+
if (typeof entry !== "object" || entry === null) {
|
|
20677
|
+
continue;
|
|
20678
|
+
}
|
|
20679
|
+
const record = entry;
|
|
20680
|
+
const id3 = typeof record.id === "string" ? record.id.trim() : "";
|
|
20681
|
+
if (id3.length === 0) {
|
|
20682
|
+
continue;
|
|
20683
|
+
}
|
|
20684
|
+
models.push({
|
|
20685
|
+
id: id3,
|
|
20686
|
+
displayName: typeof record.name === "string" ? record.name : void 0,
|
|
20687
|
+
ownedBy: typeof record.owned_by === "string" ? record.owned_by : void 0,
|
|
20688
|
+
createdAt: parseUpstreamCreated(record.created),
|
|
20689
|
+
raw: record
|
|
20758
20690
|
});
|
|
20759
20691
|
}
|
|
20760
|
-
|
|
20761
|
-
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
return
|
|
20765
|
-
}
|
|
20766
|
-
async target(actor3, id3) {
|
|
20767
|
-
const target = (await this.deps.membershipStore.list(actor3.tenantId, actor3.projectId)).find((item) => item.id === id3);
|
|
20768
|
-
if (!target) throw new ProjectMembershipServiceError("MEMBERSHIP_NOT_FOUND", 404, "Project membership not found");
|
|
20769
|
-
return target;
|
|
20692
|
+
return models;
|
|
20693
|
+
}
|
|
20694
|
+
function parseUpstreamCreated(created) {
|
|
20695
|
+
if (typeof created !== "number" || !Number.isFinite(created)) {
|
|
20696
|
+
return void 0;
|
|
20770
20697
|
}
|
|
20771
|
-
|
|
20772
|
-
|
|
20773
|
-
|
|
20698
|
+
const date = new Date(created * 1e3);
|
|
20699
|
+
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
20700
|
+
}
|
|
20701
|
+
var openAiCompatibleAdapter = {
|
|
20702
|
+
protocol: "openai-compatible",
|
|
20703
|
+
async discover({ baseURL, apiKey, signal }) {
|
|
20704
|
+
const endpoint = buildModelsEndpoint(baseURL);
|
|
20705
|
+
const headers = { Accept: "application/json" };
|
|
20706
|
+
if (apiKey) {
|
|
20707
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
20774
20708
|
}
|
|
20775
|
-
|
|
20776
|
-
|
|
20709
|
+
let response;
|
|
20710
|
+
try {
|
|
20711
|
+
response = await fetch(endpoint, {
|
|
20712
|
+
method: "GET",
|
|
20713
|
+
headers,
|
|
20714
|
+
// Never follow redirects: a redirected request could reach an
|
|
20715
|
+
// address that bypassed the SSRF checks.
|
|
20716
|
+
redirect: "manual",
|
|
20717
|
+
signal
|
|
20718
|
+
});
|
|
20719
|
+
} catch (error) {
|
|
20720
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
20721
|
+
throw new ModelProviderServiceError(
|
|
20722
|
+
"UPSTREAM_TIMEOUT",
|
|
20723
|
+
504,
|
|
20724
|
+
"Upstream did not respond in time",
|
|
20725
|
+
true
|
|
20726
|
+
);
|
|
20727
|
+
}
|
|
20728
|
+
throw new ModelProviderServiceError(
|
|
20729
|
+
"UPSTREAM_UNREACHABLE",
|
|
20730
|
+
502,
|
|
20731
|
+
"Upstream could not be reached",
|
|
20732
|
+
true
|
|
20733
|
+
);
|
|
20734
|
+
}
|
|
20735
|
+
if (response.status >= 300 && response.status < 400) {
|
|
20736
|
+
throw new ModelProviderServiceError(
|
|
20737
|
+
"UPSTREAM_REDIRECT_BLOCKED",
|
|
20738
|
+
502,
|
|
20739
|
+
"Upstream returned a redirect; configure the final URL directly"
|
|
20740
|
+
);
|
|
20741
|
+
}
|
|
20742
|
+
if (response.status === 401 || response.status === 403) {
|
|
20743
|
+
throw new ModelProviderServiceError(
|
|
20744
|
+
"UPSTREAM_AUTH_FAILED",
|
|
20745
|
+
502,
|
|
20746
|
+
"Upstream rejected the configured credentials"
|
|
20747
|
+
);
|
|
20748
|
+
}
|
|
20749
|
+
if (response.status === 429) {
|
|
20750
|
+
throw new ModelProviderServiceError(
|
|
20751
|
+
"UPSTREAM_RATE_LIMITED",
|
|
20752
|
+
502,
|
|
20753
|
+
"Upstream rate limited the request",
|
|
20754
|
+
true
|
|
20755
|
+
);
|
|
20756
|
+
}
|
|
20757
|
+
if (!response.ok) {
|
|
20758
|
+
throw new ModelProviderServiceError(
|
|
20759
|
+
"UPSTREAM_UNREACHABLE",
|
|
20760
|
+
502,
|
|
20761
|
+
"Upstream returned an error response",
|
|
20762
|
+
response.status >= 500
|
|
20763
|
+
);
|
|
20764
|
+
}
|
|
20765
|
+
const body = await readBodyWithLimit(response, MAX_RESPONSE_BYTES);
|
|
20766
|
+
let payload;
|
|
20767
|
+
try {
|
|
20768
|
+
payload = JSON.parse(body);
|
|
20769
|
+
} catch {
|
|
20770
|
+
throw new ModelProviderServiceError(
|
|
20771
|
+
"UPSTREAM_INVALID_RESPONSE",
|
|
20772
|
+
502,
|
|
20773
|
+
"Upstream response was not valid JSON"
|
|
20774
|
+
);
|
|
20777
20775
|
}
|
|
20776
|
+
return parseModelsPayload(payload);
|
|
20778
20777
|
}
|
|
20779
|
-
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
throw new ProjectMembershipServiceError("LAST_OWNER", 409, "The project must retain an owner");
|
|
20778
|
+
};
|
|
20779
|
+
async function readBodyWithLimit(response, maxBytes) {
|
|
20780
|
+
if (!response.body) {
|
|
20781
|
+
return response.text();
|
|
20784
20782
|
}
|
|
20785
|
-
|
|
20783
|
+
const reader = response.body.getReader();
|
|
20784
|
+
const decoder = new TextDecoder();
|
|
20785
|
+
let received = 0;
|
|
20786
|
+
let text = "";
|
|
20787
|
+
try {
|
|
20788
|
+
for (; ; ) {
|
|
20789
|
+
const { done, value } = await reader.read();
|
|
20790
|
+
if (done) {
|
|
20791
|
+
break;
|
|
20792
|
+
}
|
|
20793
|
+
if (!value) {
|
|
20794
|
+
continue;
|
|
20795
|
+
}
|
|
20796
|
+
received += value.byteLength;
|
|
20797
|
+
if (received > maxBytes) {
|
|
20798
|
+
await reader.cancel();
|
|
20799
|
+
throw new ModelProviderServiceError(
|
|
20800
|
+
"UPSTREAM_INVALID_RESPONSE",
|
|
20801
|
+
502,
|
|
20802
|
+
"Upstream response was too large"
|
|
20803
|
+
);
|
|
20804
|
+
}
|
|
20805
|
+
text += decoder.decode(value, { stream: true });
|
|
20806
|
+
}
|
|
20807
|
+
} finally {
|
|
20808
|
+
reader.releaseLock();
|
|
20809
|
+
}
|
|
20810
|
+
return text + decoder.decode();
|
|
20811
|
+
}
|
|
20812
|
+
var adapters = /* @__PURE__ */ new Map([
|
|
20813
|
+
[openAiCompatibleAdapter.protocol, openAiCompatibleAdapter]
|
|
20814
|
+
]);
|
|
20815
|
+
function getDiscoveryAdapter(protocol) {
|
|
20816
|
+
return adapters.get(protocol);
|
|
20817
|
+
}
|
|
20818
|
+
|
|
20819
|
+
// src/services/model-provider/ModelProviderService.ts
|
|
20820
|
+
var PHASE1_REGISTRY_TENANT = "default";
|
|
20821
|
+
var ALLOWED_LLM_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
20822
|
+
var unsafeReasonToErrorCode = {
|
|
20823
|
+
INVALID_BASE_URL: "INVALID_BASE_URL",
|
|
20824
|
+
UNSUPPORTED_SCHEME: "INVALID_BASE_URL",
|
|
20825
|
+
CREDENTIALS_IN_URL: "INVALID_BASE_URL"
|
|
20826
|
+
};
|
|
20827
|
+
var ModelProviderService = class {
|
|
20828
|
+
constructor(deps) {
|
|
20829
|
+
this.deps = deps;
|
|
20830
|
+
this.idFactory = deps.idFactory ?? (() => (0, import_node_crypto14.randomUUID)());
|
|
20831
|
+
}
|
|
20832
|
+
/**
|
|
20833
|
+
* List the tenant's provider configurations.
|
|
20834
|
+
*/
|
|
20835
|
+
async list(userId, tenantId) {
|
|
20836
|
+
await this.assertMember(userId, tenantId);
|
|
20837
|
+
const providers = await this.deps.providerStore.listProviders(tenantId);
|
|
20838
|
+
const models = await this.deps.providerStore.listModels(tenantId);
|
|
20839
|
+
const modelCounts = /* @__PURE__ */ new Map();
|
|
20840
|
+
for (const model of models) {
|
|
20841
|
+
modelCounts.set(
|
|
20842
|
+
model.providerId,
|
|
20843
|
+
(modelCounts.get(model.providerId) ?? 0) + 1
|
|
20844
|
+
);
|
|
20845
|
+
}
|
|
20846
|
+
return providers.map(
|
|
20847
|
+
(provider) => this.toPublicEntry(provider, modelCounts.get(provider.id) ?? 0)
|
|
20848
|
+
);
|
|
20849
|
+
}
|
|
20850
|
+
/**
|
|
20851
|
+
* Read one provider configuration.
|
|
20852
|
+
*/
|
|
20853
|
+
async get(userId, tenantId, id3) {
|
|
20854
|
+
await this.assertMember(userId, tenantId);
|
|
20855
|
+
const provider = await this.requireProvider(tenantId, id3);
|
|
20856
|
+
const models = await this.deps.providerStore.listModels(tenantId, id3);
|
|
20857
|
+
return this.toPublicEntry(provider, models.length);
|
|
20858
|
+
}
|
|
20859
|
+
/**
|
|
20860
|
+
* The full model catalog visible to a tenant.
|
|
20861
|
+
*/
|
|
20862
|
+
async catalog(userId, tenantId) {
|
|
20863
|
+
await this.assertMember(userId, tenantId);
|
|
20864
|
+
const providers = await this.deps.providerStore.listProviders(tenantId);
|
|
20865
|
+
const snapshot = await this.deps.providerStore.listModels(tenantId);
|
|
20866
|
+
const providerNames = new Map(
|
|
20867
|
+
providers.map((provider) => [provider.id, provider.name])
|
|
20868
|
+
);
|
|
20869
|
+
const disabledProviderIds = new Set(
|
|
20870
|
+
providers.filter((provider) => !provider.enabled).map((p) => p.id)
|
|
20871
|
+
);
|
|
20872
|
+
const modelCounts = /* @__PURE__ */ new Map();
|
|
20873
|
+
for (const model of snapshot) {
|
|
20874
|
+
modelCounts.set(
|
|
20875
|
+
model.providerId,
|
|
20876
|
+
(modelCounts.get(model.providerId) ?? 0) + 1
|
|
20877
|
+
);
|
|
20878
|
+
}
|
|
20879
|
+
const models = snapshot.map((model) => ({
|
|
20880
|
+
modelKey: (0, import_protocols16.toModelKey)(model.providerId, model.modelId),
|
|
20881
|
+
displayName: model.displayName,
|
|
20882
|
+
providerId: model.providerId,
|
|
20883
|
+
providerName: providerNames.get(model.providerId) ?? model.providerId,
|
|
20884
|
+
ownedBy: model.ownedBy,
|
|
20885
|
+
source: "snapshot",
|
|
20886
|
+
enabled: model.enabled,
|
|
20887
|
+
stale: model.stale ?? false,
|
|
20888
|
+
executable: model.enabled && // A disabled provider is a kill switch: its models stay visible in the
|
|
20889
|
+
// catalog but are no longer reported as runnable.
|
|
20890
|
+
!disabledProviderIds.has(model.providerId) && this.deps.registry.has(
|
|
20891
|
+
tenantId,
|
|
20892
|
+
(0, import_protocols16.toModelKey)(model.providerId, model.modelId)
|
|
20893
|
+
)
|
|
20894
|
+
}));
|
|
20895
|
+
const known = new Set(models.map((model) => model.modelKey));
|
|
20896
|
+
for (const modelKey2 of this.deps.registry.listRegistered(tenantId)) {
|
|
20897
|
+
if (known.has(modelKey2)) {
|
|
20898
|
+
continue;
|
|
20899
|
+
}
|
|
20900
|
+
models.push({
|
|
20901
|
+
modelKey: modelKey2,
|
|
20902
|
+
providerId: "",
|
|
20903
|
+
providerName: "registered",
|
|
20904
|
+
source: "registered",
|
|
20905
|
+
enabled: true,
|
|
20906
|
+
stale: false,
|
|
20907
|
+
executable: true
|
|
20908
|
+
});
|
|
20909
|
+
}
|
|
20910
|
+
return {
|
|
20911
|
+
providers: providers.map(
|
|
20912
|
+
(provider) => this.toPublicEntry(provider, modelCounts.get(provider.id) ?? 0)
|
|
20913
|
+
),
|
|
20914
|
+
models
|
|
20915
|
+
};
|
|
20916
|
+
}
|
|
20917
|
+
/**
|
|
20918
|
+
* Create a provider configuration. Does not run discovery.
|
|
20919
|
+
*/
|
|
20920
|
+
async create(userId, tenantId, input) {
|
|
20921
|
+
await this.assertAdmin(userId, tenantId);
|
|
20922
|
+
this.assertHasApiKey(input.apiKey);
|
|
20923
|
+
if (!getDiscoveryAdapter(input.protocol)) {
|
|
20924
|
+
throw new ModelProviderServiceError(
|
|
20925
|
+
"UNSUPPORTED_PROTOCOL",
|
|
20926
|
+
400,
|
|
20927
|
+
`Unsupported provider protocol`
|
|
20928
|
+
);
|
|
20929
|
+
}
|
|
20930
|
+
await this.assertSafeUrl(input.baseURL);
|
|
20931
|
+
const existing = await this.deps.providerStore.getProviderByName(
|
|
20932
|
+
tenantId,
|
|
20933
|
+
input.name
|
|
20934
|
+
);
|
|
20935
|
+
if (existing) {
|
|
20936
|
+
throw new ModelProviderServiceError(
|
|
20937
|
+
"PROVIDER_NAME_CONFLICT",
|
|
20938
|
+
409,
|
|
20939
|
+
"A provider with this name already exists"
|
|
20940
|
+
);
|
|
20941
|
+
}
|
|
20942
|
+
const id3 = this.idFactory();
|
|
20943
|
+
try {
|
|
20944
|
+
const created = await this.deps.providerStore.createProvider(
|
|
20945
|
+
tenantId,
|
|
20946
|
+
id3,
|
|
20947
|
+
{ ...input, enabled: input.enabled ?? true }
|
|
20948
|
+
);
|
|
20949
|
+
return this.toPublicEntry(created, 0);
|
|
20950
|
+
} catch (error) {
|
|
20951
|
+
throw this.mapStoreError(error);
|
|
20952
|
+
}
|
|
20953
|
+
}
|
|
20954
|
+
/**
|
|
20955
|
+
* Update a provider configuration.
|
|
20956
|
+
* An omitted or empty `apiKey` keeps the stored key.
|
|
20957
|
+
*/
|
|
20958
|
+
async update(userId, tenantId, id3, updates) {
|
|
20959
|
+
await this.assertAdmin(userId, tenantId);
|
|
20960
|
+
const existing = await this.requireProvider(tenantId, id3);
|
|
20961
|
+
if (updates.clearApiKey || !(updates.apiKey?.trim() || existing.apiKey)) {
|
|
20962
|
+
this.assertHasApiKey(void 0);
|
|
20963
|
+
}
|
|
20964
|
+
if (updates.protocol && !getDiscoveryAdapter(updates.protocol)) {
|
|
20965
|
+
throw new ModelProviderServiceError(
|
|
20966
|
+
"UNSUPPORTED_PROTOCOL",
|
|
20967
|
+
400,
|
|
20968
|
+
`Unsupported provider protocol`
|
|
20969
|
+
);
|
|
20970
|
+
}
|
|
20971
|
+
if (updates.baseURL && updates.baseURL !== existing.baseURL) {
|
|
20972
|
+
await this.assertSafeUrl(updates.baseURL);
|
|
20973
|
+
}
|
|
20974
|
+
if (updates.name && updates.name !== existing.name) {
|
|
20975
|
+
const conflicting = await this.deps.providerStore.getProviderByName(
|
|
20976
|
+
tenantId,
|
|
20977
|
+
updates.name
|
|
20978
|
+
);
|
|
20979
|
+
if (conflicting && conflicting.id !== id3) {
|
|
20980
|
+
throw new ModelProviderServiceError(
|
|
20981
|
+
"PROVIDER_NAME_CONFLICT",
|
|
20982
|
+
409,
|
|
20983
|
+
"A provider with this name already exists"
|
|
20984
|
+
);
|
|
20985
|
+
}
|
|
20986
|
+
}
|
|
20987
|
+
try {
|
|
20988
|
+
const updated = await this.deps.providerStore.updateProvider(
|
|
20989
|
+
tenantId,
|
|
20990
|
+
id3,
|
|
20991
|
+
updates
|
|
20992
|
+
);
|
|
20993
|
+
if (!updated) {
|
|
20994
|
+
throw new ModelProviderServiceError(
|
|
20995
|
+
"PROVIDER_NOT_FOUND",
|
|
20996
|
+
404,
|
|
20997
|
+
"Model provider not found"
|
|
20998
|
+
);
|
|
20999
|
+
}
|
|
21000
|
+
const models = await this.deps.providerStore.listModels(tenantId, id3);
|
|
21001
|
+
this.syncRegistry(tenantId, updated, models);
|
|
21002
|
+
return this.toPublicEntry(updated, models.length);
|
|
21003
|
+
} catch (error) {
|
|
21004
|
+
throw this.mapStoreError(error);
|
|
21005
|
+
}
|
|
21006
|
+
}
|
|
21007
|
+
/**
|
|
21008
|
+
* Delete a provider configuration, its snapshot, and any registry entries
|
|
21009
|
+
* that no other provider in the tenant still supplies.
|
|
21010
|
+
*/
|
|
21011
|
+
async remove(userId, tenantId, id3) {
|
|
21012
|
+
await this.assertAdmin(userId, tenantId);
|
|
21013
|
+
await this.requireProvider(tenantId, id3);
|
|
21014
|
+
const snapshot = await this.deps.providerStore.listModels(tenantId, id3);
|
|
21015
|
+
await this.deps.providerStore.deleteProvider(tenantId, id3);
|
|
21016
|
+
for (const model of snapshot) {
|
|
21017
|
+
this.deps.registry.remove(tenantId, (0, import_protocols16.toModelKey)(id3, model.modelId));
|
|
21018
|
+
}
|
|
21019
|
+
}
|
|
21020
|
+
/**
|
|
21021
|
+
* Discover the provider's models and replace its snapshot.
|
|
21022
|
+
*/
|
|
21023
|
+
async discover(userId, tenantId, id3) {
|
|
21024
|
+
await this.assertAdmin(userId, tenantId);
|
|
21025
|
+
const provider = await this.requireProvider(tenantId, id3);
|
|
21026
|
+
if (!provider.enabled) {
|
|
21027
|
+
throw new ModelProviderServiceError(
|
|
21028
|
+
"PROVIDER_DISABLED",
|
|
21029
|
+
400,
|
|
21030
|
+
"Model provider is disabled"
|
|
21031
|
+
);
|
|
21032
|
+
}
|
|
21033
|
+
const adapter = getDiscoveryAdapter(provider.protocol);
|
|
21034
|
+
if (!adapter) {
|
|
21035
|
+
throw new ModelProviderServiceError(
|
|
21036
|
+
"UNSUPPORTED_PROTOCOL",
|
|
21037
|
+
400,
|
|
21038
|
+
`Unsupported provider protocol`
|
|
21039
|
+
);
|
|
21040
|
+
}
|
|
21041
|
+
await this.assertSafeUrl(provider.baseURL);
|
|
21042
|
+
const controller = new AbortController();
|
|
21043
|
+
const timeout = setTimeout(
|
|
21044
|
+
() => controller.abort(),
|
|
21045
|
+
resolveDiscoveryTimeoutMs()
|
|
21046
|
+
);
|
|
21047
|
+
const previous = this.shouldRegister(tenantId) ? await this.deps.providerStore.listModels(tenantId, id3) : [];
|
|
21048
|
+
let discovered;
|
|
21049
|
+
try {
|
|
21050
|
+
discovered = await adapter.discover({
|
|
21051
|
+
baseURL: provider.baseURL,
|
|
21052
|
+
apiKey: provider.apiKey,
|
|
21053
|
+
signal: controller.signal
|
|
21054
|
+
});
|
|
21055
|
+
} catch (error) {
|
|
21056
|
+
if (error instanceof ModelProviderServiceError) {
|
|
21057
|
+
await this.deps.providerStore.markProviderStatus(
|
|
21058
|
+
tenantId,
|
|
21059
|
+
id3,
|
|
21060
|
+
"error",
|
|
21061
|
+
error.code
|
|
21062
|
+
);
|
|
21063
|
+
throw error;
|
|
21064
|
+
}
|
|
21065
|
+
throw error;
|
|
21066
|
+
} finally {
|
|
21067
|
+
clearTimeout(timeout);
|
|
21068
|
+
}
|
|
21069
|
+
const now = /* @__PURE__ */ new Date();
|
|
21070
|
+
const inserted = await this.deps.providerStore.replaceProviderModels(
|
|
21071
|
+
tenantId,
|
|
21072
|
+
id3,
|
|
21073
|
+
discovered
|
|
21074
|
+
);
|
|
21075
|
+
await this.deps.providerStore.markProviderStatus(
|
|
21076
|
+
tenantId,
|
|
21077
|
+
id3,
|
|
21078
|
+
"ok",
|
|
21079
|
+
void 0,
|
|
21080
|
+
now
|
|
21081
|
+
);
|
|
21082
|
+
const registered = [];
|
|
21083
|
+
if (this.shouldRegister(tenantId)) {
|
|
21084
|
+
const snapshot = await this.deps.providerStore.listModels(tenantId, id3);
|
|
21085
|
+
const currentKeys = new Set(
|
|
21086
|
+
snapshot.map((model) => (0, import_protocols16.toModelKey)(id3, model.modelId))
|
|
21087
|
+
);
|
|
21088
|
+
for (const model of previous) {
|
|
21089
|
+
const modelKey2 = (0, import_protocols16.toModelKey)(id3, model.modelId);
|
|
21090
|
+
if (!currentKeys.has(modelKey2)) {
|
|
21091
|
+
this.deps.registry.remove(tenantId, modelKey2);
|
|
21092
|
+
}
|
|
21093
|
+
}
|
|
21094
|
+
registered.push(...this.syncRegistry(tenantId, provider, snapshot));
|
|
21095
|
+
}
|
|
21096
|
+
return {
|
|
21097
|
+
discovered,
|
|
21098
|
+
registered,
|
|
21099
|
+
status: "ok"
|
|
21100
|
+
};
|
|
21101
|
+
}
|
|
21102
|
+
/**
|
|
21103
|
+
* Bring the execution registry in line with a provider's current state.
|
|
21104
|
+
*
|
|
21105
|
+
* Registration copies `apiKey` and `baseURL` by value, so a rotated
|
|
21106
|
+
* credential or a changed endpoint only takes effect once the model is
|
|
21107
|
+
* re-registered. Disabled models — and every model of a disabled provider —
|
|
21108
|
+
* are removed instead.
|
|
21109
|
+
*
|
|
21110
|
+
* @returns The model keys left registered
|
|
21111
|
+
*/
|
|
21112
|
+
/**
|
|
21113
|
+
* Probe an upstream without touching the store.
|
|
21114
|
+
*
|
|
21115
|
+
* Powers the provider form's "test latency" and "list models" actions, which
|
|
21116
|
+
* validate a base URL and API key the user has typed but not yet saved. The
|
|
21117
|
+
* URL is validated exactly as on create/update and is never persisted, so the
|
|
21118
|
+
* "discover resolves its URL from the store" rule is not weakened — nothing
|
|
21119
|
+
* a probe learns can reach the snapshot.
|
|
21120
|
+
*/
|
|
21121
|
+
async probe(userId, tenantId, input) {
|
|
21122
|
+
await this.assertAdmin(userId, tenantId);
|
|
21123
|
+
const protocol = input.protocol ?? "openai-compatible";
|
|
21124
|
+
const adapter = getDiscoveryAdapter(protocol);
|
|
21125
|
+
if (!adapter) {
|
|
21126
|
+
throw new ModelProviderServiceError(
|
|
21127
|
+
"UNSUPPORTED_PROTOCOL",
|
|
21128
|
+
400,
|
|
21129
|
+
`Unsupported provider protocol`
|
|
21130
|
+
);
|
|
21131
|
+
}
|
|
21132
|
+
await this.assertSafeUrl(input.baseURL);
|
|
21133
|
+
let apiKey = input.apiKey || void 0;
|
|
21134
|
+
if (!apiKey && input.providerId) {
|
|
21135
|
+
const existing = await this.requireProvider(tenantId, input.providerId);
|
|
21136
|
+
apiKey = existing.apiKey;
|
|
21137
|
+
}
|
|
21138
|
+
const controller = new AbortController();
|
|
21139
|
+
const timeout = setTimeout(
|
|
21140
|
+
() => controller.abort(),
|
|
21141
|
+
resolveDiscoveryTimeoutMs()
|
|
21142
|
+
);
|
|
21143
|
+
const startedAt = Date.now();
|
|
21144
|
+
try {
|
|
21145
|
+
const models = await adapter.discover({
|
|
21146
|
+
baseURL: input.baseURL,
|
|
21147
|
+
apiKey,
|
|
21148
|
+
signal: controller.signal
|
|
21149
|
+
});
|
|
21150
|
+
return {
|
|
21151
|
+
reachable: true,
|
|
21152
|
+
latencyMs: Date.now() - startedAt,
|
|
21153
|
+
models
|
|
21154
|
+
};
|
|
21155
|
+
} catch (error) {
|
|
21156
|
+
if (error instanceof ModelProviderServiceError) {
|
|
21157
|
+
return {
|
|
21158
|
+
reachable: false,
|
|
21159
|
+
latencyMs: Date.now() - startedAt,
|
|
21160
|
+
models: [],
|
|
21161
|
+
error: error.code
|
|
21162
|
+
};
|
|
21163
|
+
}
|
|
21164
|
+
throw error;
|
|
21165
|
+
} finally {
|
|
21166
|
+
clearTimeout(timeout);
|
|
21167
|
+
}
|
|
21168
|
+
}
|
|
21169
|
+
/**
|
|
21170
|
+
* Set exactly which models this provider contributes.
|
|
21171
|
+
*
|
|
21172
|
+
* This is the "add model" action: the caller passes the ids it picked from
|
|
21173
|
+
* the live list, and the provider's model set becomes precisely those. Ids
|
|
21174
|
+
* are expected to have come from a probe — model names cannot be guessed,
|
|
21175
|
+
* since a wrong id surfaces later as "model not found" or an authorisation
|
|
21176
|
+
* error rather than at configuration time.
|
|
21177
|
+
*/
|
|
21178
|
+
async setModels(userId, tenantId, id3, selection) {
|
|
21179
|
+
await this.assertAdmin(userId, tenantId);
|
|
21180
|
+
const provider = await this.requireProvider(tenantId, id3);
|
|
21181
|
+
const previous = await this.deps.providerStore.listModels(tenantId, id3);
|
|
21182
|
+
const previousById = new Map(
|
|
21183
|
+
previous.map((model) => [model.modelId, model])
|
|
21184
|
+
);
|
|
21185
|
+
await this.deps.providerStore.replaceProviderModels(
|
|
21186
|
+
tenantId,
|
|
21187
|
+
id3,
|
|
21188
|
+
selection.map((entry) => ({
|
|
21189
|
+
id: entry.modelId,
|
|
21190
|
+
enabled: entry.enabled,
|
|
21191
|
+
// A model the upstream stopped offering stays in the set, flagged
|
|
21192
|
+
// rather than dropped: deleting it would silently break anything
|
|
21193
|
+
// referencing its key.
|
|
21194
|
+
stale: entry.stale ?? false,
|
|
21195
|
+
// The selection carries only ids and states, so a model already in the
|
|
21196
|
+
// snapshot keeps the metadata discovery gave it.
|
|
21197
|
+
displayName: previousById.get(entry.modelId)?.displayName,
|
|
21198
|
+
ownedBy: previousById.get(entry.modelId)?.ownedBy
|
|
21199
|
+
}))
|
|
21200
|
+
);
|
|
21201
|
+
const models = await this.deps.providerStore.listModels(tenantId, id3);
|
|
21202
|
+
const kept = new Set(models.map((model) => model.modelId));
|
|
21203
|
+
for (const model of previous) {
|
|
21204
|
+
if (!kept.has(model.modelId)) {
|
|
21205
|
+
this.deps.registry.remove(
|
|
21206
|
+
tenantId,
|
|
21207
|
+
(0, import_protocols16.toModelKey)(provider.id, model.modelId)
|
|
21208
|
+
);
|
|
21209
|
+
}
|
|
21210
|
+
}
|
|
21211
|
+
this.syncRegistry(tenantId, provider, models);
|
|
21212
|
+
return this.toPublicEntry(provider, models.length);
|
|
21213
|
+
}
|
|
21214
|
+
syncRegistry(tenantId, provider, models) {
|
|
21215
|
+
if (!this.shouldRegister(tenantId)) {
|
|
21216
|
+
return [];
|
|
21217
|
+
}
|
|
21218
|
+
const registered = [];
|
|
21219
|
+
for (const model of models) {
|
|
21220
|
+
const modelKey2 = (0, import_protocols16.toModelKey)(provider.id, model.modelId);
|
|
21221
|
+
if (!provider.enabled || !model.enabled) {
|
|
21222
|
+
this.deps.registry.remove(tenantId, modelKey2);
|
|
21223
|
+
continue;
|
|
21224
|
+
}
|
|
21225
|
+
this.deps.registry.register(
|
|
21226
|
+
tenantId,
|
|
21227
|
+
modelKey2,
|
|
21228
|
+
this.toLlmConfig(provider, model.modelId)
|
|
21229
|
+
);
|
|
21230
|
+
registered.push(modelKey2);
|
|
21231
|
+
}
|
|
21232
|
+
return registered;
|
|
21233
|
+
}
|
|
21234
|
+
/**
|
|
21235
|
+
* Enable or disable a single discovered model.
|
|
21236
|
+
*/
|
|
21237
|
+
async setModelEnabled(userId, tenantId, id3, modelId, enabled) {
|
|
21238
|
+
await this.assertAdmin(userId, tenantId);
|
|
21239
|
+
const provider = await this.requireProvider(tenantId, id3);
|
|
21240
|
+
const updated = await this.deps.providerStore.setModelEnabled(
|
|
21241
|
+
tenantId,
|
|
21242
|
+
id3,
|
|
21243
|
+
modelId,
|
|
21244
|
+
enabled
|
|
21245
|
+
);
|
|
21246
|
+
if (!updated) {
|
|
21247
|
+
throw new ModelProviderServiceError(
|
|
21248
|
+
"MODEL_KEY_CONFLICT",
|
|
21249
|
+
404,
|
|
21250
|
+
"Model not found for this provider"
|
|
21251
|
+
);
|
|
21252
|
+
}
|
|
21253
|
+
const models = await this.deps.providerStore.listModels(tenantId, id3);
|
|
21254
|
+
this.syncRegistry(tenantId, provider, models);
|
|
21255
|
+
return this.toPublicEntry(provider, models.length);
|
|
21256
|
+
}
|
|
21257
|
+
/**
|
|
21258
|
+
* Whether discovered models should be registered for execution.
|
|
21259
|
+
*/
|
|
21260
|
+
shouldRegister(tenantId) {
|
|
21261
|
+
return tenantId === PHASE1_REGISTRY_TENANT;
|
|
21262
|
+
}
|
|
21263
|
+
/**
|
|
21264
|
+
* Build the runtime LLM config for a discovered model.
|
|
21265
|
+
*
|
|
21266
|
+
* `apiKeyEnvName`, `extra` and `modelKwargs` are deliberately omitted — they
|
|
21267
|
+
* would let a tenant name an arbitrary process environment variable to read
|
|
21268
|
+
* credentials from, or inject arbitrary constructor options.
|
|
21269
|
+
*/
|
|
21270
|
+
toLlmConfig(provider, modelId) {
|
|
21271
|
+
if (!ALLOWED_LLM_PROVIDERS.has(provider.llmProvider)) {
|
|
21272
|
+
throw new ModelProviderServiceError(
|
|
21273
|
+
"UNSUPPORTED_PROTOCOL",
|
|
21274
|
+
400,
|
|
21275
|
+
"Provider is configured with an unsupported LLM provider"
|
|
21276
|
+
);
|
|
21277
|
+
}
|
|
21278
|
+
return {
|
|
21279
|
+
provider: provider.llmProvider,
|
|
21280
|
+
model: modelId,
|
|
21281
|
+
displayName: modelId,
|
|
21282
|
+
streaming: true,
|
|
21283
|
+
apiKey: provider.apiKey,
|
|
21284
|
+
baseURL: provider.baseURL,
|
|
21285
|
+
// Chat Completions and the Responses API are different routes with
|
|
21286
|
+
// different request fields; without this the gateway would post
|
|
21287
|
+
// `messages` to an endpoint expecting `input`.
|
|
21288
|
+
...provider.apiStyle === "responses" ? { useResponsesApi: true } : {}
|
|
21289
|
+
};
|
|
21290
|
+
}
|
|
21291
|
+
/**
|
|
21292
|
+
* The sole projection from an internal provider entry to an API-safe shape.
|
|
21293
|
+
*/
|
|
21294
|
+
toPublicEntry(provider, modelCount) {
|
|
21295
|
+
return {
|
|
21296
|
+
id: provider.id,
|
|
21297
|
+
tenantId: provider.tenantId,
|
|
21298
|
+
name: provider.name,
|
|
21299
|
+
displayName: provider.displayName,
|
|
21300
|
+
protocol: provider.protocol,
|
|
21301
|
+
apiStyle: provider.apiStyle,
|
|
21302
|
+
llmProvider: provider.llmProvider,
|
|
21303
|
+
baseURL: provider.baseURL,
|
|
21304
|
+
hasApiKey: Boolean(provider.apiKey),
|
|
21305
|
+
apiKeyHint: provider.apiKeyHint,
|
|
21306
|
+
enabled: provider.enabled,
|
|
21307
|
+
status: provider.status,
|
|
21308
|
+
lastError: provider.lastError,
|
|
21309
|
+
lastDiscoveredAt: provider.lastDiscoveredAt?.toISOString(),
|
|
21310
|
+
modelCount,
|
|
21311
|
+
createdAt: provider.createdAt.toISOString(),
|
|
21312
|
+
updatedAt: provider.updatedAt.toISOString()
|
|
21313
|
+
};
|
|
21314
|
+
}
|
|
21315
|
+
/**
|
|
21316
|
+
* Reject a provider that would end up without a credential.
|
|
21317
|
+
*
|
|
21318
|
+
* @param apiKey - The key the provider would hold, if any
|
|
21319
|
+
* @throws {ModelProviderServiceError} When no key is present
|
|
21320
|
+
*/
|
|
21321
|
+
assertHasApiKey(apiKey) {
|
|
21322
|
+
if (!apiKey?.trim()) {
|
|
21323
|
+
throw new ModelProviderServiceError(
|
|
21324
|
+
"INVALID_INPUT",
|
|
21325
|
+
400,
|
|
21326
|
+
"A model provider must have an API key"
|
|
21327
|
+
);
|
|
21328
|
+
}
|
|
21329
|
+
}
|
|
21330
|
+
/**
|
|
21331
|
+
* Load a provider or fail with 404.
|
|
21332
|
+
*
|
|
21333
|
+
* Callers always pass the session's tenant, so a provider belonging to
|
|
21334
|
+
* another tenant is indistinguishable from a missing one.
|
|
21335
|
+
*/
|
|
21336
|
+
async requireProvider(tenantId, id3) {
|
|
21337
|
+
const provider = await this.deps.providerStore.getProviderById(tenantId, id3);
|
|
21338
|
+
if (!provider) {
|
|
21339
|
+
throw new ModelProviderServiceError(
|
|
21340
|
+
"PROVIDER_NOT_FOUND",
|
|
21341
|
+
404,
|
|
21342
|
+
"Model provider not found"
|
|
21343
|
+
);
|
|
21344
|
+
}
|
|
21345
|
+
return provider;
|
|
21346
|
+
}
|
|
21347
|
+
/**
|
|
21348
|
+
* Require that the caller belongs to the tenant.
|
|
21349
|
+
* An absent userId means the unauthenticated development fallback.
|
|
21350
|
+
*/
|
|
21351
|
+
async assertMember(userId, tenantId) {
|
|
21352
|
+
if (!userId) {
|
|
21353
|
+
return;
|
|
21354
|
+
}
|
|
21355
|
+
const link = await this.deps.userTenantLinkStore.getLink(userId, tenantId);
|
|
21356
|
+
if (!link) {
|
|
21357
|
+
throw new ModelProviderServiceError(
|
|
21358
|
+
"TENANT_ACCESS_DENIED",
|
|
21359
|
+
403,
|
|
21360
|
+
"Tenant access denied"
|
|
21361
|
+
);
|
|
21362
|
+
}
|
|
21363
|
+
}
|
|
21364
|
+
/**
|
|
21365
|
+
* Require that the caller is a tenant owner or admin.
|
|
21366
|
+
* Roles are read from the tenant link, never from the session token.
|
|
21367
|
+
*/
|
|
21368
|
+
async assertAdmin(userId, tenantId) {
|
|
21369
|
+
await this.assertMember(userId, tenantId);
|
|
21370
|
+
if (!userId) {
|
|
21371
|
+
return;
|
|
21372
|
+
}
|
|
21373
|
+
const link = await this.deps.userTenantLinkStore.getLink(userId, tenantId);
|
|
21374
|
+
if (link?.role !== "owner" && link?.role !== "admin") {
|
|
21375
|
+
throw new ModelProviderServiceError(
|
|
21376
|
+
"FORBIDDEN_ROLE",
|
|
21377
|
+
403,
|
|
21378
|
+
"Only tenant owner or admin can manage model providers"
|
|
21379
|
+
);
|
|
21380
|
+
}
|
|
21381
|
+
}
|
|
21382
|
+
/**
|
|
21383
|
+
* Validate the shape of an upstream URL and translate failures into service
|
|
21384
|
+
* errors.
|
|
21385
|
+
*
|
|
21386
|
+
* Addresses are deliberately not restricted: other tenant-configured URLs in
|
|
21387
|
+
* this platform (metrics servers, MCP servers) reach wherever they point, and
|
|
21388
|
+
* self-hosted model servers normally sit on the gateway's own network.
|
|
21389
|
+
*
|
|
21390
|
+
* The mapping is explicit so the public error codes stay decoupled from the
|
|
21391
|
+
* reasons the URL utility happens to use internally.
|
|
21392
|
+
*/
|
|
21393
|
+
async assertSafeUrl(baseURL) {
|
|
21394
|
+
try {
|
|
21395
|
+
await (0, import_core47.assertSafeUpstreamUrl)(baseURL);
|
|
21396
|
+
} catch (error) {
|
|
21397
|
+
if (error instanceof import_core47.UnsafeUpstreamUrlError) {
|
|
21398
|
+
throw new ModelProviderServiceError(
|
|
21399
|
+
unsafeReasonToErrorCode[error.reason],
|
|
21400
|
+
400,
|
|
21401
|
+
error.message
|
|
21402
|
+
);
|
|
21403
|
+
}
|
|
21404
|
+
throw error;
|
|
21405
|
+
}
|
|
21406
|
+
}
|
|
21407
|
+
/**
|
|
21408
|
+
* Translate a store-level unique violation into a stable service error.
|
|
21409
|
+
*/
|
|
21410
|
+
mapStoreError(error) {
|
|
21411
|
+
if (error instanceof ModelProviderServiceError) {
|
|
21412
|
+
return error;
|
|
21413
|
+
}
|
|
21414
|
+
const code = error?.code;
|
|
21415
|
+
if (code === "23505") {
|
|
21416
|
+
return new ModelProviderServiceError(
|
|
21417
|
+
"PROVIDER_NAME_CONFLICT",
|
|
21418
|
+
409,
|
|
21419
|
+
"A provider with this name already exists"
|
|
21420
|
+
);
|
|
21421
|
+
}
|
|
21422
|
+
if (code === "23503") {
|
|
21423
|
+
return new ModelProviderServiceError(
|
|
21424
|
+
"INVALID_INPUT",
|
|
21425
|
+
400,
|
|
21426
|
+
"Model provider references a missing record"
|
|
21427
|
+
);
|
|
21428
|
+
}
|
|
21429
|
+
throw error;
|
|
21430
|
+
}
|
|
21431
|
+
};
|
|
21432
|
+
|
|
21433
|
+
// src/schemas/model-provider.ts
|
|
21434
|
+
var import_zod10 = require("zod");
|
|
21435
|
+
var providerId = import_zod10.z.string().trim().min(1).max(255);
|
|
21436
|
+
var providerParamsSchema = import_zod10.z.object({ providerId }).strict();
|
|
21437
|
+
var protocolSchema = import_zod10.z.enum(["openai-compatible"]);
|
|
21438
|
+
var apiStyleSchema = import_zod10.z.enum(["chat-completions", "responses"]);
|
|
21439
|
+
var createProviderSchema = import_zod10.z.object({
|
|
21440
|
+
name: import_zod10.z.string().trim().min(1).max(255),
|
|
21441
|
+
displayName: import_zod10.z.string().trim().max(255).optional(),
|
|
21442
|
+
protocol: protocolSchema,
|
|
21443
|
+
apiStyle: apiStyleSchema.optional(),
|
|
21444
|
+
baseURL: import_zod10.z.string().trim().min(1).max(2048),
|
|
21445
|
+
// Required, not optional: see CreateModelProviderRequest.apiKey — a
|
|
21446
|
+
// keyless provider would make the gateway send its own env credential.
|
|
21447
|
+
apiKey: import_zod10.z.string().trim().min(1).max(1024),
|
|
21448
|
+
enabled: import_zod10.z.boolean().optional()
|
|
21449
|
+
}).strict();
|
|
21450
|
+
var updateProviderSchema = createProviderSchema.partial().extend({
|
|
21451
|
+
/**
|
|
21452
|
+
* Remove the stored API key. Needed because an empty `apiKey` means "keep":
|
|
21453
|
+
* without this a leaked credential could not be revoked.
|
|
21454
|
+
*/
|
|
21455
|
+
clearApiKey: import_zod10.z.boolean().optional()
|
|
21456
|
+
}).strict();
|
|
21457
|
+
var setModelEnabledSchema = import_zod10.z.object({
|
|
21458
|
+
modelId: import_zod10.z.string().trim().min(1).max(512),
|
|
21459
|
+
enabled: import_zod10.z.boolean()
|
|
21460
|
+
}).strict();
|
|
21461
|
+
var discoverSchema = import_zod10.z.object({}).strict();
|
|
21462
|
+
var setModelsSchema = import_zod10.z.object({
|
|
21463
|
+
models: import_zod10.z.array(
|
|
21464
|
+
import_zod10.z.object({
|
|
21465
|
+
modelId: import_zod10.z.string().trim().min(1).max(512),
|
|
21466
|
+
enabled: import_zod10.z.boolean(),
|
|
21467
|
+
stale: import_zod10.z.boolean().optional()
|
|
21468
|
+
}).strict()
|
|
21469
|
+
).max(1e3)
|
|
21470
|
+
}).strict();
|
|
21471
|
+
var probeProviderSchema = import_zod10.z.object({
|
|
21472
|
+
baseURL: import_zod10.z.string().trim().min(1).max(2048),
|
|
21473
|
+
apiKey: import_zod10.z.string().max(1024).optional(),
|
|
21474
|
+
providerId: import_zod10.z.string().trim().min(1).max(255).optional(),
|
|
21475
|
+
protocol: protocolSchema.optional()
|
|
21476
|
+
}).strict();
|
|
21477
|
+
|
|
21478
|
+
// src/controllers/tenant-scope.ts
|
|
21479
|
+
var TenantScopeError2 = class extends Error {
|
|
21480
|
+
/** Creates a tenant scope error. */
|
|
21481
|
+
constructor(code, statusCode, message) {
|
|
21482
|
+
super(message);
|
|
21483
|
+
this.code = code;
|
|
21484
|
+
this.statusCode = statusCode;
|
|
21485
|
+
this.name = "TenantScopeError";
|
|
21486
|
+
}
|
|
21487
|
+
};
|
|
21488
|
+
function resolveTenantScope(request, authRequired2) {
|
|
21489
|
+
const authenticated = request.user?.tenantId;
|
|
21490
|
+
const header2 = request.headers["x-tenant-id"];
|
|
21491
|
+
const requested = Array.isArray(header2) ? header2[0] : header2;
|
|
21492
|
+
if (authenticated) {
|
|
21493
|
+
if (requested && requested !== authenticated) {
|
|
21494
|
+
throw new TenantScopeError2(
|
|
21495
|
+
"TENANT_ACCESS_DENIED",
|
|
21496
|
+
403,
|
|
21497
|
+
"Tenant access denied"
|
|
21498
|
+
);
|
|
21499
|
+
}
|
|
21500
|
+
return { tenantId: authenticated, userId: request.user?.id };
|
|
21501
|
+
}
|
|
21502
|
+
if (authRequired2) {
|
|
21503
|
+
throw new TenantScopeError2(
|
|
21504
|
+
"UNAUTHENTICATED",
|
|
21505
|
+
401,
|
|
21506
|
+
"Console session required"
|
|
21507
|
+
);
|
|
21508
|
+
}
|
|
21509
|
+
return { tenantId: requested || "default" };
|
|
21510
|
+
}
|
|
21511
|
+
|
|
21512
|
+
// src/controllers/model-providers.ts
|
|
21513
|
+
init_Logger();
|
|
21514
|
+
var logger4 = new Logger({ serviceName: "lattice/gateway/model-providers" });
|
|
21515
|
+
function sendError2(reply, error) {
|
|
21516
|
+
if (error instanceof ModelProviderServiceError) {
|
|
21517
|
+
return reply.status(error.statusCode).send({
|
|
21518
|
+
success: false,
|
|
21519
|
+
error: {
|
|
21520
|
+
code: error.code,
|
|
21521
|
+
message: error.message,
|
|
21522
|
+
retryable: error.retryable
|
|
21523
|
+
}
|
|
21524
|
+
});
|
|
21525
|
+
}
|
|
21526
|
+
if (error instanceof TenantScopeError2) {
|
|
21527
|
+
return reply.status(error.statusCode).send({
|
|
21528
|
+
success: false,
|
|
21529
|
+
error: {
|
|
21530
|
+
code: error.code,
|
|
21531
|
+
message: error.message,
|
|
21532
|
+
retryable: false
|
|
21533
|
+
}
|
|
21534
|
+
});
|
|
21535
|
+
}
|
|
21536
|
+
if (error instanceof import_zod11.ZodError) {
|
|
21537
|
+
return reply.status(400).send({
|
|
21538
|
+
success: false,
|
|
21539
|
+
error: {
|
|
21540
|
+
code: "INVALID_INPUT",
|
|
21541
|
+
message: "Invalid request",
|
|
21542
|
+
retryable: false
|
|
21543
|
+
}
|
|
21544
|
+
});
|
|
21545
|
+
}
|
|
21546
|
+
logger4.error("Model provider controller error", {
|
|
21547
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
21548
|
+
});
|
|
21549
|
+
return reply.status(500).send({
|
|
21550
|
+
success: false,
|
|
21551
|
+
error: {
|
|
21552
|
+
code: "INTERNAL_ERROR",
|
|
21553
|
+
message: "Internal server error",
|
|
21554
|
+
retryable: false
|
|
21555
|
+
}
|
|
21556
|
+
});
|
|
21557
|
+
}
|
|
21558
|
+
var ModelProviderController = class {
|
|
21559
|
+
constructor(deps, options = {}) {
|
|
21560
|
+
this.service = new ModelProviderService(deps);
|
|
21561
|
+
this.authRequired = options.authRequired ?? true;
|
|
21562
|
+
}
|
|
21563
|
+
/** GET /api/tenant-models */
|
|
21564
|
+
async getCatalog(request, reply) {
|
|
21565
|
+
try {
|
|
21566
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21567
|
+
const catalog = await this.service.catalog(scope.userId, scope.tenantId);
|
|
21568
|
+
return reply.send({ success: true, data: catalog });
|
|
21569
|
+
} catch (error) {
|
|
21570
|
+
return sendError2(reply, error);
|
|
21571
|
+
}
|
|
21572
|
+
}
|
|
21573
|
+
/** GET /api/model-providers */
|
|
21574
|
+
async listProviders(request, reply) {
|
|
21575
|
+
try {
|
|
21576
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21577
|
+
const records = await this.service.list(scope.userId, scope.tenantId);
|
|
21578
|
+
return reply.send({
|
|
21579
|
+
success: true,
|
|
21580
|
+
data: { records, total: records.length }
|
|
21581
|
+
});
|
|
21582
|
+
} catch (error) {
|
|
21583
|
+
return sendError2(reply, error);
|
|
21584
|
+
}
|
|
21585
|
+
}
|
|
21586
|
+
/** GET /api/model-providers/:providerId */
|
|
21587
|
+
async getProvider(request, reply) {
|
|
21588
|
+
try {
|
|
21589
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21590
|
+
const { providerId: providerId2 } = providerParamsSchema.parse(request.params);
|
|
21591
|
+
return reply.send({
|
|
21592
|
+
success: true,
|
|
21593
|
+
data: await this.service.get(scope.userId, scope.tenantId, providerId2)
|
|
21594
|
+
});
|
|
21595
|
+
} catch (error) {
|
|
21596
|
+
return sendError2(reply, error);
|
|
21597
|
+
}
|
|
21598
|
+
}
|
|
21599
|
+
/** POST /api/model-providers */
|
|
21600
|
+
async createProvider(request, reply) {
|
|
21601
|
+
try {
|
|
21602
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21603
|
+
const input = createProviderSchema.parse(request.body);
|
|
21604
|
+
return reply.status(201).send({
|
|
21605
|
+
success: true,
|
|
21606
|
+
data: await this.service.create(scope.userId, scope.tenantId, input)
|
|
21607
|
+
});
|
|
21608
|
+
} catch (error) {
|
|
21609
|
+
return sendError2(reply, error);
|
|
21610
|
+
}
|
|
21611
|
+
}
|
|
21612
|
+
/** PUT /api/model-providers/:providerId */
|
|
21613
|
+
async updateProvider(request, reply) {
|
|
21614
|
+
try {
|
|
21615
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21616
|
+
const { providerId: providerId2 } = providerParamsSchema.parse(request.params);
|
|
21617
|
+
const input = updateProviderSchema.parse(request.body);
|
|
21618
|
+
return reply.send({
|
|
21619
|
+
success: true,
|
|
21620
|
+
data: await this.service.update(
|
|
21621
|
+
scope.userId,
|
|
21622
|
+
scope.tenantId,
|
|
21623
|
+
providerId2,
|
|
21624
|
+
input
|
|
21625
|
+
)
|
|
21626
|
+
});
|
|
21627
|
+
} catch (error) {
|
|
21628
|
+
return sendError2(reply, error);
|
|
21629
|
+
}
|
|
21630
|
+
}
|
|
21631
|
+
/** DELETE /api/model-providers/:providerId */
|
|
21632
|
+
async deleteProvider(request, reply) {
|
|
21633
|
+
try {
|
|
21634
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21635
|
+
const { providerId: providerId2 } = providerParamsSchema.parse(request.params);
|
|
21636
|
+
await this.service.remove(scope.userId, scope.tenantId, providerId2);
|
|
21637
|
+
return reply.send({ success: true, data: null });
|
|
21638
|
+
} catch (error) {
|
|
21639
|
+
return sendError2(reply, error);
|
|
21640
|
+
}
|
|
21641
|
+
}
|
|
21642
|
+
/** POST /api/model-providers/probe */
|
|
21643
|
+
async probeProvider(request, reply) {
|
|
21644
|
+
try {
|
|
21645
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21646
|
+
const input = probeProviderSchema.parse(request.body);
|
|
21647
|
+
return reply.send({
|
|
21648
|
+
success: true,
|
|
21649
|
+
data: await this.service.probe(scope.userId, scope.tenantId, input)
|
|
21650
|
+
});
|
|
21651
|
+
} catch (error) {
|
|
21652
|
+
return sendError2(reply, error);
|
|
21653
|
+
}
|
|
21654
|
+
}
|
|
21655
|
+
/** PUT /api/model-providers/:providerId/models */
|
|
21656
|
+
async setProviderModels(request, reply) {
|
|
21657
|
+
try {
|
|
21658
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21659
|
+
const { providerId: providerId2 } = providerParamsSchema.parse(request.params);
|
|
21660
|
+
const { models } = setModelsSchema.parse(request.body);
|
|
21661
|
+
return reply.send({
|
|
21662
|
+
success: true,
|
|
21663
|
+
data: await this.service.setModels(
|
|
21664
|
+
scope.userId,
|
|
21665
|
+
scope.tenantId,
|
|
21666
|
+
providerId2,
|
|
21667
|
+
models
|
|
21668
|
+
)
|
|
21669
|
+
});
|
|
21670
|
+
} catch (error) {
|
|
21671
|
+
return sendError2(reply, error);
|
|
21672
|
+
}
|
|
21673
|
+
}
|
|
21674
|
+
/** POST /api/model-providers/:providerId/discover */
|
|
21675
|
+
async discoverProvider(request, reply) {
|
|
21676
|
+
try {
|
|
21677
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21678
|
+
const { providerId: providerId2 } = providerParamsSchema.parse(request.params);
|
|
21679
|
+
discoverSchema.parse(request.body ?? {});
|
|
21680
|
+
return reply.send({
|
|
21681
|
+
success: true,
|
|
21682
|
+
data: await this.service.discover(
|
|
21683
|
+
scope.userId,
|
|
21684
|
+
scope.tenantId,
|
|
21685
|
+
providerId2
|
|
21686
|
+
)
|
|
21687
|
+
});
|
|
21688
|
+
} catch (error) {
|
|
21689
|
+
return sendError2(reply, error);
|
|
21690
|
+
}
|
|
21691
|
+
}
|
|
21692
|
+
/** POST /api/model-providers/:providerId/models/enabled */
|
|
21693
|
+
async setModelEnabled(request, reply) {
|
|
21694
|
+
try {
|
|
21695
|
+
const scope = resolveTenantScope(request, this.authRequired);
|
|
21696
|
+
const { providerId: providerId2 } = providerParamsSchema.parse(request.params);
|
|
21697
|
+
const { modelId, enabled } = setModelEnabledSchema.parse(request.body);
|
|
21698
|
+
return reply.send({
|
|
21699
|
+
success: true,
|
|
21700
|
+
data: await this.service.setModelEnabled(
|
|
21701
|
+
scope.userId,
|
|
21702
|
+
scope.tenantId,
|
|
21703
|
+
providerId2,
|
|
21704
|
+
modelId,
|
|
21705
|
+
enabled
|
|
21706
|
+
)
|
|
21707
|
+
});
|
|
21708
|
+
} catch (error) {
|
|
21709
|
+
return sendError2(reply, error);
|
|
21710
|
+
}
|
|
21711
|
+
}
|
|
21712
|
+
};
|
|
21713
|
+
function registerModelProviderRoutes(app2, controller) {
|
|
21714
|
+
app2.get("/api/tenant-models", controller.getCatalog.bind(controller));
|
|
21715
|
+
app2.get("/api/model-providers", controller.listProviders.bind(controller));
|
|
21716
|
+
app2.post("/api/model-providers", controller.createProvider.bind(controller));
|
|
21717
|
+
app2.post("/api/model-providers/probe", controller.probeProvider.bind(controller));
|
|
21718
|
+
app2.get(
|
|
21719
|
+
"/api/model-providers/:providerId",
|
|
21720
|
+
controller.getProvider.bind(controller)
|
|
21721
|
+
);
|
|
21722
|
+
app2.put(
|
|
21723
|
+
"/api/model-providers/:providerId",
|
|
21724
|
+
controller.updateProvider.bind(controller)
|
|
21725
|
+
);
|
|
21726
|
+
app2.delete(
|
|
21727
|
+
"/api/model-providers/:providerId",
|
|
21728
|
+
controller.deleteProvider.bind(controller)
|
|
21729
|
+
);
|
|
21730
|
+
app2.post(
|
|
21731
|
+
"/api/model-providers/:providerId/discover",
|
|
21732
|
+
controller.discoverProvider.bind(controller)
|
|
21733
|
+
);
|
|
21734
|
+
app2.put(
|
|
21735
|
+
"/api/model-providers/:providerId/models",
|
|
21736
|
+
controller.setProviderModels.bind(controller)
|
|
21737
|
+
);
|
|
21738
|
+
app2.post(
|
|
21739
|
+
"/api/model-providers/:providerId/models/enabled",
|
|
21740
|
+
controller.setModelEnabled.bind(controller)
|
|
21741
|
+
);
|
|
21742
|
+
}
|
|
21743
|
+
|
|
21744
|
+
// src/services/collaboration/ProjectRoomService.ts
|
|
21745
|
+
var import_node_crypto15 = require("crypto");
|
|
21746
|
+
var ProjectRoomService = class {
|
|
21747
|
+
/** Creates a project room service from explicitly injected collaborators. */
|
|
21748
|
+
constructor(deps) {
|
|
21749
|
+
this.deps = deps;
|
|
21750
|
+
this.idFactory = deps.idFactory ?? import_node_crypto15.randomUUID;
|
|
21751
|
+
}
|
|
21752
|
+
/**
|
|
21753
|
+
* Authorizes read access and ensures the project's main room.
|
|
21754
|
+
*
|
|
21755
|
+
* @remarks
|
|
21756
|
+
* This method is safe to call while holding the process-wide Project
|
|
21757
|
+
* mutation mutex: it never re-enters it. Coordinator backfill is a separate
|
|
21758
|
+
* entry point ({@link ensureMainRoomWithCoordinator}) because it acquires
|
|
21759
|
+
* that mutex internally.
|
|
21760
|
+
*/
|
|
21761
|
+
async ensureMainRoom(input) {
|
|
21762
|
+
const { room } = await this.authorizedRoom(input);
|
|
21763
|
+
return room;
|
|
21764
|
+
}
|
|
21765
|
+
/**
|
|
21766
|
+
* Authorizes read access, ensures the main room, and lazily backfills the
|
|
21767
|
+
* built-in coordinator for business projects.
|
|
21768
|
+
*
|
|
21769
|
+
* @remarks
|
|
21770
|
+
* Only call this outside the Project mutation mutex (room open / bootstrap
|
|
21771
|
+
* paths); calling it under the mutex deadlocks on the coordinator ensurer.
|
|
21772
|
+
*/
|
|
21773
|
+
async ensureMainRoomWithCoordinator(input) {
|
|
21774
|
+
const { project, room } = await this.authorizedRoom(input);
|
|
21775
|
+
if ((project.kind === "business" || project.kind === "public") && this.deps.ensureCoordinator) {
|
|
21776
|
+
try {
|
|
21777
|
+
await this.deps.ensureCoordinator({
|
|
21778
|
+
tenantId: input.tenantId,
|
|
21779
|
+
workspaceId: project.workspaceId,
|
|
21780
|
+
projectId: project.id,
|
|
21781
|
+
roomId: room.id
|
|
21782
|
+
});
|
|
21783
|
+
} catch (error) {
|
|
21784
|
+
this.deps.log?.warn("project-room:coordinator-ensure-failed", {
|
|
21785
|
+
tenantId: input.tenantId,
|
|
21786
|
+
projectId: project.id,
|
|
21787
|
+
errorName: error instanceof Error ? error.name : "UnknownError"
|
|
21788
|
+
});
|
|
21789
|
+
}
|
|
21790
|
+
}
|
|
21791
|
+
return room;
|
|
21792
|
+
}
|
|
21793
|
+
async authorizedRoom(input) {
|
|
21794
|
+
const { project } = await this.deps.accessService.requireProjectAccess({
|
|
21795
|
+
...input,
|
|
21796
|
+
capability: "read"
|
|
21797
|
+
});
|
|
21798
|
+
const room = await this.deps.roomStore.ensureMainRoom({
|
|
21799
|
+
id: this.idFactory(),
|
|
21800
|
+
tenantId: input.tenantId,
|
|
21801
|
+
workspaceId: project.workspaceId,
|
|
21802
|
+
projectId: project.id,
|
|
21803
|
+
name: project.name
|
|
21804
|
+
});
|
|
21805
|
+
return { project, room };
|
|
21806
|
+
}
|
|
21807
|
+
};
|
|
21808
|
+
|
|
21809
|
+
// src/services/collaboration/ProjectMembershipService.ts
|
|
21810
|
+
var import_node_crypto16 = require("crypto");
|
|
21811
|
+
var ProjectMembershipServiceError = class extends Error {
|
|
21812
|
+
constructor(code, statusCode, message) {
|
|
21813
|
+
super(message);
|
|
21814
|
+
this.code = code;
|
|
21815
|
+
this.statusCode = statusCode;
|
|
21816
|
+
this.name = "ProjectMembershipServiceError";
|
|
21817
|
+
}
|
|
21818
|
+
};
|
|
21819
|
+
var membershipConflictNames2 = /* @__PURE__ */ new Set(["DuplicateProjectMembershipError", "ProjectMembershipIdConflictError"]);
|
|
21820
|
+
function isMembershipConflict(error) {
|
|
21821
|
+
return error instanceof Error && membershipConflictNames2.has(error.name);
|
|
21822
|
+
}
|
|
21823
|
+
var ProjectMembershipService = class {
|
|
21824
|
+
constructor(deps) {
|
|
21825
|
+
this.deps = deps;
|
|
21826
|
+
this.idFactory = deps.idFactory ?? import_node_crypto16.randomUUID;
|
|
21827
|
+
this.mutationMutex = deps.projectMutationMutex ?? import_core26.projectMutationMutex;
|
|
21828
|
+
}
|
|
21829
|
+
async list(actor3) {
|
|
21830
|
+
await this.deps.accessService.requireRosterAccess(actor3);
|
|
21831
|
+
const membership = await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, actor3.userId);
|
|
21832
|
+
if (!membership || membership.status !== "active") this.accessDenied();
|
|
21833
|
+
return this.deps.membershipStore.list(actor3.tenantId, actor3.projectId);
|
|
21834
|
+
}
|
|
21835
|
+
async add(actor3, input) {
|
|
21836
|
+
return this.withProjectLock(actor3, async () => {
|
|
21837
|
+
const access = await this.authorize(actor3);
|
|
21838
|
+
if (input.role === "owner" && access.role !== "owner") this.accessDenied();
|
|
21839
|
+
if (!await this.deps.userTenantLinkStore.getLink(input.userId, actor3.tenantId)) {
|
|
21840
|
+
throw new ProjectMembershipServiceError("TARGET_USER_NOT_IN_TENANT", 400, "Target user is not in the tenant");
|
|
21841
|
+
}
|
|
21842
|
+
if (await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, input.userId)) {
|
|
21843
|
+
throw new ProjectMembershipServiceError("MEMBERSHIP_EXISTS", 409, "Project membership already exists");
|
|
21844
|
+
}
|
|
21845
|
+
try {
|
|
21846
|
+
const created = await this.deps.membershipStore.create({
|
|
21847
|
+
id: this.idFactory(),
|
|
21848
|
+
tenantId: actor3.tenantId,
|
|
21849
|
+
projectId: actor3.projectId,
|
|
21850
|
+
userId: input.userId,
|
|
21851
|
+
role: input.role,
|
|
21852
|
+
status: "active"
|
|
21853
|
+
});
|
|
21854
|
+
this.publish(created, "added", `membership:${created.id}:add:${created.joinedAt.toISOString()}`);
|
|
21855
|
+
return created;
|
|
21856
|
+
} catch (error) {
|
|
21857
|
+
if (error instanceof Error && error.name === "DuplicateProjectMembershipError") {
|
|
21858
|
+
throw new ProjectMembershipServiceError("MEMBERSHIP_EXISTS", 409, "Project membership already exists");
|
|
21859
|
+
}
|
|
21860
|
+
if (isMembershipConflict(error)) {
|
|
21861
|
+
throw new ProjectMembershipServiceError("MEMBERSHIP_CONFLICT", 409, "Project membership could not be created");
|
|
21862
|
+
}
|
|
21863
|
+
throw error;
|
|
21864
|
+
}
|
|
21865
|
+
});
|
|
21866
|
+
}
|
|
21867
|
+
async updateRole(actor3, input) {
|
|
21868
|
+
return this.withProjectLock(actor3, async () => {
|
|
21869
|
+
const access = await this.authorize(actor3);
|
|
21870
|
+
const target = await this.target(actor3, input.membershipId);
|
|
21871
|
+
this.requireActiveAndCurrent(target, input.expectedUpdatedAt);
|
|
21872
|
+
if ((target.role === "owner" || input.role === "owner") && access.role !== "owner") this.accessDenied();
|
|
21873
|
+
const updated = await this.mapMutation(await this.deps.membershipStore.updateRole({ tenantId: actor3.tenantId, id: input.membershipId, role: input.role, expectedUpdatedAt: input.expectedUpdatedAt }));
|
|
21874
|
+
this.publish(updated, "role_changed", `membership:${updated.id}:role:${updated.role}:${input.expectedUpdatedAt.toISOString()}`);
|
|
21875
|
+
return updated;
|
|
21876
|
+
});
|
|
21877
|
+
}
|
|
21878
|
+
async remove(actor3, input) {
|
|
21879
|
+
return this.withProjectLock(actor3, async () => {
|
|
21880
|
+
const access = await this.authorize(actor3);
|
|
21881
|
+
const target = await this.target(actor3, input.membershipId);
|
|
21882
|
+
this.requireActiveAndCurrent(target, input.expectedUpdatedAt);
|
|
21883
|
+
if (target.role === "owner" && access.role !== "owner") this.accessDenied();
|
|
21884
|
+
const removed = await this.mapMutation(await this.deps.membershipStore.remove({ tenantId: actor3.tenantId, id: input.membershipId, expectedUpdatedAt: input.expectedUpdatedAt }));
|
|
21885
|
+
this.publish(removed, "removed", `membership:${removed.id}:remove:${input.expectedUpdatedAt.toISOString()}`);
|
|
21886
|
+
return removed;
|
|
21887
|
+
});
|
|
21888
|
+
}
|
|
21889
|
+
async authorize(actor3) {
|
|
21890
|
+
await this.deps.accessService.requireProjectAccess({ ...actor3, capability: "manage" });
|
|
21891
|
+
const membership = await this.deps.membershipStore.findByUser(actor3.tenantId, actor3.projectId, actor3.userId);
|
|
21892
|
+
if (!membership || membership.status !== "active") this.accessDenied();
|
|
21893
|
+
return membership;
|
|
21894
|
+
}
|
|
21895
|
+
async target(actor3, id3) {
|
|
21896
|
+
const target = (await this.deps.membershipStore.list(actor3.tenantId, actor3.projectId)).find((item) => item.id === id3);
|
|
21897
|
+
if (!target) throw new ProjectMembershipServiceError("MEMBERSHIP_NOT_FOUND", 404, "Project membership not found");
|
|
21898
|
+
return target;
|
|
21899
|
+
}
|
|
21900
|
+
requireActiveAndCurrent(target, expectedUpdatedAt2) {
|
|
21901
|
+
if (target.status !== "active") {
|
|
21902
|
+
throw new ProjectMembershipServiceError("MEMBERSHIP_NOT_FOUND", 404, "Project membership not found");
|
|
21903
|
+
}
|
|
21904
|
+
if (!Number.isFinite(expectedUpdatedAt2.getTime()) || target.updatedAt.getTime() !== expectedUpdatedAt2.getTime()) {
|
|
21905
|
+
throw new ProjectMembershipServiceError("MEMBERSHIP_CONFLICT", 409, "Project membership changed; reload before saving");
|
|
21906
|
+
}
|
|
21907
|
+
}
|
|
21908
|
+
async mapMutation(result) {
|
|
21909
|
+
if (result.kind === "updated" || result.kind === "removed") return result.membership;
|
|
21910
|
+
if (result.kind === "not_found") throw new ProjectMembershipServiceError("MEMBERSHIP_NOT_FOUND", 404, "Project membership not found");
|
|
21911
|
+
if (result.kind === "conflict") throw new ProjectMembershipServiceError("MEMBERSHIP_CONFLICT", 409, "Project membership changed; reload before saving");
|
|
21912
|
+
throw new ProjectMembershipServiceError("LAST_OWNER", 409, "The project must retain an owner");
|
|
21913
|
+
}
|
|
21914
|
+
publish(membership, change, sourceKey) {
|
|
20786
21915
|
if (!this.deps.realtime) return;
|
|
20787
21916
|
if (!this.deps.roomStore) {
|
|
20788
21917
|
this.reportRealtimeFailure(membership.id);
|
|
@@ -20828,7 +21957,7 @@ var ProjectMembershipService = class {
|
|
|
20828
21957
|
};
|
|
20829
21958
|
|
|
20830
21959
|
// src/services/collaboration/ProjectRosterService.ts
|
|
20831
|
-
var
|
|
21960
|
+
var import_node_crypto17 = require("crypto");
|
|
20832
21961
|
var ProjectRosterServiceError = class extends Error {
|
|
20833
21962
|
/** Creates a stable roster service error. */
|
|
20834
21963
|
constructor(code, statusCode, message) {
|
|
@@ -20847,7 +21976,7 @@ var ProjectRosterService = class {
|
|
|
20847
21976
|
/** Creates a roster service from explicitly injected collaborators. */
|
|
20848
21977
|
constructor(deps) {
|
|
20849
21978
|
this.deps = deps;
|
|
20850
|
-
this.idFactory = deps.idFactory ??
|
|
21979
|
+
this.idFactory = deps.idFactory ?? import_node_crypto17.randomUUID;
|
|
20851
21980
|
this.mutationMutex = deps.projectMutationMutex ?? import_core26.projectMutationMutex;
|
|
20852
21981
|
}
|
|
20853
21982
|
/**
|
|
@@ -21113,15 +22242,15 @@ function publicProjectBot(project, roomId, agent, now) {
|
|
|
21113
22242
|
}
|
|
21114
22243
|
|
|
21115
22244
|
// src/services/collaboration/RoomMessageService.ts
|
|
21116
|
-
var
|
|
22245
|
+
var import_node_crypto18 = require("crypto");
|
|
21117
22246
|
|
|
21118
22247
|
// src/services/collaboration/ProjectBotMembershipSnapshot.ts
|
|
21119
|
-
var
|
|
22248
|
+
var import_protocols17 = require("@axiom-lattice/protocols");
|
|
21120
22249
|
var requiredKeys = ["assistantId", "id", "joinedAt", "mentionName", "projectId", "role", "roomId", "roomThreadId", "status", "tenantId", "title", "updatedAt", "workspaceId"];
|
|
21121
22250
|
function snapshotProjectBotMembership(value) {
|
|
21122
22251
|
try {
|
|
21123
22252
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
21124
|
-
const values = (0,
|
|
22253
|
+
const values = (0, import_protocols17.snapshotExactRecord)(value, requiredKeys, ["responsibility"]);
|
|
21125
22254
|
if (!values) return void 0;
|
|
21126
22255
|
const read = (key) => values[key];
|
|
21127
22256
|
const id3 = read("id");
|
|
@@ -21202,7 +22331,7 @@ var RoomMessageService = class {
|
|
|
21202
22331
|
constructor(deps, options = {}) {
|
|
21203
22332
|
this.deps = deps;
|
|
21204
22333
|
this.idempotentOutcomes = /* @__PURE__ */ new Map();
|
|
21205
|
-
this.idFactory = deps.idFactory ??
|
|
22334
|
+
this.idFactory = deps.idFactory ?? import_node_crypto18.randomUUID;
|
|
21206
22335
|
const limit = options.idempotentOutcomeLimit ?? 1e3;
|
|
21207
22336
|
if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("idempotentOutcomeLimit must be a positive safe integer");
|
|
21208
22337
|
this.idempotentOutcomeLimit = limit;
|
|
@@ -21383,7 +22512,7 @@ var RoomMessageService = class {
|
|
|
21383
22512
|
let idempotencyKey;
|
|
21384
22513
|
if (value.requestIdempotencyKey !== void 0) {
|
|
21385
22514
|
if (typeof value.requestIdempotencyKey !== "string" || !/^[\x21-\x7e]{1,200}$/.test(value.requestIdempotencyKey.trim())) throw new RoomMessageServiceError("INVALID_MESSAGE", 400, "Invalid idempotency key");
|
|
21386
|
-
idempotencyKey = `room-human:${actor3.userId}:${(0,
|
|
22515
|
+
idempotencyKey = `room-human:${actor3.userId}:${(0, import_node_crypto18.createHash)("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
|
|
21387
22516
|
}
|
|
21388
22517
|
return { text, mentions, botIds, ...value.replyToMessageId === void 0 ? {} : { replyToMessageId: value.replyToMessageId }, ...idempotencyKey === void 0 ? {} : { idempotencyKey } };
|
|
21389
22518
|
}
|
|
@@ -21440,7 +22569,7 @@ function cloneRoomMessage(message) {
|
|
|
21440
22569
|
};
|
|
21441
22570
|
}
|
|
21442
22571
|
function fingerprintRequest(input) {
|
|
21443
|
-
return (0,
|
|
22572
|
+
return (0, import_node_crypto18.createHash)("sha256").update(JSON.stringify({
|
|
21444
22573
|
text: input.text,
|
|
21445
22574
|
mentions: input.mentions,
|
|
21446
22575
|
replyToMessageId: input.replyToMessageId ?? null
|
|
@@ -21451,11 +22580,11 @@ function matchesNormalizedPayload(message, input) {
|
|
|
21451
22580
|
}
|
|
21452
22581
|
|
|
21453
22582
|
// src/routes/project-rooms.ts
|
|
21454
|
-
var
|
|
21455
|
-
var
|
|
22583
|
+
var import_zod13 = require("zod");
|
|
22584
|
+
var import_protocols18 = require("@axiom-lattice/protocols");
|
|
21456
22585
|
|
|
21457
22586
|
// src/services/collaboration/RoomInterruptService.ts
|
|
21458
|
-
var
|
|
22587
|
+
var import_node_crypto19 = require("crypto");
|
|
21459
22588
|
var RoomInterruptServiceError = class extends Error {
|
|
21460
22589
|
/** Creates a stable room interrupt error. */
|
|
21461
22590
|
constructor(code, statusCode, message) {
|
|
@@ -21469,7 +22598,7 @@ var RoomInterruptService = class {
|
|
|
21469
22598
|
/** Creates the service from explicitly injected stores and dispatcher. */
|
|
21470
22599
|
constructor(deps) {
|
|
21471
22600
|
this.deps = deps;
|
|
21472
|
-
this.idFactory = deps.idFactory ??
|
|
22601
|
+
this.idFactory = deps.idFactory ?? import_node_crypto19.randomUUID;
|
|
21473
22602
|
}
|
|
21474
22603
|
/** Resolves a pending clarify interrupt with no answers. */
|
|
21475
22604
|
async skip(input) {
|
|
@@ -21527,52 +22656,52 @@ var RoomInterruptService = class {
|
|
|
21527
22656
|
};
|
|
21528
22657
|
|
|
21529
22658
|
// src/schemas/project-room.ts
|
|
21530
|
-
var
|
|
21531
|
-
var id2 =
|
|
21532
|
-
var isoDate =
|
|
21533
|
-
var expectedUpdatedAt =
|
|
21534
|
-
var projectRoomParamsSchema =
|
|
21535
|
-
var membershipParamsSchema =
|
|
21536
|
-
var roomMessagesQuerySchema =
|
|
21537
|
-
beforeCreatedAt:
|
|
22659
|
+
var import_zod12 = require("zod");
|
|
22660
|
+
var id2 = import_zod12.z.string().trim().min(1).max(128);
|
|
22661
|
+
var isoDate = import_zod12.z.string().datetime({ offset: true }).transform((value) => new Date(value));
|
|
22662
|
+
var expectedUpdatedAt = import_zod12.z.object({ expectedUpdatedAt: isoDate }).strict();
|
|
22663
|
+
var projectRoomParamsSchema = import_zod12.z.object({ projectId: id2 }).strict();
|
|
22664
|
+
var membershipParamsSchema = import_zod12.z.object({ projectId: id2, membershipId: id2 }).strict();
|
|
22665
|
+
var roomMessagesQuerySchema = import_zod12.z.object({
|
|
22666
|
+
beforeCreatedAt: import_zod12.z.string().datetime({ offset: true }).transform((value) => new Date(value)).optional(),
|
|
21538
22667
|
beforeId: id2.optional(),
|
|
21539
|
-
limit:
|
|
22668
|
+
limit: import_zod12.z.coerce.number().int().min(1).max(100).optional()
|
|
21540
22669
|
}).strict().refine((value) => value.beforeCreatedAt === void 0 === (value.beforeId === void 0), { message: "Both cursor fields are required" });
|
|
21541
|
-
var createRoomMessageSchema =
|
|
21542
|
-
text:
|
|
21543
|
-
mentions:
|
|
21544
|
-
|
|
21545
|
-
|
|
22670
|
+
var createRoomMessageSchema = import_zod12.z.object({
|
|
22671
|
+
text: import_zod12.z.string(),
|
|
22672
|
+
mentions: import_zod12.z.array(import_zod12.z.union([
|
|
22673
|
+
import_zod12.z.object({ type: import_zod12.z.literal("team") }).strict(),
|
|
22674
|
+
import_zod12.z.object({ type: import_zod12.z.literal("bot"), membershipId: id2 }).strict()
|
|
21546
22675
|
])).optional(),
|
|
21547
22676
|
replyToMessageId: id2.optional()
|
|
21548
22677
|
}).strict();
|
|
21549
|
-
var addProjectMemberSchema =
|
|
21550
|
-
var updateProjectMemberSchema =
|
|
22678
|
+
var addProjectMemberSchema = import_zod12.z.object({ userId: id2, role: import_zod12.z.enum(["owner", "admin", "member", "viewer"]) }).strict();
|
|
22679
|
+
var updateProjectMemberSchema = import_zod12.z.object({ role: import_zod12.z.enum(["owner", "admin", "member", "viewer"]), expectedUpdatedAt: isoDate }).strict();
|
|
21551
22680
|
var mutationExpectedUpdatedAtSchema = expectedUpdatedAt;
|
|
21552
|
-
var roomMessageRetryParamsSchema =
|
|
21553
|
-
var emptyObjectSchema =
|
|
21554
|
-
var idempotencyKeySchema =
|
|
21555
|
-
var addProjectBotSchema =
|
|
22681
|
+
var roomMessageRetryParamsSchema = import_zod12.z.object({ projectId: id2, messageId: id2 }).strict();
|
|
22682
|
+
var emptyObjectSchema = import_zod12.z.object({}).strict();
|
|
22683
|
+
var idempotencyKeySchema = import_zod12.z.string().refine((value) => value.trim().length >= 1 && value.trim().length <= 200 && /^[\x20-\x7e]+$/.test(value.trim()), "Invalid idempotency key").transform((value) => value.trim());
|
|
22684
|
+
var addProjectBotSchema = import_zod12.z.object({
|
|
21556
22685
|
assistantId: id2,
|
|
21557
|
-
role:
|
|
21558
|
-
title:
|
|
21559
|
-
responsibility:
|
|
21560
|
-
mentionName:
|
|
22686
|
+
role: import_zod12.z.enum(["coordinator", "specialist"]),
|
|
22687
|
+
title: import_zod12.z.string(),
|
|
22688
|
+
responsibility: import_zod12.z.string().optional(),
|
|
22689
|
+
mentionName: import_zod12.z.string()
|
|
21561
22690
|
}).strict();
|
|
21562
|
-
var updateProjectBotSchema =
|
|
21563
|
-
role:
|
|
21564
|
-
title:
|
|
21565
|
-
responsibility:
|
|
21566
|
-
mentionName:
|
|
22691
|
+
var updateProjectBotSchema = import_zod12.z.object({
|
|
22692
|
+
role: import_zod12.z.enum(["coordinator", "specialist"]).optional(),
|
|
22693
|
+
title: import_zod12.z.string().optional(),
|
|
22694
|
+
responsibility: import_zod12.z.string().optional(),
|
|
22695
|
+
mentionName: import_zod12.z.string().optional(),
|
|
21567
22696
|
expectedUpdatedAt: isoDate
|
|
21568
22697
|
}).strict().refine((value) => value.role !== void 0 || value.title !== void 0 || value.responsibility !== void 0 || value.mentionName !== void 0, { message: "At least one mutable field is required" });
|
|
21569
|
-
var resumeInterruptSchema =
|
|
22698
|
+
var resumeInterruptSchema = import_zod12.z.object({
|
|
21570
22699
|
membershipId: id2,
|
|
21571
|
-
answers:
|
|
21572
|
-
questionIndex:
|
|
21573
|
-
otherText:
|
|
21574
|
-
selectedOptions:
|
|
21575
|
-
filePath:
|
|
22700
|
+
answers: import_zod12.z.array(import_zod12.z.object({
|
|
22701
|
+
questionIndex: import_zod12.z.number().int().nonnegative(),
|
|
22702
|
+
otherText: import_zod12.z.string().optional(),
|
|
22703
|
+
selectedOptions: import_zod12.z.array(import_zod12.z.string()).optional(),
|
|
22704
|
+
filePath: import_zod12.z.string().optional()
|
|
21576
22705
|
}).strict()).optional()
|
|
21577
22706
|
}).strict();
|
|
21578
22707
|
|
|
@@ -21661,7 +22790,7 @@ function actor(request, projectId) {
|
|
|
21661
22790
|
return { tenantId: user.tenantId, userId: user.id, projectId };
|
|
21662
22791
|
}
|
|
21663
22792
|
function toProjectRoomMessage(message) {
|
|
21664
|
-
const publicMessage = (0,
|
|
22793
|
+
const publicMessage = (0, import_protocols18.toProjectRoomPublicMessage)(message);
|
|
21665
22794
|
if (publicMessage === void 0) throw new Error("Room message could not be mapped to its public DTO");
|
|
21666
22795
|
return publicMessage;
|
|
21667
22796
|
}
|
|
@@ -21670,7 +22799,7 @@ async function run(reply, operation) {
|
|
|
21670
22799
|
const result = await operation();
|
|
21671
22800
|
return reply.code(result.statusCode ?? 200).send({ success: true, data: result.data });
|
|
21672
22801
|
} catch (error) {
|
|
21673
|
-
if (error instanceof
|
|
22802
|
+
if (error instanceof import_zod13.ZodError) return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
21674
22803
|
if (error instanceof RoomInterruptServiceError) return reply.code(error.statusCode).send({ success: false, error: { code: error.code, message: error.message, retryable: false } });
|
|
21675
22804
|
if (error instanceof ProjectRoomAuthError || error instanceof ProjectCollaborationError || error instanceof ProjectMembershipServiceError || error instanceof ProjectRosterServiceError || error instanceof RoomMessageServiceError) return reply.code(error.statusCode).send({ success: false, error: { code: error.code, message: error.message, retryable: error instanceof RoomMessageServiceError && error.retryable } });
|
|
21676
22805
|
return reply.code(500).send({ success: false, error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false } });
|
|
@@ -21678,7 +22807,7 @@ async function run(reply, operation) {
|
|
|
21678
22807
|
}
|
|
21679
22808
|
|
|
21680
22809
|
// src/routes/project-roster.ts
|
|
21681
|
-
var
|
|
22810
|
+
var import_zod14 = require("zod");
|
|
21682
22811
|
var ProjectRosterAuthError = class extends Error {
|
|
21683
22812
|
constructor() {
|
|
21684
22813
|
super(...arguments);
|
|
@@ -21718,17 +22847,17 @@ async function execute(reply, operation) {
|
|
|
21718
22847
|
const result = await operation();
|
|
21719
22848
|
return reply.code(result.statusCode ?? 200).send({ success: true, data: result.data });
|
|
21720
22849
|
} catch (error) {
|
|
21721
|
-
if (error instanceof
|
|
22850
|
+
if (error instanceof import_zod14.ZodError) return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
21722
22851
|
if (error instanceof ProjectRosterAuthError || error instanceof ProjectCollaborationError || error instanceof ProjectRosterServiceError) return reply.code(error.statusCode).send({ success: false, error: { code: error.code, message: error.message, retryable: false } });
|
|
21723
22852
|
return reply.code(500).send({ success: false, error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false } });
|
|
21724
22853
|
}
|
|
21725
22854
|
}
|
|
21726
22855
|
|
|
21727
22856
|
// src/routes/cospace.ts
|
|
21728
|
-
var
|
|
21729
|
-
var
|
|
21730
|
-
var
|
|
21731
|
-
var
|
|
22857
|
+
var import_node_crypto20 = require("crypto");
|
|
22858
|
+
var import_zod15 = require("zod");
|
|
22859
|
+
var import_core48 = require("@axiom-lattice/core");
|
|
22860
|
+
var import_protocols19 = require("@axiom-lattice/protocols");
|
|
21732
22861
|
var CoSpaceAuthError = class extends Error {
|
|
21733
22862
|
constructor() {
|
|
21734
22863
|
super(...arguments);
|
|
@@ -21756,7 +22885,7 @@ async function computeUnread(deps, tenantId, userId, roomId, lastMessage) {
|
|
|
21756
22885
|
});
|
|
21757
22886
|
}
|
|
21758
22887
|
function registerCoSpaceRoutes(app2, deps) {
|
|
21759
|
-
const idFactory = deps.idFactory ?? (() => (0,
|
|
22888
|
+
const idFactory = deps.idFactory ?? (() => (0, import_node_crypto20.randomUUID)());
|
|
21760
22889
|
app2.get(
|
|
21761
22890
|
"/api/workspaces/:workspaceId/rooms",
|
|
21762
22891
|
async (request, reply) => {
|
|
@@ -21805,7 +22934,7 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
21805
22934
|
limit: 1
|
|
21806
22935
|
});
|
|
21807
22936
|
const lastMessage = messages[0];
|
|
21808
|
-
const publicLastMessage = lastMessage === void 0 ? void 0 : (0,
|
|
22937
|
+
const publicLastMessage = lastMessage === void 0 ? void 0 : (0, import_protocols19.toProjectRoomPublicMessage)(lastMessage);
|
|
21809
22938
|
const memberships = await deps.membershipStore.list(
|
|
21810
22939
|
actor3.tenantId,
|
|
21811
22940
|
project.id
|
|
@@ -21872,7 +23001,7 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
21872
23001
|
rooms.unshift({
|
|
21873
23002
|
project: publicRoom.project,
|
|
21874
23003
|
room: publicRoom.room,
|
|
21875
|
-
lastMessage: publicLastMessage === void 0 ? void 0 : (0,
|
|
23004
|
+
lastMessage: publicLastMessage === void 0 ? void 0 : (0, import_protocols19.toProjectRoomPublicMessage)(publicLastMessage),
|
|
21876
23005
|
participantCount: publicMemberships.filter((m) => m.status === "active").length,
|
|
21877
23006
|
participantNames: publicParticipantNames,
|
|
21878
23007
|
isPublic: true,
|
|
@@ -21912,14 +23041,14 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
21912
23041
|
}
|
|
21913
23042
|
let projectId;
|
|
21914
23043
|
if (body.type === "human") {
|
|
21915
|
-
projectId = (0,
|
|
23044
|
+
projectId = (0, import_core48.generateDirectHumanProjectId)(
|
|
21916
23045
|
actor3.tenantId,
|
|
21917
23046
|
params.workspaceId,
|
|
21918
23047
|
actor3.userId,
|
|
21919
23048
|
body.targetUserId
|
|
21920
23049
|
);
|
|
21921
23050
|
} else {
|
|
21922
|
-
projectId = (0,
|
|
23051
|
+
projectId = (0, import_core48.generateDirectBotProjectId)(
|
|
21923
23052
|
actor3.tenantId,
|
|
21924
23053
|
params.workspaceId,
|
|
21925
23054
|
actor3.userId,
|
|
@@ -22126,7 +23255,7 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
22126
23255
|
);
|
|
22127
23256
|
const bots = [];
|
|
22128
23257
|
for (const assistant of assistants) {
|
|
22129
|
-
const directProjectId = (0,
|
|
23258
|
+
const directProjectId = (0, import_core48.generateDirectBotProjectId)(
|
|
22130
23259
|
actor3.tenantId,
|
|
22131
23260
|
params.workspaceId,
|
|
22132
23261
|
actor3.userId,
|
|
@@ -22188,7 +23317,7 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
22188
23317
|
if (link.userId === actor3.userId) continue;
|
|
22189
23318
|
const user = await deps.userStore.getUserById(link.userId);
|
|
22190
23319
|
if (!user) continue;
|
|
22191
|
-
const directProjectId = (0,
|
|
23320
|
+
const directProjectId = (0, import_core48.generateDirectHumanProjectId)(
|
|
22192
23321
|
actor3.tenantId,
|
|
22193
23322
|
params.workspaceId,
|
|
22194
23323
|
actor3.userId,
|
|
@@ -22277,7 +23406,7 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
22277
23406
|
data: result.data
|
|
22278
23407
|
});
|
|
22279
23408
|
} catch (error) {
|
|
22280
|
-
if (error instanceof
|
|
23409
|
+
if (error instanceof import_zod15.ZodError) {
|
|
22281
23410
|
return reply.code(400).send({
|
|
22282
23411
|
success: false,
|
|
22283
23412
|
error: {
|
|
@@ -22310,9 +23439,9 @@ function registerCoSpaceRoutes(app2, deps) {
|
|
|
22310
23439
|
}
|
|
22311
23440
|
|
|
22312
23441
|
// src/routes/push-notifications.ts
|
|
22313
|
-
var
|
|
22314
|
-
var subscriptionSchema =
|
|
22315
|
-
var deleteSchema =
|
|
23442
|
+
var import_zod16 = require("zod");
|
|
23443
|
+
var subscriptionSchema = import_zod16.z.object({ endpoint: import_zod16.z.string().min(1), keys: import_zod16.z.object({ p256dh: import_zod16.z.string().min(1), auth: import_zod16.z.string().min(1) }).strict() }).strict();
|
|
23444
|
+
var deleteSchema = import_zod16.z.object({ endpoint: import_zod16.z.string().min(1) }).strict();
|
|
22316
23445
|
function actor2(request) {
|
|
22317
23446
|
const user = request.user;
|
|
22318
23447
|
return user?.id && user.tenantId ? { tenantId: user.tenantId, userId: user.id } : null;
|
|
@@ -22340,7 +23469,7 @@ function registerPushNotificationRoutes(app2, deps) {
|
|
|
22340
23469
|
});
|
|
22341
23470
|
return reply.code(204).send();
|
|
22342
23471
|
} catch (error) {
|
|
22343
|
-
if (error instanceof
|
|
23472
|
+
if (error instanceof import_zod16.ZodError) {
|
|
22344
23473
|
return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
22345
23474
|
}
|
|
22346
23475
|
return reply.code(500).send({ success: false, error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false } });
|
|
@@ -22354,7 +23483,7 @@ function registerPushNotificationRoutes(app2, deps) {
|
|
|
22354
23483
|
await deps.subscriptionStore.delete(current.tenantId, current.userId, body.endpoint);
|
|
22355
23484
|
return reply.code(204).send();
|
|
22356
23485
|
} catch (error) {
|
|
22357
|
-
if (error instanceof
|
|
23486
|
+
if (error instanceof import_zod16.ZodError) {
|
|
22358
23487
|
return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
22359
23488
|
}
|
|
22360
23489
|
return reply.code(500).send({ success: false, error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false } });
|
|
@@ -22585,7 +23714,7 @@ var RoomTargetResolver = class {
|
|
|
22585
23714
|
};
|
|
22586
23715
|
|
|
22587
23716
|
// src/services/collaboration/RoomDispatchService.ts
|
|
22588
|
-
var
|
|
23717
|
+
var import_protocols20 = require("@axiom-lattice/protocols");
|
|
22589
23718
|
|
|
22590
23719
|
// src/router/InternalRoomDispatch.ts
|
|
22591
23720
|
var INTERNAL_ROOM_DISPATCH = Symbol("internal-room-dispatch");
|
|
@@ -22687,7 +23816,7 @@ function snapshotDispatchSource(value) {
|
|
|
22687
23816
|
};
|
|
22688
23817
|
}
|
|
22689
23818
|
function snapshotMentions(value) {
|
|
22690
|
-
const rows = (0,
|
|
23819
|
+
const rows = (0, import_protocols20.snapshotExactArray)(value);
|
|
22691
23820
|
if (!rows) return void 0;
|
|
22692
23821
|
const result = [];
|
|
22693
23822
|
for (const row of rows) {
|
|
@@ -22705,15 +23834,15 @@ function snapshotMentions(value) {
|
|
|
22705
23834
|
function exactValues(value, required, optional = []) {
|
|
22706
23835
|
try {
|
|
22707
23836
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
22708
|
-
return (0,
|
|
23837
|
+
return (0, import_protocols20.snapshotExactRecord)(value, required, optional);
|
|
22709
23838
|
} catch {
|
|
22710
23839
|
return void 0;
|
|
22711
23840
|
}
|
|
22712
23841
|
}
|
|
22713
23842
|
|
|
22714
23843
|
// src/services/collaboration/CoordinatorWakeNotifier.ts
|
|
22715
|
-
var
|
|
22716
|
-
var
|
|
23844
|
+
var import_node_crypto21 = require("crypto");
|
|
23845
|
+
var import_protocols21 = require("@axiom-lattice/protocols");
|
|
22717
23846
|
var SYNTHETIC_WAKE_AUTHOR = "coordinator-wake";
|
|
22718
23847
|
var CoordinatorWakeNotifier = class {
|
|
22719
23848
|
/** Creates a bounded Project Task lifecycle observer that wakes the Room Coordinator. */
|
|
@@ -22726,7 +23855,7 @@ var CoordinatorWakeNotifier = class {
|
|
|
22726
23855
|
this.accepted = /* @__PURE__ */ new Set();
|
|
22727
23856
|
this.maxPending = deps.maxPending ?? 1024;
|
|
22728
23857
|
if (!Number.isSafeInteger(this.maxPending) || this.maxPending < 1) throw new RangeError("maxPending must be a positive safe integer");
|
|
22729
|
-
this.idFactory = deps.idFactory ??
|
|
23858
|
+
this.idFactory = deps.idFactory ?? import_node_crypto21.randomUUID;
|
|
22730
23859
|
}
|
|
22731
23860
|
/** Admits one notification without awaiting any store or dispatch operation. */
|
|
22732
23861
|
handle(value, anchorMessageId) {
|
|
@@ -22869,7 +23998,7 @@ var CoordinatorWakeNotifier = class {
|
|
|
22869
23998
|
}
|
|
22870
23999
|
}
|
|
22871
24000
|
parse(value) {
|
|
22872
|
-
const row = (0,
|
|
24001
|
+
const row = (0, import_protocols21.snapshotExactRecord)(value, ["tenantId", "workspaceId", "projectId", "taskId", "status", "action", "eventKey", "updatedAt"], ["threadId"]);
|
|
22873
24002
|
const actions = ["in_progress", "interrupted", "failed", "completed", "cancelled", "reassigned"];
|
|
22874
24003
|
const statuses = ["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"];
|
|
22875
24004
|
if (!row || !actions.includes(String(row.action)) || !statuses.includes(String(row.status)) || !["tenantId", "workspaceId", "projectId", "taskId", "eventKey", "updatedAt"].every((key) => typeof row[key] === "string" && row[key].length > 0) || !Number.isFinite(Date.parse(row.updatedAt)) || row.threadId !== void 0 && typeof row.threadId !== "string") return void 0;
|
|
@@ -23003,9 +24132,9 @@ function createRoomMessageRetentionJob(input) {
|
|
|
23003
24132
|
}
|
|
23004
24133
|
|
|
23005
24134
|
// src/services/collaboration/ProjectTaskExecutionMutex.ts
|
|
23006
|
-
var
|
|
24135
|
+
var import_protocols22 = require("@axiom-lattice/protocols");
|
|
23007
24136
|
function machineKey(scope) {
|
|
23008
|
-
const snapshot = (0,
|
|
24137
|
+
const snapshot = (0, import_protocols22.snapshotExactRecord)(scope, ["tenantId", "assistantId", "projectId"]);
|
|
23009
24138
|
if (!snapshot) throw new Error("Invalid Project task machine scope");
|
|
23010
24139
|
const values = [snapshot.tenantId, snapshot.assistantId, snapshot.projectId];
|
|
23011
24140
|
for (const value of values) {
|
|
@@ -23081,9 +24210,9 @@ var ProjectTaskExecutionMutex = class {
|
|
|
23081
24210
|
};
|
|
23082
24211
|
|
|
23083
24212
|
// src/services/collaboration/ProjectTaskObserver.ts
|
|
23084
|
-
var
|
|
23085
|
-
var
|
|
23086
|
-
var
|
|
24213
|
+
var import_node_crypto22 = require("crypto");
|
|
24214
|
+
var import_core49 = require("@axiom-lattice/core");
|
|
24215
|
+
var import_protocols23 = require("@axiom-lattice/protocols");
|
|
23087
24216
|
var SYSTEM_ACTOR2 = "system";
|
|
23088
24217
|
var TERMINAL_ACTIONS = ["completed", "failed", "interrupted", "cancelled"];
|
|
23089
24218
|
var ProjectTaskObserver = class {
|
|
@@ -23104,7 +24233,7 @@ var ProjectTaskObserver = class {
|
|
|
23104
24233
|
throw new Error(`Invalid Project task observer bound: ${name}`);
|
|
23105
24234
|
}
|
|
23106
24235
|
}
|
|
23107
|
-
this.runBridge = (0,
|
|
24236
|
+
this.runBridge = (0, import_core49.createTaskRunBridge)({ lifecycle: this.deps.lifecycle });
|
|
23108
24237
|
}
|
|
23109
24238
|
/** Fire-and-forget notifier adapter that never waits for observer work or machine acquisition. */
|
|
23110
24239
|
async listener(event) {
|
|
@@ -23634,7 +24763,7 @@ var ProjectTaskObserver = class {
|
|
|
23634
24763
|
action: "activity",
|
|
23635
24764
|
summary: "Project task recovery warning",
|
|
23636
24765
|
detail: { type: "project_task_recovery_warning", phase, message },
|
|
23637
|
-
eventKey: `project-task-recovery-warning:${task.id}:${phase}:${(0,
|
|
24766
|
+
eventKey: `project-task-recovery-warning:${task.id}:${phase}:${(0, import_node_crypto22.createHash)("sha256").update(message).digest("hex")}`
|
|
23638
24767
|
});
|
|
23639
24768
|
}
|
|
23640
24769
|
async safeWarning(task, phase, error) {
|
|
@@ -23838,7 +24967,7 @@ var ProjectTaskObserver = class {
|
|
|
23838
24967
|
}
|
|
23839
24968
|
}
|
|
23840
24969
|
async persistOverflowWarning(event) {
|
|
23841
|
-
const scopeDigest = (0,
|
|
24970
|
+
const scopeDigest = (0, import_node_crypto22.createHash)("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
|
|
23842
24971
|
try {
|
|
23843
24972
|
await this.deps.workItems.createIfAbsentByEventKey({
|
|
23844
24973
|
tenantId: event.tenantId,
|
|
@@ -23929,7 +25058,7 @@ function errorMessage3(error) {
|
|
|
23929
25058
|
}
|
|
23930
25059
|
function readCreatedBy(value) {
|
|
23931
25060
|
try {
|
|
23932
|
-
const row = (0,
|
|
25061
|
+
const row = (0, import_protocols23.snapshotExactRecord)(value, ["assistantId", "membershipId"]);
|
|
23933
25062
|
return row && typeof row.assistantId === "string" && row.assistantId.length > 0 && typeof row.membershipId === "string" && row.membershipId.length > 0 ? { assistantId: row.assistantId, membershipId: row.membershipId } : void 0;
|
|
23934
25063
|
} catch {
|
|
23935
25064
|
return void 0;
|
|
@@ -23943,8 +25072,8 @@ function combineSignals(left, right) {
|
|
|
23943
25072
|
}
|
|
23944
25073
|
|
|
23945
25074
|
// src/services/collaboration/RoomProjectionService.ts
|
|
23946
|
-
var
|
|
23947
|
-
var
|
|
25075
|
+
var import_node_crypto23 = require("crypto");
|
|
25076
|
+
var import_protocols24 = require("@axiom-lattice/protocols");
|
|
23948
25077
|
var ROOM_PROJECTABLE_ACTIONS = [
|
|
23949
25078
|
"interrupted",
|
|
23950
25079
|
"failed",
|
|
@@ -24023,7 +25152,7 @@ var RoomProjectionService = class {
|
|
|
24023
25152
|
projectId: task.projectId,
|
|
24024
25153
|
roomId: room.id,
|
|
24025
25154
|
author: { type: "system" },
|
|
24026
|
-
content: { type: "text", text: `${
|
|
25155
|
+
content: { type: "text", text: `${import_protocols24.PROJECT_ROOM_TASK_DELEGATED_LABEL}${import_protocols24.PROJECT_ROOM_TASK_DELEGATED_SEPARATOR}${input.fromLabel} \u2192 ${input.toLabel}` },
|
|
24027
25156
|
mentions: [],
|
|
24028
25157
|
source: "task",
|
|
24029
25158
|
sourceId: task.id,
|
|
@@ -24158,7 +25287,7 @@ function safeRepairIdentity(value, scope) {
|
|
|
24158
25287
|
return taskId && eventKey && tenantId === scope.tenantId && workspaceId === scope.workspaceId && projectId === scope.projectId ? { taskId, eventKey } : void 0;
|
|
24159
25288
|
}
|
|
24160
25289
|
function tupleKey(prefix, values) {
|
|
24161
|
-
const digest = (0,
|
|
25290
|
+
const digest = (0, import_node_crypto23.createHash)("sha256").update(JSON.stringify(values)).digest("hex");
|
|
24162
25291
|
return `${prefix}:${digest}`;
|
|
24163
25292
|
}
|
|
24164
25293
|
function parseProjectionInput(taskValue, itemValue) {
|
|
@@ -24174,7 +25303,7 @@ function parseProjectionInput(taskValue, itemValue) {
|
|
|
24174
25303
|
}
|
|
24175
25304
|
}
|
|
24176
25305
|
function snapshotProjectableWorkItem(value) {
|
|
24177
|
-
const row = (0,
|
|
25306
|
+
const row = (0, import_protocols24.snapshotExactRecord)(value, ["id", "taskId", "tenantId", "action", "actor", "createdAt"], [
|
|
24178
25307
|
"workspaceId",
|
|
24179
25308
|
"projectId",
|
|
24180
25309
|
"threadId",
|
|
@@ -24184,7 +25313,7 @@ function snapshotProjectableWorkItem(value) {
|
|
|
24184
25313
|
"eventKey"
|
|
24185
25314
|
]);
|
|
24186
25315
|
const createdAt = cloneExactDate(row?.createdAt);
|
|
24187
|
-
if (!row || !createdAt || typeof row.id !== "string" || !row.id || typeof row.taskId !== "string" || !row.taskId || typeof row.tenantId !== "string" || !row.tenantId || typeof row.workspaceId !== "string" || !row.workspaceId || typeof row.projectId !== "string" || !row.projectId || typeof row.actor !== "string" || !row.actor || typeof row.eventKey !== "string" || !row.eventKey || !
|
|
25316
|
+
if (!row || !createdAt || typeof row.id !== "string" || !row.id || typeof row.taskId !== "string" || !row.taskId || typeof row.tenantId !== "string" || !row.tenantId || typeof row.workspaceId !== "string" || !row.workspaceId || typeof row.projectId !== "string" || !row.projectId || typeof row.actor !== "string" || !row.actor || typeof row.eventKey !== "string" || !row.eventKey || !import_protocols24.PROJECT_TASK_LIFECYCLE_ACTIONS.includes(row.action) || row.threadId !== void 0 && typeof row.threadId !== "string" || row.summary !== void 0 && typeof row.summary !== "string" || row.attempt !== void 0 && (typeof row.attempt !== "number" || !Number.isFinite(row.attempt))) return void 0;
|
|
24188
25317
|
try {
|
|
24189
25318
|
const detail = row.detail === void 0 ? void 0 : snapshotJsonValue(row.detail);
|
|
24190
25319
|
if (detail !== void 0 && !isRecord3(detail)) return void 0;
|
|
@@ -24211,26 +25340,26 @@ function parseIso(value) {
|
|
|
24211
25340
|
return typeof value === "string" && value.length > 0 && Number.isFinite(Date.parse(value));
|
|
24212
25341
|
}
|
|
24213
25342
|
function parseInterruption(value) {
|
|
24214
|
-
const common = (0,
|
|
25343
|
+
const common = (0, import_protocols24.snapshotExactRecord)(value, ["type", "summary", "requestedAt"]);
|
|
24215
25344
|
if (common && (common.type === "missing_input" || common.type === "user_decision") && typeof common.summary === "string" && common.summary.length > 0 && parseIso(common.requestedAt)) {
|
|
24216
25345
|
return { type: common.type, summary: common.summary, requestedAt: common.requestedAt };
|
|
24217
25346
|
}
|
|
24218
|
-
const review = (0,
|
|
25347
|
+
const review = (0, import_protocols24.snapshotExactRecord)(value, ["type", "evidenceId"]);
|
|
24219
25348
|
return review?.type === "review_required" && typeof review.evidenceId === "string" && review.evidenceId.length > 0 ? { type: "review_required", evidenceId: review.evidenceId } : void 0;
|
|
24220
25349
|
}
|
|
24221
25350
|
function parseBeliefImpact(value) {
|
|
24222
|
-
const rows = (0,
|
|
25351
|
+
const rows = (0, import_protocols24.snapshotExactArray)(value);
|
|
24223
25352
|
if (!rows) return void 0;
|
|
24224
25353
|
const impacts = [];
|
|
24225
25354
|
for (const row of rows) {
|
|
24226
|
-
const entry = (0,
|
|
25355
|
+
const entry = (0, import_protocols24.snapshotExactRecord)(row, ["key", "after", "basis"]);
|
|
24227
25356
|
if (!entry || typeof entry.key !== "string" || !entry.key || typeof entry.after !== "number" || !Number.isFinite(entry.after) || typeof entry.basis !== "string" || !entry.basis) return void 0;
|
|
24228
25357
|
impacts.push({ key: entry.key, after: entry.after, basis: entry.basis });
|
|
24229
25358
|
}
|
|
24230
25359
|
return impacts;
|
|
24231
25360
|
}
|
|
24232
25361
|
function parseCompletion(item) {
|
|
24233
|
-
const row = (0,
|
|
25362
|
+
const row = (0, import_protocols24.snapshotExactRecord)(item.detail, ["evidenceId", "eventKey", "result", "beliefImpact"]);
|
|
24234
25363
|
const beliefImpact = parseBeliefImpact(row?.beliefImpact);
|
|
24235
25364
|
if (!row || typeof row.evidenceId !== "string" || !row.evidenceId || typeof row.eventKey !== "string" || !row.eventKey || item.eventKey !== `${row.eventKey}:completed` || typeof row.result !== "string" || !beliefImpact) return void 0;
|
|
24236
25365
|
return { evidenceId: row.evidenceId, eventKey: row.eventKey, result: row.result, beliefImpact };
|
|
@@ -24240,19 +25369,19 @@ function parseProjection(item) {
|
|
|
24240
25369
|
return parseInterruption(item.detail) ? { kind: "interrupted", text: "Task interrupted" } : void 0;
|
|
24241
25370
|
}
|
|
24242
25371
|
if (item.action === "failed") {
|
|
24243
|
-
const row = (0,
|
|
25372
|
+
const row = (0, import_protocols24.snapshotExactRecord)(item.detail, ["failureReason"]);
|
|
24244
25373
|
return typeof row?.failureReason === "string" && row.failureReason ? { kind: "failed", text: "Task failed" } : void 0;
|
|
24245
25374
|
}
|
|
24246
25375
|
if (item.action === "completed") {
|
|
24247
25376
|
return parseCompletion(item) ? { kind: "completed", text: "Task completed" } : void 0;
|
|
24248
25377
|
}
|
|
24249
25378
|
if (item.action === "cancelled") {
|
|
24250
|
-
const empty = (0,
|
|
24251
|
-
const summarized = (0,
|
|
25379
|
+
const empty = (0, import_protocols24.snapshotExactRecord)(item.detail, []);
|
|
25380
|
+
const summarized = (0, import_protocols24.snapshotExactRecord)(item.detail, ["summary"]);
|
|
24252
25381
|
return empty || typeof summarized?.summary === "string" && summarized.summary ? { kind: "cancelled", text: "Task cancelled" } : void 0;
|
|
24253
25382
|
}
|
|
24254
25383
|
if (item.action === "reassigned") {
|
|
24255
|
-
const row = (0,
|
|
25384
|
+
const row = (0, import_protocols24.snapshotExactRecord)(
|
|
24256
25385
|
item.detail,
|
|
24257
25386
|
["previousOwnerId", "previousThreadId", "ownerId", "membershipId", "threadId"]
|
|
24258
25387
|
);
|
|
@@ -24261,7 +25390,7 @@ function parseProjection(item) {
|
|
|
24261
25390
|
return void 0;
|
|
24262
25391
|
}
|
|
24263
25392
|
function assertCanonicalProjection(value, expected) {
|
|
24264
|
-
const row = (0,
|
|
25393
|
+
const row = (0, import_protocols24.snapshotExactRecord)(value, [
|
|
24265
25394
|
"id",
|
|
24266
25395
|
"tenantId",
|
|
24267
25396
|
"workspaceId",
|
|
@@ -24275,9 +25404,9 @@ function assertCanonicalProjection(value, expected) {
|
|
|
24275
25404
|
"idempotencyKey",
|
|
24276
25405
|
"createdAt"
|
|
24277
25406
|
]);
|
|
24278
|
-
const author = (0,
|
|
24279
|
-
const content = (0,
|
|
24280
|
-
const mentions = (0,
|
|
25407
|
+
const author = (0, import_protocols24.snapshotExactRecord)(row?.author, ["type"]);
|
|
25408
|
+
const content = (0, import_protocols24.snapshotExactRecord)(row?.content, ["type", "text"]);
|
|
25409
|
+
const mentions = (0, import_protocols24.snapshotExactArray)(row?.mentions);
|
|
24281
25410
|
const createdAt = cloneExactDate(row?.createdAt);
|
|
24282
25411
|
if (!row || !createdAt || author?.type !== "system" || content?.type !== "text" || content.text !== expected.content.text || !mentions || mentions.length !== 0 || typeof row.id !== "string" || !row.id || row.tenantId !== expected.tenantId || row.workspaceId !== expected.workspaceId || row.projectId !== expected.projectId || row.roomId !== expected.roomId || row.source !== "task" || row.sourceId !== expected.sourceId || row.idempotencyKey !== expected.idempotencyKey) {
|
|
24283
25412
|
throw new Error("Invalid canonical task projection");
|
|
@@ -24298,7 +25427,7 @@ function assertCanonicalProjection(value, expected) {
|
|
|
24298
25427
|
};
|
|
24299
25428
|
}
|
|
24300
25429
|
function snapshotLifecycleCursor(value) {
|
|
24301
|
-
const row = (0,
|
|
25430
|
+
const row = (0, import_protocols24.snapshotExactRecord)(value, ["id", "taskId", "tenantId", "action", "actor", "createdAt"], [
|
|
24302
25431
|
"workspaceId",
|
|
24303
25432
|
"projectId",
|
|
24304
25433
|
"threadId",
|
|
@@ -24315,11 +25444,11 @@ function isRecord3(value) {
|
|
|
24315
25444
|
}
|
|
24316
25445
|
|
|
24317
25446
|
// src/services/collaboration/ProjectRoomRealtimeRuntime.ts
|
|
24318
|
-
var
|
|
25447
|
+
var import_core50 = require("@axiom-lattice/core");
|
|
24319
25448
|
|
|
24320
25449
|
// src/services/collaboration/ProjectRoomEventBroker.ts
|
|
24321
|
-
var
|
|
24322
|
-
var
|
|
25450
|
+
var import_node_crypto24 = require("crypto");
|
|
25451
|
+
var import_protocols25 = require("@axiom-lattice/protocols");
|
|
24323
25452
|
var DEFAULT_MAX_EVENTS_PER_ROOM = 1e3;
|
|
24324
25453
|
var DEFAULT_MAX_EVENT_AGE_MS = 10 * 60 * 1e3;
|
|
24325
25454
|
var DEFAULT_MAX_ROOMS = 1e3;
|
|
@@ -24343,7 +25472,7 @@ var ProjectRoomEventBroker = class {
|
|
|
24343
25472
|
this.subscriberCount = 0;
|
|
24344
25473
|
this.usageClock = 0;
|
|
24345
25474
|
this.closed = false;
|
|
24346
|
-
this.epoch = options.epoch ?? (0,
|
|
25475
|
+
this.epoch = options.epoch ?? (0, import_node_crypto24.randomUUID)();
|
|
24347
25476
|
if (!UUID_PATTERN.test(this.epoch)) {
|
|
24348
25477
|
throw new TypeError("Project Room event epoch must be a lowercase canonical UUID");
|
|
24349
25478
|
}
|
|
@@ -24382,7 +25511,7 @@ var ProjectRoomEventBroker = class {
|
|
|
24382
25511
|
const canonicalScope = snapshotScope(scope);
|
|
24383
25512
|
const key = scopeKey2(canonicalScope);
|
|
24384
25513
|
const now = this.currentTime();
|
|
24385
|
-
if (this.sequence >= Number.MAX_SAFE_INTEGER) throw new
|
|
25514
|
+
if (this.sequence >= Number.MAX_SAFE_INTEGER) throw new import_protocols25.ProjectRoomBrokerCapacityError();
|
|
24386
25515
|
const nextSequence = this.sequence + 1;
|
|
24387
25516
|
const event = snapshotJson({
|
|
24388
25517
|
...snapshotDraft(draft),
|
|
@@ -24565,11 +25694,11 @@ var ProjectRoomEventBroker = class {
|
|
|
24565
25694
|
}
|
|
24566
25695
|
resolveCursor(key, afterEventId, plan) {
|
|
24567
25696
|
if (afterEventId === void 0) return void 0;
|
|
24568
|
-
const cursor = (0,
|
|
24569
|
-
if (!cursor) throw new
|
|
24570
|
-
if (cursor.epoch !== this.epoch) throw new
|
|
25697
|
+
const cursor = (0, import_protocols25.parseProjectRoomEventId)(afterEventId);
|
|
25698
|
+
if (!cursor) throw new import_protocols25.ProjectRoomCursorError("CURSOR_EXPIRED");
|
|
25699
|
+
if (cursor.epoch !== this.epoch) throw new import_protocols25.ProjectRoomCursorError("SERVER_RESTART");
|
|
24571
25700
|
if (plan.sequences.has(cursor.sequence) || this.sequenceScopes.get(cursor.sequence) !== key) {
|
|
24572
|
-
throw new
|
|
25701
|
+
throw new import_protocols25.ProjectRoomCursorError("CURSOR_EXPIRED");
|
|
24573
25702
|
}
|
|
24574
25703
|
return cursor.sequence;
|
|
24575
25704
|
}
|
|
@@ -24661,8 +25790,8 @@ function snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
|
24661
25790
|
}
|
|
24662
25791
|
|
|
24663
25792
|
// src/services/collaboration/ProjectRoomRealtimePublisher.ts
|
|
24664
|
-
var
|
|
24665
|
-
var
|
|
25793
|
+
var import_node_crypto25 = require("crypto");
|
|
25794
|
+
var import_protocols26 = require("@axiom-lattice/protocols");
|
|
24666
25795
|
var BrokerProjectRoomRealtimePublisher = class {
|
|
24667
25796
|
/** Creates a publisher around a broker and structured warning logger. */
|
|
24668
25797
|
constructor(deps) {
|
|
@@ -24676,7 +25805,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24676
25805
|
/** Publishes a canonical message after projecting it to its public shape. */
|
|
24677
25806
|
messageCreated(message) {
|
|
24678
25807
|
this.publish("message.created", () => {
|
|
24679
|
-
const projection = (0,
|
|
25808
|
+
const projection = (0, import_protocols26.snapshotProjectRoomMessageRealtime)(message);
|
|
24680
25809
|
if (!projection) return void 0;
|
|
24681
25810
|
return {
|
|
24682
25811
|
sourceKey: `message:${projection.publicMessage.id}`,
|
|
@@ -24691,7 +25820,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24691
25820
|
this.publish("roster.changed", () => {
|
|
24692
25821
|
const membership = snapshotInput(input, "roster");
|
|
24693
25822
|
if (membership.membership.tenantId !== membership.tenantId || membership.membership.roomId !== membership.roomId || !safeId2(membership.membership.projectId)) throw new TypeError("Invalid roster scope");
|
|
24694
|
-
const projection = (0,
|
|
25823
|
+
const projection = (0, import_protocols26.snapshotProjectRoomBotMembershipRealtime)(membership.membership);
|
|
24695
25824
|
if (!projection) return void 0;
|
|
24696
25825
|
return {
|
|
24697
25826
|
sourceKey: membership.sourceKey,
|
|
@@ -24706,7 +25835,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24706
25835
|
this.publish("membership.changed", () => {
|
|
24707
25836
|
const membership = snapshotInput(input, "membership");
|
|
24708
25837
|
if (membership.membership.tenantId !== membership.tenantId || !safeId2(membership.membership.projectId)) throw new TypeError("Invalid membership scope");
|
|
24709
|
-
const projection = (0,
|
|
25838
|
+
const projection = (0, import_protocols26.snapshotProjectRoomMembershipRealtime)(membership.membership);
|
|
24710
25839
|
if (!projection) return void 0;
|
|
24711
25840
|
return {
|
|
24712
25841
|
sourceKey: membership.sourceKey,
|
|
@@ -24735,11 +25864,11 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24735
25864
|
/** Publishes a membership notice onto the affected user's channel scope. */
|
|
24736
25865
|
membershipAffected(input) {
|
|
24737
25866
|
this.publish("membership.affected", () => {
|
|
24738
|
-
const value = (0,
|
|
25867
|
+
const value = (0, import_protocols26.snapshotExactRecord)(input, ["tenantId", "userId", "change", "projectId", "roomId", "sourceKey"]);
|
|
24739
25868
|
if (!value || !safeId2(value.tenantId) || !safeId2(value.userId) || !safeId2(value.projectId) || !safeId2(value.roomId) || !safeSourceKey(value.sourceKey) || !["added", "removed", "role_changed"].includes(value.change)) {
|
|
24740
25869
|
throw new TypeError("Invalid membership input");
|
|
24741
25870
|
}
|
|
24742
|
-
const scope = (0,
|
|
25871
|
+
const scope = (0, import_protocols26.projectRoomUserEventScope)(value.tenantId, value.userId);
|
|
24743
25872
|
return {
|
|
24744
25873
|
sourceKey: value.sourceKey,
|
|
24745
25874
|
id: value.sourceKey,
|
|
@@ -24755,11 +25884,11 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24755
25884
|
/** Publishes a read-marker update onto the acting user's channel scope. */
|
|
24756
25885
|
readChanged(input) {
|
|
24757
25886
|
this.publish("read.changed", () => {
|
|
24758
|
-
const value = (0,
|
|
25887
|
+
const value = (0, import_protocols26.snapshotExactRecord)(input, ["tenantId", "userId", "projectId", "roomId", "lastReadAt", "sourceKey"]);
|
|
24759
25888
|
if (!value || !safeId2(value.tenantId) || !safeId2(value.userId) || !safeId2(value.projectId) || !safeId2(value.roomId) || !safeSourceKey(value.sourceKey) || typeof value.lastReadAt !== "string" || !Number.isFinite(Date.parse(value.lastReadAt))) {
|
|
24760
25889
|
throw new TypeError("Invalid read input");
|
|
24761
25890
|
}
|
|
24762
|
-
const scope = (0,
|
|
25891
|
+
const scope = (0, import_protocols26.projectRoomUserEventScope)(value.tenantId, value.userId);
|
|
24763
25892
|
return {
|
|
24764
25893
|
sourceKey: value.sourceKey,
|
|
24765
25894
|
id: value.sourceKey,
|
|
@@ -24777,7 +25906,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24777
25906
|
try {
|
|
24778
25907
|
event = build();
|
|
24779
25908
|
if (!event) throw new Error("Invalid Project Room realtime projection");
|
|
24780
|
-
const dedupeKey = (0,
|
|
25909
|
+
const dedupeKey = (0, import_node_crypto25.createHash)("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
|
|
24781
25910
|
if (this.sourceKeys.has(dedupeKey)) return;
|
|
24782
25911
|
this.deps.broker.publish(event.scope, event.draft);
|
|
24783
25912
|
this.sourceKeys.set(dedupeKey, true);
|
|
@@ -24800,13 +25929,13 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
24800
25929
|
};
|
|
24801
25930
|
function snapshotInput(input, kind) {
|
|
24802
25931
|
const keys2 = kind === "message" ? ["tenantId", "roomId", "message", "sourceKey"] : kind === "roster" ? ["tenantId", "roomId", "membership", "sourceKey"] : ["tenantId", "roomId", "membership", "sourceKey"];
|
|
24803
|
-
const value = (0,
|
|
25932
|
+
const value = (0, import_protocols26.snapshotExactRecord)(input, [...keys2, "change"]);
|
|
24804
25933
|
const validChange = kind === "roster" ? ["added", "updated", "paused", "resumed", "removed"].includes(value?.change) : ["added", "role_changed", "removed"].includes(value?.change);
|
|
24805
25934
|
if (!value || !safeId2(value.tenantId) || !safeId2(value.roomId) || !safeSourceKey(value.sourceKey) || !validChange) throw new TypeError(`Invalid ${kind} input`);
|
|
24806
25935
|
return value;
|
|
24807
25936
|
}
|
|
24808
25937
|
function snapshotTaskInput(input) {
|
|
24809
|
-
const value = (0,
|
|
25938
|
+
const value = (0, import_protocols26.snapshotExactRecord)(input, ["tenantId", "roomId", "projectId", "taskId", "status", "ownerMembershipId", "updatedAt", "sourceKey"]);
|
|
24810
25939
|
if (!value || !safeId2(value.tenantId) || !safeId2(value.roomId) || !safeSourceKey(value.sourceKey)) throw new TypeError("Invalid task input");
|
|
24811
25940
|
if (!safeId2(value.projectId) || !safeId2(value.taskId) || !safeId2(value.ownerMembershipId) || !["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"].includes(value.status)) throw new TypeError("Invalid task input");
|
|
24812
25941
|
if (typeof value.updatedAt !== "string") throw new TypeError("Invalid task timestamp");
|
|
@@ -24831,7 +25960,7 @@ function safeErrorCode(error) {
|
|
|
24831
25960
|
}
|
|
24832
25961
|
|
|
24833
25962
|
// src/services/collaboration/ProjectRoomTaskEventSubscriber.ts
|
|
24834
|
-
var
|
|
25963
|
+
var import_protocols27 = require("@axiom-lattice/protocols");
|
|
24835
25964
|
var ProjectRoomTaskEventSubscriber = class {
|
|
24836
25965
|
/** Creates a bounded Project task lifecycle observer. */
|
|
24837
25966
|
constructor(deps) {
|
|
@@ -24941,7 +26070,7 @@ var ProjectRoomTaskEventSubscriber = class {
|
|
|
24941
26070
|
}
|
|
24942
26071
|
}
|
|
24943
26072
|
parse(value) {
|
|
24944
|
-
const row = (0,
|
|
26073
|
+
const row = (0, import_protocols27.snapshotExactRecord)(value, ["tenantId", "workspaceId", "projectId", "taskId", "status", "action", "eventKey", "updatedAt"], ["threadId"]);
|
|
24945
26074
|
const actions = ["in_progress", "interrupted", "failed", "completed", "cancelled", "reassigned"];
|
|
24946
26075
|
const statuses = ["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"];
|
|
24947
26076
|
if (!row || !actions.includes(String(row.action)) || !statuses.includes(String(row.status)) || !["tenantId", "workspaceId", "projectId", "taskId", "eventKey", "updatedAt"].every((key) => typeof row[key] === "string" && row[key].length > 0) || !Number.isFinite(Date.parse(row.updatedAt)) || row.threadId !== void 0 && typeof row.threadId !== "string") return void 0;
|
|
@@ -25017,7 +26146,7 @@ function createProjectRoomRealtimeRuntime(deps) {
|
|
|
25017
26146
|
broker,
|
|
25018
26147
|
publisher,
|
|
25019
26148
|
subscriber,
|
|
25020
|
-
subscribe: (listener) =>
|
|
26149
|
+
subscribe: (listener) => import_core50.taskLifecycleNotifier.subscribe(listener),
|
|
25021
26150
|
log: deps.log,
|
|
25022
26151
|
shutdownTimeoutMs: deps.shutdownTimeoutMs
|
|
25023
26152
|
});
|
|
@@ -25137,7 +26266,7 @@ function createRoomPushNotifier(input) {
|
|
|
25137
26266
|
}
|
|
25138
26267
|
|
|
25139
26268
|
// src/services/collaboration/WorkspaceRoomEventsService.ts
|
|
25140
|
-
var
|
|
26269
|
+
var import_protocols28 = require("@axiom-lattice/protocols");
|
|
25141
26270
|
|
|
25142
26271
|
// src/services/collaboration/ProjectRoomSseWriter.ts
|
|
25143
26272
|
var DEFAULT_MAX_FRAMES = 256;
|
|
@@ -25385,7 +26514,7 @@ var WorkspaceRoomEventsService = class {
|
|
|
25385
26514
|
const clear = (timer) => {
|
|
25386
26515
|
if (timer !== void 0) clearTimeout(timer);
|
|
25387
26516
|
};
|
|
25388
|
-
const userScope = (0,
|
|
26517
|
+
const userScope = (0, import_protocols28.projectRoomUserEventScope)(actor3.tenantId, actor3.userId);
|
|
25389
26518
|
const userScopeKey = JSON.stringify([userScope.tenantId, userScope.roomId, userScope.projectId]);
|
|
25390
26519
|
const roomScopeKey = (scope) => JSON.stringify([scope.tenantId, scope.roomId, scope.projectId]);
|
|
25391
26520
|
const serializeScoped = (scope, event) => scope === null ? `id: ${event.id}
|
|
@@ -25495,7 +26624,7 @@ data: ${JSON.stringify({ ...event, scope })}
|
|
|
25495
26624
|
accessTimer = setTimeout(() => {
|
|
25496
26625
|
accessTimer = void 0;
|
|
25497
26626
|
if (closed) return;
|
|
25498
|
-
const projectIds = [...subscriptions.values()].map((entry) => entry.scope.projectId).filter((projectId) => projectId !==
|
|
26627
|
+
const projectIds = [...subscriptions.values()].map((entry) => entry.scope.projectId).filter((projectId) => projectId !== import_protocols28.PROJECT_ROOM_USER_CHANNEL_PROJECT);
|
|
25499
26628
|
Promise.all(projectIds.map((projectId) => Promise.resolve().then(() => this.deps.requireReadAccess({ tenantId: actor3.tenantId, userId: actor3.userId, projectId })))).then(() => {
|
|
25500
26629
|
if (!closed) scheduleAccessCheck();
|
|
25501
26630
|
}, () => {
|
|
@@ -25538,7 +26667,7 @@ function createWorkspaceMemberRoomLister(deps) {
|
|
|
25538
26667
|
}
|
|
25539
26668
|
|
|
25540
26669
|
// src/routes/workspace-room-events.ts
|
|
25541
|
-
var
|
|
26670
|
+
var import_protocols29 = require("@axiom-lattice/protocols");
|
|
25542
26671
|
function registerWorkspaceRoomEventRoutes(app2, deps) {
|
|
25543
26672
|
app2.get("/api/workspaces/:workspaceId/room-events", async (request, reply) => {
|
|
25544
26673
|
try {
|
|
@@ -25566,7 +26695,7 @@ function registerWorkspaceRoomEventRoutes(app2, deps) {
|
|
|
25566
26695
|
});
|
|
25567
26696
|
}
|
|
25568
26697
|
function exactParams(params) {
|
|
25569
|
-
const value = (0,
|
|
26698
|
+
const value = (0, import_protocols29.snapshotExactRecord)(params, ["workspaceId"]);
|
|
25570
26699
|
if (!value || typeof value.workspaceId !== "string" || !value.workspaceId.trim()) throw new InvalidRealtimeRequestError();
|
|
25571
26700
|
return { workspaceId: value.workspaceId };
|
|
25572
26701
|
}
|
|
@@ -25694,7 +26823,7 @@ function snapshotDataRecord(value) {
|
|
|
25694
26823
|
for (const key of Reflect.ownKeys(descriptors)) {
|
|
25695
26824
|
if (typeof key !== "string") return void 0;
|
|
25696
26825
|
const descriptor = descriptors[key];
|
|
25697
|
-
const data = (0,
|
|
26826
|
+
const data = (0, import_protocols30.descriptorDataValue)(descriptor);
|
|
25698
26827
|
if (!data) return void 0;
|
|
25699
26828
|
result[key] = data.value;
|
|
25700
26829
|
}
|
|
@@ -25709,7 +26838,7 @@ function isPublicRoomAddress(message) {
|
|
|
25709
26838
|
}
|
|
25710
26839
|
async function validateReassignmentThread(threads, input) {
|
|
25711
26840
|
const thread = snapshotThread(await threads.getThreadById(input.tenantId, input.newThreadId));
|
|
25712
|
-
const metadata = (0,
|
|
26841
|
+
const metadata = (0, import_protocols30.snapshotExactRecord)(thread?.metadata, [
|
|
25713
26842
|
"source",
|
|
25714
26843
|
"kind",
|
|
25715
26844
|
"workspaceId",
|
|
@@ -25755,35 +26884,35 @@ function hookErrorMessage(error) {
|
|
|
25755
26884
|
function wrapReassignmentThreadError(error) {
|
|
25756
26885
|
if (error instanceof ProjectTaskThreadError) {
|
|
25757
26886
|
if (error.code === "PROJECT_TASK_THREAD_SCOPE_INVALID") {
|
|
25758
|
-
return new
|
|
26887
|
+
return new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
|
|
25759
26888
|
hint: "Project task, room, or roster scope changed; re-read the task and retry."
|
|
25760
26889
|
});
|
|
25761
26890
|
}
|
|
25762
26891
|
if (error.code === "PROJECT_TASK_THREAD_OWNER_INVALID") {
|
|
25763
|
-
return new
|
|
26892
|
+
return new import_core51.ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
|
|
25764
26893
|
hint: "The target already owns this task; re-read before dispatching."
|
|
25765
26894
|
});
|
|
25766
26895
|
}
|
|
25767
|
-
return new
|
|
26896
|
+
return new import_core51.ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
|
|
25768
26897
|
hint: "Task Thread changed concurrently; re-read the task and retry."
|
|
25769
26898
|
});
|
|
25770
26899
|
}
|
|
25771
|
-
return new
|
|
26900
|
+
return new import_core51.ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
|
|
25772
26901
|
hint: "Re-read the task and retry the dispatch."
|
|
25773
26902
|
});
|
|
25774
26903
|
}
|
|
25775
26904
|
function wrapReassignmentAuthorizationError(error) {
|
|
25776
|
-
if (error instanceof
|
|
26905
|
+
if (error instanceof import_core51.ProjectTaskOwnerPolicyError) {
|
|
25777
26906
|
if (error.code === "PROJECT_TASK_CALLER_INACTIVE") {
|
|
25778
|
-
return new
|
|
26907
|
+
return new import_core51.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
|
|
25779
26908
|
hint: "Dispatch from an active Project Room member bot."
|
|
25780
26909
|
});
|
|
25781
26910
|
}
|
|
25782
|
-
return new
|
|
26911
|
+
return new import_core51.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
|
|
25783
26912
|
hint: "Dispatch to an active bot in this Project's roster."
|
|
25784
26913
|
});
|
|
25785
26914
|
}
|
|
25786
|
-
return new
|
|
26915
|
+
return new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
|
|
25787
26916
|
hint: "Re-read the task and roster, then retry the dispatch."
|
|
25788
26917
|
});
|
|
25789
26918
|
}
|
|
@@ -25812,43 +26941,43 @@ async function transferGovernedDispatchOwner(task, options, deps) {
|
|
|
25812
26941
|
const agentId = options?.agentId;
|
|
25813
26942
|
if (!agentId || agentId === task.ownerId) return void 0;
|
|
25814
26943
|
if (!task.workspaceId || !task.projectId) {
|
|
25815
|
-
throw new
|
|
26944
|
+
throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is not governed by a Project lifecycle.", {
|
|
25816
26945
|
hint: "Dispatch governed tasks from a trusted Project run; ordinary tasks execute immediately."
|
|
25817
26946
|
});
|
|
25818
26947
|
}
|
|
25819
26948
|
return deps.mutex.runExclusive({ tenantId: task.tenantId, projectId: task.projectId }, async () => {
|
|
25820
26949
|
const current = await deps.tasks.getById(task.tenantId, task.id);
|
|
25821
26950
|
if (!current || !isProjectLifecycleTask(current) || current.workspaceId !== task.workspaceId || current.projectId !== task.projectId) {
|
|
25822
|
-
throw new
|
|
26951
|
+
throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is no longer a Project lifecycle task.", {
|
|
25823
26952
|
hint: "Re-read the task and confirm it still belongs to this Project before dispatching."
|
|
25824
26953
|
});
|
|
25825
26954
|
}
|
|
25826
26955
|
if (current.ownerType !== "agent") {
|
|
25827
|
-
throw new
|
|
26956
|
+
throw new import_core51.ManagedTaskStartError("AGENT_TASK_REQUIRED", "Project task owner must be an Agent.", {
|
|
25828
26957
|
hint: "Assign the task to an Agent before dispatching."
|
|
25829
26958
|
});
|
|
25830
26959
|
}
|
|
25831
26960
|
if (current.status !== "pending") {
|
|
25832
|
-
throw new
|
|
26961
|
+
throw new import_core51.ManagedTaskStartError("TASK_NOT_DISPATCHABLE", "Only a `pending` Project task can be dispatched.", {
|
|
25833
26962
|
hint: `This task is '${current.status}'. Resolve it or create a new task before dispatching.`
|
|
25834
26963
|
});
|
|
25835
26964
|
}
|
|
25836
26965
|
if (agentId === current.ownerId) return current;
|
|
25837
26966
|
const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
|
|
25838
26967
|
if (!room || room.tenantId !== current.tenantId || room.workspaceId !== current.workspaceId || room.projectId !== current.projectId || room.type !== "main") {
|
|
25839
|
-
throw new
|
|
26968
|
+
throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Project main room is unavailable.", {
|
|
25840
26969
|
hint: "Open or repair the Project Room before dispatching."
|
|
25841
26970
|
});
|
|
25842
26971
|
}
|
|
25843
26972
|
const target = await deps.botMemberships.findByAssistant(current.tenantId, current.projectId, agentId);
|
|
25844
26973
|
if (!target || target.status !== "active" || target.tenantId !== current.tenantId || target.workspaceId !== current.workspaceId || target.projectId !== current.projectId || target.assistantId !== agentId || target.roomId !== room.id) {
|
|
25845
|
-
throw new
|
|
26974
|
+
throw new import_core51.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", "Target Project task owner is not active in the exact roster.", {
|
|
25846
26975
|
hint: "Dispatch to an active bot in this Project's roster."
|
|
25847
26976
|
});
|
|
25848
26977
|
}
|
|
25849
26978
|
const caller = options?.callerMembershipId ? await deps.botMemberships.findById(current.tenantId, options.callerMembershipId) : null;
|
|
25850
26979
|
if (!caller || !options?.callerAssistantId || caller.id !== options.callerMembershipId || caller.assistantId !== options.callerAssistantId || caller.status !== "active" || caller.tenantId !== current.tenantId || caller.workspaceId !== current.workspaceId || caller.projectId !== current.projectId || caller.roomId !== room.id) {
|
|
25851
|
-
throw new
|
|
26980
|
+
throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", "Current Project task caller is not active in the exact roster.", {
|
|
25852
26981
|
hint: "Dispatch from an active Project Room member bot."
|
|
25853
26982
|
});
|
|
25854
26983
|
}
|
|
@@ -25883,7 +27012,7 @@ async function transferGovernedDispatchOwner(task, options, deps) {
|
|
|
25883
27012
|
actor: `agent:${caller.assistantId}`
|
|
25884
27013
|
});
|
|
25885
27014
|
if (!result.success) {
|
|
25886
|
-
throw new
|
|
27015
|
+
throw new import_core51.ManagedTaskStartError(result.code, result.error, {
|
|
25887
27016
|
hint: result.hint ?? reassignmentFailureHint(result.code),
|
|
25888
27017
|
...result.retryable !== void 0 ? { retryable: result.retryable } : {}
|
|
25889
27018
|
});
|
|
@@ -25892,19 +27021,19 @@ async function transferGovernedDispatchOwner(task, options, deps) {
|
|
|
25892
27021
|
});
|
|
25893
27022
|
}
|
|
25894
27023
|
var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
25895
|
-
const webAppStore = (0,
|
|
25896
|
-
const assistantStore = (0,
|
|
25897
|
-
const projectStore = (0,
|
|
25898
|
-
const projectMembershipStore = (0,
|
|
25899
|
-
const userTenantLinkStore = (0,
|
|
25900
|
-
const projectRoomStore = (0,
|
|
25901
|
-
const projectBotMembershipStore = (0,
|
|
25902
|
-
const projectRoomMessageStore = (0,
|
|
25903
|
-
const projectRoomReadStateStore = (0,
|
|
25904
|
-
const userPushSubscriptionStore = (0,
|
|
25905
|
-
const threadStore = (0,
|
|
25906
|
-
const taskStore = (0,
|
|
25907
|
-
const taskWorkItemStore = (0,
|
|
27024
|
+
const webAppStore = (0, import_core51.getStoreLattice)("default", "agentWebApp").store;
|
|
27025
|
+
const assistantStore = (0, import_core51.getStoreLattice)("default", "assistant").store;
|
|
27026
|
+
const projectStore = (0, import_core51.getStoreLattice)("default", "project").store;
|
|
27027
|
+
const projectMembershipStore = (0, import_core51.getStoreLattice)("default", "projectMembership").store;
|
|
27028
|
+
const userTenantLinkStore = (0, import_core51.getStoreLattice)("default", "userTenantLink").store;
|
|
27029
|
+
const projectRoomStore = (0, import_core51.getStoreLattice)("default", "projectRoom").store;
|
|
27030
|
+
const projectBotMembershipStore = (0, import_core51.getStoreLattice)("default", "projectBotMembership").store;
|
|
27031
|
+
const projectRoomMessageStore = (0, import_core51.getStoreLattice)("default", "projectRoomMessage").store;
|
|
27032
|
+
const projectRoomReadStateStore = (0, import_core51.getStoreLattice)("default", "projectRoomReadState").store;
|
|
27033
|
+
const userPushSubscriptionStore = (0, import_core51.getStoreLattice)("default", "userPushSubscription").store;
|
|
27034
|
+
const threadStore = (0, import_core51.getStoreLattice)("default", "thread").store;
|
|
27035
|
+
const taskStore = (0, import_core51.getStoreLattice)("default", "task").store;
|
|
27036
|
+
const taskWorkItemStore = (0, import_core51.getStoreLattice)("default", "taskWorkItem").store;
|
|
25908
27037
|
const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
|
|
25909
27038
|
const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
|
|
25910
27039
|
const realtimeMode = resolveProjectRoomRealtimeMode();
|
|
@@ -25970,7 +27099,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
25970
27099
|
updateAssistant: assistantStore.updateAssistant.bind(assistantStore)
|
|
25971
27100
|
});
|
|
25972
27101
|
const resolveCoordinatorMentionName = async (tenantId) => {
|
|
25973
|
-
const tenant2 = await (0,
|
|
27102
|
+
const tenant2 = await (0, import_core51.getStoreLattice)("default", "tenant").store.getTenantById(tenantId);
|
|
25974
27103
|
const value = tenant2?.metadata?.coordinatorMentionName;
|
|
25975
27104
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
25976
27105
|
};
|
|
@@ -25997,7 +27126,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
25997
27126
|
return membership !== null && membership.projectId === projectId ? membership : null;
|
|
25998
27127
|
},
|
|
25999
27128
|
getThreadStatus: async ({ tenantId, assistantId, threadId, workspaceId, projectId }) => {
|
|
26000
|
-
const agent =
|
|
27129
|
+
const agent = import_core51.agentInstanceManager.getAgent({
|
|
26001
27130
|
tenant_id: tenantId,
|
|
26002
27131
|
assistant_id: assistantId,
|
|
26003
27132
|
thread_id: threadId,
|
|
@@ -26006,9 +27135,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26006
27135
|
});
|
|
26007
27136
|
return String(await agent.getRunStatus());
|
|
26008
27137
|
},
|
|
26009
|
-
dispatcher: (0,
|
|
27138
|
+
dispatcher: (0, import_core51.createTrustedAgentMessageDispatcher)((scope) => import_core51.agentInstanceManager.getAgent(scope))
|
|
26010
27139
|
});
|
|
26011
|
-
const membershipService = new ProjectMembershipService({ membershipStore: projectMembershipStore, userTenantLinkStore, accessService, projectMutationMutex:
|
|
27140
|
+
const membershipService = new ProjectMembershipService({ membershipStore: projectMembershipStore, userTenantLinkStore, accessService, projectMutationMutex: import_core51.projectMutationMutex, roomStore: projectRoomStore, realtime, onRealtimeFailure: realtime && channelDeps?.warn ? ({ code, membershipId }) => channelDeps.warn?.("project-room:membership-realtime-failed", { code, membershipId }) : void 0 });
|
|
26012
27141
|
const rosterService = new ProjectRosterService({
|
|
26013
27142
|
accessService,
|
|
26014
27143
|
roomService,
|
|
@@ -26019,7 +27148,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26019
27148
|
assistantStore,
|
|
26020
27149
|
taskStore,
|
|
26021
27150
|
bindingLifecycle,
|
|
26022
|
-
projectMutationMutex:
|
|
27151
|
+
projectMutationMutex: import_core51.projectMutationMutex,
|
|
26023
27152
|
realtime,
|
|
26024
27153
|
onRealtimeFailure: realtime && channelDeps?.warn ? ({ code, membershipId }) => channelDeps.warn?.("project-room:roster-realtime-failed", { code, membershipId }) : void 0
|
|
26025
27154
|
});
|
|
@@ -26074,10 +27203,10 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26074
27203
|
});
|
|
26075
27204
|
}
|
|
26076
27205
|
if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
|
|
26077
|
-
const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory:
|
|
26078
|
-
const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? (0,
|
|
26079
|
-
const reassignmentAuthorizer = (0,
|
|
26080
|
-
new
|
|
27206
|
+
const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: import_node_crypto26.randomUUID });
|
|
27207
|
+
const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? (0, import_core51.createTaskLifecycleService)({ taskStore, workItemStore: taskWorkItemStore });
|
|
27208
|
+
const reassignmentAuthorizer = (0, import_core51.createProjectTaskReassignmentAuthorizer)(
|
|
27209
|
+
new import_core51.ProjectTaskOwnerPolicy(projectBotMembershipStore),
|
|
26081
27210
|
(input) => validateReassignmentThread(threadStore, input)
|
|
26082
27211
|
);
|
|
26083
27212
|
const taskCommandService = new TaskCommandService({
|
|
@@ -26086,7 +27215,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26086
27215
|
lifecycle: taskLifecycle,
|
|
26087
27216
|
execution: projectTaskComposition?.lease.runtime.execution,
|
|
26088
27217
|
threads: taskThreadService,
|
|
26089
|
-
agents: { getAgent: (params) =>
|
|
27218
|
+
agents: { getAgent: (params) => import_core51.agentInstanceManager.getAgent(params) },
|
|
26090
27219
|
taskScope: (task) => resolveCommandTaskScope(task, {
|
|
26091
27220
|
rooms: projectRoomStore,
|
|
26092
27221
|
bots: projectBotMembershipStore,
|
|
@@ -26124,7 +27253,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26124
27253
|
readStateService,
|
|
26125
27254
|
interruptService: roomInterruptService,
|
|
26126
27255
|
userDirectory: {
|
|
26127
|
-
getUserById: (id3) => (0,
|
|
27256
|
+
getUserById: (id3) => (0, import_core51.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
|
|
26128
27257
|
}
|
|
26129
27258
|
});
|
|
26130
27259
|
registerProjectRosterRoutes(app2, { service: rosterService });
|
|
@@ -26135,7 +27264,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26135
27264
|
createProject: projectStore.createProject.bind(projectStore)
|
|
26136
27265
|
},
|
|
26137
27266
|
workspaceStore: {
|
|
26138
|
-
getWorkspaceById: (tenantId, id3) => (0,
|
|
27267
|
+
getWorkspaceById: (tenantId, id3) => (0, import_core51.getStoreLattice)("default", "workspace").store.getWorkspaceById(tenantId, id3)
|
|
26139
27268
|
},
|
|
26140
27269
|
membershipStore: {
|
|
26141
27270
|
list: projectMembershipStore.list.bind(projectMembershipStore),
|
|
@@ -26158,7 +27287,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26158
27287
|
getUsersByTenant: userTenantLinkStore.getUsersByTenant.bind(userTenantLinkStore)
|
|
26159
27288
|
},
|
|
26160
27289
|
userStore: {
|
|
26161
|
-
getUserById: (id3) => (0,
|
|
27290
|
+
getUserById: (id3) => (0, import_core51.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
|
|
26162
27291
|
},
|
|
26163
27292
|
assistantStore: {
|
|
26164
27293
|
getAssistantById: assistantStore.getAssistantById.bind(assistantStore),
|
|
@@ -26180,18 +27309,28 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26180
27309
|
}
|
|
26181
27310
|
});
|
|
26182
27311
|
registerCapabilityBundleRoutes(app2, new CapabilityBundleController({
|
|
26183
|
-
bundleStore: (0,
|
|
27312
|
+
bundleStore: (0, import_core51.getStoreLattice)("default", "capabilityBundle").store,
|
|
26184
27313
|
projectStore
|
|
26185
27314
|
}, { authRequired: process.env.AUTH_REQUIRED === "true" }));
|
|
27315
|
+
registerModelProviderRoutes(app2, new ModelProviderController({
|
|
27316
|
+
providerStore: (0, import_core51.getStoreLattice)("default", "modelProvider").store,
|
|
27317
|
+
userTenantLinkStore,
|
|
27318
|
+
registry: {
|
|
27319
|
+
register: (tenantId, modelKey2, config) => import_core51.modelLatticeManager.registerLatticeWithTenant(tenantId, modelKey2, config),
|
|
27320
|
+
has: (tenantId, modelKey2) => import_core51.modelLatticeManager.hasLatticeWithTenant(tenantId, modelKey2),
|
|
27321
|
+
remove: (tenantId, modelKey2) => import_core51.modelLatticeManager.removeLatticeWithTenant(tenantId, modelKey2),
|
|
27322
|
+
listRegistered: (tenantId) => import_core51.modelLatticeManager.getLatticeKeysByTenant(tenantId)
|
|
27323
|
+
}
|
|
27324
|
+
}, { authRequired: process.env.AUTH_REQUIRED === "true" }));
|
|
26186
27325
|
registerAgentWebAppRoutes(app2, {
|
|
26187
27326
|
service: new AgentWebAppService({
|
|
26188
27327
|
webAppStore,
|
|
26189
27328
|
assistantStore,
|
|
26190
27329
|
projectStore,
|
|
26191
|
-
modelRegistry: { has: (key) =>
|
|
27330
|
+
modelRegistry: { has: (key) => import_core51.modelLatticeManager.hasLattice(key) },
|
|
26192
27331
|
configuredAgentResolver: createConfiguredAgentResolver({
|
|
26193
|
-
hydrate: (tenantId) =>
|
|
26194
|
-
has: (tenantId, assistantId) =>
|
|
27332
|
+
hydrate: (tenantId) => import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
|
|
27333
|
+
has: (tenantId, assistantId) => import_core51.agentLatticeManager.hasAgentLatticeWithTenant(tenantId, assistantId)
|
|
26195
27334
|
})
|
|
26196
27335
|
})
|
|
26197
27336
|
});
|
|
@@ -26228,28 +27367,28 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26228
27367
|
},
|
|
26229
27368
|
tasks: taskStore,
|
|
26230
27369
|
workItems: taskWorkItemStore,
|
|
26231
|
-
projectMutationMutex:
|
|
27370
|
+
projectMutationMutex: import_core51.projectMutationMutex,
|
|
26232
27371
|
...projectTaskComposition ? { commands: taskCommandService } : {}
|
|
26233
27372
|
}),
|
|
26234
27373
|
service: new AgentWebAppRuntimeService({
|
|
26235
27374
|
webAppStore,
|
|
26236
|
-
threadStore: (0,
|
|
27375
|
+
threadStore: (0, import_core51.getStoreLattice)("default", "thread").store,
|
|
26237
27376
|
projectStore,
|
|
26238
27377
|
assistantResolver: {
|
|
26239
27378
|
resolve: async (tenantId, assistantId) => {
|
|
26240
27379
|
const stored = await assistantStore.getAssistantById(tenantId, assistantId);
|
|
26241
27380
|
if (stored) return { id: stored.id, name: stored.name, description: stored.description };
|
|
26242
|
-
await
|
|
26243
|
-
const config =
|
|
27381
|
+
await import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId);
|
|
27382
|
+
const config = import_core51.agentLatticeManager.getAgentConfigWithTenant(tenantId, assistantId);
|
|
26244
27383
|
return config ? { id: assistantId, name: config.name, description: config.description } : null;
|
|
26245
27384
|
}
|
|
26246
27385
|
},
|
|
26247
27386
|
modelRegistry: {
|
|
26248
|
-
has: (key) =>
|
|
27387
|
+
has: (key) => import_core51.modelLatticeManager.hasLattice(key),
|
|
26249
27388
|
label: (key) => key
|
|
26250
27389
|
},
|
|
26251
27390
|
messageReader: {
|
|
26252
|
-
getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) =>
|
|
27391
|
+
getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core51.agentInstanceManager.getAgent({
|
|
26253
27392
|
tenant_id: tenantId,
|
|
26254
27393
|
assistant_id: assistantId,
|
|
26255
27394
|
thread_id: threadId,
|
|
@@ -26258,7 +27397,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26258
27397
|
}).getCurrentMessages()
|
|
26259
27398
|
},
|
|
26260
27399
|
agentResolver: {
|
|
26261
|
-
getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) =>
|
|
27400
|
+
getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core51.agentInstanceManager.getAgent({
|
|
26262
27401
|
tenant_id: tenantId,
|
|
26263
27402
|
assistant_id: assistantId,
|
|
26264
27403
|
thread_id: threadId,
|
|
@@ -26276,8 +27415,8 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26276
27415
|
if (webApp.tenantId !== preview.tenantId) return false;
|
|
26277
27416
|
const assistant = await assistantStore.getAssistantById(preview.tenantId, webApp.assistantId);
|
|
26278
27417
|
if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === preview.userId;
|
|
26279
|
-
await
|
|
26280
|
-
return
|
|
27418
|
+
await import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(preview.tenantId);
|
|
27419
|
+
return import_core51.agentLatticeManager.hasAgentLatticeWithTenant(preview.tenantId, webApp.assistantId);
|
|
26281
27420
|
}
|
|
26282
27421
|
})
|
|
26283
27422
|
});
|
|
@@ -26334,14 +27473,14 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26334
27473
|
app2.post("/api/personal-assistant", createPersonalAssistant);
|
|
26335
27474
|
app2.get("/api/personal-assistant", getPersonalAssistant);
|
|
26336
27475
|
app2.delete("/api/personal-assistant", deletePersonalAssistant);
|
|
26337
|
-
|
|
27476
|
+
import_core51.executionAuthorityRegistry.register({
|
|
26338
27477
|
id: "project-lifecycle",
|
|
26339
27478
|
matches: isProjectLifecycleTask,
|
|
26340
27479
|
code: "PROJECT_TASK_EXECUTION_MANAGED",
|
|
26341
27480
|
message: "Project tasks are executed by the durable Project queue runtime",
|
|
26342
27481
|
onManagedStart: async (task, options2) => {
|
|
26343
27482
|
if (!projectTaskComposition && options2?.agentId && options2.agentId !== task.ownerId) {
|
|
26344
|
-
throw new
|
|
27483
|
+
throw new import_core51.ManagedTaskStartError(
|
|
26345
27484
|
"PROJECT_TASK_RUNTIME_UNAVAILABLE",
|
|
26346
27485
|
"Project task ownership transfer requires the Project task runtime.",
|
|
26347
27486
|
{ hint: "Project task execution is disabled; enable the Project task runtime or dispatch to the current owner." }
|
|
@@ -26354,11 +27493,11 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26354
27493
|
threads: taskThreadService,
|
|
26355
27494
|
reassignmentAuthorizer,
|
|
26356
27495
|
lifecycle: taskLifecycle,
|
|
26357
|
-
mutex:
|
|
27496
|
+
mutex: import_core51.projectMutationMutex
|
|
26358
27497
|
}) : void 0;
|
|
26359
27498
|
const admitted = transferred ?? task;
|
|
26360
27499
|
if (!admitted.workspaceId || !admitted.projectId) return;
|
|
26361
|
-
void
|
|
27500
|
+
void import_core51.projectTaskAdmissionNotifier.publish({
|
|
26362
27501
|
tenantId: admitted.tenantId,
|
|
26363
27502
|
workspaceId: admitted.workspaceId,
|
|
26364
27503
|
projectId: admitted.projectId,
|
|
@@ -26372,7 +27511,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26372
27511
|
access: accessService,
|
|
26373
27512
|
tasks: taskStore,
|
|
26374
27513
|
workItems: taskWorkItemStore,
|
|
26375
|
-
projectMutationMutex:
|
|
27514
|
+
projectMutationMutex: import_core51.projectMutationMutex,
|
|
26376
27515
|
commands: taskCommandService,
|
|
26377
27516
|
readableProjects: async ({ tenantId, userId, workspaceId }) => {
|
|
26378
27517
|
const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
|
|
@@ -26640,9 +27779,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
26640
27779
|
function tryCreateProjectTaskRuntime(input) {
|
|
26641
27780
|
let projectWorkItems;
|
|
26642
27781
|
try {
|
|
26643
|
-
projectWorkItems = (0,
|
|
27782
|
+
projectWorkItems = (0, import_protocols30.requireProjectTaskWorkItemStore)(input.taskWorkItemStore);
|
|
26644
27783
|
} catch (error) {
|
|
26645
|
-
if (error instanceof
|
|
27784
|
+
if (error instanceof import_protocols30.ProjectTaskStoreUnsupportedError) {
|
|
26646
27785
|
input.log?.warn("project-task:runtime-unavailable", {
|
|
26647
27786
|
code: error.code,
|
|
26648
27787
|
missingMethods: error.missingMethods
|
|
@@ -26654,9 +27793,9 @@ function tryCreateProjectTaskRuntime(input) {
|
|
|
26654
27793
|
let stores;
|
|
26655
27794
|
try {
|
|
26656
27795
|
stores = {
|
|
26657
|
-
tenants: (0,
|
|
26658
|
-
workspaces: (0,
|
|
26659
|
-
queue: (0,
|
|
27796
|
+
tenants: (0, import_core51.getStoreLattice)("default", "tenant").store,
|
|
27797
|
+
workspaces: (0, import_core51.getStoreLattice)("default", "workspace").store,
|
|
27798
|
+
queue: (0, import_core51.getStoreLattice)("default", "threadMessageQueue").store
|
|
26660
27799
|
};
|
|
26661
27800
|
const missing = missingRuntimeMethod(input, stores);
|
|
26662
27801
|
if (missing) return { reason: missing };
|
|
@@ -26687,8 +27826,8 @@ function tryCreateProjectTaskRuntime(input) {
|
|
|
26687
27826
|
function createRuntime(input, stores, log2) {
|
|
26688
27827
|
const controller = new AbortController();
|
|
26689
27828
|
const mutex = new ProjectTaskExecutionMutex();
|
|
26690
|
-
const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory:
|
|
26691
|
-
const lifecycle = (0,
|
|
27829
|
+
const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: import_node_crypto26.randomUUID });
|
|
27830
|
+
const lifecycle = (0, import_core51.createTaskLifecycleService)({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
|
|
26692
27831
|
const projection = new RoomProjectionService({
|
|
26693
27832
|
messages: input.projectRoomMessageStore,
|
|
26694
27833
|
events: input.taskWorkItemStore,
|
|
@@ -26696,9 +27835,9 @@ function createRuntime(input, stores, log2) {
|
|
|
26696
27835
|
warnings: input.taskWorkItemStore,
|
|
26697
27836
|
log: log2,
|
|
26698
27837
|
realtime: input.realtime,
|
|
26699
|
-
idFactory:
|
|
27838
|
+
idFactory: import_node_crypto26.randomUUID
|
|
26700
27839
|
});
|
|
26701
|
-
const agentFor = (task, thread) =>
|
|
27840
|
+
const agentFor = (task, thread) => import_core51.agentInstanceManager.getAgent({
|
|
26702
27841
|
tenant_id: task.tenantId,
|
|
26703
27842
|
assistant_id: task.ownerId,
|
|
26704
27843
|
thread_id: thread.id,
|
|
@@ -26712,7 +27851,7 @@ function createRuntime(input, stores, log2) {
|
|
|
26712
27851
|
bots: input.projectBotMembershipStore,
|
|
26713
27852
|
threads,
|
|
26714
27853
|
lifecycle,
|
|
26715
|
-
dispatcher: (0,
|
|
27854
|
+
dispatcher: (0, import_core51.createTrustedAgentMessageDispatcher)((scope) => import_core51.agentInstanceManager.getAgent(scope)),
|
|
26716
27855
|
queue: stores.queue,
|
|
26717
27856
|
agentFor
|
|
26718
27857
|
}, {
|
|
@@ -26739,8 +27878,8 @@ function createRuntime(input, stores, log2) {
|
|
|
26739
27878
|
const taskLifecycleListener = ((event) => {
|
|
26740
27879
|
void observer.listener(event);
|
|
26741
27880
|
});
|
|
26742
|
-
const unsubscribe =
|
|
26743
|
-
const unsubscribeAdmission =
|
|
27881
|
+
const unsubscribe = import_core51.taskLifecycleNotifier.subscribe(taskLifecycleListener);
|
|
27882
|
+
const unsubscribeAdmission = import_core51.projectTaskAdmissionNotifier.subscribe((event, signal) => {
|
|
26744
27883
|
observer.admit(event, signal);
|
|
26745
27884
|
});
|
|
26746
27885
|
let reconciliationWork;
|
|
@@ -26887,12 +28026,12 @@ function registerResourceRoutes(app2, controller) {
|
|
|
26887
28026
|
}
|
|
26888
28027
|
|
|
26889
28028
|
// src/export_registrations/index.ts
|
|
26890
|
-
var
|
|
28029
|
+
var import_core62 = require("@axiom-lattice/core");
|
|
26891
28030
|
|
|
26892
28031
|
// src/export_registrations/skill.registration.ts
|
|
26893
|
-
var
|
|
28032
|
+
var import_core52 = require("@axiom-lattice/core");
|
|
26894
28033
|
function getStore(context) {
|
|
26895
|
-
return new
|
|
28034
|
+
return new import_core52.SandboxSkillStore({
|
|
26896
28035
|
workspaceId: context?.workspaceId,
|
|
26897
28036
|
projectId: context?.projectId
|
|
26898
28037
|
});
|
|
@@ -26910,7 +28049,7 @@ var skillRegistration = {
|
|
|
26910
28049
|
async listForExport(tenantId, context) {
|
|
26911
28050
|
const store2 = getStore(context);
|
|
26912
28051
|
const skills = await store2.getAllSkills(tenantId, toStoreContext(context));
|
|
26913
|
-
return skills.filter((s) => !(0,
|
|
28052
|
+
return skills.filter((s) => !(0, import_core52.isBuiltInSkill)(s.id)).map((s) => ({
|
|
26914
28053
|
_exportId: "",
|
|
26915
28054
|
data: {
|
|
26916
28055
|
id: s.id,
|
|
@@ -26975,7 +28114,7 @@ var skillRegistration = {
|
|
|
26975
28114
|
const insertions = [];
|
|
26976
28115
|
for (const entity of entities) {
|
|
26977
28116
|
const id3 = entity.data.id;
|
|
26978
|
-
if ((0,
|
|
28117
|
+
if ((0, import_core52.isBuiltInSkill)(id3)) continue;
|
|
26979
28118
|
if (existingIds.has(id3)) {
|
|
26980
28119
|
conflicts.push({
|
|
26981
28120
|
_exportId: entity._exportId,
|
|
@@ -27034,9 +28173,9 @@ var skillRegistration = {
|
|
|
27034
28173
|
};
|
|
27035
28174
|
|
|
27036
28175
|
// src/export_registrations/agent.registration.ts
|
|
27037
|
-
var
|
|
28176
|
+
var import_core53 = require("@axiom-lattice/core");
|
|
27038
28177
|
function getStore2() {
|
|
27039
|
-
return (0,
|
|
28178
|
+
return (0, import_core53.getStoreLattice)("default", "assistant").store;
|
|
27040
28179
|
}
|
|
27041
28180
|
var agentRegistration = {
|
|
27042
28181
|
entityType: "agent",
|
|
@@ -27116,9 +28255,9 @@ var agentRegistration = {
|
|
|
27116
28255
|
};
|
|
27117
28256
|
|
|
27118
28257
|
// src/export_registrations/database-config.registration.ts
|
|
27119
|
-
var
|
|
28258
|
+
var import_core54 = require("@axiom-lattice/core");
|
|
27120
28259
|
function getStore3() {
|
|
27121
|
-
return (0,
|
|
28260
|
+
return (0, import_core54.getStoreLattice)("default", "database").store;
|
|
27122
28261
|
}
|
|
27123
28262
|
var databaseConfigRegistration = {
|
|
27124
28263
|
entityType: "database_config",
|
|
@@ -27198,9 +28337,9 @@ var databaseConfigRegistration = {
|
|
|
27198
28337
|
};
|
|
27199
28338
|
|
|
27200
28339
|
// src/export_registrations/metrics-config.registration.ts
|
|
27201
|
-
var
|
|
28340
|
+
var import_core55 = require("@axiom-lattice/core");
|
|
27202
28341
|
function getStore4() {
|
|
27203
|
-
return (0,
|
|
28342
|
+
return (0, import_core55.getStoreLattice)("default", "metrics").store;
|
|
27204
28343
|
}
|
|
27205
28344
|
var metricsConfigRegistration = {
|
|
27206
28345
|
entityType: "metrics_config",
|
|
@@ -27280,9 +28419,9 @@ var metricsConfigRegistration = {
|
|
|
27280
28419
|
};
|
|
27281
28420
|
|
|
27282
28421
|
// src/export_registrations/mcp-config.registration.ts
|
|
27283
|
-
var
|
|
28422
|
+
var import_core56 = require("@axiom-lattice/core");
|
|
27284
28423
|
function getStore5() {
|
|
27285
|
-
return (0,
|
|
28424
|
+
return (0, import_core56.getStoreLattice)("default", "mcp").store;
|
|
27286
28425
|
}
|
|
27287
28426
|
var mcpConfigRegistration = {
|
|
27288
28427
|
entityType: "mcp_config",
|
|
@@ -27365,9 +28504,9 @@ var mcpConfigRegistration = {
|
|
|
27365
28504
|
};
|
|
27366
28505
|
|
|
27367
28506
|
// src/export_registrations/connection.registration.ts
|
|
27368
|
-
var
|
|
28507
|
+
var import_core57 = require("@axiom-lattice/core");
|
|
27369
28508
|
function getStore6() {
|
|
27370
|
-
return (0,
|
|
28509
|
+
return (0, import_core57.getStoreLattice)("default", "connection").store;
|
|
27371
28510
|
}
|
|
27372
28511
|
async function listAllConnections(tenantId) {
|
|
27373
28512
|
const store2 = getStore6();
|
|
@@ -27449,9 +28588,9 @@ var connectionRegistration = {
|
|
|
27449
28588
|
};
|
|
27450
28589
|
|
|
27451
28590
|
// src/export_registrations/collection.registration.ts
|
|
27452
|
-
var
|
|
28591
|
+
var import_core58 = require("@axiom-lattice/core");
|
|
27453
28592
|
function getStore7() {
|
|
27454
|
-
return (0,
|
|
28593
|
+
return (0, import_core58.getStoreLattice)("default", "collection").store;
|
|
27455
28594
|
}
|
|
27456
28595
|
var collectionRegistration = {
|
|
27457
28596
|
entityType: "collection",
|
|
@@ -27522,7 +28661,7 @@ var collectionRegistration = {
|
|
|
27522
28661
|
};
|
|
27523
28662
|
|
|
27524
28663
|
// src/export_registrations/channel-installation.registration.ts
|
|
27525
|
-
var
|
|
28664
|
+
var import_core59 = require("@axiom-lattice/core");
|
|
27526
28665
|
var import_crypto11 = require("crypto");
|
|
27527
28666
|
var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
27528
28667
|
var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
|
|
@@ -27556,7 +28695,7 @@ function validateImportData(data) {
|
|
|
27556
28695
|
};
|
|
27557
28696
|
}
|
|
27558
28697
|
function getStore8() {
|
|
27559
|
-
return (0,
|
|
28698
|
+
return (0, import_core59.getStoreLattice)("default", "channelInstallation").store;
|
|
27560
28699
|
}
|
|
27561
28700
|
function getOverwriteError(tenantId, incomingChannel, existing) {
|
|
27562
28701
|
if (!existing) return "Channel installation overwrite target must exist";
|
|
@@ -27690,9 +28829,9 @@ var channelInstallationRegistration = {
|
|
|
27690
28829
|
};
|
|
27691
28830
|
|
|
27692
28831
|
// src/export_registrations/menu.registration.ts
|
|
27693
|
-
var
|
|
28832
|
+
var import_core60 = require("@axiom-lattice/core");
|
|
27694
28833
|
function getStore9() {
|
|
27695
|
-
return (0,
|
|
28834
|
+
return (0, import_core60.getStoreLattice)("default", "menu").store;
|
|
27696
28835
|
}
|
|
27697
28836
|
var menuRegistration = {
|
|
27698
28837
|
entityType: "menu",
|
|
@@ -27770,9 +28909,9 @@ var menuRegistration = {
|
|
|
27770
28909
|
};
|
|
27771
28910
|
|
|
27772
28911
|
// src/export_registrations/eval.registration.ts
|
|
27773
|
-
var
|
|
28912
|
+
var import_core61 = require("@axiom-lattice/core");
|
|
27774
28913
|
function getStore10() {
|
|
27775
|
-
return (0,
|
|
28914
|
+
return (0, import_core61.getStoreLattice)("default", "eval").store;
|
|
27776
28915
|
}
|
|
27777
28916
|
var evalRegistration = {
|
|
27778
28917
|
entityType: "eval",
|
|
@@ -28098,7 +29237,7 @@ var evalRegistration = {
|
|
|
28098
29237
|
|
|
28099
29238
|
// src/export_registrations/index.ts
|
|
28100
29239
|
function registerAllBuiltinEntities() {
|
|
28101
|
-
const registry =
|
|
29240
|
+
const registry = import_core62.ExportableEntityRegistry.getInstance();
|
|
28102
29241
|
registry.register(skillRegistration);
|
|
28103
29242
|
registry.register(agentRegistration);
|
|
28104
29243
|
registry.register(databaseConfigRegistration);
|
|
@@ -28197,13 +29336,13 @@ function createRateLimitMiddleware(maxRequests = 10, windowMs = 60 * 1e3, maxEnt
|
|
|
28197
29336
|
|
|
28198
29337
|
// src/router/middlewares/auditLogger.ts
|
|
28199
29338
|
init_Logger();
|
|
28200
|
-
var
|
|
29339
|
+
var logger5 = new Logger({ serviceName: "lattice/gateway/audit" });
|
|
28201
29340
|
function createAuditLoggerMiddleware() {
|
|
28202
29341
|
return async (ctx, next) => {
|
|
28203
29342
|
const start2 = Date.now();
|
|
28204
29343
|
try {
|
|
28205
29344
|
await next();
|
|
28206
|
-
|
|
29345
|
+
logger5.info("message routed", {
|
|
28207
29346
|
event: "message:routed",
|
|
28208
29347
|
channel: ctx.inboundMessage.channel,
|
|
28209
29348
|
senderId: ctx.inboundMessage.sender.id,
|
|
@@ -28213,7 +29352,7 @@ function createAuditLoggerMiddleware() {
|
|
|
28213
29352
|
status: "success"
|
|
28214
29353
|
});
|
|
28215
29354
|
} catch (error) {
|
|
28216
|
-
|
|
29355
|
+
logger5.error(
|
|
28217
29356
|
error instanceof Error ? error.message : String(error),
|
|
28218
29357
|
{
|
|
28219
29358
|
event: "message:error",
|
|
@@ -28229,7 +29368,7 @@ function createAuditLoggerMiddleware() {
|
|
|
28229
29368
|
}
|
|
28230
29369
|
|
|
28231
29370
|
// src/index.ts
|
|
28232
|
-
var
|
|
29371
|
+
var import_core81 = require("@axiom-lattice/core");
|
|
28233
29372
|
|
|
28234
29373
|
// src/swagger.ts
|
|
28235
29374
|
var import_swagger = __toESM(require("@fastify/swagger"));
|
|
@@ -28294,7 +29433,8 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
28294
29433
|
};
|
|
28295
29434
|
|
|
28296
29435
|
// src/services/agent_task_consumer.ts
|
|
28297
|
-
var
|
|
29436
|
+
var import_node_crypto27 = require("crypto");
|
|
29437
|
+
var import_core63 = require("@axiom-lattice/core");
|
|
28298
29438
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
28299
29439
|
const {
|
|
28300
29440
|
assistant_id,
|
|
@@ -28312,51 +29452,97 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
28312
29452
|
console.log(
|
|
28313
29453
|
`\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
|
|
28314
29454
|
);
|
|
28315
|
-
const agent =
|
|
28316
|
-
await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core62.QueueMode.STEER);
|
|
29455
|
+
const agent = import_core63.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
|
|
28317
29456
|
if (callback_event) {
|
|
28318
|
-
|
|
28319
|
-
|
|
28320
|
-
|
|
28321
|
-
|
|
28322
|
-
|
|
28323
|
-
|
|
28324
|
-
|
|
28325
|
-
|
|
28326
|
-
|
|
28327
|
-
|
|
28328
|
-
|
|
28329
|
-
|
|
28330
|
-
|
|
28331
|
-
|
|
28332
|
-
|
|
28333
|
-
|
|
28334
|
-
|
|
28335
|
-
|
|
28336
|
-
|
|
28337
|
-
|
|
28338
|
-
|
|
28339
|
-
|
|
29457
|
+
const callbackEvent = callback_event;
|
|
29458
|
+
const messageId = typeof input?.id === "string" && input.id !== "" ? input.id : (0, import_node_crypto27.randomUUID)();
|
|
29459
|
+
let settled = false;
|
|
29460
|
+
const subscriptions = [];
|
|
29461
|
+
const dispose = () => {
|
|
29462
|
+
for (const [eventName, handler] of subscriptions) {
|
|
29463
|
+
agent.unsubscribe(eventName, handler);
|
|
29464
|
+
}
|
|
29465
|
+
};
|
|
29466
|
+
const settle = (publish) => {
|
|
29467
|
+
if (settled) return;
|
|
29468
|
+
settled = true;
|
|
29469
|
+
dispose();
|
|
29470
|
+
publish();
|
|
29471
|
+
};
|
|
29472
|
+
const belongsToMessage = (evt) => evt?.messageId === messageId;
|
|
29473
|
+
const onCompleted = (evt) => {
|
|
29474
|
+
if (!belongsToMessage(evt)) return;
|
|
29475
|
+
settle(() => {
|
|
29476
|
+
import_core63.eventBus.publish(callbackEvent, {
|
|
29477
|
+
success: true,
|
|
29478
|
+
state: evt.state
|
|
29479
|
+
});
|
|
29480
|
+
if (main_thread_id && main_tenant_id) {
|
|
29481
|
+
try {
|
|
29482
|
+
const mainAgent = import_core63.agentInstanceManager.getAgent({
|
|
29483
|
+
assistant_id: main_assistant_id ?? assistant_id,
|
|
29484
|
+
thread_id: main_thread_id,
|
|
29485
|
+
tenant_id: main_tenant_id,
|
|
29486
|
+
workspace_id: runConfig?.workspaceId,
|
|
29487
|
+
project_id: runConfig?.projectId
|
|
29488
|
+
});
|
|
29489
|
+
if (mainAgent) {
|
|
29490
|
+
const messages = evt.state?.values?.messages;
|
|
29491
|
+
const lastAIMessage = messages?.filter((m) => m.type === "ai" || m.getType?.() === "ai").pop();
|
|
29492
|
+
const summary = lastAIMessage?.content?.substring(0, 500) ?? "(no output)";
|
|
29493
|
+
mainAgent.addMessage(
|
|
29494
|
+
{
|
|
29495
|
+
input: {
|
|
29496
|
+
message: `[Async task completed]
|
|
28340
29497
|
task_id: ${thread_id}
|
|
28341
29498
|
${summary}`
|
|
29499
|
+
}
|
|
28342
29500
|
}
|
|
28343
|
-
|
|
28344
|
-
|
|
28345
|
-
|
|
28346
|
-
|
|
28347
|
-
|
|
29501
|
+
).catch((err) => {
|
|
29502
|
+
console.error("Failed to notify main thread:", err);
|
|
29503
|
+
});
|
|
29504
|
+
mainAgent.updateAsyncTaskStatus(thread_id, "completed");
|
|
29505
|
+
}
|
|
29506
|
+
} catch (err) {
|
|
29507
|
+
console.error("Failed to notify main thread:", err);
|
|
28348
29508
|
}
|
|
28349
|
-
} catch (err) {
|
|
28350
|
-
console.error("Failed to notify main thread:", err);
|
|
28351
29509
|
}
|
|
28352
|
-
}
|
|
28353
|
-
});
|
|
28354
|
-
agent.subscribeOnce("message:interrupted", (evt) => {
|
|
28355
|
-
import_core62.eventBus.publish(callback_event, {
|
|
28356
|
-
success: true,
|
|
28357
|
-
state: evt.state
|
|
28358
29510
|
});
|
|
28359
|
-
}
|
|
29511
|
+
};
|
|
29512
|
+
const onInterrupted = (evt) => {
|
|
29513
|
+
if (!belongsToMessage(evt)) return;
|
|
29514
|
+
settle(() => {
|
|
29515
|
+
import_core63.eventBus.publish(callbackEvent, {
|
|
29516
|
+
success: true,
|
|
29517
|
+
state: evt.state
|
|
29518
|
+
});
|
|
29519
|
+
});
|
|
29520
|
+
};
|
|
29521
|
+
const onFailed = (evt) => {
|
|
29522
|
+
if (!belongsToMessage(evt)) return;
|
|
29523
|
+
settle(() => {
|
|
29524
|
+
import_core63.eventBus.publish(callbackEvent, {
|
|
29525
|
+
success: false,
|
|
29526
|
+
error: evt?.error instanceof Error ? evt.error.message : typeof evt?.error === "string" ? evt.error : "Agent execution failed"
|
|
29527
|
+
});
|
|
29528
|
+
});
|
|
29529
|
+
};
|
|
29530
|
+
subscriptions.push(
|
|
29531
|
+
["message:completed", onCompleted],
|
|
29532
|
+
["message:interrupted", onInterrupted],
|
|
29533
|
+
["message:failed", onFailed]
|
|
29534
|
+
);
|
|
29535
|
+
agent.subscribe("message:completed", onCompleted);
|
|
29536
|
+
agent.subscribe("message:interrupted", onInterrupted);
|
|
29537
|
+
agent.subscribe("message:failed", onFailed);
|
|
29538
|
+
try {
|
|
29539
|
+
await agent.addMessage({ input: { ...input, id: messageId }, command, custom_run_config: runConfig }, import_core63.QueueMode.STEER);
|
|
29540
|
+
} catch (error) {
|
|
29541
|
+
dispose();
|
|
29542
|
+
throw error;
|
|
29543
|
+
}
|
|
29544
|
+
} else {
|
|
29545
|
+
await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core63.QueueMode.STEER);
|
|
28360
29546
|
}
|
|
28361
29547
|
return true;
|
|
28362
29548
|
} catch (error) {
|
|
@@ -28375,7 +29561,7 @@ ${summary}`
|
|
|
28375
29561
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
28376
29562
|
}
|
|
28377
29563
|
if (callback_event) {
|
|
28378
|
-
|
|
29564
|
+
import_core63.eventBus.publish(callback_event, {
|
|
28379
29565
|
success: false,
|
|
28380
29566
|
error: error instanceof Error ? error.message : String(error)
|
|
28381
29567
|
});
|
|
@@ -28412,7 +29598,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
28412
29598
|
* 初始化事件监听和队列轮询
|
|
28413
29599
|
*/
|
|
28414
29600
|
initialize() {
|
|
28415
|
-
|
|
29601
|
+
import_core63.eventBus.subscribe(import_core63.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
|
|
28416
29602
|
this.startPollingQueue();
|
|
28417
29603
|
console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
|
|
28418
29604
|
}
|
|
@@ -28531,7 +29717,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
28531
29717
|
handleAgentTask(taskRequest).catch((error) => {
|
|
28532
29718
|
console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
|
|
28533
29719
|
if (taskRequest.callback_event) {
|
|
28534
|
-
|
|
29720
|
+
import_core63.eventBus.publish(taskRequest.callback_event, {
|
|
28535
29721
|
success: false,
|
|
28536
29722
|
error: error instanceof Error ? error.message : String(error)
|
|
28537
29723
|
});
|
|
@@ -28546,14 +29732,14 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
|
|
|
28546
29732
|
var AgentTaskConsumer = _AgentTaskConsumer;
|
|
28547
29733
|
|
|
28548
29734
|
// src/index.ts
|
|
28549
|
-
var
|
|
29735
|
+
var import_core82 = require("@axiom-lattice/core");
|
|
28550
29736
|
|
|
28551
29737
|
// src/channels/ChannelInfrastructure.ts
|
|
28552
|
-
var
|
|
29738
|
+
var import_core65 = require("@axiom-lattice/core");
|
|
28553
29739
|
|
|
28554
29740
|
// src/router/MessageRouter.ts
|
|
28555
|
-
var
|
|
28556
|
-
var
|
|
29741
|
+
var import_core64 = require("@axiom-lattice/core");
|
|
29742
|
+
var import_protocols31 = require("@axiom-lattice/protocols");
|
|
28557
29743
|
var import_crypto12 = require("crypto");
|
|
28558
29744
|
var BindingNotFoundError = class extends Error {
|
|
28559
29745
|
constructor(message) {
|
|
@@ -28588,10 +29774,10 @@ function clarifyText(clarify) {
|
|
|
28588
29774
|
return ["```clarify", JSON.stringify({ questions: clarify.questions }), "```"].join("\n");
|
|
28589
29775
|
}
|
|
28590
29776
|
function exactDataValues(value, required, optional = []) {
|
|
28591
|
-
return (0,
|
|
29777
|
+
return (0, import_protocols31.snapshotExactRecord)(value, required, optional);
|
|
28592
29778
|
}
|
|
28593
29779
|
function uniqueNonemptyStrings(value) {
|
|
28594
|
-
const snapshot = (0,
|
|
29780
|
+
const snapshot = (0, import_protocols31.snapshotExactArray)(value);
|
|
28595
29781
|
if (!snapshot || snapshot.length === 0 || snapshot.some((entry) => typeof entry !== "string" || entry.length === 0)) return void 0;
|
|
28596
29782
|
const values = snapshot;
|
|
28597
29783
|
return new Set(values).size === values.length ? values : void 0;
|
|
@@ -28838,7 +30024,7 @@ var MessageRouter = class {
|
|
|
28838
30024
|
channel: message.channel,
|
|
28839
30025
|
adapterChannel: adapter.channel
|
|
28840
30026
|
}, "Thread resolved by adapter strategy");
|
|
28841
|
-
const threadStore = (0,
|
|
30027
|
+
const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
|
|
28842
30028
|
try {
|
|
28843
30029
|
await threadStore.createThread(
|
|
28844
30030
|
tenantId,
|
|
@@ -28875,7 +30061,7 @@ var MessageRouter = class {
|
|
|
28875
30061
|
}
|
|
28876
30062
|
}
|
|
28877
30063
|
if (!threadId) {
|
|
28878
|
-
const threadStore = (0,
|
|
30064
|
+
const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
|
|
28879
30065
|
const newThreadId = (0, import_crypto12.randomUUID)();
|
|
28880
30066
|
console.log({
|
|
28881
30067
|
event: "dispatch:thread:create",
|
|
@@ -28915,7 +30101,7 @@ var MessageRouter = class {
|
|
|
28915
30101
|
senderId: message.sender.id,
|
|
28916
30102
|
contentLength: message.content.text.length
|
|
28917
30103
|
}, "Dispatching to agent");
|
|
28918
|
-
const agent =
|
|
30104
|
+
const agent = import_core64.agentInstanceManager.getAgent({
|
|
28919
30105
|
tenant_id: tenantId,
|
|
28920
30106
|
assistant_id: agentId,
|
|
28921
30107
|
thread_id: threadId,
|
|
@@ -29102,9 +30288,9 @@ var MessageRouter = class {
|
|
|
29102
30288
|
};
|
|
29103
30289
|
|
|
29104
30290
|
// src/channels/room/RoomChannelAdapter.ts
|
|
29105
|
-
var
|
|
29106
|
-
var
|
|
29107
|
-
var roomConfigSchema =
|
|
30291
|
+
var import_zod17 = require("zod");
|
|
30292
|
+
var import_protocols32 = require("@axiom-lattice/protocols");
|
|
30293
|
+
var roomConfigSchema = import_zod17.z.object({}).strict();
|
|
29108
30294
|
function createRoomChannelAdapter(writer) {
|
|
29109
30295
|
return {
|
|
29110
30296
|
channel: "room",
|
|
@@ -29164,7 +30350,7 @@ function exactStrings(value, keys2) {
|
|
|
29164
30350
|
function exactValues2(value, required, optional = []) {
|
|
29165
30351
|
try {
|
|
29166
30352
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
29167
|
-
return (0,
|
|
30353
|
+
return (0, import_protocols32.snapshotExactRecord)(value, required, optional);
|
|
29168
30354
|
} catch {
|
|
29169
30355
|
return void 0;
|
|
29170
30356
|
}
|
|
@@ -29188,7 +30374,7 @@ function createChannelInfrastructure(deps) {
|
|
|
29188
30374
|
onMessageCommitted: deps.onMessageCommitted ?? (deps.realtime ? (message) => deps.realtime?.messageCreated(message) : void 0),
|
|
29189
30375
|
onCallbackFailure: (event) => deps.warn("project-room:message-commit-callback-failed", event)
|
|
29190
30376
|
};
|
|
29191
|
-
deps.adapterRegistry.register(createRoomChannelAdapter(new
|
|
30377
|
+
deps.adapterRegistry.register(createRoomChannelAdapter(new import_core65.RoomAgentMessageService(writerDeps)));
|
|
29192
30378
|
} catch (error) {
|
|
29193
30379
|
deps.warn("Room channel unavailable", { error: error instanceof Error ? error.message : String(error) });
|
|
29194
30380
|
}
|
|
@@ -29209,25 +30395,25 @@ async function prepareProjectTaskRuntime(runtime, log2) {
|
|
|
29209
30395
|
}
|
|
29210
30396
|
|
|
29211
30397
|
// src/index.ts
|
|
29212
|
-
var
|
|
30398
|
+
var import_protocols39 = require("@axiom-lattice/protocols");
|
|
29213
30399
|
process.on("unhandledRejection", (reason, promise) => {
|
|
29214
30400
|
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
|
|
29215
30401
|
});
|
|
29216
30402
|
var DEFAULT_LOGGER_CONFIG = {
|
|
29217
30403
|
name: "default",
|
|
29218
30404
|
description: "Default logger for lattice-gateway service",
|
|
29219
|
-
type:
|
|
30405
|
+
type: import_protocols39.LoggerType.PINO,
|
|
29220
30406
|
serviceName: "lattice/gateway",
|
|
29221
30407
|
loggerName: "lattice/gateway"
|
|
29222
30408
|
};
|
|
29223
30409
|
var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
|
|
29224
|
-
var
|
|
30410
|
+
var logger7 = loggerLattice.client;
|
|
29225
30411
|
function initializeLogger(config) {
|
|
29226
|
-
if (
|
|
29227
|
-
|
|
30412
|
+
if (import_core82.loggerLatticeManager.hasLattice("default")) {
|
|
30413
|
+
import_core82.loggerLatticeManager.removeLattice("default");
|
|
29228
30414
|
}
|
|
29229
|
-
(0,
|
|
29230
|
-
return (0,
|
|
30415
|
+
(0, import_core82.registerLoggerLattice)("default", config);
|
|
30416
|
+
return (0, import_core82.getLoggerLattice)("default");
|
|
29231
30417
|
}
|
|
29232
30418
|
var app = (0, import_fastify.default)({
|
|
29233
30419
|
logger: false,
|
|
@@ -29308,7 +30494,7 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
29308
30494
|
"x-request-id": getHeaderValue(request.headers["x-request-id"]),
|
|
29309
30495
|
"x-user-id": getHeaderValue(request.headers["x-user-id"])
|
|
29310
30496
|
};
|
|
29311
|
-
|
|
30497
|
+
logger7.error(
|
|
29312
30498
|
`\u8BF7\u6C42\u9519\u8BEF: ${request.method} ${request.url} error:${error.message}`,
|
|
29313
30499
|
{
|
|
29314
30500
|
...context,
|
|
@@ -29324,7 +30510,7 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
29324
30510
|
});
|
|
29325
30511
|
function getConfiguredSandboxProvider() {
|
|
29326
30512
|
const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
|
|
29327
|
-
return (0,
|
|
30513
|
+
return (0, import_core82.createSandboxProvider)({
|
|
29328
30514
|
type: sandboxProviderType,
|
|
29329
30515
|
remoteBaseURL: process.env.SANDBOX_BASE_URL,
|
|
29330
30516
|
microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
|
|
@@ -29340,33 +30526,33 @@ function getConfiguredSandboxProvider() {
|
|
|
29340
30526
|
}
|
|
29341
30527
|
async function restoreMcpConnections() {
|
|
29342
30528
|
try {
|
|
29343
|
-
const storeLattice = (0,
|
|
30529
|
+
const storeLattice = (0, import_core82.getStoreLattice)("default", "mcp");
|
|
29344
30530
|
const store2 = storeLattice.store;
|
|
29345
30531
|
if (!store2) {
|
|
29346
|
-
|
|
30532
|
+
logger7.info("MCP store not configured, skipping connection restoration");
|
|
29347
30533
|
return;
|
|
29348
30534
|
}
|
|
29349
30535
|
const configs = (await store2.getAllConfigsWithoutTenant()).filter(
|
|
29350
30536
|
(c2) => c2.status === "connected"
|
|
29351
30537
|
);
|
|
29352
30538
|
if (configs.length === 0) return;
|
|
29353
|
-
|
|
30539
|
+
logger7.info(`Restoring ${configs.length} MCP server connections...`);
|
|
29354
30540
|
for (const config of configs) {
|
|
29355
30541
|
try {
|
|
29356
|
-
const connection = (0,
|
|
29357
|
-
|
|
30542
|
+
const connection = (0, import_core82.convertToConnection)(config.config);
|
|
30543
|
+
import_core82.mcpManager.registerServer(config.tenantId, config.key, connection, {
|
|
29358
30544
|
timeoutMs: config.config.timeout,
|
|
29359
30545
|
maxAttempts: config.config.retryAttempts,
|
|
29360
30546
|
selectedTools: config.selectedTools
|
|
29361
30547
|
});
|
|
29362
30548
|
} catch (err) {
|
|
29363
|
-
|
|
30549
|
+
logger7.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
|
|
29364
30550
|
}
|
|
29365
30551
|
}
|
|
29366
|
-
await
|
|
30552
|
+
await import_core82.mcpManager.connectAll();
|
|
29367
30553
|
const results = await Promise.allSettled(
|
|
29368
30554
|
configs.map(
|
|
29369
|
-
(config) =>
|
|
30555
|
+
(config) => import_core82.mcpManager.registerToolsToToolLattice(
|
|
29370
30556
|
config.tenantId,
|
|
29371
30557
|
config.key,
|
|
29372
30558
|
config.selectedTools
|
|
@@ -29376,15 +30562,15 @@ async function restoreMcpConnections() {
|
|
|
29376
30562
|
results.forEach((result, index) => {
|
|
29377
30563
|
if (result.status === "rejected") {
|
|
29378
30564
|
const config = configs[index];
|
|
29379
|
-
|
|
30565
|
+
logger7.warn(
|
|
29380
30566
|
`Failed to register tools for MCP server "${config.tenantId}/${config.key}"`,
|
|
29381
30567
|
{ error: result.reason }
|
|
29382
30568
|
);
|
|
29383
30569
|
}
|
|
29384
30570
|
});
|
|
29385
|
-
|
|
30571
|
+
logger7.info(`MCP connection restoration complete: ${configs.length} servers`);
|
|
29386
30572
|
} catch (err) {
|
|
29387
|
-
|
|
30573
|
+
logger7.error("MCP connection restoration failed", { error: err });
|
|
29388
30574
|
}
|
|
29389
30575
|
}
|
|
29390
30576
|
var start = async (config) => {
|
|
@@ -29397,17 +30583,17 @@ var start = async (config) => {
|
|
|
29397
30583
|
file: config.loggerConfig.file || DEFAULT_LOGGER_CONFIG.file
|
|
29398
30584
|
};
|
|
29399
30585
|
loggerLattice = initializeLogger(loggerConfig);
|
|
29400
|
-
|
|
30586
|
+
logger7 = loggerLattice.client;
|
|
29401
30587
|
}
|
|
29402
30588
|
app.decorate("loggerLattice", loggerLattice);
|
|
29403
30589
|
const projectRoomRealtimeMode = resolveProjectRoomRealtimeMode();
|
|
29404
30590
|
let projectRoomRealtimeRuntime;
|
|
29405
30591
|
if (projectRoomRealtimeMode === "single-gateway") {
|
|
29406
30592
|
projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
|
|
29407
|
-
tasks: (0,
|
|
29408
|
-
rooms: (0,
|
|
29409
|
-
memberships: (0,
|
|
29410
|
-
log: { warn: (message, context) =>
|
|
30593
|
+
tasks: (0, import_core82.getStoreLattice)("default", "task").store,
|
|
30594
|
+
rooms: (0, import_core82.getStoreLattice)("default", "projectRoom").store,
|
|
30595
|
+
memberships: (0, import_core82.getStoreLattice)("default", "projectBotMembership").store,
|
|
30596
|
+
log: { warn: (message, context) => logger7.warn(message, context) }
|
|
29411
30597
|
});
|
|
29412
30598
|
}
|
|
29413
30599
|
let channelDeps;
|
|
@@ -29420,18 +30606,18 @@ var start = async (config) => {
|
|
|
29420
30606
|
const infrastructure = createChannelInfrastructure({
|
|
29421
30607
|
adapterRegistry,
|
|
29422
30608
|
getStore: ((kind) => getStore11("default", kind).store),
|
|
29423
|
-
trustedDispatcher: (0,
|
|
30609
|
+
trustedDispatcher: (0, import_core82.createTrustedAgentMessageDispatcher)((scope) => import_core82.agentInstanceManager.getAgent(scope)),
|
|
29424
30610
|
middlewares: [
|
|
29425
30611
|
createDeduplicationMiddleware(),
|
|
29426
30612
|
createRateLimitMiddleware(),
|
|
29427
30613
|
createAuditLoggerMiddleware()
|
|
29428
30614
|
],
|
|
29429
|
-
warn: (message, context) =>
|
|
30615
|
+
warn: (message, context) => logger7.warn(message, context),
|
|
29430
30616
|
realtime: projectRoomRealtimeRuntime?.publisher
|
|
29431
30617
|
});
|
|
29432
30618
|
const bindingStore = infrastructure.bindingRegistry;
|
|
29433
30619
|
const installationStore = infrastructure.installationStore;
|
|
29434
|
-
(0,
|
|
30620
|
+
(0, import_core81.setBindingRegistry)(bindingStore);
|
|
29435
30621
|
const router = infrastructure.router;
|
|
29436
30622
|
app.addHook("onClose", async () => router.dispose());
|
|
29437
30623
|
channelDeps = {
|
|
@@ -29440,18 +30626,18 @@ var start = async (config) => {
|
|
|
29440
30626
|
installationStore,
|
|
29441
30627
|
bindingRegistry: bindingStore,
|
|
29442
30628
|
realtime: projectRoomRealtimeRuntime?.publisher,
|
|
29443
|
-
warn: (message, context) =>
|
|
30629
|
+
warn: (message, context) => logger7.warn(message, context)
|
|
29444
30630
|
};
|
|
29445
30631
|
} catch (err) {
|
|
29446
|
-
|
|
30632
|
+
logger7.warn("Channel infrastructure unavailable", {
|
|
29447
30633
|
error: err instanceof Error ? err.message : String(err)
|
|
29448
30634
|
});
|
|
29449
30635
|
}
|
|
29450
|
-
(0,
|
|
30636
|
+
(0, import_core81.setEvalRunService)(evalRunner);
|
|
29451
30637
|
try {
|
|
29452
|
-
const menuStore = (0,
|
|
29453
|
-
(0,
|
|
29454
|
-
|
|
30638
|
+
const menuStore = (0, import_core82.getStoreLattice)("default", "menu").store;
|
|
30639
|
+
(0, import_core81.setMenuRegistry)(menuStore);
|
|
30640
|
+
logger7.info("Menu registry initialized");
|
|
29455
30641
|
} catch {
|
|
29456
30642
|
}
|
|
29457
30643
|
registerAllBuiltinEntities();
|
|
@@ -29460,17 +30646,17 @@ var start = async (config) => {
|
|
|
29460
30646
|
}
|
|
29461
30647
|
const routes = registerLatticeRoutes(app, channelDeps, {
|
|
29462
30648
|
projectTaskEnabled: process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false",
|
|
29463
|
-
projectTaskLog: { warn: (message, context) =>
|
|
30649
|
+
projectTaskLog: { warn: (message, context) => logger7.warn(message, context) },
|
|
29464
30650
|
projectRoomRealtimeRuntime
|
|
29465
30651
|
});
|
|
29466
30652
|
try {
|
|
29467
30653
|
const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await Promise.resolve().then(() => (init_A2AAuthService(), A2AAuthService_exports));
|
|
29468
30654
|
const { registerA2AStandardRoutes: registerA2AStandardRoutes2 } = await Promise.resolve().then(() => (init_a2a_standard(), a2a_standard_exports));
|
|
29469
30655
|
const a2a = await Promise.resolve().then(() => (init_a2a(), a2a_exports));
|
|
29470
|
-
const a2aKeyStore = (0,
|
|
29471
|
-
const assistantStore = (0,
|
|
29472
|
-
const projectStore = (0,
|
|
29473
|
-
const taskStore = (0,
|
|
30656
|
+
const a2aKeyStore = (0, import_core82.getStoreLattice)("default", "a2aApiKey").store;
|
|
30657
|
+
const assistantStore = (0, import_core82.getStoreLattice)("default", "assistant").store;
|
|
30658
|
+
const projectStore = (0, import_core82.getStoreLattice)("default", "project").store;
|
|
30659
|
+
const taskStore = (0, import_core82.getStoreLattice)("default", "task").store;
|
|
29474
30660
|
const authService = new A2AAuthService2({
|
|
29475
30661
|
keyStore: a2aKeyStore,
|
|
29476
30662
|
assistantStore,
|
|
@@ -29488,9 +30674,9 @@ var start = async (config) => {
|
|
|
29488
30674
|
getProjectStore: () => projectStore
|
|
29489
30675
|
});
|
|
29490
30676
|
a2a.registerA2ARoutes(app);
|
|
29491
|
-
|
|
30677
|
+
logger7.info("A2A standard routes registered");
|
|
29492
30678
|
} catch (err) {
|
|
29493
|
-
|
|
30679
|
+
logger7.warn("A2A standard routes unavailable", {
|
|
29494
30680
|
error: err instanceof Error ? err.message : String(err)
|
|
29495
30681
|
});
|
|
29496
30682
|
}
|
|
@@ -29509,8 +30695,8 @@ var start = async (config) => {
|
|
|
29509
30695
|
const { CollectionService: CollectionService2 } = await Promise.resolve().then(() => (init_collection_service(), collection_service_exports));
|
|
29510
30696
|
openLattice2.register(createKbProvider2(new CollectionService2()));
|
|
29511
30697
|
openLattice2.register(createOpenSandboxProvider2());
|
|
29512
|
-
const threadStore = (0,
|
|
29513
|
-
const assistantStore = (0,
|
|
30698
|
+
const threadStore = (0, import_core82.getStoreLattice)("default", "thread").store;
|
|
30699
|
+
const assistantStore = (0, import_core82.getStoreLattice)("default", "assistant").store;
|
|
29514
30700
|
openLattice2.register(
|
|
29515
30701
|
createAgentProvider2({
|
|
29516
30702
|
threadStore,
|
|
@@ -29527,30 +30713,30 @@ var start = async (config) => {
|
|
|
29527
30713
|
registerOpenFilesRoutes2(app);
|
|
29528
30714
|
registerOpenAuditRoutes2(app);
|
|
29529
30715
|
registerOpenExposureRoutes2(app);
|
|
29530
|
-
|
|
30716
|
+
logger7.info("Open API routes registered (kb + sandbox providers, session exchange)");
|
|
29531
30717
|
} catch (err) {
|
|
29532
|
-
|
|
30718
|
+
logger7.warn("Open API routes unavailable", {
|
|
29533
30719
|
error: err instanceof Error ? err.message : String(err)
|
|
29534
30720
|
});
|
|
29535
30721
|
}
|
|
29536
|
-
if (!
|
|
29537
|
-
|
|
29538
|
-
|
|
30722
|
+
if (!import_core82.sandboxLatticeManager.hasLattice("default")) {
|
|
30723
|
+
import_core82.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
|
|
30724
|
+
logger7.info("Registered sandbox manager from env configuration");
|
|
29539
30725
|
}
|
|
29540
30726
|
try {
|
|
29541
30727
|
const { ResourceController: ResourceController2 } = await Promise.resolve().then(() => (init_resources(), resources_exports));
|
|
29542
|
-
const sharedResourceStore = (0,
|
|
29543
|
-
const cache = new
|
|
30728
|
+
const sharedResourceStore = (0, import_core82.getStoreLattice)("default", "sharedResource").store;
|
|
30729
|
+
const cache = new import_core82.TokenCache();
|
|
29544
30730
|
const resourceController = new ResourceController2({
|
|
29545
30731
|
store: sharedResourceStore,
|
|
29546
|
-
sandboxManager:
|
|
30732
|
+
sandboxManager: import_core82.sandboxLatticeManager,
|
|
29547
30733
|
cache,
|
|
29548
|
-
logger:
|
|
30734
|
+
logger: logger7
|
|
29549
30735
|
});
|
|
29550
30736
|
registerResourceRoutes(app, resourceController);
|
|
29551
|
-
|
|
30737
|
+
logger7.info("Resource share routes registered");
|
|
29552
30738
|
} catch (err) {
|
|
29553
|
-
|
|
30739
|
+
logger7.warn("Resource share infrastructure unavailable", {
|
|
29554
30740
|
error: err instanceof Error ? err.message : String(err)
|
|
29555
30741
|
});
|
|
29556
30742
|
}
|
|
@@ -29562,17 +30748,17 @@ var start = async (config) => {
|
|
|
29562
30748
|
{ deps: { router: channelDeps.router } }
|
|
29563
30749
|
);
|
|
29564
30750
|
} catch (err) {
|
|
29565
|
-
|
|
30751
|
+
logger7.error("Failed to start channel connections", {
|
|
29566
30752
|
error: err instanceof Error ? err.message : String(err)
|
|
29567
30753
|
});
|
|
29568
30754
|
}
|
|
29569
30755
|
}
|
|
29570
30756
|
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
29571
30757
|
if (routes.projectTaskRuntime) {
|
|
29572
|
-
await prepareProjectTaskRuntime(routes.projectTaskRuntime,
|
|
30758
|
+
await prepareProjectTaskRuntime(routes.projectTaskRuntime, logger7);
|
|
29573
30759
|
}
|
|
29574
30760
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
29575
|
-
|
|
30761
|
+
logger7.info(`Lattice Gateway is running on port: ${target_port}`);
|
|
29576
30762
|
const queueServiceConfig = config?.queueServiceConfig;
|
|
29577
30763
|
if (queueServiceConfig) {
|
|
29578
30764
|
setQueueServiceType(queueServiceConfig.type);
|
|
@@ -29582,17 +30768,17 @@ var start = async (config) => {
|
|
|
29582
30768
|
}
|
|
29583
30769
|
}
|
|
29584
30770
|
if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
|
|
29585
|
-
|
|
29586
|
-
|
|
30771
|
+
import_core82.agentInstanceManager.restore().then((stats) => {
|
|
30772
|
+
logger7.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
|
|
29587
30773
|
}).catch((error) => {
|
|
29588
|
-
|
|
30774
|
+
logger7.error("Agent recovery failed", { error });
|
|
29589
30775
|
});
|
|
29590
30776
|
}
|
|
29591
30777
|
restoreMcpConnections().catch((error) => {
|
|
29592
|
-
|
|
30778
|
+
logger7.error("MCP connection restoration failed", { error });
|
|
29593
30779
|
});
|
|
29594
30780
|
} catch (err) {
|
|
29595
|
-
|
|
30781
|
+
logger7.error("Server start failed", { error: err });
|
|
29596
30782
|
process.exit(1);
|
|
29597
30783
|
}
|
|
29598
30784
|
};
|