@axiom-lattice/gateway 4.3.2 → 4.4.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/{a2a-standard-ALXWP7V6.mjs → a2a-standard-P7JDL6ZA.mjs} +63 -35
- package/dist/a2a-standard-P7JDL6ZA.mjs.map +1 -0
- package/dist/{registerExposedPlugins-63EUF4VS.mjs → chunk-3XIIQ6EK.mjs} +62 -18
- package/dist/chunk-3XIIQ6EK.mjs.map +1 -0
- package/dist/chunk-526UYVNQ.mjs +157 -0
- package/dist/chunk-526UYVNQ.mjs.map +1 -0
- package/dist/chunk-73QZQQOT.mjs +43 -0
- package/dist/chunk-73QZQQOT.mjs.map +1 -0
- package/dist/{chunk-GF4GGTWJ.mjs → chunk-HVIKCUOF.mjs} +10 -7
- package/dist/chunk-HVIKCUOF.mjs.map +1 -0
- package/dist/embed/embed-page-AgentDetailSideApp-PSRQVFNR.js +1 -0
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-ZNW72EEJ.js → embed-page-AgentGovernanceWorkbench-UPQHJZD7.js} +1 -1
- package/dist/embed/embed-page-MessageGraph-PDNQ67T4.js +6 -0
- package/dist/embed/{embed-page-TasksSideApp-F4XUHPTG.css → embed-page-MessageGraph-UT2TCPSO.css} +1 -1
- package/dist/embed/{embed-page-ReactChart-XS7D26VP.js → embed-page-ReactChart-25KAFLQ3.js} +1 -1
- package/dist/embed/embed-page-ReactChart-MXGF35OQ.css +1 -0
- package/dist/embed/embed-page-SkillFlow-TKBMB5VV.js +1 -0
- package/dist/embed/{embed-page-TasksSideApp-MQTUCJO4.js → embed-page-TasksSideApp-DLH5LRMS.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-2NQ3OOYI.css → embed-page-TasksSideApp-HV6DKQ26.css} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-7T2HRXLG.js → embed-page-TeamWorkspace-2WJJWF25.js} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-DXKGNQ3C.css → embed-page-TeamWorkspace-YQFWXDLI.css} +1 -1
- package/dist/embed/{embed-page-MessageGraph-TYOOATYV.css → embed-page-TopologyRuntimeView-CWOXLVFW.css} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-PU2D7VZ7.js → embed-page-TopologyRuntimeView-WDQRHDMO.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-ST76M33F.js → embed-page-WorkflowAutomationView-BAMJ2WVB.js} +1 -1
- package/dist/embed/embed-page-WorkflowAutomationView-YQFB3CTV.css +1 -0
- package/dist/embed/{embed-page-chunk-T25T2YE6.js → embed-page-chunk-HMTKPZ5B.js} +7 -5
- package/dist/embed/{embed-page-chunk-ZKJ7Z7P3.js → embed-page-chunk-LOXLXZNP.js} +1 -1
- package/dist/embed/{embed-page-chunk-Z4S4OYA4.js → embed-page-chunk-MQDYZS3S.js} +1 -1
- package/dist/embed/embed-page-chunk-OBM5EUWZ.js +6974 -0
- package/dist/embed/{embed-page-chunk-RLP3BC7G.js → embed-page-chunk-SSC7ANWV.js} +2 -2
- package/dist/embed/embed-page-chunk-TL2AYM76.js +2 -0
- package/dist/embed/{embed-page-chunk-LE6OFJ7R.js → embed-page-chunk-XVUA7UHE.js} +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.js +5103 -3988
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1566 -411
- package/dist/index.mjs.map +1 -1
- package/dist/{open-HH7RKGML.mjs → open-AMEMJOFJ.mjs} +99 -14
- package/dist/open-AMEMJOFJ.mjs.map +1 -0
- package/dist/{open-catalog-WYS2AENM.mjs → open-catalog-SZQ435AY.mjs} +34 -3
- package/dist/open-catalog-SZQ435AY.mjs.map +1 -0
- package/dist/{open-exposure-FK4G6O6L.mjs → open-exposure-I44Q4M3T.mjs} +37 -7
- package/dist/open-exposure-I44Q4M3T.mjs.map +1 -0
- package/dist/{open-files-GVRMK5PL.mjs → open-files-EJ6O2SGX.mjs} +4 -4
- package/dist/{open-session-3VN65PRR.mjs → open-session-LBTCU3AE.mjs} +4 -4
- package/dist/{openLattice-GAAGQJST.mjs → openLattice-Q6M5S6CY.mjs} +4 -2
- package/dist/registerExposedPlugins-6GCOE64C.mjs +11 -0
- package/dist/{resources-JFKAPS45.mjs → resources-DXPXAXEZ.mjs} +16 -9
- package/dist/{resources-JFKAPS45.mjs.map → resources-DXPXAXEZ.mjs.map} +1 -1
- package/package.json +11 -8
- package/dist/a2a-standard-ALXWP7V6.mjs.map +0 -1
- package/dist/chunk-GF4GGTWJ.mjs.map +0 -1
- package/dist/chunk-QKPZWWHH.mjs +0 -433
- package/dist/chunk-QKPZWWHH.mjs.map +0 -1
- package/dist/embed/embed-page-AgentDetailSideApp-7PQ2JKG6.js +0 -1
- package/dist/embed/embed-page-MessageGraph-P7DBTZCL.js +0 -6
- package/dist/embed/embed-page-ReactChart-VBM467ER.css +0 -1
- package/dist/embed/embed-page-SkillFlow-HPH37PZH.js +0 -1
- package/dist/embed/embed-page-WorkflowAutomationView-MAYS27YJ.css +0 -1
- package/dist/embed/embed-page-chunk-4ZSGPIBZ.js +0 -1
- package/dist/embed/embed-page-chunk-BRETUPSG.js +0 -4094
- package/dist/embed/embed-page-chunk-I42T7B7V.js +0 -2415
- package/dist/embed/embed-page-chunk-VRV57DFE.js +0 -466
- package/dist/embed/embed-page-chunk-ZXFZANZM.js +0 -1
- package/dist/mcp-configs-WCY5EXCW.mjs +0 -30
- package/dist/open-HH7RKGML.mjs.map +0 -1
- package/dist/open-catalog-WYS2AENM.mjs.map +0 -1
- package/dist/open-exposure-FK4G6O6L.mjs.map +0 -1
- package/dist/registerExposedPlugins-63EUF4VS.mjs.map +0 -1
- /package/dist/{open-files-GVRMK5PL.mjs.map → open-files-EJ6O2SGX.mjs.map} +0 -0
- /package/dist/{open-session-3VN65PRR.mjs.map → open-session-LBTCU3AE.mjs.map} +0 -0
- /package/dist/{mcp-configs-WCY5EXCW.mjs.map → openLattice-Q6M5S6CY.mjs.map} +0 -0
- /package/dist/{openLattice-GAAGQJST.mjs.map → registerExposedPlugins-6GCOE64C.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,10 @@ import {
|
|
|
3
3
|
getFilenameFromPath,
|
|
4
4
|
sendBufferWithRange
|
|
5
5
|
} from "./chunk-EMYRCJOI.mjs";
|
|
6
|
+
import {
|
|
7
|
+
PluginToolScopeError,
|
|
8
|
+
invokePluginTool
|
|
9
|
+
} from "./chunk-73QZQQOT.mjs";
|
|
6
10
|
import {
|
|
7
11
|
buildPreviewTokenScript
|
|
8
12
|
} from "./chunk-2POW27EI.mjs";
|
|
@@ -10,9 +14,6 @@ import "./chunk-WKNZ7GBC.mjs";
|
|
|
10
14
|
import {
|
|
11
15
|
CollectionService
|
|
12
16
|
} from "./chunk-PWHUWFXL.mjs";
|
|
13
|
-
import {
|
|
14
|
-
registerMcpServerConfigRoutes
|
|
15
|
-
} from "./chunk-QKPZWWHH.mjs";
|
|
16
17
|
import {
|
|
17
18
|
Logger,
|
|
18
19
|
getQrCode,
|
|
@@ -41,7 +42,7 @@ import staticPlugin from "@fastify/static";
|
|
|
41
42
|
import path4 from "path";
|
|
42
43
|
|
|
43
44
|
// src/routes/index.ts
|
|
44
|
-
import { randomUUID as
|
|
45
|
+
import { randomUUID as randomUUID23 } from "crypto";
|
|
45
46
|
import {
|
|
46
47
|
descriptorDataValue as descriptorDataValue3,
|
|
47
48
|
snapshotExactRecord as snapshotExactRecord14,
|
|
@@ -2691,7 +2692,15 @@ function serializeSchema(schema) {
|
|
|
2691
2692
|
}
|
|
2692
2693
|
async function getToolConfigs(request, reply) {
|
|
2693
2694
|
try {
|
|
2694
|
-
const
|
|
2695
|
+
const tenantId = request.user?.tenantId || request.headers["x-tenant-id"] || "default";
|
|
2696
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2697
|
+
for (const lattice of toolLatticeManager.getAllByTenant("default")) {
|
|
2698
|
+
byKey.set(lattice.key, lattice);
|
|
2699
|
+
}
|
|
2700
|
+
for (const lattice of toolLatticeManager.getAllByTenant(tenantId)) {
|
|
2701
|
+
byKey.set(lattice.key, lattice);
|
|
2702
|
+
}
|
|
2703
|
+
const allLattices = [...byKey.values()];
|
|
2695
2704
|
const toolConfigs = allLattices.map((lattice) => {
|
|
2696
2705
|
const config = { ...lattice.config };
|
|
2697
2706
|
const serializedSchema = config.schema ? serializeSchema(config.schema) : void 0;
|
|
@@ -4390,13 +4399,11 @@ async function importApply(request, reply) {
|
|
|
4390
4399
|
}
|
|
4391
4400
|
|
|
4392
4401
|
// src/controllers/middleware-types.ts
|
|
4393
|
-
import {
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
} from "@axiom-lattice/core";
|
|
4399
|
-
async function collectMiddlewareMetas(tenantId) {
|
|
4402
|
+
import { serializePluginMeta } from "@axiom-lattice/core";
|
|
4403
|
+
|
|
4404
|
+
// src/services/plugins/tenantPluginResolver.ts
|
|
4405
|
+
import { PluginRegistry as PluginRegistry2, getSandboxPluginCatalog as getSandboxPluginCatalog2, mergePluginSources } from "@axiom-lattice/core";
|
|
4406
|
+
async function listTenantPlugins(tenantId) {
|
|
4400
4407
|
let tenantPlugins = [];
|
|
4401
4408
|
try {
|
|
4402
4409
|
tenantPlugins = (await getSandboxPluginCatalog2().ensureLoaded(tenantId)).plugins;
|
|
@@ -4404,7 +4411,15 @@ async function collectMiddlewareMetas(tenantId) {
|
|
|
4404
4411
|
tenantPlugins = [];
|
|
4405
4412
|
}
|
|
4406
4413
|
const globalPlugins = PluginRegistry2.list().map((type) => PluginRegistry2.get(type)).filter((p) => Boolean(p));
|
|
4407
|
-
return mergePluginSources(globalPlugins, tenantPlugins)
|
|
4414
|
+
return mergePluginSources(globalPlugins, tenantPlugins);
|
|
4415
|
+
}
|
|
4416
|
+
async function resolveTenantPluginByType(tenantId, type) {
|
|
4417
|
+
return (await listTenantPlugins(tenantId)).find((p) => p.meta.type === type);
|
|
4418
|
+
}
|
|
4419
|
+
|
|
4420
|
+
// src/controllers/middleware-types.ts
|
|
4421
|
+
async function collectMiddlewareMetas(tenantId) {
|
|
4422
|
+
return (await listTenantPlugins(tenantId)).map(serializePluginMeta);
|
|
4408
4423
|
}
|
|
4409
4424
|
function middlewareTypesRoutes(fastify2) {
|
|
4410
4425
|
fastify2.get("/api/middleware-types", async (request, reply) => {
|
|
@@ -7542,7 +7557,7 @@ function registerMetricsServerConfigRoutes(app2) {
|
|
|
7542
7557
|
}
|
|
7543
7558
|
|
|
7544
7559
|
// src/controllers/connections.ts
|
|
7545
|
-
import { ConnectionRegistry
|
|
7560
|
+
import { ConnectionRegistry } from "@axiom-lattice/core";
|
|
7546
7561
|
import { z as z2 } from "zod";
|
|
7547
7562
|
var createBody = z2.object({
|
|
7548
7563
|
type: z2.string().min(1),
|
|
@@ -7563,15 +7578,7 @@ function getTenant(request) {
|
|
|
7563
7578
|
return request.headers["x-tenant-id"] || "default";
|
|
7564
7579
|
}
|
|
7565
7580
|
async function getTenantPlugin(request, type) {
|
|
7566
|
-
|
|
7567
|
-
let tenantPlugins = [];
|
|
7568
|
-
try {
|
|
7569
|
-
tenantPlugins = (await getSandboxPluginCatalog3().ensureLoaded(tenantId)).plugins;
|
|
7570
|
-
} catch {
|
|
7571
|
-
tenantPlugins = [];
|
|
7572
|
-
}
|
|
7573
|
-
const globalPlugins = PluginRegistry3.list().map((t2) => PluginRegistry3.get(t2)).filter((p) => Boolean(p));
|
|
7574
|
-
return mergePluginSources2(globalPlugins, tenantPlugins).find((p) => p.meta.type === type);
|
|
7581
|
+
return resolveTenantPluginByType(getTenant(request), type);
|
|
7575
7582
|
}
|
|
7576
7583
|
function maskSecrets(config) {
|
|
7577
7584
|
const sensitive = /* @__PURE__ */ new Set([
|
|
@@ -7710,14 +7717,862 @@ function connectionRoutes(fastify2) {
|
|
|
7710
7717
|
});
|
|
7711
7718
|
}
|
|
7712
7719
|
|
|
7720
|
+
// src/controllers/mcp-configs.ts
|
|
7721
|
+
import {
|
|
7722
|
+
getStoreLattice as getStoreLattice9,
|
|
7723
|
+
mcpManager,
|
|
7724
|
+
McpLatticeManager,
|
|
7725
|
+
convertToConnection
|
|
7726
|
+
} from "@axiom-lattice/core";
|
|
7727
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
7728
|
+
function getTenantId10(request) {
|
|
7729
|
+
const userTenantId = request.user?.tenantId;
|
|
7730
|
+
if (userTenantId) {
|
|
7731
|
+
return userTenantId;
|
|
7732
|
+
}
|
|
7733
|
+
return request.headers["x-tenant-id"] || "default";
|
|
7734
|
+
}
|
|
7735
|
+
async function getMcpServerConfigList(request, reply) {
|
|
7736
|
+
const tenantId = getTenantId10(request);
|
|
7737
|
+
try {
|
|
7738
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7739
|
+
const store = storeLattice.store;
|
|
7740
|
+
const configs = await store.getAllConfigs(tenantId);
|
|
7741
|
+
return {
|
|
7742
|
+
success: true,
|
|
7743
|
+
message: "MCP server configurations retrieved successfully",
|
|
7744
|
+
data: {
|
|
7745
|
+
records: configs,
|
|
7746
|
+
total: configs.length
|
|
7747
|
+
}
|
|
7748
|
+
};
|
|
7749
|
+
} catch (error) {
|
|
7750
|
+
console.error("Failed to get MCP server configs:", error);
|
|
7751
|
+
return {
|
|
7752
|
+
success: false,
|
|
7753
|
+
message: "Failed to retrieve MCP server configurations",
|
|
7754
|
+
data: {
|
|
7755
|
+
records: [],
|
|
7756
|
+
total: 0
|
|
7757
|
+
}
|
|
7758
|
+
};
|
|
7759
|
+
}
|
|
7760
|
+
}
|
|
7761
|
+
async function getMcpServerConfig(request, reply) {
|
|
7762
|
+
const tenantId = getTenantId10(request);
|
|
7763
|
+
const { key } = request.params;
|
|
7764
|
+
try {
|
|
7765
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7766
|
+
const store = storeLattice.store;
|
|
7767
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
7768
|
+
if (!config) {
|
|
7769
|
+
return {
|
|
7770
|
+
success: false,
|
|
7771
|
+
message: "MCP server configuration not found"
|
|
7772
|
+
};
|
|
7773
|
+
}
|
|
7774
|
+
return {
|
|
7775
|
+
success: true,
|
|
7776
|
+
message: "MCP server configuration retrieved successfully",
|
|
7777
|
+
data: config
|
|
7778
|
+
};
|
|
7779
|
+
} catch (error) {
|
|
7780
|
+
console.error("Failed to get MCP server config:", error);
|
|
7781
|
+
return {
|
|
7782
|
+
success: false,
|
|
7783
|
+
message: "Failed to retrieve MCP server configuration"
|
|
7784
|
+
};
|
|
7785
|
+
}
|
|
7786
|
+
}
|
|
7787
|
+
async function createMcpServerConfig(request, reply) {
|
|
7788
|
+
const tenantId = getTenantId10(request);
|
|
7789
|
+
const body = request.body;
|
|
7790
|
+
try {
|
|
7791
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7792
|
+
const store = storeLattice.store;
|
|
7793
|
+
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
7794
|
+
if (existing) {
|
|
7795
|
+
reply.code(409);
|
|
7796
|
+
return {
|
|
7797
|
+
success: false,
|
|
7798
|
+
message: "MCP server configuration with this key already exists"
|
|
7799
|
+
};
|
|
7800
|
+
}
|
|
7801
|
+
const id3 = body.id || randomUUID10();
|
|
7802
|
+
const config = await store.createConfig(tenantId, id3, body);
|
|
7803
|
+
try {
|
|
7804
|
+
await connectAndRegisterTools(config);
|
|
7805
|
+
await store.updateConfig(tenantId, id3, { status: "connected" });
|
|
7806
|
+
config.status = "connected";
|
|
7807
|
+
} catch (error) {
|
|
7808
|
+
console.warn("Failed to auto-connect MCP server:", error);
|
|
7809
|
+
await store.updateConfig(tenantId, id3, { status: "error" });
|
|
7810
|
+
config.status = "error";
|
|
7811
|
+
}
|
|
7812
|
+
reply.code(201);
|
|
7813
|
+
return {
|
|
7814
|
+
success: true,
|
|
7815
|
+
message: "MCP server configuration created successfully",
|
|
7816
|
+
data: config
|
|
7817
|
+
};
|
|
7818
|
+
} catch (error) {
|
|
7819
|
+
console.error("Failed to create MCP server config:", error);
|
|
7820
|
+
return {
|
|
7821
|
+
success: false,
|
|
7822
|
+
message: "Failed to create MCP server configuration"
|
|
7823
|
+
};
|
|
7824
|
+
}
|
|
7825
|
+
}
|
|
7826
|
+
async function updateMcpServerConfig(request, reply) {
|
|
7827
|
+
const tenantId = getTenantId10(request);
|
|
7828
|
+
const { key } = request.params;
|
|
7829
|
+
const updates = request.body;
|
|
7830
|
+
try {
|
|
7831
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7832
|
+
const store = storeLattice.store;
|
|
7833
|
+
const existing = await store.getConfigByKey(tenantId, key);
|
|
7834
|
+
if (!existing) {
|
|
7835
|
+
reply.code(404);
|
|
7836
|
+
return {
|
|
7837
|
+
success: false,
|
|
7838
|
+
message: "MCP server configuration not found"
|
|
7839
|
+
};
|
|
7840
|
+
}
|
|
7841
|
+
const shouldReconnect = updates.config !== void 0;
|
|
7842
|
+
const updated = await store.updateConfig(tenantId, existing.id, updates);
|
|
7843
|
+
if (!updated) {
|
|
7844
|
+
return {
|
|
7845
|
+
success: false,
|
|
7846
|
+
message: "Failed to update MCP server configuration"
|
|
7847
|
+
};
|
|
7848
|
+
}
|
|
7849
|
+
if (shouldReconnect) {
|
|
7850
|
+
try {
|
|
7851
|
+
const connection = convertToConnection(updated.config);
|
|
7852
|
+
mcpManager.registerServer(tenantId, key, connection, {
|
|
7853
|
+
timeoutMs: updated.config.timeout,
|
|
7854
|
+
maxAttempts: updated.config.retryAttempts
|
|
7855
|
+
});
|
|
7856
|
+
await mcpManager.ensureConnected(tenantId, key);
|
|
7857
|
+
await mcpManager.registerToolsToToolLattice(tenantId, key, updated.selectedTools);
|
|
7858
|
+
await store.updateConfig(tenantId, existing.id, { status: "connected" });
|
|
7859
|
+
updated.status = "connected";
|
|
7860
|
+
} catch (error) {
|
|
7861
|
+
console.warn("Failed to reconnect MCP server:", error);
|
|
7862
|
+
await store.updateConfig(tenantId, existing.id, { status: "error" });
|
|
7863
|
+
updated.status = "error";
|
|
7864
|
+
}
|
|
7865
|
+
}
|
|
7866
|
+
return {
|
|
7867
|
+
success: true,
|
|
7868
|
+
message: "MCP server configuration updated successfully",
|
|
7869
|
+
data: updated
|
|
7870
|
+
};
|
|
7871
|
+
} catch (error) {
|
|
7872
|
+
console.error("Failed to update MCP server config:", error);
|
|
7873
|
+
return {
|
|
7874
|
+
success: false,
|
|
7875
|
+
message: "Failed to update MCP server configuration"
|
|
7876
|
+
};
|
|
7877
|
+
}
|
|
7878
|
+
}
|
|
7879
|
+
async function deleteMcpServerConfig(request, reply) {
|
|
7880
|
+
const tenantId = getTenantId10(request);
|
|
7881
|
+
const { keyOrId } = request.params;
|
|
7882
|
+
try {
|
|
7883
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7884
|
+
const store = storeLattice.store;
|
|
7885
|
+
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
7886
|
+
let configKey = keyOrId;
|
|
7887
|
+
if (!config) {
|
|
7888
|
+
config = await store.getConfigById(tenantId, keyOrId);
|
|
7889
|
+
if (config) {
|
|
7890
|
+
configKey = config.key;
|
|
7891
|
+
}
|
|
7892
|
+
}
|
|
7893
|
+
if (!config) {
|
|
7894
|
+
reply.code(404);
|
|
7895
|
+
return {
|
|
7896
|
+
success: false,
|
|
7897
|
+
message: "MCP server configuration not found"
|
|
7898
|
+
};
|
|
7899
|
+
}
|
|
7900
|
+
try {
|
|
7901
|
+
if (mcpManager.hasServer(tenantId, configKey)) {
|
|
7902
|
+
await mcpManager.removeServer(tenantId, configKey);
|
|
7903
|
+
}
|
|
7904
|
+
} catch (error) {
|
|
7905
|
+
console.warn("Failed to remove from MCP manager:", error);
|
|
7906
|
+
}
|
|
7907
|
+
const deleted = await store.deleteConfig(tenantId, config.id);
|
|
7908
|
+
if (!deleted) {
|
|
7909
|
+
return {
|
|
7910
|
+
success: false,
|
|
7911
|
+
message: "Failed to delete MCP server configuration"
|
|
7912
|
+
};
|
|
7913
|
+
}
|
|
7914
|
+
return {
|
|
7915
|
+
success: true,
|
|
7916
|
+
message: "MCP server configuration deleted successfully"
|
|
7917
|
+
};
|
|
7918
|
+
} catch (error) {
|
|
7919
|
+
console.error("Failed to delete MCP server config:", error);
|
|
7920
|
+
return {
|
|
7921
|
+
success: false,
|
|
7922
|
+
message: "Failed to delete MCP server configuration"
|
|
7923
|
+
};
|
|
7924
|
+
}
|
|
7925
|
+
}
|
|
7926
|
+
async function testMcpServerConnection(request, reply) {
|
|
7927
|
+
const tenantId = getTenantId10(request);
|
|
7928
|
+
const { key } = request.params;
|
|
7929
|
+
try {
|
|
7930
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7931
|
+
const store = storeLattice.store;
|
|
7932
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
7933
|
+
if (!config) {
|
|
7934
|
+
reply.code(404);
|
|
7935
|
+
return {
|
|
7936
|
+
success: false,
|
|
7937
|
+
message: "MCP server configuration not found"
|
|
7938
|
+
};
|
|
7939
|
+
}
|
|
7940
|
+
try {
|
|
7941
|
+
const testKey = `__test_${key}_${Date.now()}`;
|
|
7942
|
+
const connection = convertToConnection(config.config);
|
|
7943
|
+
const { latency } = await McpLatticeManager.testConnection(testKey, connection);
|
|
7944
|
+
return {
|
|
7945
|
+
success: true,
|
|
7946
|
+
message: "Connection test successful",
|
|
7947
|
+
data: {
|
|
7948
|
+
connected: true,
|
|
7949
|
+
latency
|
|
7950
|
+
}
|
|
7951
|
+
};
|
|
7952
|
+
} catch (error) {
|
|
7953
|
+
return {
|
|
7954
|
+
success: true,
|
|
7955
|
+
message: "Connection test failed",
|
|
7956
|
+
data: {
|
|
7957
|
+
connected: false,
|
|
7958
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
7959
|
+
}
|
|
7960
|
+
};
|
|
7961
|
+
}
|
|
7962
|
+
} catch (error) {
|
|
7963
|
+
console.error("Failed to test MCP server connection:", error);
|
|
7964
|
+
return {
|
|
7965
|
+
success: false,
|
|
7966
|
+
message: "Failed to test MCP server connection",
|
|
7967
|
+
data: {
|
|
7968
|
+
connected: false,
|
|
7969
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
7970
|
+
}
|
|
7971
|
+
};
|
|
7972
|
+
}
|
|
7973
|
+
}
|
|
7974
|
+
async function listMcpServerTools(request, reply) {
|
|
7975
|
+
const tenantId = getTenantId10(request);
|
|
7976
|
+
const { key } = request.params;
|
|
7977
|
+
try {
|
|
7978
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
7979
|
+
const store = storeLattice.store;
|
|
7980
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
7981
|
+
if (!config) {
|
|
7982
|
+
reply.code(404);
|
|
7983
|
+
return {
|
|
7984
|
+
success: false,
|
|
7985
|
+
message: "MCP server configuration not found"
|
|
7986
|
+
};
|
|
7987
|
+
}
|
|
7988
|
+
if (!mcpManager.isServerConnected(tenantId, key)) {
|
|
7989
|
+
await connectAndRegisterTools(config);
|
|
7990
|
+
}
|
|
7991
|
+
if (config.status !== "connected") {
|
|
7992
|
+
await store.updateConfig(tenantId, config.id, { status: "connected" });
|
|
7993
|
+
}
|
|
7994
|
+
const tools = await mcpManager.getServerTools(tenantId, key);
|
|
7995
|
+
return {
|
|
7996
|
+
success: true,
|
|
7997
|
+
message: "Tools retrieved successfully",
|
|
7998
|
+
data: {
|
|
7999
|
+
tools
|
|
8000
|
+
}
|
|
8001
|
+
};
|
|
8002
|
+
} catch (error) {
|
|
8003
|
+
console.error("Failed to list MCP tools:", error);
|
|
8004
|
+
return {
|
|
8005
|
+
success: false,
|
|
8006
|
+
message: "Failed to retrieve tools"
|
|
8007
|
+
};
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
async function connectMcpServer(request, reply) {
|
|
8011
|
+
const tenantId = getTenantId10(request);
|
|
8012
|
+
const { key } = request.params;
|
|
8013
|
+
try {
|
|
8014
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
8015
|
+
const store = storeLattice.store;
|
|
8016
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
8017
|
+
if (!config) {
|
|
8018
|
+
reply.code(404);
|
|
8019
|
+
return {
|
|
8020
|
+
success: false,
|
|
8021
|
+
message: "MCP server configuration not found"
|
|
8022
|
+
};
|
|
8023
|
+
}
|
|
8024
|
+
await connectAndRegisterTools(config);
|
|
8025
|
+
const updated = await store.updateConfig(tenantId, config.id, {
|
|
8026
|
+
status: "connected"
|
|
8027
|
+
});
|
|
8028
|
+
return {
|
|
8029
|
+
success: true,
|
|
8030
|
+
message: "MCP server connected successfully",
|
|
8031
|
+
data: updated || config
|
|
8032
|
+
};
|
|
8033
|
+
} catch (error) {
|
|
8034
|
+
console.error("Failed to connect MCP server:", error);
|
|
8035
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
8036
|
+
const store = storeLattice.store;
|
|
8037
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
8038
|
+
if (config) {
|
|
8039
|
+
await store.updateConfig(tenantId, config.id, { status: "error" });
|
|
8040
|
+
}
|
|
8041
|
+
return {
|
|
8042
|
+
success: false,
|
|
8043
|
+
message: `Failed to connect MCP server: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
8044
|
+
};
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
async function disconnectMcpServer(request, reply) {
|
|
8048
|
+
const tenantId = getTenantId10(request);
|
|
8049
|
+
const { key } = request.params;
|
|
8050
|
+
try {
|
|
8051
|
+
const storeLattice = getStoreLattice9("default", "mcp");
|
|
8052
|
+
const store = storeLattice.store;
|
|
8053
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
8054
|
+
if (!config) {
|
|
8055
|
+
reply.code(404);
|
|
8056
|
+
return {
|
|
8057
|
+
success: false,
|
|
8058
|
+
message: "MCP server configuration not found"
|
|
8059
|
+
};
|
|
8060
|
+
}
|
|
8061
|
+
if (mcpManager.hasServer(tenantId, key)) {
|
|
8062
|
+
await mcpManager.removeServer(tenantId, key);
|
|
8063
|
+
}
|
|
8064
|
+
const updated = await store.updateConfig(tenantId, config.id, {
|
|
8065
|
+
status: "disconnected"
|
|
8066
|
+
});
|
|
8067
|
+
return {
|
|
8068
|
+
success: true,
|
|
8069
|
+
message: "MCP server disconnected successfully",
|
|
8070
|
+
data: updated || config
|
|
8071
|
+
};
|
|
8072
|
+
} catch (error) {
|
|
8073
|
+
console.error("Failed to disconnect MCP server:", error);
|
|
8074
|
+
return {
|
|
8075
|
+
success: false,
|
|
8076
|
+
message: "Failed to disconnect MCP server"
|
|
8077
|
+
};
|
|
8078
|
+
}
|
|
8079
|
+
}
|
|
8080
|
+
async function testMcpServerTools(request, reply) {
|
|
8081
|
+
const body = request.body;
|
|
8082
|
+
try {
|
|
8083
|
+
if (!body.config) {
|
|
8084
|
+
reply.code(400);
|
|
8085
|
+
return {
|
|
8086
|
+
success: false,
|
|
8087
|
+
message: "config is required"
|
|
8088
|
+
};
|
|
8089
|
+
}
|
|
8090
|
+
const testKey = `__test_${Date.now()}`;
|
|
8091
|
+
const connection = convertToConnection(body.config);
|
|
8092
|
+
const { tools } = await McpLatticeManager.testConnection(testKey, connection);
|
|
8093
|
+
return {
|
|
8094
|
+
success: true,
|
|
8095
|
+
message: "Tools retrieved successfully",
|
|
8096
|
+
data: {
|
|
8097
|
+
tools
|
|
8098
|
+
}
|
|
8099
|
+
};
|
|
8100
|
+
} catch (error) {
|
|
8101
|
+
console.error("Failed to test MCP server tools:", error);
|
|
8102
|
+
return {
|
|
8103
|
+
success: false,
|
|
8104
|
+
message: `Failed to retrieve tools: ${error instanceof Error ? error.message : String(error)}`
|
|
8105
|
+
};
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
async function connectAndRegisterTools(config) {
|
|
8109
|
+
const connection = convertToConnection(config.config);
|
|
8110
|
+
mcpManager.registerServer(config.tenantId, config.key, connection, {
|
|
8111
|
+
timeoutMs: config.config.timeout,
|
|
8112
|
+
maxAttempts: config.config.retryAttempts,
|
|
8113
|
+
selectedTools: config.selectedTools
|
|
8114
|
+
});
|
|
8115
|
+
await mcpManager.ensureConnected(config.tenantId, config.key);
|
|
8116
|
+
await mcpManager.registerToolsToToolLattice(
|
|
8117
|
+
config.tenantId,
|
|
8118
|
+
config.key,
|
|
8119
|
+
config.selectedTools
|
|
8120
|
+
);
|
|
8121
|
+
}
|
|
8122
|
+
function registerMcpServerConfigRoutes(app2) {
|
|
8123
|
+
app2.get("/api/mcp-servers", getMcpServerConfigList);
|
|
8124
|
+
app2.get("/api/mcp-servers/:key", getMcpServerConfig);
|
|
8125
|
+
app2.post("/api/mcp-servers", createMcpServerConfig);
|
|
8126
|
+
app2.put("/api/mcp-servers/:key", updateMcpServerConfig);
|
|
8127
|
+
app2.delete("/api/mcp-servers/:keyOrId", deleteMcpServerConfig);
|
|
8128
|
+
app2.post("/api/mcp-servers/:key/test", testMcpServerConnection);
|
|
8129
|
+
app2.get("/api/mcp-servers/:key/tools", listMcpServerTools);
|
|
8130
|
+
app2.post("/api/mcp-servers/:key/connect", connectMcpServer);
|
|
8131
|
+
app2.post("/api/mcp-servers/:key/disconnect", disconnectMcpServer);
|
|
8132
|
+
app2.post("/api/mcp-servers/test-tools", testMcpServerTools);
|
|
8133
|
+
}
|
|
8134
|
+
|
|
8135
|
+
// src/routes/mcp-apps.ts
|
|
8136
|
+
import {
|
|
8137
|
+
parseMcpUiRef
|
|
8138
|
+
} from "@axiom-lattice/protocols";
|
|
8139
|
+
import {
|
|
8140
|
+
getSandboxPluginCatalog as getSandboxPluginCatalog3,
|
|
8141
|
+
mcpManager as mcpManager2
|
|
8142
|
+
} from "@axiom-lattice/core";
|
|
8143
|
+
|
|
8144
|
+
// src/services/mcp-ui/UiResourceResolver.ts
|
|
8145
|
+
import { createHash as createHash2 } from "crypto";
|
|
8146
|
+
var DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
8147
|
+
function publicFieldsOf(payload) {
|
|
8148
|
+
return {
|
|
8149
|
+
mimeType: payload.mimeType,
|
|
8150
|
+
html: payload.html,
|
|
8151
|
+
...payload.csp !== void 0 ? { csp: payload.csp } : {},
|
|
8152
|
+
...payload.permissions !== void 0 ? { permissions: payload.permissions } : {},
|
|
8153
|
+
...payload.prefersBorder !== void 0 ? { prefersBorder: payload.prefersBorder } : {}
|
|
8154
|
+
};
|
|
8155
|
+
}
|
|
8156
|
+
var UiResourceResolver = class {
|
|
8157
|
+
constructor(deps) {
|
|
8158
|
+
this.deps = deps;
|
|
8159
|
+
this.inFlight = /* @__PURE__ */ new Map();
|
|
8160
|
+
}
|
|
8161
|
+
keyOf(ref) {
|
|
8162
|
+
return ref.kind === "mcp" ? `mcp\0${ref.serverKey}\0${ref.resourceUri}` : `plugin\0${ref.pluginType}\0${ref.resource}`;
|
|
8163
|
+
}
|
|
8164
|
+
fullKey(tenantId, ref) {
|
|
8165
|
+
return `${tenantId}\0${this.keyOf(ref)}`;
|
|
8166
|
+
}
|
|
8167
|
+
/**
|
|
8168
|
+
* Resolve a UI resource for a tenant.
|
|
8169
|
+
*
|
|
8170
|
+
* @param tenantId Tenant scope.
|
|
8171
|
+
* @param ref The fence ref.
|
|
8172
|
+
* @returns Public UI resource payload (no cache metadata).
|
|
8173
|
+
*/
|
|
8174
|
+
async resolve(tenantId, ref) {
|
|
8175
|
+
const key = this.fullKey(tenantId, ref);
|
|
8176
|
+
const ttl = this.deps.ttlMs ?? DEFAULT_TTL_MS;
|
|
8177
|
+
const cached = await this.deps.store.get(key);
|
|
8178
|
+
if (cached && Date.now() - cached.fetchedAt < ttl) return publicFieldsOf(cached);
|
|
8179
|
+
const existing = this.inFlight.get(key);
|
|
8180
|
+
if (existing) return existing;
|
|
8181
|
+
const promise = this.fetchAndCache(tenantId, ref, key, cached);
|
|
8182
|
+
this.inFlight.set(key, promise);
|
|
8183
|
+
try {
|
|
8184
|
+
return await promise;
|
|
8185
|
+
} finally {
|
|
8186
|
+
this.inFlight.delete(key);
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8189
|
+
/**
|
|
8190
|
+
* Evict the cached entry for a tenant/ref.
|
|
8191
|
+
*
|
|
8192
|
+
* @param tenantId Tenant scope.
|
|
8193
|
+
* @param ref The fence ref.
|
|
8194
|
+
*/
|
|
8195
|
+
async invalidate(tenantId, ref) {
|
|
8196
|
+
await this.deps.store.delete(this.fullKey(tenantId, ref));
|
|
8197
|
+
}
|
|
8198
|
+
async fetchAndCache(tenantId, ref, key, cached) {
|
|
8199
|
+
try {
|
|
8200
|
+
const fresh = ref.kind === "mcp" ? await this.deps.fetchMcp(tenantId, ref.serverKey, ref.resourceUri) : await this.deps.fetchPlugin(tenantId, ref.pluginType, ref.resource);
|
|
8201
|
+
const entry = {
|
|
8202
|
+
...publicFieldsOf(fresh),
|
|
8203
|
+
hash: createHash2("sha256").update(fresh.html).digest("hex"),
|
|
8204
|
+
fetchedAt: Date.now()
|
|
8205
|
+
};
|
|
8206
|
+
await this.deps.store.set(key, entry);
|
|
8207
|
+
return publicFieldsOf(fresh);
|
|
8208
|
+
} catch (error) {
|
|
8209
|
+
if (cached) return { ...publicFieldsOf(cached), stale: true };
|
|
8210
|
+
throw error;
|
|
8211
|
+
}
|
|
8212
|
+
}
|
|
8213
|
+
};
|
|
8214
|
+
|
|
8215
|
+
// src/services/mcp-ui/McpUiResourceCacheStore.ts
|
|
8216
|
+
var InMemoryMcpUiResourceCacheStore = class {
|
|
8217
|
+
constructor() {
|
|
8218
|
+
this.map = /* @__PURE__ */ new Map();
|
|
8219
|
+
}
|
|
8220
|
+
async get(key) {
|
|
8221
|
+
return this.map.get(key);
|
|
8222
|
+
}
|
|
8223
|
+
async set(key, entry) {
|
|
8224
|
+
this.map.set(key, entry);
|
|
8225
|
+
}
|
|
8226
|
+
async delete(key) {
|
|
8227
|
+
this.map.delete(key);
|
|
8228
|
+
}
|
|
8229
|
+
async deleteByPrefix(prefix) {
|
|
8230
|
+
for (const key of this.map.keys()) {
|
|
8231
|
+
if (key.startsWith(prefix)) this.map.delete(key);
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
8234
|
+
};
|
|
8235
|
+
|
|
8236
|
+
// src/services/mcp-ui/buildCsp.ts
|
|
8237
|
+
var DEFAULT_CSP = [
|
|
8238
|
+
"default-src 'none'",
|
|
8239
|
+
"script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'",
|
|
8240
|
+
"style-src 'self' 'unsafe-inline'",
|
|
8241
|
+
"img-src 'self' data:",
|
|
8242
|
+
"media-src 'self' data:",
|
|
8243
|
+
// Fonts are restricted to the document origin; declared `resourceDomains`
|
|
8244
|
+
// are the only origins ever added (no `data:` — a base64-embedded font must
|
|
8245
|
+
// be served from a declared origin).
|
|
8246
|
+
"font-src 'self'",
|
|
8247
|
+
"object-src 'none'",
|
|
8248
|
+
"connect-src 'none'",
|
|
8249
|
+
"frame-src 'none'",
|
|
8250
|
+
"base-uri 'self'"
|
|
8251
|
+
].join("; ");
|
|
8252
|
+
var ORIGIN_PATTERN = /^(https?|wss?):\/\/(\*\.)?[^\s;,'"*]+$/;
|
|
8253
|
+
function declaredOrigins(domains) {
|
|
8254
|
+
if (!Array.isArray(domains)) return [];
|
|
8255
|
+
return domains.filter((domain) => typeof domain === "string" && ORIGIN_PATTERN.test(domain));
|
|
8256
|
+
}
|
|
8257
|
+
function buildCsp(csp) {
|
|
8258
|
+
if (!csp) return DEFAULT_CSP;
|
|
8259
|
+
const resourceOrigins = declaredOrigins(csp.resourceDomains);
|
|
8260
|
+
const resource = resourceOrigins.join(" ");
|
|
8261
|
+
const withResourceOrigins = (base) => resource ? `${base} ${resource}` : base;
|
|
8262
|
+
const img = ["'self'", "data:", ...resourceOrigins].join(" ");
|
|
8263
|
+
const connect = declaredOrigins(csp.connectDomains);
|
|
8264
|
+
const frame = declaredOrigins(csp.frameDomains);
|
|
8265
|
+
const baseUri = declaredOrigins(csp.baseUriDomains);
|
|
8266
|
+
const parts = [
|
|
8267
|
+
"default-src 'none'",
|
|
8268
|
+
withResourceOrigins("script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'"),
|
|
8269
|
+
withResourceOrigins("style-src 'self' 'unsafe-inline'"),
|
|
8270
|
+
`img-src ${img}`,
|
|
8271
|
+
`media-src ${["'self'", "data:", ...resourceOrigins].join(" ")}`,
|
|
8272
|
+
withResourceOrigins("font-src 'self'"),
|
|
8273
|
+
"object-src 'none'",
|
|
8274
|
+
`connect-src ${connect.length ? connect.join(" ") : "'none'"}`,
|
|
8275
|
+
`frame-src ${frame.length ? frame.join(" ") : "'none'"}`,
|
|
8276
|
+
`base-uri ${baseUri.length ? baseUri.join(" ") : "'self'"}`
|
|
8277
|
+
];
|
|
8278
|
+
return parts.join("; ");
|
|
8279
|
+
}
|
|
8280
|
+
|
|
8281
|
+
// src/services/mcp-ui/sandboxPage.ts
|
|
8282
|
+
var DEFAULT_CSP_LITERAL = JSON.stringify(buildCsp(void 0));
|
|
8283
|
+
var SANDBOX_HTML = `<!doctype html>
|
|
8284
|
+
<html lang="en">
|
|
8285
|
+
<head>
|
|
8286
|
+
<meta charset="utf-8" />
|
|
8287
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8288
|
+
<title>MCP App Sandbox</title>
|
|
8289
|
+
<style>html, body { margin: 0; height: 100%; }</style>
|
|
8290
|
+
</head>
|
|
8291
|
+
<body>
|
|
8292
|
+
<script>
|
|
8293
|
+
(function () {
|
|
8294
|
+
var HOST_READY = "ui/notifications/sandbox-proxy-ready";
|
|
8295
|
+
var RESOURCE_READY = "ui/notifications/sandbox-resource-ready";
|
|
8296
|
+
var CONTROL_PREFIX = "ui/notifications/sandbox-";
|
|
8297
|
+
var DEFAULT_CSP = ${DEFAULT_CSP_LITERAL};
|
|
8298
|
+
|
|
8299
|
+
function withCsp(html, csp) {
|
|
8300
|
+
var policy = typeof csp === "string" && csp.length > 0 ? csp : DEFAULT_CSP;
|
|
8301
|
+
var escaped = policy.replace(/&/g, "&").replace(/"/g, """);
|
|
8302
|
+
var meta = '<meta http-equiv="Content-Security-Policy" content="' + escaped + '">';
|
|
8303
|
+
// Insert at the very start (after an optional doctype) so the parser
|
|
8304
|
+
// places the meta in the implicit head. Never search for a literal
|
|
8305
|
+
// <head>: a resource could hide a decoy inside a comment/string and
|
|
8306
|
+
// slip its real head past the splice.
|
|
8307
|
+
var doctype = /^\\s*<!doctype[^>]*>/i.exec(html);
|
|
8308
|
+
if (doctype) {
|
|
8309
|
+
return html.slice(0, doctype[0].length) + meta + html.slice(doctype[0].length);
|
|
8310
|
+
}
|
|
8311
|
+
return meta + html;
|
|
8312
|
+
}
|
|
8313
|
+
|
|
8314
|
+
// Size reporter injected into every View document. The host renders the
|
|
8315
|
+
// View inside an opaque-origin iframe it cannot measure, so auto-height
|
|
8316
|
+
// only happens when the View posts ui/notifications/size-changed. Most
|
|
8317
|
+
// hand-written plugin apps never do; this script makes every app report
|
|
8318
|
+
// its own content size. It only measures and postMessages \u2014 it never
|
|
8319
|
+
// mutates the app's DOM.
|
|
8320
|
+
var SIZE_REPORTER = [
|
|
8321
|
+
"(function(){",
|
|
8322
|
+
"function send(){",
|
|
8323
|
+
"try{",
|
|
8324
|
+
"var d=document.documentElement,b=document.body;",
|
|
8325
|
+
"var w=Math.ceil(Math.max(d?d.scrollWidth:0,b?b.scrollWidth:0));",
|
|
8326
|
+
"var h=Math.ceil(Math.max(d?d.scrollHeight:0,b?b.scrollHeight:0));",
|
|
8327
|
+
"window.parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/size-changed',params:{width:w,height:h}},'*');",
|
|
8328
|
+
"}catch(e){}",
|
|
8329
|
+
"}",
|
|
8330
|
+
"send();",
|
|
8331
|
+
"window.addEventListener('load',send);",
|
|
8332
|
+
"if(typeof ResizeObserver!=='undefined'){",
|
|
8333
|
+
"var ro=new ResizeObserver(send);",
|
|
8334
|
+
"ro.observe(document.documentElement);",
|
|
8335
|
+
"if(document.body)ro.observe(document.body);",
|
|
8336
|
+
"}else{window.addEventListener('resize',send);}",
|
|
8337
|
+
"})();"
|
|
8338
|
+
].join("");
|
|
8339
|
+
|
|
8340
|
+
function withSizeReporter(html) {
|
|
8341
|
+
// Split the closing tag so this template literal never contains a
|
|
8342
|
+
// literal script terminator that would end the sandbox page's own
|
|
8343
|
+
// script block during HTML parsing.
|
|
8344
|
+
var tag = "<scr" + "ipt>" + SIZE_REPORTER + "<" + "/scr" + "ipt>";
|
|
8345
|
+
var lower = html.toLowerCase();
|
|
8346
|
+
var bodyEnd = lower.lastIndexOf("</body>");
|
|
8347
|
+
if (bodyEnd >= 0) return html.slice(0, bodyEnd) + tag + html.slice(bodyEnd);
|
|
8348
|
+
return html + tag;
|
|
8349
|
+
}
|
|
8350
|
+
|
|
8351
|
+
var inner = null;
|
|
8352
|
+
|
|
8353
|
+
window.parent.postMessage({ jsonrpc: "2.0", method: HOST_READY, params: {} }, "*");
|
|
8354
|
+
|
|
8355
|
+
window.addEventListener("message", function (event) {
|
|
8356
|
+
if (event.source !== window.parent) return;
|
|
8357
|
+
var data = event.data;
|
|
8358
|
+
if (!data || typeof data !== "object") return;
|
|
8359
|
+
|
|
8360
|
+
if (data.method === RESOURCE_READY) {
|
|
8361
|
+
var params = data.params || {};
|
|
8362
|
+
if (inner && inner.parentNode) inner.parentNode.removeChild(inner);
|
|
8363
|
+
inner = document.createElement("iframe");
|
|
8364
|
+
inner.setAttribute("sandbox", params.sandbox || "allow-scripts");
|
|
8365
|
+
inner.setAttribute("srcdoc", withCsp(withSizeReporter(String(params.html || "")), params.csp));
|
|
8366
|
+
inner.style.cssText = "width:100%;height:100%;border:0;display:block";
|
|
8367
|
+
document.body.appendChild(inner);
|
|
8368
|
+
return;
|
|
8369
|
+
}
|
|
8370
|
+
|
|
8371
|
+
if (typeof data.method === "string" && data.method.indexOf(CONTROL_PREFIX) === 0) return;
|
|
8372
|
+
if (inner && inner.contentWindow) inner.contentWindow.postMessage(data, "*");
|
|
8373
|
+
});
|
|
8374
|
+
|
|
8375
|
+
window.addEventListener("message", function (event) {
|
|
8376
|
+
if (inner && event.source === inner.contentWindow) {
|
|
8377
|
+
var data = event.data;
|
|
8378
|
+
if (data && typeof data === "object" && typeof data.method === "string"
|
|
8379
|
+
&& data.method.indexOf(CONTROL_PREFIX) === 0) return;
|
|
8380
|
+
window.parent.postMessage(event.data, "*");
|
|
8381
|
+
}
|
|
8382
|
+
});
|
|
8383
|
+
})();
|
|
8384
|
+
</script>
|
|
8385
|
+
</body>
|
|
8386
|
+
</html>
|
|
8387
|
+
`;
|
|
8388
|
+
|
|
8389
|
+
// src/routes/mcp-apps.ts
|
|
8390
|
+
var SANDBOX_HTML_CONTENT_TYPE = "text/html; charset=utf-8";
|
|
8391
|
+
var PluginMethodNotSupportedError = class extends Error {
|
|
8392
|
+
/**
|
|
8393
|
+
* @param method - Unsupported JSON-RPC method.
|
|
8394
|
+
*/
|
|
8395
|
+
constructor(method) {
|
|
8396
|
+
super(`plugin backend does not support ${method}`);
|
|
8397
|
+
this.name = "PluginMethodNotSupportedError";
|
|
8398
|
+
}
|
|
8399
|
+
};
|
|
8400
|
+
var ALLOWED_METHODS = /* @__PURE__ */ new Set(["tools/call", "tools/list", "resources/read"]);
|
|
8401
|
+
function asRecord(value) {
|
|
8402
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
8403
|
+
}
|
|
8404
|
+
function makeMcpAppHandlers(deps) {
|
|
8405
|
+
return {
|
|
8406
|
+
/**
|
|
8407
|
+
* Resolve the `mcp_app` UI resource for a parsed ref.
|
|
8408
|
+
*
|
|
8409
|
+
* @param tenantId Tenant scope.
|
|
8410
|
+
* @param rawRef Untrusted ref payload.
|
|
8411
|
+
* @returns The resource payload plus a ready-to-use `cspPolicy` string
|
|
8412
|
+
* ({@link buildCsp}, always non-empty), or a 400/502 error envelope.
|
|
8413
|
+
*/
|
|
8414
|
+
async resource(tenantId, rawRef) {
|
|
8415
|
+
const ref = parseMcpUiRef(rawRef);
|
|
8416
|
+
if (!ref) return { status: 400, body: { error: "INVALID_REF" } };
|
|
8417
|
+
if (ref.kind === "plugin" && ref.pluginType === "") {
|
|
8418
|
+
return { status: 400, body: { error: "INVALID_REF" } };
|
|
8419
|
+
}
|
|
8420
|
+
try {
|
|
8421
|
+
const payload = await deps.resolveUiResource(tenantId, ref);
|
|
8422
|
+
return { status: 200, body: { ...payload, cspPolicy: buildCsp(payload.csp) } };
|
|
8423
|
+
} catch (error) {
|
|
8424
|
+
console.warn(JSON.stringify({
|
|
8425
|
+
event: "mcp-apps:resource-failed",
|
|
8426
|
+
tenantId,
|
|
8427
|
+
kind: ref.kind,
|
|
8428
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8429
|
+
}));
|
|
8430
|
+
return { status: 502, body: { error: "RESOURCE_UNAVAILABLE" } };
|
|
8431
|
+
}
|
|
8432
|
+
},
|
|
8433
|
+
/**
|
|
8434
|
+
* Proxy an allow-listed JSON-RPC method for a parsed ref.
|
|
8435
|
+
*
|
|
8436
|
+
* @param tenantId Tenant scope.
|
|
8437
|
+
* @param rawRef Untrusted ref payload.
|
|
8438
|
+
* @param method JSON-RPC method name.
|
|
8439
|
+
* @param params JSON-RPC params.
|
|
8440
|
+
* @param context Request-scoped run context forwarded to `tools/call`.
|
|
8441
|
+
* @returns The backend result, or a 400/403/502 error envelope.
|
|
8442
|
+
*/
|
|
8443
|
+
async rpc(tenantId, rawRef, method, params, context) {
|
|
8444
|
+
const ref = parseMcpUiRef(rawRef);
|
|
8445
|
+
if (!ref) return { status: 400, body: { error: "INVALID_REF" } };
|
|
8446
|
+
if (!ALLOWED_METHODS.has(method)) return { status: 403, body: { error: "METHOD_NOT_ALLOWED" } };
|
|
8447
|
+
try {
|
|
8448
|
+
if (method === "tools/call") {
|
|
8449
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
8450
|
+
if (name === "") return { status: 400, body: { error: "INVALID_PARAMS" } };
|
|
8451
|
+
if ("pluginType" in ref && ref.pluginType === "") {
|
|
8452
|
+
return { status: 400, body: { error: "INVALID_REF" } };
|
|
8453
|
+
}
|
|
8454
|
+
return {
|
|
8455
|
+
status: 200,
|
|
8456
|
+
body: await deps.callTool(tenantId, ref, name, asRecord(params.arguments) ?? {}, context)
|
|
8457
|
+
};
|
|
8458
|
+
}
|
|
8459
|
+
if (method === "resources/read") {
|
|
8460
|
+
const uri = typeof params.uri === "string" ? params.uri : "";
|
|
8461
|
+
if (uri === "") return { status: 400, body: { error: "INVALID_PARAMS" } };
|
|
8462
|
+
return { status: 200, body: await deps.readResource(tenantId, ref, uri) };
|
|
8463
|
+
}
|
|
8464
|
+
return { status: 200, body: { tools: await deps.listTools(tenantId, ref) } };
|
|
8465
|
+
} catch (error) {
|
|
8466
|
+
if (error instanceof PluginToolScopeError) {
|
|
8467
|
+
return { status: 403, body: { error: "PLUGIN_TOOL_SCOPE" } };
|
|
8468
|
+
}
|
|
8469
|
+
if (error instanceof PluginMethodNotSupportedError) {
|
|
8470
|
+
return { status: 403, body: { error: "PLUGIN_METHOD_UNSUPPORTED" } };
|
|
8471
|
+
}
|
|
8472
|
+
const context2 = { tenantId, kind: ref.kind, method };
|
|
8473
|
+
if (method === "resources/read") context2.uri = typeof params.uri === "string" ? params.uri : "";
|
|
8474
|
+
if (method === "tools/call") context2.name = typeof params.name === "string" ? params.name : "";
|
|
8475
|
+
console.warn(JSON.stringify({
|
|
8476
|
+
event: "mcp-apps:rpc-failed",
|
|
8477
|
+
...context2,
|
|
8478
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8479
|
+
}));
|
|
8480
|
+
return { status: 502, body: { error: "RPC_FAILED" } };
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
};
|
|
8484
|
+
}
|
|
8485
|
+
async function fetchPluginUiResource(backend, tenantId, pluginType, resource) {
|
|
8486
|
+
const plugin = await backend.resolvePlugin(tenantId, pluginType);
|
|
8487
|
+
if (!plugin) throw new Error(`plugin "${pluginType}" not found`);
|
|
8488
|
+
const tool = (plugin.meta.tools ?? []).find((t2) => t2.ui?.resource === resource);
|
|
8489
|
+
if (!tool?.ui) throw new Error(`plugin "${pluginType}" has no tool declaring ui resource "${resource}"`);
|
|
8490
|
+
const html = await backend.catalog.readPluginFile(tenantId, pluginType, resource);
|
|
8491
|
+
return {
|
|
8492
|
+
mimeType: "text/html",
|
|
8493
|
+
html,
|
|
8494
|
+
csp: tool.ui.csp,
|
|
8495
|
+
permissions: tool.ui.permissions,
|
|
8496
|
+
prefersBorder: tool.ui.prefersBorder
|
|
8497
|
+
};
|
|
8498
|
+
}
|
|
8499
|
+
async function callPluginTool(backend, tenantId, ref, name, args, context) {
|
|
8500
|
+
return invokePluginTool(backend, tenantId, ref.pluginType, name, args, context);
|
|
8501
|
+
}
|
|
8502
|
+
function createDefaultPluginBackend() {
|
|
8503
|
+
return {
|
|
8504
|
+
resolvePlugin: (tenantId, type) => resolveTenantPluginByType(tenantId, type),
|
|
8505
|
+
catalog: {
|
|
8506
|
+
readPluginFile: (tenantId, pluginType, relPath) => getSandboxPluginCatalog3().readPluginFile(tenantId, pluginType, relPath),
|
|
8507
|
+
getValidatedManifest: (tenantId, pluginType) => getSandboxPluginCatalog3().getValidatedManifest(tenantId, pluginType),
|
|
8508
|
+
getExecutor: () => getSandboxPluginCatalog3().getExecutor()
|
|
8509
|
+
}
|
|
8510
|
+
};
|
|
8511
|
+
}
|
|
8512
|
+
function resolveMcpAppDeps(pluginBackend = createDefaultPluginBackend()) {
|
|
8513
|
+
const uiResourceResolver = new UiResourceResolver({
|
|
8514
|
+
store: new InMemoryMcpUiResourceCacheStore(),
|
|
8515
|
+
fetchMcp: (tenantId, serverKey, resourceUri) => mcpManager2.readUiResource(tenantId, serverKey, resourceUri),
|
|
8516
|
+
fetchPlugin: (tenantId, pluginType, resource) => fetchPluginUiResource(pluginBackend, tenantId, pluginType, resource)
|
|
8517
|
+
});
|
|
8518
|
+
return {
|
|
8519
|
+
resolveUiResource: (tenantId, ref) => uiResourceResolver.resolve(tenantId, ref),
|
|
8520
|
+
callTool: (tenantId, ref, name, args, context) => ref.kind === "mcp" ? mcpManager2.callServerTool(tenantId, ref.serverKey, name, args) : callPluginTool(pluginBackend, tenantId, ref, name, args, context),
|
|
8521
|
+
// Plugin refs reach these only after `tools/call`; the handler treats the
|
|
8522
|
+
// rejection as an unsupported method (403). The branches are live because
|
|
8523
|
+
// the pure handler deliberately delegates method policy to the deps.
|
|
8524
|
+
readResource: (tenantId, ref, uri) => ref.kind === "mcp" ? mcpManager2.readServerResource(tenantId, ref.serverKey, uri) : Promise.reject(new PluginMethodNotSupportedError("resources/read")),
|
|
8525
|
+
listTools: (tenantId, ref) => ref.kind === "mcp" ? mcpManager2.getAvailableTools(tenantId, ref.serverKey) : Promise.reject(new PluginMethodNotSupportedError("tools/list"))
|
|
8526
|
+
};
|
|
8527
|
+
}
|
|
8528
|
+
function firstHeader(request, name) {
|
|
8529
|
+
const header2 = request.headers[name];
|
|
8530
|
+
return Array.isArray(header2) ? header2[0] : header2;
|
|
8531
|
+
}
|
|
8532
|
+
function tenantOf(request) {
|
|
8533
|
+
const user = request.user;
|
|
8534
|
+
return user?.tenantId ?? firstHeader(request, "x-tenant-id") ?? "default";
|
|
8535
|
+
}
|
|
8536
|
+
function runContextOf(request) {
|
|
8537
|
+
return {
|
|
8538
|
+
tenantId: tenantOf(request),
|
|
8539
|
+
workspaceId: firstHeader(request, "x-workspace-id"),
|
|
8540
|
+
projectId: firstHeader(request, "x-project-id")
|
|
8541
|
+
};
|
|
8542
|
+
}
|
|
8543
|
+
function registerMcpAppRoutes(fastify2) {
|
|
8544
|
+
const handlers = makeMcpAppHandlers(resolveMcpAppDeps());
|
|
8545
|
+
fastify2.get("/mcp-apps/sandbox.html", async (_request, reply) => {
|
|
8546
|
+
return reply.header("content-type", SANDBOX_HTML_CONTENT_TYPE).header("x-content-type-options", "nosniff").header("cache-control", "no-store").send(SANDBOX_HTML);
|
|
8547
|
+
});
|
|
8548
|
+
fastify2.post("/api/mcp-apps/resource", async (request, reply) => {
|
|
8549
|
+
const body = asRecord(request.body) ?? {};
|
|
8550
|
+
const result = await handlers.resource(tenantOf(request), body.ref);
|
|
8551
|
+
return reply.code(result.status).send(result.body);
|
|
8552
|
+
});
|
|
8553
|
+
fastify2.post("/api/mcp-apps/rpc", async (request, reply) => {
|
|
8554
|
+
const body = asRecord(request.body) ?? {};
|
|
8555
|
+
const params = asRecord(body.params) ?? {};
|
|
8556
|
+
const context = runContextOf(request);
|
|
8557
|
+
const result = await handlers.rpc(
|
|
8558
|
+
context.tenantId,
|
|
8559
|
+
body.ref,
|
|
8560
|
+
String(body.method ?? ""),
|
|
8561
|
+
params,
|
|
8562
|
+
context
|
|
8563
|
+
);
|
|
8564
|
+
return reply.code(result.status).send(result.body);
|
|
8565
|
+
});
|
|
8566
|
+
}
|
|
8567
|
+
|
|
7713
8568
|
// src/controllers/eval.ts
|
|
7714
|
-
import { getStoreLattice as
|
|
8569
|
+
import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
|
|
7715
8570
|
import { v4 as uuidv43 } from "uuid";
|
|
7716
8571
|
|
|
7717
8572
|
// src/services/eval-runner.ts
|
|
7718
8573
|
import { EventEmitter } from "events";
|
|
7719
8574
|
import {
|
|
7720
|
-
getStoreLattice as
|
|
8575
|
+
getStoreLattice as getStoreLattice10,
|
|
7721
8576
|
modelLatticeManager as modelLatticeManager2,
|
|
7722
8577
|
LatticeEvalProject
|
|
7723
8578
|
} from "@axiom-lattice/core";
|
|
@@ -8047,7 +8902,7 @@ var EvalRunner = class {
|
|
|
8047
8902
|
return ctx !== void 0 && !ctx.terminal.workerSettled && (tenantId === void 0 || ctx.tenantId === tenantId);
|
|
8048
8903
|
}
|
|
8049
8904
|
getEvalStore() {
|
|
8050
|
-
return
|
|
8905
|
+
return getStoreLattice10("default", "eval").store;
|
|
8051
8906
|
}
|
|
8052
8907
|
releaseProjectLease(leaseKey, leaseToken) {
|
|
8053
8908
|
if (this.projectLeases.get(leaseKey) === leaseToken) this.projectLeases.delete(leaseKey);
|
|
@@ -8056,7 +8911,7 @@ var EvalRunner = class {
|
|
|
8056
8911
|
var evalRunner = new EvalRunner();
|
|
8057
8912
|
|
|
8058
8913
|
// src/controllers/eval.ts
|
|
8059
|
-
function
|
|
8914
|
+
function getTenantId11(request) {
|
|
8060
8915
|
const userTenantId = request.user?.tenantId;
|
|
8061
8916
|
if (userTenantId) {
|
|
8062
8917
|
return userTenantId;
|
|
@@ -8064,7 +8919,7 @@ function getTenantId10(request) {
|
|
|
8064
8919
|
return request.headers["x-tenant-id"] || "default";
|
|
8065
8920
|
}
|
|
8066
8921
|
function getEvalStore() {
|
|
8067
|
-
return
|
|
8922
|
+
return getStoreLattice11("default", "eval").store;
|
|
8068
8923
|
}
|
|
8069
8924
|
async function getOwnedSuite(store, tenantId, projectId, suiteId) {
|
|
8070
8925
|
const project = await store.getProjectById(tenantId, projectId);
|
|
@@ -8080,7 +8935,7 @@ async function getOwnedCase(store, tenantId, projectId, suiteId, caseId) {
|
|
|
8080
8935
|
}
|
|
8081
8936
|
async function createProject(request, reply) {
|
|
8082
8937
|
try {
|
|
8083
|
-
const tenantId =
|
|
8938
|
+
const tenantId = getTenantId11(request);
|
|
8084
8939
|
const store = getEvalStore();
|
|
8085
8940
|
const id3 = uuidv43();
|
|
8086
8941
|
const data = request.body;
|
|
@@ -8137,7 +8992,7 @@ async function createProject(request, reply) {
|
|
|
8137
8992
|
}
|
|
8138
8993
|
async function listProjects(request, reply) {
|
|
8139
8994
|
try {
|
|
8140
|
-
const tenantId =
|
|
8995
|
+
const tenantId = getTenantId11(request);
|
|
8141
8996
|
const store = getEvalStore();
|
|
8142
8997
|
let projects = await store.getProjectsByTenant(tenantId);
|
|
8143
8998
|
if (projects.length === 0) {
|
|
@@ -8171,7 +9026,7 @@ async function listProjects(request, reply) {
|
|
|
8171
9026
|
}
|
|
8172
9027
|
async function getProject(request, reply) {
|
|
8173
9028
|
try {
|
|
8174
|
-
const tenantId =
|
|
9029
|
+
const tenantId = getTenantId11(request);
|
|
8175
9030
|
const store = getEvalStore();
|
|
8176
9031
|
const { pid } = request.params;
|
|
8177
9032
|
const project = await store.getProjectById(tenantId, pid);
|
|
@@ -8191,7 +9046,7 @@ async function getProject(request, reply) {
|
|
|
8191
9046
|
}
|
|
8192
9047
|
async function updateProject(request, reply) {
|
|
8193
9048
|
try {
|
|
8194
|
-
const tenantId =
|
|
9049
|
+
const tenantId = getTenantId11(request);
|
|
8195
9050
|
const store = getEvalStore();
|
|
8196
9051
|
const { pid } = request.params;
|
|
8197
9052
|
const existing = await store.getProjectById(tenantId, pid);
|
|
@@ -8235,7 +9090,7 @@ async function updateProject(request, reply) {
|
|
|
8235
9090
|
async function deleteProject(request, reply) {
|
|
8236
9091
|
let releaseDeletion;
|
|
8237
9092
|
try {
|
|
8238
|
-
const tenantId =
|
|
9093
|
+
const tenantId = getTenantId11(request);
|
|
8239
9094
|
const store = getEvalStore();
|
|
8240
9095
|
const { pid } = request.params;
|
|
8241
9096
|
const existing = await store.getProjectById(tenantId, pid);
|
|
@@ -8273,7 +9128,7 @@ async function deleteProject(request, reply) {
|
|
|
8273
9128
|
}
|
|
8274
9129
|
async function createSuite(request, reply) {
|
|
8275
9130
|
try {
|
|
8276
|
-
const tenantId =
|
|
9131
|
+
const tenantId = getTenantId11(request);
|
|
8277
9132
|
const store = getEvalStore();
|
|
8278
9133
|
const { pid } = request.params;
|
|
8279
9134
|
const project = await store.getProjectById(tenantId, pid);
|
|
@@ -8295,7 +9150,7 @@ async function createSuite(request, reply) {
|
|
|
8295
9150
|
}
|
|
8296
9151
|
async function updateSuite(request, reply) {
|
|
8297
9152
|
try {
|
|
8298
|
-
const tenantId =
|
|
9153
|
+
const tenantId = getTenantId11(request);
|
|
8299
9154
|
const store = getEvalStore();
|
|
8300
9155
|
const { pid, sid } = request.params;
|
|
8301
9156
|
const existing = await getOwnedSuite(store, tenantId, pid, sid);
|
|
@@ -8315,7 +9170,7 @@ async function updateSuite(request, reply) {
|
|
|
8315
9170
|
}
|
|
8316
9171
|
async function deleteSuite(request, reply) {
|
|
8317
9172
|
try {
|
|
8318
|
-
const tenantId =
|
|
9173
|
+
const tenantId = getTenantId11(request);
|
|
8319
9174
|
const store = getEvalStore();
|
|
8320
9175
|
const { pid, sid } = request.params;
|
|
8321
9176
|
const existing = await getOwnedSuite(store, tenantId, pid, sid);
|
|
@@ -8334,7 +9189,7 @@ async function deleteSuite(request, reply) {
|
|
|
8334
9189
|
}
|
|
8335
9190
|
async function createCase(request, reply) {
|
|
8336
9191
|
try {
|
|
8337
|
-
const tenantId =
|
|
9192
|
+
const tenantId = getTenantId11(request);
|
|
8338
9193
|
const store = getEvalStore();
|
|
8339
9194
|
const { pid, sid } = request.params;
|
|
8340
9195
|
const suite = await getOwnedSuite(store, tenantId, pid, sid);
|
|
@@ -8364,7 +9219,7 @@ async function createCase(request, reply) {
|
|
|
8364
9219
|
}
|
|
8365
9220
|
async function listCasesForSuite(request, reply) {
|
|
8366
9221
|
try {
|
|
8367
|
-
const tenantId =
|
|
9222
|
+
const tenantId = getTenantId11(request);
|
|
8368
9223
|
const store = getEvalStore();
|
|
8369
9224
|
const { pid, sid } = request.params;
|
|
8370
9225
|
const suite = await getOwnedSuite(store, tenantId, pid, sid);
|
|
@@ -8384,7 +9239,7 @@ async function listCasesForSuite(request, reply) {
|
|
|
8384
9239
|
}
|
|
8385
9240
|
async function updateCase(request, reply) {
|
|
8386
9241
|
try {
|
|
8387
|
-
const tenantId =
|
|
9242
|
+
const tenantId = getTenantId11(request);
|
|
8388
9243
|
const store = getEvalStore();
|
|
8389
9244
|
const { pid, sid, cid } = request.params;
|
|
8390
9245
|
const existing = await getOwnedCase(store, tenantId, pid, sid, cid);
|
|
@@ -8404,7 +9259,7 @@ async function updateCase(request, reply) {
|
|
|
8404
9259
|
}
|
|
8405
9260
|
async function deleteCase(request, reply) {
|
|
8406
9261
|
try {
|
|
8407
|
-
const tenantId =
|
|
9262
|
+
const tenantId = getTenantId11(request);
|
|
8408
9263
|
const store = getEvalStore();
|
|
8409
9264
|
const { pid, sid, cid } = request.params;
|
|
8410
9265
|
const existing = await getOwnedCase(store, tenantId, pid, sid, cid);
|
|
@@ -8436,7 +9291,7 @@ function registerEvalRoutes(app2) {
|
|
|
8436
9291
|
app2.delete("/api/eval/projects/:pid/suites/:sid/cases/:cid", deleteCase);
|
|
8437
9292
|
app2.post("/api/eval/projects/:pid/runs", async (request, reply) => {
|
|
8438
9293
|
try {
|
|
8439
|
-
const tenantId =
|
|
9294
|
+
const tenantId = getTenantId11(request);
|
|
8440
9295
|
const { pid } = request.params;
|
|
8441
9296
|
const body = request.body ?? {};
|
|
8442
9297
|
const bodyWs = request.headers["x-workspace-id"] || "";
|
|
@@ -8455,7 +9310,7 @@ function registerEvalRoutes(app2) {
|
|
|
8455
9310
|
});
|
|
8456
9311
|
app2.get("/api/eval/runs", async (request, reply) => {
|
|
8457
9312
|
try {
|
|
8458
|
-
const tenantId =
|
|
9313
|
+
const tenantId = getTenantId11(request);
|
|
8459
9314
|
const query = request.query;
|
|
8460
9315
|
const runs = await getEvalStore().getRunsByTenant(tenantId, { projectId: query.project_id, status: query.status });
|
|
8461
9316
|
reply.send({ success: true, message: "Ok", data: { records: runs, total: runs.length } });
|
|
@@ -8465,7 +9320,7 @@ function registerEvalRoutes(app2) {
|
|
|
8465
9320
|
});
|
|
8466
9321
|
app2.get("/api/eval/runs/:rid", async (request, reply) => {
|
|
8467
9322
|
try {
|
|
8468
|
-
const tenantId =
|
|
9323
|
+
const tenantId = getTenantId11(request);
|
|
8469
9324
|
const { rid } = request.params;
|
|
8470
9325
|
const run2 = await getEvalStore().getRunById(tenantId, rid);
|
|
8471
9326
|
if (!run2) return reply.status(404).send({ success: false, message: "Run not found" });
|
|
@@ -8476,7 +9331,7 @@ function registerEvalRoutes(app2) {
|
|
|
8476
9331
|
}
|
|
8477
9332
|
});
|
|
8478
9333
|
app2.get("/api/eval/runs/:rid/stream", async (request, reply) => {
|
|
8479
|
-
const tenantId =
|
|
9334
|
+
const tenantId = getTenantId11(request);
|
|
8480
9335
|
const { rid } = request.params;
|
|
8481
9336
|
const store = getEvalStore();
|
|
8482
9337
|
const ownedRun = await store.getRunById(tenantId, rid);
|
|
@@ -8552,7 +9407,7 @@ data: ${JSON.stringify(event.data)}
|
|
|
8552
9407
|
});
|
|
8553
9408
|
app2.post("/api/eval/runs/:rid/abort", async (request, reply) => {
|
|
8554
9409
|
try {
|
|
8555
|
-
const tenantId =
|
|
9410
|
+
const tenantId = getTenantId11(request);
|
|
8556
9411
|
const { rid } = request.params;
|
|
8557
9412
|
const run2 = await getEvalStore().getRunById(tenantId, rid);
|
|
8558
9413
|
if (!run2) return reply.status(404).send({ success: false, message: "Run not found" });
|
|
@@ -8565,7 +9420,7 @@ data: ${JSON.stringify(event.data)}
|
|
|
8565
9420
|
});
|
|
8566
9421
|
app2.delete("/api/eval/runs/:rid", async (request, reply) => {
|
|
8567
9422
|
try {
|
|
8568
|
-
const tenantId =
|
|
9423
|
+
const tenantId = getTenantId11(request);
|
|
8569
9424
|
const { rid } = request.params;
|
|
8570
9425
|
const store = getEvalStore();
|
|
8571
9426
|
const run2 = await store.getRunById(tenantId, rid);
|
|
@@ -8582,7 +9437,7 @@ data: ${JSON.stringify(event.data)}
|
|
|
8582
9437
|
});
|
|
8583
9438
|
app2.get("/api/eval/reports/projects/:pid", async (request, reply) => {
|
|
8584
9439
|
try {
|
|
8585
|
-
const tenantId =
|
|
9440
|
+
const tenantId = getTenantId11(request);
|
|
8586
9441
|
const { pid } = request.params;
|
|
8587
9442
|
const report = await getEvalStore().getProjectReport(tenantId, pid);
|
|
8588
9443
|
if (!report) return reply.status(404).send({ success: false, message: "Project not found" });
|
|
@@ -8594,11 +9449,11 @@ data: ${JSON.stringify(event.data)}
|
|
|
8594
9449
|
}
|
|
8595
9450
|
|
|
8596
9451
|
// src/controllers/users.ts
|
|
8597
|
-
import { getStoreLattice as
|
|
9452
|
+
import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
|
|
8598
9453
|
import { v4 as uuidv44 } from "uuid";
|
|
8599
9454
|
var UsersController = class {
|
|
8600
9455
|
constructor() {
|
|
8601
|
-
this.userStore =
|
|
9456
|
+
this.userStore = getStoreLattice12("default", "user").store;
|
|
8602
9457
|
}
|
|
8603
9458
|
async listUsers(request, reply) {
|
|
8604
9459
|
const { email } = request.query;
|
|
@@ -8676,11 +9531,11 @@ function registerUserRoutes(app2) {
|
|
|
8676
9531
|
}
|
|
8677
9532
|
|
|
8678
9533
|
// src/controllers/tenants.ts
|
|
8679
|
-
import { getStoreLattice as
|
|
9534
|
+
import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
|
|
8680
9535
|
import { v4 as uuidv45 } from "uuid";
|
|
8681
9536
|
var TenantsController = class {
|
|
8682
9537
|
constructor() {
|
|
8683
|
-
this.tenantStore =
|
|
9538
|
+
this.tenantStore = getStoreLattice13("default", "tenant").store;
|
|
8684
9539
|
}
|
|
8685
9540
|
// ==================== Tenant CRUD ====================
|
|
8686
9541
|
async listTenants(request, reply) {
|
|
@@ -8744,7 +9599,7 @@ function registerTenantRoutes(app2) {
|
|
|
8744
9599
|
}
|
|
8745
9600
|
|
|
8746
9601
|
// src/controllers/auth.ts
|
|
8747
|
-
import { getStoreLattice as
|
|
9602
|
+
import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
|
|
8748
9603
|
import { v4 as uuidv46 } from "uuid";
|
|
8749
9604
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
8750
9605
|
var defaultAuthConfig = {
|
|
@@ -8754,9 +9609,9 @@ var defaultAuthConfig = {
|
|
|
8754
9609
|
};
|
|
8755
9610
|
var AuthController = class {
|
|
8756
9611
|
constructor(config = {}) {
|
|
8757
|
-
this.userStore =
|
|
8758
|
-
this.tenantStore =
|
|
8759
|
-
this.userTenantLinkStore =
|
|
9612
|
+
this.userStore = getStoreLattice14("default", "user").store;
|
|
9613
|
+
this.tenantStore = getStoreLattice14("default", "tenant").store;
|
|
9614
|
+
this.userTenantLinkStore = getStoreLattice14("default", "userTenantLink").store;
|
|
8760
9615
|
this.config = { ...defaultAuthConfig, ...config };
|
|
8761
9616
|
}
|
|
8762
9617
|
async register(request, reply) {
|
|
@@ -8915,7 +9770,7 @@ var AuthController = class {
|
|
|
8915
9770
|
const linkMeta = link?.metadata || {};
|
|
8916
9771
|
let personalAssistant = null;
|
|
8917
9772
|
if (linkMeta.personalAssistantId) {
|
|
8918
|
-
const assistantStore =
|
|
9773
|
+
const assistantStore = getStoreLattice14("default", "assistant").store;
|
|
8919
9774
|
const exists = await assistantStore.hasAssistant(tenantId, linkMeta.personalAssistantId);
|
|
8920
9775
|
if (exists) {
|
|
8921
9776
|
personalAssistant = {
|
|
@@ -9170,7 +10025,7 @@ function isStaleConsoleSessionCredential(authHeader) {
|
|
|
9170
10025
|
function bypassesConsoleAuth(request) {
|
|
9171
10026
|
const path5 = request.url.split("?")[0];
|
|
9172
10027
|
const method = request.method;
|
|
9173
|
-
if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path5) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path5) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path5) || method === "GET" && /^\/embed\/[^/]+$/.test(path5) || (method === "POST" || method === "DELETE") && path5 === "/open/mcp" || method === "POST" && path5 === "/open/session" || (method === "GET" || method === "POST") && path5.startsWith("/open/sandbox/files/")) return true;
|
|
10028
|
+
if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path5) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path5) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path5) || method === "GET" && /^\/embed\/[^/]+$/.test(path5) || method === "GET" && path5 === "/mcp-apps/sandbox.html" || (method === "POST" || method === "DELETE") && path5 === "/open/mcp" || method === "POST" && path5 === "/open/session" || (method === "GET" || method === "POST") && path5.startsWith("/open/sandbox/files/")) return true;
|
|
9174
10029
|
if (request.method === "GET" && /^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path5)) {
|
|
9175
10030
|
return true;
|
|
9176
10031
|
}
|
|
@@ -9451,6 +10306,44 @@ function decryptLarkPayload(encryptKey, encryptedPayload) {
|
|
|
9451
10306
|
]).toString("utf8");
|
|
9452
10307
|
return JSON.parse(plaintext);
|
|
9453
10308
|
}
|
|
10309
|
+
function extractVerificationToken(body) {
|
|
10310
|
+
if (typeof body.token === "string") {
|
|
10311
|
+
return body.token;
|
|
10312
|
+
}
|
|
10313
|
+
if (typeof body.header?.token === "string") {
|
|
10314
|
+
return body.header.token;
|
|
10315
|
+
}
|
|
10316
|
+
return void 0;
|
|
10317
|
+
}
|
|
10318
|
+
function verifyLarkParsedBody(body, config) {
|
|
10319
|
+
if (!config.verificationToken) {
|
|
10320
|
+
return true;
|
|
10321
|
+
}
|
|
10322
|
+
return extractVerificationToken(body) === config.verificationToken;
|
|
10323
|
+
}
|
|
10324
|
+
function headerValue(request, name) {
|
|
10325
|
+
const value = request.headers[name];
|
|
10326
|
+
return Array.isArray(value) ? value[0] : value;
|
|
10327
|
+
}
|
|
10328
|
+
function verifyLarkRequestSignature(request, config) {
|
|
10329
|
+
if (!config.encryptKey) {
|
|
10330
|
+
return true;
|
|
10331
|
+
}
|
|
10332
|
+
const timestamp = headerValue(request, "x-lark-request-timestamp");
|
|
10333
|
+
const nonce = headerValue(request, "x-lark-request-nonce");
|
|
10334
|
+
const signature = headerValue(request, "x-lark-signature");
|
|
10335
|
+
if (!timestamp || !nonce || !signature) {
|
|
10336
|
+
return false;
|
|
10337
|
+
}
|
|
10338
|
+
const content = timestamp + nonce + config.encryptKey + JSON.stringify(request.body ?? {});
|
|
10339
|
+
const expected = crypto2.createHash("sha256").update(content).digest("hex");
|
|
10340
|
+
const expectedBuffer = Buffer.from(expected, "utf8");
|
|
10341
|
+
const actualBuffer = Buffer.from(signature, "utf8");
|
|
10342
|
+
if (actualBuffer.length !== expectedBuffer.length) {
|
|
10343
|
+
return false;
|
|
10344
|
+
}
|
|
10345
|
+
return crypto2.timingSafeEqual(actualBuffer, expectedBuffer);
|
|
10346
|
+
}
|
|
9454
10347
|
|
|
9455
10348
|
// src/channels/lark/controller.ts
|
|
9456
10349
|
var logger2 = new Logger({ serviceName: "lattice/gateway/lark" });
|
|
@@ -9463,11 +10356,21 @@ function createLarkEventHandler(deps) {
|
|
|
9463
10356
|
return;
|
|
9464
10357
|
}
|
|
9465
10358
|
const larkInstallation = installation;
|
|
9466
|
-
|
|
10359
|
+
let body;
|
|
10360
|
+
try {
|
|
10361
|
+
body = parseLarkRequestBody(request.body, larkInstallation.config.encryptKey);
|
|
10362
|
+
} catch {
|
|
10363
|
+
reply.status(400).send({ success: false, message: "Invalid request" });
|
|
10364
|
+
return;
|
|
10365
|
+
}
|
|
9467
10366
|
if (body.type === "url_verification" && body.challenge) {
|
|
9468
10367
|
reply.status(200).send({ challenge: body.challenge });
|
|
9469
10368
|
return;
|
|
9470
10369
|
}
|
|
10370
|
+
if (!verifyLarkParsedBody(body, larkInstallation.config) || !verifyLarkRequestSignature(request, larkInstallation.config)) {
|
|
10371
|
+
reply.status(403).send({ success: false, message: "Forbidden" });
|
|
10372
|
+
return;
|
|
10373
|
+
}
|
|
9471
10374
|
const inboundMessage = await larkChannelAdapter.receive(request.body, larkInstallation);
|
|
9472
10375
|
if (!inboundMessage) {
|
|
9473
10376
|
reply.status(200).send();
|
|
@@ -9559,7 +10462,7 @@ function registerChannelRoutes(app2, dependencies) {
|
|
|
9559
10462
|
}
|
|
9560
10463
|
|
|
9561
10464
|
// src/controllers/channel-installations.ts
|
|
9562
|
-
import { randomUUID as
|
|
10465
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
9563
10466
|
import {
|
|
9564
10467
|
descriptorDataValue
|
|
9565
10468
|
} from "@axiom-lattice/protocols";
|
|
@@ -9569,7 +10472,7 @@ function setChannelControllerDeps(deps) {
|
|
|
9569
10472
|
_adapterRegistry = deps.adapterRegistry;
|
|
9570
10473
|
_router = deps.router;
|
|
9571
10474
|
}
|
|
9572
|
-
function
|
|
10475
|
+
function getTenantId12(request) {
|
|
9573
10476
|
const userTenantId = request.user?.tenantId;
|
|
9574
10477
|
if (typeof userTenantId === "string" && userTenantId) {
|
|
9575
10478
|
return userTenantId;
|
|
@@ -9686,8 +10589,8 @@ function snapshotPlainObject(value) {
|
|
|
9686
10589
|
}
|
|
9687
10590
|
}
|
|
9688
10591
|
async function getInstallationStore() {
|
|
9689
|
-
const { getStoreLattice:
|
|
9690
|
-
const store =
|
|
10592
|
+
const { getStoreLattice: getStoreLattice29 } = await import("@axiom-lattice/core");
|
|
10593
|
+
const store = getStoreLattice29("default", "channelInstallation").store;
|
|
9691
10594
|
if (store) return store;
|
|
9692
10595
|
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
9693
10596
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -9699,7 +10602,7 @@ async function getInstallationStore() {
|
|
|
9699
10602
|
});
|
|
9700
10603
|
}
|
|
9701
10604
|
async function getChannelInstallationList(request) {
|
|
9702
|
-
const tenantId =
|
|
10605
|
+
const tenantId = getTenantId12(request);
|
|
9703
10606
|
const { channel } = request.query;
|
|
9704
10607
|
try {
|
|
9705
10608
|
const store = await getInstallationStore();
|
|
@@ -9725,7 +10628,7 @@ async function getChannelInstallationList(request) {
|
|
|
9725
10628
|
}
|
|
9726
10629
|
}
|
|
9727
10630
|
async function getChannelInstallation(request, reply) {
|
|
9728
|
-
const tenantId =
|
|
10631
|
+
const tenantId = getTenantId12(request);
|
|
9729
10632
|
const { installationId } = request.params;
|
|
9730
10633
|
try {
|
|
9731
10634
|
const store = await getInstallationStore();
|
|
@@ -9752,7 +10655,7 @@ async function getChannelInstallation(request, reply) {
|
|
|
9752
10655
|
}
|
|
9753
10656
|
}
|
|
9754
10657
|
async function createChannelInstallation(request, reply) {
|
|
9755
|
-
const tenantId =
|
|
10658
|
+
const tenantId = getTenantId12(request);
|
|
9756
10659
|
try {
|
|
9757
10660
|
const body = snapshotPublicCreateRequest(request.body);
|
|
9758
10661
|
if (!body) {
|
|
@@ -9763,7 +10666,7 @@ async function createChannelInstallation(request, reply) {
|
|
|
9763
10666
|
};
|
|
9764
10667
|
}
|
|
9765
10668
|
const store = await getInstallationStore();
|
|
9766
|
-
const installationId =
|
|
10669
|
+
const installationId = randomUUID11();
|
|
9767
10670
|
const createInput = {
|
|
9768
10671
|
channel: body.channel,
|
|
9769
10672
|
name: body.name,
|
|
@@ -9811,7 +10714,7 @@ async function createChannelInstallation(request, reply) {
|
|
|
9811
10714
|
}
|
|
9812
10715
|
}
|
|
9813
10716
|
async function updateChannelInstallation(request, reply) {
|
|
9814
|
-
const tenantId =
|
|
10717
|
+
const tenantId = getTenantId12(request);
|
|
9815
10718
|
const { installationId } = request.params;
|
|
9816
10719
|
try {
|
|
9817
10720
|
const store = await getInstallationStore();
|
|
@@ -9935,7 +10838,7 @@ async function updateChannelInstallation(request, reply) {
|
|
|
9935
10838
|
}
|
|
9936
10839
|
}
|
|
9937
10840
|
async function deleteChannelInstallation(request, reply) {
|
|
9938
|
-
const tenantId =
|
|
10841
|
+
const tenantId = getTenantId12(request);
|
|
9939
10842
|
const { installationId } = request.params;
|
|
9940
10843
|
try {
|
|
9941
10844
|
const store = await getInstallationStore();
|
|
@@ -10006,8 +10909,8 @@ import {
|
|
|
10006
10909
|
DuplicateChannelBindingSubjectError,
|
|
10007
10910
|
descriptorDataValue as descriptorDataValue2
|
|
10008
10911
|
} from "@axiom-lattice/protocols";
|
|
10009
|
-
import { getBindingRegistry, getStoreLattice as
|
|
10010
|
-
function
|
|
10912
|
+
import { getBindingRegistry, getStoreLattice as getStoreLattice15 } from "@axiom-lattice/core";
|
|
10913
|
+
function getTenantId13(request) {
|
|
10011
10914
|
const userTenantId = request.user?.tenantId;
|
|
10012
10915
|
if (typeof userTenantId === "string" && userTenantId) return userTenantId;
|
|
10013
10916
|
return request.headers["x-tenant-id"] || "default";
|
|
@@ -10064,7 +10967,7 @@ function snapshotPublicUpdate(value) {
|
|
|
10064
10967
|
return body;
|
|
10065
10968
|
}
|
|
10066
10969
|
function getInstallationStore2() {
|
|
10067
|
-
return
|
|
10970
|
+
return getStoreLattice15("default", "channelInstallation").store;
|
|
10068
10971
|
}
|
|
10069
10972
|
async function isValidPublicInstallation(tenantId, channelInstallationId, channel) {
|
|
10070
10973
|
const installation = await getInstallationStore2().getInstallationById(channelInstallationId);
|
|
@@ -10080,7 +10983,7 @@ function invalidCreateRequest(reply) {
|
|
|
10080
10983
|
}
|
|
10081
10984
|
async function getBindingList(request, _reply) {
|
|
10082
10985
|
void _reply;
|
|
10083
|
-
const tenantId =
|
|
10986
|
+
const tenantId = getTenantId13(request);
|
|
10084
10987
|
const { channel, agentId, channelInstallationId, limit, offset } = request.query;
|
|
10085
10988
|
try {
|
|
10086
10989
|
const registry = getBindingRegistry();
|
|
@@ -10100,7 +11003,7 @@ async function getBindingList(request, _reply) {
|
|
|
10100
11003
|
}
|
|
10101
11004
|
}
|
|
10102
11005
|
async function getBinding(request, reply) {
|
|
10103
|
-
const tenantId =
|
|
11006
|
+
const tenantId = getTenantId13(request);
|
|
10104
11007
|
try {
|
|
10105
11008
|
const registry = getBindingRegistry();
|
|
10106
11009
|
const binding = await registry.findById(tenantId, request.params.id);
|
|
@@ -10115,7 +11018,7 @@ async function getBinding(request, reply) {
|
|
|
10115
11018
|
}
|
|
10116
11019
|
}
|
|
10117
11020
|
async function createBinding(request, reply) {
|
|
10118
|
-
const tenantId =
|
|
11021
|
+
const tenantId = getTenantId13(request);
|
|
10119
11022
|
try {
|
|
10120
11023
|
const input = snapshotPublicCreate(request.body);
|
|
10121
11024
|
if (!input) return invalidCreateRequest(reply);
|
|
@@ -10140,7 +11043,7 @@ async function createBinding(request, reply) {
|
|
|
10140
11043
|
}
|
|
10141
11044
|
async function updateBinding(request, reply) {
|
|
10142
11045
|
try {
|
|
10143
|
-
const tenantId =
|
|
11046
|
+
const tenantId = getTenantId13(request);
|
|
10144
11047
|
const registry = getBindingRegistry();
|
|
10145
11048
|
const existing = await registry.findById(tenantId, request.params.id);
|
|
10146
11049
|
if (!existing || isInternalBinding(existing)) {
|
|
@@ -10167,7 +11070,7 @@ async function updateBinding(request, reply) {
|
|
|
10167
11070
|
}
|
|
10168
11071
|
async function deleteBinding(request, reply) {
|
|
10169
11072
|
try {
|
|
10170
|
-
const tenantId =
|
|
11073
|
+
const tenantId = getTenantId13(request);
|
|
10171
11074
|
const registry = getBindingRegistry();
|
|
10172
11075
|
const existing = await registry.findById(tenantId, request.params.id);
|
|
10173
11076
|
if (!existing || isInternalBinding(existing)) {
|
|
@@ -10184,7 +11087,7 @@ async function deleteBinding(request, reply) {
|
|
|
10184
11087
|
}
|
|
10185
11088
|
async function resolveBinding(request, _reply) {
|
|
10186
11089
|
void _reply;
|
|
10187
|
-
const tenantId =
|
|
11090
|
+
const tenantId = getTenantId13(request);
|
|
10188
11091
|
const { channel, senderId, channelInstallationId } = request.query;
|
|
10189
11092
|
if (channel === "room") {
|
|
10190
11093
|
return { success: false, message: "No binding found", data: null };
|
|
@@ -10214,7 +11117,7 @@ function registerChannelBindingRoutes(app2) {
|
|
|
10214
11117
|
|
|
10215
11118
|
// src/controllers/menu-items.ts
|
|
10216
11119
|
import { getMenuRegistry } from "@axiom-lattice/core";
|
|
10217
|
-
function
|
|
11120
|
+
function getTenantId14(request) {
|
|
10218
11121
|
const userTenantId = request.user?.tenantId;
|
|
10219
11122
|
if (userTenantId) return userTenantId;
|
|
10220
11123
|
return request.headers["x-tenant-id"] || "default";
|
|
@@ -10223,7 +11126,7 @@ function errorMessage(err) {
|
|
|
10223
11126
|
return err instanceof Error ? err.message : "Unexpected error";
|
|
10224
11127
|
}
|
|
10225
11128
|
async function getMenuItemList(request, _reply) {
|
|
10226
|
-
const tenantId =
|
|
11129
|
+
const tenantId = getTenantId14(request);
|
|
10227
11130
|
try {
|
|
10228
11131
|
const registry = getMenuRegistry();
|
|
10229
11132
|
const items = await registry.list({ tenantId, menuTarget: request.query.menuTarget });
|
|
@@ -10235,7 +11138,7 @@ async function getMenuItemList(request, _reply) {
|
|
|
10235
11138
|
}
|
|
10236
11139
|
}
|
|
10237
11140
|
async function getMenuItem(request, reply) {
|
|
10238
|
-
const tenantId =
|
|
11141
|
+
const tenantId = getTenantId14(request);
|
|
10239
11142
|
try {
|
|
10240
11143
|
const registry = getMenuRegistry();
|
|
10241
11144
|
const item = await registry.getById(request.params.id);
|
|
@@ -10252,7 +11155,7 @@ async function getMenuItem(request, reply) {
|
|
|
10252
11155
|
}
|
|
10253
11156
|
}
|
|
10254
11157
|
async function createMenuItem(request, reply) {
|
|
10255
|
-
const tenantId =
|
|
11158
|
+
const tenantId = getTenantId14(request);
|
|
10256
11159
|
try {
|
|
10257
11160
|
const registry = getMenuRegistry();
|
|
10258
11161
|
const item = await registry.create({ ...request.body, tenantId });
|
|
@@ -10267,7 +11170,7 @@ async function createMenuItem(request, reply) {
|
|
|
10267
11170
|
}
|
|
10268
11171
|
async function updateMenuItem(request, reply) {
|
|
10269
11172
|
try {
|
|
10270
|
-
const tenantId =
|
|
11173
|
+
const tenantId = getTenantId14(request);
|
|
10271
11174
|
const registry = getMenuRegistry();
|
|
10272
11175
|
const existing = await registry.getById(request.params.id);
|
|
10273
11176
|
if (!existing || existing.tenantId !== tenantId) {
|
|
@@ -10285,7 +11188,7 @@ async function updateMenuItem(request, reply) {
|
|
|
10285
11188
|
}
|
|
10286
11189
|
async function deleteMenuItem(request, reply) {
|
|
10287
11190
|
try {
|
|
10288
|
-
const tenantId =
|
|
11191
|
+
const tenantId = getTenantId14(request);
|
|
10289
11192
|
const registry = getMenuRegistry();
|
|
10290
11193
|
const existing = await registry.getById(request.params.id);
|
|
10291
11194
|
if (!existing || existing.tenantId !== tenantId) {
|
|
@@ -10564,11 +11467,11 @@ var registerSTTRoutes = (app2) => {
|
|
|
10564
11467
|
};
|
|
10565
11468
|
|
|
10566
11469
|
// src/controllers/local-a2a.ts
|
|
10567
|
-
import { getStoreLattice as
|
|
11470
|
+
import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
|
|
10568
11471
|
import {
|
|
10569
11472
|
AgentType as AgentType2
|
|
10570
11473
|
} from "@axiom-lattice/protocols";
|
|
10571
|
-
function
|
|
11474
|
+
function getTenantId15(request) {
|
|
10572
11475
|
const userTenantId = request.user?.tenantId;
|
|
10573
11476
|
if (userTenantId) return userTenantId;
|
|
10574
11477
|
return request.headers["x-tenant-id"] || "default";
|
|
@@ -10580,7 +11483,7 @@ function getLocalRuntime(assistant) {
|
|
|
10580
11483
|
return graphDefinition.localRuntime;
|
|
10581
11484
|
}
|
|
10582
11485
|
async function getLocalA2AAssistants(tenantId) {
|
|
10583
|
-
const storeLattice =
|
|
11486
|
+
const storeLattice = getStoreLattice16("default", "assistant");
|
|
10584
11487
|
const assistants = await storeLattice.store.getAllAssistants(tenantId);
|
|
10585
11488
|
const results = [];
|
|
10586
11489
|
for (const assistant of assistants) {
|
|
@@ -10592,7 +11495,7 @@ async function getLocalA2AAssistants(tenantId) {
|
|
|
10592
11495
|
return results;
|
|
10593
11496
|
}
|
|
10594
11497
|
async function getLocalA2AAssistant(tenantId, assistantId) {
|
|
10595
|
-
const storeLattice =
|
|
11498
|
+
const storeLattice = getStoreLattice16("default", "assistant");
|
|
10596
11499
|
const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
|
|
10597
11500
|
if (!assistant) return null;
|
|
10598
11501
|
const runtime = getLocalRuntime(assistant);
|
|
@@ -10605,7 +11508,7 @@ async function requireManagedAssistant(request, reply) {
|
|
|
10605
11508
|
reply.status(400).send({ success: false, error: "assistantId is required" });
|
|
10606
11509
|
return null;
|
|
10607
11510
|
}
|
|
10608
|
-
const registered = await getLocalA2AAssistant(
|
|
11511
|
+
const registered = await getLocalA2AAssistant(getTenantId15(request), assistantId);
|
|
10609
11512
|
if (!registered) {
|
|
10610
11513
|
reply.status(404).send({
|
|
10611
11514
|
success: false,
|
|
@@ -10616,7 +11519,7 @@ async function requireManagedAssistant(request, reply) {
|
|
|
10616
11519
|
return { assistantId, localRuntime: registered.localRuntime };
|
|
10617
11520
|
}
|
|
10618
11521
|
async function getLocalA2AStatus(request, reply) {
|
|
10619
|
-
const tenantId =
|
|
11522
|
+
const tenantId = getTenantId15(request);
|
|
10620
11523
|
const managed = await getLocalA2AAssistants(tenantId);
|
|
10621
11524
|
const states = await Promise.all(
|
|
10622
11525
|
managed.map(
|
|
@@ -10723,7 +11626,7 @@ async function getLocalA2ATemplates(_request, reply) {
|
|
|
10723
11626
|
}
|
|
10724
11627
|
|
|
10725
11628
|
// src/routes/a2a-test-connection.ts
|
|
10726
|
-
import { randomUUID as
|
|
11629
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
10727
11630
|
function describeTestConnectionError(error, agentCardUrl) {
|
|
10728
11631
|
if (!(error instanceof Error)) {
|
|
10729
11632
|
return `Failed to fetch agent card from ${agentCardUrl}: ${String(error)}`;
|
|
@@ -10763,7 +11666,7 @@ async function ensureLocalRuntimeForConnectionTest(localRuntime, agentCardUrl) {
|
|
|
10763
11666
|
return { error: portValidation.error };
|
|
10764
11667
|
}
|
|
10765
11668
|
const state = await localA2AService.startProvider(
|
|
10766
|
-
`test-${
|
|
11669
|
+
`test-${randomUUID12()}`,
|
|
10767
11670
|
testRuntime
|
|
10768
11671
|
);
|
|
10769
11672
|
if (state.status === "running") return { runtimeId: state.runtimeId };
|
|
@@ -10830,7 +11733,6 @@ function registerTaskRoutes(app2, controller) {
|
|
|
10830
11733
|
app2.delete("/api/tasks/:id", controller.remove);
|
|
10831
11734
|
app2.patch("/api/tasks/:id/complete", controller.complete);
|
|
10832
11735
|
app2.get("/api/tasks/:id/work-items", controller.workItems);
|
|
10833
|
-
app2.post("/api/projects/:projectId/tasks/:id/reassign", controller.reassign);
|
|
10834
11736
|
}
|
|
10835
11737
|
|
|
10836
11738
|
// src/routes/index.ts
|
|
@@ -10840,20 +11742,22 @@ import {
|
|
|
10840
11742
|
createProjectTaskReassignmentAuthorizer,
|
|
10841
11743
|
createTaskLifecycleService,
|
|
10842
11744
|
createTrustedAgentMessageDispatcher,
|
|
10843
|
-
|
|
11745
|
+
executionAuthorityRegistry,
|
|
11746
|
+
getStoreLattice as getStoreLattice17,
|
|
11747
|
+
ManagedTaskStartError,
|
|
10844
11748
|
modelLatticeManager as modelLatticeManager3,
|
|
10845
11749
|
ProjectTaskOwnerPolicy,
|
|
11750
|
+
ProjectTaskOwnerPolicyError as ProjectTaskOwnerPolicyError2,
|
|
10846
11751
|
projectMutationMutex as projectMutationMutex2,
|
|
10847
|
-
projectTaskAdmissionNotifier
|
|
11752
|
+
projectTaskAdmissionNotifier,
|
|
10848
11753
|
taskLifecycleNotifier as taskLifecycleNotifier2
|
|
10849
11754
|
} from "@axiom-lattice/core";
|
|
10850
11755
|
|
|
10851
11756
|
// src/controllers/tasks.ts
|
|
10852
11757
|
import { snapshotExactRecord as snapshotExactRecord3 } from "@axiom-lattice/protocols";
|
|
10853
|
-
import { ProjectTaskOwnerPolicyError
|
|
11758
|
+
import { ProjectTaskOwnerPolicyError } from "@axiom-lattice/core";
|
|
10854
11759
|
|
|
10855
11760
|
// src/services/collaboration/ProjectTaskThreadService.ts
|
|
10856
|
-
import { isProjectLifecycleTask } from "@axiom-lattice/core";
|
|
10857
11761
|
import {
|
|
10858
11762
|
snapshotExactRecord as snapshotExactRecord2
|
|
10859
11763
|
} from "@axiom-lattice/protocols";
|
|
@@ -11060,6 +11964,12 @@ function isTaskFileRef(value) {
|
|
|
11060
11964
|
return !!row && typeof row.uri === "string" && !!row.uri && (row.name === void 0 || typeof row.name === "string") && (row.mimeType === void 0 || typeof row.mimeType === "string") && (row.addedBy === void 0 || row.addedBy === "user" || row.addedBy === "agent");
|
|
11061
11965
|
}
|
|
11062
11966
|
|
|
11967
|
+
// src/services/collaboration/projectTaskScope.ts
|
|
11968
|
+
function isProjectLifecycleTask(task) {
|
|
11969
|
+
const source = task.context?.source;
|
|
11970
|
+
return typeof task.workspaceId === "string" && task.workspaceId.length > 0 && typeof task.projectId === "string" && task.projectId.length > 0 && (source === "project_room" || source === "project_task");
|
|
11971
|
+
}
|
|
11972
|
+
|
|
11063
11973
|
// src/services/collaboration/ProjectTaskThreadService.ts
|
|
11064
11974
|
var ProjectTaskThreadError = class extends Error {
|
|
11065
11975
|
/**
|
|
@@ -11236,20 +12146,7 @@ var protectedLifecycleKeys = /* @__PURE__ */ new Set([
|
|
|
11236
12146
|
"status",
|
|
11237
12147
|
"interruptedAt"
|
|
11238
12148
|
]);
|
|
11239
|
-
function
|
|
11240
|
-
try {
|
|
11241
|
-
void projectTaskAdmissionNotifier.publish({
|
|
11242
|
-
tenantId: task.tenantId,
|
|
11243
|
-
workspaceId: task.workspaceId,
|
|
11244
|
-
projectId: task.projectId,
|
|
11245
|
-
taskId: task.id,
|
|
11246
|
-
ownerId: task.ownerId,
|
|
11247
|
-
updatedAt: Date.prototype.toISOString.call(task.updatedAt)
|
|
11248
|
-
}).catch(() => void 0);
|
|
11249
|
-
} catch {
|
|
11250
|
-
}
|
|
11251
|
-
}
|
|
11252
|
-
function getTenantId15(request) {
|
|
12149
|
+
function getTenantId16(request) {
|
|
11253
12150
|
const user = request.user;
|
|
11254
12151
|
const value = user?.tenantId ?? request.headers["x-tenant-id"] ?? "default";
|
|
11255
12152
|
if (typeof value !== "string" || !value) throw new HttpTaskError(400, "INVALID_TASK_INPUT");
|
|
@@ -11358,13 +12255,6 @@ function snapshotComplete(value) {
|
|
|
11358
12255
|
if (row.result !== void 0 && typeof row.result !== "string") throw new HttpTaskError(400, "INVALID_TASK_INPUT");
|
|
11359
12256
|
return row;
|
|
11360
12257
|
}
|
|
11361
|
-
function snapshotReassign(value) {
|
|
11362
|
-
const row = exactObject(value, ["ownerId", "expectedThreadId"], ["ownerId"]);
|
|
11363
|
-
if (typeof row.ownerId !== "string" || !row.ownerId || row.expectedThreadId !== void 0 && (typeof row.expectedThreadId !== "string" || !row.expectedThreadId)) {
|
|
11364
|
-
throw new HttpTaskError(400, "INVALID_TASK_INPUT");
|
|
11365
|
-
}
|
|
11366
|
-
return row;
|
|
11367
|
-
}
|
|
11368
12258
|
function containsLifecycleField(value) {
|
|
11369
12259
|
if (!value || typeof value !== "object") return false;
|
|
11370
12260
|
if (Array.isArray(value)) return value.some(containsLifecycleField);
|
|
@@ -11379,18 +12269,6 @@ function governedCreate(body) {
|
|
|
11379
12269
|
}
|
|
11380
12270
|
return void 0;
|
|
11381
12271
|
}
|
|
11382
|
-
function governedUpdate(task, body) {
|
|
11383
|
-
const has = (key) => Object.prototype.hasOwnProperty.call(body, key);
|
|
11384
|
-
if (has("ownerType") && body.ownerType !== task.ownerType || has("ownerId") && body.ownerId !== task.ownerId) {
|
|
11385
|
-
return "TASK_OWNERSHIP_IMMUTABLE";
|
|
11386
|
-
}
|
|
11387
|
-
return void 0;
|
|
11388
|
-
}
|
|
11389
|
-
function lifecycleStatus(code) {
|
|
11390
|
-
if (code === "TASK_NOT_FOUND") return 404;
|
|
11391
|
-
if (code === "PROJECT_TASK_REASSIGNMENT_UNAUTHORIZED") return 403;
|
|
11392
|
-
return code.endsWith("_FAILED") ? 500 : 409;
|
|
11393
|
-
}
|
|
11394
12272
|
function notFound() {
|
|
11395
12273
|
return new HttpTaskError(404, "TASK_NOT_FOUND", "TASK_NOT_FOUND");
|
|
11396
12274
|
}
|
|
@@ -11490,7 +12368,7 @@ async function validateReferences(store, task, parentId, dependencies) {
|
|
|
11490
12368
|
function createTaskController(deps) {
|
|
11491
12369
|
const mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
|
|
11492
12370
|
const authorize = async (request, projectId, capability2) => deps.access.requireProjectAccess({
|
|
11493
|
-
tenantId:
|
|
12371
|
+
tenantId: getTenantId16(request),
|
|
11494
12372
|
userId: getUserId4(request),
|
|
11495
12373
|
projectId,
|
|
11496
12374
|
capability: capability2
|
|
@@ -11507,7 +12385,7 @@ function createTaskController(deps) {
|
|
|
11507
12385
|
});
|
|
11508
12386
|
};
|
|
11509
12387
|
const list = withErrors(async (request) => {
|
|
11510
|
-
const tenantId =
|
|
12388
|
+
const tenantId = getTenantId16(request);
|
|
11511
12389
|
const userId = getUserId4(request);
|
|
11512
12390
|
const query = exactObject(request.query, ["projectId", "status", "priority", "limit", "offset", "ownerType", "ownerId", "parentId", "metadata"]);
|
|
11513
12391
|
const projectId = typeof query.projectId === "string" ? query.projectId : void 0;
|
|
@@ -11579,14 +12457,14 @@ function createTaskController(deps) {
|
|
|
11579
12457
|
});
|
|
11580
12458
|
const get = withErrors(async (request, reply) => {
|
|
11581
12459
|
await preauthorizeExplicitProject(request, authorize);
|
|
11582
|
-
const current = await deps.tasks.getById(
|
|
12460
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11583
12461
|
if (!current) return sendNotFound(reply);
|
|
11584
12462
|
await authorizeRead(request, current, authorize);
|
|
11585
12463
|
return { success: true, data: current };
|
|
11586
12464
|
});
|
|
11587
12465
|
const create = withErrors(async (request, reply) => {
|
|
11588
12466
|
const body = snapshotCreate(request.body);
|
|
11589
|
-
const tenantId =
|
|
12467
|
+
const tenantId = getTenantId16(request);
|
|
11590
12468
|
const userId = getUserId4(request);
|
|
11591
12469
|
const governed = governedCreate(body);
|
|
11592
12470
|
if (governed) return reply.code(409).send({ success: false, code: governed });
|
|
@@ -11632,19 +12510,16 @@ function createTaskController(deps) {
|
|
|
11632
12510
|
requireReview: false,
|
|
11633
12511
|
context: { ...body.context ?? {}, source: "project_room" }
|
|
11634
12512
|
});
|
|
11635
|
-
if (ownerType === "agent") notifyProjectTaskAdmission(created);
|
|
11636
12513
|
return reply.code(201).send({ success: true, data: created });
|
|
11637
12514
|
});
|
|
11638
12515
|
});
|
|
11639
12516
|
const update = withErrors(async (request, reply) => {
|
|
11640
12517
|
const body = snapshotUpdate(request.body);
|
|
11641
12518
|
await preauthorizeExplicitProject(request, authorize);
|
|
11642
|
-
const current = await deps.tasks.getById(
|
|
12519
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11643
12520
|
if (!current) return sendNotFound(reply);
|
|
11644
12521
|
await authorizeMutation(request, current, authorize);
|
|
11645
12522
|
return lockedTask(request, current, async (locked) => {
|
|
11646
|
-
const governed = governedUpdate(locked, body);
|
|
11647
|
-
if (governed) return reply.code(409).send({ success: false, code: governed });
|
|
11648
12523
|
if (body.projectId !== void 0 && body.projectId !== locked.projectId || body.workspaceId !== void 0 && body.workspaceId !== locked.workspaceId) throw notFound();
|
|
11649
12524
|
await validateReferences(deps.tasks, locked, body.parentId, body.dependencies);
|
|
11650
12525
|
const updates = { ...body };
|
|
@@ -11657,7 +12532,7 @@ function createTaskController(deps) {
|
|
|
11657
12532
|
});
|
|
11658
12533
|
const remove = withErrors(async (request, reply) => {
|
|
11659
12534
|
await preauthorizeExplicitProject(request, authorize);
|
|
11660
|
-
const current = await deps.tasks.getById(
|
|
12535
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11661
12536
|
if (!current) return sendNotFound(reply);
|
|
11662
12537
|
await authorizeMutation(request, current, authorize);
|
|
11663
12538
|
return lockedTask(request, current, async (locked) => {
|
|
@@ -11671,7 +12546,7 @@ function createTaskController(deps) {
|
|
|
11671
12546
|
const complete = withErrors(async (request, reply) => {
|
|
11672
12547
|
const body = snapshotComplete(request.body);
|
|
11673
12548
|
await preauthorizeExplicitProject(request, authorize);
|
|
11674
|
-
const current = await deps.tasks.getById(
|
|
12549
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11675
12550
|
if (!current) return sendNotFound(reply);
|
|
11676
12551
|
await authorizeMutation(request, current, authorize);
|
|
11677
12552
|
return lockedTask(request, current, async (locked) => {
|
|
@@ -11688,7 +12563,7 @@ function createTaskController(deps) {
|
|
|
11688
12563
|
});
|
|
11689
12564
|
const workItems = withErrors(async (request, reply) => {
|
|
11690
12565
|
await preauthorizeExplicitProject(request, authorize);
|
|
11691
|
-
const current = await deps.tasks.getById(
|
|
12566
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11692
12567
|
if (!current) return sendNotFound(reply);
|
|
11693
12568
|
await authorizeRead(request, current, authorize);
|
|
11694
12569
|
const query = exactObject(request.query, ["action", "limit", "offset"]);
|
|
@@ -11704,66 +12579,7 @@ function createTaskController(deps) {
|
|
|
11704
12579
|
});
|
|
11705
12580
|
return reply.send({ success: true, data: rows });
|
|
11706
12581
|
});
|
|
11707
|
-
|
|
11708
|
-
const body = snapshotReassign(request.body);
|
|
11709
|
-
const pathProject = request.params.projectId;
|
|
11710
|
-
const access = await authorize(request, pathProject, "manage");
|
|
11711
|
-
const snapshot = await deps.tasks.getById(getTenantId15(request), request.params.id);
|
|
11712
|
-
if (!snapshot || snapshot.projectId !== access.project.id || snapshot.workspaceId !== access.project.workspaceId || snapshot.ownerType !== "agent") throw notFound();
|
|
11713
|
-
const projectId = snapshot.projectId;
|
|
11714
|
-
return deps.executionMutex.runExclusive({
|
|
11715
|
-
tenantId: snapshot.tenantId,
|
|
11716
|
-
assistantId: snapshot.ownerId,
|
|
11717
|
-
projectId
|
|
11718
|
-
}, async () => {
|
|
11719
|
-
const machineTask = await deps.tasks.getById(snapshot.tenantId, snapshot.id);
|
|
11720
|
-
if (!machineTask || machineTask.tenantId !== snapshot.tenantId || machineTask.projectId !== snapshot.projectId || machineTask.workspaceId !== snapshot.workspaceId || machineTask.ownerType !== "agent" || machineTask.ownerId !== snapshot.ownerId || machineTask.status !== snapshot.status || machineTask.updatedAt.getTime() !== snapshot.updatedAt.getTime()) {
|
|
11721
|
-
throw new HttpTaskError(409, "TASK_STATUS_CONFLICT");
|
|
11722
|
-
}
|
|
11723
|
-
return mutationMutex.runExclusive({ tenantId: snapshot.tenantId, projectId }, async () => {
|
|
11724
|
-
const lockedAccess = await authorize(request, pathProject, "manage");
|
|
11725
|
-
if (lockedAccess.project.id !== access.project.id || lockedAccess.project.workspaceId !== access.project.workspaceId) {
|
|
11726
|
-
throw new ProjectCollaborationError("PROJECT_ACCESS_DENIED", 403, "Project access denied");
|
|
11727
|
-
}
|
|
11728
|
-
const current = await deps.tasks.getById(snapshot.tenantId, snapshot.id);
|
|
11729
|
-
if (!current || current.tenantId !== snapshot.tenantId || current.projectId !== snapshot.projectId || current.workspaceId !== snapshot.workspaceId || current.ownerType !== "agent" || current.ownerId !== snapshot.ownerId || current.status !== snapshot.status || current.updatedAt.getTime() !== snapshot.updatedAt.getTime()) {
|
|
11730
|
-
throw new HttpTaskError(409, "TASK_STATUS_CONFLICT");
|
|
11731
|
-
}
|
|
11732
|
-
const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
|
|
11733
|
-
const target = await deps.bots.findByAssistant(current.tenantId, current.projectId, body.ownerId);
|
|
11734
|
-
if (!room || !target || target.status !== "active" || target.roomId !== room.id || target.workspaceId !== current.workspaceId) {
|
|
11735
|
-
return reply.code(409).send({ success: false, code: "PROJECT_TASK_OWNER_INACTIVE" });
|
|
11736
|
-
}
|
|
11737
|
-
const caller = await deps.bots.findByAssistant(current.tenantId, current.projectId, current.ownerId);
|
|
11738
|
-
if (!caller || caller.status !== "active" || caller.roomId !== room.id || caller.workspaceId !== current.workspaceId) {
|
|
11739
|
-
return reply.code(409).send({ success: false, code: "PROJECT_TASK_CALLER_INACTIVE" });
|
|
11740
|
-
}
|
|
11741
|
-
const ensured = await deps.threadService.ensure({ task: current, room, membership: target, mode: "reassignment" });
|
|
11742
|
-
const authorization = await deps.reassignmentAuthorizer.authorize({
|
|
11743
|
-
tenantId: current.tenantId,
|
|
11744
|
-
workspaceId: current.workspaceId,
|
|
11745
|
-
projectId: current.projectId,
|
|
11746
|
-
roomId: room.id,
|
|
11747
|
-
taskId: current.id,
|
|
11748
|
-
callerMembershipId: caller.id,
|
|
11749
|
-
callerAssistantId: caller.assistantId,
|
|
11750
|
-
targetMembershipId: target.id,
|
|
11751
|
-
targetAssistantId: target.assistantId,
|
|
11752
|
-
...body.expectedThreadId === void 0 ? {} : { expectedThreadId: body.expectedThreadId },
|
|
11753
|
-
newThreadId: ensured.thread.id
|
|
11754
|
-
});
|
|
11755
|
-
const result = await deps.lifecycle.reassignProjectTask({
|
|
11756
|
-
tenantId: current.tenantId,
|
|
11757
|
-
taskId: current.id,
|
|
11758
|
-
authorization,
|
|
11759
|
-
actor: `user:${getUserId4(request)}`
|
|
11760
|
-
});
|
|
11761
|
-
if (!result.success) return reply.code(lifecycleStatus(result.code)).send(result);
|
|
11762
|
-
return reply.send({ success: true, data: result.task, orphanCandidate: false });
|
|
11763
|
-
});
|
|
11764
|
-
});
|
|
11765
|
-
});
|
|
11766
|
-
return { list, get, create, update, remove, complete, workItems, reassign };
|
|
12582
|
+
return { list, get, create, update, remove, complete, workItems };
|
|
11767
12583
|
}
|
|
11768
12584
|
function parseMetadata(value) {
|
|
11769
12585
|
let parsed = value;
|
|
@@ -12082,7 +12898,7 @@ import { createHmac as createHmac3 } from "crypto";
|
|
|
12082
12898
|
import { z as z5, ZodError as ZodError2 } from "zod";
|
|
12083
12899
|
|
|
12084
12900
|
// src/services/agent-web-app/AgentWebAppRuntimeService.ts
|
|
12085
|
-
import { createHash as
|
|
12901
|
+
import { createHash as createHash3, randomUUID as randomUUID13 } from "crypto";
|
|
12086
12902
|
import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock } from "@axiom-lattice/protocols";
|
|
12087
12903
|
function isRecord2(value) {
|
|
12088
12904
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12276,10 +13092,10 @@ ${formatFileRefsSection(refs)}` : input.content;
|
|
|
12276
13092
|
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
|
|
12277
13093
|
this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
|
|
12278
13094
|
if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
|
|
12279
|
-
const userDigest =
|
|
12280
|
-
const contentDigest =
|
|
13095
|
+
const userDigest = createHash3("sha256").update(userId, "utf8").digest("hex");
|
|
13096
|
+
const contentDigest = createHash3("sha256").update(file.bytes).digest("hex");
|
|
12281
13097
|
const displayName = file.name;
|
|
12282
|
-
const physicalName = `${
|
|
13098
|
+
const physicalName = `${randomUUID13()}-${sanitizePhysicalFilename(file.name)}`;
|
|
12283
13099
|
const saved = await this.deps.saveProjectFile({
|
|
12284
13100
|
tenantId: runtime.webApp.tenantId,
|
|
12285
13101
|
workspaceId: runtime.project.workspaceId,
|
|
@@ -12431,7 +13247,7 @@ ${formatFileRefsSection(refs)}` : input.content;
|
|
|
12431
13247
|
const thread = await this.deps.threadStore.createThread(
|
|
12432
13248
|
runtime.webApp.tenantId,
|
|
12433
13249
|
runtime.webApp.assistantId,
|
|
12434
|
-
|
|
13250
|
+
randomUUID13(),
|
|
12435
13251
|
{ metadata: this.metadata(runtime, userId, normalizeLabel(rawLabel)) }
|
|
12436
13252
|
);
|
|
12437
13253
|
return toRuntimeThread(thread);
|
|
@@ -12483,7 +13299,7 @@ function implicitThreadId(runtime, userId) {
|
|
|
12483
13299
|
userId,
|
|
12484
13300
|
runtime.project.id
|
|
12485
13301
|
]);
|
|
12486
|
-
const bytes =
|
|
13302
|
+
const bytes = createHash3("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
|
|
12487
13303
|
bytes[6] = bytes[6] & 15 | 128;
|
|
12488
13304
|
bytes[8] = bytes[8] & 63 | 128;
|
|
12489
13305
|
const hex = bytes.toString("hex");
|
|
@@ -13251,7 +14067,7 @@ function registerAgentWebAppEmbedRoutes(app2, deps = {}) {
|
|
|
13251
14067
|
}
|
|
13252
14068
|
|
|
13253
14069
|
// src/services/agent-web-app/AgentWebAppAttachmentRef.ts
|
|
13254
|
-
import { createCipheriv, createDecipheriv, createHash as
|
|
14070
|
+
import { createCipheriv, createDecipheriv, createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
|
|
13255
14071
|
var TTL_MS = 15 * 6e4;
|
|
13256
14072
|
function resolveAgentWebAppAttachmentSecret(consoleSecret) {
|
|
13257
14073
|
return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
|
|
@@ -13260,7 +14076,7 @@ var AgentWebAppAttachmentRefService = class {
|
|
|
13260
14076
|
constructor(secret, now = Date.now) {
|
|
13261
14077
|
this.now = now;
|
|
13262
14078
|
if (!secret) throw new Error("Attachment signing secret is required");
|
|
13263
|
-
this.key =
|
|
14079
|
+
this.key = createHash4("sha256").update(secret, "utf8").digest();
|
|
13264
14080
|
}
|
|
13265
14081
|
sign(input) {
|
|
13266
14082
|
const payload = {
|
|
@@ -13322,7 +14138,7 @@ function decodeCanonical(value, expectedBytes) {
|
|
|
13322
14138
|
return decoded;
|
|
13323
14139
|
}
|
|
13324
14140
|
function digestUser(userId) {
|
|
13325
|
-
return
|
|
14141
|
+
return createHash4("sha256").update(userId, "utf8").digest("hex");
|
|
13326
14142
|
}
|
|
13327
14143
|
function isAttachmentPayload(value) {
|
|
13328
14144
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -13682,12 +14498,12 @@ function registerCapabilityBundleRoutes(app2, controller) {
|
|
|
13682
14498
|
}
|
|
13683
14499
|
|
|
13684
14500
|
// src/services/collaboration/ProjectRoomService.ts
|
|
13685
|
-
import { randomUUID as
|
|
14501
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
13686
14502
|
var ProjectRoomService = class {
|
|
13687
14503
|
/** Creates a project room service from explicitly injected collaborators. */
|
|
13688
14504
|
constructor(deps) {
|
|
13689
14505
|
this.deps = deps;
|
|
13690
|
-
this.idFactory = deps.idFactory ??
|
|
14506
|
+
this.idFactory = deps.idFactory ?? randomUUID14;
|
|
13691
14507
|
}
|
|
13692
14508
|
/**
|
|
13693
14509
|
* Authorizes read access and ensures the project's main room.
|
|
@@ -13747,7 +14563,7 @@ var ProjectRoomService = class {
|
|
|
13747
14563
|
};
|
|
13748
14564
|
|
|
13749
14565
|
// src/services/collaboration/ProjectMembershipService.ts
|
|
13750
|
-
import { randomUUID as
|
|
14566
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
13751
14567
|
var ProjectMembershipServiceError = class extends Error {
|
|
13752
14568
|
constructor(code, statusCode, message) {
|
|
13753
14569
|
super(message);
|
|
@@ -13763,7 +14579,7 @@ function isMembershipConflict(error) {
|
|
|
13763
14579
|
var ProjectMembershipService = class {
|
|
13764
14580
|
constructor(deps) {
|
|
13765
14581
|
this.deps = deps;
|
|
13766
|
-
this.idFactory = deps.idFactory ??
|
|
14582
|
+
this.idFactory = deps.idFactory ?? randomUUID15;
|
|
13767
14583
|
this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
|
|
13768
14584
|
}
|
|
13769
14585
|
async list(actor3) {
|
|
@@ -13897,7 +14713,7 @@ var ProjectMembershipService = class {
|
|
|
13897
14713
|
};
|
|
13898
14714
|
|
|
13899
14715
|
// src/services/collaboration/ProjectRosterService.ts
|
|
13900
|
-
import { randomUUID as
|
|
14716
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
13901
14717
|
var ProjectRosterServiceError = class extends Error {
|
|
13902
14718
|
/** Creates a stable roster service error. */
|
|
13903
14719
|
constructor(code, statusCode, message) {
|
|
@@ -13916,7 +14732,7 @@ var ProjectRosterService = class {
|
|
|
13916
14732
|
/** Creates a roster service from explicitly injected collaborators. */
|
|
13917
14733
|
constructor(deps) {
|
|
13918
14734
|
this.deps = deps;
|
|
13919
|
-
this.idFactory = deps.idFactory ??
|
|
14735
|
+
this.idFactory = deps.idFactory ?? randomUUID16;
|
|
13920
14736
|
this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
|
|
13921
14737
|
}
|
|
13922
14738
|
/** Lists all bot memberships visible to a project reader. */
|
|
@@ -14150,7 +14966,7 @@ var ProjectRosterService = class {
|
|
|
14150
14966
|
};
|
|
14151
14967
|
|
|
14152
14968
|
// src/services/collaboration/RoomMessageService.ts
|
|
14153
|
-
import { createHash as
|
|
14969
|
+
import { createHash as createHash5, randomUUID as randomUUID17 } from "crypto";
|
|
14154
14970
|
|
|
14155
14971
|
// src/services/collaboration/ProjectBotMembershipSnapshot.ts
|
|
14156
14972
|
import { snapshotExactRecord as snapshotExactRecord4 } from "@axiom-lattice/protocols";
|
|
@@ -14239,7 +15055,7 @@ var RoomMessageService = class {
|
|
|
14239
15055
|
constructor(deps, options = {}) {
|
|
14240
15056
|
this.deps = deps;
|
|
14241
15057
|
this.idempotentOutcomes = /* @__PURE__ */ new Map();
|
|
14242
|
-
this.idFactory = deps.idFactory ??
|
|
15058
|
+
this.idFactory = deps.idFactory ?? randomUUID17;
|
|
14243
15059
|
const limit = options.idempotentOutcomeLimit ?? 1e3;
|
|
14244
15060
|
if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("idempotentOutcomeLimit must be a positive safe integer");
|
|
14245
15061
|
this.idempotentOutcomeLimit = limit;
|
|
@@ -14420,7 +15236,7 @@ var RoomMessageService = class {
|
|
|
14420
15236
|
let idempotencyKey;
|
|
14421
15237
|
if (value.requestIdempotencyKey !== void 0) {
|
|
14422
15238
|
if (typeof value.requestIdempotencyKey !== "string" || !/^[\x21-\x7e]{1,200}$/.test(value.requestIdempotencyKey.trim())) throw new RoomMessageServiceError("INVALID_MESSAGE", 400, "Invalid idempotency key");
|
|
14423
|
-
idempotencyKey = `room-human:${actor3.userId}:${
|
|
15239
|
+
idempotencyKey = `room-human:${actor3.userId}:${createHash5("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
|
|
14424
15240
|
}
|
|
14425
15241
|
return { text, mentions, botIds, ...value.replyToMessageId === void 0 ? {} : { replyToMessageId: value.replyToMessageId }, ...idempotencyKey === void 0 ? {} : { idempotencyKey } };
|
|
14426
15242
|
}
|
|
@@ -14477,7 +15293,7 @@ function cloneRoomMessage(message) {
|
|
|
14477
15293
|
};
|
|
14478
15294
|
}
|
|
14479
15295
|
function fingerprintRequest(input) {
|
|
14480
|
-
return
|
|
15296
|
+
return createHash5("sha256").update(JSON.stringify({
|
|
14481
15297
|
text: input.text,
|
|
14482
15298
|
mentions: input.mentions,
|
|
14483
15299
|
replyToMessageId: input.replyToMessageId ?? null
|
|
@@ -14492,7 +15308,7 @@ import { ZodError as ZodError3 } from "zod";
|
|
|
14492
15308
|
import { toProjectRoomPublicMessage } from "@axiom-lattice/protocols";
|
|
14493
15309
|
|
|
14494
15310
|
// src/services/collaboration/RoomInterruptService.ts
|
|
14495
|
-
import { randomUUID as
|
|
15311
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
14496
15312
|
|
|
14497
15313
|
// src/router/roomInterrupt.ts
|
|
14498
15314
|
var CLARIFY_FENCE = /^```clarify\n([\s\S]*?)\n```$/;
|
|
@@ -14540,7 +15356,7 @@ var RoomInterruptService = class {
|
|
|
14540
15356
|
/** Creates the service from explicitly injected stores and dispatcher. */
|
|
14541
15357
|
constructor(deps) {
|
|
14542
15358
|
this.deps = deps;
|
|
14543
|
-
this.idFactory = deps.idFactory ??
|
|
15359
|
+
this.idFactory = deps.idFactory ?? randomUUID18;
|
|
14544
15360
|
}
|
|
14545
15361
|
/** Resolves a pending clarify interrupt with no answers. */
|
|
14546
15362
|
async skip(input) {
|
|
@@ -14588,6 +15404,7 @@ var RoomInterruptService = class {
|
|
|
14588
15404
|
inputMessageId,
|
|
14589
15405
|
role: membership.role,
|
|
14590
15406
|
title: membership.title,
|
|
15407
|
+
source: "project_room",
|
|
14591
15408
|
...membership.responsibility === void 0 ? {} : { responsibility: membership.responsibility }
|
|
14592
15409
|
}
|
|
14593
15410
|
}
|
|
@@ -14795,7 +15612,7 @@ async function execute(reply, operation) {
|
|
|
14795
15612
|
}
|
|
14796
15613
|
|
|
14797
15614
|
// src/routes/cospace.ts
|
|
14798
|
-
import { randomUUID as
|
|
15615
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
14799
15616
|
import { ZodError as ZodError5 } from "zod";
|
|
14800
15617
|
import {
|
|
14801
15618
|
generateDirectHumanProjectId,
|
|
@@ -14829,7 +15646,7 @@ async function computeUnread(deps, tenantId, userId, roomId, lastMessage) {
|
|
|
14829
15646
|
});
|
|
14830
15647
|
}
|
|
14831
15648
|
function registerCoSpaceRoutes(app2, deps) {
|
|
14832
|
-
const idFactory = deps.idFactory ?? (() =>
|
|
15649
|
+
const idFactory = deps.idFactory ?? (() => randomUUID19());
|
|
14833
15650
|
app2.get(
|
|
14834
15651
|
"/api/workspaces/:workspaceId/rooms",
|
|
14835
15652
|
async (request, reply) => {
|
|
@@ -15713,6 +16530,7 @@ var RoomDispatchService = class {
|
|
|
15713
16530
|
inputMessageId: "pending-router-input",
|
|
15714
16531
|
role: membership.role,
|
|
15715
16532
|
title: membership.title,
|
|
16533
|
+
source: "project_room",
|
|
15716
16534
|
...membership.responsibility === void 0 ? {} : { responsibility: membership.responsibility }
|
|
15717
16535
|
} }
|
|
15718
16536
|
}
|
|
@@ -15784,8 +16602,7 @@ function exactValues(value, required, optional = []) {
|
|
|
15784
16602
|
}
|
|
15785
16603
|
|
|
15786
16604
|
// src/services/collaboration/CoordinatorWakeNotifier.ts
|
|
15787
|
-
import { randomUUID as
|
|
15788
|
-
import { isProjectLifecycleTask as isProjectLifecycleTask2 } from "@axiom-lattice/core";
|
|
16605
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
15789
16606
|
import {
|
|
15790
16607
|
snapshotExactRecord as snapshotExactRecord6
|
|
15791
16608
|
} from "@axiom-lattice/protocols";
|
|
@@ -15801,7 +16618,7 @@ var CoordinatorWakeNotifier = class {
|
|
|
15801
16618
|
this.accepted = /* @__PURE__ */ new Set();
|
|
15802
16619
|
this.maxPending = deps.maxPending ?? 1024;
|
|
15803
16620
|
if (!Number.isSafeInteger(this.maxPending) || this.maxPending < 1) throw new RangeError("maxPending must be a positive safe integer");
|
|
15804
|
-
this.idFactory = deps.idFactory ??
|
|
16621
|
+
this.idFactory = deps.idFactory ?? randomUUID20;
|
|
15805
16622
|
}
|
|
15806
16623
|
/** Admits one notification without awaiting any store or dispatch operation. */
|
|
15807
16624
|
handle(value, anchorMessageId) {
|
|
@@ -15873,7 +16690,7 @@ var CoordinatorWakeNotifier = class {
|
|
|
15873
16690
|
try {
|
|
15874
16691
|
const task = snapshotTaskItem(await this.deps.tasks.getById(notification.tenantId, notification.taskId));
|
|
15875
16692
|
if (this.closed) return;
|
|
15876
|
-
if (!task || !
|
|
16693
|
+
if (!task || !isProjectLifecycleTask(task) || task.tenantId !== notification.tenantId || task.workspaceId !== notification.workspaceId || task.projectId !== notification.projectId || task.status !== notification.status || task.updatedAt.toISOString() !== notification.updatedAt || notification.threadId !== void 0 && task.context?.thread_id !== notification.threadId) {
|
|
15877
16694
|
this.warn("project-room:coordinator-wake-stale", { taskId: safeId(notification.taskId), eventKey: safeId(notification.eventKey) });
|
|
15878
16695
|
return;
|
|
15879
16696
|
}
|
|
@@ -16156,10 +16973,10 @@ var ProjectTaskExecutionMutex = class {
|
|
|
16156
16973
|
};
|
|
16157
16974
|
|
|
16158
16975
|
// src/services/collaboration/ProjectTaskExecutionService.ts
|
|
16159
|
-
import { randomUUID as
|
|
16976
|
+
import { randomUUID as randomUUID21 } from "crypto";
|
|
16160
16977
|
import {
|
|
16978
|
+
createTaskRunBridge,
|
|
16161
16979
|
ThreadStatus as ThreadStatus3,
|
|
16162
|
-
isProjectLifecycleTask as isProjectLifecycleTask3,
|
|
16163
16980
|
parseExternalDependency
|
|
16164
16981
|
} from "@axiom-lattice/core";
|
|
16165
16982
|
import {
|
|
@@ -16193,6 +17010,7 @@ var ProjectTaskExecutionService = class {
|
|
|
16193
17010
|
if (!Number.isFinite(options.lifecycleSettlementTimeoutMs) || options.lifecycleSettlementTimeoutMs <= 0) {
|
|
16194
17011
|
throw new Error("Project task lifecycle settlement timeout must be positive");
|
|
16195
17012
|
}
|
|
17013
|
+
this.runBridge = createTaskRunBridge({ lifecycle: this.deps.lifecycle });
|
|
16196
17014
|
}
|
|
16197
17015
|
/** Validates, atomically starts, dispatches, and settles a pending Project Task. */
|
|
16198
17016
|
async start(input, signal) {
|
|
@@ -16200,24 +17018,24 @@ var ProjectTaskExecutionService = class {
|
|
|
16200
17018
|
this.throwIfCancelled(signal);
|
|
16201
17019
|
const ensured = await this.deps.threads.ensure({ ...validated, mode: "current_owner" });
|
|
16202
17020
|
this.throwIfCancelled(signal);
|
|
16203
|
-
const
|
|
17021
|
+
const begun = await this.runBridge.beginRun({
|
|
16204
17022
|
tenantId: input.tenantId,
|
|
16205
|
-
|
|
17023
|
+
task: validated.task,
|
|
16206
17024
|
actor: SYSTEM_ACTOR,
|
|
16207
17025
|
threadId: ensured.thread.id
|
|
16208
17026
|
});
|
|
16209
|
-
if (!
|
|
16210
|
-
if (signal?.aborted && (!
|
|
16211
|
-
if (!
|
|
17027
|
+
if (!begun.success) throw new ProjectTaskExecutionError(begun.code, begun.error);
|
|
17028
|
+
if (signal?.aborted && (!begun.transitioned || !begun.mutated)) this.throwIfCancelled(signal);
|
|
17029
|
+
if (!begun.transitioned || !begun.mutated) {
|
|
16212
17030
|
throw new ProjectTaskExecutionError("PROJECT_TASK_START_CONFLICT", "Another caller already started this task");
|
|
16213
17031
|
}
|
|
16214
17032
|
if (signal?.aborted) return this.compensateCancellation(
|
|
16215
|
-
|
|
17033
|
+
begun.task,
|
|
16216
17034
|
ensured.thread,
|
|
16217
|
-
`project-task:${
|
|
17035
|
+
`project-task:${begun.task.id}:${ensured.thread.id}:start`,
|
|
16218
17036
|
false
|
|
16219
17037
|
);
|
|
16220
|
-
const current = await this.requireCurrentThread(
|
|
17038
|
+
const current = await this.requireCurrentThread(begun.task, ensured.thread);
|
|
16221
17039
|
if (signal?.aborted) return this.compensateCancellation(
|
|
16222
17040
|
current,
|
|
16223
17041
|
ensured.thread,
|
|
@@ -16360,7 +17178,7 @@ var ProjectTaskExecutionService = class {
|
|
|
16360
17178
|
}
|
|
16361
17179
|
async requireTask(input) {
|
|
16362
17180
|
const value = snapshotTaskItem(await this.deps.tasks.getById(input.tenantId, input.taskId));
|
|
16363
|
-
if (!value || value.tenantId !== input.tenantId || value.id !== input.taskId || !
|
|
17181
|
+
if (!value || value.tenantId !== input.tenantId || value.id !== input.taskId || !isProjectLifecycleTask(value)) {
|
|
16364
17182
|
throw new ProjectTaskExecutionError("PROJECT_TASK_NOT_READY", "Project task does not exist or is out of scope");
|
|
16365
17183
|
}
|
|
16366
17184
|
return value;
|
|
@@ -16424,7 +17242,7 @@ var ProjectTaskExecutionService = class {
|
|
|
16424
17242
|
return current;
|
|
16425
17243
|
}
|
|
16426
17244
|
async dispatchRun(task, thread, mode, signal) {
|
|
16427
|
-
const messageId = (this.options.idFactory ??
|
|
17245
|
+
const messageId = (this.options.idFactory ?? randomUUID21)();
|
|
16428
17246
|
if (signal?.aborted) return this.compensateCancellation(task, thread, messageId, false);
|
|
16429
17247
|
const settlement = this.armSettlement(task, thread, messageId);
|
|
16430
17248
|
try {
|
|
@@ -16618,13 +17436,12 @@ var ProjectTaskExecutionService = class {
|
|
|
16618
17436
|
const summary = clarify.questions.find(
|
|
16619
17437
|
(question) => typeof question.question === "string" && question.question.trim().length > 0
|
|
16620
17438
|
)?.question.trim() ?? "Waiting for clarification from the room";
|
|
16621
|
-
const result = await this.withLifecycleTimeout(this.
|
|
17439
|
+
const result = await this.withLifecycleTimeout(this.runBridge.settleRun({
|
|
16622
17440
|
tenantId: task.tenantId,
|
|
16623
|
-
|
|
16624
|
-
type: "missing_input",
|
|
16625
|
-
summary,
|
|
17441
|
+
task,
|
|
16626
17442
|
actor: SYSTEM_ACTOR,
|
|
16627
|
-
threadId: thread.id
|
|
17443
|
+
threadId: thread.id,
|
|
17444
|
+
outcome: { kind: "interrupted", summary }
|
|
16628
17445
|
}), void 0);
|
|
16629
17446
|
if (!result || !result.success) return void 0;
|
|
16630
17447
|
return result.task ?? await this.reloadTask(task);
|
|
@@ -16686,12 +17503,12 @@ var ProjectTaskExecutionService = class {
|
|
|
16686
17503
|
}
|
|
16687
17504
|
let failed;
|
|
16688
17505
|
try {
|
|
16689
|
-
failed = await this.
|
|
17506
|
+
failed = await this.runBridge.settleRun({
|
|
16690
17507
|
tenantId: task.tenantId,
|
|
16691
|
-
|
|
16692
|
-
failureReason: error,
|
|
17508
|
+
task,
|
|
16693
17509
|
actor: SYSTEM_ACTOR,
|
|
16694
|
-
threadId: thread.id
|
|
17510
|
+
threadId: thread.id,
|
|
17511
|
+
outcome: { kind: "failed", lifecycleHandled: false, error }
|
|
16695
17512
|
});
|
|
16696
17513
|
} catch {
|
|
16697
17514
|
return { kind: "failed", task: current, thread, messageId, error, cause, lifecycleHandled: false };
|
|
@@ -16752,7 +17569,8 @@ function buildTrustedTaskContext(task, thread, inputMessageId) {
|
|
|
16752
17569
|
assistantId: task.ownerId,
|
|
16753
17570
|
taskId: task.id,
|
|
16754
17571
|
threadId: thread.id,
|
|
16755
|
-
inputMessageId
|
|
17572
|
+
inputMessageId,
|
|
17573
|
+
source: "project_task"
|
|
16756
17574
|
};
|
|
16757
17575
|
}
|
|
16758
17576
|
function taskThreadMetadata(thread) {
|
|
@@ -16806,9 +17624,9 @@ function validRecoveryRow(row, task, thread) {
|
|
|
16806
17624
|
}
|
|
16807
17625
|
|
|
16808
17626
|
// src/services/collaboration/ProjectTaskObserver.ts
|
|
16809
|
-
import { createHash as
|
|
17627
|
+
import { createHash as createHash6 } from "crypto";
|
|
16810
17628
|
import {
|
|
16811
|
-
|
|
17629
|
+
createTaskRunBridge as createTaskRunBridge2,
|
|
16812
17630
|
parseExternalDependency as parseExternalDependency2
|
|
16813
17631
|
} from "@axiom-lattice/core";
|
|
16814
17632
|
import {
|
|
@@ -16835,6 +17653,7 @@ var ProjectTaskObserver = class {
|
|
|
16835
17653
|
throw new Error(`Invalid Project task observer bound: ${name}`);
|
|
16836
17654
|
}
|
|
16837
17655
|
}
|
|
17656
|
+
this.runBridge = createTaskRunBridge2({ lifecycle: this.deps.lifecycle });
|
|
16838
17657
|
}
|
|
16839
17658
|
/** Fire-and-forget notifier adapter that never waits for observer work or machine acquisition. */
|
|
16840
17659
|
async listener(event) {
|
|
@@ -16890,15 +17709,15 @@ var ProjectTaskObserver = class {
|
|
|
16890
17709
|
if (signal?.aborted) continue;
|
|
16891
17710
|
const eventTime = Date.parse(hint.event.updatedAt);
|
|
16892
17711
|
if (task && task.status === "pending" && task.ownerType === "agent" && task.workspaceId === hint.event.workspaceId && task.projectId === hint.event.projectId && task.ownerId === hint.event.ownerId && task.updatedAt.getTime() >= eventTime) {
|
|
16893
|
-
await this.reconcileTask(task, signal);
|
|
17712
|
+
await this.reconcileTask(task, signal, "dispatch");
|
|
16894
17713
|
}
|
|
16895
17714
|
}
|
|
16896
17715
|
}
|
|
16897
17716
|
/** Awaits one notification for tests, startup repair, or other non-notifier callers. */
|
|
16898
17717
|
async notify(event) {
|
|
16899
17718
|
const task = snapshotTaskItem(await this.deps.tasks.getById(event.tenantId, event.taskId));
|
|
17719
|
+
if (!task || !isProjectLifecycleTask(task) || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId) return;
|
|
16900
17720
|
const item = await this.deps.workItems.findByEventKey(event.tenantId, event.taskId, event.eventKey);
|
|
16901
|
-
if (!task || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId) return;
|
|
16902
17721
|
const matches = item !== null && item !== void 0 && item.eventKey === event.eventKey && item.action === event.action && item.taskId === event.taskId && item.tenantId === event.tenantId && item.workspaceId === event.workspaceId && item.projectId === event.projectId && item.threadId === event.threadId;
|
|
16903
17722
|
let anchorMessageId;
|
|
16904
17723
|
if (matches && task.status === event.status && task.updatedAt.toISOString() === event.updatedAt && (event.threadId === void 0 || task.context?.thread_id === event.threadId)) {
|
|
@@ -16956,7 +17775,11 @@ var ProjectTaskObserver = class {
|
|
|
16956
17775
|
return result;
|
|
16957
17776
|
}
|
|
16958
17777
|
result.taskTruncated || (result.taskTruncated = pass.truncated);
|
|
16959
|
-
if (pass.truncated) this.deps.log.warn("Project task startup scan truncated", {
|
|
17778
|
+
if (pass.truncated) this.deps.log.warn("Project task startup scan truncated", {
|
|
17779
|
+
code: "PROJECT_TASK_STARTUP_SCAN_TRUNCATED",
|
|
17780
|
+
tenantId: tenant2.id,
|
|
17781
|
+
status
|
|
17782
|
+
});
|
|
16960
17783
|
for (const scope of pass.scopes) discovered.set(scopeKey(scope), scope);
|
|
16961
17784
|
for (const id3 of pass.ids) {
|
|
16962
17785
|
if (signal?.aborted || this.disposed) {
|
|
@@ -16978,16 +17801,44 @@ var ProjectTaskObserver = class {
|
|
|
16978
17801
|
return result;
|
|
16979
17802
|
}
|
|
16980
17803
|
await this.repairProjects(selected, discovered, result, signal);
|
|
17804
|
+
if (result.taskTruncated || result.projectEnumerationTruncated || result.projectionTruncated) {
|
|
17805
|
+
this.deps.log.warn("Project task startup reconciliation truncated", {
|
|
17806
|
+
code: "PROJECT_TASK_STARTUP_RECONCILIATION_TRUNCATED",
|
|
17807
|
+
taskTruncated: result.taskTruncated,
|
|
17808
|
+
projectEnumerationTruncated: result.projectEnumerationTruncated,
|
|
17809
|
+
projectionTruncated: result.projectionTruncated
|
|
17810
|
+
});
|
|
17811
|
+
}
|
|
16981
17812
|
return result;
|
|
16982
17813
|
}
|
|
16983
|
-
/** Reconciles one persisted Agent-owned Project Task
|
|
16984
|
-
async reconcileTask(task, signal) {
|
|
17814
|
+
/** Reconciles one persisted Agent-owned Project Task under an explicit intent. */
|
|
17815
|
+
async reconcileTask(task, signal, intent = "converge") {
|
|
16985
17816
|
signal ?? (signal = this.deps.signal);
|
|
16986
17817
|
if (signal?.aborted || this.disposed) return;
|
|
16987
|
-
if (!
|
|
17818
|
+
if (!isProjectLifecycleTask(task) || task.ownerType !== "agent") return;
|
|
16988
17819
|
if (await this.belongsToLearningRound(task)) return;
|
|
16989
17820
|
if (task.status === "pending") {
|
|
16990
|
-
if (
|
|
17821
|
+
if (signal?.aborted || this.disposed) return;
|
|
17822
|
+
const dependencies = await this.dependencyState(task);
|
|
17823
|
+
if (signal?.aborted || this.disposed) return;
|
|
17824
|
+
if (dependencies === "unmet") {
|
|
17825
|
+
await this.convergeDependencyUnmet(task);
|
|
17826
|
+
return;
|
|
17827
|
+
}
|
|
17828
|
+
if (dependencies === "wait" && await this.hasDependencyCycle(task)) {
|
|
17829
|
+
await this.convergeDependencyCycle(task);
|
|
17830
|
+
return;
|
|
17831
|
+
}
|
|
17832
|
+
if (dependencies !== "ready") return;
|
|
17833
|
+
try {
|
|
17834
|
+
await this.requireActiveOwner(task);
|
|
17835
|
+
} catch (error) {
|
|
17836
|
+
await this.convergeInterrupted(task, `No active executor: ${errorMessage3(error)}`);
|
|
17837
|
+
return;
|
|
17838
|
+
}
|
|
17839
|
+
if (intent === "dispatch" && await this.isReady(task) && !signal?.aborted && !this.disposed) {
|
|
17840
|
+
await this.runStart(task, signal);
|
|
17841
|
+
}
|
|
16991
17842
|
return;
|
|
16992
17843
|
}
|
|
16993
17844
|
if (task.status === "in_progress") {
|
|
@@ -17099,20 +17950,13 @@ var ProjectTaskObserver = class {
|
|
|
17099
17950
|
const classification = await this.deps.execution.classifyRecovery(current, ensured.thread);
|
|
17100
17951
|
if (signal?.aborted || this.disposed) return;
|
|
17101
17952
|
if (classification.kind === "invalid") {
|
|
17102
|
-
await this.
|
|
17953
|
+
await this.convergeInterrupted(
|
|
17954
|
+
current,
|
|
17955
|
+
`Task execution state is not recoverable: ${classification.reason}`
|
|
17956
|
+
);
|
|
17103
17957
|
return;
|
|
17104
17958
|
}
|
|
17105
17959
|
if (classification.kind === "missing") {
|
|
17106
|
-
const exact2 = snapshotTaskItem(await this.deps.tasks.getById(current.tenantId, current.id));
|
|
17107
|
-
if (!exact2 || exact2.status !== "in_progress" || exact2.context?.thread_id !== ensured.thread.id) return;
|
|
17108
|
-
const failed = await this.deps.lifecycle.failTask({
|
|
17109
|
-
tenantId: current.tenantId,
|
|
17110
|
-
taskId: current.id,
|
|
17111
|
-
actor: SYSTEM_ACTOR2,
|
|
17112
|
-
threadId: ensured.thread.id,
|
|
17113
|
-
failureReason: "Task execution queue state was not recoverable"
|
|
17114
|
-
});
|
|
17115
|
-
await this.checkLifecycle(current, "queue-fail", failed);
|
|
17116
17960
|
return;
|
|
17117
17961
|
}
|
|
17118
17962
|
const recovery = {
|
|
@@ -17185,46 +18029,45 @@ var ProjectTaskObserver = class {
|
|
|
17185
18029
|
if (!current || current.ownerId !== outcome.thread.assistantId || current.context?.thread_id !== outcome.thread.id) return;
|
|
17186
18030
|
if (isTerminal(current.status) || current.status === "review") return;
|
|
17187
18031
|
if (outcome.kind === "completed") {
|
|
17188
|
-
const result2 = await this.
|
|
18032
|
+
const result2 = await this.runBridge.settleRun({
|
|
17189
18033
|
tenantId: current.tenantId,
|
|
17190
|
-
|
|
18034
|
+
task: current,
|
|
17191
18035
|
actor: SYSTEM_ACTOR2,
|
|
17192
18036
|
threadId: outcome.thread.id,
|
|
17193
|
-
|
|
18037
|
+
outcome: { kind: "completed" }
|
|
17194
18038
|
});
|
|
17195
18039
|
await this.checkLifecycle(current, "outcome-completed", result2);
|
|
17196
18040
|
return;
|
|
17197
18041
|
}
|
|
17198
18042
|
if (outcome.kind === "cancelled") {
|
|
17199
|
-
const result2 = await this.
|
|
18043
|
+
const result2 = await this.runBridge.settleRun({
|
|
17200
18044
|
tenantId: current.tenantId,
|
|
17201
|
-
|
|
18045
|
+
task: current,
|
|
17202
18046
|
actor: SYSTEM_ACTOR2,
|
|
17203
18047
|
threadId: outcome.thread.id,
|
|
17204
|
-
|
|
18048
|
+
outcome: { kind: "cancelled" }
|
|
17205
18049
|
});
|
|
17206
18050
|
await this.checkLifecycle(current, "outcome-cancelled", result2);
|
|
17207
18051
|
return;
|
|
17208
18052
|
}
|
|
17209
18053
|
if (outcome.kind === "failed" && outcome.lifecycleHandled) return;
|
|
17210
18054
|
if (outcome.kind === "interrupted") {
|
|
17211
|
-
const result2 = await this.
|
|
18055
|
+
const result2 = await this.runBridge.settleRun({
|
|
17212
18056
|
tenantId: current.tenantId,
|
|
17213
|
-
|
|
18057
|
+
task: current,
|
|
17214
18058
|
actor: SYSTEM_ACTOR2,
|
|
17215
18059
|
threadId: outcome.thread.id,
|
|
17216
|
-
|
|
17217
|
-
summary: "Task execution interrupted"
|
|
18060
|
+
outcome: { kind: "interrupted" }
|
|
17218
18061
|
});
|
|
17219
18062
|
await this.checkLifecycle(current, "outcome-interrupted", result2);
|
|
17220
18063
|
return;
|
|
17221
18064
|
}
|
|
17222
|
-
const result = await this.
|
|
18065
|
+
const result = await this.runBridge.settleRun({
|
|
17223
18066
|
tenantId: current.tenantId,
|
|
17224
|
-
|
|
18067
|
+
task: current,
|
|
17225
18068
|
actor: SYSTEM_ACTOR2,
|
|
17226
18069
|
threadId: outcome.thread.id,
|
|
17227
|
-
|
|
18070
|
+
outcome: { kind: "failed", lifecycleHandled: false, error: outcome.error }
|
|
17228
18071
|
});
|
|
17229
18072
|
await this.checkLifecycle(current, `outcome-${outcome.cause}`, result);
|
|
17230
18073
|
}
|
|
@@ -17271,7 +18114,7 @@ var ProjectTaskObserver = class {
|
|
|
17271
18114
|
});
|
|
17272
18115
|
for (const value of batch) {
|
|
17273
18116
|
if (!rows.has(value.id)) rows.set(value.id, { id: value.id, createdAt: requireDate(value.createdAt) });
|
|
17274
|
-
if (
|
|
18117
|
+
if (isProjectLifecycleTask(value)) {
|
|
17275
18118
|
const scope = { tenantId: value.tenantId, workspaceId: value.workspaceId, projectId: value.projectId };
|
|
17276
18119
|
scopes.set(scopeKey(scope), scope);
|
|
17277
18120
|
}
|
|
@@ -17388,7 +18231,7 @@ var ProjectTaskObserver = class {
|
|
|
17388
18231
|
action: "activity",
|
|
17389
18232
|
summary: "Project task recovery warning",
|
|
17390
18233
|
detail: { type: "project_task_recovery_warning", phase, message },
|
|
17391
|
-
eventKey: `project-task-recovery-warning:${task.id}:${phase}:${
|
|
18234
|
+
eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash6("sha256").update(message).digest("hex")}`
|
|
17392
18235
|
});
|
|
17393
18236
|
}
|
|
17394
18237
|
async safeWarning(task, phase, error) {
|
|
@@ -17402,6 +18245,134 @@ var ProjectTaskObserver = class {
|
|
|
17402
18245
|
});
|
|
17403
18246
|
}
|
|
17404
18247
|
}
|
|
18248
|
+
/**
|
|
18249
|
+
* Converges an unexecutable task to a durable interruption.
|
|
18250
|
+
*
|
|
18251
|
+
* @param task Exact task snapshot that cannot make progress through automation.
|
|
18252
|
+
* @param summary Human-readable reason recorded with the interruption.
|
|
18253
|
+
* @remarks Never leaves a task silently pending behind a warning; an `in_progress`
|
|
18254
|
+
* task with no dispatched queue rows is intentionally left untouched by recovery.
|
|
18255
|
+
* The interruption is visible and resumable once an executor is available.
|
|
18256
|
+
*/
|
|
18257
|
+
async convergeInterrupted(task, summary) {
|
|
18258
|
+
if (this.disposed) return;
|
|
18259
|
+
try {
|
|
18260
|
+
const result = await this.runBridge.settleRun({
|
|
18261
|
+
tenantId: task.tenantId,
|
|
18262
|
+
task,
|
|
18263
|
+
actor: SYSTEM_ACTOR2,
|
|
18264
|
+
outcome: { kind: "interrupted", summary }
|
|
18265
|
+
});
|
|
18266
|
+
if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
|
|
18267
|
+
await this.safeWarning(task, "converge", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? summary}`);
|
|
18268
|
+
}
|
|
18269
|
+
} catch (error) {
|
|
18270
|
+
await this.safeWarning(task, "converge", error);
|
|
18271
|
+
}
|
|
18272
|
+
}
|
|
18273
|
+
/**
|
|
18274
|
+
* Classifies a task's dependencies as ready, still waiting, or permanently unmet.
|
|
18275
|
+
*
|
|
18276
|
+
* @param task Exact task snapshot whose dependencies are inspected.
|
|
18277
|
+
* @returns `ready` when every dependency is completed, `unmet` when a
|
|
18278
|
+
* dependency is terminal but not completed (failed/cancelled/missing),
|
|
18279
|
+
* otherwise `wait`.
|
|
18280
|
+
*/
|
|
18281
|
+
async dependencyState(task) {
|
|
18282
|
+
for (const dependencyId of task.dependencies ?? []) {
|
|
18283
|
+
let dependency;
|
|
18284
|
+
try {
|
|
18285
|
+
dependency = snapshotTaskItem(await this.deps.tasks.getById(task.tenantId, dependencyId));
|
|
18286
|
+
} catch {
|
|
18287
|
+
return "wait";
|
|
18288
|
+
}
|
|
18289
|
+
if (!dependency || dependency.id !== dependencyId || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId) return "unmet";
|
|
18290
|
+
if (dependency.status === "completed") continue;
|
|
18291
|
+
if (dependency.status === "failed" || dependency.status === "cancelled") return "unmet";
|
|
18292
|
+
return "wait";
|
|
18293
|
+
}
|
|
18294
|
+
return "ready";
|
|
18295
|
+
}
|
|
18296
|
+
/**
|
|
18297
|
+
* Converges a task whose dependencies can never be met to a durable failure.
|
|
18298
|
+
*
|
|
18299
|
+
* @param task Exact task snapshot with terminal non-completed dependencies.
|
|
18300
|
+
*/
|
|
18301
|
+
async convergeDependencyUnmet(task) {
|
|
18302
|
+
if (this.disposed) return;
|
|
18303
|
+
try {
|
|
18304
|
+
const result = await this.runBridge.settleRun({
|
|
18305
|
+
tenantId: task.tenantId,
|
|
18306
|
+
task,
|
|
18307
|
+
actor: SYSTEM_ACTOR2,
|
|
18308
|
+
outcome: {
|
|
18309
|
+
kind: "failed",
|
|
18310
|
+
lifecycleHandled: false,
|
|
18311
|
+
error: `Dependency unmet: ${(task.dependencies ?? []).join(", ")}`
|
|
18312
|
+
}
|
|
18313
|
+
});
|
|
18314
|
+
if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
|
|
18315
|
+
await this.safeWarning(task, "dependency", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "dependency unmet"}`);
|
|
18316
|
+
}
|
|
18317
|
+
} catch (error) {
|
|
18318
|
+
await this.safeWarning(task, "dependency", error);
|
|
18319
|
+
}
|
|
18320
|
+
}
|
|
18321
|
+
/**
|
|
18322
|
+
* Detects whether a still-waiting task sits on a dependency cycle.
|
|
18323
|
+
*
|
|
18324
|
+
* @param task Exact pending task snapshot.
|
|
18325
|
+
* @returns True when following `dependencies` edges reaches the task again.
|
|
18326
|
+
* @remarks Bounded; unrelated cycles are ignored by the visited set.
|
|
18327
|
+
*/
|
|
18328
|
+
async hasDependencyCycle(task) {
|
|
18329
|
+
const MAX_READS = 1e3;
|
|
18330
|
+
const visited = /* @__PURE__ */ new Set();
|
|
18331
|
+
const stack = [...task.dependencies ?? []];
|
|
18332
|
+
let reads = 0;
|
|
18333
|
+
while (stack.length > 0) {
|
|
18334
|
+
const currentId = stack.pop();
|
|
18335
|
+
if (currentId === task.id) return true;
|
|
18336
|
+
if (visited.has(currentId)) continue;
|
|
18337
|
+
if (reads >= MAX_READS) return false;
|
|
18338
|
+
visited.add(currentId);
|
|
18339
|
+
reads += 1;
|
|
18340
|
+
let dependency;
|
|
18341
|
+
try {
|
|
18342
|
+
dependency = snapshotTaskItem(await this.deps.tasks.getById(task.tenantId, currentId));
|
|
18343
|
+
} catch {
|
|
18344
|
+
return false;
|
|
18345
|
+
}
|
|
18346
|
+
if (!dependency) continue;
|
|
18347
|
+
for (const next of dependency.dependencies ?? []) stack.push(next);
|
|
18348
|
+
}
|
|
18349
|
+
return false;
|
|
18350
|
+
}
|
|
18351
|
+
/**
|
|
18352
|
+
* Converges a task stuck on a dependency cycle to a durable failure.
|
|
18353
|
+
*
|
|
18354
|
+
* @param task Exact pending task snapshot on the cycle.
|
|
18355
|
+
*/
|
|
18356
|
+
async convergeDependencyCycle(task) {
|
|
18357
|
+
if (this.disposed) return;
|
|
18358
|
+
try {
|
|
18359
|
+
const result = await this.runBridge.settleRun({
|
|
18360
|
+
tenantId: task.tenantId,
|
|
18361
|
+
task,
|
|
18362
|
+
actor: SYSTEM_ACTOR2,
|
|
18363
|
+
outcome: {
|
|
18364
|
+
kind: "failed",
|
|
18365
|
+
lifecycleHandled: false,
|
|
18366
|
+
error: `Dependency cycle: ${(task.dependencies ?? []).join(", ")}`
|
|
18367
|
+
}
|
|
18368
|
+
});
|
|
18369
|
+
if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
|
|
18370
|
+
await this.safeWarning(task, "dependency-cycle", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "dependency cycle"}`);
|
|
18371
|
+
}
|
|
18372
|
+
} catch (error) {
|
|
18373
|
+
await this.safeWarning(task, "dependency-cycle", error);
|
|
18374
|
+
}
|
|
18375
|
+
}
|
|
17405
18376
|
async checkLifecycle(task, phase, result) {
|
|
17406
18377
|
if (result.success) return;
|
|
17407
18378
|
await this.safeWarning(task, phase, `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "Lifecycle rejected"}`);
|
|
@@ -17446,16 +18417,25 @@ var ProjectTaskObserver = class {
|
|
|
17446
18417
|
async runOverflowReconciliations() {
|
|
17447
18418
|
while (!this.disposed && this.overflowScopes.size > 0) {
|
|
17448
18419
|
const entries = [...this.overflowScopes.entries()];
|
|
17449
|
-
|
|
18420
|
+
const applicable = [];
|
|
18421
|
+
for (const [key, scope] of entries) {
|
|
18422
|
+
const task = snapshotTaskItem(await this.deps.tasks.getById(scope.event.tenantId, scope.event.taskId));
|
|
18423
|
+
if (!task || !isProjectLifecycleTask(task) || task.workspaceId !== scope.event.workspaceId || task.projectId !== scope.event.projectId) {
|
|
18424
|
+
if (this.overflowScopes.get(key)?.generation === scope.generation) this.overflowScopes.delete(key);
|
|
18425
|
+
continue;
|
|
18426
|
+
}
|
|
18427
|
+
applicable.push([key, scope]);
|
|
18428
|
+
}
|
|
18429
|
+
for (const [, { event }] of applicable) await this.persistOverflowWarning(event);
|
|
17450
18430
|
while (this.notificationTails.size > 0) await Promise.allSettled([...this.notificationTails.values()]);
|
|
17451
|
-
for (const [key, { event, generation }] of
|
|
18431
|
+
for (const [key, { event, generation }] of applicable) {
|
|
17452
18432
|
await this.reconcileOverflowScope(event);
|
|
17453
18433
|
if (this.overflowScopes.get(key)?.generation === generation) this.overflowScopes.delete(key);
|
|
17454
18434
|
}
|
|
17455
18435
|
}
|
|
17456
18436
|
}
|
|
17457
18437
|
async persistOverflowWarning(event) {
|
|
17458
|
-
const scopeDigest =
|
|
18438
|
+
const scopeDigest = createHash6("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
|
|
17459
18439
|
try {
|
|
17460
18440
|
await this.deps.workItems.createIfAbsentByEventKey({
|
|
17461
18441
|
tenantId: event.tenantId,
|
|
@@ -17514,7 +18494,7 @@ var ProjectTaskObserver = class {
|
|
|
17514
18494
|
}
|
|
17515
18495
|
};
|
|
17516
18496
|
function machineScope(task) {
|
|
17517
|
-
if (!task.projectId || task.
|
|
18497
|
+
if (!task.projectId || task.ownerType !== "agent") {
|
|
17518
18498
|
throw new Error("Invalid Project task machine scope");
|
|
17519
18499
|
}
|
|
17520
18500
|
return { tenantId: task.tenantId, assistantId: task.ownerId, projectId: task.projectId };
|
|
@@ -17564,8 +18544,7 @@ function combineSignals(left, right) {
|
|
|
17564
18544
|
}
|
|
17565
18545
|
|
|
17566
18546
|
// src/services/collaboration/RoomProjectionService.ts
|
|
17567
|
-
import { createHash as
|
|
17568
|
-
import { isProjectLifecycleTask as isProjectLifecycleTask5 } from "@axiom-lattice/core";
|
|
18547
|
+
import { createHash as createHash7 } from "crypto";
|
|
17569
18548
|
import {
|
|
17570
18549
|
PROJECT_ROOM_TASK_DELEGATED_LABEL,
|
|
17571
18550
|
PROJECT_ROOM_TASK_DELEGATED_SEPARATOR,
|
|
@@ -17715,7 +18694,7 @@ var RoomProjectionService = class {
|
|
|
17715
18694
|
await this.warning("INVALID_PROJECTABLE_EVENT", { taskId: row.taskId, eventKey: row.eventKey }, input);
|
|
17716
18695
|
continue;
|
|
17717
18696
|
}
|
|
17718
|
-
if (task.ownerType !== "agent" || !
|
|
18697
|
+
if (task.ownerType !== "agent" || !isProjectLifecycleTask(task)) {
|
|
17719
18698
|
result.skipped += 1;
|
|
17720
18699
|
continue;
|
|
17721
18700
|
}
|
|
@@ -17760,7 +18739,7 @@ var RoomProjectionService = class {
|
|
|
17760
18739
|
};
|
|
17761
18740
|
function snapshotProjectTask(value) {
|
|
17762
18741
|
const task = snapshotTaskItem(value);
|
|
17763
|
-
return task && task.ownerType === "agent" &&
|
|
18742
|
+
return task && task.ownerType === "agent" && isProjectLifecycleTask(task) ? task : void 0;
|
|
17764
18743
|
}
|
|
17765
18744
|
function ownEnumerableString(value, key) {
|
|
17766
18745
|
try {
|
|
@@ -17786,7 +18765,7 @@ function safeRepairIdentity(value, scope) {
|
|
|
17786
18765
|
return taskId && eventKey && tenantId === scope.tenantId && workspaceId === scope.workspaceId && projectId === scope.projectId ? { taskId, eventKey } : void 0;
|
|
17787
18766
|
}
|
|
17788
18767
|
function tupleKey(prefix, values) {
|
|
17789
|
-
const digest =
|
|
18768
|
+
const digest = createHash7("sha256").update(JSON.stringify(values)).digest("hex");
|
|
17790
18769
|
return `${prefix}:${digest}`;
|
|
17791
18770
|
}
|
|
17792
18771
|
function parseProjectionInput(taskValue, itemValue) {
|
|
@@ -17956,7 +18935,7 @@ function isRecord3(value) {
|
|
|
17956
18935
|
import { taskLifecycleNotifier } from "@axiom-lattice/core";
|
|
17957
18936
|
|
|
17958
18937
|
// src/services/collaboration/ProjectRoomEventBroker.ts
|
|
17959
|
-
import { randomUUID as
|
|
18938
|
+
import { randomUUID as randomUUID22 } from "crypto";
|
|
17960
18939
|
import {
|
|
17961
18940
|
parseProjectRoomEventId,
|
|
17962
18941
|
ProjectRoomBrokerCapacityError,
|
|
@@ -17985,7 +18964,7 @@ var ProjectRoomEventBroker = class {
|
|
|
17985
18964
|
this.subscriberCount = 0;
|
|
17986
18965
|
this.usageClock = 0;
|
|
17987
18966
|
this.closed = false;
|
|
17988
|
-
this.epoch = options.epoch ??
|
|
18967
|
+
this.epoch = options.epoch ?? randomUUID22();
|
|
17989
18968
|
if (!UUID_PATTERN.test(this.epoch)) {
|
|
17990
18969
|
throw new TypeError("Project Room event epoch must be a lowercase canonical UUID");
|
|
17991
18970
|
}
|
|
@@ -18303,7 +19282,7 @@ function snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
|
18303
19282
|
}
|
|
18304
19283
|
|
|
18305
19284
|
// src/services/collaboration/ProjectRoomRealtimePublisher.ts
|
|
18306
|
-
import { createHash as
|
|
19285
|
+
import { createHash as createHash8 } from "crypto";
|
|
18307
19286
|
import {
|
|
18308
19287
|
projectRoomUserEventScope,
|
|
18309
19288
|
snapshotExactRecord as snapshotExactRecord11,
|
|
@@ -18425,7 +19404,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
18425
19404
|
try {
|
|
18426
19405
|
event = build();
|
|
18427
19406
|
if (!event) throw new Error("Invalid Project Room realtime projection");
|
|
18428
|
-
const dedupeKey =
|
|
19407
|
+
const dedupeKey = createHash8("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
|
|
18429
19408
|
if (this.sourceKeys.has(dedupeKey)) return;
|
|
18430
19409
|
this.deps.broker.publish(event.scope, event.draft);
|
|
18431
19410
|
this.sourceKeys.set(dedupeKey, true);
|
|
@@ -18479,7 +19458,6 @@ function safeErrorCode(error) {
|
|
|
18479
19458
|
}
|
|
18480
19459
|
|
|
18481
19460
|
// src/services/collaboration/ProjectRoomTaskEventSubscriber.ts
|
|
18482
|
-
import { isProjectLifecycleTask as isProjectLifecycleTask6 } from "@axiom-lattice/core";
|
|
18483
19461
|
import { snapshotExactRecord as snapshotExactRecord12 } from "@axiom-lattice/protocols";
|
|
18484
19462
|
var ProjectRoomTaskEventSubscriber = class {
|
|
18485
19463
|
/** Creates a bounded Project task lifecycle observer. */
|
|
@@ -18562,8 +19540,8 @@ var ProjectRoomTaskEventSubscriber = class {
|
|
|
18562
19540
|
try {
|
|
18563
19541
|
const taskValue = await this.deps.tasks.getById(event.tenantId, event.taskId);
|
|
18564
19542
|
const task = taskValue === null ? void 0 : snapshotTaskItem(taskValue);
|
|
18565
|
-
if (this.closed) return;
|
|
18566
|
-
if (
|
|
19543
|
+
if (this.closed || !task || !isProjectLifecycleTask(task)) return;
|
|
19544
|
+
if (task.tenantId !== event.tenantId || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId || task.status !== event.status || task.updatedAt.toISOString() !== event.updatedAt || event.threadId !== void 0 && task.context?.thread_id !== event.threadId) {
|
|
18567
19545
|
this.warn("project-room:task-event-stale", { taskId: safeId3(event.taskId), eventKey: safeId3(event.eventKey) });
|
|
18568
19546
|
return;
|
|
18569
19547
|
}
|
|
@@ -19372,20 +20350,163 @@ async function validateReassignmentThread(threads, input) {
|
|
|
19372
20350
|
throw new ProjectTaskThreadError("PROJECT_TASK_THREAD_METADATA_INVALID", "Invalid reassignment Task Thread metadata");
|
|
19373
20351
|
}
|
|
19374
20352
|
}
|
|
20353
|
+
function hookErrorMessage(error) {
|
|
20354
|
+
if (error instanceof Error && error.message) return error.message;
|
|
20355
|
+
return "Managed dispatch transfer failed.";
|
|
20356
|
+
}
|
|
20357
|
+
function wrapReassignmentThreadError(error) {
|
|
20358
|
+
if (error instanceof ProjectTaskThreadError) {
|
|
20359
|
+
if (error.code === "PROJECT_TASK_THREAD_SCOPE_INVALID") {
|
|
20360
|
+
return new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
|
|
20361
|
+
hint: "Project task, room, or roster scope changed; re-read the task and retry."
|
|
20362
|
+
});
|
|
20363
|
+
}
|
|
20364
|
+
if (error.code === "PROJECT_TASK_THREAD_OWNER_INVALID") {
|
|
20365
|
+
return new ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
|
|
20366
|
+
hint: "The target already owns this task; re-read before dispatching."
|
|
20367
|
+
});
|
|
20368
|
+
}
|
|
20369
|
+
return new ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
|
|
20370
|
+
hint: "Task Thread changed concurrently; re-read the task and retry."
|
|
20371
|
+
});
|
|
20372
|
+
}
|
|
20373
|
+
return new ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
|
|
20374
|
+
hint: "Re-read the task and retry the dispatch."
|
|
20375
|
+
});
|
|
20376
|
+
}
|
|
20377
|
+
function wrapReassignmentAuthorizationError(error) {
|
|
20378
|
+
if (error instanceof ProjectTaskOwnerPolicyError2) {
|
|
20379
|
+
if (error.code === "PROJECT_TASK_CALLER_INACTIVE") {
|
|
20380
|
+
return new ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
|
|
20381
|
+
hint: "Dispatch from an active Project Room member bot."
|
|
20382
|
+
});
|
|
20383
|
+
}
|
|
20384
|
+
return new ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
|
|
20385
|
+
hint: "Dispatch to an active bot in this Project's roster."
|
|
20386
|
+
});
|
|
20387
|
+
}
|
|
20388
|
+
return new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
|
|
20389
|
+
hint: "Re-read the task and roster, then retry the dispatch."
|
|
20390
|
+
});
|
|
20391
|
+
}
|
|
20392
|
+
function reassignmentFailureHint(code) {
|
|
20393
|
+
if (code === "TASK_THREAD_CONFLICT") {
|
|
20394
|
+
return "Task Thread changed concurrently; re-read the task and retry.";
|
|
20395
|
+
}
|
|
20396
|
+
if (code === "TASK_REASSIGN_NO_CHANGE") {
|
|
20397
|
+
return "The target already owns this task; re-read before dispatching.";
|
|
20398
|
+
}
|
|
20399
|
+
if (code === "TASK_STATUS_CONFLICT") {
|
|
20400
|
+
return "Task changed concurrently; re-read the task and retry.";
|
|
20401
|
+
}
|
|
20402
|
+
if (code === "TASK_TERMINAL") {
|
|
20403
|
+
return "Terminal tasks are immutable; create a new task for this work.";
|
|
20404
|
+
}
|
|
20405
|
+
if (code === "REVIEW_APPROVAL_REQUIRED") {
|
|
20406
|
+
return "Resolve the review decision (approve/reject) before dispatching.";
|
|
20407
|
+
}
|
|
20408
|
+
if (code === "TASK_REASSIGN_INTERRUPT_REQUIRED") {
|
|
20409
|
+
return "Resume or cancel the running task before dispatching.";
|
|
20410
|
+
}
|
|
20411
|
+
return "Re-read the task and retry the dispatch.";
|
|
20412
|
+
}
|
|
20413
|
+
async function transferGovernedDispatchOwner(task, options, deps) {
|
|
20414
|
+
const agentId = options?.agentId;
|
|
20415
|
+
if (!agentId || agentId === task.ownerId) return void 0;
|
|
20416
|
+
if (!task.workspaceId || !task.projectId) {
|
|
20417
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is not governed by a Project lifecycle.", {
|
|
20418
|
+
hint: "Dispatch governed tasks from a trusted Project run; ordinary tasks execute immediately."
|
|
20419
|
+
});
|
|
20420
|
+
}
|
|
20421
|
+
return deps.mutex.runExclusive({ tenantId: task.tenantId, projectId: task.projectId }, async () => {
|
|
20422
|
+
const current = await deps.tasks.getById(task.tenantId, task.id);
|
|
20423
|
+
if (!current || !isProjectLifecycleTask(current) || current.workspaceId !== task.workspaceId || current.projectId !== task.projectId) {
|
|
20424
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is no longer a Project lifecycle task.", {
|
|
20425
|
+
hint: "Re-read the task and confirm it still belongs to this Project before dispatching."
|
|
20426
|
+
});
|
|
20427
|
+
}
|
|
20428
|
+
if (current.ownerType !== "agent") {
|
|
20429
|
+
throw new ManagedTaskStartError("AGENT_TASK_REQUIRED", "Project task owner must be an Agent.", {
|
|
20430
|
+
hint: "Assign the task to an Agent before dispatching."
|
|
20431
|
+
});
|
|
20432
|
+
}
|
|
20433
|
+
if (current.status !== "pending") {
|
|
20434
|
+
throw new ManagedTaskStartError("TASK_NOT_DISPATCHABLE", "Only a `pending` Project task can be dispatched.", {
|
|
20435
|
+
hint: `This task is '${current.status}'. Resolve it or create a new task before dispatching.`
|
|
20436
|
+
});
|
|
20437
|
+
}
|
|
20438
|
+
if (agentId === current.ownerId) return current;
|
|
20439
|
+
const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
|
|
20440
|
+
if (!room || room.tenantId !== current.tenantId || room.workspaceId !== current.workspaceId || room.projectId !== current.projectId || room.type !== "main") {
|
|
20441
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Project main room is unavailable.", {
|
|
20442
|
+
hint: "Open or repair the Project Room before dispatching."
|
|
20443
|
+
});
|
|
20444
|
+
}
|
|
20445
|
+
const target = await deps.botMemberships.findByAssistant(current.tenantId, current.projectId, agentId);
|
|
20446
|
+
if (!target || target.status !== "active" || target.tenantId !== current.tenantId || target.workspaceId !== current.workspaceId || target.projectId !== current.projectId || target.assistantId !== agentId || target.roomId !== room.id) {
|
|
20447
|
+
throw new ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", "Target Project task owner is not active in the exact roster.", {
|
|
20448
|
+
hint: "Dispatch to an active bot in this Project's roster."
|
|
20449
|
+
});
|
|
20450
|
+
}
|
|
20451
|
+
const caller = options?.callerMembershipId ? await deps.botMemberships.findById(current.tenantId, options.callerMembershipId) : null;
|
|
20452
|
+
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) {
|
|
20453
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", "Current Project task caller is not active in the exact roster.", {
|
|
20454
|
+
hint: "Dispatch from an active Project Room member bot."
|
|
20455
|
+
});
|
|
20456
|
+
}
|
|
20457
|
+
let ensured;
|
|
20458
|
+
try {
|
|
20459
|
+
ensured = await deps.threads.ensure({ task: current, room, membership: target, mode: "reassignment" });
|
|
20460
|
+
} catch (error) {
|
|
20461
|
+
throw wrapReassignmentThreadError(error);
|
|
20462
|
+
}
|
|
20463
|
+
let authorization;
|
|
20464
|
+
try {
|
|
20465
|
+
authorization = await deps.reassignmentAuthorizer.authorize({
|
|
20466
|
+
tenantId: current.tenantId,
|
|
20467
|
+
workspaceId: current.workspaceId,
|
|
20468
|
+
projectId: current.projectId,
|
|
20469
|
+
roomId: room.id,
|
|
20470
|
+
taskId: current.id,
|
|
20471
|
+
callerMembershipId: caller.id,
|
|
20472
|
+
callerAssistantId: caller.assistantId,
|
|
20473
|
+
targetMembershipId: target.id,
|
|
20474
|
+
targetAssistantId: target.assistantId,
|
|
20475
|
+
...typeof current.context?.thread_id === "string" ? { expectedThreadId: current.context.thread_id } : {},
|
|
20476
|
+
newThreadId: ensured.thread.id
|
|
20477
|
+
});
|
|
20478
|
+
} catch (error) {
|
|
20479
|
+
throw wrapReassignmentAuthorizationError(error);
|
|
20480
|
+
}
|
|
20481
|
+
const result = await deps.lifecycle.reassignProjectTask({
|
|
20482
|
+
tenantId: current.tenantId,
|
|
20483
|
+
taskId: current.id,
|
|
20484
|
+
authorization,
|
|
20485
|
+
actor: `agent:${caller.assistantId}`
|
|
20486
|
+
});
|
|
20487
|
+
if (!result.success) {
|
|
20488
|
+
throw new ManagedTaskStartError(result.code, result.error, {
|
|
20489
|
+
hint: result.hint ?? reassignmentFailureHint(result.code),
|
|
20490
|
+
...result.retryable !== void 0 ? { retryable: result.retryable } : {}
|
|
20491
|
+
});
|
|
20492
|
+
}
|
|
20493
|
+
return result.task;
|
|
20494
|
+
});
|
|
20495
|
+
}
|
|
19375
20496
|
var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
19376
|
-
const webAppStore =
|
|
19377
|
-
const assistantStore =
|
|
19378
|
-
const projectStore =
|
|
19379
|
-
const projectMembershipStore =
|
|
19380
|
-
const userTenantLinkStore =
|
|
19381
|
-
const projectRoomStore =
|
|
19382
|
-
const projectBotMembershipStore =
|
|
19383
|
-
const projectRoomMessageStore =
|
|
19384
|
-
const projectRoomReadStateStore =
|
|
19385
|
-
const userPushSubscriptionStore =
|
|
19386
|
-
const threadStore =
|
|
19387
|
-
const taskStore =
|
|
19388
|
-
const taskWorkItemStore =
|
|
20497
|
+
const webAppStore = getStoreLattice17("default", "agentWebApp").store;
|
|
20498
|
+
const assistantStore = getStoreLattice17("default", "assistant").store;
|
|
20499
|
+
const projectStore = getStoreLattice17("default", "project").store;
|
|
20500
|
+
const projectMembershipStore = getStoreLattice17("default", "projectMembership").store;
|
|
20501
|
+
const userTenantLinkStore = getStoreLattice17("default", "userTenantLink").store;
|
|
20502
|
+
const projectRoomStore = getStoreLattice17("default", "projectRoom").store;
|
|
20503
|
+
const projectBotMembershipStore = getStoreLattice17("default", "projectBotMembership").store;
|
|
20504
|
+
const projectRoomMessageStore = getStoreLattice17("default", "projectRoomMessage").store;
|
|
20505
|
+
const projectRoomReadStateStore = getStoreLattice17("default", "projectRoomReadState").store;
|
|
20506
|
+
const userPushSubscriptionStore = getStoreLattice17("default", "userPushSubscription").store;
|
|
20507
|
+
const threadStore = getStoreLattice17("default", "thread").store;
|
|
20508
|
+
const taskStore = getStoreLattice17("default", "task").store;
|
|
20509
|
+
const taskWorkItemStore = getStoreLattice17("default", "taskWorkItem").store;
|
|
19389
20510
|
const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
|
|
19390
20511
|
const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
|
|
19391
20512
|
const realtimeMode = resolveProjectRoomRealtimeMode();
|
|
@@ -19451,7 +20572,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19451
20572
|
updateAssistant: assistantStore.updateAssistant.bind(assistantStore)
|
|
19452
20573
|
});
|
|
19453
20574
|
const resolveCoordinatorMentionName = async (tenantId) => {
|
|
19454
|
-
const tenant2 = await
|
|
20575
|
+
const tenant2 = await getStoreLattice17("default", "tenant").store.getTenantById(tenantId);
|
|
19455
20576
|
const value = tenant2?.metadata?.coordinatorMentionName;
|
|
19456
20577
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
19457
20578
|
};
|
|
@@ -19542,7 +20663,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19542
20663
|
});
|
|
19543
20664
|
}
|
|
19544
20665
|
if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
|
|
19545
|
-
const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory:
|
|
20666
|
+
const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: randomUUID23 });
|
|
19546
20667
|
const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? createTaskLifecycleService({ taskStore, workItemStore: taskWorkItemStore });
|
|
19547
20668
|
const reassignmentAuthorizer = createProjectTaskReassignmentAuthorizer(
|
|
19548
20669
|
new ProjectTaskOwnerPolicy(projectBotMembershipStore),
|
|
@@ -19578,7 +20699,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19578
20699
|
readStateService,
|
|
19579
20700
|
interruptService: roomInterruptService,
|
|
19580
20701
|
userDirectory: {
|
|
19581
|
-
getUserById: (id3) =>
|
|
20702
|
+
getUserById: (id3) => getStoreLattice17("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
|
|
19582
20703
|
}
|
|
19583
20704
|
});
|
|
19584
20705
|
registerProjectRosterRoutes(app2, { service: rosterService });
|
|
@@ -19589,7 +20710,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19589
20710
|
createProject: projectStore.createProject.bind(projectStore)
|
|
19590
20711
|
},
|
|
19591
20712
|
workspaceStore: {
|
|
19592
|
-
getWorkspaceById: (tenantId, id3) =>
|
|
20713
|
+
getWorkspaceById: (tenantId, id3) => getStoreLattice17("default", "workspace").store.getWorkspaceById(tenantId, id3)
|
|
19593
20714
|
},
|
|
19594
20715
|
membershipStore: {
|
|
19595
20716
|
list: projectMembershipStore.list.bind(projectMembershipStore),
|
|
@@ -19612,7 +20733,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19612
20733
|
getUsersByTenant: userTenantLinkStore.getUsersByTenant.bind(userTenantLinkStore)
|
|
19613
20734
|
},
|
|
19614
20735
|
userStore: {
|
|
19615
|
-
getUserById: (id3) =>
|
|
20736
|
+
getUserById: (id3) => getStoreLattice17("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
|
|
19616
20737
|
},
|
|
19617
20738
|
assistantStore: {
|
|
19618
20739
|
getAssistantById: assistantStore.getAssistantById.bind(assistantStore),
|
|
@@ -19634,7 +20755,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19634
20755
|
}
|
|
19635
20756
|
});
|
|
19636
20757
|
registerCapabilityBundleRoutes(app2, new CapabilityBundleController({
|
|
19637
|
-
bundleStore:
|
|
20758
|
+
bundleStore: getStoreLattice17("default", "capabilityBundle").store,
|
|
19638
20759
|
projectStore
|
|
19639
20760
|
}, { authRequired: process.env.AUTH_REQUIRED === "true" }));
|
|
19640
20761
|
registerAgentWebAppRoutes(app2, {
|
|
@@ -19682,19 +20803,11 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19682
20803
|
},
|
|
19683
20804
|
tasks: taskStore,
|
|
19684
20805
|
workItems: taskWorkItemStore,
|
|
19685
|
-
|
|
19686
|
-
rooms: projectRoomStore,
|
|
19687
|
-
threadService: taskThreadService,
|
|
19688
|
-
reassignmentAuthorizer,
|
|
19689
|
-
lifecycle: taskLifecycle,
|
|
19690
|
-
projectMutationMutex: projectMutationMutex2,
|
|
19691
|
-
executionMutex: projectTaskComposition?.mutex ?? {
|
|
19692
|
-
runExclusive: (_scope, operation) => operation()
|
|
19693
|
-
}
|
|
20806
|
+
projectMutationMutex: projectMutationMutex2
|
|
19694
20807
|
}),
|
|
19695
20808
|
service: new AgentWebAppRuntimeService({
|
|
19696
20809
|
webAppStore,
|
|
19697
|
-
threadStore:
|
|
20810
|
+
threadStore: getStoreLattice17("default", "thread").store,
|
|
19698
20811
|
projectStore,
|
|
19699
20812
|
assistantResolver: {
|
|
19700
20813
|
resolve: async (tenantId, assistantId) => {
|
|
@@ -19795,19 +20908,45 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19795
20908
|
app2.post("/api/personal-assistant", createPersonalAssistant);
|
|
19796
20909
|
app2.get("/api/personal-assistant", getPersonalAssistant);
|
|
19797
20910
|
app2.delete("/api/personal-assistant", deletePersonalAssistant);
|
|
20911
|
+
executionAuthorityRegistry.register({
|
|
20912
|
+
id: "project-lifecycle",
|
|
20913
|
+
matches: isProjectLifecycleTask,
|
|
20914
|
+
code: "PROJECT_TASK_EXECUTION_MANAGED",
|
|
20915
|
+
message: "Project tasks are executed by the durable Project queue runtime",
|
|
20916
|
+
onManagedStart: async (task, options2) => {
|
|
20917
|
+
if (!projectTaskComposition && options2?.agentId && options2.agentId !== task.ownerId) {
|
|
20918
|
+
throw new ManagedTaskStartError(
|
|
20919
|
+
"PROJECT_TASK_RUNTIME_UNAVAILABLE",
|
|
20920
|
+
"Project task ownership transfer requires the Project task runtime.",
|
|
20921
|
+
{ hint: "Project task execution is disabled; enable the Project task runtime or dispatch to the current owner." }
|
|
20922
|
+
);
|
|
20923
|
+
}
|
|
20924
|
+
const transferred = projectTaskComposition ? await transferGovernedDispatchOwner(task, options2, {
|
|
20925
|
+
tasks: taskStore,
|
|
20926
|
+
rooms: projectRoomStore,
|
|
20927
|
+
botMemberships: projectBotMembershipStore,
|
|
20928
|
+
threads: taskThreadService,
|
|
20929
|
+
reassignmentAuthorizer,
|
|
20930
|
+
lifecycle: taskLifecycle,
|
|
20931
|
+
mutex: projectMutationMutex2
|
|
20932
|
+
}) : void 0;
|
|
20933
|
+
const admitted = transferred ?? task;
|
|
20934
|
+
if (!admitted.workspaceId || !admitted.projectId) return;
|
|
20935
|
+
void projectTaskAdmissionNotifier.publish({
|
|
20936
|
+
tenantId: admitted.tenantId,
|
|
20937
|
+
workspaceId: admitted.workspaceId,
|
|
20938
|
+
projectId: admitted.projectId,
|
|
20939
|
+
taskId: admitted.id,
|
|
20940
|
+
ownerId: admitted.ownerId,
|
|
20941
|
+
updatedAt: admitted.updatedAt.toISOString()
|
|
20942
|
+
});
|
|
20943
|
+
}
|
|
20944
|
+
});
|
|
19798
20945
|
registerTaskRoutes(app2, createTaskController({
|
|
19799
20946
|
access: accessService,
|
|
19800
20947
|
tasks: taskStore,
|
|
19801
20948
|
workItems: taskWorkItemStore,
|
|
19802
|
-
bots: projectBotMembershipStore,
|
|
19803
|
-
rooms: projectRoomStore,
|
|
19804
|
-
threadService: taskThreadService,
|
|
19805
|
-
reassignmentAuthorizer,
|
|
19806
|
-
lifecycle: taskLifecycle,
|
|
19807
20949
|
projectMutationMutex: projectMutationMutex2,
|
|
19808
|
-
executionMutex: projectTaskComposition?.mutex ?? {
|
|
19809
|
-
runExclusive: (_scope, operation) => operation()
|
|
19810
|
-
},
|
|
19811
20950
|
readableProjects: async ({ tenantId, userId, workspaceId }) => {
|
|
19812
20951
|
const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
|
|
19813
20952
|
const readable = [];
|
|
@@ -19931,6 +21070,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19931
21070
|
executeDataQuery
|
|
19932
21071
|
);
|
|
19933
21072
|
registerMcpServerConfigRoutes(app2);
|
|
21073
|
+
registerMcpAppRoutes(app2);
|
|
19934
21074
|
registerPushNotificationRoutes(app2, {
|
|
19935
21075
|
subscriptionStore: userPushSubscriptionStore,
|
|
19936
21076
|
vapidPublicKey: process.env.VAPID_PUBLIC_KEY || null
|
|
@@ -20087,9 +21227,9 @@ function tryCreateProjectTaskRuntime(input) {
|
|
|
20087
21227
|
let stores;
|
|
20088
21228
|
try {
|
|
20089
21229
|
stores = {
|
|
20090
|
-
tenants:
|
|
20091
|
-
workspaces:
|
|
20092
|
-
queue:
|
|
21230
|
+
tenants: getStoreLattice17("default", "tenant").store,
|
|
21231
|
+
workspaces: getStoreLattice17("default", "workspace").store,
|
|
21232
|
+
queue: getStoreLattice17("default", "threadMessageQueue").store
|
|
20093
21233
|
};
|
|
20094
21234
|
const missing = missingRuntimeMethod(input, stores);
|
|
20095
21235
|
if (missing) return { reason: missing };
|
|
@@ -20120,7 +21260,7 @@ function tryCreateProjectTaskRuntime(input) {
|
|
|
20120
21260
|
function createRuntime(input, stores, log2) {
|
|
20121
21261
|
const controller = new AbortController();
|
|
20122
21262
|
const mutex = new ProjectTaskExecutionMutex();
|
|
20123
|
-
const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory:
|
|
21263
|
+
const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: randomUUID23 });
|
|
20124
21264
|
const lifecycle = createTaskLifecycleService({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
|
|
20125
21265
|
const projection = new RoomProjectionService({
|
|
20126
21266
|
messages: input.projectRoomMessageStore,
|
|
@@ -20129,7 +21269,7 @@ function createRuntime(input, stores, log2) {
|
|
|
20129
21269
|
warnings: input.taskWorkItemStore,
|
|
20130
21270
|
log: log2,
|
|
20131
21271
|
realtime: input.realtime,
|
|
20132
|
-
idFactory:
|
|
21272
|
+
idFactory: randomUUID23
|
|
20133
21273
|
});
|
|
20134
21274
|
const agentFor = (task, thread) => agentInstanceManager6.getAgent({
|
|
20135
21275
|
tenant_id: task.tenantId,
|
|
@@ -20173,7 +21313,7 @@ function createRuntime(input, stores, log2) {
|
|
|
20173
21313
|
void observer.listener(event);
|
|
20174
21314
|
});
|
|
20175
21315
|
const unsubscribe = taskLifecycleNotifier2.subscribe(taskLifecycleListener);
|
|
20176
|
-
const unsubscribeAdmission =
|
|
21316
|
+
const unsubscribeAdmission = projectTaskAdmissionNotifier.subscribe((event, signal) => {
|
|
20177
21317
|
observer.admit(event, signal);
|
|
20178
21318
|
});
|
|
20179
21319
|
let reconciliationWork;
|
|
@@ -20466,9 +21606,9 @@ var skillRegistration = {
|
|
|
20466
21606
|
};
|
|
20467
21607
|
|
|
20468
21608
|
// src/export_registrations/agent.registration.ts
|
|
20469
|
-
import { getStoreLattice as
|
|
21609
|
+
import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
|
|
20470
21610
|
function getStore2() {
|
|
20471
|
-
return
|
|
21611
|
+
return getStoreLattice18("default", "assistant").store;
|
|
20472
21612
|
}
|
|
20473
21613
|
var agentRegistration = {
|
|
20474
21614
|
entityType: "agent",
|
|
@@ -20548,9 +21688,9 @@ var agentRegistration = {
|
|
|
20548
21688
|
};
|
|
20549
21689
|
|
|
20550
21690
|
// src/export_registrations/database-config.registration.ts
|
|
20551
|
-
import { getStoreLattice as
|
|
21691
|
+
import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
|
|
20552
21692
|
function getStore3() {
|
|
20553
|
-
return
|
|
21693
|
+
return getStoreLattice19("default", "database").store;
|
|
20554
21694
|
}
|
|
20555
21695
|
var databaseConfigRegistration = {
|
|
20556
21696
|
entityType: "database_config",
|
|
@@ -20630,9 +21770,9 @@ var databaseConfigRegistration = {
|
|
|
20630
21770
|
};
|
|
20631
21771
|
|
|
20632
21772
|
// src/export_registrations/metrics-config.registration.ts
|
|
20633
|
-
import { getStoreLattice as
|
|
21773
|
+
import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
|
|
20634
21774
|
function getStore4() {
|
|
20635
|
-
return
|
|
21775
|
+
return getStoreLattice20("default", "metrics").store;
|
|
20636
21776
|
}
|
|
20637
21777
|
var metricsConfigRegistration = {
|
|
20638
21778
|
entityType: "metrics_config",
|
|
@@ -20712,9 +21852,9 @@ var metricsConfigRegistration = {
|
|
|
20712
21852
|
};
|
|
20713
21853
|
|
|
20714
21854
|
// src/export_registrations/mcp-config.registration.ts
|
|
20715
|
-
import { getStoreLattice as
|
|
21855
|
+
import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
|
|
20716
21856
|
function getStore5() {
|
|
20717
|
-
return
|
|
21857
|
+
return getStoreLattice21("default", "mcp").store;
|
|
20718
21858
|
}
|
|
20719
21859
|
var mcpConfigRegistration = {
|
|
20720
21860
|
entityType: "mcp_config",
|
|
@@ -20797,9 +21937,9 @@ var mcpConfigRegistration = {
|
|
|
20797
21937
|
};
|
|
20798
21938
|
|
|
20799
21939
|
// src/export_registrations/connection.registration.ts
|
|
20800
|
-
import { getStoreLattice as
|
|
21940
|
+
import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
|
|
20801
21941
|
function getStore6() {
|
|
20802
|
-
return
|
|
21942
|
+
return getStoreLattice22("default", "connection").store;
|
|
20803
21943
|
}
|
|
20804
21944
|
async function listAllConnections(tenantId) {
|
|
20805
21945
|
const store = getStore6();
|
|
@@ -20881,9 +22021,9 @@ var connectionRegistration = {
|
|
|
20881
22021
|
};
|
|
20882
22022
|
|
|
20883
22023
|
// src/export_registrations/collection.registration.ts
|
|
20884
|
-
import { getStoreLattice as
|
|
22024
|
+
import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
|
|
20885
22025
|
function getStore7() {
|
|
20886
|
-
return
|
|
22026
|
+
return getStoreLattice23("default", "collection").store;
|
|
20887
22027
|
}
|
|
20888
22028
|
var collectionRegistration = {
|
|
20889
22029
|
entityType: "collection",
|
|
@@ -20954,8 +22094,8 @@ var collectionRegistration = {
|
|
|
20954
22094
|
};
|
|
20955
22095
|
|
|
20956
22096
|
// src/export_registrations/channel-installation.registration.ts
|
|
20957
|
-
import { getStoreLattice as
|
|
20958
|
-
import { randomUUID as
|
|
22097
|
+
import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
|
|
22098
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
20959
22099
|
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;
|
|
20960
22100
|
var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
|
|
20961
22101
|
function isPublicChannel(channel) {
|
|
@@ -20988,7 +22128,7 @@ function validateImportData(data) {
|
|
|
20988
22128
|
};
|
|
20989
22129
|
}
|
|
20990
22130
|
function getStore8() {
|
|
20991
|
-
return
|
|
22131
|
+
return getStoreLattice24("default", "channelInstallation").store;
|
|
20992
22132
|
}
|
|
20993
22133
|
function getOverwriteError(tenantId, incomingChannel, existing) {
|
|
20994
22134
|
if (!existing) return "Channel installation overwrite target must exist";
|
|
@@ -21114,7 +22254,7 @@ var channelInstallationRegistration = {
|
|
|
21114
22254
|
}
|
|
21115
22255
|
id3 = sourceId;
|
|
21116
22256
|
} else {
|
|
21117
|
-
id3 =
|
|
22257
|
+
id3 = randomUUID24();
|
|
21118
22258
|
}
|
|
21119
22259
|
await store.createInstallation(tenantId, id3, createInput);
|
|
21120
22260
|
return { newId: id3 };
|
|
@@ -21122,9 +22262,9 @@ var channelInstallationRegistration = {
|
|
|
21122
22262
|
};
|
|
21123
22263
|
|
|
21124
22264
|
// src/export_registrations/menu.registration.ts
|
|
21125
|
-
import { getStoreLattice as
|
|
22265
|
+
import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
|
|
21126
22266
|
function getStore9() {
|
|
21127
|
-
return
|
|
22267
|
+
return getStoreLattice25("default", "menu").store;
|
|
21128
22268
|
}
|
|
21129
22269
|
var menuRegistration = {
|
|
21130
22270
|
entityType: "menu",
|
|
@@ -21202,9 +22342,9 @@ var menuRegistration = {
|
|
|
21202
22342
|
};
|
|
21203
22343
|
|
|
21204
22344
|
// src/export_registrations/eval.registration.ts
|
|
21205
|
-
import { getStoreLattice as
|
|
22345
|
+
import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
|
|
21206
22346
|
function getStore10() {
|
|
21207
|
-
return
|
|
22347
|
+
return getStoreLattice26("default", "eval").store;
|
|
21208
22348
|
}
|
|
21209
22349
|
var evalRegistration = {
|
|
21210
22350
|
entityType: "eval",
|
|
@@ -21982,11 +23122,12 @@ import {
|
|
|
21982
23122
|
getLoggerLattice,
|
|
21983
23123
|
loggerLatticeManager,
|
|
21984
23124
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
21985
|
-
getStoreLattice as
|
|
23125
|
+
getStoreLattice as getStoreLattice28,
|
|
21986
23126
|
agentInstanceManager as agentInstanceManager9,
|
|
21987
23127
|
createSandboxProvider,
|
|
21988
23128
|
TokenCache,
|
|
21989
|
-
mcpManager,
|
|
23129
|
+
mcpManager as mcpManager3,
|
|
23130
|
+
convertToConnection as convertToConnection2,
|
|
21990
23131
|
createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2
|
|
21991
23132
|
} from "@axiom-lattice/core";
|
|
21992
23133
|
|
|
@@ -21995,14 +23136,14 @@ import { RoomAgentMessageService } from "@axiom-lattice/core";
|
|
|
21995
23136
|
|
|
21996
23137
|
// src/router/MessageRouter.ts
|
|
21997
23138
|
import {
|
|
21998
|
-
getStoreLattice as
|
|
23139
|
+
getStoreLattice as getStoreLattice27,
|
|
21999
23140
|
agentInstanceManager as agentInstanceManager8
|
|
22000
23141
|
} from "@axiom-lattice/core";
|
|
22001
23142
|
import {
|
|
22002
23143
|
snapshotExactArray as snapshotExactArray3,
|
|
22003
23144
|
snapshotExactRecord as snapshotExactRecord15
|
|
22004
23145
|
} from "@axiom-lattice/protocols";
|
|
22005
|
-
import { randomUUID as
|
|
23146
|
+
import { randomUUID as randomUUID25 } from "crypto";
|
|
22006
23147
|
var BindingNotFoundError = class extends Error {
|
|
22007
23148
|
constructor(message) {
|
|
22008
23149
|
super(message);
|
|
@@ -22095,7 +23236,7 @@ function validateRoomTrustedDispatch(message, options, binding, agentId) {
|
|
|
22095
23236
|
"inputMessageId",
|
|
22096
23237
|
"role",
|
|
22097
23238
|
"title"
|
|
22098
|
-
], ["responsibility"]);
|
|
23239
|
+
], ["responsibility", "source"]);
|
|
22099
23240
|
if (!binding || !targetEnvelope || !target || !context || !authority || typeof authority.expectedRoomThreadId !== "string" || authority.expectedRoomThreadId.length === 0 || trusted?.executionMode !== "followup") return false;
|
|
22100
23241
|
const expectedSender = `room:${context.roomId}:membership:${context.membershipId}`;
|
|
22101
23242
|
return targetEnvelope.adapterChannel === "room" && targetEnvelope.channelInstallationId === message.channelInstallationId && target.sourceRoomMessageId === context.sourceRoomMessageId && target.targetMembershipId === context.membershipId && target.inputMessageId === context.inputMessageId && message.tenantId === context.tenantId && message.sender.id === expectedSender && message.conversation?.id === context.roomId && message.conversation?.type === "group" && binding.tenantId === context.tenantId && binding.channel === "room" && binding.channelInstallationId === message.channelInstallationId && binding.senderId === expectedSender && binding.agentId === context.assistantId && binding.workspaceId === context.workspaceId && binding.projectId === context.projectId && binding.threadId === authority.expectedRoomThreadId && binding.threadMode === "fixed" && agentId === context.assistantId;
|
|
@@ -22286,7 +23427,7 @@ var MessageRouter = class {
|
|
|
22286
23427
|
channel: message.channel,
|
|
22287
23428
|
adapterChannel: adapter.channel
|
|
22288
23429
|
}, "Thread resolved by adapter strategy");
|
|
22289
|
-
const threadStore =
|
|
23430
|
+
const threadStore = getStoreLattice27("default", "thread").store;
|
|
22290
23431
|
try {
|
|
22291
23432
|
await threadStore.createThread(
|
|
22292
23433
|
tenantId,
|
|
@@ -22323,8 +23464,8 @@ var MessageRouter = class {
|
|
|
22323
23464
|
}
|
|
22324
23465
|
}
|
|
22325
23466
|
if (!threadId) {
|
|
22326
|
-
const threadStore =
|
|
22327
|
-
const newThreadId =
|
|
23467
|
+
const threadStore = getStoreLattice27("default", "thread").store;
|
|
23468
|
+
const newThreadId = randomUUID25();
|
|
22328
23469
|
console.log({
|
|
22329
23470
|
event: "dispatch:thread:create",
|
|
22330
23471
|
agentId,
|
|
@@ -22370,7 +23511,7 @@ var MessageRouter = class {
|
|
|
22370
23511
|
workspace_id: binding?.workspaceId || "",
|
|
22371
23512
|
project_id: binding?.projectId || ""
|
|
22372
23513
|
});
|
|
22373
|
-
const inputMessageId =
|
|
23514
|
+
const inputMessageId = randomUUID25();
|
|
22374
23515
|
const replyTarget = internalRoom ? correlatedRoomReplyTarget(message.replyTarget, inputMessageId) : message.replyTarget;
|
|
22375
23516
|
const replyAdapter = replyTarget ? this.adapterRegistry.get(replyTarget.adapterChannel) : void 0;
|
|
22376
23517
|
if (options.trustedMessageOptions && (!this.trustedDispatcher || !replyTarget || !replyAdapter)) {
|
|
@@ -22786,35 +23927,49 @@ function getConfiguredSandboxProvider() {
|
|
|
22786
23927
|
}
|
|
22787
23928
|
async function restoreMcpConnections() {
|
|
22788
23929
|
try {
|
|
22789
|
-
const storeLattice =
|
|
23930
|
+
const storeLattice = getStoreLattice28("default", "mcp");
|
|
22790
23931
|
const store = storeLattice.store;
|
|
22791
23932
|
if (!store) {
|
|
22792
23933
|
logger5.info("MCP store not configured, skipping connection restoration");
|
|
22793
23934
|
return;
|
|
22794
23935
|
}
|
|
22795
|
-
const configs = await store.
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
for (const config of
|
|
23936
|
+
const configs = (await store.getAllConfigsWithoutTenant()).filter(
|
|
23937
|
+
(c2) => c2.status === "connected"
|
|
23938
|
+
);
|
|
23939
|
+
if (configs.length === 0) return;
|
|
23940
|
+
logger5.info(`Restoring ${configs.length} MCP server connections...`);
|
|
23941
|
+
for (const config of configs) {
|
|
22801
23942
|
try {
|
|
22802
|
-
const connection =
|
|
22803
|
-
|
|
23943
|
+
const connection = convertToConnection2(config.config);
|
|
23944
|
+
mcpManager3.registerServer(config.tenantId, config.key, connection, {
|
|
23945
|
+
timeoutMs: config.config.timeout,
|
|
23946
|
+
maxAttempts: config.config.retryAttempts,
|
|
23947
|
+
selectedTools: config.selectedTools
|
|
23948
|
+
});
|
|
22804
23949
|
} catch (err) {
|
|
22805
|
-
logger5.warn(`Failed to prepare MCP server "${config.key}"
|
|
23950
|
+
logger5.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
|
|
22806
23951
|
}
|
|
22807
23952
|
}
|
|
22808
|
-
await
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
23953
|
+
await mcpManager3.connectAll();
|
|
23954
|
+
const results = await Promise.allSettled(
|
|
23955
|
+
configs.map(
|
|
23956
|
+
(config) => mcpManager3.registerToolsToToolLattice(
|
|
23957
|
+
config.tenantId,
|
|
23958
|
+
config.key,
|
|
23959
|
+
config.selectedTools
|
|
23960
|
+
)
|
|
23961
|
+
)
|
|
23962
|
+
);
|
|
23963
|
+
results.forEach((result, index) => {
|
|
23964
|
+
if (result.status === "rejected") {
|
|
23965
|
+
const config = configs[index];
|
|
23966
|
+
logger5.warn(
|
|
23967
|
+
`Failed to register tools for MCP server "${config.tenantId}/${config.key}"`,
|
|
23968
|
+
{ error: result.reason }
|
|
23969
|
+
);
|
|
22815
23970
|
}
|
|
22816
|
-
}
|
|
22817
|
-
logger5.info(`MCP connection restoration complete: ${
|
|
23971
|
+
});
|
|
23972
|
+
logger5.info(`MCP connection restoration complete: ${configs.length} servers`);
|
|
22818
23973
|
} catch (err) {
|
|
22819
23974
|
logger5.error("MCP connection restoration failed", { error: err });
|
|
22820
23975
|
}
|
|
@@ -22836,9 +23991,9 @@ var start = async (config) => {
|
|
|
22836
23991
|
let projectRoomRealtimeRuntime;
|
|
22837
23992
|
if (projectRoomRealtimeMode === "single-gateway") {
|
|
22838
23993
|
projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
|
|
22839
|
-
tasks:
|
|
22840
|
-
rooms:
|
|
22841
|
-
memberships:
|
|
23994
|
+
tasks: getStoreLattice28("default", "task").store,
|
|
23995
|
+
rooms: getStoreLattice28("default", "projectRoom").store,
|
|
23996
|
+
memberships: getStoreLattice28("default", "projectBotMembership").store,
|
|
22842
23997
|
log: { warn: (message, context) => logger5.warn(message, context) }
|
|
22843
23998
|
});
|
|
22844
23999
|
}
|
|
@@ -22881,7 +24036,7 @@ var start = async (config) => {
|
|
|
22881
24036
|
}
|
|
22882
24037
|
setEvalRunService(evalRunner);
|
|
22883
24038
|
try {
|
|
22884
|
-
const menuStore =
|
|
24039
|
+
const menuStore = getStoreLattice28("default", "menu").store;
|
|
22885
24040
|
setMenuRegistry(menuStore);
|
|
22886
24041
|
logger5.info("Menu registry initialized");
|
|
22887
24042
|
} catch {
|
|
@@ -22897,12 +24052,12 @@ var start = async (config) => {
|
|
|
22897
24052
|
});
|
|
22898
24053
|
try {
|
|
22899
24054
|
const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-7UGQ7GMW.mjs");
|
|
22900
|
-
const { registerA2AStandardRoutes } = await import("./a2a-standard-
|
|
24055
|
+
const { registerA2AStandardRoutes } = await import("./a2a-standard-P7JDL6ZA.mjs");
|
|
22901
24056
|
const a2a = await import("./a2a-FGYX2HH3.mjs");
|
|
22902
|
-
const a2aKeyStore =
|
|
22903
|
-
const assistantStore =
|
|
22904
|
-
const projectStore =
|
|
22905
|
-
const taskStore =
|
|
24057
|
+
const a2aKeyStore = getStoreLattice28("default", "a2aApiKey").store;
|
|
24058
|
+
const assistantStore = getStoreLattice28("default", "assistant").store;
|
|
24059
|
+
const projectStore = getStoreLattice28("default", "project").store;
|
|
24060
|
+
const taskStore = getStoreLattice28("default", "task").store;
|
|
22906
24061
|
const authService = new A2AAuthService({
|
|
22907
24062
|
keyStore: a2aKeyStore,
|
|
22908
24063
|
assistantStore,
|
|
@@ -22927,22 +24082,22 @@ var start = async (config) => {
|
|
|
22927
24082
|
});
|
|
22928
24083
|
}
|
|
22929
24084
|
try {
|
|
22930
|
-
const { registerOpenRoutes } = await import("./open-
|
|
22931
|
-
const { registerOpenSessionRoutes } = await import("./open-session-
|
|
22932
|
-
const { registerOpenCatalogRoutes } = await import("./open-catalog-
|
|
22933
|
-
const { registerOpenFilesRoutes } = await import("./open-files-
|
|
24085
|
+
const { registerOpenRoutes } = await import("./open-AMEMJOFJ.mjs");
|
|
24086
|
+
const { registerOpenSessionRoutes } = await import("./open-session-LBTCU3AE.mjs");
|
|
24087
|
+
const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
|
|
24088
|
+
const { registerOpenFilesRoutes } = await import("./open-files-EJ6O2SGX.mjs");
|
|
22934
24089
|
const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
|
|
22935
|
-
const { registerOpenExposureRoutes } = await import("./open-exposure-
|
|
22936
|
-
const { openLattice } = await import("./openLattice-
|
|
24090
|
+
const { registerOpenExposureRoutes } = await import("./open-exposure-I44Q4M3T.mjs");
|
|
24091
|
+
const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
|
|
22937
24092
|
const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
|
|
22938
24093
|
const { createOpenSandboxProvider } = await import("./sandboxProvider-I3P66VXT.mjs");
|
|
22939
24094
|
const { createAgentProvider } = await import("./agentProvider-X4FVQPKW.mjs");
|
|
22940
|
-
const { registerExposedPluginTools } = await import("./registerExposedPlugins-
|
|
24095
|
+
const { registerExposedPluginTools } = await import("./registerExposedPlugins-6GCOE64C.mjs");
|
|
22941
24096
|
const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
|
|
22942
24097
|
openLattice.register(createKbProvider(new CollectionService2()));
|
|
22943
24098
|
openLattice.register(createOpenSandboxProvider());
|
|
22944
|
-
const threadStore =
|
|
22945
|
-
const assistantStore =
|
|
24099
|
+
const threadStore = getStoreLattice28("default", "thread").store;
|
|
24100
|
+
const assistantStore = getStoreLattice28("default", "assistant").store;
|
|
22946
24101
|
openLattice.register(
|
|
22947
24102
|
createAgentProvider({
|
|
22948
24103
|
threadStore,
|
|
@@ -22970,8 +24125,8 @@ var start = async (config) => {
|
|
|
22970
24125
|
logger5.info("Registered sandbox manager from env configuration");
|
|
22971
24126
|
}
|
|
22972
24127
|
try {
|
|
22973
|
-
const { ResourceController } = await import("./resources-
|
|
22974
|
-
const sharedResourceStore =
|
|
24128
|
+
const { ResourceController } = await import("./resources-DXPXAXEZ.mjs");
|
|
24129
|
+
const sharedResourceStore = getStoreLattice28("default", "sharedResource").store;
|
|
22975
24130
|
const cache = new TokenCache();
|
|
22976
24131
|
const resourceController = new ResourceController({
|
|
22977
24132
|
store: sharedResourceStore,
|