@axiom-lattice/gateway 4.3.3 → 4.4.1

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