@axiom-lattice/gateway 4.3.2 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/{a2a-standard-ALXWP7V6.mjs → a2a-standard-P7JDL6ZA.mjs} +63 -35
  2. package/dist/a2a-standard-P7JDL6ZA.mjs.map +1 -0
  3. package/dist/{registerExposedPlugins-63EUF4VS.mjs → chunk-3XIIQ6EK.mjs} +62 -18
  4. package/dist/chunk-3XIIQ6EK.mjs.map +1 -0
  5. package/dist/chunk-526UYVNQ.mjs +157 -0
  6. package/dist/chunk-526UYVNQ.mjs.map +1 -0
  7. package/dist/chunk-73QZQQOT.mjs +43 -0
  8. package/dist/chunk-73QZQQOT.mjs.map +1 -0
  9. package/dist/{chunk-GF4GGTWJ.mjs → chunk-HVIKCUOF.mjs} +10 -7
  10. package/dist/chunk-HVIKCUOF.mjs.map +1 -0
  11. package/dist/embed/embed-page-AgentDetailSideApp-PSRQVFNR.js +1 -0
  12. package/dist/embed/{embed-page-AgentGovernanceWorkbench-ZNW72EEJ.js → embed-page-AgentGovernanceWorkbench-UPQHJZD7.js} +1 -1
  13. package/dist/embed/embed-page-MessageGraph-PDNQ67T4.js +6 -0
  14. package/dist/embed/{embed-page-TasksSideApp-F4XUHPTG.css → embed-page-MessageGraph-UT2TCPSO.css} +1 -1
  15. package/dist/embed/{embed-page-ReactChart-XS7D26VP.js → embed-page-ReactChart-25KAFLQ3.js} +1 -1
  16. package/dist/embed/embed-page-ReactChart-MXGF35OQ.css +1 -0
  17. package/dist/embed/embed-page-SkillFlow-TKBMB5VV.js +1 -0
  18. package/dist/embed/{embed-page-TasksSideApp-MQTUCJO4.js → embed-page-TasksSideApp-DLH5LRMS.js} +1 -1
  19. package/dist/embed/{embed-page-TopologyRuntimeView-2NQ3OOYI.css → embed-page-TasksSideApp-HV6DKQ26.css} +1 -1
  20. package/dist/embed/{embed-page-TeamWorkspace-7T2HRXLG.js → embed-page-TeamWorkspace-2WJJWF25.js} +1 -1
  21. package/dist/embed/{embed-page-TeamWorkspace-DXKGNQ3C.css → embed-page-TeamWorkspace-YQFWXDLI.css} +1 -1
  22. package/dist/embed/{embed-page-MessageGraph-TYOOATYV.css → embed-page-TopologyRuntimeView-CWOXLVFW.css} +1 -1
  23. package/dist/embed/{embed-page-TopologyRuntimeView-PU2D7VZ7.js → embed-page-TopologyRuntimeView-WDQRHDMO.js} +1 -1
  24. package/dist/embed/{embed-page-WorkflowAutomationView-ST76M33F.js → embed-page-WorkflowAutomationView-BAMJ2WVB.js} +1 -1
  25. package/dist/embed/embed-page-WorkflowAutomationView-YQFB3CTV.css +1 -0
  26. package/dist/embed/{embed-page-chunk-T25T2YE6.js → embed-page-chunk-HMTKPZ5B.js} +7 -5
  27. package/dist/embed/{embed-page-chunk-ZKJ7Z7P3.js → embed-page-chunk-LOXLXZNP.js} +1 -1
  28. package/dist/embed/{embed-page-chunk-Z4S4OYA4.js → embed-page-chunk-MQDYZS3S.js} +1 -1
  29. package/dist/embed/embed-page-chunk-OBM5EUWZ.js +6974 -0
  30. package/dist/embed/{embed-page-chunk-RLP3BC7G.js → embed-page-chunk-SSC7ANWV.js} +2 -2
  31. package/dist/embed/embed-page-chunk-TL2AYM76.js +2 -0
  32. package/dist/embed/{embed-page-chunk-LE6OFJ7R.js → embed-page-chunk-XVUA7UHE.js} +1 -1
  33. package/dist/embed/embed-page.js +1 -1
  34. package/dist/index.js +5103 -3988
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +1566 -411
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/{open-HH7RKGML.mjs → open-AMEMJOFJ.mjs} +99 -14
  39. package/dist/open-AMEMJOFJ.mjs.map +1 -0
  40. package/dist/{open-catalog-WYS2AENM.mjs → open-catalog-SZQ435AY.mjs} +34 -3
  41. package/dist/open-catalog-SZQ435AY.mjs.map +1 -0
  42. package/dist/{open-exposure-FK4G6O6L.mjs → open-exposure-I44Q4M3T.mjs} +37 -7
  43. package/dist/open-exposure-I44Q4M3T.mjs.map +1 -0
  44. package/dist/{open-files-GVRMK5PL.mjs → open-files-EJ6O2SGX.mjs} +4 -4
  45. package/dist/{open-session-3VN65PRR.mjs → open-session-LBTCU3AE.mjs} +4 -4
  46. package/dist/{openLattice-GAAGQJST.mjs → openLattice-Q6M5S6CY.mjs} +4 -2
  47. package/dist/registerExposedPlugins-6GCOE64C.mjs +11 -0
  48. package/dist/{resources-JFKAPS45.mjs → resources-DXPXAXEZ.mjs} +16 -9
  49. package/dist/{resources-JFKAPS45.mjs.map → resources-DXPXAXEZ.mjs.map} +1 -1
  50. package/package.json +11 -8
  51. package/dist/a2a-standard-ALXWP7V6.mjs.map +0 -1
  52. package/dist/chunk-GF4GGTWJ.mjs.map +0 -1
  53. package/dist/chunk-QKPZWWHH.mjs +0 -433
  54. package/dist/chunk-QKPZWWHH.mjs.map +0 -1
  55. package/dist/embed/embed-page-AgentDetailSideApp-7PQ2JKG6.js +0 -1
  56. package/dist/embed/embed-page-MessageGraph-P7DBTZCL.js +0 -6
  57. package/dist/embed/embed-page-ReactChart-VBM467ER.css +0 -1
  58. package/dist/embed/embed-page-SkillFlow-HPH37PZH.js +0 -1
  59. package/dist/embed/embed-page-WorkflowAutomationView-MAYS27YJ.css +0 -1
  60. package/dist/embed/embed-page-chunk-4ZSGPIBZ.js +0 -1
  61. package/dist/embed/embed-page-chunk-BRETUPSG.js +0 -4094
  62. package/dist/embed/embed-page-chunk-I42T7B7V.js +0 -2415
  63. package/dist/embed/embed-page-chunk-VRV57DFE.js +0 -466
  64. package/dist/embed/embed-page-chunk-ZXFZANZM.js +0 -1
  65. package/dist/mcp-configs-WCY5EXCW.mjs +0 -30
  66. package/dist/open-HH7RKGML.mjs.map +0 -1
  67. package/dist/open-catalog-WYS2AENM.mjs.map +0 -1
  68. package/dist/open-exposure-FK4G6O6L.mjs.map +0 -1
  69. package/dist/registerExposedPlugins-63EUF4VS.mjs.map +0 -1
  70. /package/dist/{open-files-GVRMK5PL.mjs.map → open-files-EJ6O2SGX.mjs.map} +0 -0
  71. /package/dist/{open-session-3VN65PRR.mjs.map → open-session-LBTCU3AE.mjs.map} +0 -0
  72. /package/dist/{mcp-configs-WCY5EXCW.mjs.map → openLattice-Q6M5S6CY.mjs.map} +0 -0
  73. /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 randomUUID22 } from "crypto";
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 allLattices = toolLatticeManager.getAllLattices();
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
- PluginRegistry as PluginRegistry2,
4395
- getSandboxPluginCatalog as getSandboxPluginCatalog2,
4396
- mergePluginSources,
4397
- serializePluginMeta
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).map(serializePluginMeta);
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, PluginRegistry as PluginRegistry3, getSandboxPluginCatalog as getSandboxPluginCatalog3, mergePluginSources as mergePluginSources2 } from "@axiom-lattice/core";
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
- const tenantId = getTenant(request);
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, "&amp;").replace(/"/g, "&quot;");
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 getStoreLattice10 } from "@axiom-lattice/core";
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 getStoreLattice9,
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 getStoreLattice9("default", "eval").store;
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 getTenantId10(request) {
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 getStoreLattice10("default", "eval").store;
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 = getTenantId10(request);
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 getStoreLattice11 } from "@axiom-lattice/core";
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 = getStoreLattice11("default", "user").store;
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 getStoreLattice12 } from "@axiom-lattice/core";
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 = getStoreLattice12("default", "tenant").store;
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 getStoreLattice13 } from "@axiom-lattice/core";
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 = getStoreLattice13("default", "user").store;
8758
- this.tenantStore = getStoreLattice13("default", "tenant").store;
8759
- this.userTenantLinkStore = getStoreLattice13("default", "userTenantLink").store;
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 = getStoreLattice13("default", "assistant").store;
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
- const body = parseLarkRequestBody(request.body, larkInstallation.config.encryptKey);
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 randomUUID10 } from "crypto";
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 getTenantId11(request) {
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: getStoreLattice28 } = await import("@axiom-lattice/core");
9690
- const store = getStoreLattice28("default", "channelInstallation").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 = getTenantId11(request);
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 = getTenantId11(request);
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 = getTenantId11(request);
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 = randomUUID10();
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 = getTenantId11(request);
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 = getTenantId11(request);
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 getStoreLattice14 } from "@axiom-lattice/core";
10010
- function getTenantId12(request) {
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 getStoreLattice14("default", "channelInstallation").store;
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 = getTenantId12(request);
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 = getTenantId12(request);
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 = getTenantId12(request);
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 = getTenantId12(request);
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 = getTenantId12(request);
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 = getTenantId12(request);
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 getTenantId13(request) {
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 = getTenantId13(request);
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 = getTenantId13(request);
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 = getTenantId13(request);
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 = getTenantId13(request);
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 = getTenantId13(request);
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 getStoreLattice15 } from "@axiom-lattice/core";
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 getTenantId14(request) {
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 = getStoreLattice15("default", "assistant");
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 = getStoreLattice15("default", "assistant");
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(getTenantId14(request), assistantId);
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 = getTenantId14(request);
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 randomUUID11 } from "crypto";
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-${randomUUID11()}`,
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
- getStoreLattice as getStoreLattice16,
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 as projectTaskAdmissionNotifier2,
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, projectTaskAdmissionNotifier } from "@axiom-lattice/core";
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 notifyProjectTaskAdmission(task) {
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: getTenantId15(request),
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 = getTenantId15(request);
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(getTenantId15(request), request.params.id);
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 = getTenantId15(request);
12467
+ const tenantId = getTenantId16(request);
11590
12468
  const userId = getUserId4(request);
11591
12469
  const governed = governedCreate(body);
11592
12470
  if (governed) return reply.code(409).send({ success: false, code: governed });
@@ -11632,19 +12510,16 @@ function createTaskController(deps) {
11632
12510
  requireReview: false,
11633
12511
  context: { ...body.context ?? {}, source: "project_room" }
11634
12512
  });
11635
- if (ownerType === "agent") notifyProjectTaskAdmission(created);
11636
12513
  return reply.code(201).send({ success: true, data: created });
11637
12514
  });
11638
12515
  });
11639
12516
  const update = withErrors(async (request, reply) => {
11640
12517
  const body = snapshotUpdate(request.body);
11641
12518
  await preauthorizeExplicitProject(request, authorize);
11642
- const current = await deps.tasks.getById(getTenantId15(request), request.params.id);
12519
+ const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
11643
12520
  if (!current) return sendNotFound(reply);
11644
12521
  await authorizeMutation(request, current, authorize);
11645
12522
  return lockedTask(request, current, async (locked) => {
11646
- const governed = governedUpdate(locked, body);
11647
- if (governed) return reply.code(409).send({ success: false, code: governed });
11648
12523
  if (body.projectId !== void 0 && body.projectId !== locked.projectId || body.workspaceId !== void 0 && body.workspaceId !== locked.workspaceId) throw notFound();
11649
12524
  await validateReferences(deps.tasks, locked, body.parentId, body.dependencies);
11650
12525
  const updates = { ...body };
@@ -11657,7 +12532,7 @@ function createTaskController(deps) {
11657
12532
  });
11658
12533
  const remove = withErrors(async (request, reply) => {
11659
12534
  await preauthorizeExplicitProject(request, authorize);
11660
- const current = await deps.tasks.getById(getTenantId15(request), request.params.id);
12535
+ const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
11661
12536
  if (!current) return sendNotFound(reply);
11662
12537
  await authorizeMutation(request, current, authorize);
11663
12538
  return lockedTask(request, current, async (locked) => {
@@ -11671,7 +12546,7 @@ function createTaskController(deps) {
11671
12546
  const complete = withErrors(async (request, reply) => {
11672
12547
  const body = snapshotComplete(request.body);
11673
12548
  await preauthorizeExplicitProject(request, authorize);
11674
- const current = await deps.tasks.getById(getTenantId15(request), request.params.id);
12549
+ const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
11675
12550
  if (!current) return sendNotFound(reply);
11676
12551
  await authorizeMutation(request, current, authorize);
11677
12552
  return lockedTask(request, current, async (locked) => {
@@ -11688,7 +12563,7 @@ function createTaskController(deps) {
11688
12563
  });
11689
12564
  const workItems = withErrors(async (request, reply) => {
11690
12565
  await preauthorizeExplicitProject(request, authorize);
11691
- const current = await deps.tasks.getById(getTenantId15(request), request.params.id);
12566
+ const current = await deps.tasks.getById(getTenantId16(request), request.params.id);
11692
12567
  if (!current) return sendNotFound(reply);
11693
12568
  await authorizeRead(request, current, authorize);
11694
12569
  const query = exactObject(request.query, ["action", "limit", "offset"]);
@@ -11704,66 +12579,7 @@ function createTaskController(deps) {
11704
12579
  });
11705
12580
  return reply.send({ success: true, data: rows });
11706
12581
  });
11707
- const reassign = withErrors(async (request, reply) => {
11708
- const body = snapshotReassign(request.body);
11709
- const pathProject = request.params.projectId;
11710
- const access = await authorize(request, pathProject, "manage");
11711
- const snapshot = await deps.tasks.getById(getTenantId15(request), request.params.id);
11712
- if (!snapshot || snapshot.projectId !== access.project.id || snapshot.workspaceId !== access.project.workspaceId || snapshot.ownerType !== "agent") throw notFound();
11713
- const projectId = snapshot.projectId;
11714
- return deps.executionMutex.runExclusive({
11715
- tenantId: snapshot.tenantId,
11716
- assistantId: snapshot.ownerId,
11717
- projectId
11718
- }, async () => {
11719
- const machineTask = await deps.tasks.getById(snapshot.tenantId, snapshot.id);
11720
- if (!machineTask || machineTask.tenantId !== snapshot.tenantId || machineTask.projectId !== snapshot.projectId || machineTask.workspaceId !== snapshot.workspaceId || machineTask.ownerType !== "agent" || machineTask.ownerId !== snapshot.ownerId || machineTask.status !== snapshot.status || machineTask.updatedAt.getTime() !== snapshot.updatedAt.getTime()) {
11721
- throw new HttpTaskError(409, "TASK_STATUS_CONFLICT");
11722
- }
11723
- return mutationMutex.runExclusive({ tenantId: snapshot.tenantId, projectId }, async () => {
11724
- const lockedAccess = await authorize(request, pathProject, "manage");
11725
- if (lockedAccess.project.id !== access.project.id || lockedAccess.project.workspaceId !== access.project.workspaceId) {
11726
- throw new ProjectCollaborationError("PROJECT_ACCESS_DENIED", 403, "Project access denied");
11727
- }
11728
- const current = await deps.tasks.getById(snapshot.tenantId, snapshot.id);
11729
- if (!current || current.tenantId !== snapshot.tenantId || current.projectId !== snapshot.projectId || current.workspaceId !== snapshot.workspaceId || current.ownerType !== "agent" || current.ownerId !== snapshot.ownerId || current.status !== snapshot.status || current.updatedAt.getTime() !== snapshot.updatedAt.getTime()) {
11730
- throw new HttpTaskError(409, "TASK_STATUS_CONFLICT");
11731
- }
11732
- const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
11733
- const target = await deps.bots.findByAssistant(current.tenantId, current.projectId, body.ownerId);
11734
- if (!room || !target || target.status !== "active" || target.roomId !== room.id || target.workspaceId !== current.workspaceId) {
11735
- return reply.code(409).send({ success: false, code: "PROJECT_TASK_OWNER_INACTIVE" });
11736
- }
11737
- const caller = await deps.bots.findByAssistant(current.tenantId, current.projectId, current.ownerId);
11738
- if (!caller || caller.status !== "active" || caller.roomId !== room.id || caller.workspaceId !== current.workspaceId) {
11739
- return reply.code(409).send({ success: false, code: "PROJECT_TASK_CALLER_INACTIVE" });
11740
- }
11741
- const ensured = await deps.threadService.ensure({ task: current, room, membership: target, mode: "reassignment" });
11742
- const authorization = await deps.reassignmentAuthorizer.authorize({
11743
- tenantId: current.tenantId,
11744
- workspaceId: current.workspaceId,
11745
- projectId: current.projectId,
11746
- roomId: room.id,
11747
- taskId: current.id,
11748
- callerMembershipId: caller.id,
11749
- callerAssistantId: caller.assistantId,
11750
- targetMembershipId: target.id,
11751
- targetAssistantId: target.assistantId,
11752
- ...body.expectedThreadId === void 0 ? {} : { expectedThreadId: body.expectedThreadId },
11753
- newThreadId: ensured.thread.id
11754
- });
11755
- const result = await deps.lifecycle.reassignProjectTask({
11756
- tenantId: current.tenantId,
11757
- taskId: current.id,
11758
- authorization,
11759
- actor: `user:${getUserId4(request)}`
11760
- });
11761
- if (!result.success) return reply.code(lifecycleStatus(result.code)).send(result);
11762
- return reply.send({ success: true, data: result.task, orphanCandidate: false });
11763
- });
11764
- });
11765
- });
11766
- return { list, get, create, update, remove, complete, workItems, reassign };
12582
+ return { list, get, create, update, remove, complete, workItems };
11767
12583
  }
11768
12584
  function parseMetadata(value) {
11769
12585
  let parsed = value;
@@ -12082,7 +12898,7 @@ import { createHmac as createHmac3 } from "crypto";
12082
12898
  import { z as z5, ZodError as ZodError2 } from "zod";
12083
12899
 
12084
12900
  // src/services/agent-web-app/AgentWebAppRuntimeService.ts
12085
- import { createHash as createHash2, randomUUID as randomUUID12 } from "crypto";
12901
+ import { createHash as createHash3, randomUUID as randomUUID13 } from "crypto";
12086
12902
  import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock } from "@axiom-lattice/protocols";
12087
12903
  function isRecord2(value) {
12088
12904
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -12276,10 +13092,10 @@ ${formatFileRefsSection(refs)}` : input.content;
12276
13092
  const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
12277
13093
  this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
12278
13094
  if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
12279
- const userDigest = createHash2("sha256").update(userId, "utf8").digest("hex");
12280
- const contentDigest = createHash2("sha256").update(file.bytes).digest("hex");
13095
+ const userDigest = createHash3("sha256").update(userId, "utf8").digest("hex");
13096
+ const contentDigest = createHash3("sha256").update(file.bytes).digest("hex");
12281
13097
  const displayName = file.name;
12282
- const physicalName = `${randomUUID12()}-${sanitizePhysicalFilename(file.name)}`;
13098
+ const physicalName = `${randomUUID13()}-${sanitizePhysicalFilename(file.name)}`;
12283
13099
  const saved = await this.deps.saveProjectFile({
12284
13100
  tenantId: runtime.webApp.tenantId,
12285
13101
  workspaceId: runtime.project.workspaceId,
@@ -12431,7 +13247,7 @@ ${formatFileRefsSection(refs)}` : input.content;
12431
13247
  const thread = await this.deps.threadStore.createThread(
12432
13248
  runtime.webApp.tenantId,
12433
13249
  runtime.webApp.assistantId,
12434
- randomUUID12(),
13250
+ randomUUID13(),
12435
13251
  { metadata: this.metadata(runtime, userId, normalizeLabel(rawLabel)) }
12436
13252
  );
12437
13253
  return toRuntimeThread(thread);
@@ -12483,7 +13299,7 @@ function implicitThreadId(runtime, userId) {
12483
13299
  userId,
12484
13300
  runtime.project.id
12485
13301
  ]);
12486
- const bytes = createHash2("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
13302
+ const bytes = createHash3("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
12487
13303
  bytes[6] = bytes[6] & 15 | 128;
12488
13304
  bytes[8] = bytes[8] & 63 | 128;
12489
13305
  const hex = bytes.toString("hex");
@@ -13251,7 +14067,7 @@ function registerAgentWebAppEmbedRoutes(app2, deps = {}) {
13251
14067
  }
13252
14068
 
13253
14069
  // src/services/agent-web-app/AgentWebAppAttachmentRef.ts
13254
- import { createCipheriv, createDecipheriv, createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
14070
+ import { createCipheriv, createDecipheriv, createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
13255
14071
  var TTL_MS = 15 * 6e4;
13256
14072
  function resolveAgentWebAppAttachmentSecret(consoleSecret) {
13257
14073
  return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
@@ -13260,7 +14076,7 @@ var AgentWebAppAttachmentRefService = class {
13260
14076
  constructor(secret, now = Date.now) {
13261
14077
  this.now = now;
13262
14078
  if (!secret) throw new Error("Attachment signing secret is required");
13263
- this.key = createHash3("sha256").update(secret, "utf8").digest();
14079
+ this.key = createHash4("sha256").update(secret, "utf8").digest();
13264
14080
  }
13265
14081
  sign(input) {
13266
14082
  const payload = {
@@ -13322,7 +14138,7 @@ function decodeCanonical(value, expectedBytes) {
13322
14138
  return decoded;
13323
14139
  }
13324
14140
  function digestUser(userId) {
13325
- return createHash3("sha256").update(userId, "utf8").digest("hex");
14141
+ return createHash4("sha256").update(userId, "utf8").digest("hex");
13326
14142
  }
13327
14143
  function isAttachmentPayload(value) {
13328
14144
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
@@ -13682,12 +14498,12 @@ function registerCapabilityBundleRoutes(app2, controller) {
13682
14498
  }
13683
14499
 
13684
14500
  // src/services/collaboration/ProjectRoomService.ts
13685
- import { randomUUID as randomUUID13 } from "crypto";
14501
+ import { randomUUID as randomUUID14 } from "crypto";
13686
14502
  var ProjectRoomService = class {
13687
14503
  /** Creates a project room service from explicitly injected collaborators. */
13688
14504
  constructor(deps) {
13689
14505
  this.deps = deps;
13690
- this.idFactory = deps.idFactory ?? randomUUID13;
14506
+ this.idFactory = deps.idFactory ?? randomUUID14;
13691
14507
  }
13692
14508
  /**
13693
14509
  * Authorizes read access and ensures the project's main room.
@@ -13747,7 +14563,7 @@ var ProjectRoomService = class {
13747
14563
  };
13748
14564
 
13749
14565
  // src/services/collaboration/ProjectMembershipService.ts
13750
- import { randomUUID as randomUUID14 } from "crypto";
14566
+ import { randomUUID as randomUUID15 } from "crypto";
13751
14567
  var ProjectMembershipServiceError = class extends Error {
13752
14568
  constructor(code, statusCode, message) {
13753
14569
  super(message);
@@ -13763,7 +14579,7 @@ function isMembershipConflict(error) {
13763
14579
  var ProjectMembershipService = class {
13764
14580
  constructor(deps) {
13765
14581
  this.deps = deps;
13766
- this.idFactory = deps.idFactory ?? randomUUID14;
14582
+ this.idFactory = deps.idFactory ?? randomUUID15;
13767
14583
  this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
13768
14584
  }
13769
14585
  async list(actor3) {
@@ -13897,7 +14713,7 @@ var ProjectMembershipService = class {
13897
14713
  };
13898
14714
 
13899
14715
  // src/services/collaboration/ProjectRosterService.ts
13900
- import { randomUUID as randomUUID15 } from "crypto";
14716
+ import { randomUUID as randomUUID16 } from "crypto";
13901
14717
  var ProjectRosterServiceError = class extends Error {
13902
14718
  /** Creates a stable roster service error. */
13903
14719
  constructor(code, statusCode, message) {
@@ -13916,7 +14732,7 @@ var ProjectRosterService = class {
13916
14732
  /** Creates a roster service from explicitly injected collaborators. */
13917
14733
  constructor(deps) {
13918
14734
  this.deps = deps;
13919
- this.idFactory = deps.idFactory ?? randomUUID15;
14735
+ this.idFactory = deps.idFactory ?? randomUUID16;
13920
14736
  this.mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
13921
14737
  }
13922
14738
  /** Lists all bot memberships visible to a project reader. */
@@ -14150,7 +14966,7 @@ var ProjectRosterService = class {
14150
14966
  };
14151
14967
 
14152
14968
  // src/services/collaboration/RoomMessageService.ts
14153
- import { createHash as createHash4, randomUUID as randomUUID16 } from "crypto";
14969
+ import { createHash as createHash5, randomUUID as randomUUID17 } from "crypto";
14154
14970
 
14155
14971
  // src/services/collaboration/ProjectBotMembershipSnapshot.ts
14156
14972
  import { snapshotExactRecord as snapshotExactRecord4 } from "@axiom-lattice/protocols";
@@ -14239,7 +15055,7 @@ var RoomMessageService = class {
14239
15055
  constructor(deps, options = {}) {
14240
15056
  this.deps = deps;
14241
15057
  this.idempotentOutcomes = /* @__PURE__ */ new Map();
14242
- this.idFactory = deps.idFactory ?? randomUUID16;
15058
+ this.idFactory = deps.idFactory ?? randomUUID17;
14243
15059
  const limit = options.idempotentOutcomeLimit ?? 1e3;
14244
15060
  if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("idempotentOutcomeLimit must be a positive safe integer");
14245
15061
  this.idempotentOutcomeLimit = limit;
@@ -14420,7 +15236,7 @@ var RoomMessageService = class {
14420
15236
  let idempotencyKey;
14421
15237
  if (value.requestIdempotencyKey !== void 0) {
14422
15238
  if (typeof value.requestIdempotencyKey !== "string" || !/^[\x21-\x7e]{1,200}$/.test(value.requestIdempotencyKey.trim())) throw new RoomMessageServiceError("INVALID_MESSAGE", 400, "Invalid idempotency key");
14423
- idempotencyKey = `room-human:${actor3.userId}:${createHash4("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
15239
+ idempotencyKey = `room-human:${actor3.userId}:${createHash5("sha256").update(value.requestIdempotencyKey.trim(), "utf8").digest("hex")}`;
14424
15240
  }
14425
15241
  return { text, mentions, botIds, ...value.replyToMessageId === void 0 ? {} : { replyToMessageId: value.replyToMessageId }, ...idempotencyKey === void 0 ? {} : { idempotencyKey } };
14426
15242
  }
@@ -14477,7 +15293,7 @@ function cloneRoomMessage(message) {
14477
15293
  };
14478
15294
  }
14479
15295
  function fingerprintRequest(input) {
14480
- return createHash4("sha256").update(JSON.stringify({
15296
+ return createHash5("sha256").update(JSON.stringify({
14481
15297
  text: input.text,
14482
15298
  mentions: input.mentions,
14483
15299
  replyToMessageId: input.replyToMessageId ?? null
@@ -14492,7 +15308,7 @@ import { ZodError as ZodError3 } from "zod";
14492
15308
  import { toProjectRoomPublicMessage } from "@axiom-lattice/protocols";
14493
15309
 
14494
15310
  // src/services/collaboration/RoomInterruptService.ts
14495
- import { randomUUID as randomUUID17 } from "crypto";
15311
+ import { randomUUID as randomUUID18 } from "crypto";
14496
15312
 
14497
15313
  // src/router/roomInterrupt.ts
14498
15314
  var CLARIFY_FENCE = /^```clarify\n([\s\S]*?)\n```$/;
@@ -14540,7 +15356,7 @@ var RoomInterruptService = class {
14540
15356
  /** Creates the service from explicitly injected stores and dispatcher. */
14541
15357
  constructor(deps) {
14542
15358
  this.deps = deps;
14543
- this.idFactory = deps.idFactory ?? randomUUID17;
15359
+ this.idFactory = deps.idFactory ?? randomUUID18;
14544
15360
  }
14545
15361
  /** Resolves a pending clarify interrupt with no answers. */
14546
15362
  async skip(input) {
@@ -14588,6 +15404,7 @@ var RoomInterruptService = class {
14588
15404
  inputMessageId,
14589
15405
  role: membership.role,
14590
15406
  title: membership.title,
15407
+ source: "project_room",
14591
15408
  ...membership.responsibility === void 0 ? {} : { responsibility: membership.responsibility }
14592
15409
  }
14593
15410
  }
@@ -14795,7 +15612,7 @@ async function execute(reply, operation) {
14795
15612
  }
14796
15613
 
14797
15614
  // src/routes/cospace.ts
14798
- import { randomUUID as randomUUID18 } from "crypto";
15615
+ import { randomUUID as randomUUID19 } from "crypto";
14799
15616
  import { ZodError as ZodError5 } from "zod";
14800
15617
  import {
14801
15618
  generateDirectHumanProjectId,
@@ -14829,7 +15646,7 @@ async function computeUnread(deps, tenantId, userId, roomId, lastMessage) {
14829
15646
  });
14830
15647
  }
14831
15648
  function registerCoSpaceRoutes(app2, deps) {
14832
- const idFactory = deps.idFactory ?? (() => randomUUID18());
15649
+ const idFactory = deps.idFactory ?? (() => randomUUID19());
14833
15650
  app2.get(
14834
15651
  "/api/workspaces/:workspaceId/rooms",
14835
15652
  async (request, reply) => {
@@ -15713,6 +16530,7 @@ var RoomDispatchService = class {
15713
16530
  inputMessageId: "pending-router-input",
15714
16531
  role: membership.role,
15715
16532
  title: membership.title,
16533
+ source: "project_room",
15716
16534
  ...membership.responsibility === void 0 ? {} : { responsibility: membership.responsibility }
15717
16535
  } }
15718
16536
  }
@@ -15784,8 +16602,7 @@ function exactValues(value, required, optional = []) {
15784
16602
  }
15785
16603
 
15786
16604
  // src/services/collaboration/CoordinatorWakeNotifier.ts
15787
- import { randomUUID as randomUUID19 } from "crypto";
15788
- import { isProjectLifecycleTask as isProjectLifecycleTask2 } from "@axiom-lattice/core";
16605
+ import { randomUUID as randomUUID20 } from "crypto";
15789
16606
  import {
15790
16607
  snapshotExactRecord as snapshotExactRecord6
15791
16608
  } from "@axiom-lattice/protocols";
@@ -15801,7 +16618,7 @@ var CoordinatorWakeNotifier = class {
15801
16618
  this.accepted = /* @__PURE__ */ new Set();
15802
16619
  this.maxPending = deps.maxPending ?? 1024;
15803
16620
  if (!Number.isSafeInteger(this.maxPending) || this.maxPending < 1) throw new RangeError("maxPending must be a positive safe integer");
15804
- this.idFactory = deps.idFactory ?? randomUUID19;
16621
+ this.idFactory = deps.idFactory ?? randomUUID20;
15805
16622
  }
15806
16623
  /** Admits one notification without awaiting any store or dispatch operation. */
15807
16624
  handle(value, anchorMessageId) {
@@ -15873,7 +16690,7 @@ var CoordinatorWakeNotifier = class {
15873
16690
  try {
15874
16691
  const task = snapshotTaskItem(await this.deps.tasks.getById(notification.tenantId, notification.taskId));
15875
16692
  if (this.closed) return;
15876
- if (!task || !isProjectLifecycleTask2(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) {
16693
+ if (!task || !isProjectLifecycleTask(task) || task.tenantId !== notification.tenantId || task.workspaceId !== notification.workspaceId || task.projectId !== notification.projectId || task.status !== notification.status || task.updatedAt.toISOString() !== notification.updatedAt || notification.threadId !== void 0 && task.context?.thread_id !== notification.threadId) {
15877
16694
  this.warn("project-room:coordinator-wake-stale", { taskId: safeId(notification.taskId), eventKey: safeId(notification.eventKey) });
15878
16695
  return;
15879
16696
  }
@@ -16156,10 +16973,10 @@ var ProjectTaskExecutionMutex = class {
16156
16973
  };
16157
16974
 
16158
16975
  // src/services/collaboration/ProjectTaskExecutionService.ts
16159
- import { randomUUID as randomUUID20 } from "crypto";
16976
+ import { randomUUID as randomUUID21 } from "crypto";
16160
16977
  import {
16978
+ createTaskRunBridge,
16161
16979
  ThreadStatus as ThreadStatus3,
16162
- isProjectLifecycleTask as isProjectLifecycleTask3,
16163
16980
  parseExternalDependency
16164
16981
  } from "@axiom-lattice/core";
16165
16982
  import {
@@ -16193,6 +17010,7 @@ var ProjectTaskExecutionService = class {
16193
17010
  if (!Number.isFinite(options.lifecycleSettlementTimeoutMs) || options.lifecycleSettlementTimeoutMs <= 0) {
16194
17011
  throw new Error("Project task lifecycle settlement timeout must be positive");
16195
17012
  }
17013
+ this.runBridge = createTaskRunBridge({ lifecycle: this.deps.lifecycle });
16196
17014
  }
16197
17015
  /** Validates, atomically starts, dispatches, and settles a pending Project Task. */
16198
17016
  async start(input, signal) {
@@ -16200,24 +17018,24 @@ var ProjectTaskExecutionService = class {
16200
17018
  this.throwIfCancelled(signal);
16201
17019
  const ensured = await this.deps.threads.ensure({ ...validated, mode: "current_owner" });
16202
17020
  this.throwIfCancelled(signal);
16203
- const started = await this.deps.lifecycle.startTask({
17021
+ const begun = await this.runBridge.beginRun({
16204
17022
  tenantId: input.tenantId,
16205
- taskId: input.taskId,
17023
+ task: validated.task,
16206
17024
  actor: SYSTEM_ACTOR,
16207
17025
  threadId: ensured.thread.id
16208
17026
  });
16209
- if (!started.success) throw new ProjectTaskExecutionError(started.code, started.error);
16210
- if (signal?.aborted && (!started.transitioned || !started.mutated)) this.throwIfCancelled(signal);
16211
- if (!started.transitioned || !started.mutated) {
17027
+ if (!begun.success) throw new ProjectTaskExecutionError(begun.code, begun.error);
17028
+ if (signal?.aborted && (!begun.transitioned || !begun.mutated)) this.throwIfCancelled(signal);
17029
+ if (!begun.transitioned || !begun.mutated) {
16212
17030
  throw new ProjectTaskExecutionError("PROJECT_TASK_START_CONFLICT", "Another caller already started this task");
16213
17031
  }
16214
17032
  if (signal?.aborted) return this.compensateCancellation(
16215
- started.task,
17033
+ begun.task,
16216
17034
  ensured.thread,
16217
- `project-task:${started.task.id}:${ensured.thread.id}:start`,
17035
+ `project-task:${begun.task.id}:${ensured.thread.id}:start`,
16218
17036
  false
16219
17037
  );
16220
- const current = await this.requireCurrentThread(started.task, ensured.thread);
17038
+ const current = await this.requireCurrentThread(begun.task, ensured.thread);
16221
17039
  if (signal?.aborted) return this.compensateCancellation(
16222
17040
  current,
16223
17041
  ensured.thread,
@@ -16360,7 +17178,7 @@ var ProjectTaskExecutionService = class {
16360
17178
  }
16361
17179
  async requireTask(input) {
16362
17180
  const value = snapshotTaskItem(await this.deps.tasks.getById(input.tenantId, input.taskId));
16363
- if (!value || value.tenantId !== input.tenantId || value.id !== input.taskId || !isProjectLifecycleTask3(value)) {
17181
+ if (!value || value.tenantId !== input.tenantId || value.id !== input.taskId || !isProjectLifecycleTask(value)) {
16364
17182
  throw new ProjectTaskExecutionError("PROJECT_TASK_NOT_READY", "Project task does not exist or is out of scope");
16365
17183
  }
16366
17184
  return value;
@@ -16424,7 +17242,7 @@ var ProjectTaskExecutionService = class {
16424
17242
  return current;
16425
17243
  }
16426
17244
  async dispatchRun(task, thread, mode, signal) {
16427
- const messageId = (this.options.idFactory ?? randomUUID20)();
17245
+ const messageId = (this.options.idFactory ?? randomUUID21)();
16428
17246
  if (signal?.aborted) return this.compensateCancellation(task, thread, messageId, false);
16429
17247
  const settlement = this.armSettlement(task, thread, messageId);
16430
17248
  try {
@@ -16618,13 +17436,12 @@ var ProjectTaskExecutionService = class {
16618
17436
  const summary = clarify.questions.find(
16619
17437
  (question) => typeof question.question === "string" && question.question.trim().length > 0
16620
17438
  )?.question.trim() ?? "Waiting for clarification from the room";
16621
- const result = await this.withLifecycleTimeout(this.deps.lifecycle.interruptTask({
17439
+ const result = await this.withLifecycleTimeout(this.runBridge.settleRun({
16622
17440
  tenantId: task.tenantId,
16623
- taskId: task.id,
16624
- type: "missing_input",
16625
- summary,
17441
+ task,
16626
17442
  actor: SYSTEM_ACTOR,
16627
- threadId: thread.id
17443
+ threadId: thread.id,
17444
+ outcome: { kind: "interrupted", summary }
16628
17445
  }), void 0);
16629
17446
  if (!result || !result.success) return void 0;
16630
17447
  return result.task ?? await this.reloadTask(task);
@@ -16686,12 +17503,12 @@ var ProjectTaskExecutionService = class {
16686
17503
  }
16687
17504
  let failed;
16688
17505
  try {
16689
- failed = await this.deps.lifecycle.failTask({
17506
+ failed = await this.runBridge.settleRun({
16690
17507
  tenantId: task.tenantId,
16691
- taskId: task.id,
16692
- failureReason: error,
17508
+ task,
16693
17509
  actor: SYSTEM_ACTOR,
16694
- threadId: thread.id
17510
+ threadId: thread.id,
17511
+ outcome: { kind: "failed", lifecycleHandled: false, error }
16695
17512
  });
16696
17513
  } catch {
16697
17514
  return { kind: "failed", task: current, thread, messageId, error, cause, lifecycleHandled: false };
@@ -16752,7 +17569,8 @@ function buildTrustedTaskContext(task, thread, inputMessageId) {
16752
17569
  assistantId: task.ownerId,
16753
17570
  taskId: task.id,
16754
17571
  threadId: thread.id,
16755
- inputMessageId
17572
+ inputMessageId,
17573
+ source: "project_task"
16756
17574
  };
16757
17575
  }
16758
17576
  function taskThreadMetadata(thread) {
@@ -16806,9 +17624,9 @@ function validRecoveryRow(row, task, thread) {
16806
17624
  }
16807
17625
 
16808
17626
  // src/services/collaboration/ProjectTaskObserver.ts
16809
- import { createHash as createHash5 } from "crypto";
17627
+ import { createHash as createHash6 } from "crypto";
16810
17628
  import {
16811
- isProjectLifecycleTask as isProjectLifecycleTask4,
17629
+ createTaskRunBridge as createTaskRunBridge2,
16812
17630
  parseExternalDependency as parseExternalDependency2
16813
17631
  } from "@axiom-lattice/core";
16814
17632
  import {
@@ -16835,6 +17653,7 @@ var ProjectTaskObserver = class {
16835
17653
  throw new Error(`Invalid Project task observer bound: ${name}`);
16836
17654
  }
16837
17655
  }
17656
+ this.runBridge = createTaskRunBridge2({ lifecycle: this.deps.lifecycle });
16838
17657
  }
16839
17658
  /** Fire-and-forget notifier adapter that never waits for observer work or machine acquisition. */
16840
17659
  async listener(event) {
@@ -16890,15 +17709,15 @@ var ProjectTaskObserver = class {
16890
17709
  if (signal?.aborted) continue;
16891
17710
  const eventTime = Date.parse(hint.event.updatedAt);
16892
17711
  if (task && task.status === "pending" && task.ownerType === "agent" && task.workspaceId === hint.event.workspaceId && task.projectId === hint.event.projectId && task.ownerId === hint.event.ownerId && task.updatedAt.getTime() >= eventTime) {
16893
- await this.reconcileTask(task, signal);
17712
+ await this.reconcileTask(task, signal, "dispatch");
16894
17713
  }
16895
17714
  }
16896
17715
  }
16897
17716
  /** Awaits one notification for tests, startup repair, or other non-notifier callers. */
16898
17717
  async notify(event) {
16899
17718
  const task = snapshotTaskItem(await this.deps.tasks.getById(event.tenantId, event.taskId));
17719
+ if (!task || !isProjectLifecycleTask(task) || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId) return;
16900
17720
  const item = await this.deps.workItems.findByEventKey(event.tenantId, event.taskId, event.eventKey);
16901
- if (!task || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId) return;
16902
17721
  const matches = item !== null && item !== void 0 && item.eventKey === event.eventKey && item.action === event.action && item.taskId === event.taskId && item.tenantId === event.tenantId && item.workspaceId === event.workspaceId && item.projectId === event.projectId && item.threadId === event.threadId;
16903
17722
  let anchorMessageId;
16904
17723
  if (matches && task.status === event.status && task.updatedAt.toISOString() === event.updatedAt && (event.threadId === void 0 || task.context?.thread_id === event.threadId)) {
@@ -16956,7 +17775,11 @@ var ProjectTaskObserver = class {
16956
17775
  return result;
16957
17776
  }
16958
17777
  result.taskTruncated || (result.taskTruncated = pass.truncated);
16959
- if (pass.truncated) this.deps.log.warn("Project task startup scan truncated", { tenantId: tenant2.id, status });
17778
+ if (pass.truncated) this.deps.log.warn("Project task startup scan truncated", {
17779
+ code: "PROJECT_TASK_STARTUP_SCAN_TRUNCATED",
17780
+ tenantId: tenant2.id,
17781
+ status
17782
+ });
16960
17783
  for (const scope of pass.scopes) discovered.set(scopeKey(scope), scope);
16961
17784
  for (const id3 of pass.ids) {
16962
17785
  if (signal?.aborted || this.disposed) {
@@ -16978,16 +17801,44 @@ var ProjectTaskObserver = class {
16978
17801
  return result;
16979
17802
  }
16980
17803
  await this.repairProjects(selected, discovered, result, signal);
17804
+ if (result.taskTruncated || result.projectEnumerationTruncated || result.projectionTruncated) {
17805
+ this.deps.log.warn("Project task startup reconciliation truncated", {
17806
+ code: "PROJECT_TASK_STARTUP_RECONCILIATION_TRUNCATED",
17807
+ taskTruncated: result.taskTruncated,
17808
+ projectEnumerationTruncated: result.projectEnumerationTruncated,
17809
+ projectionTruncated: result.projectionTruncated
17810
+ });
17811
+ }
16981
17812
  return result;
16982
17813
  }
16983
- /** Reconciles one persisted Agent-owned Project Task according to its current lifecycle status. */
16984
- async reconcileTask(task, signal) {
17814
+ /** Reconciles one persisted Agent-owned Project Task under an explicit intent. */
17815
+ async reconcileTask(task, signal, intent = "converge") {
16985
17816
  signal ?? (signal = this.deps.signal);
16986
17817
  if (signal?.aborted || this.disposed) return;
16987
- if (!isProjectLifecycleTask4(task) || task.ownerType !== "agent") return;
17818
+ if (!isProjectLifecycleTask(task) || task.ownerType !== "agent") return;
16988
17819
  if (await this.belongsToLearningRound(task)) return;
16989
17820
  if (task.status === "pending") {
16990
- if (await this.isReady(task) && !signal?.aborted && !this.disposed) await this.runStart(task, signal);
17821
+ if (signal?.aborted || this.disposed) return;
17822
+ const dependencies = await this.dependencyState(task);
17823
+ if (signal?.aborted || this.disposed) return;
17824
+ if (dependencies === "unmet") {
17825
+ await this.convergeDependencyUnmet(task);
17826
+ return;
17827
+ }
17828
+ if (dependencies === "wait" && await this.hasDependencyCycle(task)) {
17829
+ await this.convergeDependencyCycle(task);
17830
+ return;
17831
+ }
17832
+ if (dependencies !== "ready") return;
17833
+ try {
17834
+ await this.requireActiveOwner(task);
17835
+ } catch (error) {
17836
+ await this.convergeInterrupted(task, `No active executor: ${errorMessage3(error)}`);
17837
+ return;
17838
+ }
17839
+ if (intent === "dispatch" && await this.isReady(task) && !signal?.aborted && !this.disposed) {
17840
+ await this.runStart(task, signal);
17841
+ }
16991
17842
  return;
16992
17843
  }
16993
17844
  if (task.status === "in_progress") {
@@ -17099,20 +17950,13 @@ var ProjectTaskObserver = class {
17099
17950
  const classification = await this.deps.execution.classifyRecovery(current, ensured.thread);
17100
17951
  if (signal?.aborted || this.disposed) return;
17101
17952
  if (classification.kind === "invalid") {
17102
- await this.safeWarning(current, "queue", classification.reason);
17953
+ await this.convergeInterrupted(
17954
+ current,
17955
+ `Task execution state is not recoverable: ${classification.reason}`
17956
+ );
17103
17957
  return;
17104
17958
  }
17105
17959
  if (classification.kind === "missing") {
17106
- const exact2 = snapshotTaskItem(await this.deps.tasks.getById(current.tenantId, current.id));
17107
- if (!exact2 || exact2.status !== "in_progress" || exact2.context?.thread_id !== ensured.thread.id) return;
17108
- const failed = await this.deps.lifecycle.failTask({
17109
- tenantId: current.tenantId,
17110
- taskId: current.id,
17111
- actor: SYSTEM_ACTOR2,
17112
- threadId: ensured.thread.id,
17113
- failureReason: "Task execution queue state was not recoverable"
17114
- });
17115
- await this.checkLifecycle(current, "queue-fail", failed);
17116
17960
  return;
17117
17961
  }
17118
17962
  const recovery = {
@@ -17185,46 +18029,45 @@ var ProjectTaskObserver = class {
17185
18029
  if (!current || current.ownerId !== outcome.thread.assistantId || current.context?.thread_id !== outcome.thread.id) return;
17186
18030
  if (isTerminal(current.status) || current.status === "review") return;
17187
18031
  if (outcome.kind === "completed") {
17188
- const result2 = await this.deps.lifecycle.failTask({
18032
+ const result2 = await this.runBridge.settleRun({
17189
18033
  tenantId: current.tenantId,
17190
- taskId: current.id,
18034
+ task: current,
17191
18035
  actor: SYSTEM_ACTOR2,
17192
18036
  threadId: outcome.thread.id,
17193
- failureReason: "Agent execution ended without a task lifecycle transition"
18037
+ outcome: { kind: "completed" }
17194
18038
  });
17195
18039
  await this.checkLifecycle(current, "outcome-completed", result2);
17196
18040
  return;
17197
18041
  }
17198
18042
  if (outcome.kind === "cancelled") {
17199
- const result2 = await this.deps.lifecycle.cancelTask({
18043
+ const result2 = await this.runBridge.settleRun({
17200
18044
  tenantId: current.tenantId,
17201
- taskId: current.id,
18045
+ task: current,
17202
18046
  actor: SYSTEM_ACTOR2,
17203
18047
  threadId: outcome.thread.id,
17204
- summary: "Task execution cancelled"
18048
+ outcome: { kind: "cancelled" }
17205
18049
  });
17206
18050
  await this.checkLifecycle(current, "outcome-cancelled", result2);
17207
18051
  return;
17208
18052
  }
17209
18053
  if (outcome.kind === "failed" && outcome.lifecycleHandled) return;
17210
18054
  if (outcome.kind === "interrupted") {
17211
- const result2 = await this.deps.lifecycle.interruptTask({
18055
+ const result2 = await this.runBridge.settleRun({
17212
18056
  tenantId: current.tenantId,
17213
- taskId: current.id,
18057
+ task: current,
17214
18058
  actor: SYSTEM_ACTOR2,
17215
18059
  threadId: outcome.thread.id,
17216
- type: "missing_input",
17217
- summary: "Task execution interrupted"
18060
+ outcome: { kind: "interrupted" }
17218
18061
  });
17219
18062
  await this.checkLifecycle(current, "outcome-interrupted", result2);
17220
18063
  return;
17221
18064
  }
17222
- const result = await this.deps.lifecycle.failTask({
18065
+ const result = await this.runBridge.settleRun({
17223
18066
  tenantId: current.tenantId,
17224
- taskId: current.id,
18067
+ task: current,
17225
18068
  actor: SYSTEM_ACTOR2,
17226
18069
  threadId: outcome.thread.id,
17227
- failureReason: outcome.error
18070
+ outcome: { kind: "failed", lifecycleHandled: false, error: outcome.error }
17228
18071
  });
17229
18072
  await this.checkLifecycle(current, `outcome-${outcome.cause}`, result);
17230
18073
  }
@@ -17271,7 +18114,7 @@ var ProjectTaskObserver = class {
17271
18114
  });
17272
18115
  for (const value of batch) {
17273
18116
  if (!rows.has(value.id)) rows.set(value.id, { id: value.id, createdAt: requireDate(value.createdAt) });
17274
- if (isProjectLifecycleTask4(value)) {
18117
+ if (isProjectLifecycleTask(value)) {
17275
18118
  const scope = { tenantId: value.tenantId, workspaceId: value.workspaceId, projectId: value.projectId };
17276
18119
  scopes.set(scopeKey(scope), scope);
17277
18120
  }
@@ -17388,7 +18231,7 @@ var ProjectTaskObserver = class {
17388
18231
  action: "activity",
17389
18232
  summary: "Project task recovery warning",
17390
18233
  detail: { type: "project_task_recovery_warning", phase, message },
17391
- eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash5("sha256").update(message).digest("hex")}`
18234
+ eventKey: `project-task-recovery-warning:${task.id}:${phase}:${createHash6("sha256").update(message).digest("hex")}`
17392
18235
  });
17393
18236
  }
17394
18237
  async safeWarning(task, phase, error) {
@@ -17402,6 +18245,134 @@ var ProjectTaskObserver = class {
17402
18245
  });
17403
18246
  }
17404
18247
  }
18248
+ /**
18249
+ * Converges an unexecutable task to a durable interruption.
18250
+ *
18251
+ * @param task Exact task snapshot that cannot make progress through automation.
18252
+ * @param summary Human-readable reason recorded with the interruption.
18253
+ * @remarks Never leaves a task silently pending behind a warning; an `in_progress`
18254
+ * task with no dispatched queue rows is intentionally left untouched by recovery.
18255
+ * The interruption is visible and resumable once an executor is available.
18256
+ */
18257
+ async convergeInterrupted(task, summary) {
18258
+ if (this.disposed) return;
18259
+ try {
18260
+ const result = await this.runBridge.settleRun({
18261
+ tenantId: task.tenantId,
18262
+ task,
18263
+ actor: SYSTEM_ACTOR2,
18264
+ outcome: { kind: "interrupted", summary }
18265
+ });
18266
+ if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
18267
+ await this.safeWarning(task, "converge", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? summary}`);
18268
+ }
18269
+ } catch (error) {
18270
+ await this.safeWarning(task, "converge", error);
18271
+ }
18272
+ }
18273
+ /**
18274
+ * Classifies a task's dependencies as ready, still waiting, or permanently unmet.
18275
+ *
18276
+ * @param task Exact task snapshot whose dependencies are inspected.
18277
+ * @returns `ready` when every dependency is completed, `unmet` when a
18278
+ * dependency is terminal but not completed (failed/cancelled/missing),
18279
+ * otherwise `wait`.
18280
+ */
18281
+ async dependencyState(task) {
18282
+ for (const dependencyId of task.dependencies ?? []) {
18283
+ let dependency;
18284
+ try {
18285
+ dependency = snapshotTaskItem(await this.deps.tasks.getById(task.tenantId, dependencyId));
18286
+ } catch {
18287
+ return "wait";
18288
+ }
18289
+ if (!dependency || dependency.id !== dependencyId || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId) return "unmet";
18290
+ if (dependency.status === "completed") continue;
18291
+ if (dependency.status === "failed" || dependency.status === "cancelled") return "unmet";
18292
+ return "wait";
18293
+ }
18294
+ return "ready";
18295
+ }
18296
+ /**
18297
+ * Converges a task whose dependencies can never be met to a durable failure.
18298
+ *
18299
+ * @param task Exact task snapshot with terminal non-completed dependencies.
18300
+ */
18301
+ async convergeDependencyUnmet(task) {
18302
+ if (this.disposed) return;
18303
+ try {
18304
+ const result = await this.runBridge.settleRun({
18305
+ tenantId: task.tenantId,
18306
+ task,
18307
+ actor: SYSTEM_ACTOR2,
18308
+ outcome: {
18309
+ kind: "failed",
18310
+ lifecycleHandled: false,
18311
+ error: `Dependency unmet: ${(task.dependencies ?? []).join(", ")}`
18312
+ }
18313
+ });
18314
+ if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
18315
+ await this.safeWarning(task, "dependency", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "dependency unmet"}`);
18316
+ }
18317
+ } catch (error) {
18318
+ await this.safeWarning(task, "dependency", error);
18319
+ }
18320
+ }
18321
+ /**
18322
+ * Detects whether a still-waiting task sits on a dependency cycle.
18323
+ *
18324
+ * @param task Exact pending task snapshot.
18325
+ * @returns True when following `dependencies` edges reaches the task again.
18326
+ * @remarks Bounded; unrelated cycles are ignored by the visited set.
18327
+ */
18328
+ async hasDependencyCycle(task) {
18329
+ const MAX_READS = 1e3;
18330
+ const visited = /* @__PURE__ */ new Set();
18331
+ const stack = [...task.dependencies ?? []];
18332
+ let reads = 0;
18333
+ while (stack.length > 0) {
18334
+ const currentId = stack.pop();
18335
+ if (currentId === task.id) return true;
18336
+ if (visited.has(currentId)) continue;
18337
+ if (reads >= MAX_READS) return false;
18338
+ visited.add(currentId);
18339
+ reads += 1;
18340
+ let dependency;
18341
+ try {
18342
+ dependency = snapshotTaskItem(await this.deps.tasks.getById(task.tenantId, currentId));
18343
+ } catch {
18344
+ return false;
18345
+ }
18346
+ if (!dependency) continue;
18347
+ for (const next of dependency.dependencies ?? []) stack.push(next);
18348
+ }
18349
+ return false;
18350
+ }
18351
+ /**
18352
+ * Converges a task stuck on a dependency cycle to a durable failure.
18353
+ *
18354
+ * @param task Exact pending task snapshot on the cycle.
18355
+ */
18356
+ async convergeDependencyCycle(task) {
18357
+ if (this.disposed) return;
18358
+ try {
18359
+ const result = await this.runBridge.settleRun({
18360
+ tenantId: task.tenantId,
18361
+ task,
18362
+ actor: SYSTEM_ACTOR2,
18363
+ outcome: {
18364
+ kind: "failed",
18365
+ lifecycleHandled: false,
18366
+ error: `Dependency cycle: ${(task.dependencies ?? []).join(", ")}`
18367
+ }
18368
+ });
18369
+ if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
18370
+ await this.safeWarning(task, "dependency-cycle", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "dependency cycle"}`);
18371
+ }
18372
+ } catch (error) {
18373
+ await this.safeWarning(task, "dependency-cycle", error);
18374
+ }
18375
+ }
17405
18376
  async checkLifecycle(task, phase, result) {
17406
18377
  if (result.success) return;
17407
18378
  await this.safeWarning(task, phase, `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? "Lifecycle rejected"}`);
@@ -17446,16 +18417,25 @@ var ProjectTaskObserver = class {
17446
18417
  async runOverflowReconciliations() {
17447
18418
  while (!this.disposed && this.overflowScopes.size > 0) {
17448
18419
  const entries = [...this.overflowScopes.entries()];
17449
- for (const [, { event }] of entries) await this.persistOverflowWarning(event);
18420
+ const applicable = [];
18421
+ for (const [key, scope] of entries) {
18422
+ const task = snapshotTaskItem(await this.deps.tasks.getById(scope.event.tenantId, scope.event.taskId));
18423
+ if (!task || !isProjectLifecycleTask(task) || task.workspaceId !== scope.event.workspaceId || task.projectId !== scope.event.projectId) {
18424
+ if (this.overflowScopes.get(key)?.generation === scope.generation) this.overflowScopes.delete(key);
18425
+ continue;
18426
+ }
18427
+ applicable.push([key, scope]);
18428
+ }
18429
+ for (const [, { event }] of applicable) await this.persistOverflowWarning(event);
17450
18430
  while (this.notificationTails.size > 0) await Promise.allSettled([...this.notificationTails.values()]);
17451
- for (const [key, { event, generation }] of entries) {
18431
+ for (const [key, { event, generation }] of applicable) {
17452
18432
  await this.reconcileOverflowScope(event);
17453
18433
  if (this.overflowScopes.get(key)?.generation === generation) this.overflowScopes.delete(key);
17454
18434
  }
17455
18435
  }
17456
18436
  }
17457
18437
  async persistOverflowWarning(event) {
17458
- const scopeDigest = createHash5("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
18438
+ const scopeDigest = createHash6("sha256").update(JSON.stringify([event.tenantId, event.workspaceId, event.projectId])).digest("hex");
17459
18439
  try {
17460
18440
  await this.deps.workItems.createIfAbsentByEventKey({
17461
18441
  tenantId: event.tenantId,
@@ -17514,7 +18494,7 @@ var ProjectTaskObserver = class {
17514
18494
  }
17515
18495
  };
17516
18496
  function machineScope(task) {
17517
- if (!task.projectId || task.projectId === "default" || task.ownerType !== "agent") {
18497
+ if (!task.projectId || task.ownerType !== "agent") {
17518
18498
  throw new Error("Invalid Project task machine scope");
17519
18499
  }
17520
18500
  return { tenantId: task.tenantId, assistantId: task.ownerId, projectId: task.projectId };
@@ -17564,8 +18544,7 @@ function combineSignals(left, right) {
17564
18544
  }
17565
18545
 
17566
18546
  // src/services/collaboration/RoomProjectionService.ts
17567
- import { createHash as createHash6 } from "crypto";
17568
- import { isProjectLifecycleTask as isProjectLifecycleTask5 } from "@axiom-lattice/core";
18547
+ import { createHash as createHash7 } from "crypto";
17569
18548
  import {
17570
18549
  PROJECT_ROOM_TASK_DELEGATED_LABEL,
17571
18550
  PROJECT_ROOM_TASK_DELEGATED_SEPARATOR,
@@ -17715,7 +18694,7 @@ var RoomProjectionService = class {
17715
18694
  await this.warning("INVALID_PROJECTABLE_EVENT", { taskId: row.taskId, eventKey: row.eventKey }, input);
17716
18695
  continue;
17717
18696
  }
17718
- if (task.ownerType !== "agent" || !isProjectLifecycleTask5(task)) {
18697
+ if (task.ownerType !== "agent" || !isProjectLifecycleTask(task)) {
17719
18698
  result.skipped += 1;
17720
18699
  continue;
17721
18700
  }
@@ -17760,7 +18739,7 @@ var RoomProjectionService = class {
17760
18739
  };
17761
18740
  function snapshotProjectTask(value) {
17762
18741
  const task = snapshotTaskItem(value);
17763
- return task && task.ownerType === "agent" && isProjectLifecycleTask5(task) ? task : void 0;
18742
+ return task && task.ownerType === "agent" && isProjectLifecycleTask(task) ? task : void 0;
17764
18743
  }
17765
18744
  function ownEnumerableString(value, key) {
17766
18745
  try {
@@ -17786,7 +18765,7 @@ function safeRepairIdentity(value, scope) {
17786
18765
  return taskId && eventKey && tenantId === scope.tenantId && workspaceId === scope.workspaceId && projectId === scope.projectId ? { taskId, eventKey } : void 0;
17787
18766
  }
17788
18767
  function tupleKey(prefix, values) {
17789
- const digest = createHash6("sha256").update(JSON.stringify(values)).digest("hex");
18768
+ const digest = createHash7("sha256").update(JSON.stringify(values)).digest("hex");
17790
18769
  return `${prefix}:${digest}`;
17791
18770
  }
17792
18771
  function parseProjectionInput(taskValue, itemValue) {
@@ -17956,7 +18935,7 @@ function isRecord3(value) {
17956
18935
  import { taskLifecycleNotifier } from "@axiom-lattice/core";
17957
18936
 
17958
18937
  // src/services/collaboration/ProjectRoomEventBroker.ts
17959
- import { randomUUID as randomUUID21 } from "crypto";
18938
+ import { randomUUID as randomUUID22 } from "crypto";
17960
18939
  import {
17961
18940
  parseProjectRoomEventId,
17962
18941
  ProjectRoomBrokerCapacityError,
@@ -17985,7 +18964,7 @@ var ProjectRoomEventBroker = class {
17985
18964
  this.subscriberCount = 0;
17986
18965
  this.usageClock = 0;
17987
18966
  this.closed = false;
17988
- this.epoch = options.epoch ?? randomUUID21();
18967
+ this.epoch = options.epoch ?? randomUUID22();
17989
18968
  if (!UUID_PATTERN.test(this.epoch)) {
17990
18969
  throw new TypeError("Project Room event epoch must be a lowercase canonical UUID");
17991
18970
  }
@@ -18303,7 +19282,7 @@ function snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
18303
19282
  }
18304
19283
 
18305
19284
  // src/services/collaboration/ProjectRoomRealtimePublisher.ts
18306
- import { createHash as createHash7 } from "crypto";
19285
+ import { createHash as createHash8 } from "crypto";
18307
19286
  import {
18308
19287
  projectRoomUserEventScope,
18309
19288
  snapshotExactRecord as snapshotExactRecord11,
@@ -18425,7 +19404,7 @@ var BrokerProjectRoomRealtimePublisher = class {
18425
19404
  try {
18426
19405
  event = build();
18427
19406
  if (!event) throw new Error("Invalid Project Room realtime projection");
18428
- const dedupeKey = createHash7("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
19407
+ const dedupeKey = createHash8("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
18429
19408
  if (this.sourceKeys.has(dedupeKey)) return;
18430
19409
  this.deps.broker.publish(event.scope, event.draft);
18431
19410
  this.sourceKeys.set(dedupeKey, true);
@@ -18479,7 +19458,6 @@ function safeErrorCode(error) {
18479
19458
  }
18480
19459
 
18481
19460
  // src/services/collaboration/ProjectRoomTaskEventSubscriber.ts
18482
- import { isProjectLifecycleTask as isProjectLifecycleTask6 } from "@axiom-lattice/core";
18483
19461
  import { snapshotExactRecord as snapshotExactRecord12 } from "@axiom-lattice/protocols";
18484
19462
  var ProjectRoomTaskEventSubscriber = class {
18485
19463
  /** Creates a bounded Project task lifecycle observer. */
@@ -18562,8 +19540,8 @@ var ProjectRoomTaskEventSubscriber = class {
18562
19540
  try {
18563
19541
  const taskValue = await this.deps.tasks.getById(event.tenantId, event.taskId);
18564
19542
  const task = taskValue === null ? void 0 : snapshotTaskItem(taskValue);
18565
- if (this.closed) return;
18566
- if (!task || !isProjectLifecycleTask6(task) || 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) {
19543
+ if (this.closed || !task || !isProjectLifecycleTask(task)) return;
19544
+ if (task.tenantId !== event.tenantId || task.workspaceId !== event.workspaceId || task.projectId !== event.projectId || task.status !== event.status || task.updatedAt.toISOString() !== event.updatedAt || event.threadId !== void 0 && task.context?.thread_id !== event.threadId) {
18567
19545
  this.warn("project-room:task-event-stale", { taskId: safeId3(event.taskId), eventKey: safeId3(event.eventKey) });
18568
19546
  return;
18569
19547
  }
@@ -19372,20 +20350,163 @@ async function validateReassignmentThread(threads, input) {
19372
20350
  throw new ProjectTaskThreadError("PROJECT_TASK_THREAD_METADATA_INVALID", "Invalid reassignment Task Thread metadata");
19373
20351
  }
19374
20352
  }
20353
+ function hookErrorMessage(error) {
20354
+ if (error instanceof Error && error.message) return error.message;
20355
+ return "Managed dispatch transfer failed.";
20356
+ }
20357
+ function wrapReassignmentThreadError(error) {
20358
+ if (error instanceof ProjectTaskThreadError) {
20359
+ if (error.code === "PROJECT_TASK_THREAD_SCOPE_INVALID") {
20360
+ return new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
20361
+ hint: "Project task, room, or roster scope changed; re-read the task and retry."
20362
+ });
20363
+ }
20364
+ if (error.code === "PROJECT_TASK_THREAD_OWNER_INVALID") {
20365
+ return new ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
20366
+ hint: "The target already owns this task; re-read before dispatching."
20367
+ });
20368
+ }
20369
+ return new ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
20370
+ hint: "Task Thread changed concurrently; re-read the task and retry."
20371
+ });
20372
+ }
20373
+ return new ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
20374
+ hint: "Re-read the task and retry the dispatch."
20375
+ });
20376
+ }
20377
+ function wrapReassignmentAuthorizationError(error) {
20378
+ if (error instanceof ProjectTaskOwnerPolicyError2) {
20379
+ if (error.code === "PROJECT_TASK_CALLER_INACTIVE") {
20380
+ return new ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
20381
+ hint: "Dispatch from an active Project Room member bot."
20382
+ });
20383
+ }
20384
+ return new ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
20385
+ hint: "Dispatch to an active bot in this Project's roster."
20386
+ });
20387
+ }
20388
+ return new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
20389
+ hint: "Re-read the task and roster, then retry the dispatch."
20390
+ });
20391
+ }
20392
+ function reassignmentFailureHint(code) {
20393
+ if (code === "TASK_THREAD_CONFLICT") {
20394
+ return "Task Thread changed concurrently; re-read the task and retry.";
20395
+ }
20396
+ if (code === "TASK_REASSIGN_NO_CHANGE") {
20397
+ return "The target already owns this task; re-read before dispatching.";
20398
+ }
20399
+ if (code === "TASK_STATUS_CONFLICT") {
20400
+ return "Task changed concurrently; re-read the task and retry.";
20401
+ }
20402
+ if (code === "TASK_TERMINAL") {
20403
+ return "Terminal tasks are immutable; create a new task for this work.";
20404
+ }
20405
+ if (code === "REVIEW_APPROVAL_REQUIRED") {
20406
+ return "Resolve the review decision (approve/reject) before dispatching.";
20407
+ }
20408
+ if (code === "TASK_REASSIGN_INTERRUPT_REQUIRED") {
20409
+ return "Resume or cancel the running task before dispatching.";
20410
+ }
20411
+ return "Re-read the task and retry the dispatch.";
20412
+ }
20413
+ async function transferGovernedDispatchOwner(task, options, deps) {
20414
+ const agentId = options?.agentId;
20415
+ if (!agentId || agentId === task.ownerId) return void 0;
20416
+ if (!task.workspaceId || !task.projectId) {
20417
+ throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is not governed by a Project lifecycle.", {
20418
+ hint: "Dispatch governed tasks from a trusted Project run; ordinary tasks execute immediately."
20419
+ });
20420
+ }
20421
+ return deps.mutex.runExclusive({ tenantId: task.tenantId, projectId: task.projectId }, async () => {
20422
+ const current = await deps.tasks.getById(task.tenantId, task.id);
20423
+ if (!current || !isProjectLifecycleTask(current) || current.workspaceId !== task.workspaceId || current.projectId !== task.projectId) {
20424
+ throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is no longer a Project lifecycle task.", {
20425
+ hint: "Re-read the task and confirm it still belongs to this Project before dispatching."
20426
+ });
20427
+ }
20428
+ if (current.ownerType !== "agent") {
20429
+ throw new ManagedTaskStartError("AGENT_TASK_REQUIRED", "Project task owner must be an Agent.", {
20430
+ hint: "Assign the task to an Agent before dispatching."
20431
+ });
20432
+ }
20433
+ if (current.status !== "pending") {
20434
+ throw new ManagedTaskStartError("TASK_NOT_DISPATCHABLE", "Only a `pending` Project task can be dispatched.", {
20435
+ hint: `This task is '${current.status}'. Resolve it or create a new task before dispatching.`
20436
+ });
20437
+ }
20438
+ if (agentId === current.ownerId) return current;
20439
+ const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
20440
+ if (!room || room.tenantId !== current.tenantId || room.workspaceId !== current.workspaceId || room.projectId !== current.projectId || room.type !== "main") {
20441
+ throw new ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Project main room is unavailable.", {
20442
+ hint: "Open or repair the Project Room before dispatching."
20443
+ });
20444
+ }
20445
+ const target = await deps.botMemberships.findByAssistant(current.tenantId, current.projectId, agentId);
20446
+ if (!target || target.status !== "active" || target.tenantId !== current.tenantId || target.workspaceId !== current.workspaceId || target.projectId !== current.projectId || target.assistantId !== agentId || target.roomId !== room.id) {
20447
+ throw new ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", "Target Project task owner is not active in the exact roster.", {
20448
+ hint: "Dispatch to an active bot in this Project's roster."
20449
+ });
20450
+ }
20451
+ const caller = options?.callerMembershipId ? await deps.botMemberships.findById(current.tenantId, options.callerMembershipId) : null;
20452
+ if (!caller || !options?.callerAssistantId || caller.id !== options.callerMembershipId || caller.assistantId !== options.callerAssistantId || caller.status !== "active" || caller.tenantId !== current.tenantId || caller.workspaceId !== current.workspaceId || caller.projectId !== current.projectId || caller.roomId !== room.id) {
20453
+ throw new ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", "Current Project task caller is not active in the exact roster.", {
20454
+ hint: "Dispatch from an active Project Room member bot."
20455
+ });
20456
+ }
20457
+ let ensured;
20458
+ try {
20459
+ ensured = await deps.threads.ensure({ task: current, room, membership: target, mode: "reassignment" });
20460
+ } catch (error) {
20461
+ throw wrapReassignmentThreadError(error);
20462
+ }
20463
+ let authorization;
20464
+ try {
20465
+ authorization = await deps.reassignmentAuthorizer.authorize({
20466
+ tenantId: current.tenantId,
20467
+ workspaceId: current.workspaceId,
20468
+ projectId: current.projectId,
20469
+ roomId: room.id,
20470
+ taskId: current.id,
20471
+ callerMembershipId: caller.id,
20472
+ callerAssistantId: caller.assistantId,
20473
+ targetMembershipId: target.id,
20474
+ targetAssistantId: target.assistantId,
20475
+ ...typeof current.context?.thread_id === "string" ? { expectedThreadId: current.context.thread_id } : {},
20476
+ newThreadId: ensured.thread.id
20477
+ });
20478
+ } catch (error) {
20479
+ throw wrapReassignmentAuthorizationError(error);
20480
+ }
20481
+ const result = await deps.lifecycle.reassignProjectTask({
20482
+ tenantId: current.tenantId,
20483
+ taskId: current.id,
20484
+ authorization,
20485
+ actor: `agent:${caller.assistantId}`
20486
+ });
20487
+ if (!result.success) {
20488
+ throw new ManagedTaskStartError(result.code, result.error, {
20489
+ hint: result.hint ?? reassignmentFailureHint(result.code),
20490
+ ...result.retryable !== void 0 ? { retryable: result.retryable } : {}
20491
+ });
20492
+ }
20493
+ return result.task;
20494
+ });
20495
+ }
19375
20496
  var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19376
- const webAppStore = getStoreLattice16("default", "agentWebApp").store;
19377
- const assistantStore = getStoreLattice16("default", "assistant").store;
19378
- const projectStore = getStoreLattice16("default", "project").store;
19379
- const projectMembershipStore = getStoreLattice16("default", "projectMembership").store;
19380
- const userTenantLinkStore = getStoreLattice16("default", "userTenantLink").store;
19381
- const projectRoomStore = getStoreLattice16("default", "projectRoom").store;
19382
- const projectBotMembershipStore = getStoreLattice16("default", "projectBotMembership").store;
19383
- const projectRoomMessageStore = getStoreLattice16("default", "projectRoomMessage").store;
19384
- const projectRoomReadStateStore = getStoreLattice16("default", "projectRoomReadState").store;
19385
- const userPushSubscriptionStore = getStoreLattice16("default", "userPushSubscription").store;
19386
- const threadStore = getStoreLattice16("default", "thread").store;
19387
- const taskStore = getStoreLattice16("default", "task").store;
19388
- const taskWorkItemStore = getStoreLattice16("default", "taskWorkItem").store;
20497
+ const webAppStore = getStoreLattice17("default", "agentWebApp").store;
20498
+ const assistantStore = getStoreLattice17("default", "assistant").store;
20499
+ const projectStore = getStoreLattice17("default", "project").store;
20500
+ const projectMembershipStore = getStoreLattice17("default", "projectMembership").store;
20501
+ const userTenantLinkStore = getStoreLattice17("default", "userTenantLink").store;
20502
+ const projectRoomStore = getStoreLattice17("default", "projectRoom").store;
20503
+ const projectBotMembershipStore = getStoreLattice17("default", "projectBotMembership").store;
20504
+ const projectRoomMessageStore = getStoreLattice17("default", "projectRoomMessage").store;
20505
+ const projectRoomReadStateStore = getStoreLattice17("default", "projectRoomReadState").store;
20506
+ const userPushSubscriptionStore = getStoreLattice17("default", "userPushSubscription").store;
20507
+ const threadStore = getStoreLattice17("default", "thread").store;
20508
+ const taskStore = getStoreLattice17("default", "task").store;
20509
+ const taskWorkItemStore = getStoreLattice17("default", "taskWorkItem").store;
19389
20510
  const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
19390
20511
  const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
19391
20512
  const realtimeMode = resolveProjectRoomRealtimeMode();
@@ -19451,7 +20572,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19451
20572
  updateAssistant: assistantStore.updateAssistant.bind(assistantStore)
19452
20573
  });
19453
20574
  const resolveCoordinatorMentionName = async (tenantId) => {
19454
- const tenant2 = await getStoreLattice16("default", "tenant").store.getTenantById(tenantId);
20575
+ const tenant2 = await getStoreLattice17("default", "tenant").store.getTenantById(tenantId);
19455
20576
  const value = tenant2?.metadata?.coordinatorMentionName;
19456
20577
  return typeof value === "string" && value.length > 0 ? value : void 0;
19457
20578
  };
@@ -19542,7 +20663,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19542
20663
  });
19543
20664
  }
19544
20665
  if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
19545
- const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: randomUUID22 });
20666
+ const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: randomUUID23 });
19546
20667
  const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? createTaskLifecycleService({ taskStore, workItemStore: taskWorkItemStore });
19547
20668
  const reassignmentAuthorizer = createProjectTaskReassignmentAuthorizer(
19548
20669
  new ProjectTaskOwnerPolicy(projectBotMembershipStore),
@@ -19578,7 +20699,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19578
20699
  readStateService,
19579
20700
  interruptService: roomInterruptService,
19580
20701
  userDirectory: {
19581
- getUserById: (id3) => getStoreLattice16("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
20702
+ getUserById: (id3) => getStoreLattice17("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
19582
20703
  }
19583
20704
  });
19584
20705
  registerProjectRosterRoutes(app2, { service: rosterService });
@@ -19589,7 +20710,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19589
20710
  createProject: projectStore.createProject.bind(projectStore)
19590
20711
  },
19591
20712
  workspaceStore: {
19592
- getWorkspaceById: (tenantId, id3) => getStoreLattice16("default", "workspace").store.getWorkspaceById(tenantId, id3)
20713
+ getWorkspaceById: (tenantId, id3) => getStoreLattice17("default", "workspace").store.getWorkspaceById(tenantId, id3)
19593
20714
  },
19594
20715
  membershipStore: {
19595
20716
  list: projectMembershipStore.list.bind(projectMembershipStore),
@@ -19612,7 +20733,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19612
20733
  getUsersByTenant: userTenantLinkStore.getUsersByTenant.bind(userTenantLinkStore)
19613
20734
  },
19614
20735
  userStore: {
19615
- getUserById: (id3) => getStoreLattice16("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
20736
+ getUserById: (id3) => getStoreLattice17("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
19616
20737
  },
19617
20738
  assistantStore: {
19618
20739
  getAssistantById: assistantStore.getAssistantById.bind(assistantStore),
@@ -19634,7 +20755,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19634
20755
  }
19635
20756
  });
19636
20757
  registerCapabilityBundleRoutes(app2, new CapabilityBundleController({
19637
- bundleStore: getStoreLattice16("default", "capabilityBundle").store,
20758
+ bundleStore: getStoreLattice17("default", "capabilityBundle").store,
19638
20759
  projectStore
19639
20760
  }, { authRequired: process.env.AUTH_REQUIRED === "true" }));
19640
20761
  registerAgentWebAppRoutes(app2, {
@@ -19682,19 +20803,11 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19682
20803
  },
19683
20804
  tasks: taskStore,
19684
20805
  workItems: taskWorkItemStore,
19685
- bots: projectBotMembershipStore,
19686
- rooms: projectRoomStore,
19687
- threadService: taskThreadService,
19688
- reassignmentAuthorizer,
19689
- lifecycle: taskLifecycle,
19690
- projectMutationMutex: projectMutationMutex2,
19691
- executionMutex: projectTaskComposition?.mutex ?? {
19692
- runExclusive: (_scope, operation) => operation()
19693
- }
20806
+ projectMutationMutex: projectMutationMutex2
19694
20807
  }),
19695
20808
  service: new AgentWebAppRuntimeService({
19696
20809
  webAppStore,
19697
- threadStore: getStoreLattice16("default", "thread").store,
20810
+ threadStore: getStoreLattice17("default", "thread").store,
19698
20811
  projectStore,
19699
20812
  assistantResolver: {
19700
20813
  resolve: async (tenantId, assistantId) => {
@@ -19795,19 +20908,45 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19795
20908
  app2.post("/api/personal-assistant", createPersonalAssistant);
19796
20909
  app2.get("/api/personal-assistant", getPersonalAssistant);
19797
20910
  app2.delete("/api/personal-assistant", deletePersonalAssistant);
20911
+ executionAuthorityRegistry.register({
20912
+ id: "project-lifecycle",
20913
+ matches: isProjectLifecycleTask,
20914
+ code: "PROJECT_TASK_EXECUTION_MANAGED",
20915
+ message: "Project tasks are executed by the durable Project queue runtime",
20916
+ onManagedStart: async (task, options2) => {
20917
+ if (!projectTaskComposition && options2?.agentId && options2.agentId !== task.ownerId) {
20918
+ throw new ManagedTaskStartError(
20919
+ "PROJECT_TASK_RUNTIME_UNAVAILABLE",
20920
+ "Project task ownership transfer requires the Project task runtime.",
20921
+ { hint: "Project task execution is disabled; enable the Project task runtime or dispatch to the current owner." }
20922
+ );
20923
+ }
20924
+ const transferred = projectTaskComposition ? await transferGovernedDispatchOwner(task, options2, {
20925
+ tasks: taskStore,
20926
+ rooms: projectRoomStore,
20927
+ botMemberships: projectBotMembershipStore,
20928
+ threads: taskThreadService,
20929
+ reassignmentAuthorizer,
20930
+ lifecycle: taskLifecycle,
20931
+ mutex: projectMutationMutex2
20932
+ }) : void 0;
20933
+ const admitted = transferred ?? task;
20934
+ if (!admitted.workspaceId || !admitted.projectId) return;
20935
+ void projectTaskAdmissionNotifier.publish({
20936
+ tenantId: admitted.tenantId,
20937
+ workspaceId: admitted.workspaceId,
20938
+ projectId: admitted.projectId,
20939
+ taskId: admitted.id,
20940
+ ownerId: admitted.ownerId,
20941
+ updatedAt: admitted.updatedAt.toISOString()
20942
+ });
20943
+ }
20944
+ });
19798
20945
  registerTaskRoutes(app2, createTaskController({
19799
20946
  access: accessService,
19800
20947
  tasks: taskStore,
19801
20948
  workItems: taskWorkItemStore,
19802
- bots: projectBotMembershipStore,
19803
- rooms: projectRoomStore,
19804
- threadService: taskThreadService,
19805
- reassignmentAuthorizer,
19806
- lifecycle: taskLifecycle,
19807
20949
  projectMutationMutex: projectMutationMutex2,
19808
- executionMutex: projectTaskComposition?.mutex ?? {
19809
- runExclusive: (_scope, operation) => operation()
19810
- },
19811
20950
  readableProjects: async ({ tenantId, userId, workspaceId }) => {
19812
20951
  const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
19813
20952
  const readable = [];
@@ -19931,6 +21070,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
19931
21070
  executeDataQuery
19932
21071
  );
19933
21072
  registerMcpServerConfigRoutes(app2);
21073
+ registerMcpAppRoutes(app2);
19934
21074
  registerPushNotificationRoutes(app2, {
19935
21075
  subscriptionStore: userPushSubscriptionStore,
19936
21076
  vapidPublicKey: process.env.VAPID_PUBLIC_KEY || null
@@ -20087,9 +21227,9 @@ function tryCreateProjectTaskRuntime(input) {
20087
21227
  let stores;
20088
21228
  try {
20089
21229
  stores = {
20090
- tenants: getStoreLattice16("default", "tenant").store,
20091
- workspaces: getStoreLattice16("default", "workspace").store,
20092
- queue: getStoreLattice16("default", "threadMessageQueue").store
21230
+ tenants: getStoreLattice17("default", "tenant").store,
21231
+ workspaces: getStoreLattice17("default", "workspace").store,
21232
+ queue: getStoreLattice17("default", "threadMessageQueue").store
20093
21233
  };
20094
21234
  const missing = missingRuntimeMethod(input, stores);
20095
21235
  if (missing) return { reason: missing };
@@ -20120,7 +21260,7 @@ function tryCreateProjectTaskRuntime(input) {
20120
21260
  function createRuntime(input, stores, log2) {
20121
21261
  const controller = new AbortController();
20122
21262
  const mutex = new ProjectTaskExecutionMutex();
20123
- const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: randomUUID22 });
21263
+ const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: randomUUID23 });
20124
21264
  const lifecycle = createTaskLifecycleService({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
20125
21265
  const projection = new RoomProjectionService({
20126
21266
  messages: input.projectRoomMessageStore,
@@ -20129,7 +21269,7 @@ function createRuntime(input, stores, log2) {
20129
21269
  warnings: input.taskWorkItemStore,
20130
21270
  log: log2,
20131
21271
  realtime: input.realtime,
20132
- idFactory: randomUUID22
21272
+ idFactory: randomUUID23
20133
21273
  });
20134
21274
  const agentFor = (task, thread) => agentInstanceManager6.getAgent({
20135
21275
  tenant_id: task.tenantId,
@@ -20173,7 +21313,7 @@ function createRuntime(input, stores, log2) {
20173
21313
  void observer.listener(event);
20174
21314
  });
20175
21315
  const unsubscribe = taskLifecycleNotifier2.subscribe(taskLifecycleListener);
20176
- const unsubscribeAdmission = projectTaskAdmissionNotifier2.subscribe((event, signal) => {
21316
+ const unsubscribeAdmission = projectTaskAdmissionNotifier.subscribe((event, signal) => {
20177
21317
  observer.admit(event, signal);
20178
21318
  });
20179
21319
  let reconciliationWork;
@@ -20466,9 +21606,9 @@ var skillRegistration = {
20466
21606
  };
20467
21607
 
20468
21608
  // src/export_registrations/agent.registration.ts
20469
- import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
21609
+ import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
20470
21610
  function getStore2() {
20471
- return getStoreLattice17("default", "assistant").store;
21611
+ return getStoreLattice18("default", "assistant").store;
20472
21612
  }
20473
21613
  var agentRegistration = {
20474
21614
  entityType: "agent",
@@ -20548,9 +21688,9 @@ var agentRegistration = {
20548
21688
  };
20549
21689
 
20550
21690
  // src/export_registrations/database-config.registration.ts
20551
- import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
21691
+ import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
20552
21692
  function getStore3() {
20553
- return getStoreLattice18("default", "database").store;
21693
+ return getStoreLattice19("default", "database").store;
20554
21694
  }
20555
21695
  var databaseConfigRegistration = {
20556
21696
  entityType: "database_config",
@@ -20630,9 +21770,9 @@ var databaseConfigRegistration = {
20630
21770
  };
20631
21771
 
20632
21772
  // src/export_registrations/metrics-config.registration.ts
20633
- import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
21773
+ import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
20634
21774
  function getStore4() {
20635
- return getStoreLattice19("default", "metrics").store;
21775
+ return getStoreLattice20("default", "metrics").store;
20636
21776
  }
20637
21777
  var metricsConfigRegistration = {
20638
21778
  entityType: "metrics_config",
@@ -20712,9 +21852,9 @@ var metricsConfigRegistration = {
20712
21852
  };
20713
21853
 
20714
21854
  // src/export_registrations/mcp-config.registration.ts
20715
- import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
21855
+ import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
20716
21856
  function getStore5() {
20717
- return getStoreLattice20("default", "mcp").store;
21857
+ return getStoreLattice21("default", "mcp").store;
20718
21858
  }
20719
21859
  var mcpConfigRegistration = {
20720
21860
  entityType: "mcp_config",
@@ -20797,9 +21937,9 @@ var mcpConfigRegistration = {
20797
21937
  };
20798
21938
 
20799
21939
  // src/export_registrations/connection.registration.ts
20800
- import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
21940
+ import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
20801
21941
  function getStore6() {
20802
- return getStoreLattice21("default", "connection").store;
21942
+ return getStoreLattice22("default", "connection").store;
20803
21943
  }
20804
21944
  async function listAllConnections(tenantId) {
20805
21945
  const store = getStore6();
@@ -20881,9 +22021,9 @@ var connectionRegistration = {
20881
22021
  };
20882
22022
 
20883
22023
  // src/export_registrations/collection.registration.ts
20884
- import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
22024
+ import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
20885
22025
  function getStore7() {
20886
- return getStoreLattice22("default", "collection").store;
22026
+ return getStoreLattice23("default", "collection").store;
20887
22027
  }
20888
22028
  var collectionRegistration = {
20889
22029
  entityType: "collection",
@@ -20954,8 +22094,8 @@ var collectionRegistration = {
20954
22094
  };
20955
22095
 
20956
22096
  // src/export_registrations/channel-installation.registration.ts
20957
- import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
20958
- import { randomUUID as randomUUID23 } from "crypto";
22097
+ import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
22098
+ import { randomUUID as randomUUID24 } from "crypto";
20959
22099
  var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
20960
22100
  var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
20961
22101
  function isPublicChannel(channel) {
@@ -20988,7 +22128,7 @@ function validateImportData(data) {
20988
22128
  };
20989
22129
  }
20990
22130
  function getStore8() {
20991
- return getStoreLattice23("default", "channelInstallation").store;
22131
+ return getStoreLattice24("default", "channelInstallation").store;
20992
22132
  }
20993
22133
  function getOverwriteError(tenantId, incomingChannel, existing) {
20994
22134
  if (!existing) return "Channel installation overwrite target must exist";
@@ -21114,7 +22254,7 @@ var channelInstallationRegistration = {
21114
22254
  }
21115
22255
  id3 = sourceId;
21116
22256
  } else {
21117
- id3 = randomUUID23();
22257
+ id3 = randomUUID24();
21118
22258
  }
21119
22259
  await store.createInstallation(tenantId, id3, createInput);
21120
22260
  return { newId: id3 };
@@ -21122,9 +22262,9 @@ var channelInstallationRegistration = {
21122
22262
  };
21123
22263
 
21124
22264
  // src/export_registrations/menu.registration.ts
21125
- import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
22265
+ import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
21126
22266
  function getStore9() {
21127
- return getStoreLattice24("default", "menu").store;
22267
+ return getStoreLattice25("default", "menu").store;
21128
22268
  }
21129
22269
  var menuRegistration = {
21130
22270
  entityType: "menu",
@@ -21202,9 +22342,9 @@ var menuRegistration = {
21202
22342
  };
21203
22343
 
21204
22344
  // src/export_registrations/eval.registration.ts
21205
- import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
22345
+ import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
21206
22346
  function getStore10() {
21207
- return getStoreLattice25("default", "eval").store;
22347
+ return getStoreLattice26("default", "eval").store;
21208
22348
  }
21209
22349
  var evalRegistration = {
21210
22350
  entityType: "eval",
@@ -21982,11 +23122,12 @@ import {
21982
23122
  getLoggerLattice,
21983
23123
  loggerLatticeManager,
21984
23124
  sandboxLatticeManager as sandboxLatticeManager2,
21985
- getStoreLattice as getStoreLattice27,
23125
+ getStoreLattice as getStoreLattice28,
21986
23126
  agentInstanceManager as agentInstanceManager9,
21987
23127
  createSandboxProvider,
21988
23128
  TokenCache,
21989
- mcpManager,
23129
+ mcpManager as mcpManager3,
23130
+ convertToConnection as convertToConnection2,
21990
23131
  createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2
21991
23132
  } from "@axiom-lattice/core";
21992
23133
 
@@ -21995,14 +23136,14 @@ import { RoomAgentMessageService } from "@axiom-lattice/core";
21995
23136
 
21996
23137
  // src/router/MessageRouter.ts
21997
23138
  import {
21998
- getStoreLattice as getStoreLattice26,
23139
+ getStoreLattice as getStoreLattice27,
21999
23140
  agentInstanceManager as agentInstanceManager8
22000
23141
  } from "@axiom-lattice/core";
22001
23142
  import {
22002
23143
  snapshotExactArray as snapshotExactArray3,
22003
23144
  snapshotExactRecord as snapshotExactRecord15
22004
23145
  } from "@axiom-lattice/protocols";
22005
- import { randomUUID as randomUUID24 } from "crypto";
23146
+ import { randomUUID as randomUUID25 } from "crypto";
22006
23147
  var BindingNotFoundError = class extends Error {
22007
23148
  constructor(message) {
22008
23149
  super(message);
@@ -22095,7 +23236,7 @@ function validateRoomTrustedDispatch(message, options, binding, agentId) {
22095
23236
  "inputMessageId",
22096
23237
  "role",
22097
23238
  "title"
22098
- ], ["responsibility"]);
23239
+ ], ["responsibility", "source"]);
22099
23240
  if (!binding || !targetEnvelope || !target || !context || !authority || typeof authority.expectedRoomThreadId !== "string" || authority.expectedRoomThreadId.length === 0 || trusted?.executionMode !== "followup") return false;
22100
23241
  const expectedSender = `room:${context.roomId}:membership:${context.membershipId}`;
22101
23242
  return targetEnvelope.adapterChannel === "room" && targetEnvelope.channelInstallationId === message.channelInstallationId && target.sourceRoomMessageId === context.sourceRoomMessageId && target.targetMembershipId === context.membershipId && target.inputMessageId === context.inputMessageId && message.tenantId === context.tenantId && message.sender.id === expectedSender && message.conversation?.id === context.roomId && message.conversation?.type === "group" && binding.tenantId === context.tenantId && binding.channel === "room" && binding.channelInstallationId === message.channelInstallationId && binding.senderId === expectedSender && binding.agentId === context.assistantId && binding.workspaceId === context.workspaceId && binding.projectId === context.projectId && binding.threadId === authority.expectedRoomThreadId && binding.threadMode === "fixed" && agentId === context.assistantId;
@@ -22286,7 +23427,7 @@ var MessageRouter = class {
22286
23427
  channel: message.channel,
22287
23428
  adapterChannel: adapter.channel
22288
23429
  }, "Thread resolved by adapter strategy");
22289
- const threadStore = getStoreLattice26("default", "thread").store;
23430
+ const threadStore = getStoreLattice27("default", "thread").store;
22290
23431
  try {
22291
23432
  await threadStore.createThread(
22292
23433
  tenantId,
@@ -22323,8 +23464,8 @@ var MessageRouter = class {
22323
23464
  }
22324
23465
  }
22325
23466
  if (!threadId) {
22326
- const threadStore = getStoreLattice26("default", "thread").store;
22327
- const newThreadId = randomUUID24();
23467
+ const threadStore = getStoreLattice27("default", "thread").store;
23468
+ const newThreadId = randomUUID25();
22328
23469
  console.log({
22329
23470
  event: "dispatch:thread:create",
22330
23471
  agentId,
@@ -22370,7 +23511,7 @@ var MessageRouter = class {
22370
23511
  workspace_id: binding?.workspaceId || "",
22371
23512
  project_id: binding?.projectId || ""
22372
23513
  });
22373
- const inputMessageId = randomUUID24();
23514
+ const inputMessageId = randomUUID25();
22374
23515
  const replyTarget = internalRoom ? correlatedRoomReplyTarget(message.replyTarget, inputMessageId) : message.replyTarget;
22375
23516
  const replyAdapter = replyTarget ? this.adapterRegistry.get(replyTarget.adapterChannel) : void 0;
22376
23517
  if (options.trustedMessageOptions && (!this.trustedDispatcher || !replyTarget || !replyAdapter)) {
@@ -22786,35 +23927,49 @@ function getConfiguredSandboxProvider() {
22786
23927
  }
22787
23928
  async function restoreMcpConnections() {
22788
23929
  try {
22789
- const storeLattice = getStoreLattice27("default", "mcp");
23930
+ const storeLattice = getStoreLattice28("default", "mcp");
22790
23931
  const store = storeLattice.store;
22791
23932
  if (!store) {
22792
23933
  logger5.info("MCP store not configured, skipping connection restoration");
22793
23934
  return;
22794
23935
  }
22795
- const configs = await store.getAllConfigs("default");
22796
- const connectedConfigs = configs.filter((c2) => c2.status === "connected");
22797
- if (connectedConfigs.length === 0) return;
22798
- logger5.info(`Restoring ${connectedConfigs.length} MCP server connections...`);
22799
- const { convertToConnection } = await import("./mcp-configs-WCY5EXCW.mjs");
22800
- for (const config of connectedConfigs) {
23936
+ const configs = (await store.getAllConfigsWithoutTenant()).filter(
23937
+ (c2) => c2.status === "connected"
23938
+ );
23939
+ if (configs.length === 0) return;
23940
+ logger5.info(`Restoring ${configs.length} MCP server connections...`);
23941
+ for (const config of configs) {
22801
23942
  try {
22802
- const connection = convertToConnection(config.config);
22803
- mcpManager.addServer(config.key, connection);
23943
+ const connection = convertToConnection2(config.config);
23944
+ mcpManager3.registerServer(config.tenantId, config.key, connection, {
23945
+ timeoutMs: config.config.timeout,
23946
+ maxAttempts: config.config.retryAttempts,
23947
+ selectedTools: config.selectedTools
23948
+ });
22804
23949
  } catch (err) {
22805
- logger5.warn(`Failed to prepare MCP server "${config.key}" for restoration`, { error: err });
23950
+ logger5.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
22806
23951
  }
22807
23952
  }
22808
- await mcpManager.connect();
22809
- for (const config of connectedConfigs) {
22810
- try {
22811
- await mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
22812
- logger5.info(`MCP server "${config.key}" restored with ${config.selectedTools.length} tools`);
22813
- } catch (err) {
22814
- logger5.warn(`Failed to register tools for MCP server "${config.key}"`, { error: err });
23953
+ await mcpManager3.connectAll();
23954
+ const results = await Promise.allSettled(
23955
+ configs.map(
23956
+ (config) => mcpManager3.registerToolsToToolLattice(
23957
+ config.tenantId,
23958
+ config.key,
23959
+ config.selectedTools
23960
+ )
23961
+ )
23962
+ );
23963
+ results.forEach((result, index) => {
23964
+ if (result.status === "rejected") {
23965
+ const config = configs[index];
23966
+ logger5.warn(
23967
+ `Failed to register tools for MCP server "${config.tenantId}/${config.key}"`,
23968
+ { error: result.reason }
23969
+ );
22815
23970
  }
22816
- }
22817
- logger5.info(`MCP connection restoration complete: ${connectedConfigs.length} servers`);
23971
+ });
23972
+ logger5.info(`MCP connection restoration complete: ${configs.length} servers`);
22818
23973
  } catch (err) {
22819
23974
  logger5.error("MCP connection restoration failed", { error: err });
22820
23975
  }
@@ -22836,9 +23991,9 @@ var start = async (config) => {
22836
23991
  let projectRoomRealtimeRuntime;
22837
23992
  if (projectRoomRealtimeMode === "single-gateway") {
22838
23993
  projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
22839
- tasks: getStoreLattice27("default", "task").store,
22840
- rooms: getStoreLattice27("default", "projectRoom").store,
22841
- memberships: getStoreLattice27("default", "projectBotMembership").store,
23994
+ tasks: getStoreLattice28("default", "task").store,
23995
+ rooms: getStoreLattice28("default", "projectRoom").store,
23996
+ memberships: getStoreLattice28("default", "projectBotMembership").store,
22842
23997
  log: { warn: (message, context) => logger5.warn(message, context) }
22843
23998
  });
22844
23999
  }
@@ -22881,7 +24036,7 @@ var start = async (config) => {
22881
24036
  }
22882
24037
  setEvalRunService(evalRunner);
22883
24038
  try {
22884
- const menuStore = getStoreLattice27("default", "menu").store;
24039
+ const menuStore = getStoreLattice28("default", "menu").store;
22885
24040
  setMenuRegistry(menuStore);
22886
24041
  logger5.info("Menu registry initialized");
22887
24042
  } catch {
@@ -22897,12 +24052,12 @@ var start = async (config) => {
22897
24052
  });
22898
24053
  try {
22899
24054
  const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-7UGQ7GMW.mjs");
22900
- const { registerA2AStandardRoutes } = await import("./a2a-standard-ALXWP7V6.mjs");
24055
+ const { registerA2AStandardRoutes } = await import("./a2a-standard-P7JDL6ZA.mjs");
22901
24056
  const a2a = await import("./a2a-FGYX2HH3.mjs");
22902
- const a2aKeyStore = getStoreLattice27("default", "a2aApiKey").store;
22903
- const assistantStore = getStoreLattice27("default", "assistant").store;
22904
- const projectStore = getStoreLattice27("default", "project").store;
22905
- const taskStore = getStoreLattice27("default", "task").store;
24057
+ const a2aKeyStore = getStoreLattice28("default", "a2aApiKey").store;
24058
+ const assistantStore = getStoreLattice28("default", "assistant").store;
24059
+ const projectStore = getStoreLattice28("default", "project").store;
24060
+ const taskStore = getStoreLattice28("default", "task").store;
22906
24061
  const authService = new A2AAuthService({
22907
24062
  keyStore: a2aKeyStore,
22908
24063
  assistantStore,
@@ -22927,22 +24082,22 @@ var start = async (config) => {
22927
24082
  });
22928
24083
  }
22929
24084
  try {
22930
- const { registerOpenRoutes } = await import("./open-HH7RKGML.mjs");
22931
- const { registerOpenSessionRoutes } = await import("./open-session-3VN65PRR.mjs");
22932
- const { registerOpenCatalogRoutes } = await import("./open-catalog-WYS2AENM.mjs");
22933
- const { registerOpenFilesRoutes } = await import("./open-files-GVRMK5PL.mjs");
24085
+ const { registerOpenRoutes } = await import("./open-AMEMJOFJ.mjs");
24086
+ const { registerOpenSessionRoutes } = await import("./open-session-LBTCU3AE.mjs");
24087
+ const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
24088
+ const { registerOpenFilesRoutes } = await import("./open-files-EJ6O2SGX.mjs");
22934
24089
  const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
22935
- const { registerOpenExposureRoutes } = await import("./open-exposure-FK4G6O6L.mjs");
22936
- const { openLattice } = await import("./openLattice-GAAGQJST.mjs");
24090
+ const { registerOpenExposureRoutes } = await import("./open-exposure-I44Q4M3T.mjs");
24091
+ const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
22937
24092
  const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
22938
24093
  const { createOpenSandboxProvider } = await import("./sandboxProvider-I3P66VXT.mjs");
22939
24094
  const { createAgentProvider } = await import("./agentProvider-X4FVQPKW.mjs");
22940
- const { registerExposedPluginTools } = await import("./registerExposedPlugins-63EUF4VS.mjs");
24095
+ const { registerExposedPluginTools } = await import("./registerExposedPlugins-6GCOE64C.mjs");
22941
24096
  const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
22942
24097
  openLattice.register(createKbProvider(new CollectionService2()));
22943
24098
  openLattice.register(createOpenSandboxProvider());
22944
- const threadStore = getStoreLattice27("default", "thread").store;
22945
- const assistantStore = getStoreLattice27("default", "assistant").store;
24099
+ const threadStore = getStoreLattice28("default", "thread").store;
24100
+ const assistantStore = getStoreLattice28("default", "assistant").store;
22946
24101
  openLattice.register(
22947
24102
  createAgentProvider({
22948
24103
  threadStore,
@@ -22970,8 +24125,8 @@ var start = async (config) => {
22970
24125
  logger5.info("Registered sandbox manager from env configuration");
22971
24126
  }
22972
24127
  try {
22973
- const { ResourceController } = await import("./resources-JFKAPS45.mjs");
22974
- const sharedResourceStore = getStoreLattice27("default", "sharedResource").store;
24128
+ const { ResourceController } = await import("./resources-DXPXAXEZ.mjs");
24129
+ const sharedResourceStore = getStoreLattice28("default", "sharedResource").store;
22975
24130
  const cache = new TokenCache();
22976
24131
  const resourceController = new ResourceController({
22977
24132
  store: sharedResourceStore,