@axiom-lattice/gateway 4.3.3 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{a2a-standard-ALXWP7V6.mjs → a2a-standard-RWR5JTMH.mjs} +64 -36
- package/dist/a2a-standard-RWR5JTMH.mjs.map +1 -0
- package/dist/{registerExposedPlugins-ZBOEHJYH.mjs → chunk-3XIIQ6EK.mjs} +61 -19
- 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-2BDEWFQ6.js +1 -0
- package/dist/embed/embed-page-AgentGovernanceWorkbench-WG7E3G5T.js +5 -0
- package/dist/embed/embed-page-MessageGraph-5SWPEQ4V.js +6 -0
- package/dist/embed/{embed-page-TasksSideApp-F4XUHPTG.css → embed-page-MessageGraph-UT2TCPSO.css} +1 -1
- package/dist/embed/embed-page-ReactChart-MXGF35OQ.css +1 -0
- package/dist/embed/{embed-page-ReactChart-XS7D26VP.js → embed-page-ReactChart-ZYWRXIMC.js} +1 -1
- package/dist/embed/embed-page-SkillFlow-TKBMB5VV.js +1 -0
- package/dist/embed/{embed-page-TopologyRuntimeView-2NQ3OOYI.css → embed-page-TasksSideApp-HV6DKQ26.css} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-MQTUCJO4.js → embed-page-TasksSideApp-X6255GEF.js} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-7T2HRXLG.js → embed-page-TeamWorkspace-XNZM7AV7.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-DGSHSTIU.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-ST76M33F.js → embed-page-WorkflowAutomationView-BHIRIWV3.js} +1 -1
- package/dist/embed/embed-page-WorkflowAutomationView-YQFB3CTV.css +1 -0
- package/dist/embed/embed-page-chunk-DEDTYRZE.js +245 -0
- package/dist/embed/{embed-page-chunk-LE6OFJ7R.js → embed-page-chunk-HEO7X7LH.js} +1 -1
- package/dist/embed/{embed-page-chunk-T25T2YE6.js → embed-page-chunk-HMTKPZ5B.js} +7 -5
- package/dist/embed/embed-page-chunk-JMZ7OODN.js +2 -0
- package/dist/embed/embed-page-chunk-SPC4BYNU.js +6974 -0
- package/dist/embed/{embed-page-chunk-ZKJ7Z7P3.js → embed-page-chunk-XUXEGF3S.js} +1 -1
- package/dist/embed/{embed-page-chunk-Z4S4OYA4.js → embed-page-chunk-YCX5YZRP.js} +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.js +5188 -4089
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1574 -433
- 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-AgentGovernanceWorkbench-ZNW72EEJ.js +0 -5
- 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-RLP3BC7G.js +0 -245
- 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-ZBOEHJYH.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 });
|
|
@@ -11619,6 +12497,8 @@ function createTaskController(deps) {
|
|
|
11619
12497
|
ownerType = "agent";
|
|
11620
12498
|
ownerId = body.ownerId ?? "";
|
|
11621
12499
|
}
|
|
12500
|
+
const requestedSource = body.context?.source;
|
|
12501
|
+
const source = typeof requestedSource === "string" && requestedSource.length > 0 && requestedSource !== "project_room" && requestedSource !== "project_task" ? requestedSource : "project_room";
|
|
11622
12502
|
const created = await deps.tasks.create({
|
|
11623
12503
|
...body,
|
|
11624
12504
|
tenantId,
|
|
@@ -11630,21 +12510,18 @@ function createTaskController(deps) {
|
|
|
11630
12510
|
result: void 0,
|
|
11631
12511
|
failureReason: void 0,
|
|
11632
12512
|
requireReview: false,
|
|
11633
|
-
context: { ...body.context ?? {}, source
|
|
12513
|
+
context: { ...body.context ?? {}, source }
|
|
11634
12514
|
});
|
|
11635
|
-
if (ownerType === "agent") notifyProjectTaskAdmission(created);
|
|
11636
12515
|
return reply.code(201).send({ success: true, data: created });
|
|
11637
12516
|
});
|
|
11638
12517
|
});
|
|
11639
12518
|
const update = withErrors(async (request, reply) => {
|
|
11640
12519
|
const body = snapshotUpdate(request.body);
|
|
11641
12520
|
await preauthorizeExplicitProject(request, authorize);
|
|
11642
|
-
const current = await deps.tasks.getById(
|
|
12521
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11643
12522
|
if (!current) return sendNotFound(reply);
|
|
11644
12523
|
await authorizeMutation(request, current, authorize);
|
|
11645
12524
|
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
12525
|
if (body.projectId !== void 0 && body.projectId !== locked.projectId || body.workspaceId !== void 0 && body.workspaceId !== locked.workspaceId) throw notFound();
|
|
11649
12526
|
await validateReferences(deps.tasks, locked, body.parentId, body.dependencies);
|
|
11650
12527
|
const updates = { ...body };
|
|
@@ -11657,7 +12534,7 @@ function createTaskController(deps) {
|
|
|
11657
12534
|
});
|
|
11658
12535
|
const remove = withErrors(async (request, reply) => {
|
|
11659
12536
|
await preauthorizeExplicitProject(request, authorize);
|
|
11660
|
-
const current = await deps.tasks.getById(
|
|
12537
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11661
12538
|
if (!current) return sendNotFound(reply);
|
|
11662
12539
|
await authorizeMutation(request, current, authorize);
|
|
11663
12540
|
return lockedTask(request, current, async (locked) => {
|
|
@@ -11671,7 +12548,7 @@ function createTaskController(deps) {
|
|
|
11671
12548
|
const complete = withErrors(async (request, reply) => {
|
|
11672
12549
|
const body = snapshotComplete(request.body);
|
|
11673
12550
|
await preauthorizeExplicitProject(request, authorize);
|
|
11674
|
-
const current = await deps.tasks.getById(
|
|
12551
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11675
12552
|
if (!current) return sendNotFound(reply);
|
|
11676
12553
|
await authorizeMutation(request, current, authorize);
|
|
11677
12554
|
return lockedTask(request, current, async (locked) => {
|
|
@@ -11688,7 +12565,7 @@ function createTaskController(deps) {
|
|
|
11688
12565
|
});
|
|
11689
12566
|
const workItems = withErrors(async (request, reply) => {
|
|
11690
12567
|
await preauthorizeExplicitProject(request, authorize);
|
|
11691
|
-
const current = await deps.tasks.getById(
|
|
12568
|
+
const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
|
|
11692
12569
|
if (!current) return sendNotFound(reply);
|
|
11693
12570
|
await authorizeRead(request, current, authorize);
|
|
11694
12571
|
const query = exactObject(request.query, ["action", "limit", "offset"]);
|
|
@@ -11704,66 +12581,7 @@ function createTaskController(deps) {
|
|
|
11704
12581
|
});
|
|
11705
12582
|
return reply.send({ success: true, data: rows });
|
|
11706
12583
|
});
|
|
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 };
|
|
12584
|
+
return { list, get, create, update, remove, complete, workItems };
|
|
11767
12585
|
}
|
|
11768
12586
|
function parseMetadata(value) {
|
|
11769
12587
|
let parsed = value;
|
|
@@ -12082,7 +12900,7 @@ import { createHmac as createHmac3 } from "crypto";
|
|
|
12082
12900
|
import { z as z5, ZodError as ZodError2 } from "zod";
|
|
12083
12901
|
|
|
12084
12902
|
// src/services/agent-web-app/AgentWebAppRuntimeService.ts
|
|
12085
|
-
import { createHash as
|
|
12903
|
+
import { createHash as createHash3, randomUUID as randomUUID13 } from "crypto";
|
|
12086
12904
|
import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock } from "@axiom-lattice/protocols";
|
|
12087
12905
|
function isRecord2(value) {
|
|
12088
12906
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12276,10 +13094,10 @@ ${formatFileRefsSection(refs)}` : input.content;
|
|
|
12276
13094
|
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
|
|
12277
13095
|
this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
|
|
12278
13096
|
if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
|
|
12279
|
-
const userDigest =
|
|
12280
|
-
const contentDigest =
|
|
13097
|
+
const userDigest = createHash3("sha256").update(userId, "utf8").digest("hex");
|
|
13098
|
+
const contentDigest = createHash3("sha256").update(file.bytes).digest("hex");
|
|
12281
13099
|
const displayName = file.name;
|
|
12282
|
-
const physicalName = `${
|
|
13100
|
+
const physicalName = `${randomUUID13()}-${sanitizePhysicalFilename(file.name)}`;
|
|
12283
13101
|
const saved = await this.deps.saveProjectFile({
|
|
12284
13102
|
tenantId: runtime.webApp.tenantId,
|
|
12285
13103
|
workspaceId: runtime.project.workspaceId,
|
|
@@ -12431,7 +13249,7 @@ ${formatFileRefsSection(refs)}` : input.content;
|
|
|
12431
13249
|
const thread = await this.deps.threadStore.createThread(
|
|
12432
13250
|
runtime.webApp.tenantId,
|
|
12433
13251
|
runtime.webApp.assistantId,
|
|
12434
|
-
|
|
13252
|
+
randomUUID13(),
|
|
12435
13253
|
{ metadata: this.metadata(runtime, userId, normalizeLabel(rawLabel)) }
|
|
12436
13254
|
);
|
|
12437
13255
|
return toRuntimeThread(thread);
|
|
@@ -12483,7 +13301,7 @@ function implicitThreadId(runtime, userId) {
|
|
|
12483
13301
|
userId,
|
|
12484
13302
|
runtime.project.id
|
|
12485
13303
|
]);
|
|
12486
|
-
const bytes =
|
|
13304
|
+
const bytes = createHash3("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
|
|
12487
13305
|
bytes[6] = bytes[6] & 15 | 128;
|
|
12488
13306
|
bytes[8] = bytes[8] & 63 | 128;
|
|
12489
13307
|
const hex = bytes.toString("hex");
|
|
@@ -13251,7 +14069,7 @@ function registerAgentWebAppEmbedRoutes(app2, deps = {}) {
|
|
|
13251
14069
|
}
|
|
13252
14070
|
|
|
13253
14071
|
// src/services/agent-web-app/AgentWebAppAttachmentRef.ts
|
|
13254
|
-
import { createCipheriv, createDecipheriv, createHash as
|
|
14072
|
+
import { createCipheriv, createDecipheriv, createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
|
|
13255
14073
|
var TTL_MS = 15 * 6e4;
|
|
13256
14074
|
function resolveAgentWebAppAttachmentSecret(consoleSecret) {
|
|
13257
14075
|
return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
|
|
@@ -13260,7 +14078,7 @@ var AgentWebAppAttachmentRefService = class {
|
|
|
13260
14078
|
constructor(secret, now = Date.now) {
|
|
13261
14079
|
this.now = now;
|
|
13262
14080
|
if (!secret) throw new Error("Attachment signing secret is required");
|
|
13263
|
-
this.key =
|
|
14081
|
+
this.key = createHash4("sha256").update(secret, "utf8").digest();
|
|
13264
14082
|
}
|
|
13265
14083
|
sign(input) {
|
|
13266
14084
|
const payload = {
|
|
@@ -13322,7 +14140,7 @@ function decodeCanonical(value, expectedBytes) {
|
|
|
13322
14140
|
return decoded;
|
|
13323
14141
|
}
|
|
13324
14142
|
function digestUser(userId) {
|
|
13325
|
-
return
|
|
14143
|
+
return createHash4("sha256").update(userId, "utf8").digest("hex");
|
|
13326
14144
|
}
|
|
13327
14145
|
function isAttachmentPayload(value) {
|
|
13328
14146
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -13682,12 +14500,12 @@ function registerCapabilityBundleRoutes(app2, controller) {
|
|
|
13682
14500
|
}
|
|
13683
14501
|
|
|
13684
14502
|
// src/services/collaboration/ProjectRoomService.ts
|
|
13685
|
-
import { randomUUID as
|
|
14503
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
13686
14504
|
var ProjectRoomService = class {
|
|
13687
14505
|
/** Creates a project room service from explicitly injected collaborators. */
|
|
13688
14506
|
constructor(deps) {
|
|
13689
14507
|
this.deps = deps;
|
|
13690
|
-
this.idFactory = deps.idFactory ??
|
|
14508
|
+
this.idFactory = deps.idFactory ?? randomUUID14;
|
|
13691
14509
|
}
|
|
13692
14510
|
/**
|
|
13693
14511
|
* Authorizes read access and ensures the project's main room.
|
|
@@ -13747,7 +14565,7 @@ var ProjectRoomService = class {
|
|
|
13747
14565
|
};
|
|
13748
14566
|
|
|
13749
14567
|
// src/services/collaboration/ProjectMembershipService.ts
|
|
13750
|
-
import { randomUUID as
|
|
14568
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
13751
14569
|
var ProjectMembershipServiceError = class extends Error {
|
|
13752
14570
|
constructor(code, statusCode, message) {
|
|
13753
14571
|
super(message);
|
|
@@ -13763,7 +14581,7 @@ function isMembershipConflict(error) {
|
|
|
13763
14581
|
var ProjectMembershipService = class {
|
|
13764
14582
|
constructor(deps) {
|
|
13765
14583
|
this.deps = deps;
|
|
13766
|
-
this.idFactory = deps.idFactory ??
|
|
14584
|
+
this.idFactory = deps.idFactory ?? randomUUID15;
|
|
13767
14585
|
this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
|
|
13768
14586
|
}
|
|
13769
14587
|
async list(actor3) {
|
|
@@ -13897,7 +14715,7 @@ var ProjectMembershipService = class {
|
|
|
13897
14715
|
};
|
|
13898
14716
|
|
|
13899
14717
|
// src/services/collaboration/ProjectRosterService.ts
|
|
13900
|
-
import { randomUUID as
|
|
14718
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
13901
14719
|
var ProjectRosterServiceError = class extends Error {
|
|
13902
14720
|
/** Creates a stable roster service error. */
|
|
13903
14721
|
constructor(code, statusCode, message) {
|
|
@@ -13916,7 +14734,7 @@ var ProjectRosterService = class {
|
|
|
13916
14734
|
/** Creates a roster service from explicitly injected collaborators. */
|
|
13917
14735
|
constructor(deps) {
|
|
13918
14736
|
this.deps = deps;
|
|
13919
|
-
this.idFactory = deps.idFactory ??
|
|
14737
|
+
this.idFactory = deps.idFactory ?? randomUUID16;
|
|
13920
14738
|
this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
|
|
13921
14739
|
}
|
|
13922
14740
|
/** Lists all bot memberships visible to a project reader. */
|
|
@@ -14150,7 +14968,7 @@ var ProjectRosterService = class {
|
|
|
14150
14968
|
};
|
|
14151
14969
|
|
|
14152
14970
|
// src/services/collaboration/RoomMessageService.ts
|
|
14153
|
-
import { createHash as
|
|
14971
|
+
import { createHash as createHash5, randomUUID as randomUUID17 } from "crypto";
|
|
14154
14972
|
|
|
14155
14973
|
// src/services/collaboration/ProjectBotMembershipSnapshot.ts
|
|
14156
14974
|
import { snapshotExactRecord as snapshotExactRecord4 } from "@axiom-lattice/protocols";
|
|
@@ -14239,7 +15057,7 @@ var RoomMessageService = class {
|
|
|
14239
15057
|
constructor(deps, options = {}) {
|
|
14240
15058
|
this.deps = deps;
|
|
14241
15059
|
this.idempotentOutcomes = /* @__PURE__ */ new Map();
|
|
14242
|
-
this.idFactory = deps.idFactory ??
|
|
15060
|
+
this.idFactory = deps.idFactory ?? randomUUID17;
|
|
14243
15061
|
const limit = options.idempotentOutcomeLimit ?? 1e3;
|
|
14244
15062
|
if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("idempotentOutcomeLimit must be a positive safe integer");
|
|
14245
15063
|
this.idempotentOutcomeLimit = limit;
|
|
@@ -14420,7 +15238,7 @@ var RoomMessageService = class {
|
|
|
14420
15238
|
let idempotencyKey;
|
|
14421
15239
|
if (value.requestIdempotencyKey !== void 0) {
|
|
14422
15240
|
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}:${
|
|
15241
|
+
idempotencyKey = `room-human:${actor3.userId}:${createHash5("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
|
|
14424
15242
|
}
|
|
14425
15243
|
return { text, mentions, botIds, ...value.replyToMessageId === void 0 ? {} : { replyToMessageId: value.replyToMessageId }, ...idempotencyKey === void 0 ? {} : { idempotencyKey } };
|
|
14426
15244
|
}
|
|
@@ -14477,7 +15295,7 @@ function cloneRoomMessage(message) {
|
|
|
14477
15295
|
};
|
|
14478
15296
|
}
|
|
14479
15297
|
function fingerprintRequest(input) {
|
|
14480
|
-
return
|
|
15298
|
+
return createHash5("sha256").update(JSON.stringify({
|
|
14481
15299
|
text: input.text,
|
|
14482
15300
|
mentions: input.mentions,
|
|
14483
15301
|
replyToMessageId: input.replyToMessageId ?? null
|
|
@@ -14492,7 +15310,7 @@ import { ZodError as ZodError3 } from "zod";
|
|
|
14492
15310
|
import { toProjectRoomPublicMessage } from "@axiom-lattice/protocols";
|
|
14493
15311
|
|
|
14494
15312
|
// src/services/collaboration/RoomInterruptService.ts
|
|
14495
|
-
import { randomUUID as
|
|
15313
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
14496
15314
|
|
|
14497
15315
|
// src/router/roomInterrupt.ts
|
|
14498
15316
|
var CLARIFY_FENCE = /^```clarify\n([\s\S]*?)\n```$/;
|
|
@@ -14540,7 +15358,7 @@ var RoomInterruptService = class {
|
|
|
14540
15358
|
/** Creates the service from explicitly injected stores and dispatcher. */
|
|
14541
15359
|
constructor(deps) {
|
|
14542
15360
|
this.deps = deps;
|
|
14543
|
-
this.idFactory = deps.idFactory ??
|
|
15361
|
+
this.idFactory = deps.idFactory ?? randomUUID18;
|
|
14544
15362
|
}
|
|
14545
15363
|
/** Resolves a pending clarify interrupt with no answers. */
|
|
14546
15364
|
async skip(input) {
|
|
@@ -14588,6 +15406,7 @@ var RoomInterruptService = class {
|
|
|
14588
15406
|
inputMessageId,
|
|
14589
15407
|
role: membership.role,
|
|
14590
15408
|
title: membership.title,
|
|
15409
|
+
source: "project_room",
|
|
14591
15410
|
...membership.responsibility === void 0 ? {} : { responsibility: membership.responsibility }
|
|
14592
15411
|
}
|
|
14593
15412
|
}
|
|
@@ -14795,7 +15614,7 @@ async function execute(reply, operation) {
|
|
|
14795
15614
|
}
|
|
14796
15615
|
|
|
14797
15616
|
// src/routes/cospace.ts
|
|
14798
|
-
import { randomUUID as
|
|
15617
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
14799
15618
|
import { ZodError as ZodError5 } from "zod";
|
|
14800
15619
|
import {
|
|
14801
15620
|
generateDirectHumanProjectId,
|
|
@@ -14829,7 +15648,7 @@ async function computeUnread(deps, tenantId, userId, roomId, lastMessage) {
|
|
|
14829
15648
|
});
|
|
14830
15649
|
}
|
|
14831
15650
|
function registerCoSpaceRoutes(app2, deps) {
|
|
14832
|
-
const idFactory = deps.idFactory ?? (() =>
|
|
15651
|
+
const idFactory = deps.idFactory ?? (() => randomUUID19());
|
|
14833
15652
|
app2.get(
|
|
14834
15653
|
"/api/workspaces/:workspaceId/rooms",
|
|
14835
15654
|
async (request, reply) => {
|
|
@@ -15713,6 +16532,7 @@ var RoomDispatchService = class {
|
|
|
15713
16532
|
inputMessageId: "pending-router-input",
|
|
15714
16533
|
role: membership.role,
|
|
15715
16534
|
title: membership.title,
|
|
16535
|
+
source: "project_room",
|
|
15716
16536
|
...membership.responsibility === void 0 ? {} : { responsibility: membership.responsibility }
|
|
15717
16537
|
} }
|
|
15718
16538
|
}
|
|
@@ -15784,8 +16604,7 @@ function exactValues(value, required, optional = []) {
|
|
|
15784
16604
|
}
|
|
15785
16605
|
|
|
15786
16606
|
// src/services/collaboration/CoordinatorWakeNotifier.ts
|
|
15787
|
-
import { randomUUID as
|
|
15788
|
-
import { isProjectLifecycleTask as isProjectLifecycleTask2 } from "@axiom-lattice/core";
|
|
16607
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
15789
16608
|
import {
|
|
15790
16609
|
snapshotExactRecord as snapshotExactRecord6
|
|
15791
16610
|
} from "@axiom-lattice/protocols";
|
|
@@ -15801,7 +16620,7 @@ var CoordinatorWakeNotifier = class {
|
|
|
15801
16620
|
this.accepted = /* @__PURE__ */ new Set();
|
|
15802
16621
|
this.maxPending = deps.maxPending ?? 1024;
|
|
15803
16622
|
if (!Number.isSafeInteger(this.maxPending) || this.maxPending < 1) throw new RangeError("maxPending must be a positive safe integer");
|
|
15804
|
-
this.idFactory = deps.idFactory ??
|
|
16623
|
+
this.idFactory = deps.idFactory ?? randomUUID20;
|
|
15805
16624
|
}
|
|
15806
16625
|
/** Admits one notification without awaiting any store or dispatch operation. */
|
|
15807
16626
|
handle(value, anchorMessageId) {
|
|
@@ -15873,7 +16692,7 @@ var CoordinatorWakeNotifier = class {
|
|
|
15873
16692
|
try {
|
|
15874
16693
|
const task = snapshotTaskItem(await this.deps.tasks.getById(notification.tenantId, notification.taskId));
|
|
15875
16694
|
if (this.closed) return;
|
|
15876
|
-
if (!task || !
|
|
16695
|
+
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
16696
|
this.warn("project-room:coordinator-wake-stale", { taskId: safeId(notification.taskId), eventKey: safeId(notification.eventKey) });
|
|
15878
16697
|
return;
|
|
15879
16698
|
}
|
|
@@ -16156,10 +16975,10 @@ var ProjectTaskExecutionMutex = class {
|
|
|
16156
16975
|
};
|
|
16157
16976
|
|
|
16158
16977
|
// src/services/collaboration/ProjectTaskExecutionService.ts
|
|
16159
|
-
import { randomUUID as
|
|
16978
|
+
import { randomUUID as randomUUID21 } from "crypto";
|
|
16160
16979
|
import {
|
|
16980
|
+
createTaskRunBridge,
|
|
16161
16981
|
ThreadStatus as ThreadStatus3,
|
|
16162
|
-
isProjectLifecycleTask as isProjectLifecycleTask3,
|
|
16163
16982
|
parseExternalDependency
|
|
16164
16983
|
} from "@axiom-lattice/core";
|
|
16165
16984
|
import {
|
|
@@ -16193,6 +17012,7 @@ var ProjectTaskExecutionService = class {
|
|
|
16193
17012
|
if (!Number.isFinite(options.lifecycleSettlementTimeoutMs) || options.lifecycleSettlementTimeoutMs <= 0) {
|
|
16194
17013
|
throw new Error("Project task lifecycle settlement timeout must be positive");
|
|
16195
17014
|
}
|
|
17015
|
+
this.runBridge = createTaskRunBridge({ lifecycle: this.deps.lifecycle });
|
|
16196
17016
|
}
|
|
16197
17017
|
/** Validates, atomically starts, dispatches, and settles a pending Project Task. */
|
|
16198
17018
|
async start(input, signal) {
|
|
@@ -16200,24 +17020,24 @@ var ProjectTaskExecutionService = class {
|
|
|
16200
17020
|
this.throwIfCancelled(signal);
|
|
16201
17021
|
const ensured = await this.deps.threads.ensure({ ...validated, mode: "current_owner" });
|
|
16202
17022
|
this.throwIfCancelled(signal);
|
|
16203
|
-
const
|
|
17023
|
+
const begun = await this.runBridge.beginRun({
|
|
16204
17024
|
tenantId: input.tenantId,
|
|
16205
|
-
|
|
17025
|
+
task: validated.task,
|
|
16206
17026
|
actor: SYSTEM_ACTOR,
|
|
16207
17027
|
threadId: ensured.thread.id
|
|
16208
17028
|
});
|
|
16209
|
-
if (!
|
|
16210
|
-
if (signal?.aborted && (!
|
|
16211
|
-
if (!
|
|
17029
|
+
if (!begun.success) throw new ProjectTaskExecutionError(begun.code, begun.error);
|
|
17030
|
+
if (signal?.aborted && (!begun.transitioned || !begun.mutated)) this.throwIfCancelled(signal);
|
|
17031
|
+
if (!begun.transitioned || !begun.mutated) {
|
|
16212
17032
|
throw new ProjectTaskExecutionError("PROJECT_TASK_START_CONFLICT", "Another caller already started this task");
|
|
16213
17033
|
}
|
|
16214
17034
|
if (signal?.aborted) return this.compensateCancellation(
|
|
16215
|
-
|
|
17035
|
+
begun.task,
|
|
16216
17036
|
ensured.thread,
|
|
16217
|
-
`project-task:${
|
|
17037
|
+
`project-task:${begun.task.id}:${ensured.thread.id}:start`,
|
|
16218
17038
|
false
|
|
16219
17039
|
);
|
|
16220
|
-
const current = await this.requireCurrentThread(
|
|
17040
|
+
const current = await this.requireCurrentThread(begun.task, ensured.thread);
|
|
16221
17041
|
if (signal?.aborted) return this.compensateCancellation(
|
|
16222
17042
|
current,
|
|
16223
17043
|
ensured.thread,
|
|
@@ -16360,7 +17180,7 @@ var ProjectTaskExecutionService = class {
|
|
|
16360
17180
|
}
|
|
16361
17181
|
async requireTask(input) {
|
|
16362
17182
|
const value = snapshotTaskItem(await this.deps.tasks.getById(input.tenantId, input.taskId));
|
|
16363
|
-
if (!value || value.tenantId !== input.tenantId || value.id !== input.taskId || !
|
|
17183
|
+
if (!value || value.tenantId !== input.tenantId || value.id !== input.taskId || !isProjectLifecycleTask(value)) {
|
|
16364
17184
|
throw new ProjectTaskExecutionError("PROJECT_TASK_NOT_READY", "Project task does not exist or is out of scope");
|
|
16365
17185
|
}
|
|
16366
17186
|
return value;
|
|
@@ -16424,7 +17244,7 @@ var ProjectTaskExecutionService = class {
|
|
|
16424
17244
|
return current;
|
|
16425
17245
|
}
|
|
16426
17246
|
async dispatchRun(task, thread, mode, signal) {
|
|
16427
|
-
const messageId = (this.options.idFactory ??
|
|
17247
|
+
const messageId = (this.options.idFactory ?? randomUUID21)();
|
|
16428
17248
|
if (signal?.aborted) return this.compensateCancellation(task, thread, messageId, false);
|
|
16429
17249
|
const settlement = this.armSettlement(task, thread, messageId);
|
|
16430
17250
|
try {
|
|
@@ -16618,13 +17438,12 @@ var ProjectTaskExecutionService = class {
|
|
|
16618
17438
|
const summary = clarify.questions.find(
|
|
16619
17439
|
(question) => typeof question.question === "string" && question.question.trim().length > 0
|
|
16620
17440
|
)?.question.trim() ?? "Waiting for clarification from the room";
|
|
16621
|
-
const result = await this.withLifecycleTimeout(this.
|
|
17441
|
+
const result = await this.withLifecycleTimeout(this.runBridge.settleRun({
|
|
16622
17442
|
tenantId: task.tenantId,
|
|
16623
|
-
|
|
16624
|
-
type: "missing_input",
|
|
16625
|
-
summary,
|
|
17443
|
+
task,
|
|
16626
17444
|
actor: SYSTEM_ACTOR,
|
|
16627
|
-
threadId: thread.id
|
|
17445
|
+
threadId: thread.id,
|
|
17446
|
+
outcome: { kind: "interrupted", summary }
|
|
16628
17447
|
}), void 0);
|
|
16629
17448
|
if (!result || !result.success) return void 0;
|
|
16630
17449
|
return result.task ?? await this.reloadTask(task);
|
|
@@ -16686,12 +17505,12 @@ var ProjectTaskExecutionService = class {
|
|
|
16686
17505
|
}
|
|
16687
17506
|
let failed;
|
|
16688
17507
|
try {
|
|
16689
|
-
failed = await this.
|
|
17508
|
+
failed = await this.runBridge.settleRun({
|
|
16690
17509
|
tenantId: task.tenantId,
|
|
16691
|
-
|
|
16692
|
-
failureReason: error,
|
|
17510
|
+
task,
|
|
16693
17511
|
actor: SYSTEM_ACTOR,
|
|
16694
|
-
threadId: thread.id
|
|
17512
|
+
threadId: thread.id,
|
|
17513
|
+
outcome: { kind: "failed", lifecycleHandled: false, error }
|
|
16695
17514
|
});
|
|
16696
17515
|
} catch {
|
|
16697
17516
|
return { kind: "failed", task: current, thread, messageId, error, cause, lifecycleHandled: false };
|
|
@@ -16752,7 +17571,8 @@ function buildTrustedTaskContext(task, thread, inputMessageId) {
|
|
|
16752
17571
|
assistantId: task.ownerId,
|
|
16753
17572
|
taskId: task.id,
|
|
16754
17573
|
threadId: thread.id,
|
|
16755
|
-
inputMessageId
|
|
17574
|
+
inputMessageId,
|
|
17575
|
+
source: "project_task"
|
|
16756
17576
|
};
|
|
16757
17577
|
}
|
|
16758
17578
|
function taskThreadMetadata(thread) {
|
|
@@ -16806,9 +17626,9 @@ function validRecoveryRow(row, task, thread) {
|
|
|
16806
17626
|
}
|
|
16807
17627
|
|
|
16808
17628
|
// src/services/collaboration/ProjectTaskObserver.ts
|
|
16809
|
-
import { createHash as
|
|
17629
|
+
import { createHash as createHash6 } from "crypto";
|
|
16810
17630
|
import {
|
|
16811
|
-
|
|
17631
|
+
createTaskRunBridge as createTaskRunBridge2,
|
|
16812
17632
|
parseExternalDependency as parseExternalDependency2
|
|
16813
17633
|
} from "@axiom-lattice/core";
|
|
16814
17634
|
import {
|
|
@@ -16816,7 +17636,6 @@ import {
|
|
|
16816
17636
|
} from "@axiom-lattice/protocols";
|
|
16817
17637
|
var SYSTEM_ACTOR2 = "system";
|
|
16818
17638
|
var TERMINAL_ACTIONS = ["completed", "failed", "interrupted", "cancelled"];
|
|
16819
|
-
var MAX_LEARNING_ROUND_ANCESTORS = 8;
|
|
16820
17639
|
var ProjectTaskObserver = class {
|
|
16821
17640
|
/** @param deps Durable stores and Project Task runtime services. @param options Positive bounded scan limits. */
|
|
16822
17641
|
constructor(deps, options) {
|
|
@@ -16835,6 +17654,7 @@ var ProjectTaskObserver = class {
|
|
|
16835
17654
|
throw new Error(`Invalid Project task observer bound: ${name}`);
|
|
16836
17655
|
}
|
|
16837
17656
|
}
|
|
17657
|
+
this.runBridge = createTaskRunBridge2({ lifecycle: this.deps.lifecycle });
|
|
16838
17658
|
}
|
|
16839
17659
|
/** Fire-and-forget notifier adapter that never waits for observer work or machine acquisition. */
|
|
16840
17660
|
async listener(event) {
|
|
@@ -16890,15 +17710,15 @@ var ProjectTaskObserver = class {
|
|
|
16890
17710
|
if (signal?.aborted) continue;
|
|
16891
17711
|
const eventTime = Date.parse(hint.event.updatedAt);
|
|
16892
17712
|
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);
|
|
17713
|
+
await this.reconcileTask(task, signal, "dispatch");
|
|
16894
17714
|
}
|
|
16895
17715
|
}
|
|
16896
17716
|
}
|
|
16897
17717
|
/** Awaits one notification for tests, startup repair, or other non-notifier callers. */
|
|
16898
17718
|
async notify(event) {
|
|
16899
17719
|
const task = snapshotTaskItem(await this.deps.tasks.getById(event.tenantId, event.taskId));
|
|
17720
|
+
if (!task || !isProjectLifecycleTask(task) || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId) return;
|
|
16900
17721
|
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
17722
|
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
17723
|
let anchorMessageId;
|
|
16904
17724
|
if (matches && task.status === event.status && task.updatedAt.toISOString() === event.updatedAt && (event.threadId === void 0 || task.context?.thread_id === event.threadId)) {
|
|
@@ -16956,7 +17776,11 @@ var ProjectTaskObserver = class {
|
|
|
16956
17776
|
return result;
|
|
16957
17777
|
}
|
|
16958
17778
|
result.taskTruncated || (result.taskTruncated = pass.truncated);
|
|
16959
|
-
if (pass.truncated) this.deps.log.warn("Project task startup scan truncated", {
|
|
17779
|
+
if (pass.truncated) this.deps.log.warn("Project task startup scan truncated", {
|
|
17780
|
+
code: "PROJECT_TASK_STARTUP_SCAN_TRUNCATED",
|
|
17781
|
+
tenantId: tenant2.id,
|
|
17782
|
+
status
|
|
17783
|
+
});
|
|
16960
17784
|
for (const scope of pass.scopes) discovered.set(scopeKey(scope), scope);
|
|
16961
17785
|
for (const id3 of pass.ids) {
|
|
16962
17786
|
if (signal?.aborted || this.disposed) {
|
|
@@ -16978,16 +17802,43 @@ var ProjectTaskObserver = class {
|
|
|
16978
17802
|
return result;
|
|
16979
17803
|
}
|
|
16980
17804
|
await this.repairProjects(selected, discovered, result, signal);
|
|
17805
|
+
if (result.taskTruncated || result.projectEnumerationTruncated || result.projectionTruncated) {
|
|
17806
|
+
this.deps.log.warn("Project task startup reconciliation truncated", {
|
|
17807
|
+
code: "PROJECT_TASK_STARTUP_RECONCILIATION_TRUNCATED",
|
|
17808
|
+
taskTruncated: result.taskTruncated,
|
|
17809
|
+
projectEnumerationTruncated: result.projectEnumerationTruncated,
|
|
17810
|
+
projectionTruncated: result.projectionTruncated
|
|
17811
|
+
});
|
|
17812
|
+
}
|
|
16981
17813
|
return result;
|
|
16982
17814
|
}
|
|
16983
|
-
/** Reconciles one persisted Agent-owned Project Task
|
|
16984
|
-
async reconcileTask(task, signal) {
|
|
17815
|
+
/** Reconciles one persisted Agent-owned Project Task under an explicit intent. */
|
|
17816
|
+
async reconcileTask(task, signal, intent = "converge") {
|
|
16985
17817
|
signal ?? (signal = this.deps.signal);
|
|
16986
17818
|
if (signal?.aborted || this.disposed) return;
|
|
16987
|
-
if (!
|
|
16988
|
-
if (await this.belongsToLearningRound(task)) return;
|
|
17819
|
+
if (!isProjectLifecycleTask(task) || task.ownerType !== "agent") 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") {
|
|
@@ -17009,7 +17860,6 @@ var ProjectTaskObserver = class {
|
|
|
17009
17860
|
const createdBy = readCreatedBy(task.context?.created_by);
|
|
17010
17861
|
if (!createdBy || task.ownerType !== "agent" || createdBy.assistantId === task.ownerId) return;
|
|
17011
17862
|
try {
|
|
17012
|
-
if (await this.belongsToLearningRound(task)) return;
|
|
17013
17863
|
const room = await this.exactMainRoom(task);
|
|
17014
17864
|
const from = await this.deps.bots.findById(task.tenantId, createdBy.membershipId) ?? await this.deps.bots.findByAssistant(task.tenantId, task.projectId, createdBy.assistantId);
|
|
17015
17865
|
const to = await this.deps.bots.findByAssistant(task.tenantId, task.projectId, task.ownerId);
|
|
@@ -17026,22 +17876,6 @@ var ProjectTaskObserver = class {
|
|
|
17026
17876
|
await this.safeWarning(task, "delegation", error);
|
|
17027
17877
|
}
|
|
17028
17878
|
}
|
|
17029
|
-
// SCOPE-COMPAT: learning rounds (metadata.trainingRound) and their subtrees execute in the
|
|
17030
|
-
// architect's builder thread (canvas CopilotPanel), not the Project Room runtime. Exempting
|
|
17031
|
-
// them here keeps admission hints, startup reconciliation, and dependent re-evaluation from
|
|
17032
|
-
// claiming these tasks — no roster warnings, no restart noise, and no double execution if
|
|
17033
|
-
// the architect later joins the room roster.
|
|
17034
|
-
async belongsToLearningRound(task) {
|
|
17035
|
-
let current = task;
|
|
17036
|
-
for (let depth = 0; current && depth < MAX_LEARNING_ROUND_ANCESTORS; depth += 1) {
|
|
17037
|
-
if (current.metadata?.trainingRound === true) return true;
|
|
17038
|
-
if (!current.parentId) return false;
|
|
17039
|
-
const parent = snapshotTaskItem(await this.deps.tasks.getById(current.tenantId, current.parentId));
|
|
17040
|
-
if (!parent || parent.id !== current.parentId || parent.tenantId !== current.tenantId) return false;
|
|
17041
|
-
current = parent;
|
|
17042
|
-
}
|
|
17043
|
-
return false;
|
|
17044
|
-
}
|
|
17045
17879
|
/** Collects every bounded dependent ID before re-reading or scheduling any dependent. */
|
|
17046
17880
|
async reevaluateDependents(task) {
|
|
17047
17881
|
const { ids } = await this.collectDependentIds(task);
|
|
@@ -17099,20 +17933,13 @@ var ProjectTaskObserver = class {
|
|
|
17099
17933
|
const classification = await this.deps.execution.classifyRecovery(current, ensured.thread);
|
|
17100
17934
|
if (signal?.aborted || this.disposed) return;
|
|
17101
17935
|
if (classification.kind === "invalid") {
|
|
17102
|
-
await this.
|
|
17936
|
+
await this.convergeInterrupted(
|
|
17937
|
+
current,
|
|
17938
|
+
`Task execution state is not recoverable: ${classification.reason}`
|
|
17939
|
+
);
|
|
17103
17940
|
return;
|
|
17104
17941
|
}
|
|
17105
17942
|
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
17943
|
return;
|
|
17117
17944
|
}
|
|
17118
17945
|
const recovery = {
|
|
@@ -17147,7 +17974,6 @@ var ProjectTaskObserver = class {
|
|
|
17147
17974
|
}
|
|
17148
17975
|
async wakeParent(child) {
|
|
17149
17976
|
if (!child.parentId) return;
|
|
17150
|
-
if (await this.belongsToLearningRound(child)) return;
|
|
17151
17977
|
const parent = snapshotTaskItem(await this.deps.tasks.getById(child.tenantId, child.parentId));
|
|
17152
17978
|
if (!parent || parent.id !== child.parentId || child.parentId !== parent.id || parent.ownerType !== "agent" || parent.workspaceId !== child.workspaceId || parent.projectId !== child.projectId || parent.status !== "interrupted" || !parent.dependencies?.includes(child.id)) return;
|
|
17153
17979
|
const interruption = parseExternalDependency2(parent.context?.interruption);
|
|
@@ -17185,46 +18011,45 @@ var ProjectTaskObserver = class {
|
|
|
17185
18011
|
if (!current || current.ownerId !== outcome.thread.assistantId || current.context?.thread_id !== outcome.thread.id) return;
|
|
17186
18012
|
if (isTerminal(current.status) || current.status === "review") return;
|
|
17187
18013
|
if (outcome.kind === "completed") {
|
|
17188
|
-
const result2 = await this.
|
|
18014
|
+
const result2 = await this.runBridge.settleRun({
|
|
17189
18015
|
tenantId: current.tenantId,
|
|
17190
|
-
|
|
18016
|
+
task: current,
|
|
17191
18017
|
actor: SYSTEM_ACTOR2,
|
|
17192
18018
|
threadId: outcome.thread.id,
|
|
17193
|
-
|
|
18019
|
+
outcome: { kind: "completed" }
|
|
17194
18020
|
});
|
|
17195
18021
|
await this.checkLifecycle(current, "outcome-completed", result2);
|
|
17196
18022
|
return;
|
|
17197
18023
|
}
|
|
17198
18024
|
if (outcome.kind === "cancelled") {
|
|
17199
|
-
const result2 = await this.
|
|
18025
|
+
const result2 = await this.runBridge.settleRun({
|
|
17200
18026
|
tenantId: current.tenantId,
|
|
17201
|
-
|
|
18027
|
+
task: current,
|
|
17202
18028
|
actor: SYSTEM_ACTOR2,
|
|
17203
18029
|
threadId: outcome.thread.id,
|
|
17204
|
-
|
|
18030
|
+
outcome: { kind: "cancelled" }
|
|
17205
18031
|
});
|
|
17206
18032
|
await this.checkLifecycle(current, "outcome-cancelled", result2);
|
|
17207
18033
|
return;
|
|
17208
18034
|
}
|
|
17209
18035
|
if (outcome.kind === "failed" && outcome.lifecycleHandled) return;
|
|
17210
18036
|
if (outcome.kind === "interrupted") {
|
|
17211
|
-
const result2 = await this.
|
|
18037
|
+
const result2 = await this.runBridge.settleRun({
|
|
17212
18038
|
tenantId: current.tenantId,
|
|
17213
|
-
|
|
18039
|
+
task: current,
|
|
17214
18040
|
actor: SYSTEM_ACTOR2,
|
|
17215
18041
|
threadId: outcome.thread.id,
|
|
17216
|
-
|
|
17217
|
-
summary: "Task execution interrupted"
|
|
18042
|
+
outcome: { kind: "interrupted" }
|
|
17218
18043
|
});
|
|
17219
18044
|
await this.checkLifecycle(current, "outcome-interrupted", result2);
|
|
17220
18045
|
return;
|
|
17221
18046
|
}
|
|
17222
|
-
const result = await this.
|
|
18047
|
+
const result = await this.runBridge.settleRun({
|
|
17223
18048
|
tenantId: current.tenantId,
|
|
17224
|
-
|
|
18049
|
+
task: current,
|
|
17225
18050
|
actor: SYSTEM_ACTOR2,
|
|
17226
18051
|
threadId: outcome.thread.id,
|
|
17227
|
-
|
|
18052
|
+
outcome: { kind: "failed", lifecycleHandled: false, error: outcome.error }
|
|
17228
18053
|
});
|
|
17229
18054
|
await this.checkLifecycle(current, `outcome-${outcome.cause}`, result);
|
|
17230
18055
|
}
|
|
@@ -17271,7 +18096,7 @@ var ProjectTaskObserver = class {
|
|
|
17271
18096
|
});
|
|
17272
18097
|
for (const value of batch) {
|
|
17273
18098
|
if (!rows.has(value.id)) rows.set(value.id, { id: value.id, createdAt: requireDate(value.createdAt) });
|
|
17274
|
-
if (
|
|
18099
|
+
if (isProjectLifecycleTask(value)) {
|
|
17275
18100
|
const scope = { tenantId: value.tenantId, workspaceId: value.workspaceId, projectId: value.projectId };
|
|
17276
18101
|
scopes.set(scopeKey(scope), scope);
|
|
17277
18102
|
}
|
|
@@ -17388,7 +18213,7 @@ var ProjectTaskObserver = class {
|
|
|
17388
18213
|
action: "activity",
|
|
17389
18214
|
summary: "Project task recovery warning",
|
|
17390
18215
|
detail: { type: "project_task_recovery_warning", phase, message },
|
|
17391
|
-
eventKey: `project-task-recovery-warning:${task.id}:${phase}:${
|
|
18216
|
+
eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash6("sha256").update(message).digest("hex")}`
|
|
17392
18217
|
});
|
|
17393
18218
|
}
|
|
17394
18219
|
async safeWarning(task, phase, error) {
|
|
@@ -17402,6 +18227,134 @@ var ProjectTaskObserver = class {
|
|
|
17402
18227
|
});
|
|
17403
18228
|
}
|
|
17404
18229
|
}
|
|
18230
|
+
/**
|
|
18231
|
+
* Converges an unexecutable task to a durable interruption.
|
|
18232
|
+
*
|
|
18233
|
+
* @param task Exact task snapshot that cannot make progress through automation.
|
|
18234
|
+
* @param summary Human-readable reason recorded with the interruption.
|
|
18235
|
+
* @remarks Never leaves a task silently pending behind a warning; an `in_progress`
|
|
18236
|
+
* task with no dispatched queue rows is intentionally left untouched by recovery.
|
|
18237
|
+
* The interruption is visible and resumable once an executor is available.
|
|
18238
|
+
*/
|
|
18239
|
+
async convergeInterrupted(task, summary) {
|
|
18240
|
+
if (this.disposed) return;
|
|
18241
|
+
try {
|
|
18242
|
+
const result = await this.runBridge.settleRun({
|
|
18243
|
+
tenantId: task.tenantId,
|
|
18244
|
+
task,
|
|
18245
|
+
actor: SYSTEM_ACTOR2,
|
|
18246
|
+
outcome: { kind: "interrupted", summary }
|
|
18247
|
+
});
|
|
18248
|
+
if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
|
|
18249
|
+
await this.safeWarning(task, "converge", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? summary}`);
|
|
18250
|
+
}
|
|
18251
|
+
} catch (error) {
|
|
18252
|
+
await this.safeWarning(task, "converge", error);
|
|
18253
|
+
}
|
|
18254
|
+
}
|
|
18255
|
+
/**
|
|
18256
|
+
* Classifies a task's dependencies as ready, still waiting, or permanently unmet.
|
|
18257
|
+
*
|
|
18258
|
+
* @param task Exact task snapshot whose dependencies are inspected.
|
|
18259
|
+
* @returns `ready` when every dependency is completed, `unmet` when a
|
|
18260
|
+
* dependency is terminal but not completed (failed/cancelled/missing),
|
|
18261
|
+
* otherwise `wait`.
|
|
18262
|
+
*/
|
|
18263
|
+
async dependencyState(task) {
|
|
18264
|
+
for (const dependencyId of task.dependencies ?? []) {
|
|
18265
|
+
let dependency;
|
|
18266
|
+
try {
|
|
18267
|
+
dependency = snapshotTaskItem(await this.deps.tasks.getById(task.tenantId, dependencyId));
|
|
18268
|
+
} catch {
|
|
18269
|
+
return "wait";
|
|
18270
|
+
}
|
|
18271
|
+
if (!dependency || dependency.id !== dependencyId || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId) return "unmet";
|
|
18272
|
+
if (dependency.status === "completed") continue;
|
|
18273
|
+
if (dependency.status === "failed" || dependency.status === "cancelled") return "unmet";
|
|
18274
|
+
return "wait";
|
|
18275
|
+
}
|
|
18276
|
+
return "ready";
|
|
18277
|
+
}
|
|
18278
|
+
/**
|
|
18279
|
+
* Converges a task whose dependencies can never be met to a durable failure.
|
|
18280
|
+
*
|
|
18281
|
+
* @param task Exact task snapshot with terminal non-completed dependencies.
|
|
18282
|
+
*/
|
|
18283
|
+
async convergeDependencyUnmet(task) {
|
|
18284
|
+
if (this.disposed) return;
|
|
18285
|
+
try {
|
|
18286
|
+
const result = await this.runBridge.settleRun({
|
|
18287
|
+
tenantId: task.tenantId,
|
|
18288
|
+
task,
|
|
18289
|
+
actor: SYSTEM_ACTOR2,
|
|
18290
|
+
outcome: {
|
|
18291
|
+
kind: "failed",
|
|
18292
|
+
lifecycleHandled: false,
|
|
18293
|
+
error: `Dependency unmet: ${(task.dependencies ?? []).join(", ")}`
|
|
18294
|
+
}
|
|
18295
|
+
});
|
|
18296
|
+
if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
|
|
18297
|
+
await this.safeWarning(task, "dependency", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "dependency unmet"}`);
|
|
18298
|
+
}
|
|
18299
|
+
} catch (error) {
|
|
18300
|
+
await this.safeWarning(task, "dependency", error);
|
|
18301
|
+
}
|
|
18302
|
+
}
|
|
18303
|
+
/**
|
|
18304
|
+
* Detects whether a still-waiting task sits on a dependency cycle.
|
|
18305
|
+
*
|
|
18306
|
+
* @param task Exact pending task snapshot.
|
|
18307
|
+
* @returns True when following `dependencies` edges reaches the task again.
|
|
18308
|
+
* @remarks Bounded; unrelated cycles are ignored by the visited set.
|
|
18309
|
+
*/
|
|
18310
|
+
async hasDependencyCycle(task) {
|
|
18311
|
+
const MAX_READS = 1e3;
|
|
18312
|
+
const visited = /* @__PURE__ */ new Set();
|
|
18313
|
+
const stack = [...task.dependencies ?? []];
|
|
18314
|
+
let reads = 0;
|
|
18315
|
+
while (stack.length > 0) {
|
|
18316
|
+
const currentId = stack.pop();
|
|
18317
|
+
if (currentId === task.id) return true;
|
|
18318
|
+
if (visited.has(currentId)) continue;
|
|
18319
|
+
if (reads >= MAX_READS) return false;
|
|
18320
|
+
visited.add(currentId);
|
|
18321
|
+
reads += 1;
|
|
18322
|
+
let dependency;
|
|
18323
|
+
try {
|
|
18324
|
+
dependency = snapshotTaskItem(await this.deps.tasks.getById(task.tenantId, currentId));
|
|
18325
|
+
} catch {
|
|
18326
|
+
return false;
|
|
18327
|
+
}
|
|
18328
|
+
if (!dependency) continue;
|
|
18329
|
+
for (const next of dependency.dependencies ?? []) stack.push(next);
|
|
18330
|
+
}
|
|
18331
|
+
return false;
|
|
18332
|
+
}
|
|
18333
|
+
/**
|
|
18334
|
+
* Converges a task stuck on a dependency cycle to a durable failure.
|
|
18335
|
+
*
|
|
18336
|
+
* @param task Exact pending task snapshot on the cycle.
|
|
18337
|
+
*/
|
|
18338
|
+
async convergeDependencyCycle(task) {
|
|
18339
|
+
if (this.disposed) return;
|
|
18340
|
+
try {
|
|
18341
|
+
const result = await this.runBridge.settleRun({
|
|
18342
|
+
tenantId: task.tenantId,
|
|
18343
|
+
task,
|
|
18344
|
+
actor: SYSTEM_ACTOR2,
|
|
18345
|
+
outcome: {
|
|
18346
|
+
kind: "failed",
|
|
18347
|
+
lifecycleHandled: false,
|
|
18348
|
+
error: `Dependency cycle: ${(task.dependencies ?? []).join(", ")}`
|
|
18349
|
+
}
|
|
18350
|
+
});
|
|
18351
|
+
if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
|
|
18352
|
+
await this.safeWarning(task, "dependency-cycle", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "dependency cycle"}`);
|
|
18353
|
+
}
|
|
18354
|
+
} catch (error) {
|
|
18355
|
+
await this.safeWarning(task, "dependency-cycle", error);
|
|
18356
|
+
}
|
|
18357
|
+
}
|
|
17405
18358
|
async checkLifecycle(task, phase, result) {
|
|
17406
18359
|
if (result.success) return;
|
|
17407
18360
|
await this.safeWarning(task, phase, `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "Lifecycle rejected"}`);
|
|
@@ -17446,16 +18399,25 @@ var ProjectTaskObserver = class {
|
|
|
17446
18399
|
async runOverflowReconciliations() {
|
|
17447
18400
|
while (!this.disposed && this.overflowScopes.size > 0) {
|
|
17448
18401
|
const entries = [...this.overflowScopes.entries()];
|
|
17449
|
-
|
|
18402
|
+
const applicable = [];
|
|
18403
|
+
for (const [key, scope] of entries) {
|
|
18404
|
+
const task = snapshotTaskItem(await this.deps.tasks.getById(scope.event.tenantId, scope.event.taskId));
|
|
18405
|
+
if (!task || !isProjectLifecycleTask(task) || task.workspaceId !== scope.event.workspaceId || task.projectId !== scope.event.projectId) {
|
|
18406
|
+
if (this.overflowScopes.get(key)?.generation === scope.generation) this.overflowScopes.delete(key);
|
|
18407
|
+
continue;
|
|
18408
|
+
}
|
|
18409
|
+
applicable.push([key, scope]);
|
|
18410
|
+
}
|
|
18411
|
+
for (const [, { event }] of applicable) await this.persistOverflowWarning(event);
|
|
17450
18412
|
while (this.notificationTails.size > 0) await Promise.allSettled([...this.notificationTails.values()]);
|
|
17451
|
-
for (const [key, { event, generation }] of
|
|
18413
|
+
for (const [key, { event, generation }] of applicable) {
|
|
17452
18414
|
await this.reconcileOverflowScope(event);
|
|
17453
18415
|
if (this.overflowScopes.get(key)?.generation === generation) this.overflowScopes.delete(key);
|
|
17454
18416
|
}
|
|
17455
18417
|
}
|
|
17456
18418
|
}
|
|
17457
18419
|
async persistOverflowWarning(event) {
|
|
17458
|
-
const scopeDigest =
|
|
18420
|
+
const scopeDigest = createHash6("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
|
|
17459
18421
|
try {
|
|
17460
18422
|
await this.deps.workItems.createIfAbsentByEventKey({
|
|
17461
18423
|
tenantId: event.tenantId,
|
|
@@ -17514,7 +18476,7 @@ var ProjectTaskObserver = class {
|
|
|
17514
18476
|
}
|
|
17515
18477
|
};
|
|
17516
18478
|
function machineScope(task) {
|
|
17517
|
-
if (!task.projectId || task.
|
|
18479
|
+
if (!task.projectId || task.ownerType !== "agent") {
|
|
17518
18480
|
throw new Error("Invalid Project task machine scope");
|
|
17519
18481
|
}
|
|
17520
18482
|
return { tenantId: task.tenantId, assistantId: task.ownerId, projectId: task.projectId };
|
|
@@ -17564,8 +18526,7 @@ function combineSignals(left, right) {
|
|
|
17564
18526
|
}
|
|
17565
18527
|
|
|
17566
18528
|
// src/services/collaboration/RoomProjectionService.ts
|
|
17567
|
-
import { createHash as
|
|
17568
|
-
import { isProjectLifecycleTask as isProjectLifecycleTask5 } from "@axiom-lattice/core";
|
|
18529
|
+
import { createHash as createHash7 } from "crypto";
|
|
17569
18530
|
import {
|
|
17570
18531
|
PROJECT_ROOM_TASK_DELEGATED_LABEL,
|
|
17571
18532
|
PROJECT_ROOM_TASK_DELEGATED_SEPARATOR,
|
|
@@ -17715,7 +18676,7 @@ var RoomProjectionService = class {
|
|
|
17715
18676
|
await this.warning("INVALID_PROJECTABLE_EVENT", { taskId: row.taskId, eventKey: row.eventKey }, input);
|
|
17716
18677
|
continue;
|
|
17717
18678
|
}
|
|
17718
|
-
if (task.ownerType !== "agent" || !
|
|
18679
|
+
if (task.ownerType !== "agent" || !isProjectLifecycleTask(task)) {
|
|
17719
18680
|
result.skipped += 1;
|
|
17720
18681
|
continue;
|
|
17721
18682
|
}
|
|
@@ -17760,7 +18721,7 @@ var RoomProjectionService = class {
|
|
|
17760
18721
|
};
|
|
17761
18722
|
function snapshotProjectTask(value) {
|
|
17762
18723
|
const task = snapshotTaskItem(value);
|
|
17763
|
-
return task && task.ownerType === "agent" &&
|
|
18724
|
+
return task && task.ownerType === "agent" && isProjectLifecycleTask(task) ? task : void 0;
|
|
17764
18725
|
}
|
|
17765
18726
|
function ownEnumerableString(value, key) {
|
|
17766
18727
|
try {
|
|
@@ -17786,7 +18747,7 @@ function safeRepairIdentity(value, scope) {
|
|
|
17786
18747
|
return taskId && eventKey && tenantId === scope.tenantId && workspaceId === scope.workspaceId && projectId === scope.projectId ? { taskId, eventKey } : void 0;
|
|
17787
18748
|
}
|
|
17788
18749
|
function tupleKey(prefix, values) {
|
|
17789
|
-
const digest =
|
|
18750
|
+
const digest = createHash7("sha256").update(JSON.stringify(values)).digest("hex");
|
|
17790
18751
|
return `${prefix}:${digest}`;
|
|
17791
18752
|
}
|
|
17792
18753
|
function parseProjectionInput(taskValue, itemValue) {
|
|
@@ -17956,7 +18917,7 @@ function isRecord3(value) {
|
|
|
17956
18917
|
import { taskLifecycleNotifier } from "@axiom-lattice/core";
|
|
17957
18918
|
|
|
17958
18919
|
// src/services/collaboration/ProjectRoomEventBroker.ts
|
|
17959
|
-
import { randomUUID as
|
|
18920
|
+
import { randomUUID as randomUUID22 } from "crypto";
|
|
17960
18921
|
import {
|
|
17961
18922
|
parseProjectRoomEventId,
|
|
17962
18923
|
ProjectRoomBrokerCapacityError,
|
|
@@ -17985,7 +18946,7 @@ var ProjectRoomEventBroker = class {
|
|
|
17985
18946
|
this.subscriberCount = 0;
|
|
17986
18947
|
this.usageClock = 0;
|
|
17987
18948
|
this.closed = false;
|
|
17988
|
-
this.epoch = options.epoch ??
|
|
18949
|
+
this.epoch = options.epoch ?? randomUUID22();
|
|
17989
18950
|
if (!UUID_PATTERN.test(this.epoch)) {
|
|
17990
18951
|
throw new TypeError("Project Room event epoch must be a lowercase canonical UUID");
|
|
17991
18952
|
}
|
|
@@ -18303,7 +19264,7 @@ function snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
|
18303
19264
|
}
|
|
18304
19265
|
|
|
18305
19266
|
// src/services/collaboration/ProjectRoomRealtimePublisher.ts
|
|
18306
|
-
import { createHash as
|
|
19267
|
+
import { createHash as createHash8 } from "crypto";
|
|
18307
19268
|
import {
|
|
18308
19269
|
projectRoomUserEventScope,
|
|
18309
19270
|
snapshotExactRecord as snapshotExactRecord11,
|
|
@@ -18425,7 +19386,7 @@ var BrokerProjectRoomRealtimePublisher = class {
|
|
|
18425
19386
|
try {
|
|
18426
19387
|
event = build();
|
|
18427
19388
|
if (!event) throw new Error("Invalid Project Room realtime projection");
|
|
18428
|
-
const dedupeKey =
|
|
19389
|
+
const dedupeKey = createHash8("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
|
|
18429
19390
|
if (this.sourceKeys.has(dedupeKey)) return;
|
|
18430
19391
|
this.deps.broker.publish(event.scope, event.draft);
|
|
18431
19392
|
this.sourceKeys.set(dedupeKey, true);
|
|
@@ -18479,7 +19440,6 @@ function safeErrorCode(error) {
|
|
|
18479
19440
|
}
|
|
18480
19441
|
|
|
18481
19442
|
// src/services/collaboration/ProjectRoomTaskEventSubscriber.ts
|
|
18482
|
-
import { isProjectLifecycleTask as isProjectLifecycleTask6 } from "@axiom-lattice/core";
|
|
18483
19443
|
import { snapshotExactRecord as snapshotExactRecord12 } from "@axiom-lattice/protocols";
|
|
18484
19444
|
var ProjectRoomTaskEventSubscriber = class {
|
|
18485
19445
|
/** Creates a bounded Project task lifecycle observer. */
|
|
@@ -18562,8 +19522,8 @@ var ProjectRoomTaskEventSubscriber = class {
|
|
|
18562
19522
|
try {
|
|
18563
19523
|
const taskValue = await this.deps.tasks.getById(event.tenantId, event.taskId);
|
|
18564
19524
|
const task = taskValue === null ? void 0 : snapshotTaskItem(taskValue);
|
|
18565
|
-
if (this.closed) return;
|
|
18566
|
-
if (
|
|
19525
|
+
if (this.closed || !task || !isProjectLifecycleTask(task)) return;
|
|
19526
|
+
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
19527
|
this.warn("project-room:task-event-stale", { taskId: safeId3(event.taskId), eventKey: safeId3(event.eventKey) });
|
|
18568
19528
|
return;
|
|
18569
19529
|
}
|
|
@@ -19372,20 +20332,163 @@ async function validateReassignmentThread(threads, input) {
|
|
|
19372
20332
|
throw new ProjectTaskThreadError("PROJECT_TASK_THREAD_METADATA_INVALID", "Invalid reassignment Task Thread metadata");
|
|
19373
20333
|
}
|
|
19374
20334
|
}
|
|
20335
|
+
function hookErrorMessage(error) {
|
|
20336
|
+
if (error instanceof Error && error.message) return error.message;
|
|
20337
|
+
return "Managed dispatch transfer failed.";
|
|
20338
|
+
}
|
|
20339
|
+
function wrapReassignmentThreadError(error) {
|
|
20340
|
+
if (error instanceof ProjectTaskThreadError) {
|
|
20341
|
+
if (error.code === "PROJECT_TASK_THREAD_SCOPE_INVALID") {
|
|
20342
|
+
return new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
|
|
20343
|
+
hint: "Project task, room, or roster scope changed; re-read the task and retry."
|
|
20344
|
+
});
|
|
20345
|
+
}
|
|
20346
|
+
if (error.code === "PROJECT_TASK_THREAD_OWNER_INVALID") {
|
|
20347
|
+
return new ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
|
|
20348
|
+
hint: "The target already owns this task; re-read before dispatching."
|
|
20349
|
+
});
|
|
20350
|
+
}
|
|
20351
|
+
return new ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
|
|
20352
|
+
hint: "Task Thread changed concurrently; re-read the task and retry."
|
|
20353
|
+
});
|
|
20354
|
+
}
|
|
20355
|
+
return new ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
|
|
20356
|
+
hint: "Re-read the task and retry the dispatch."
|
|
20357
|
+
});
|
|
20358
|
+
}
|
|
20359
|
+
function wrapReassignmentAuthorizationError(error) {
|
|
20360
|
+
if (error instanceof ProjectTaskOwnerPolicyError2) {
|
|
20361
|
+
if (error.code === "PROJECT_TASK_CALLER_INACTIVE") {
|
|
20362
|
+
return new ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
|
|
20363
|
+
hint: "Dispatch from an active Project Room member bot."
|
|
20364
|
+
});
|
|
20365
|
+
}
|
|
20366
|
+
return new ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
|
|
20367
|
+
hint: "Dispatch to an active bot in this Project's roster."
|
|
20368
|
+
});
|
|
20369
|
+
}
|
|
20370
|
+
return new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
|
|
20371
|
+
hint: "Re-read the task and roster, then retry the dispatch."
|
|
20372
|
+
});
|
|
20373
|
+
}
|
|
20374
|
+
function reassignmentFailureHint(code) {
|
|
20375
|
+
if (code === "TASK_THREAD_CONFLICT") {
|
|
20376
|
+
return "Task Thread changed concurrently; re-read the task and retry.";
|
|
20377
|
+
}
|
|
20378
|
+
if (code === "TASK_REASSIGN_NO_CHANGE") {
|
|
20379
|
+
return "The target already owns this task; re-read before dispatching.";
|
|
20380
|
+
}
|
|
20381
|
+
if (code === "TASK_STATUS_CONFLICT") {
|
|
20382
|
+
return "Task changed concurrently; re-read the task and retry.";
|
|
20383
|
+
}
|
|
20384
|
+
if (code === "TASK_TERMINAL") {
|
|
20385
|
+
return "Terminal tasks are immutable; create a new task for this work.";
|
|
20386
|
+
}
|
|
20387
|
+
if (code === "REVIEW_APPROVAL_REQUIRED") {
|
|
20388
|
+
return "Resolve the review decision (approve/reject) before dispatching.";
|
|
20389
|
+
}
|
|
20390
|
+
if (code === "TASK_REASSIGN_INTERRUPT_REQUIRED") {
|
|
20391
|
+
return "Resume or cancel the running task before dispatching.";
|
|
20392
|
+
}
|
|
20393
|
+
return "Re-read the task and retry the dispatch.";
|
|
20394
|
+
}
|
|
20395
|
+
async function transferGovernedDispatchOwner(task, options, deps) {
|
|
20396
|
+
const agentId = options?.agentId;
|
|
20397
|
+
if (!agentId || agentId === task.ownerId) return void 0;
|
|
20398
|
+
if (!task.workspaceId || !task.projectId) {
|
|
20399
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is not governed by a Project lifecycle.", {
|
|
20400
|
+
hint: "Dispatch governed tasks from a trusted Project run; ordinary tasks execute immediately."
|
|
20401
|
+
});
|
|
20402
|
+
}
|
|
20403
|
+
return deps.mutex.runExclusive({ tenantId: task.tenantId, projectId: task.projectId }, async () => {
|
|
20404
|
+
const current = await deps.tasks.getById(task.tenantId, task.id);
|
|
20405
|
+
if (!current || !isProjectLifecycleTask(current) || current.workspaceId !== task.workspaceId || current.projectId !== task.projectId) {
|
|
20406
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is no longer a Project lifecycle task.", {
|
|
20407
|
+
hint: "Re-read the task and confirm it still belongs to this Project before dispatching."
|
|
20408
|
+
});
|
|
20409
|
+
}
|
|
20410
|
+
if (current.ownerType !== "agent") {
|
|
20411
|
+
throw new ManagedTaskStartError("AGENT_TASK_REQUIRED", "Project task owner must be an Agent.", {
|
|
20412
|
+
hint: "Assign the task to an Agent before dispatching."
|
|
20413
|
+
});
|
|
20414
|
+
}
|
|
20415
|
+
if (current.status !== "pending") {
|
|
20416
|
+
throw new ManagedTaskStartError("TASK_NOT_DISPATCHABLE", "Only a `pending` Project task can be dispatched.", {
|
|
20417
|
+
hint: `This task is '${current.status}'. Resolve it or create a new task before dispatching.`
|
|
20418
|
+
});
|
|
20419
|
+
}
|
|
20420
|
+
if (agentId === current.ownerId) return current;
|
|
20421
|
+
const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
|
|
20422
|
+
if (!room || room.tenantId !== current.tenantId || room.workspaceId !== current.workspaceId || room.projectId !== current.projectId || room.type !== "main") {
|
|
20423
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Project main room is unavailable.", {
|
|
20424
|
+
hint: "Open or repair the Project Room before dispatching."
|
|
20425
|
+
});
|
|
20426
|
+
}
|
|
20427
|
+
const target = await deps.botMemberships.findByAssistant(current.tenantId, current.projectId, agentId);
|
|
20428
|
+
if (!target || target.status !== "active" || target.tenantId !== current.tenantId || target.workspaceId !== current.workspaceId || target.projectId !== current.projectId || target.assistantId !== agentId || target.roomId !== room.id) {
|
|
20429
|
+
throw new ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", "Target Project task owner is not active in the exact roster.", {
|
|
20430
|
+
hint: "Dispatch to an active bot in this Project's roster."
|
|
20431
|
+
});
|
|
20432
|
+
}
|
|
20433
|
+
const caller = options?.callerMembershipId ? await deps.botMemberships.findById(current.tenantId, options.callerMembershipId) : null;
|
|
20434
|
+
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) {
|
|
20435
|
+
throw new ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", "Current Project task caller is not active in the exact roster.", {
|
|
20436
|
+
hint: "Dispatch from an active Project Room member bot."
|
|
20437
|
+
});
|
|
20438
|
+
}
|
|
20439
|
+
let ensured;
|
|
20440
|
+
try {
|
|
20441
|
+
ensured = await deps.threads.ensure({ task: current, room, membership: target, mode: "reassignment" });
|
|
20442
|
+
} catch (error) {
|
|
20443
|
+
throw wrapReassignmentThreadError(error);
|
|
20444
|
+
}
|
|
20445
|
+
let authorization;
|
|
20446
|
+
try {
|
|
20447
|
+
authorization = await deps.reassignmentAuthorizer.authorize({
|
|
20448
|
+
tenantId: current.tenantId,
|
|
20449
|
+
workspaceId: current.workspaceId,
|
|
20450
|
+
projectId: current.projectId,
|
|
20451
|
+
roomId: room.id,
|
|
20452
|
+
taskId: current.id,
|
|
20453
|
+
callerMembershipId: caller.id,
|
|
20454
|
+
callerAssistantId: caller.assistantId,
|
|
20455
|
+
targetMembershipId: target.id,
|
|
20456
|
+
targetAssistantId: target.assistantId,
|
|
20457
|
+
...typeof current.context?.thread_id === "string" ? { expectedThreadId: current.context.thread_id } : {},
|
|
20458
|
+
newThreadId: ensured.thread.id
|
|
20459
|
+
});
|
|
20460
|
+
} catch (error) {
|
|
20461
|
+
throw wrapReassignmentAuthorizationError(error);
|
|
20462
|
+
}
|
|
20463
|
+
const result = await deps.lifecycle.reassignProjectTask({
|
|
20464
|
+
tenantId: current.tenantId,
|
|
20465
|
+
taskId: current.id,
|
|
20466
|
+
authorization,
|
|
20467
|
+
actor: `agent:${caller.assistantId}`
|
|
20468
|
+
});
|
|
20469
|
+
if (!result.success) {
|
|
20470
|
+
throw new ManagedTaskStartError(result.code, result.error, {
|
|
20471
|
+
hint: result.hint ?? reassignmentFailureHint(result.code),
|
|
20472
|
+
...result.retryable !== void 0 ? { retryable: result.retryable } : {}
|
|
20473
|
+
});
|
|
20474
|
+
}
|
|
20475
|
+
return result.task;
|
|
20476
|
+
});
|
|
20477
|
+
}
|
|
19375
20478
|
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 =
|
|
20479
|
+
const webAppStore = getStoreLattice17("default", "agentWebApp").store;
|
|
20480
|
+
const assistantStore = getStoreLattice17("default", "assistant").store;
|
|
20481
|
+
const projectStore = getStoreLattice17("default", "project").store;
|
|
20482
|
+
const projectMembershipStore = getStoreLattice17("default", "projectMembership").store;
|
|
20483
|
+
const userTenantLinkStore = getStoreLattice17("default", "userTenantLink").store;
|
|
20484
|
+
const projectRoomStore = getStoreLattice17("default", "projectRoom").store;
|
|
20485
|
+
const projectBotMembershipStore = getStoreLattice17("default", "projectBotMembership").store;
|
|
20486
|
+
const projectRoomMessageStore = getStoreLattice17("default", "projectRoomMessage").store;
|
|
20487
|
+
const projectRoomReadStateStore = getStoreLattice17("default", "projectRoomReadState").store;
|
|
20488
|
+
const userPushSubscriptionStore = getStoreLattice17("default", "userPushSubscription").store;
|
|
20489
|
+
const threadStore = getStoreLattice17("default", "thread").store;
|
|
20490
|
+
const taskStore = getStoreLattice17("default", "task").store;
|
|
20491
|
+
const taskWorkItemStore = getStoreLattice17("default", "taskWorkItem").store;
|
|
19389
20492
|
const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
|
|
19390
20493
|
const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
|
|
19391
20494
|
const realtimeMode = resolveProjectRoomRealtimeMode();
|
|
@@ -19451,7 +20554,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19451
20554
|
updateAssistant: assistantStore.updateAssistant.bind(assistantStore)
|
|
19452
20555
|
});
|
|
19453
20556
|
const resolveCoordinatorMentionName = async (tenantId) => {
|
|
19454
|
-
const tenant2 = await
|
|
20557
|
+
const tenant2 = await getStoreLattice17("default", "tenant").store.getTenantById(tenantId);
|
|
19455
20558
|
const value = tenant2?.metadata?.coordinatorMentionName;
|
|
19456
20559
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
19457
20560
|
};
|
|
@@ -19542,7 +20645,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19542
20645
|
});
|
|
19543
20646
|
}
|
|
19544
20647
|
if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
|
|
19545
|
-
const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory:
|
|
20648
|
+
const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: randomUUID23 });
|
|
19546
20649
|
const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? createTaskLifecycleService({ taskStore, workItemStore: taskWorkItemStore });
|
|
19547
20650
|
const reassignmentAuthorizer = createProjectTaskReassignmentAuthorizer(
|
|
19548
20651
|
new ProjectTaskOwnerPolicy(projectBotMembershipStore),
|
|
@@ -19578,7 +20681,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19578
20681
|
readStateService,
|
|
19579
20682
|
interruptService: roomInterruptService,
|
|
19580
20683
|
userDirectory: {
|
|
19581
|
-
getUserById: (id3) =>
|
|
20684
|
+
getUserById: (id3) => getStoreLattice17("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
|
|
19582
20685
|
}
|
|
19583
20686
|
});
|
|
19584
20687
|
registerProjectRosterRoutes(app2, { service: rosterService });
|
|
@@ -19589,7 +20692,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19589
20692
|
createProject: projectStore.createProject.bind(projectStore)
|
|
19590
20693
|
},
|
|
19591
20694
|
workspaceStore: {
|
|
19592
|
-
getWorkspaceById: (tenantId, id3) =>
|
|
20695
|
+
getWorkspaceById: (tenantId, id3) => getStoreLattice17("default", "workspace").store.getWorkspaceById(tenantId, id3)
|
|
19593
20696
|
},
|
|
19594
20697
|
membershipStore: {
|
|
19595
20698
|
list: projectMembershipStore.list.bind(projectMembershipStore),
|
|
@@ -19612,7 +20715,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19612
20715
|
getUsersByTenant: userTenantLinkStore.getUsersByTenant.bind(userTenantLinkStore)
|
|
19613
20716
|
},
|
|
19614
20717
|
userStore: {
|
|
19615
|
-
getUserById: (id3) =>
|
|
20718
|
+
getUserById: (id3) => getStoreLattice17("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
|
|
19616
20719
|
},
|
|
19617
20720
|
assistantStore: {
|
|
19618
20721
|
getAssistantById: assistantStore.getAssistantById.bind(assistantStore),
|
|
@@ -19634,7 +20737,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19634
20737
|
}
|
|
19635
20738
|
});
|
|
19636
20739
|
registerCapabilityBundleRoutes(app2, new CapabilityBundleController({
|
|
19637
|
-
bundleStore:
|
|
20740
|
+
bundleStore: getStoreLattice17("default", "capabilityBundle").store,
|
|
19638
20741
|
projectStore
|
|
19639
20742
|
}, { authRequired: process.env.AUTH_REQUIRED === "true" }));
|
|
19640
20743
|
registerAgentWebAppRoutes(app2, {
|
|
@@ -19682,19 +20785,11 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19682
20785
|
},
|
|
19683
20786
|
tasks: taskStore,
|
|
19684
20787
|
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
|
-
}
|
|
20788
|
+
projectMutationMutex: projectMutationMutex2
|
|
19694
20789
|
}),
|
|
19695
20790
|
service: new AgentWebAppRuntimeService({
|
|
19696
20791
|
webAppStore,
|
|
19697
|
-
threadStore:
|
|
20792
|
+
threadStore: getStoreLattice17("default", "thread").store,
|
|
19698
20793
|
projectStore,
|
|
19699
20794
|
assistantResolver: {
|
|
19700
20795
|
resolve: async (tenantId, assistantId) => {
|
|
@@ -19795,19 +20890,45 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19795
20890
|
app2.post("/api/personal-assistant", createPersonalAssistant);
|
|
19796
20891
|
app2.get("/api/personal-assistant", getPersonalAssistant);
|
|
19797
20892
|
app2.delete("/api/personal-assistant", deletePersonalAssistant);
|
|
20893
|
+
executionAuthorityRegistry.register({
|
|
20894
|
+
id: "project-lifecycle",
|
|
20895
|
+
matches: isProjectLifecycleTask,
|
|
20896
|
+
code: "PROJECT_TASK_EXECUTION_MANAGED",
|
|
20897
|
+
message: "Project tasks are executed by the durable Project queue runtime",
|
|
20898
|
+
onManagedStart: async (task, options2) => {
|
|
20899
|
+
if (!projectTaskComposition && options2?.agentId && options2.agentId !== task.ownerId) {
|
|
20900
|
+
throw new ManagedTaskStartError(
|
|
20901
|
+
"PROJECT_TASK_RUNTIME_UNAVAILABLE",
|
|
20902
|
+
"Project task ownership transfer requires the Project task runtime.",
|
|
20903
|
+
{ hint: "Project task execution is disabled; enable the Project task runtime or dispatch to the current owner." }
|
|
20904
|
+
);
|
|
20905
|
+
}
|
|
20906
|
+
const transferred = projectTaskComposition ? await transferGovernedDispatchOwner(task, options2, {
|
|
20907
|
+
tasks: taskStore,
|
|
20908
|
+
rooms: projectRoomStore,
|
|
20909
|
+
botMemberships: projectBotMembershipStore,
|
|
20910
|
+
threads: taskThreadService,
|
|
20911
|
+
reassignmentAuthorizer,
|
|
20912
|
+
lifecycle: taskLifecycle,
|
|
20913
|
+
mutex: projectMutationMutex2
|
|
20914
|
+
}) : void 0;
|
|
20915
|
+
const admitted = transferred ?? task;
|
|
20916
|
+
if (!admitted.workspaceId || !admitted.projectId) return;
|
|
20917
|
+
void projectTaskAdmissionNotifier.publish({
|
|
20918
|
+
tenantId: admitted.tenantId,
|
|
20919
|
+
workspaceId: admitted.workspaceId,
|
|
20920
|
+
projectId: admitted.projectId,
|
|
20921
|
+
taskId: admitted.id,
|
|
20922
|
+
ownerId: admitted.ownerId,
|
|
20923
|
+
updatedAt: admitted.updatedAt.toISOString()
|
|
20924
|
+
});
|
|
20925
|
+
}
|
|
20926
|
+
});
|
|
19798
20927
|
registerTaskRoutes(app2, createTaskController({
|
|
19799
20928
|
access: accessService,
|
|
19800
20929
|
tasks: taskStore,
|
|
19801
20930
|
workItems: taskWorkItemStore,
|
|
19802
|
-
bots: projectBotMembershipStore,
|
|
19803
|
-
rooms: projectRoomStore,
|
|
19804
|
-
threadService: taskThreadService,
|
|
19805
|
-
reassignmentAuthorizer,
|
|
19806
|
-
lifecycle: taskLifecycle,
|
|
19807
20931
|
projectMutationMutex: projectMutationMutex2,
|
|
19808
|
-
executionMutex: projectTaskComposition?.mutex ?? {
|
|
19809
|
-
runExclusive: (_scope, operation) => operation()
|
|
19810
|
-
},
|
|
19811
20932
|
readableProjects: async ({ tenantId, userId, workspaceId }) => {
|
|
19812
20933
|
const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
|
|
19813
20934
|
const readable = [];
|
|
@@ -19931,6 +21052,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
19931
21052
|
executeDataQuery
|
|
19932
21053
|
);
|
|
19933
21054
|
registerMcpServerConfigRoutes(app2);
|
|
21055
|
+
registerMcpAppRoutes(app2);
|
|
19934
21056
|
registerPushNotificationRoutes(app2, {
|
|
19935
21057
|
subscriptionStore: userPushSubscriptionStore,
|
|
19936
21058
|
vapidPublicKey: process.env.VAPID_PUBLIC_KEY || null
|
|
@@ -20087,9 +21209,9 @@ function tryCreateProjectTaskRuntime(input) {
|
|
|
20087
21209
|
let stores;
|
|
20088
21210
|
try {
|
|
20089
21211
|
stores = {
|
|
20090
|
-
tenants:
|
|
20091
|
-
workspaces:
|
|
20092
|
-
queue:
|
|
21212
|
+
tenants: getStoreLattice17("default", "tenant").store,
|
|
21213
|
+
workspaces: getStoreLattice17("default", "workspace").store,
|
|
21214
|
+
queue: getStoreLattice17("default", "threadMessageQueue").store
|
|
20093
21215
|
};
|
|
20094
21216
|
const missing = missingRuntimeMethod(input, stores);
|
|
20095
21217
|
if (missing) return { reason: missing };
|
|
@@ -20120,7 +21242,7 @@ function tryCreateProjectTaskRuntime(input) {
|
|
|
20120
21242
|
function createRuntime(input, stores, log2) {
|
|
20121
21243
|
const controller = new AbortController();
|
|
20122
21244
|
const mutex = new ProjectTaskExecutionMutex();
|
|
20123
|
-
const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory:
|
|
21245
|
+
const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: randomUUID23 });
|
|
20124
21246
|
const lifecycle = createTaskLifecycleService({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
|
|
20125
21247
|
const projection = new RoomProjectionService({
|
|
20126
21248
|
messages: input.projectRoomMessageStore,
|
|
@@ -20129,7 +21251,7 @@ function createRuntime(input, stores, log2) {
|
|
|
20129
21251
|
warnings: input.taskWorkItemStore,
|
|
20130
21252
|
log: log2,
|
|
20131
21253
|
realtime: input.realtime,
|
|
20132
|
-
idFactory:
|
|
21254
|
+
idFactory: randomUUID23
|
|
20133
21255
|
});
|
|
20134
21256
|
const agentFor = (task, thread) => agentInstanceManager6.getAgent({
|
|
20135
21257
|
tenant_id: task.tenantId,
|
|
@@ -20173,7 +21295,7 @@ function createRuntime(input, stores, log2) {
|
|
|
20173
21295
|
void observer.listener(event);
|
|
20174
21296
|
});
|
|
20175
21297
|
const unsubscribe = taskLifecycleNotifier2.subscribe(taskLifecycleListener);
|
|
20176
|
-
const unsubscribeAdmission =
|
|
21298
|
+
const unsubscribeAdmission = projectTaskAdmissionNotifier.subscribe((event, signal) => {
|
|
20177
21299
|
observer.admit(event, signal);
|
|
20178
21300
|
});
|
|
20179
21301
|
let reconciliationWork;
|
|
@@ -20466,9 +21588,9 @@ var skillRegistration = {
|
|
|
20466
21588
|
};
|
|
20467
21589
|
|
|
20468
21590
|
// src/export_registrations/agent.registration.ts
|
|
20469
|
-
import { getStoreLattice as
|
|
21591
|
+
import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
|
|
20470
21592
|
function getStore2() {
|
|
20471
|
-
return
|
|
21593
|
+
return getStoreLattice18("default", "assistant").store;
|
|
20472
21594
|
}
|
|
20473
21595
|
var agentRegistration = {
|
|
20474
21596
|
entityType: "agent",
|
|
@@ -20548,9 +21670,9 @@ var agentRegistration = {
|
|
|
20548
21670
|
};
|
|
20549
21671
|
|
|
20550
21672
|
// src/export_registrations/database-config.registration.ts
|
|
20551
|
-
import { getStoreLattice as
|
|
21673
|
+
import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
|
|
20552
21674
|
function getStore3() {
|
|
20553
|
-
return
|
|
21675
|
+
return getStoreLattice19("default", "database").store;
|
|
20554
21676
|
}
|
|
20555
21677
|
var databaseConfigRegistration = {
|
|
20556
21678
|
entityType: "database_config",
|
|
@@ -20630,9 +21752,9 @@ var databaseConfigRegistration = {
|
|
|
20630
21752
|
};
|
|
20631
21753
|
|
|
20632
21754
|
// src/export_registrations/metrics-config.registration.ts
|
|
20633
|
-
import { getStoreLattice as
|
|
21755
|
+
import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
|
|
20634
21756
|
function getStore4() {
|
|
20635
|
-
return
|
|
21757
|
+
return getStoreLattice20("default", "metrics").store;
|
|
20636
21758
|
}
|
|
20637
21759
|
var metricsConfigRegistration = {
|
|
20638
21760
|
entityType: "metrics_config",
|
|
@@ -20712,9 +21834,9 @@ var metricsConfigRegistration = {
|
|
|
20712
21834
|
};
|
|
20713
21835
|
|
|
20714
21836
|
// src/export_registrations/mcp-config.registration.ts
|
|
20715
|
-
import { getStoreLattice as
|
|
21837
|
+
import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
|
|
20716
21838
|
function getStore5() {
|
|
20717
|
-
return
|
|
21839
|
+
return getStoreLattice21("default", "mcp").store;
|
|
20718
21840
|
}
|
|
20719
21841
|
var mcpConfigRegistration = {
|
|
20720
21842
|
entityType: "mcp_config",
|
|
@@ -20797,9 +21919,9 @@ var mcpConfigRegistration = {
|
|
|
20797
21919
|
};
|
|
20798
21920
|
|
|
20799
21921
|
// src/export_registrations/connection.registration.ts
|
|
20800
|
-
import { getStoreLattice as
|
|
21922
|
+
import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
|
|
20801
21923
|
function getStore6() {
|
|
20802
|
-
return
|
|
21924
|
+
return getStoreLattice22("default", "connection").store;
|
|
20803
21925
|
}
|
|
20804
21926
|
async function listAllConnections(tenantId) {
|
|
20805
21927
|
const store = getStore6();
|
|
@@ -20881,9 +22003,9 @@ var connectionRegistration = {
|
|
|
20881
22003
|
};
|
|
20882
22004
|
|
|
20883
22005
|
// src/export_registrations/collection.registration.ts
|
|
20884
|
-
import { getStoreLattice as
|
|
22006
|
+
import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
|
|
20885
22007
|
function getStore7() {
|
|
20886
|
-
return
|
|
22008
|
+
return getStoreLattice23("default", "collection").store;
|
|
20887
22009
|
}
|
|
20888
22010
|
var collectionRegistration = {
|
|
20889
22011
|
entityType: "collection",
|
|
@@ -20954,8 +22076,8 @@ var collectionRegistration = {
|
|
|
20954
22076
|
};
|
|
20955
22077
|
|
|
20956
22078
|
// src/export_registrations/channel-installation.registration.ts
|
|
20957
|
-
import { getStoreLattice as
|
|
20958
|
-
import { randomUUID as
|
|
22079
|
+
import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
|
|
22080
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
20959
22081
|
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
22082
|
var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
|
|
20961
22083
|
function isPublicChannel(channel) {
|
|
@@ -20988,7 +22110,7 @@ function validateImportData(data) {
|
|
|
20988
22110
|
};
|
|
20989
22111
|
}
|
|
20990
22112
|
function getStore8() {
|
|
20991
|
-
return
|
|
22113
|
+
return getStoreLattice24("default", "channelInstallation").store;
|
|
20992
22114
|
}
|
|
20993
22115
|
function getOverwriteError(tenantId, incomingChannel, existing) {
|
|
20994
22116
|
if (!existing) return "Channel installation overwrite target must exist";
|
|
@@ -21114,7 +22236,7 @@ var channelInstallationRegistration = {
|
|
|
21114
22236
|
}
|
|
21115
22237
|
id3 = sourceId;
|
|
21116
22238
|
} else {
|
|
21117
|
-
id3 =
|
|
22239
|
+
id3 = randomUUID24();
|
|
21118
22240
|
}
|
|
21119
22241
|
await store.createInstallation(tenantId, id3, createInput);
|
|
21120
22242
|
return { newId: id3 };
|
|
@@ -21122,9 +22244,9 @@ var channelInstallationRegistration = {
|
|
|
21122
22244
|
};
|
|
21123
22245
|
|
|
21124
22246
|
// src/export_registrations/menu.registration.ts
|
|
21125
|
-
import { getStoreLattice as
|
|
22247
|
+
import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
|
|
21126
22248
|
function getStore9() {
|
|
21127
|
-
return
|
|
22249
|
+
return getStoreLattice25("default", "menu").store;
|
|
21128
22250
|
}
|
|
21129
22251
|
var menuRegistration = {
|
|
21130
22252
|
entityType: "menu",
|
|
@@ -21202,9 +22324,9 @@ var menuRegistration = {
|
|
|
21202
22324
|
};
|
|
21203
22325
|
|
|
21204
22326
|
// src/export_registrations/eval.registration.ts
|
|
21205
|
-
import { getStoreLattice as
|
|
22327
|
+
import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
|
|
21206
22328
|
function getStore10() {
|
|
21207
|
-
return
|
|
22329
|
+
return getStoreLattice26("default", "eval").store;
|
|
21208
22330
|
}
|
|
21209
22331
|
var evalRegistration = {
|
|
21210
22332
|
entityType: "eval",
|
|
@@ -21982,11 +23104,12 @@ import {
|
|
|
21982
23104
|
getLoggerLattice,
|
|
21983
23105
|
loggerLatticeManager,
|
|
21984
23106
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
21985
|
-
getStoreLattice as
|
|
23107
|
+
getStoreLattice as getStoreLattice28,
|
|
21986
23108
|
agentInstanceManager as agentInstanceManager9,
|
|
21987
23109
|
createSandboxProvider,
|
|
21988
23110
|
TokenCache,
|
|
21989
|
-
mcpManager,
|
|
23111
|
+
mcpManager as mcpManager3,
|
|
23112
|
+
convertToConnection as convertToConnection2,
|
|
21990
23113
|
createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2
|
|
21991
23114
|
} from "@axiom-lattice/core";
|
|
21992
23115
|
|
|
@@ -21995,14 +23118,14 @@ import { RoomAgentMessageService } from "@axiom-lattice/core";
|
|
|
21995
23118
|
|
|
21996
23119
|
// src/router/MessageRouter.ts
|
|
21997
23120
|
import {
|
|
21998
|
-
getStoreLattice as
|
|
23121
|
+
getStoreLattice as getStoreLattice27,
|
|
21999
23122
|
agentInstanceManager as agentInstanceManager8
|
|
22000
23123
|
} from "@axiom-lattice/core";
|
|
22001
23124
|
import {
|
|
22002
23125
|
snapshotExactArray as snapshotExactArray3,
|
|
22003
23126
|
snapshotExactRecord as snapshotExactRecord15
|
|
22004
23127
|
} from "@axiom-lattice/protocols";
|
|
22005
|
-
import { randomUUID as
|
|
23128
|
+
import { randomUUID as randomUUID25 } from "crypto";
|
|
22006
23129
|
var BindingNotFoundError = class extends Error {
|
|
22007
23130
|
constructor(message) {
|
|
22008
23131
|
super(message);
|
|
@@ -22095,7 +23218,7 @@ function validateRoomTrustedDispatch(message, options, binding, agentId) {
|
|
|
22095
23218
|
"inputMessageId",
|
|
22096
23219
|
"role",
|
|
22097
23220
|
"title"
|
|
22098
|
-
], ["responsibility"]);
|
|
23221
|
+
], ["responsibility", "source"]);
|
|
22099
23222
|
if (!binding || !targetEnvelope || !target || !context || !authority || typeof authority.expectedRoomThreadId !== "string" || authority.expectedRoomThreadId.length === 0 || trusted?.executionMode !== "followup") return false;
|
|
22100
23223
|
const expectedSender = `room:${context.roomId}:membership:${context.membershipId}`;
|
|
22101
23224
|
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 +23409,7 @@ var MessageRouter = class {
|
|
|
22286
23409
|
channel: message.channel,
|
|
22287
23410
|
adapterChannel: adapter.channel
|
|
22288
23411
|
}, "Thread resolved by adapter strategy");
|
|
22289
|
-
const threadStore =
|
|
23412
|
+
const threadStore = getStoreLattice27("default", "thread").store;
|
|
22290
23413
|
try {
|
|
22291
23414
|
await threadStore.createThread(
|
|
22292
23415
|
tenantId,
|
|
@@ -22323,8 +23446,8 @@ var MessageRouter = class {
|
|
|
22323
23446
|
}
|
|
22324
23447
|
}
|
|
22325
23448
|
if (!threadId) {
|
|
22326
|
-
const threadStore =
|
|
22327
|
-
const newThreadId =
|
|
23449
|
+
const threadStore = getStoreLattice27("default", "thread").store;
|
|
23450
|
+
const newThreadId = randomUUID25();
|
|
22328
23451
|
console.log({
|
|
22329
23452
|
event: "dispatch:thread:create",
|
|
22330
23453
|
agentId,
|
|
@@ -22370,7 +23493,7 @@ var MessageRouter = class {
|
|
|
22370
23493
|
workspace_id: binding?.workspaceId || "",
|
|
22371
23494
|
project_id: binding?.projectId || ""
|
|
22372
23495
|
});
|
|
22373
|
-
const inputMessageId =
|
|
23496
|
+
const inputMessageId = randomUUID25();
|
|
22374
23497
|
const replyTarget = internalRoom ? correlatedRoomReplyTarget(message.replyTarget, inputMessageId) : message.replyTarget;
|
|
22375
23498
|
const replyAdapter = replyTarget ? this.adapterRegistry.get(replyTarget.adapterChannel) : void 0;
|
|
22376
23499
|
if (options.trustedMessageOptions && (!this.trustedDispatcher || !replyTarget || !replyAdapter)) {
|
|
@@ -22406,9 +23529,13 @@ var MessageRouter = class {
|
|
|
22406
23529
|
});
|
|
22407
23530
|
}
|
|
22408
23531
|
}
|
|
23532
|
+
const metadata = message.content.metadata;
|
|
22409
23533
|
const queueMessage = {
|
|
22410
23534
|
input: { id: inputMessageId, message: message.content.text },
|
|
22411
|
-
custom_run_config:
|
|
23535
|
+
custom_run_config: {
|
|
23536
|
+
...metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {},
|
|
23537
|
+
channelRun: true
|
|
23538
|
+
},
|
|
22412
23539
|
...resumeCommand ? { command: resumeCommand } : {}
|
|
22413
23540
|
};
|
|
22414
23541
|
let cleanupReplySubscription;
|
|
@@ -22786,35 +23913,49 @@ function getConfiguredSandboxProvider() {
|
|
|
22786
23913
|
}
|
|
22787
23914
|
async function restoreMcpConnections() {
|
|
22788
23915
|
try {
|
|
22789
|
-
const storeLattice =
|
|
23916
|
+
const storeLattice = getStoreLattice28("default", "mcp");
|
|
22790
23917
|
const store = storeLattice.store;
|
|
22791
23918
|
if (!store) {
|
|
22792
23919
|
logger5.info("MCP store not configured, skipping connection restoration");
|
|
22793
23920
|
return;
|
|
22794
23921
|
}
|
|
22795
|
-
const configs = await store.
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
for (const config of
|
|
23922
|
+
const configs = (await store.getAllConfigsWithoutTenant()).filter(
|
|
23923
|
+
(c2) => c2.status === "connected"
|
|
23924
|
+
);
|
|
23925
|
+
if (configs.length === 0) return;
|
|
23926
|
+
logger5.info(`Restoring ${configs.length} MCP server connections...`);
|
|
23927
|
+
for (const config of configs) {
|
|
22801
23928
|
try {
|
|
22802
|
-
const connection =
|
|
22803
|
-
|
|
23929
|
+
const connection = convertToConnection2(config.config);
|
|
23930
|
+
mcpManager3.registerServer(config.tenantId, config.key, connection, {
|
|
23931
|
+
timeoutMs: config.config.timeout,
|
|
23932
|
+
maxAttempts: config.config.retryAttempts,
|
|
23933
|
+
selectedTools: config.selectedTools
|
|
23934
|
+
});
|
|
22804
23935
|
} catch (err) {
|
|
22805
|
-
logger5.warn(`Failed to prepare MCP server "${config.key}"
|
|
23936
|
+
logger5.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
|
|
22806
23937
|
}
|
|
22807
23938
|
}
|
|
22808
|
-
await
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
23939
|
+
await mcpManager3.connectAll();
|
|
23940
|
+
const results = await Promise.allSettled(
|
|
23941
|
+
configs.map(
|
|
23942
|
+
(config) => mcpManager3.registerToolsToToolLattice(
|
|
23943
|
+
config.tenantId,
|
|
23944
|
+
config.key,
|
|
23945
|
+
config.selectedTools
|
|
23946
|
+
)
|
|
23947
|
+
)
|
|
23948
|
+
);
|
|
23949
|
+
results.forEach((result, index) => {
|
|
23950
|
+
if (result.status === "rejected") {
|
|
23951
|
+
const config = configs[index];
|
|
23952
|
+
logger5.warn(
|
|
23953
|
+
`Failed to register tools for MCP server "${config.tenantId}/${config.key}"`,
|
|
23954
|
+
{ error: result.reason }
|
|
23955
|
+
);
|
|
22815
23956
|
}
|
|
22816
|
-
}
|
|
22817
|
-
logger5.info(`MCP connection restoration complete: ${
|
|
23957
|
+
});
|
|
23958
|
+
logger5.info(`MCP connection restoration complete: ${configs.length} servers`);
|
|
22818
23959
|
} catch (err) {
|
|
22819
23960
|
logger5.error("MCP connection restoration failed", { error: err });
|
|
22820
23961
|
}
|
|
@@ -22836,9 +23977,9 @@ var start = async (config) => {
|
|
|
22836
23977
|
let projectRoomRealtimeRuntime;
|
|
22837
23978
|
if (projectRoomRealtimeMode === "single-gateway") {
|
|
22838
23979
|
projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
|
|
22839
|
-
tasks:
|
|
22840
|
-
rooms:
|
|
22841
|
-
memberships:
|
|
23980
|
+
tasks: getStoreLattice28("default", "task").store,
|
|
23981
|
+
rooms: getStoreLattice28("default", "projectRoom").store,
|
|
23982
|
+
memberships: getStoreLattice28("default", "projectBotMembership").store,
|
|
22842
23983
|
log: { warn: (message, context) => logger5.warn(message, context) }
|
|
22843
23984
|
});
|
|
22844
23985
|
}
|
|
@@ -22881,7 +24022,7 @@ var start = async (config) => {
|
|
|
22881
24022
|
}
|
|
22882
24023
|
setEvalRunService(evalRunner);
|
|
22883
24024
|
try {
|
|
22884
|
-
const menuStore =
|
|
24025
|
+
const menuStore = getStoreLattice28("default", "menu").store;
|
|
22885
24026
|
setMenuRegistry(menuStore);
|
|
22886
24027
|
logger5.info("Menu registry initialized");
|
|
22887
24028
|
} catch {
|
|
@@ -22897,12 +24038,12 @@ var start = async (config) => {
|
|
|
22897
24038
|
});
|
|
22898
24039
|
try {
|
|
22899
24040
|
const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-7UGQ7GMW.mjs");
|
|
22900
|
-
const { registerA2AStandardRoutes } = await import("./a2a-standard-
|
|
24041
|
+
const { registerA2AStandardRoutes } = await import("./a2a-standard-RWR5JTMH.mjs");
|
|
22901
24042
|
const a2a = await import("./a2a-FGYX2HH3.mjs");
|
|
22902
|
-
const a2aKeyStore =
|
|
22903
|
-
const assistantStore =
|
|
22904
|
-
const projectStore =
|
|
22905
|
-
const taskStore =
|
|
24043
|
+
const a2aKeyStore = getStoreLattice28("default", "a2aApiKey").store;
|
|
24044
|
+
const assistantStore = getStoreLattice28("default", "assistant").store;
|
|
24045
|
+
const projectStore = getStoreLattice28("default", "project").store;
|
|
24046
|
+
const taskStore = getStoreLattice28("default", "task").store;
|
|
22906
24047
|
const authService = new A2AAuthService({
|
|
22907
24048
|
keyStore: a2aKeyStore,
|
|
22908
24049
|
assistantStore,
|
|
@@ -22927,22 +24068,22 @@ var start = async (config) => {
|
|
|
22927
24068
|
});
|
|
22928
24069
|
}
|
|
22929
24070
|
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-
|
|
24071
|
+
const { registerOpenRoutes } = await import("./open-AMEMJOFJ.mjs");
|
|
24072
|
+
const { registerOpenSessionRoutes } = await import("./open-session-LBTCU3AE.mjs");
|
|
24073
|
+
const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
|
|
24074
|
+
const { registerOpenFilesRoutes } = await import("./open-files-EJ6O2SGX.mjs");
|
|
22934
24075
|
const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
|
|
22935
|
-
const { registerOpenExposureRoutes } = await import("./open-exposure-
|
|
22936
|
-
const { openLattice } = await import("./openLattice-
|
|
24076
|
+
const { registerOpenExposureRoutes } = await import("./open-exposure-I44Q4M3T.mjs");
|
|
24077
|
+
const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
|
|
22937
24078
|
const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
|
|
22938
24079
|
const { createOpenSandboxProvider } = await import("./sandboxProvider-I3P66VXT.mjs");
|
|
22939
24080
|
const { createAgentProvider } = await import("./agentProvider-X4FVQPKW.mjs");
|
|
22940
|
-
const { registerExposedPluginTools } = await import("./registerExposedPlugins-
|
|
24081
|
+
const { registerExposedPluginTools } = await import("./registerExposedPlugins-6GCOE64C.mjs");
|
|
22941
24082
|
const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
|
|
22942
24083
|
openLattice.register(createKbProvider(new CollectionService2()));
|
|
22943
24084
|
openLattice.register(createOpenSandboxProvider());
|
|
22944
|
-
const threadStore =
|
|
22945
|
-
const assistantStore =
|
|
24085
|
+
const threadStore = getStoreLattice28("default", "thread").store;
|
|
24086
|
+
const assistantStore = getStoreLattice28("default", "assistant").store;
|
|
22946
24087
|
openLattice.register(
|
|
22947
24088
|
createAgentProvider({
|
|
22948
24089
|
threadStore,
|
|
@@ -22970,8 +24111,8 @@ var start = async (config) => {
|
|
|
22970
24111
|
logger5.info("Registered sandbox manager from env configuration");
|
|
22971
24112
|
}
|
|
22972
24113
|
try {
|
|
22973
|
-
const { ResourceController } = await import("./resources-
|
|
22974
|
-
const sharedResourceStore =
|
|
24114
|
+
const { ResourceController } = await import("./resources-DXPXAXEZ.mjs");
|
|
24115
|
+
const sharedResourceStore = getStoreLattice28("default", "sharedResource").store;
|
|
22975
24116
|
const cache = new TokenCache();
|
|
22976
24117
|
const resourceController = new ResourceController({
|
|
22977
24118
|
store: sharedResourceStore,
|