@axiom-lattice/gateway 4.7.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/{OrphanRecoveryRuntime-MQDEJX3M.mjs → OrphanRecoveryRuntime-BEFILJMU.mjs} +2 -2
  2. package/dist/{chunk-2POW27EI.mjs → chunk-7CG57LEF.mjs} +2 -2
  3. package/dist/{chunk-WKNZ7GBC.mjs → chunk-EXPERMZR.mjs} +1 -1
  4. package/dist/chunk-EXPERMZR.mjs.map +1 -0
  5. package/dist/{chunk-52SUWMOX.mjs → chunk-JUA3VJE7.mjs} +3 -2
  6. package/dist/chunk-JUA3VJE7.mjs.map +1 -0
  7. package/dist/{chunk-TJAUXMYJ.mjs → chunk-URXZPI2F.mjs} +8 -1
  8. package/dist/chunk-URXZPI2F.mjs.map +1 -0
  9. package/dist/{chunk-KDQKTU3N.mjs → chunk-ZBTG3T7K.mjs} +44 -8
  10. package/dist/chunk-ZBTG3T7K.mjs.map +1 -0
  11. package/dist/embed/{embed-page-AgentDetailSideApp-2C2ZR62Q.js → embed-page-AgentDetailSideApp-NTLAFO5C.js} +1 -1
  12. package/dist/embed/{embed-page-AgentGovernanceWorkbench-GG4ONSTS.js → embed-page-AgentGovernanceWorkbench-QPVBOMLH.js} +1 -1
  13. package/dist/embed/{embed-page-MessageGraph-WMYTVUUC.js → embed-page-MessageGraph-JAJGMYKH.js} +1 -1
  14. package/dist/embed/{embed-page-ReactChart-7XCMDMH6.js → embed-page-ReactChart-WUG6TEAZ.js} +1 -1
  15. package/dist/embed/{embed-page-SkillFlow-XMMHEHQL.js → embed-page-SkillFlow-TSBZ4V2P.js} +1 -1
  16. package/dist/embed/{embed-page-TasksSideApp-4ZJ4Q4CL.js → embed-page-TasksSideApp-TJI22YGD.js} +1 -1
  17. package/dist/embed/{embed-page-TeamWorkspace-H5OOEN3K.js → embed-page-TeamWorkspace-OJJLN5VI.js} +1 -1
  18. package/dist/embed/{embed-page-TopologyRuntimeView-QMXWERVC.js → embed-page-TopologyRuntimeView-ZFU4N6TV.js} +1 -1
  19. package/dist/embed/{embed-page-WorkflowAutomationView-L2OEGCA5.js → embed-page-WorkflowAutomationView-ALXV7VQQ.js} +1 -1
  20. package/dist/embed/{embed-page-chunk-CZE4IEB3.js → embed-page-chunk-DZQBVJ7Z.js} +1 -1
  21. package/dist/embed/{embed-page-chunk-N2ISCAS7.js → embed-page-chunk-IPIJ2SG6.js} +1 -1
  22. package/dist/embed/{embed-page-chunk-SUMYB6ZA.js → embed-page-chunk-N22EGQAJ.js} +1 -1
  23. package/dist/embed/{embed-page-chunk-OHDEKFTU.js → embed-page-chunk-O72XBKQH.js} +2 -2
  24. package/dist/embed/{embed-page-chunk-XIXSRYJY.js → embed-page-chunk-QMVIL4BJ.js} +190 -185
  25. package/dist/embed/{embed-page-chunk-2G54EIW2.js → embed-page-chunk-UT3WYBZH.js} +1 -1
  26. package/dist/embed/embed-page-chunk-YPNSOZYW.js +8 -0
  27. package/dist/embed/embed-page.js +1 -1
  28. package/dist/index.d.mts +146 -17
  29. package/dist/index.d.ts +146 -17
  30. package/dist/index.js +1190 -328
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +649 -97
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/{open-STFMNRO5.mjs → open-UVKMONVE.mjs} +5 -4
  35. package/dist/open-UVKMONVE.mjs.map +1 -0
  36. package/dist/{open-files-NPNQQ6TY.mjs → open-files-X3HXVEEM.mjs} +4 -4
  37. package/dist/{open-session-IHDGAXL3.mjs → open-session-2Y7XJJW2.mjs} +3 -3
  38. package/dist/{resources-DXPXAXEZ.mjs → resources-2L3KSUUU.mjs} +2 -2
  39. package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs +275 -0
  40. package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs.map +1 -0
  41. package/dist/{sandboxProvider-I3P66VXT.mjs → sandboxProvider-DUYWHYJN.mjs} +2 -2
  42. package/package.json +14 -8
  43. package/dist/chunk-52SUWMOX.mjs.map +0 -1
  44. package/dist/chunk-KDQKTU3N.mjs.map +0 -1
  45. package/dist/chunk-TJAUXMYJ.mjs.map +0 -1
  46. package/dist/chunk-WKNZ7GBC.mjs.map +0 -1
  47. package/dist/embed/embed-page-chunk-HEMNMC4X.js +0 -7
  48. package/dist/open-STFMNRO5.mjs.map +0 -1
  49. /package/dist/{OrphanRecoveryRuntime-MQDEJX3M.mjs.map → OrphanRecoveryRuntime-BEFILJMU.mjs.map} +0 -0
  50. /package/dist/{chunk-2POW27EI.mjs.map → chunk-7CG57LEF.mjs.map} +0 -0
  51. /package/dist/{open-files-NPNQQ6TY.mjs.map → open-files-X3HXVEEM.mjs.map} +0 -0
  52. /package/dist/{open-session-IHDGAXL3.mjs.map → open-session-2Y7XJJW2.mjs.map} +0 -0
  53. /package/dist/{resources-DXPXAXEZ.mjs.map → resources-2L3KSUUU.mjs.map} +0 -0
  54. /package/dist/{sandboxProvider-I3P66VXT.mjs.map → sandboxProvider-DUYWHYJN.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -966,7 +966,7 @@ function outcome(task) {
966
966
  });
967
967
  }
968
968
  async function backfillTopCallerGroup(tasks, top, deps) {
969
- const registry = deps.registry ?? import_core66.taskRunRegistry;
969
+ const registry = deps.registry ?? import_core70.taskRunRegistry;
970
970
  const results = [];
971
971
  const eligible = [];
972
972
  for (const task of tasks) {
@@ -974,7 +974,7 @@ async function backfillTopCallerGroup(tasks, top, deps) {
974
974
  results.push({ id: task.id, reason: "skipped:no_caller_link" });
975
975
  continue;
976
976
  }
977
- if (registry.isLive(task.id)) {
977
+ if (registry.isLive(task.id) || deps.isTaskLive && await deps.isTaskLive(task)) {
978
978
  results.push({ id: task.id, reason: "skipped:live" });
979
979
  continue;
980
980
  }
@@ -1020,15 +1020,16 @@ async function recoverTask(task, deps) {
1020
1020
  if (task.status !== "in_progress") {
1021
1021
  return { kind: "skipped", reason: "not_in_progress" };
1022
1022
  }
1023
- const registry = deps.registry ?? import_core66.taskRunRegistry;
1024
- if (registry.isLive(task.id)) {
1023
+ const registry = deps.registry ?? import_core70.taskRunRegistry;
1024
+ if (registry.isLive(task.id) || deps.isTaskLive && await deps.isTaskLive(task)) {
1025
1025
  return { kind: "skipped", reason: "live" };
1026
1026
  }
1027
- const { convergedTasks } = await (0, import_core66.reconcileStaleTaskRuns)({
1027
+ const { convergedTasks } = await (0, import_core70.reconcileStaleTaskRuns)({
1028
1028
  tasks: [task],
1029
1029
  actor: "system:on-demand-recovery",
1030
1030
  lifecycle: deps.lifecycle,
1031
- ...deps.registry ? { registry: deps.registry } : {}
1031
+ ...deps.registry ? { registry: deps.registry } : {},
1032
+ ...deps.isTaskLive ? { isTaskLive: deps.isTaskLive } : {}
1032
1033
  });
1033
1034
  const converged = convergedTasks[0];
1034
1035
  if (!converged) {
@@ -1044,7 +1045,8 @@ async function recoverTask(task, deps) {
1044
1045
  const reason = await recoverTopCaller(converged, top, {
1045
1046
  callerFor: deps.callerFor,
1046
1047
  resume: deps.resume,
1047
- ...deps.registry ? { registry: deps.registry } : {}
1048
+ ...deps.registry ? { registry: deps.registry } : {},
1049
+ ...deps.isTaskLive ? { isTaskLive: deps.isTaskLive } : {}
1048
1050
  });
1049
1051
  if (reason === "recovered") return { kind: "recovered" };
1050
1052
  if (reason === "skipped:no_pending_task_call") return { kind: "converged_no_call" };
@@ -1101,7 +1103,8 @@ async function runOrphanRecovery(deps) {
1101
1103
  try {
1102
1104
  const results = await backfillTopCallerGroup(tasks, top, {
1103
1105
  callerFor: deps.callerFor,
1104
- resume: deps.resume
1106
+ resume: deps.resume,
1107
+ ...deps.isTaskLive ? { isTaskLive: deps.isTaskLive } : {}
1105
1108
  });
1106
1109
  for (const { id: id3, reason } of results) {
1107
1110
  (reason === "recovered" ? recovered : skipped).push(`${id3}:${reason}`);
@@ -1117,12 +1120,41 @@ async function runOrphanRecovery(deps) {
1117
1120
  deps.log.info("Task orphan recovery complete", { recovered, skipped });
1118
1121
  return { recovered, skipped };
1119
1122
  }
1120
- var import_messages, import_core66;
1123
+ var import_messages, import_core70;
1121
1124
  var init_TopCallerRecovery = __esm({
1122
1125
  "src/services/collaboration/TopCallerRecovery.ts"() {
1123
1126
  "use strict";
1124
1127
  import_messages = require("@langchain/core/messages");
1125
- import_core66 = require("@axiom-lattice/core");
1128
+ import_core70 = require("@axiom-lattice/core");
1129
+ }
1130
+ });
1131
+
1132
+ // src/services/collaboration/taskRunLiveness.ts
1133
+ function createTaskRunLiveness() {
1134
+ try {
1135
+ import_core71.storeLatticeManager.getStoreLattice("default", "lease");
1136
+ } catch {
1137
+ return void 0;
1138
+ }
1139
+ return async (task) => {
1140
+ try {
1141
+ const store2 = import_core71.storeLatticeManager.getStoreLattice("default", "lease").store;
1142
+ if (await store2.get((0, import_core71.taskRunLeaseKey)(task.id))) return true;
1143
+ const threadId = typeof task.context?.thread_id === "string" ? task.context.thread_id : void 0;
1144
+ if (threadId && task.ownerId) {
1145
+ if (await store2.get((0, import_core71.threadRunLeaseKey)(task.tenantId, task.ownerId, threadId))) return true;
1146
+ }
1147
+ return false;
1148
+ } catch {
1149
+ return true;
1150
+ }
1151
+ };
1152
+ }
1153
+ var import_core71;
1154
+ var init_taskRunLiveness = __esm({
1155
+ "src/services/collaboration/taskRunLiveness.ts"() {
1156
+ "use strict";
1157
+ import_core71 = require("@axiom-lattice/core");
1126
1158
  }
1127
1159
  });
1128
1160
 
@@ -1132,24 +1164,27 @@ __export(OrphanRecoveryRuntime_exports, {
1132
1164
  createOrphanRecoveryRuntime: () => createOrphanRecoveryRuntime
1133
1165
  });
1134
1166
  function createOrphanRecoveryRuntime(deps) {
1135
- const lifecycle = deps.lifecycle ?? (0, import_core67.createTaskLifecycleService)({
1167
+ const lifecycle = deps.lifecycle ?? (0, import_core72.createTaskLifecycleService)({
1136
1168
  taskStore: deps.taskStore,
1137
1169
  workItemStore: deps.workItemStore
1138
1170
  });
1139
1171
  return {
1140
1172
  async recoverAll() {
1173
+ const isTaskLive = deps.isTaskLive ?? createTaskRunLiveness();
1141
1174
  return runOrphanRecovery({
1142
- reconcile: () => (0, import_core67.reconcileAllTenantTaskRuns)({
1175
+ reconcile: () => (0, import_core72.reconcileAllTenantTaskRuns)({
1143
1176
  actor: "system:orphan-recovery",
1144
1177
  tenantStore: {
1145
1178
  listTenants: async () => (await deps.tenantStore.getAllTenants()).map((tenant2) => ({ id: tenant2.id }))
1146
1179
  },
1147
1180
  taskStore: deps.taskStore,
1148
1181
  lifecycle,
1149
- include: (t2) => !isProjectLifecycleTask(t2)
1182
+ include: (t2) => !isProjectLifecycleTask(t2),
1183
+ ...isTaskLive ? { isTaskLive } : {}
1150
1184
  }),
1151
1185
  callerFor: deps.callerFor,
1152
1186
  resume: deps.resume,
1187
+ ...isTaskLive ? { isTaskLive } : {},
1153
1188
  log: deps.log
1154
1189
  });
1155
1190
  },
@@ -1160,23 +1195,26 @@ function createOrphanRecoveryRuntime(deps) {
1160
1195
  return { kind: "skipped", reason: "project_task" };
1161
1196
  }
1162
1197
  const orphans = await deps.taskStore.list({ tenantId: input.tenantId, status: "in_progress" });
1198
+ const isTaskLive = deps.isTaskLive ?? createTaskRunLiveness();
1163
1199
  return recoverTask(task, {
1164
1200
  lifecycle,
1165
- registry: import_core67.taskRunRegistry,
1201
+ registry: import_core72.taskRunRegistry,
1166
1202
  orphans,
1167
1203
  callerFor: deps.callerFor,
1168
- resume: deps.resume
1204
+ resume: deps.resume,
1205
+ ...isTaskLive ? { isTaskLive } : {}
1169
1206
  });
1170
1207
  }
1171
1208
  };
1172
1209
  }
1173
- var import_core67;
1210
+ var import_core72;
1174
1211
  var init_OrphanRecoveryRuntime = __esm({
1175
1212
  "src/services/collaboration/OrphanRecoveryRuntime.ts"() {
1176
1213
  "use strict";
1177
- import_core67 = require("@axiom-lattice/core");
1214
+ import_core72 = require("@axiom-lattice/core");
1178
1215
  init_TopCallerRecovery();
1179
1216
  init_projectTaskScope();
1217
+ init_taskRunLiveness();
1180
1218
  }
1181
1219
  });
1182
1220
 
@@ -1189,12 +1227,12 @@ function createRecoveryCallerDeps() {
1189
1227
  return {
1190
1228
  callerFor: async (top, tenantId) => {
1191
1229
  if (!top.assistantId) return void 0;
1192
- const load = import_core68.getAgentClientAsync ?? import_core68.getAgentClient;
1230
+ const load = import_core73.getAgentClientAsync ?? import_core73.getAgentClient;
1193
1231
  const client = await load(tenantId, top.assistantId);
1194
1232
  const config = { configurable: { thread_id: top.threadId, runConfig: { tenantId, assistant_id: top.assistantId } } };
1195
1233
  let agent;
1196
1234
  try {
1197
- agent = import_core68.agentInstanceManager.getAgent({ assistant_id: top.assistantId, thread_id: top.threadId, tenant_id: tenantId });
1235
+ agent = import_core73.agentInstanceManager.getAgent({ assistant_id: top.assistantId, thread_id: top.threadId, tenant_id: tenantId });
1198
1236
  } catch {
1199
1237
  }
1200
1238
  return agent ? { client, config, agent } : { client, config };
@@ -1211,11 +1249,11 @@ function createRecoveryCallerDeps() {
1211
1249
  }
1212
1250
  };
1213
1251
  }
1214
- var import_core68;
1252
+ var import_core73;
1215
1253
  var init_RecoveryCallerDeps = __esm({
1216
1254
  "src/services/collaboration/RecoveryCallerDeps.ts"() {
1217
1255
  "use strict";
1218
- import_core68 = require("@axiom-lattice/core");
1256
+ import_core73 = require("@axiom-lattice/core");
1219
1257
  }
1220
1258
  });
1221
1259
 
@@ -1805,13 +1843,13 @@ function isA2ATaskSnapshot(value) {
1805
1843
  function snapshotSequence(value) {
1806
1844
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1807
1845
  }
1808
- var import_node_crypto28, import_node_util, import_core69, TASK_STATUS_TO_A2A_STATE, TITLE_MAX_LENGTH, A2ATaskScopeConflictError, A2ATaskStoreAdapter;
1846
+ var import_node_crypto28, import_node_util, import_core74, TASK_STATUS_TO_A2A_STATE, TITLE_MAX_LENGTH, A2ATaskScopeConflictError, A2ATaskStoreAdapter;
1809
1847
  var init_A2ATaskStoreAdapter = __esm({
1810
1848
  "src/services/a2a/A2ATaskStoreAdapter.ts"() {
1811
1849
  "use strict";
1812
1850
  import_node_crypto28 = require("crypto");
1813
1851
  import_node_util = require("util");
1814
- import_core69 = require("@axiom-lattice/core");
1852
+ import_core74 = require("@axiom-lattice/core");
1815
1853
  TASK_STATUS_TO_A2A_STATE = {
1816
1854
  pending: "submitted",
1817
1855
  in_progress: "working",
@@ -1837,7 +1875,7 @@ var init_A2ATaskStoreAdapter = __esm({
1837
1875
  this.workItemStore = options.workItemStore;
1838
1876
  this.lifecycle = options.lifecycle;
1839
1877
  this.scope = options.scope;
1840
- this.runBridge = (0, import_core69.createTaskRunBridge)({
1878
+ this.runBridge = (0, import_core74.createTaskRunBridge)({
1841
1879
  lifecycle: this.lifecycle,
1842
1880
  taskStore: this.taskStore,
1843
1881
  workItemStore: this.workItemStore
@@ -2188,12 +2226,12 @@ function assistantHasGovernedTask(assistant) {
2188
2226
  const allowedTools = taskMiddleware?.allowedTools;
2189
2227
  return !!taskMiddleware && (allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task")));
2190
2228
  }
2191
- var import_uuid11, import_core70, import_protocols35, _AxiomAgentExecutor, AxiomAgentExecutor;
2229
+ var import_uuid11, import_core75, import_protocols35, _AxiomAgentExecutor, AxiomAgentExecutor;
2192
2230
  var init_AxiomAgentExecutor = __esm({
2193
2231
  "src/services/a2a/AxiomAgentExecutor.ts"() {
2194
2232
  "use strict";
2195
2233
  import_uuid11 = require("uuid");
2196
- import_core70 = require("@axiom-lattice/core");
2234
+ import_core75 = require("@axiom-lattice/core");
2197
2235
  import_protocols35 = require("@axiom-lattice/protocols");
2198
2236
  init_a2aFileRefs();
2199
2237
  init_task_file_refs();
@@ -2290,7 +2328,7 @@ var init_AxiomAgentExecutor = __esm({
2290
2328
  await this.taskStore.save(initialTask);
2291
2329
  bus.publish(initialTask);
2292
2330
  try {
2293
- const agent = import_core70.agentInstanceManager.getAgent(this.agentParams(threadId));
2331
+ const agent = import_core75.agentInstanceManager.getAgent(this.agentParams(threadId));
2294
2332
  const requestText = inputFiles.length > 0 ? `${text}
2295
2333
 
2296
2334
  ${formatFileRefsSection(inputFiles)}` : text;
@@ -2478,7 +2516,7 @@ ${formatFileRefsSection(inputFiles)}` : text;
2478
2516
  const threadId = task ? asString2(task.metadata?.threadId) : void 0;
2479
2517
  if (threadId) {
2480
2518
  try {
2481
- const agent = import_core70.agentInstanceManager.getAgent(this.agentParams(threadId));
2519
+ const agent = import_core75.agentInstanceManager.getAgent(this.agentParams(threadId));
2482
2520
  await agent.abort();
2483
2521
  } catch (err) {
2484
2522
  console.warn({
@@ -2852,10 +2890,10 @@ function isAsyncGenerator(value) {
2852
2890
  }
2853
2891
  function registerA2AStandardRoutes(app2, deps) {
2854
2892
  const authService = deps.authService;
2855
- const resolveTasks = deps.getTaskStoreLattice ?? (() => (0, import_core71.getStoreLattice)("default", "task").store);
2856
- const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => (0, import_core71.getStoreLattice)("default", "taskWorkItem").store);
2857
- const getAssistantStore2 = deps.getAssistantStore ?? (() => (0, import_core71.getStoreLattice)("default", "assistant").store);
2858
- const getProjectStore2 = deps.getProjectStore ?? (() => (0, import_core71.getStoreLattice)("default", "project").store);
2893
+ const resolveTasks = deps.getTaskStoreLattice ?? (() => (0, import_core76.getStoreLattice)("default", "task").store);
2894
+ const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => (0, import_core76.getStoreLattice)("default", "taskWorkItem").store);
2895
+ const getAssistantStore2 = deps.getAssistantStore ?? (() => (0, import_core76.getStoreLattice)("default", "assistant").store);
2896
+ const getProjectStore2 = deps.getProjectStore ?? (() => (0, import_core76.getStoreLattice)("default", "project").store);
2859
2897
  const runtimeRegistry = deps.runtimeRegistry ?? new A2ARuntimeRegistry();
2860
2898
  app2.addHook("onClose", async () => {
2861
2899
  runtimeRegistry.close();
@@ -2938,7 +2976,7 @@ function registerA2AStandardRoutes(app2, deps) {
2938
2976
  const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
2939
2977
  taskStore: internalTasks,
2940
2978
  workItemStore: workItems,
2941
- lifecycle: (0, import_core71.createTaskLifecycleService)({
2979
+ lifecycle: (0, import_core76.createTaskLifecycleService)({
2942
2980
  taskStore: internalTasks,
2943
2981
  workItemStore: workItems
2944
2982
  }),
@@ -3057,7 +3095,7 @@ data: ${JSON.stringify(errorResponse)}
3057
3095
  const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
3058
3096
  taskStore: internalTasks,
3059
3097
  workItemStore: workItems,
3060
- lifecycle: (0, import_core71.createTaskLifecycleService)({
3098
+ lifecycle: (0, import_core76.createTaskLifecycleService)({
3061
3099
  taskStore: internalTasks,
3062
3100
  workItemStore: workItems
3063
3101
  }),
@@ -3112,13 +3150,13 @@ data: ${JSON.stringify(data)}
3112
3150
  }
3113
3151
  );
3114
3152
  }
3115
- var import_uuid12, import_server2, import_core71, SSE_HEADERS;
3153
+ var import_uuid12, import_server2, import_core76, SSE_HEADERS;
3116
3154
  var init_a2a_standard = __esm({
3117
3155
  "src/routes/a2a-standard.ts"() {
3118
3156
  "use strict";
3119
3157
  import_uuid12 = require("uuid");
3120
3158
  import_server2 = require("@a2a-js/sdk/server");
3121
- import_core71 = require("@axiom-lattice/core");
3159
+ import_core76 = require("@axiom-lattice/core");
3122
3160
  init_A2AAuthService();
3123
3161
  init_A2AAgentCardBuilder();
3124
3162
  init_A2ATaskStoreAdapter();
@@ -3376,6 +3414,7 @@ var init_openCredentialService = __esm({
3376
3414
  projectId: token.projectId,
3377
3415
  workspaceId: token.workspaceId,
3378
3416
  sandboxName: token.sandboxName,
3417
+ depth: token.depth,
3379
3418
  grants: token.grants
3380
3419
  };
3381
3420
  }
@@ -3869,8 +3908,8 @@ function exposedDeclarations(plugin) {
3869
3908
  return (plugin.meta.tools ?? []).filter((t2) => t2.expose);
3870
3909
  }
3871
3910
  async function registerExposedPluginTools() {
3872
- for (const key of import_core72.PluginRegistry.list()) {
3873
- const plugin = import_core72.PluginRegistry.get(key);
3911
+ for (const key of import_core77.PluginRegistry.list()) {
3912
+ const plugin = import_core77.PluginRegistry.get(key);
3874
3913
  if (!plugin?.middleware) continue;
3875
3914
  const exposed = exposedDeclarations(plugin);
3876
3915
  if (exposed.length === 0) continue;
@@ -3905,8 +3944,8 @@ async function registerExposedPluginTools() {
3905
3944
  }
3906
3945
  function collectExposedPluginUiResources(grants) {
3907
3946
  const resources = /* @__PURE__ */ new Map();
3908
- for (const type of import_core72.PluginRegistry.list()) {
3909
- const plugin = import_core72.PluginRegistry.get(type);
3947
+ for (const type of import_core77.PluginRegistry.list()) {
3948
+ const plugin = import_core77.PluginRegistry.get(type);
3910
3949
  if (!plugin?.middleware) continue;
3911
3950
  const exposedNames = new Set(exposedDeclarations(plugin).map((d) => d.name));
3912
3951
  if (exposedNames.size === 0) continue;
@@ -3930,11 +3969,11 @@ function collectExposedPluginUiResources(grants) {
3930
3969
  }
3931
3970
  return resources;
3932
3971
  }
3933
- var import_core72;
3972
+ var import_core77;
3934
3973
  var init_registerExposedPlugins = __esm({
3935
3974
  "src/services/open/registerExposedPlugins.ts"() {
3936
3975
  "use strict";
3937
- import_core72 = require("@axiom-lattice/core");
3976
+ import_core77 = require("@axiom-lattice/core");
3938
3977
  init_openLattice();
3939
3978
  init_pluginExposeAdapter();
3940
3979
  }
@@ -3949,14 +3988,14 @@ function toText(result) {
3949
3988
  }
3950
3989
  function defaultCatalog() {
3951
3990
  return {
3952
- getValidatedManifest: (tenantId, type) => (0, import_core73.getSandboxPluginCatalog)().getValidatedManifest(tenantId, type),
3953
- getExecutor: () => (0, import_core73.getSandboxPluginCatalog)().getExecutor(),
3954
- readPluginFile: (tenantId, type, relPath) => (0, import_core73.getSandboxPluginCatalog)().readPluginFile(tenantId, type, relPath)
3991
+ getValidatedManifest: (tenantId, type) => (0, import_core78.getSandboxPluginCatalog)().getValidatedManifest(tenantId, type),
3992
+ getExecutor: () => (0, import_core78.getSandboxPluginCatalog)().getExecutor(),
3993
+ readPluginFile: (tenantId, type, relPath) => (0, import_core78.getSandboxPluginCatalog)().readPluginFile(tenantId, type, relPath)
3955
3994
  };
3956
3995
  }
3957
3996
  async function defaultResolveConnections(pluginType, ctx) {
3958
3997
  try {
3959
- return await (0, import_core73.resolveSandboxPluginConnections)(
3998
+ return await (0, import_core78.resolveSandboxPluginConnections)(
3960
3999
  pluginType,
3961
4000
  { connectAll: true },
3962
4001
  {
@@ -3985,7 +4024,7 @@ function mergeOpenCatalog(globalCatalog, tenantTools) {
3985
4024
  async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
3986
4025
  const catalog = deps.catalog ?? defaultCatalog();
3987
4026
  const listPlugins = deps.listPlugins ?? (async (tenant2) => {
3988
- const snapshot = await (0, import_core73.getSandboxPluginCatalog)().ensureLoaded(tenant2);
4027
+ const snapshot = await (0, import_core78.getSandboxPluginCatalog)().ensureLoaded(tenant2);
3989
4028
  return { plugins: snapshot.plugins, diagnostics: snapshot.diagnostics };
3990
4029
  });
3991
4030
  const resolveConnections = deps.resolveConnections ?? defaultResolveConnections;
@@ -4027,7 +4066,7 @@ async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
4027
4066
  domain,
4028
4067
  action: tool.name,
4029
4068
  description: tool.description,
4030
- schema: (0, import_core73.jsonSchemaToZod)(tool.schema),
4069
+ schema: (0, import_core78.jsonSchemaToZod)(tool.schema),
4031
4070
  annotations: {
4032
4071
  readOnlyHint: tool.readOnly ?? false,
4033
4072
  destructiveHint: tool.destructive ?? false
@@ -4078,11 +4117,11 @@ async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
4078
4117
  }
4079
4118
  };
4080
4119
  }
4081
- var import_core73;
4120
+ var import_core78;
4082
4121
  var init_tenantOpenPlugins = __esm({
4083
4122
  "src/services/open/tenantOpenPlugins.ts"() {
4084
4123
  "use strict";
4085
- import_core73 = require("@axiom-lattice/core");
4124
+ import_core78 = require("@axiom-lattice/core");
4086
4125
  init_openLattice();
4087
4126
  init_pluginToolExecution();
4088
4127
  }
@@ -4119,13 +4158,13 @@ function emptyTenantPlugins() {
4119
4158
  }
4120
4159
  function registerOpenRoutes(app2, deps = {}) {
4121
4160
  const openAuth = deps.openAuth ?? new OpenCredentialService(
4122
- (0, import_core74.getStoreLattice)("default", "a2aApiKey").store,
4123
- (0, import_core74.getStoreLattice)("default", "project").store
4161
+ (0, import_core79.getStoreLattice)("default", "a2aApiKey").store,
4162
+ (0, import_core79.getStoreLattice)("default", "project").store
4124
4163
  );
4125
4164
  const grantsSource = deps.grantsSource ?? ((ctx) => ctx.grants);
4126
4165
  const audit = deps.audit ?? (() => {
4127
4166
  try {
4128
- const store2 = (0, import_core74.getStoreLattice)("default", "openAudit").store;
4167
+ const store2 = (0, import_core79.getStoreLattice)("default", "openAudit").store;
4129
4168
  return new OpenAuditService(store2);
4130
4169
  } catch {
4131
4170
  return void 0;
@@ -4211,7 +4250,8 @@ function registerOpenRoutes(app2, deps = {}) {
4211
4250
  const isTenantRoute = tenant2.hasTool(toolName) && !globalToolNames.has(toolName);
4212
4251
  const runConfig = {
4213
4252
  sandboxName: context.sandboxName,
4214
- credentialId: context.credentialId
4253
+ credentialId: context.credentialId,
4254
+ depth: context.depth
4215
4255
  };
4216
4256
  const result = isTenantRoute ? await tenant2.invoke(toolName, args, {
4217
4257
  tenantId: context.tenantId,
@@ -4279,12 +4319,12 @@ function registerOpenRoutes(app2, deps = {}) {
4279
4319
  }
4280
4320
  );
4281
4321
  }
4282
- var import_types, import_core74, RPC_ERROR;
4322
+ var import_types, import_core79, RPC_ERROR;
4283
4323
  var init_open = __esm({
4284
4324
  "src/routes/open.ts"() {
4285
4325
  "use strict";
4286
4326
  import_types = require("@modelcontextprotocol/sdk/types.js");
4287
- import_core74 = require("@axiom-lattice/core");
4327
+ import_core79 = require("@axiom-lattice/core");
4288
4328
  init_openCredentialService();
4289
4329
  init_openAuditService();
4290
4330
  init_openRateLimiter();
@@ -4317,7 +4357,7 @@ function registerOpenSessionRoutes(app2) {
4317
4357
  if (typeof shareToken !== "string" || shareToken.length === 0) {
4318
4358
  return reply.status(400).send({ error: "SHARE_TOKEN_REQUIRED" });
4319
4359
  }
4320
- const store2 = (0, import_core75.getStoreLattice)("default", "sharedResource").store;
4360
+ const store2 = (0, import_core80.getStoreLattice)("default", "sharedResource").store;
4321
4361
  const record = await store2.findByToken(shareToken);
4322
4362
  if (!record || record.revoked) {
4323
4363
  return reply.status(404).send({ error: "SHARE_NOT_FOUND" });
@@ -4342,11 +4382,11 @@ function registerOpenSessionRoutes(app2) {
4342
4382
  }
4343
4383
  );
4344
4384
  }
4345
- var import_core75;
4385
+ var import_core80;
4346
4386
  var init_open_session = __esm({
4347
4387
  "src/routes/open-session.ts"() {
4348
4388
  "use strict";
4349
- import_core75 = require("@axiom-lattice/core");
4389
+ import_core80 = require("@axiom-lattice/core");
4350
4390
  init_openToken();
4351
4391
  init_previewToken();
4352
4392
  init_openLattice();
@@ -4355,13 +4395,13 @@ var init_open_session = __esm({
4355
4395
 
4356
4396
  // src/services/open/openCatalogService.ts
4357
4397
  async function defaultListTenantPlugins(tenantId) {
4358
- return (await (0, import_core76.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
4398
+ return (await (0, import_core81.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
4359
4399
  }
4360
- var import_core76, OpenCatalogService;
4400
+ var import_core81, OpenCatalogService;
4361
4401
  var init_openCatalogService = __esm({
4362
4402
  "src/services/open/openCatalogService.ts"() {
4363
4403
  "use strict";
4364
- import_core76 = require("@axiom-lattice/core");
4404
+ import_core81 = require("@axiom-lattice/core");
4365
4405
  init_openLattice();
4366
4406
  OpenCatalogService = class {
4367
4407
  constructor(assistantStore, lattice = openLattice, listTenantPlugins2 = defaultListTenantPlugins) {
@@ -4436,17 +4476,17 @@ function registerOpenCatalogRoutes(app2) {
4436
4476
  if (!tenantId) {
4437
4477
  return reply.status(401).send({ success: false, error: "Unauthorized" });
4438
4478
  }
4439
- const assistantStore = (0, import_core77.getStoreLattice)("default", "assistant").store;
4479
+ const assistantStore = (0, import_core82.getStoreLattice)("default", "assistant").store;
4440
4480
  const service2 = new OpenCatalogService(assistantStore);
4441
4481
  const catalog = await service2.buildForTenant(tenantId);
4442
4482
  return reply.status(200).send({ success: true, data: catalog });
4443
4483
  });
4444
4484
  }
4445
- var import_core77;
4485
+ var import_core82;
4446
4486
  var init_open_catalog = __esm({
4447
4487
  "src/routes/open-catalog.ts"() {
4448
4488
  "use strict";
4449
- import_core77 = require("@axiom-lattice/core");
4489
+ import_core82 = require("@axiom-lattice/core");
4450
4490
  init_openCatalogService();
4451
4491
  }
4452
4492
  });
@@ -4458,7 +4498,14 @@ __export(sandboxProvider_exports, {
4458
4498
  resolveContextSandbox: () => resolveContextSandbox
4459
4499
  });
4460
4500
  async function resolveContextSandbox(ctx) {
4461
- return import_core78.sandboxLatticeManager.getSandboxFromConfig({
4501
+ const sandboxName = ctx.runConfig?.sandboxName;
4502
+ if (typeof sandboxName === "string" && sandboxName.length > 0) {
4503
+ try {
4504
+ return await import_core83.sandboxLatticeManager.getSandbox(sandboxName);
4505
+ } catch {
4506
+ }
4507
+ }
4508
+ return import_core83.sandboxLatticeManager.getSandboxFromConfig({
4462
4509
  assistant_id: "",
4463
4510
  thread_id: "",
4464
4511
  tenantId: ctx.tenantId,
@@ -4556,12 +4603,12 @@ ${truncated}`
4556
4603
  }
4557
4604
  };
4558
4605
  }
4559
- var import_zod21, import_core78, MAX_OUTPUT_BYTES;
4606
+ var import_zod21, import_core83, MAX_OUTPUT_BYTES;
4560
4607
  var init_sandboxProvider = __esm({
4561
4608
  "src/services/open/providers/sandboxProvider.ts"() {
4562
4609
  "use strict";
4563
4610
  import_zod21 = require("zod");
4564
- import_core78 = require("@axiom-lattice/core");
4611
+ import_core83 = require("@axiom-lattice/core");
4565
4612
  MAX_OUTPUT_BYTES = 64e3;
4566
4613
  }
4567
4614
  });
@@ -4583,12 +4630,12 @@ function safePath(raw) {
4583
4630
  }
4584
4631
  function registerOpenFilesRoutes(app2, deps = {}) {
4585
4632
  const openAuth = deps.openAuth ?? new OpenCredentialService(
4586
- (0, import_core79.getStoreLattice)("default", "a2aApiKey").store,
4587
- (0, import_core79.getStoreLattice)("default", "project").store
4633
+ (0, import_core84.getStoreLattice)("default", "a2aApiKey").store,
4634
+ (0, import_core84.getStoreLattice)("default", "project").store
4588
4635
  );
4589
4636
  const audit = deps.audit ?? (() => {
4590
4637
  try {
4591
- const store2 = (0, import_core79.getStoreLattice)("default", "openAudit").store;
4638
+ const store2 = (0, import_core84.getStoreLattice)("default", "openAudit").store;
4592
4639
  return new OpenAuditService(store2);
4593
4640
  } catch {
4594
4641
  return void 0;
@@ -4698,11 +4745,11 @@ function registerOpenFilesRoutes(app2, deps = {}) {
4698
4745
  }
4699
4746
  });
4700
4747
  }
4701
- var import_core79;
4748
+ var import_core84;
4702
4749
  var init_open_files = __esm({
4703
4750
  "src/routes/open-files.ts"() {
4704
4751
  "use strict";
4705
- import_core79 = require("@axiom-lattice/core");
4752
+ import_core84 = require("@axiom-lattice/core");
4706
4753
  init_openCredentialService();
4707
4754
  init_openLattice();
4708
4755
  init_sandboxProvider();
@@ -4724,7 +4771,7 @@ function registerOpenAuditRoutes(app2) {
4724
4771
  return reply.status(401).send({ success: false, error: "Unauthorized" });
4725
4772
  }
4726
4773
  const status = request.query.status === "ok" || request.query.status === "error" ? request.query.status : void 0;
4727
- const store2 = (0, import_core80.getStoreLattice)("default", "openAudit").store;
4774
+ const store2 = (0, import_core85.getStoreLattice)("default", "openAudit").store;
4728
4775
  const records = await store2.query({
4729
4776
  tenantId,
4730
4777
  credentialId: request.query.credentialId,
@@ -4739,11 +4786,11 @@ function registerOpenAuditRoutes(app2) {
4739
4786
  }
4740
4787
  );
4741
4788
  }
4742
- var import_core80;
4789
+ var import_core85;
4743
4790
  var init_open_audit = __esm({
4744
4791
  "src/routes/open-audit.ts"() {
4745
4792
  "use strict";
4746
- import_core80 = require("@axiom-lattice/core");
4793
+ import_core85 = require("@axiom-lattice/core");
4747
4794
  }
4748
4795
  });
4749
4796
 
@@ -4760,7 +4807,7 @@ function registerOpenExposureRoutes(app2, deps = {}) {
4760
4807
  if (!tenantId) {
4761
4808
  return reply.status(401).send({ success: false, error: "Unauthorized" });
4762
4809
  }
4763
- const keyStore = (0, import_core81.getStoreLattice)("default", "a2aApiKey").store;
4810
+ const keyStore = (0, import_core86.getStoreLattice)("default", "a2aApiKey").store;
4764
4811
  const record = await keyStore.findById(request.params.id);
4765
4812
  if (!record || record.tenantId !== tenantId) {
4766
4813
  return reply.status(404).send({ success: false, error: "Key not found" });
@@ -4794,7 +4841,7 @@ function registerOpenExposureRoutes(app2, deps = {}) {
4794
4841
  hasUi: Boolean(t2.ui)
4795
4842
  }));
4796
4843
  const agentGrant = grants.find((g) => g.domain === "agent");
4797
- const assistantStore = (0, import_core81.getStoreLattice)("default", "assistant").store;
4844
+ const assistantStore = (0, import_core86.getStoreLattice)("default", "assistant").store;
4798
4845
  const agents = [];
4799
4846
  for (const assistantId of agentGrant?.items ?? []) {
4800
4847
  const assistant = await assistantStore.getAssistantById(
@@ -4829,11 +4876,11 @@ function registerOpenExposureRoutes(app2, deps = {}) {
4829
4876
  }
4830
4877
  );
4831
4878
  }
4832
- var import_core81, import_protocols38;
4879
+ var import_core86, import_protocols38;
4833
4880
  var init_open_exposure = __esm({
4834
4881
  "src/routes/open-exposure.ts"() {
4835
4882
  "use strict";
4836
- import_core81 = require("@axiom-lattice/core");
4883
+ import_core86 = require("@axiom-lattice/core");
4837
4884
  import_protocols38 = require("@axiom-lattice/protocols");
4838
4885
  init_openLattice();
4839
4886
  init_tenantOpenPlugins();
@@ -4953,7 +5000,7 @@ function createAgentProvider(deps = {}) {
4953
5000
  } catch {
4954
5001
  }
4955
5002
  }
4956
- const agent = import_core82.agentInstanceManager.getAgent({
5003
+ const agent = import_core87.agentInstanceManager.getAgent({
4957
5004
  tenant_id: ctx.tenantId,
4958
5005
  assistant_id: assistantId,
4959
5006
  thread_id: threadId,
@@ -4978,17 +5025,292 @@ function createAgentProvider(deps = {}) {
4978
5025
  }
4979
5026
  };
4980
5027
  }
4981
- var import_zod23, import_core82, import_protocols39, STOP_TYPES;
5028
+ var import_zod23, import_core87, import_protocols39, STOP_TYPES;
4982
5029
  var init_agentProvider = __esm({
4983
5030
  "src/services/open/providers/agentProvider.ts"() {
4984
5031
  "use strict";
4985
5032
  import_zod23 = require("zod");
4986
- import_core82 = require("@axiom-lattice/core");
5033
+ import_core87 = require("@axiom-lattice/core");
4987
5034
  import_protocols39 = require("@axiom-lattice/protocols");
4988
5035
  STOP_TYPES = [import_protocols39.MessageChunkTypes.MESSAGE_COMPLETED];
4989
5036
  }
4990
5037
  });
4991
5038
 
5039
+ // src/services/open/sandboxOpenReflection.ts
5040
+ function cloneGrants(grants) {
5041
+ return grants.map((grant) => ({
5042
+ ...grant,
5043
+ items: grant.items ? [...grant.items] : void 0
5044
+ }));
5045
+ }
5046
+ function normalizeOpenExpose(value) {
5047
+ if (!Array.isArray(value)) return [];
5048
+ const out = [];
5049
+ for (const entry of value) {
5050
+ if (typeof entry === "string") {
5051
+ if (entry) out.push(entry);
5052
+ } else if (entry && typeof entry === "object") {
5053
+ const name = entry.name;
5054
+ if (typeof name === "string" && name) out.push(name);
5055
+ }
5056
+ }
5057
+ return out;
5058
+ }
5059
+ function extractMiddleware(container) {
5060
+ if (!container || typeof container !== "object") return void 0;
5061
+ const raw = container.middleware;
5062
+ if (!Array.isArray(raw)) return void 0;
5063
+ const out = [];
5064
+ for (const entry of raw) {
5065
+ if (!entry || typeof entry !== "object") continue;
5066
+ const type = entry.type;
5067
+ if (typeof type !== "string" || type.length === 0) continue;
5068
+ const enabled = entry.enabled;
5069
+ const rawAllowed = entry.allowedTools;
5070
+ const allowedTools = Array.isArray(rawAllowed) ? rawAllowed.filter((t2) => typeof t2 === "string" && t2.length > 0) : void 0;
5071
+ out.push({ type, enabled: typeof enabled === "boolean" ? enabled : void 0, allowedTools });
5072
+ }
5073
+ return out;
5074
+ }
5075
+ function extractSubAgents(config) {
5076
+ if (!config || typeof config !== "object") return void 0;
5077
+ const raw = config.subAgents;
5078
+ if (!Array.isArray(raw)) return void 0;
5079
+ const out = raw.filter((id3) => typeof id3 === "string" && id3.length > 0);
5080
+ return out.length > 0 ? out : void 0;
5081
+ }
5082
+ async function computeGrants(tenantId, assistantId, deps) {
5083
+ await deps.ensure?.(tenantId);
5084
+ let context = await deps.getAgentConfig?.(tenantId, assistantId);
5085
+ if (context === void 0) {
5086
+ const middleware = await deps.getAssistantMiddleware?.(tenantId, assistantId);
5087
+ if (middleware === void 0) return [];
5088
+ context = { middleware };
5089
+ }
5090
+ const byDomain = /* @__PURE__ */ new Map();
5091
+ const add = (domain, items) => {
5092
+ let bucket = byDomain.get(domain);
5093
+ if (!bucket) {
5094
+ bucket = /* @__PURE__ */ new Set();
5095
+ byDomain.set(domain, bucket);
5096
+ }
5097
+ for (const item of items) bucket.add(item);
5098
+ };
5099
+ for (const entry of context.middleware ?? []) {
5100
+ if (!entry || entry.enabled === false) continue;
5101
+ if (typeof entry.type !== "string" || entry.type.length === 0) continue;
5102
+ let actions = deps.exposedActionsByType(entry.type);
5103
+ if (actions.length === 0) continue;
5104
+ const allowedTools = entry.allowedTools;
5105
+ if (Array.isArray(allowedTools) && allowedTools.length > 0) {
5106
+ const allowed = new Set(allowedTools);
5107
+ actions = actions.filter((action) => allowed.has(action));
5108
+ if (actions.length === 0) continue;
5109
+ }
5110
+ add(entry.type, actions);
5111
+ }
5112
+ const subAgents = context.subAgents ?? [];
5113
+ if (subAgents.length > 0 && deps.resolvableAssistants) {
5114
+ const resolvable = await deps.resolvableAssistants(tenantId);
5115
+ const kept = subAgents.filter(
5116
+ (id3) => typeof id3 === "string" && id3.length > 0 && resolvable.has(id3)
5117
+ );
5118
+ if (kept.length > 0) add("agent", kept);
5119
+ }
5120
+ return [...byDomain].map(([domain, items]) => ({ domain, items: [...items] }));
5121
+ }
5122
+ async function resolveAgentOpenGrants(tenantId, assistantId, deps) {
5123
+ try {
5124
+ const key = `${tenantId}:${assistantId}`;
5125
+ const now = deps.now ? deps.now() : Date.now();
5126
+ const hit = cache.get(key);
5127
+ if (hit && hit.expiresAt > now) return cloneGrants(hit.grants);
5128
+ const grants = await computeGrants(tenantId, assistantId, deps);
5129
+ cache.set(key, { grants, expiresAt: now + AGENT_REFLECTION_CACHE_TTL_MS });
5130
+ return cloneGrants(grants);
5131
+ } catch {
5132
+ return [];
5133
+ }
5134
+ }
5135
+ function createDefaultAgentReflectionDeps(tenantId) {
5136
+ (0, import_core88.ensureBuiltinAgentsForTenant)(tenantId);
5137
+ return {
5138
+ ensure: (t2) => import_core88.agentLatticeManager.initializeStoredAssistantsForTenant(t2),
5139
+ getAgentConfig: (t2, assistantId) => {
5140
+ const config = import_core88.agentLatticeManager.getAgentConfigWithTenant(t2, assistantId);
5141
+ if (!config) return void 0;
5142
+ return {
5143
+ middleware: extractMiddleware(config),
5144
+ subAgents: extractSubAgents(config)
5145
+ };
5146
+ },
5147
+ getAssistantMiddleware: async (t2, assistantId) => {
5148
+ const store2 = (0, import_core88.getStoreLattice)("default", "assistant").store;
5149
+ const assistant = await store2.getAssistantById(t2, assistantId);
5150
+ return extractMiddleware(assistant?.graphDefinition);
5151
+ },
5152
+ exposedActionsByType: (type) => {
5153
+ const plugin = import_core88.PluginRegistry.get(type);
5154
+ return plugin ? normalizeOpenExpose(plugin.meta.openExpose) : [];
5155
+ },
5156
+ resolvableAssistants: async (t2) => new Set(
5157
+ import_core88.agentLatticeManager.getAllAgentLatticesByTenant(t2).map((lattice) => lattice.config.key)
5158
+ )
5159
+ };
5160
+ }
5161
+ function mergeGrantsByDomain(grants) {
5162
+ const order = [];
5163
+ const items = /* @__PURE__ */ new Map();
5164
+ const unrestricted = /* @__PURE__ */ new Set();
5165
+ for (const grant of grants) {
5166
+ if (!grant || typeof grant.domain !== "string" || grant.domain.length === 0) continue;
5167
+ if (!items.has(grant.domain)) {
5168
+ items.set(grant.domain, /* @__PURE__ */ new Set());
5169
+ order.push(grant.domain);
5170
+ }
5171
+ if (!grant.items || grant.items.length === 0) {
5172
+ unrestricted.add(grant.domain);
5173
+ continue;
5174
+ }
5175
+ const bucket = items.get(grant.domain);
5176
+ if (!bucket) continue;
5177
+ for (const item of grant.items) {
5178
+ if (typeof item === "string" && item.length > 0) bucket.add(item);
5179
+ }
5180
+ }
5181
+ return order.map((domain) => {
5182
+ if (unrestricted.has(domain)) return { domain };
5183
+ return { domain, items: [...items.get(domain) ?? []] };
5184
+ });
5185
+ }
5186
+ async function computePluginGrants(tenantId, deps) {
5187
+ const grants = [...deps.globalExposedDomains?.() ?? []];
5188
+ if (deps.tenantExposedDomains) {
5189
+ grants.push(...await deps.tenantExposedDomains(tenantId));
5190
+ }
5191
+ if (deps.resolvableAssistants) {
5192
+ const resolvable = await deps.resolvableAssistants(tenantId);
5193
+ if (resolvable.size > 0) {
5194
+ grants.push({ domain: "agent", items: [...resolvable] });
5195
+ }
5196
+ }
5197
+ return mergeGrantsByDomain(grants);
5198
+ }
5199
+ async function resolvePluginOpenGrants(tenantId, deps) {
5200
+ try {
5201
+ const now = deps.now ? deps.now() : Date.now();
5202
+ const hit = pluginCache.get(tenantId);
5203
+ if (hit && hit.expiresAt > now) return cloneGrants(hit.grants);
5204
+ await deps.ensure?.(tenantId);
5205
+ const grants = await computePluginGrants(tenantId, deps);
5206
+ pluginCache.set(tenantId, { grants, expiresAt: now + PLUGIN_REFLECTION_CACHE_TTL_MS });
5207
+ return cloneGrants(grants);
5208
+ } catch {
5209
+ return [];
5210
+ }
5211
+ }
5212
+ function buildTenantExposedGrants(manifests) {
5213
+ const grants = [];
5214
+ for (const manifest of manifests) {
5215
+ const items = (manifest.tools ?? []).filter((tool) => tool.expose === true).map((tool) => tool.name);
5216
+ if (items.length > 0) grants.push({ domain: manifest.type, items });
5217
+ }
5218
+ return grants;
5219
+ }
5220
+ function createDefaultPluginReflectionDeps() {
5221
+ return {
5222
+ ensure: (tenantId) => import_core88.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
5223
+ globalExposedDomains: () => {
5224
+ const grants = [];
5225
+ for (const type of import_core88.PluginRegistry.list()) {
5226
+ const plugin = import_core88.PluginRegistry.get(type);
5227
+ const items = normalizeOpenExpose(plugin?.meta.openExpose);
5228
+ if (items.length > 0) grants.push({ domain: type, items });
5229
+ }
5230
+ return grants;
5231
+ },
5232
+ tenantExposedDomains: async (tenantId) => {
5233
+ const { manifests } = await (0, import_core88.getSandboxPluginCatalog)().ensureLoaded(tenantId);
5234
+ return buildTenantExposedGrants(Object.values(manifests));
5235
+ },
5236
+ resolvableAssistants: async (tenantId) => new Set(
5237
+ import_core88.agentLatticeManager.getAllAgentLatticesByTenant(tenantId).map((lattice) => lattice.config.key)
5238
+ )
5239
+ };
5240
+ }
5241
+ var import_core88, AGENT_REFLECTION_CACHE_TTL_MS, cache, PLUGIN_REFLECTION_CACHE_TTL_MS, pluginCache;
5242
+ var init_sandboxOpenReflection = __esm({
5243
+ "src/services/open/sandboxOpenReflection.ts"() {
5244
+ "use strict";
5245
+ import_core88 = require("@axiom-lattice/core");
5246
+ AGENT_REFLECTION_CACHE_TTL_MS = 6e4;
5247
+ cache = /* @__PURE__ */ new Map();
5248
+ PLUGIN_REFLECTION_CACHE_TTL_MS = 6e4;
5249
+ pluginCache = /* @__PURE__ */ new Map();
5250
+ }
5251
+ });
5252
+
5253
+ // src/services/open/sandboxOpenCredentialProvider.ts
5254
+ var sandboxOpenCredentialProvider_exports = {};
5255
+ __export(sandboxOpenCredentialProvider_exports, {
5256
+ DEFAULT_SANDBOX_OPEN_GRANTS: () => DEFAULT_SANDBOX_OPEN_GRANTS,
5257
+ MAX_SANDBOX_OPEN_DEPTH: () => MAX_SANDBOX_OPEN_DEPTH,
5258
+ createSandboxOpenCredentialProvider: () => createSandboxOpenCredentialProvider,
5259
+ registerGatewaySandboxOpenCredentialProvider: () => registerGatewaySandboxOpenCredentialProvider
5260
+ });
5261
+ function defaultReflection() {
5262
+ return {
5263
+ agent: (tenantId, assistantId) => resolveAgentOpenGrants(tenantId, assistantId, createDefaultAgentReflectionDeps(tenantId)),
5264
+ plugin: (tenantId) => resolvePluginOpenGrants(tenantId, createDefaultPluginReflectionDeps())
5265
+ };
5266
+ }
5267
+ function createSandboxOpenCredentialProvider(options) {
5268
+ const reflection = options.reflection ?? defaultReflection();
5269
+ return async (context) => {
5270
+ if (!options.gatewayUrl) return void 0;
5271
+ const depth = (context.depth ?? 0) + 1;
5272
+ if (depth > MAX_SANDBOX_OPEN_DEPTH) {
5273
+ options.onError?.(new Error("sandbox open credential depth exceeded"));
5274
+ return void 0;
5275
+ }
5276
+ const reflected = context.assistantId ? await reflection.agent(context.tenantId, context.assistantId) : await reflection.plugin(context.tenantId);
5277
+ const grants = mergeGrantsByDomain([...DEFAULT_SANDBOX_OPEN_GRANTS, ...reflected]);
5278
+ const token = signOpenToken({
5279
+ kind: "sandbox_token",
5280
+ tenantId: context.tenantId,
5281
+ workspaceId: context.workspaceId ?? "",
5282
+ projectId: context.projectId ?? "",
5283
+ sandboxName: context.sandboxName,
5284
+ grants,
5285
+ depth
5286
+ });
5287
+ return { gatewayUrl: options.gatewayUrl, token };
5288
+ };
5289
+ }
5290
+ function registerGatewaySandboxOpenCredentialProvider(onError) {
5291
+ return (0, import_core89.registerSandboxOpenCredentialProvider)(
5292
+ createSandboxOpenCredentialProvider({
5293
+ gatewayUrl: process.env.PUBLIC_GATEWAY_URL?.trim() || void 0,
5294
+ onError
5295
+ }),
5296
+ { onError }
5297
+ );
5298
+ }
5299
+ var import_core89, DEFAULT_SANDBOX_OPEN_GRANTS, MAX_SANDBOX_OPEN_DEPTH;
5300
+ var init_sandboxOpenCredentialProvider = __esm({
5301
+ "src/services/open/sandboxOpenCredentialProvider.ts"() {
5302
+ "use strict";
5303
+ import_core89 = require("@axiom-lattice/core");
5304
+ init_openToken();
5305
+ init_sandboxOpenReflection();
5306
+ DEFAULT_SANDBOX_OPEN_GRANTS = [
5307
+ { domain: "sandbox", items: ["files_read", "files_list", "files_write", "shell_exec"] },
5308
+ { domain: "kb", items: ["search", "entries_read"] }
5309
+ ];
5310
+ MAX_SANDBOX_OPEN_DEPTH = 5;
5311
+ }
5312
+ });
5313
+
4992
5314
  // src/services/open/sandboxGrants.ts
4993
5315
  function sanitizeShareOpenGrants(grants) {
4994
5316
  const out = [];
@@ -5015,12 +5337,12 @@ var resources_exports = {};
5015
5337
  __export(resources_exports, {
5016
5338
  ResourceController: () => ResourceController
5017
5339
  });
5018
- var import_bcryptjs, import_core83, ResourceController;
5340
+ var import_bcryptjs, import_core90, ResourceController;
5019
5341
  var init_resources = __esm({
5020
5342
  "src/controllers/resources.ts"() {
5021
5343
  "use strict";
5022
5344
  import_bcryptjs = __toESM(require("bcryptjs"));
5023
- import_core83 = require("@axiom-lattice/core");
5345
+ import_core90 = require("@axiom-lattice/core");
5024
5346
  init_mime();
5025
5347
  init_http_range();
5026
5348
  init_sandboxGrants();
@@ -5037,14 +5359,14 @@ var init_resources = __esm({
5037
5359
  if (!workspaceId || !projectId) {
5038
5360
  return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
5039
5361
  }
5040
- const payload = (0, import_core83.createSharePayload)(
5362
+ const payload = (0, import_core90.createSharePayload)(
5041
5363
  tenantId,
5042
5364
  workspaceId,
5043
5365
  projectId,
5044
5366
  userId,
5045
5367
  body
5046
5368
  );
5047
- const token = (0, import_core83.generateToken)();
5369
+ const token = (0, import_core90.generateToken)();
5048
5370
  try {
5049
5371
  await this.deps.store.create({ ...payload, token });
5050
5372
  this.deps.logger.info(
@@ -5196,7 +5518,7 @@ var init_resources = __esm({
5196
5518
  resourcePath: subPath
5197
5519
  }, subPath, ext, request, reply);
5198
5520
  }
5199
- const volume = (0, import_core83.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
5521
+ const volume = (0, import_core90.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
5200
5522
  return this._resolveAndServe(
5201
5523
  volume,
5202
5524
  subPath,
@@ -5408,8 +5730,13 @@ var init_resources = __esm({
5408
5730
  var index_exports = {};
5409
5731
  __export(index_exports, {
5410
5732
  LatticeGateway: () => LatticeGateway,
5733
+ ProjectRoomEventBroker: () => ProjectRoomEventBroker,
5734
+ ROOM_EVENT_TOPIC: () => ROOM_EVENT_TOPIC,
5411
5735
  createOrphanRecoveryRuntime: () => createOrphanRecoveryRuntime,
5412
5736
  createRecoveryCallerDeps: () => createRecoveryCallerDeps,
5737
+ createTaskRunLiveness: () => createTaskRunLiveness,
5738
+ ingestRoomEvent: () => ingestRoomEvent,
5739
+ publishRoomEvent: () => publishRoomEvent,
5413
5740
  recoverTask: () => recoverTask,
5414
5741
  recoverTopCaller: () => recoverTopCaller,
5415
5742
  resolveTopCaller: () => resolveTopCaller,
@@ -6324,6 +6651,24 @@ async function writeSse(raw, value, closed) {
6324
6651
  }
6325
6652
 
6326
6653
  // src/controllers/run.ts
6654
+ var replicaHeader = () => ({ [import_protocols2.REPLICA_HEADER]: (0, import_core3.getReplicaId)() });
6655
+ var exposeHeaders = `${import_protocols2.REPLICA_HEADER}, ${import_protocols2.AFFINITY_ECHO_HEADER}`;
6656
+ function applyAffinityEcho(request, reply) {
6657
+ for (const [name, value] of Object.entries((0, import_protocols2.affinityEchoHeaders)(request.headers[import_protocols2.AGENT_AFFINITY_HEADER]))) {
6658
+ reply.header(name, value);
6659
+ }
6660
+ }
6661
+ var AFFINITY_GRACE_MS = Number.isFinite(Number(process.env.AXIOM_AFFINITY_GRACE_MS)) ? Number(process.env.AXIOM_AFFINITY_GRACE_MS) : 500;
6662
+ async function peerOwnsThread(input) {
6663
+ if (!process.env.AXIOM_REPLICA_ID) return null;
6664
+ if (!input.tenant_id || !input.assistant_id || !input.thread_id) return null;
6665
+ const owner = await (0, import_core3.getThreadRunOwner)({
6666
+ tenantId: input.tenant_id,
6667
+ assistantId: input.assistant_id,
6668
+ threadId: input.thread_id
6669
+ });
6670
+ return owner && owner !== (0, import_core3.getReplicaId)() ? owner : null;
6671
+ }
6327
6672
  function getUserId2(request) {
6328
6673
  const authUser = request.user;
6329
6674
  if (authUser?.id) return authUser.id;
@@ -6350,6 +6695,7 @@ var createRun = async (request, reply) => {
6350
6695
  ...user_id && { user_id }
6351
6696
  };
6352
6697
  if (!assistant_id) {
6698
+ applyAffinityEcho(request, reply);
6353
6699
  reply.status(400).send({
6354
6700
  success: false,
6355
6701
  error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
@@ -6371,6 +6717,8 @@ var createRun = async (request, reply) => {
6371
6717
  command,
6372
6718
  custom_run_config: mergedConfig
6373
6719
  }, mode);
6720
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6721
+ applyAffinityEcho(request, reply);
6374
6722
  reply.status(202).send({
6375
6723
  success: true,
6376
6724
  messageId: result.messageId,
@@ -6384,7 +6732,10 @@ var createRun = async (request, reply) => {
6384
6732
  "Content-Type": "text/event-stream",
6385
6733
  "Cache-Control": "no-cache",
6386
6734
  Connection: "keep-alive",
6387
- "Access-Control-Allow-Origin": "*"
6735
+ "Access-Control-Allow-Origin": "*",
6736
+ "Access-Control-Expose-Headers": exposeHeaders,
6737
+ ...replicaHeader(),
6738
+ ...(0, import_protocols2.affinityEchoHeaders)(request.headers[import_protocols2.AGENT_AFFINITY_HEADER])
6388
6739
  });
6389
6740
  try {
6390
6741
  const messageInput = message_id ? { ...input, id: message_id } : input;
@@ -6395,6 +6746,18 @@ var createRun = async (request, reply) => {
6395
6746
  }, [import_protocols2.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols2.MessageChunkTypes.INTERRUPT, import_protocols2.MessageChunkTypes.MESSAGE_FAILED], {
6396
6747
  queueMode: mode
6397
6748
  });
6749
+ let peer = await peerOwnsThread({ tenant_id, assistant_id, thread_id });
6750
+ if (peer) {
6751
+ await new Promise((resolve4) => setTimeout(resolve4, AFFINITY_GRACE_MS));
6752
+ peer = await peerOwnsThread({ tenant_id, assistant_id, thread_id });
6753
+ }
6754
+ if (peer) {
6755
+ reply.raw.write(`data: ${JSON.stringify((0, import_protocols2.buildAffinityMissChunk)(peer, thread_id))}
6756
+
6757
+ `);
6758
+ reply.raw.end();
6759
+ return;
6760
+ }
6398
6761
  await pipeAsyncIterableToSse(reply.raw, result.stream, {
6399
6762
  onError: (error) => [{
6400
6763
  type: "error",
@@ -6426,12 +6789,15 @@ var createRun = async (request, reply) => {
6426
6789
  command,
6427
6790
  custom_run_config: mergedConfig
6428
6791
  });
6792
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6793
+ applyAffinityEcho(request, reply);
6429
6794
  reply.status(200).send({
6430
6795
  success: true,
6431
6796
  ...result
6432
6797
  });
6433
6798
  }
6434
6799
  } catch (error) {
6800
+ applyAffinityEcho(request, reply);
6435
6801
  reply.status(500).send({
6436
6802
  success: false,
6437
6803
  error: `\u521B\u5EFA\u8FD0\u884C\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
@@ -6454,9 +6820,20 @@ var resumeStream = async (request, reply) => {
6454
6820
  "Content-Type": "text/event-stream",
6455
6821
  "Cache-Control": "no-cache",
6456
6822
  Connection: "keep-alive",
6457
- "Access-Control-Allow-Origin": "*"
6823
+ "Access-Control-Allow-Origin": "*",
6824
+ "Access-Control-Expose-Headers": exposeHeaders,
6825
+ ...replicaHeader(),
6826
+ ...(0, import_protocols2.affinityEchoHeaders)(request.headers[import_protocols2.AGENT_AFFINITY_HEADER])
6458
6827
  });
6459
6828
  try {
6829
+ const peer = await peerOwnsThread({ tenant_id, assistant_id, thread_id });
6830
+ if (peer) {
6831
+ reply.raw.write(`data: ${JSON.stringify((0, import_protocols2.buildAffinityMissChunk)(peer, thread_id))}
6832
+
6833
+ `);
6834
+ reply.raw.end();
6835
+ return;
6836
+ }
6460
6837
  const agent = import_core3.agentInstanceManager.getAgent({
6461
6838
  assistant_id,
6462
6839
  thread_id,
@@ -6507,12 +6884,22 @@ var abortRun = async (request, reply) => {
6507
6884
  workspace_id,
6508
6885
  project_id
6509
6886
  });
6887
+ const peer = await peerOwnsThread({ tenant_id, assistant_id: assistantId, thread_id: threadId });
6888
+ if (peer) {
6889
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6890
+ applyAffinityEcho(request, reply);
6891
+ reply.status(409).send({ success: false, code: import_protocols2.AFFINITY_MISS_CODE, owner: peer, threadId });
6892
+ return;
6893
+ }
6510
6894
  await agent.abort();
6895
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6896
+ applyAffinityEcho(request, reply);
6511
6897
  reply.status(200).send({
6512
6898
  success: true,
6513
6899
  message: "Agent aborted"
6514
6900
  });
6515
6901
  } catch (error) {
6902
+ applyAffinityEcho(request, reply);
6516
6903
  reply.status(500).send({
6517
6904
  success: false,
6518
6905
  error: `Abort failed: ${error.message}`
@@ -6530,13 +6917,17 @@ var recoverRun = async (request, reply) => {
6530
6917
  workspace_id,
6531
6918
  project_id
6532
6919
  });
6533
- const { status } = await agent.resumeTask();
6920
+ const { status, leaseContended } = await agent.resumeTask();
6921
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6922
+ applyAffinityEcho(request, reply);
6534
6923
  reply.status(200).send({
6535
6924
  success: true,
6536
6925
  threadId,
6537
- status
6926
+ status,
6927
+ ...leaseContended ? { code: import_protocols2.AFFINITY_MISS_CODE } : {}
6538
6928
  });
6539
6929
  } catch (error) {
6930
+ applyAffinityEcho(request, reply);
6540
6931
  reply.status(500).send({
6541
6932
  success: false,
6542
6933
  error: `Recover failed: ${error.message}`
@@ -13101,6 +13492,43 @@ function connectionRoutes(fastify2) {
13101
13492
  // src/controllers/mcp-configs.ts
13102
13493
  var import_core33 = require("@axiom-lattice/core");
13103
13494
  var import_crypto8 = require("crypto");
13495
+
13496
+ // src/services/mcpConfigSync.ts
13497
+ var MCP_CONFIG_CHANGED_TOPIC = "mcp-config-changed";
13498
+ async function publishMcpConfigChanged(outbox, owner, tenantId, serverKey) {
13499
+ if (!tenantId || !serverKey) return;
13500
+ try {
13501
+ await outbox.append({
13502
+ topic: MCP_CONFIG_CHANGED_TOPIC,
13503
+ payload: { tenantId, serverKey },
13504
+ originReplicaId: owner
13505
+ });
13506
+ } catch {
13507
+ }
13508
+ }
13509
+ async function reconcileMcpConfigChange(payload, deps) {
13510
+ const tenantId = payload?.tenantId;
13511
+ const serverKey = payload?.serverKey;
13512
+ if (typeof tenantId !== "string" || tenantId.length === 0) return;
13513
+ if (typeof serverKey !== "string" || serverKey.length === 0) return;
13514
+ const config = await deps.store.getConfigByKey(tenantId, serverKey);
13515
+ if (!config || config.status !== "connected") {
13516
+ if (deps.manager.hasServer(tenantId, serverKey)) {
13517
+ await deps.manager.removeServer(tenantId, serverKey);
13518
+ }
13519
+ return;
13520
+ }
13521
+ const connection = deps.convertToConnection(config.config);
13522
+ deps.manager.registerServer(tenantId, serverKey, connection, {
13523
+ timeoutMs: config.config.timeout,
13524
+ maxAttempts: config.config.retryAttempts,
13525
+ selectedTools: config.selectedTools
13526
+ });
13527
+ await deps.manager.ensureConnected(tenantId, serverKey);
13528
+ await deps.manager.registerToolsToToolLattice(tenantId, serverKey, config.selectedTools);
13529
+ }
13530
+
13531
+ // src/controllers/mcp-configs.ts
13104
13532
  function getTenantId11(request) {
13105
13533
  const userTenantId = request.user?.tenantId;
13106
13534
  if (userTenantId) {
@@ -13108,6 +13536,14 @@ function getTenantId11(request) {
13108
13536
  }
13109
13537
  return request.headers["x-tenant-id"] || "default";
13110
13538
  }
13539
+ function broadcastMcpConfigChanged(tenantId, serverKey) {
13540
+ if (!tenantId || !serverKey) return;
13541
+ try {
13542
+ const outbox = (0, import_core33.getStoreLattice)("default", "outbox").store;
13543
+ void publishMcpConfigChanged(outbox, (0, import_core33.getReplicaId)(), tenantId, serverKey);
13544
+ } catch {
13545
+ }
13546
+ }
13111
13547
  async function getMcpServerConfigList(request, reply) {
13112
13548
  const tenantId = getTenantId11(request);
13113
13549
  try {
@@ -13185,6 +13621,7 @@ async function createMcpServerConfig(request, reply) {
13185
13621
  await store2.updateConfig(tenantId, id3, { status: "error" });
13186
13622
  config.status = "error";
13187
13623
  }
13624
+ broadcastMcpConfigChanged(tenantId, config.key);
13188
13625
  reply.code(201);
13189
13626
  return {
13190
13627
  success: true,
@@ -13214,7 +13651,9 @@ async function updateMcpServerConfig(request, reply) {
13214
13651
  message: "MCP server configuration not found"
13215
13652
  };
13216
13653
  }
13217
- const shouldReconnect = updates.config !== void 0;
13654
+ const configChanged = updates.config !== void 0;
13655
+ const toolsChanged = updates.selectedTools !== void 0;
13656
+ const statusChanged = updates.status !== void 0;
13218
13657
  const updated = await store2.updateConfig(tenantId, existing.id, updates);
13219
13658
  if (!updated) {
13220
13659
  return {
@@ -13222,7 +13661,7 @@ async function updateMcpServerConfig(request, reply) {
13222
13661
  message: "Failed to update MCP server configuration"
13223
13662
  };
13224
13663
  }
13225
- if (shouldReconnect) {
13664
+ if (configChanged) {
13226
13665
  try {
13227
13666
  const connection = (0, import_core33.convertToConnection)(updated.config);
13228
13667
  import_core33.mcpManager.registerServer(tenantId, key, connection, {
@@ -13238,6 +13677,22 @@ async function updateMcpServerConfig(request, reply) {
13238
13677
  await store2.updateConfig(tenantId, existing.id, { status: "error" });
13239
13678
  updated.status = "error";
13240
13679
  }
13680
+ } else if (toolsChanged && import_core33.mcpManager.hasServer(tenantId, key)) {
13681
+ try {
13682
+ await import_core33.mcpManager.registerToolsToToolLattice(tenantId, key, updated.selectedTools);
13683
+ } catch (error) {
13684
+ console.warn("Failed to re-register MCP tools:", error);
13685
+ }
13686
+ }
13687
+ if (statusChanged && updated.status !== "connected" && import_core33.mcpManager.hasServer(tenantId, key)) {
13688
+ try {
13689
+ await import_core33.mcpManager.removeServer(tenantId, key);
13690
+ } catch (error) {
13691
+ console.warn("Failed to remove MCP server:", error);
13692
+ }
13693
+ }
13694
+ if (configChanged || toolsChanged || statusChanged) {
13695
+ broadcastMcpConfigChanged(tenantId, key);
13241
13696
  }
13242
13697
  return {
13243
13698
  success: true,
@@ -13287,6 +13742,7 @@ async function deleteMcpServerConfig(request, reply) {
13287
13742
  message: "Failed to delete MCP server configuration"
13288
13743
  };
13289
13744
  }
13745
+ broadcastMcpConfigChanged(tenantId, configKey);
13290
13746
  return {
13291
13747
  success: true,
13292
13748
  message: "MCP server configuration deleted successfully"
@@ -13361,12 +13817,17 @@ async function listMcpServerTools(request, reply) {
13361
13817
  message: "MCP server configuration not found"
13362
13818
  };
13363
13819
  }
13364
- if (!import_core33.mcpManager.isServerConnected(tenantId, key)) {
13820
+ const wasConnected = import_core33.mcpManager.isServerConnected(tenantId, key);
13821
+ if (!wasConnected) {
13365
13822
  await connectAndRegisterTools(config);
13366
13823
  }
13367
- if (config.status !== "connected") {
13824
+ const statusStale = config.status !== "connected";
13825
+ if (statusStale) {
13368
13826
  await store2.updateConfig(tenantId, config.id, { status: "connected" });
13369
13827
  }
13828
+ if (!wasConnected || statusStale) {
13829
+ broadcastMcpConfigChanged(tenantId, key);
13830
+ }
13370
13831
  const tools = await import_core33.mcpManager.getServerTools(tenantId, key);
13371
13832
  return {
13372
13833
  success: true,
@@ -13401,6 +13862,7 @@ async function connectMcpServer(request, reply) {
13401
13862
  const updated = await store2.updateConfig(tenantId, config.id, {
13402
13863
  status: "connected"
13403
13864
  });
13865
+ broadcastMcpConfigChanged(tenantId, key);
13404
13866
  return {
13405
13867
  success: true,
13406
13868
  message: "MCP server connected successfully",
@@ -13414,6 +13876,7 @@ async function connectMcpServer(request, reply) {
13414
13876
  if (config) {
13415
13877
  await store2.updateConfig(tenantId, config.id, { status: "error" });
13416
13878
  }
13879
+ broadcastMcpConfigChanged(tenantId, key);
13417
13880
  return {
13418
13881
  success: false,
13419
13882
  message: `Failed to connect MCP server: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -13440,6 +13903,7 @@ async function disconnectMcpServer(request, reply) {
13440
13903
  const updated = await store2.updateConfig(tenantId, config.id, {
13441
13904
  status: "disconnected"
13442
13905
  });
13906
+ broadcastMcpConfigChanged(tenantId, key);
13443
13907
  return {
13444
13908
  success: true,
13445
13909
  message: "MCP server disconnected successfully",
@@ -15958,8 +16422,8 @@ function snapshotPlainObject(value) {
15958
16422
  }
15959
16423
  }
15960
16424
  async function getInstallationStore() {
15961
- const { getStoreLattice: getStoreLattice37 } = await import("@axiom-lattice/core");
15962
- const store2 = getStoreLattice37("default", "channelInstallation").store;
16425
+ const { getStoreLattice: getStoreLattice39 } = await import("@axiom-lattice/core");
16426
+ const store2 = getStoreLattice39("default", "channelInstallation").store;
15963
16427
  if (store2) return store2;
15964
16428
  const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
15965
16429
  const databaseUrl = process.env.DATABASE_URL;
@@ -17101,7 +17565,7 @@ function registerTaskRoutes(app2, controller) {
17101
17565
  }
17102
17566
 
17103
17567
  // src/routes/index.ts
17104
- var import_core51 = require("@axiom-lattice/core");
17568
+ var import_core54 = require("@axiom-lattice/core");
17105
17569
 
17106
17570
  // src/controllers/tasks.ts
17107
17571
  var import_protocols13 = require("@axiom-lattice/protocols");
@@ -17680,6 +18144,10 @@ var ProjectTaskExecutionService = class {
17680
18144
  input.inputMessageId,
17681
18145
  true
17682
18146
  );
18147
+ if (resumed.leaseContended) {
18148
+ settlement.cleanup();
18149
+ return { kind: "deferred", task: input.task, thread: input.thread, messageId: input.inputMessageId };
18150
+ }
17683
18151
  if (resumed.status === import_core44.ThreadStatus.INTERRUPTED && settlement.tryClaim()) {
17684
18152
  settlement.cleanup();
17685
18153
  const outcome2 = await this.settleInterruptedEvent(input.task, input.thread, input.inputMessageId);
@@ -19368,6 +19836,7 @@ var import_zod6 = require("zod");
19368
19836
  // src/services/agent-web-app/AgentWebAppRuntimeService.ts
19369
19837
  var import_node_crypto10 = require("crypto");
19370
19838
  var import_protocols14 = require("@axiom-lattice/protocols");
19839
+ var import_core46 = require("@axiom-lattice/core");
19371
19840
  init_task_file_refs();
19372
19841
  function isRecord2(value) {
19373
19842
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -19530,6 +19999,20 @@ var AgentWebAppRuntimeService = class {
19530
19999
  async resumeStream(webAppId, userId, threadId, messageId, selection) {
19531
20000
  const runtime = await this.resolveRuntime(webAppId, selection);
19532
20001
  await this.requireOwnedThread(runtime, userId, threadId);
20002
+ if (process.env.AXIOM_REPLICA_ID) {
20003
+ const owner = await (0, import_core46.getThreadRunOwner)({
20004
+ tenantId: runtime.webApp.tenantId,
20005
+ assistantId: runtime.webApp.assistantId,
20006
+ threadId
20007
+ });
20008
+ if (owner && owner !== (0, import_core46.getReplicaId)()) {
20009
+ return {
20010
+ stream: (async function* () {
20011
+ yield (0, import_protocols14.buildAffinityMissChunk)(owner, threadId);
20012
+ })()
20013
+ };
20014
+ }
20015
+ }
19533
20016
  const agent = this.resolveAgent(runtime, threadId);
19534
20017
  return { stream: agent.chunkStream(messageId, []) };
19535
20018
  }
@@ -19875,6 +20358,7 @@ async function verifyWebAppToken(token, issuer, options = {}) {
19875
20358
 
19876
20359
  // src/routes/agent-web-app-runtime.ts
19877
20360
  var import_protocols15 = require("@axiom-lattice/protocols");
20361
+ var import_core47 = require("@axiom-lattice/core");
19878
20362
  var idSchema = import_zod6.z.string().min(1).max(128);
19879
20363
  var paramsSchema = import_zod6.z.object({ webAppId: idSchema }).strict();
19880
20364
  var threadParamsSchema = import_zod6.z.object({ webAppId: idSchema, threadId: idSchema }).strict();
@@ -20023,7 +20507,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20023
20507
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
20024
20508
  const input = streamBodySchema.parse(request.body);
20025
20509
  const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
20026
- beginSse(reply);
20510
+ beginSse(reply, request);
20027
20511
  await pipeNativeWebAppStream(reply.raw, result.stream);
20028
20512
  } catch (error) {
20029
20513
  return normalizeError2(reply, error);
@@ -20035,7 +20519,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20035
20519
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
20036
20520
  const input = streamBodySchema.parse(request.body);
20037
20521
  const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
20038
- beginSse(reply);
20522
+ beginSse(reply, request);
20039
20523
  await pipeAgentWebAppStream(reply.raw, result.stream, result);
20040
20524
  } catch (error) {
20041
20525
  return normalizeError2(reply, error);
@@ -20047,7 +20531,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20047
20531
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
20048
20532
  const { messageId, projectId, modelKey: modelKey2 } = resumeSchema.parse(request.body);
20049
20533
  const result = await deps.service.resumeStream(webAppId, identity.userId, threadId, messageId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview));
20050
- beginSse(reply);
20534
+ beginSse(reply, request);
20051
20535
  await pipeNativeWebAppStream(reply.raw, result.stream);
20052
20536
  } catch (error) {
20053
20537
  return normalizeError2(reply, error);
@@ -20080,7 +20564,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20080
20564
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
20081
20565
  const { projectId, modelKey: modelKey2, message, response } = resumeBodySchema.parse(request.body);
20082
20566
  const result = await deps.service.resumeInterrupt(webAppId, identity.userId, threadId, interruptId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview), { message, response });
20083
- beginSse(reply);
20567
+ beginSse(reply, request);
20084
20568
  await pipeAgentWebAppStream(reply.raw, result.stream, result);
20085
20569
  } catch (error) {
20086
20570
  return normalizeError2(reply, error);
@@ -20308,6 +20792,10 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20308
20792
  if (!threadId) throw new AgentWebAppRuntimeError("INVALID_REQUEST", 400, "thread_id is required");
20309
20793
  const content = body.messages.map((message) => message.content).join("\n");
20310
20794
  const { messageId } = await deps.service.enqueueMessage(webAppId, identity.userId, threadId, content, { projectId: body.projectId, modelKey: body.modelKey });
20795
+ reply.header(import_protocols15.REPLICA_HEADER, (0, import_core47.getReplicaId)());
20796
+ for (const [name, value] of Object.entries((0, import_protocols15.affinityEchoHeaders)(request.headers[import_protocols15.AGENT_AFFINITY_HEADER]))) {
20797
+ reply.header(name, value);
20798
+ }
20311
20799
  return reply.code(202).send({ success: true, messageId, queued: true });
20312
20800
  } catch (error) {
20313
20801
  return normalizeError2(reply, error);
@@ -20328,7 +20816,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20328
20816
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
20329
20817
  const body = resumeStreamSchema2.parse(request.body ?? {});
20330
20818
  const result = await deps.service.resumeStream(webAppId, identity.userId, body.thread_id, body.message_id, { projectId: body.projectId, modelKey: body.modelKey });
20331
- beginSse(reply);
20819
+ beginSse(reply, request);
20332
20820
  await pipeNativeWebAppStream(reply.raw, result.stream);
20333
20821
  } catch (error) {
20334
20822
  return normalizeError2(reply, error);
@@ -20381,14 +20869,17 @@ async function pipeNativeWebAppStream(raw, stream) {
20381
20869
  }]
20382
20870
  });
20383
20871
  }
20384
- function beginSse(reply) {
20872
+ function beginSse(reply, request) {
20385
20873
  reply.hijack();
20386
20874
  reply.raw.writeHead(200, {
20387
20875
  "Content-Type": "text/event-stream; charset=utf-8",
20388
20876
  "Cache-Control": "no-cache, no-transform",
20389
20877
  Connection: "keep-alive",
20390
20878
  "X-Accel-Buffering": "no",
20391
- "Access-Control-Allow-Origin": "*"
20879
+ "Access-Control-Allow-Origin": "*",
20880
+ "Access-Control-Expose-Headers": `${import_protocols15.REPLICA_HEADER}, ${import_protocols15.AFFINITY_ECHO_HEADER}`,
20881
+ [import_protocols15.REPLICA_HEADER]: (0, import_core47.getReplicaId)(),
20882
+ ...(0, import_protocols15.affinityEchoHeaders)(request.headers[import_protocols15.AGENT_AFFINITY_HEADER])
20392
20883
  });
20393
20884
  }
20394
20885
  async function handle2(reply, operation) {
@@ -20624,7 +21115,7 @@ var import_zod9 = require("zod");
20624
21115
 
20625
21116
  // src/services/capability/CapabilityBundleService.ts
20626
21117
  var import_zod8 = require("zod");
20627
- var import_core46 = require("@axiom-lattice/core");
21118
+ var import_core48 = require("@axiom-lattice/core");
20628
21119
  var CapabilityBundleServiceError = class extends Error {
20629
21120
  constructor(code, message, statusCode) {
20630
21121
  super(message);
@@ -20665,7 +21156,7 @@ function projectBundleIds(config) {
20665
21156
  return [...value];
20666
21157
  }
20667
21158
  function issue(error) {
20668
- if (error instanceof import_core46.CapabilityBundleMergeError || error instanceof import_core46.CapabilityBundleValidationError) return { code: error instanceof import_core46.CapabilityBundleValidationError && error.code === "CAPABILITY_BUNDLE_INVALID_CONFIG" ? "INVALID_BUNDLE_CONFIG" : error.code, message: error.message, bundleId: error instanceof import_core46.CapabilityBundleMergeError ? error.bundleId : void 0, capabilityType: error.capabilityType, fieldPath: error.fieldPath };
21159
+ if (error instanceof import_core48.CapabilityBundleMergeError || error instanceof import_core48.CapabilityBundleValidationError) return { code: error instanceof import_core48.CapabilityBundleValidationError && error.code === "CAPABILITY_BUNDLE_INVALID_CONFIG" ? "INVALID_BUNDLE_CONFIG" : error.code, message: error.message, bundleId: error instanceof import_core48.CapabilityBundleMergeError ? error.bundleId : void 0, capabilityType: error.capabilityType, fieldPath: error.fieldPath };
20669
21160
  return { code: "CAPABILITY_BUNDLE_CONFIGURATION_ERROR", message: error instanceof Error ? error.message : "Invalid capability bundle configuration" };
20670
21161
  }
20671
21162
  var CapabilityBundleService = class {
@@ -20678,7 +21169,7 @@ var CapabilityBundleService = class {
20678
21169
  const errors = [];
20679
21170
  for (const value of values) {
20680
21171
  try {
20681
- (0, import_core46.validateCapabilityBundleInput)(value);
21172
+ (0, import_core48.validateCapabilityBundleInput)(value);
20682
21173
  bundles.push(value);
20683
21174
  } catch {
20684
21175
  errors.push({ bundleId: value.id, code: "INVALID_BUNDLE_CONFIG", message: "Capability bundle record is invalid" });
@@ -20690,7 +21181,7 @@ var CapabilityBundleService = class {
20690
21181
  const value = await this.deps.bundleStore.getById(tenantId, id3);
20691
21182
  if (!value) throw new CapabilityBundleServiceError("BUNDLE_NOT_FOUND", "Capability bundle not found", 404);
20692
21183
  try {
20693
- (0, import_core46.validateCapabilityBundleInput)(value);
21184
+ (0, import_core48.validateCapabilityBundleInput)(value);
20694
21185
  } catch {
20695
21186
  throw new CapabilityBundleServiceError("INVALID_BUNDLE_CONFIG", "Capability bundle record is invalid", 422);
20696
21187
  }
@@ -20698,7 +21189,7 @@ var CapabilityBundleService = class {
20698
21189
  }
20699
21190
  async create(tenantId, input) {
20700
21191
  rejectStableKey(input);
20701
- (0, import_core46.validateCapabilityBundleInput)(input);
21192
+ (0, import_core48.validateCapabilityBundleInput)(input);
20702
21193
  const value = input;
20703
21194
  const name = value.name.trim();
20704
21195
  const base = slugify(name);
@@ -20709,10 +21200,10 @@ var CapabilityBundleService = class {
20709
21200
  let suffix = 2;
20710
21201
  while (keys2.has(key)) key = `${base}-${suffix++}`;
20711
21202
  const normalized = { ...value, key, name };
20712
- (0, import_core46.validateCapabilityBundleInput)(normalized);
21203
+ (0, import_core48.validateCapabilityBundleInput)(normalized);
20713
21204
  try {
20714
21205
  const result = await this.deps.bundleStore.create(tenantId, normalized);
20715
- (0, import_core46.validateCapabilityBundleInput)(result);
21206
+ (0, import_core48.validateCapabilityBundleInput)(result);
20716
21207
  return result;
20717
21208
  } catch (error) {
20718
21209
  if (!isDuplicate(error)) throw error;
@@ -20723,16 +21214,16 @@ var CapabilityBundleService = class {
20723
21214
  }
20724
21215
  async update(tenantId, id3, input) {
20725
21216
  rejectStableKey(input);
20726
- (0, import_core46.validateCapabilityBundleInput)(input, { partial: true });
21217
+ (0, import_core48.validateCapabilityBundleInput)(input, { partial: true });
20727
21218
  if (!input || typeof input !== "object" || Array.isArray(input) || typeof input.expectedUpdatedAt !== "string" || !input.expectedUpdatedAt.trim()) throw new CapabilityBundleServiceError("INVALID_BUNDLE_CONFIG", "expectedUpdatedAt is required for capability bundle updates", 400);
20728
21219
  const value = input;
20729
21220
  const normalized = { ...value, ...value.name === void 0 ? {} : { name: value.name.trim() }, ...Object.prototype.hasOwnProperty.call(value, "description") ? { description: value.description?.trim() || void 0 } : {} };
20730
- (0, import_core46.validateCapabilityBundleInput)(normalized, { partial: true });
21221
+ (0, import_core48.validateCapabilityBundleInput)(normalized, { partial: true });
20731
21222
  try {
20732
21223
  const result = await this.deps.bundleStore.update(tenantId, id3, normalized);
20733
21224
  if (!result) throw new CapabilityBundleServiceError("BUNDLE_NOT_FOUND", "Capability bundle not found", 404);
20734
21225
  if ("status" in result && result.status === "conflict") throw new CapabilityBundleServiceError("BUNDLE_CONFLICT", "Capability bundle changed after it was loaded; reload before saving", 409);
20735
- (0, import_core46.validateCapabilityBundleInput)(result);
21226
+ (0, import_core48.validateCapabilityBundleInput)(result);
20736
21227
  return result;
20737
21228
  } catch (error) {
20738
21229
  if (error instanceof CapabilityBundleServiceError) throw error;
@@ -20767,8 +21258,8 @@ var CapabilityBundleService = class {
20767
21258
  if (missing.length > 0) return { bundleIds: ids, capabilities: [], overrides: [], sources: [], warnings: [], errors: missing.map((id3) => ({ code: "BUNDLE_NOT_FOUND", message: `Persisted capability bundle '${id3}' is missing`, bundleId: id3 })), revision: "" };
20768
21259
  try {
20769
21260
  const ordered = ids.map((id3) => byId.get(id3));
20770
- ordered.forEach((bundle) => (0, import_core46.validateCapabilityBundleInput)(bundle));
20771
- return (0, import_core46.mergeCapabilityBundles)(ordered);
21261
+ ordered.forEach((bundle) => (0, import_core48.validateCapabilityBundleInput)(bundle));
21262
+ return (0, import_core48.mergeCapabilityBundles)(ordered);
20772
21263
  } catch (error) {
20773
21264
  return { bundleIds: ids, capabilities: [], overrides: [], sources: [], warnings: [], errors: [issue(error)], revision: "" };
20774
21265
  }
@@ -20801,7 +21292,7 @@ var CapabilityBundleService = class {
20801
21292
  return ids.map((id3) => {
20802
21293
  const value = byId.get(id3);
20803
21294
  if (!value) throw new CapabilityBundleServiceError("BUNDLE_NOT_FOUND", "Capability bundle not found", 404);
20804
- (0, import_core46.validateCapabilityBundleInput)(value);
21295
+ (0, import_core48.validateCapabilityBundleInput)(value);
20805
21296
  return value;
20806
21297
  });
20807
21298
  }
@@ -20816,7 +21307,7 @@ var CapabilityBundleService = class {
20816
21307
  continue;
20817
21308
  }
20818
21309
  try {
20819
- (0, import_core46.validateCapabilityBundleInput)(bundle);
21310
+ (0, import_core48.validateCapabilityBundleInput)(bundle);
20820
21311
  valid.push(bundle);
20821
21312
  } catch (error) {
20822
21313
  errors.push({ ...issue(error), bundleId: id3 });
@@ -20824,7 +21315,7 @@ var CapabilityBundleService = class {
20824
21315
  }
20825
21316
  let preview;
20826
21317
  try {
20827
- preview = (0, import_core46.mergeCapabilityBundles)(valid);
21318
+ preview = (0, import_core48.mergeCapabilityBundles)(valid);
20828
21319
  } catch (error) {
20829
21320
  preview = { bundleIds: ids, capabilities: [], overrides: [], sources: [], warnings: [], errors: [issue(error)], revision: "" };
20830
21321
  }
@@ -20976,7 +21467,7 @@ var import_zod11 = require("zod");
20976
21467
  // src/services/model-provider/ModelProviderService.ts
20977
21468
  var import_node_crypto14 = require("crypto");
20978
21469
  var import_protocols16 = require("@axiom-lattice/protocols");
20979
- var import_core47 = require("@axiom-lattice/core");
21470
+ var import_core49 = require("@axiom-lattice/core");
20980
21471
 
20981
21472
  // src/services/model-provider/errors.ts
20982
21473
  var ModelProviderServiceError = class extends Error {
@@ -20989,6 +21480,17 @@ var ModelProviderServiceError = class extends Error {
20989
21480
  this.name = "ModelProviderServiceError";
20990
21481
  }
20991
21482
  };
21483
+ var UPSTREAM_ERROR_CODES = {
21484
+ UPSTREAM_UNREACHABLE: true,
21485
+ UPSTREAM_AUTH_FAILED: true,
21486
+ UPSTREAM_REDIRECT_BLOCKED: true,
21487
+ UPSTREAM_INVALID_RESPONSE: true,
21488
+ UPSTREAM_RATE_LIMITED: true,
21489
+ UPSTREAM_TIMEOUT: true
21490
+ };
21491
+ function isUpstreamErrorCode(code) {
21492
+ return Object.prototype.hasOwnProperty.call(UPSTREAM_ERROR_CODES, code);
21493
+ }
20992
21494
 
20993
21495
  // src/services/model-provider/discovery.ts
20994
21496
  var MAX_DISCOVERED_MODELS = 1e3;
@@ -21264,6 +21766,33 @@ var ModelProviderService = class {
21264
21766
  models
21265
21767
  };
21266
21768
  }
21769
+ /**
21770
+ * Hydrates the runtime model registry from the persisted provider catalog.
21771
+ *
21772
+ * @returns The model keys registered in this process.
21773
+ * @remarks Registers the Phase-1 (default tenant) enabled provider models so a
21774
+ * replica that did not perform discovery — or that restarted — can execute
21775
+ * them. Best-effort; the caller logs failures.
21776
+ */
21777
+ async hydrateRegistry() {
21778
+ const tenantId = PHASE1_REGISTRY_TENANT;
21779
+ if (!this.shouldRegister(tenantId)) {
21780
+ return [];
21781
+ }
21782
+ const providers = await this.deps.providerStore.listProviders(tenantId);
21783
+ const registered = [];
21784
+ for (const provider of providers) {
21785
+ if (!provider.enabled) {
21786
+ continue;
21787
+ }
21788
+ const models = await this.deps.providerStore.listModels(
21789
+ tenantId,
21790
+ provider.id
21791
+ );
21792
+ registered.push(...this.syncRegistry(tenantId, provider, models));
21793
+ }
21794
+ return registered;
21795
+ }
21267
21796
  /**
21268
21797
  * Create a provider configuration. Does not run discovery.
21269
21798
  */
@@ -21503,7 +22032,7 @@ var ModelProviderService = class {
21503
22032
  models
21504
22033
  };
21505
22034
  } catch (error) {
21506
- if (error instanceof ModelProviderServiceError) {
22035
+ if (error instanceof ModelProviderServiceError && isUpstreamErrorCode(error.code)) {
21507
22036
  return {
21508
22037
  reachable: false,
21509
22038
  latencyMs: Date.now() - startedAt,
@@ -21742,9 +22271,9 @@ var ModelProviderService = class {
21742
22271
  */
21743
22272
  async assertSafeUrl(baseURL) {
21744
22273
  try {
21745
- await (0, import_core47.assertSafeUpstreamUrl)(baseURL);
22274
+ await (0, import_core49.assertSafeUpstreamUrl)(baseURL);
21746
22275
  } catch (error) {
21747
- if (error instanceof import_core47.UnsafeUpstreamUrlError) {
22276
+ if (error instanceof import_core49.UnsafeUpstreamUrlError) {
21748
22277
  throw new ModelProviderServiceError(
21749
22278
  unsafeReasonToErrorCode[error.reason],
21750
22279
  400,
@@ -21920,6 +22449,10 @@ var ModelProviderController = class {
21920
22449
  return sendError2(reply, error);
21921
22450
  }
21922
22451
  }
22452
+ /** Hydrates the runtime model registry from persisted providers (startup). */
22453
+ async hydrateRegistry() {
22454
+ return this.service.hydrateRegistry();
22455
+ }
21923
22456
  /** GET /api/model-providers */
21924
22457
  async listProviders(request, reply) {
21925
22458
  try {
@@ -23206,7 +23739,7 @@ async function execute(reply, operation) {
23206
23739
  // src/routes/cospace.ts
23207
23740
  var import_node_crypto20 = require("crypto");
23208
23741
  var import_zod15 = require("zod");
23209
- var import_core48 = require("@axiom-lattice/core");
23742
+ var import_core50 = require("@axiom-lattice/core");
23210
23743
  var import_protocols19 = require("@axiom-lattice/protocols");
23211
23744
  var CoSpaceAuthError = class extends Error {
23212
23745
  constructor() {
@@ -23391,14 +23924,14 @@ function registerCoSpaceRoutes(app2, deps) {
23391
23924
  }
23392
23925
  let projectId;
23393
23926
  if (body.type === "human") {
23394
- projectId = (0, import_core48.generateDirectHumanProjectId)(
23927
+ projectId = (0, import_core50.generateDirectHumanProjectId)(
23395
23928
  actor3.tenantId,
23396
23929
  params.workspaceId,
23397
23930
  actor3.userId,
23398
23931
  body.targetUserId
23399
23932
  );
23400
23933
  } else {
23401
- projectId = (0, import_core48.generateDirectBotProjectId)(
23934
+ projectId = (0, import_core50.generateDirectBotProjectId)(
23402
23935
  actor3.tenantId,
23403
23936
  params.workspaceId,
23404
23937
  actor3.userId,
@@ -23605,7 +24138,7 @@ function registerCoSpaceRoutes(app2, deps) {
23605
24138
  );
23606
24139
  const bots = [];
23607
24140
  for (const assistant of assistants) {
23608
- const directProjectId = (0, import_core48.generateDirectBotProjectId)(
24141
+ const directProjectId = (0, import_core50.generateDirectBotProjectId)(
23609
24142
  actor3.tenantId,
23610
24143
  params.workspaceId,
23611
24144
  actor3.userId,
@@ -23667,7 +24200,7 @@ function registerCoSpaceRoutes(app2, deps) {
23667
24200
  if (link.userId === actor3.userId) continue;
23668
24201
  const user = await deps.userStore.getUserById(link.userId);
23669
24202
  if (!user) continue;
23670
- const directProjectId = (0, import_core48.generateDirectHumanProjectId)(
24203
+ const directProjectId = (0, import_core50.generateDirectHumanProjectId)(
23671
24204
  actor3.tenantId,
23672
24205
  params.workspaceId,
23673
24206
  actor3.userId,
@@ -24484,6 +25017,8 @@ function createRoomMessageRetentionJob(input) {
24484
25017
 
24485
25018
  // src/services/collaboration/ProjectTaskExecutionMutex.ts
24486
25019
  var import_protocols22 = require("@axiom-lattice/protocols");
25020
+ var import_core51 = require("@axiom-lattice/core");
25021
+ var MACHINE_LEASE_OPTIONS = { ttlMs: 6e4, acquireTimeoutMs: 9e4, retryDelayMs: 100 };
24487
25022
  function machineKey(scope) {
24488
25023
  const snapshot = (0, import_protocols22.snapshotExactRecord)(scope, ["tenantId", "assistantId", "projectId"]);
24489
25024
  if (!snapshot) throw new Error("Invalid Project task machine scope");
@@ -24507,9 +25042,12 @@ var ProjectTaskExecutionMutex = class {
24507
25042
  */
24508
25043
  async runExclusive(scope, operation, signal) {
24509
25044
  const key = machineKey(scope);
25045
+ const leaseKey = `project-task-machine:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.assistantId)}:${encodeURIComponent(scope.projectId)}`;
24510
25046
  await this.acquire(key, signal);
24511
25047
  try {
24512
- return await operation();
25048
+ const store2 = (0, import_core51.getCoordinationLeaseStore)();
25049
+ if (!store2) return await operation();
25050
+ return await (0, import_core51.withLease)(store2, leaseKey, (0, import_core51.getReplicaId)(), operation, MACHINE_LEASE_OPTIONS);
24513
25051
  } finally {
24514
25052
  this.release(key);
24515
25053
  }
@@ -24562,8 +25100,22 @@ var ProjectTaskExecutionMutex = class {
24562
25100
 
24563
25101
  // src/services/collaboration/ProjectTaskObserver.ts
24564
25102
  var import_node_crypto22 = require("crypto");
24565
- var import_core49 = require("@axiom-lattice/core");
25103
+ var import_core52 = require("@axiom-lattice/core");
24566
25104
  var import_protocols23 = require("@axiom-lattice/protocols");
25105
+
25106
+ // src/services/collaboration/projectTaskDispatchMarker.ts
25107
+ var EXECUTION_REQUESTED_AT_KEY = "execution_requested_at";
25108
+ function hasExecutionRequest(task) {
25109
+ const value = task.context?.[EXECUTION_REQUESTED_AT_KEY];
25110
+ if (typeof value !== "string" || value.length === 0) return false;
25111
+ const parsed = new Date(value);
25112
+ return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
25113
+ }
25114
+ function withExecutionRequest(context, nowIso) {
25115
+ return { ...context ?? {}, [EXECUTION_REQUESTED_AT_KEY]: nowIso };
25116
+ }
25117
+
25118
+ // src/services/collaboration/ProjectTaskObserver.ts
24567
25119
  init_projectTaskScope();
24568
25120
  var SYSTEM_ACTOR2 = "system";
24569
25121
  var TERMINAL_ACTIONS = ["completed", "failed", "interrupted", "cancelled"];
@@ -24585,7 +25137,7 @@ var ProjectTaskObserver = class {
24585
25137
  throw new Error(`Invalid Project task observer bound: ${name}`);
24586
25138
  }
24587
25139
  }
24588
- this.runBridge = (0, import_core49.createTaskRunBridge)({ lifecycle: this.deps.lifecycle });
25140
+ this.runBridge = (0, import_core52.createTaskRunBridge)({ lifecycle: this.deps.lifecycle });
24589
25141
  }
24590
25142
  /** Fire-and-forget notifier adapter that never waits for observer work or machine acquisition. */
24591
25143
  async listener(event) {
@@ -24768,7 +25320,7 @@ var ProjectTaskObserver = class {
24768
25320
  return;
24769
25321
  }
24770
25322
  }
24771
- if (intent === "dispatch" && await this.isReady(task) && !signal?.aborted && !this.disposed) {
25323
+ if ((intent === "dispatch" || hasExecutionRequest(task)) && await this.isReady(task) && !signal?.aborted && !this.disposed) {
24772
25324
  await this.runStart(task, signal);
24773
25325
  }
24774
25326
  return;
@@ -24885,7 +25437,7 @@ var ProjectTaskObserver = class {
24885
25437
  }, signal);
24886
25438
  }
24887
25439
  async mapOutcome(outcome2) {
24888
- if (outcome2.kind === "aborted") return;
25440
+ if (outcome2.kind === "aborted" || outcome2.kind === "deferred") return;
24889
25441
  const current = snapshotTaskItem(await this.deps.tasks.getById(outcome2.task.tenantId, outcome2.task.id));
24890
25442
  if (!current || current.ownerId !== outcome2.thread.assistantId || current.context?.thread_id !== outcome2.thread.id) return;
24891
25443
  if (isTerminal(current.status) || current.status === "review") return;
@@ -25801,7 +26353,7 @@ function isRecord3(value) {
25801
26353
  init_projectTaskScope();
25802
26354
 
25803
26355
  // src/services/collaboration/ProjectRoomRealtimeRuntime.ts
25804
- var import_core50 = require("@axiom-lattice/core");
26356
+ var import_core53 = require("@axiom-lattice/core");
25805
26357
 
25806
26358
  // src/services/collaboration/ProjectRoomEventBroker.ts
25807
26359
  var import_node_crypto24 = require("crypto");
@@ -25811,6 +26363,7 @@ var DEFAULT_MAX_EVENT_AGE_MS = 10 * 60 * 1e3;
25811
26363
  var DEFAULT_MAX_ROOMS = 1e3;
25812
26364
  var DEFAULT_MAX_GLOBAL_EVENTS = 1e5;
25813
26365
  var DEFAULT_MAX_GLOBAL_BYTES = 64 * 1024 * 1024;
26366
+ var DEFAULT_SOURCE_KEY_LIMIT = 2e3;
25814
26367
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
25815
26368
  var ProjectRoomBrokerResourceCapacityError = class extends Error {
25816
26369
  constructor(resource) {
@@ -25824,6 +26377,7 @@ var ProjectRoomEventBroker = class {
25824
26377
  constructor(options = {}) {
25825
26378
  this.rooms = /* @__PURE__ */ new Map();
25826
26379
  this.sequenceScopes = /* @__PURE__ */ new Map();
26380
+ this.seenSourceKeys = /* @__PURE__ */ new Map();
25827
26381
  this.eventCount = 0;
25828
26382
  this.byteCount = 0;
25829
26383
  this.subscriberCount = 0;
@@ -25851,6 +26405,10 @@ var ProjectRoomEventBroker = class {
25851
26405
  options.maxGlobalBytes ?? DEFAULT_MAX_GLOBAL_BYTES,
25852
26406
  "maxGlobalBytes"
25853
26407
  );
26408
+ this.sourceKeyLimit = positiveSafeInteger(
26409
+ options.sourceKeyLimit ?? DEFAULT_SOURCE_KEY_LIMIT,
26410
+ "sourceKeyLimit"
26411
+ );
25854
26412
  this.sequence = nonnegativeSafeInteger(options.initialSequence ?? 0, "initialSequence");
25855
26413
  this.log = options.log ?? { warn: () => void 0 };
25856
26414
  }
@@ -25862,8 +26420,22 @@ var ProjectRoomEventBroker = class {
25862
26420
  get retainedBytes() {
25863
26421
  return this.byteCount;
25864
26422
  }
25865
- /** Atomically stores an immutable event before queueing detached listener callbacks. */
25866
- publish(scope, draft) {
26423
+ /**
26424
+ * Installs (or clears) a hook invoked after a locally published event is stored
26425
+ * and queued to local subscribers. Used to mirror events to other replicas via
26426
+ * a shared transport. Best-effort: a throwing hook never affects `publish`.
26427
+ */
26428
+ setBroadcastHook(hook) {
26429
+ this.broadcast = hook;
26430
+ }
26431
+ /**
26432
+ * Atomically stores an immutable event before queueing detached listener callbacks.
26433
+ *
26434
+ * @param sourceKey Optional caller-owned fact key; when present it is retained
26435
+ * for bounded cross-replica duplicate suppression so a later foreign copy of the
26436
+ * same fact (or a local publish) is dropped by {@link ingestForeignEvent}.
26437
+ */
26438
+ publish(scope, draft, sourceKey) {
25867
26439
  this.assertOpen();
25868
26440
  const canonicalScope = snapshotScope(scope);
25869
26441
  const key = scopeKey2(canonicalScope);
@@ -25898,9 +26470,52 @@ var ProjectRoomEventBroker = class {
25898
26470
  this.sequence = nextSequence;
25899
26471
  this.eventCount += 1;
25900
26472
  this.byteCount += bytes;
26473
+ if (sourceKey !== void 0) this.rememberSourceKey(sourceKey);
25901
26474
  for (const subscription of [...room.subscriptions]) this.queueCallback(subscription, serialized);
26475
+ if (this.broadcast) {
26476
+ try {
26477
+ this.broadcast(canonicalScope, serialized, sourceKey);
26478
+ } catch {
26479
+ }
26480
+ }
25902
26481
  return parseScopedEvent(serialized);
25903
26482
  }
26483
+ /**
26484
+ * Delivers one event published by ANOTHER replica to this process's local
26485
+ * subscribers for its room, without adding it to the local replay ring or
26486
+ * consuming a local sequence (its id carries the origin replica's epoch).
26487
+ *
26488
+ * @param serializedEvent A serialized scoped business event produced by a peer.
26489
+ * @param sourceKey Optional caller-owned fact key shared by every replica's copy
26490
+ * of the same committed fact. When it was already seen (locally published or
26491
+ * ingested from a peer) the event is dropped to avoid N× duplicate delivery.
26492
+ * @returns True when it was queued to at least one local subscription.
26493
+ * @remarks Source-key deduplication is process-local and bounded, so it caps
26494
+ * duplicate fan-out within the retention window without replacing REST, which
26495
+ * remains the authority. A malformed payload or a room with no local
26496
+ * subscribers is ignored.
26497
+ */
26498
+ ingestForeignEvent(serializedEvent, sourceKey) {
26499
+ this.assertOpen();
26500
+ if (sourceKey !== void 0 && this.seenSourceKeys.has(sourceKey)) return false;
26501
+ let key;
26502
+ try {
26503
+ const event = parseScopedEvent(serializedEvent);
26504
+ key = scopeKey2(snapshotScope(event.scope));
26505
+ } catch {
26506
+ return false;
26507
+ }
26508
+ if (sourceKey !== void 0) this.rememberSourceKey(sourceKey);
26509
+ const room = this.rooms.get(key);
26510
+ if (!room || room.subscriptions.size === 0) return false;
26511
+ this.touch(room);
26512
+ let queued = false;
26513
+ for (const subscription of [...room.subscriptions]) {
26514
+ this.queueCallback(subscription, serializedEvent);
26515
+ queued = true;
26516
+ }
26517
+ return queued;
26518
+ }
25904
26519
  /** Atomically resolves replay and registers a distinct subscription for live events. */
25905
26520
  subscribe(scope, afterEventId, listener) {
25906
26521
  this.assertOpen();
@@ -25940,6 +26555,7 @@ var ProjectRoomEventBroker = class {
25940
26555
  }
25941
26556
  this.rooms.clear();
25942
26557
  this.sequenceScopes.clear();
26558
+ this.seenSourceKeys.clear();
25943
26559
  this.eventCount = 0;
25944
26560
  this.byteCount = 0;
25945
26561
  this.subscriberCount = 0;
@@ -26074,6 +26690,14 @@ var ProjectRoomEventBroker = class {
26074
26690
  this.usageClock += 1;
26075
26691
  room.lastUsed = this.usageClock;
26076
26692
  }
26693
+ rememberSourceKey(key) {
26694
+ this.seenSourceKeys.set(key, true);
26695
+ while (this.seenSourceKeys.size > this.sourceKeyLimit) {
26696
+ const oldest = this.seenSourceKeys.keys().next().value;
26697
+ if (oldest === void 0) break;
26698
+ this.seenSourceKeys.delete(oldest);
26699
+ }
26700
+ }
26077
26701
  };
26078
26702
  function positiveSafeInteger(value, name) {
26079
26703
  if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError(`${name} must be a positive safe integer`);
@@ -26265,7 +26889,7 @@ var BrokerProjectRoomRealtimePublisher = class {
26265
26889
  if (!event) throw new Error("Invalid Project Room realtime projection");
26266
26890
  const dedupeKey = (0, import_node_crypto25.createHash)("sha256").update(JSON.stringify([type, event.scope.tenantId, event.scope.projectId, event.scope.roomId, event.sourceKey])).digest("hex");
26267
26891
  if (this.sourceKeys.has(dedupeKey)) return;
26268
- this.deps.broker.publish(event.scope, event.draft);
26892
+ this.deps.broker.publish(event.scope, event.draft, event.sourceKey);
26269
26893
  this.sourceKeys.set(dedupeKey, true);
26270
26894
  while (this.sourceKeys.size > this.dedupeLimit) {
26271
26895
  const oldest = this.sourceKeys.keys().next().value;
@@ -26504,7 +27128,7 @@ function createProjectRoomRealtimeRuntime(deps) {
26504
27128
  broker,
26505
27129
  publisher,
26506
27130
  subscriber,
26507
- subscribe: (listener) => import_core50.taskLifecycleNotifier.subscribe(listener),
27131
+ subscribe: (listener) => import_core53.taskLifecycleNotifier.subscribe(listener),
26508
27132
  log: deps.log,
26509
27133
  shutdownTimeoutMs: deps.shutdownTimeoutMs
26510
27134
  });
@@ -27242,35 +27866,35 @@ function hookErrorMessage(error) {
27242
27866
  function wrapReassignmentThreadError(error) {
27243
27867
  if (error instanceof ProjectTaskThreadError) {
27244
27868
  if (error.code === "PROJECT_TASK_THREAD_SCOPE_INVALID") {
27245
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
27869
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
27246
27870
  hint: "Project task, room, or roster scope changed; re-read the task and retry."
27247
27871
  });
27248
27872
  }
27249
27873
  if (error.code === "PROJECT_TASK_THREAD_OWNER_INVALID") {
27250
- return new import_core51.ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
27874
+ return new import_core54.ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
27251
27875
  hint: "The target already owns this task; re-read before dispatching."
27252
27876
  });
27253
27877
  }
27254
- return new import_core51.ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
27878
+ return new import_core54.ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
27255
27879
  hint: "Task Thread changed concurrently; re-read the task and retry."
27256
27880
  });
27257
27881
  }
27258
- return new import_core51.ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
27882
+ return new import_core54.ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
27259
27883
  hint: "Re-read the task and retry the dispatch."
27260
27884
  });
27261
27885
  }
27262
27886
  function wrapReassignmentAuthorizationError(error) {
27263
- if (error instanceof import_core51.ProjectTaskOwnerPolicyError) {
27887
+ if (error instanceof import_core54.ProjectTaskOwnerPolicyError) {
27264
27888
  if (error.code === "PROJECT_TASK_CALLER_INACTIVE") {
27265
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
27889
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
27266
27890
  hint: "Dispatch from an active Project Room member bot."
27267
27891
  });
27268
27892
  }
27269
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
27893
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
27270
27894
  hint: "Dispatch to an active bot in this Project's roster."
27271
27895
  });
27272
27896
  }
27273
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
27897
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
27274
27898
  hint: "Re-read the task and roster, then retry the dispatch."
27275
27899
  });
27276
27900
  }
@@ -27299,43 +27923,43 @@ async function transferGovernedDispatchOwner(task, options, deps) {
27299
27923
  const agentId = options?.agentId;
27300
27924
  if (!agentId || agentId === task.ownerId) return void 0;
27301
27925
  if (!task.workspaceId || !task.projectId) {
27302
- throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is not governed by a Project lifecycle.", {
27926
+ throw new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is not governed by a Project lifecycle.", {
27303
27927
  hint: "Dispatch governed tasks from a trusted Project run; ordinary tasks execute immediately."
27304
27928
  });
27305
27929
  }
27306
27930
  return deps.mutex.runExclusive({ tenantId: task.tenantId, projectId: task.projectId }, async () => {
27307
27931
  const current = await deps.tasks.getById(task.tenantId, task.id);
27308
27932
  if (!current || !isProjectLifecycleTask(current) || current.workspaceId !== task.workspaceId || current.projectId !== task.projectId) {
27309
- throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is no longer a Project lifecycle task.", {
27933
+ throw new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Task is no longer a Project lifecycle task.", {
27310
27934
  hint: "Re-read the task and confirm it still belongs to this Project before dispatching."
27311
27935
  });
27312
27936
  }
27313
27937
  if (current.ownerType !== "agent") {
27314
- throw new import_core51.ManagedTaskStartError("AGENT_TASK_REQUIRED", "Project task owner must be an Agent.", {
27938
+ throw new import_core54.ManagedTaskStartError("AGENT_TASK_REQUIRED", "Project task owner must be an Agent.", {
27315
27939
  hint: "Assign the task to an Agent before dispatching."
27316
27940
  });
27317
27941
  }
27318
27942
  if (current.status !== "pending") {
27319
- throw new import_core51.ManagedTaskStartError("TASK_NOT_DISPATCHABLE", "Only a `pending` Project task can be dispatched.", {
27943
+ throw new import_core54.ManagedTaskStartError("TASK_NOT_DISPATCHABLE", "Only a `pending` Project task can be dispatched.", {
27320
27944
  hint: `This task is '${current.status}'. Resolve it or create a new task before dispatching.`
27321
27945
  });
27322
27946
  }
27323
27947
  if (agentId === current.ownerId) return current;
27324
27948
  const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
27325
27949
  if (!room || room.tenantId !== current.tenantId || room.workspaceId !== current.workspaceId || room.projectId !== current.projectId || room.type !== "main") {
27326
- throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Project main room is unavailable.", {
27950
+ throw new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", "Project main room is unavailable.", {
27327
27951
  hint: "Open or repair the Project Room before dispatching."
27328
27952
  });
27329
27953
  }
27330
27954
  const target = await deps.botMemberships.findByAssistant(current.tenantId, current.projectId, agentId);
27331
27955
  if (!target || target.status !== "active" || target.tenantId !== current.tenantId || target.workspaceId !== current.workspaceId || target.projectId !== current.projectId || target.assistantId !== agentId || target.roomId !== room.id) {
27332
- throw new import_core51.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", "Target Project task owner is not active in the exact roster.", {
27956
+ throw new import_core54.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", "Target Project task owner is not active in the exact roster.", {
27333
27957
  hint: "Dispatch to an active bot in this Project's roster."
27334
27958
  });
27335
27959
  }
27336
27960
  const caller = options?.callerMembershipId ? await deps.botMemberships.findById(current.tenantId, options.callerMembershipId) : null;
27337
27961
  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) {
27338
- throw new import_core51.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", "Current Project task caller is not active in the exact roster.", {
27962
+ throw new import_core54.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", "Current Project task caller is not active in the exact roster.", {
27339
27963
  hint: "Dispatch from an active Project Room member bot."
27340
27964
  });
27341
27965
  }
@@ -27370,7 +27994,7 @@ async function transferGovernedDispatchOwner(task, options, deps) {
27370
27994
  actor: `agent:${caller.assistantId}`
27371
27995
  });
27372
27996
  if (!result.success) {
27373
- throw new import_core51.ManagedTaskStartError(result.code, result.error, {
27997
+ throw new import_core54.ManagedTaskStartError(result.code, result.error, {
27374
27998
  hint: result.hint ?? reassignmentFailureHint(result.code),
27375
27999
  ...result.retryable !== void 0 ? { retryable: result.retryable } : {}
27376
28000
  });
@@ -27379,19 +28003,19 @@ async function transferGovernedDispatchOwner(task, options, deps) {
27379
28003
  });
27380
28004
  }
27381
28005
  var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27382
- const webAppStore = (0, import_core51.getStoreLattice)("default", "agentWebApp").store;
27383
- const assistantStore = (0, import_core51.getStoreLattice)("default", "assistant").store;
27384
- const projectStore = (0, import_core51.getStoreLattice)("default", "project").store;
27385
- const projectMembershipStore = (0, import_core51.getStoreLattice)("default", "projectMembership").store;
27386
- const userTenantLinkStore = (0, import_core51.getStoreLattice)("default", "userTenantLink").store;
27387
- const projectRoomStore = (0, import_core51.getStoreLattice)("default", "projectRoom").store;
27388
- const projectBotMembershipStore = (0, import_core51.getStoreLattice)("default", "projectBotMembership").store;
27389
- const projectRoomMessageStore = (0, import_core51.getStoreLattice)("default", "projectRoomMessage").store;
27390
- const projectRoomReadStateStore = (0, import_core51.getStoreLattice)("default", "projectRoomReadState").store;
27391
- const userPushSubscriptionStore = (0, import_core51.getStoreLattice)("default", "userPushSubscription").store;
27392
- const threadStore = (0, import_core51.getStoreLattice)("default", "thread").store;
27393
- const taskStore = (0, import_core51.getStoreLattice)("default", "task").store;
27394
- const taskWorkItemStore = (0, import_core51.getStoreLattice)("default", "taskWorkItem").store;
28006
+ const webAppStore = (0, import_core54.getStoreLattice)("default", "agentWebApp").store;
28007
+ const assistantStore = (0, import_core54.getStoreLattice)("default", "assistant").store;
28008
+ const projectStore = (0, import_core54.getStoreLattice)("default", "project").store;
28009
+ const projectMembershipStore = (0, import_core54.getStoreLattice)("default", "projectMembership").store;
28010
+ const userTenantLinkStore = (0, import_core54.getStoreLattice)("default", "userTenantLink").store;
28011
+ const projectRoomStore = (0, import_core54.getStoreLattice)("default", "projectRoom").store;
28012
+ const projectBotMembershipStore = (0, import_core54.getStoreLattice)("default", "projectBotMembership").store;
28013
+ const projectRoomMessageStore = (0, import_core54.getStoreLattice)("default", "projectRoomMessage").store;
28014
+ const projectRoomReadStateStore = (0, import_core54.getStoreLattice)("default", "projectRoomReadState").store;
28015
+ const userPushSubscriptionStore = (0, import_core54.getStoreLattice)("default", "userPushSubscription").store;
28016
+ const threadStore = (0, import_core54.getStoreLattice)("default", "thread").store;
28017
+ const taskStore = (0, import_core54.getStoreLattice)("default", "task").store;
28018
+ const taskWorkItemStore = (0, import_core54.getStoreLattice)("default", "taskWorkItem").store;
27395
28019
  const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
27396
28020
  const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
27397
28021
  const realtimeMode = resolveProjectRoomRealtimeMode();
@@ -27457,7 +28081,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27457
28081
  updateAssistant: assistantStore.updateAssistant.bind(assistantStore)
27458
28082
  });
27459
28083
  const resolveCoordinatorMentionName = async (tenantId) => {
27460
- const tenant2 = await (0, import_core51.getStoreLattice)("default", "tenant").store.getTenantById(tenantId);
28084
+ const tenant2 = await (0, import_core54.getStoreLattice)("default", "tenant").store.getTenantById(tenantId);
27461
28085
  const value = tenant2?.metadata?.coordinatorMentionName;
27462
28086
  return typeof value === "string" && value.length > 0 ? value : void 0;
27463
28087
  };
@@ -27484,7 +28108,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27484
28108
  return membership !== null && membership.projectId === projectId ? membership : null;
27485
28109
  },
27486
28110
  getThreadStatus: async ({ tenantId, assistantId, threadId, workspaceId, projectId }) => {
27487
- const agent = import_core51.agentInstanceManager.getAgent({
28111
+ const agent = import_core54.agentInstanceManager.getAgent({
27488
28112
  tenant_id: tenantId,
27489
28113
  assistant_id: assistantId,
27490
28114
  thread_id: threadId,
@@ -27493,9 +28117,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27493
28117
  });
27494
28118
  return String(await agent.getRunStatus());
27495
28119
  },
27496
- dispatcher: (0, import_core51.createTrustedAgentMessageDispatcher)((scope) => import_core51.agentInstanceManager.getAgent(scope))
28120
+ dispatcher: (0, import_core54.createTrustedAgentMessageDispatcher)((scope) => import_core54.agentInstanceManager.getAgent(scope))
27497
28121
  });
27498
- const membershipService = new ProjectMembershipService({ membershipStore: projectMembershipStore, userTenantLinkStore, accessService, projectMutationMutex: import_core51.projectMutationMutex, roomStore: projectRoomStore, realtime, onRealtimeFailure: realtime && channelDeps?.warn ? ({ code, membershipId }) => channelDeps.warn?.("project-room:membership-realtime-failed", { code, membershipId }) : void 0 });
28122
+ const membershipService = new ProjectMembershipService({ membershipStore: projectMembershipStore, userTenantLinkStore, accessService, projectMutationMutex: import_core54.projectMutationMutex, roomStore: projectRoomStore, realtime, onRealtimeFailure: realtime && channelDeps?.warn ? ({ code, membershipId }) => channelDeps.warn?.("project-room:membership-realtime-failed", { code, membershipId }) : void 0 });
27499
28123
  const rosterService = new ProjectRosterService({
27500
28124
  accessService,
27501
28125
  roomService,
@@ -27506,7 +28130,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27506
28130
  assistantStore,
27507
28131
  taskStore,
27508
28132
  bindingLifecycle,
27509
- projectMutationMutex: import_core51.projectMutationMutex,
28133
+ projectMutationMutex: import_core54.projectMutationMutex,
27510
28134
  realtime,
27511
28135
  onRealtimeFailure: realtime && channelDeps?.warn ? ({ code, membershipId }) => channelDeps.warn?.("project-room:roster-realtime-failed", { code, membershipId }) : void 0
27512
28136
  });
@@ -27562,9 +28186,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27562
28186
  }
27563
28187
  if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
27564
28188
  const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: import_node_crypto26.randomUUID });
27565
- const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? (0, import_core51.createTaskLifecycleService)({ taskStore, workItemStore: taskWorkItemStore });
27566
- const reassignmentAuthorizer = (0, import_core51.createProjectTaskReassignmentAuthorizer)(
27567
- new import_core51.ProjectTaskOwnerPolicy(projectBotMembershipStore),
28189
+ const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? (0, import_core54.createTaskLifecycleService)({ taskStore, workItemStore: taskWorkItemStore });
28190
+ const reassignmentAuthorizer = (0, import_core54.createProjectTaskReassignmentAuthorizer)(
28191
+ new import_core54.ProjectTaskOwnerPolicy(projectBotMembershipStore),
27568
28192
  (input) => validateReassignmentThread(threadStore, input)
27569
28193
  );
27570
28194
  const taskCommandService = new TaskCommandService({
@@ -27573,7 +28197,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27573
28197
  lifecycle: taskLifecycle,
27574
28198
  execution: projectTaskComposition?.lease.runtime.execution,
27575
28199
  threads: taskThreadService,
27576
- agents: { getAgent: (params) => import_core51.agentInstanceManager.getAgent(params) },
28200
+ agents: { getAgent: (params) => import_core54.agentInstanceManager.getAgent(params) },
27577
28201
  taskScope: (task) => resolveCommandTaskScope(task, {
27578
28202
  rooms: projectRoomStore,
27579
28203
  bots: projectBotMembershipStore,
@@ -27611,7 +28235,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27611
28235
  readStateService,
27612
28236
  interruptService: roomInterruptService,
27613
28237
  userDirectory: {
27614
- getUserById: (id3) => (0, import_core51.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
28238
+ getUserById: (id3) => (0, import_core54.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { name: u.name } : null)
27615
28239
  }
27616
28240
  });
27617
28241
  registerProjectRosterRoutes(app2, { service: rosterService });
@@ -27622,7 +28246,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27622
28246
  createProject: projectStore.createProject.bind(projectStore)
27623
28247
  },
27624
28248
  workspaceStore: {
27625
- getWorkspaceById: (tenantId, id3) => (0, import_core51.getStoreLattice)("default", "workspace").store.getWorkspaceById(tenantId, id3)
28249
+ getWorkspaceById: (tenantId, id3) => (0, import_core54.getStoreLattice)("default", "workspace").store.getWorkspaceById(tenantId, id3)
27626
28250
  },
27627
28251
  membershipStore: {
27628
28252
  list: projectMembershipStore.list.bind(projectMembershipStore),
@@ -27645,7 +28269,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27645
28269
  getUsersByTenant: userTenantLinkStore.getUsersByTenant.bind(userTenantLinkStore)
27646
28270
  },
27647
28271
  userStore: {
27648
- getUserById: (id3) => (0, import_core51.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
28272
+ getUserById: (id3) => (0, import_core54.getStoreLattice)("default", "user").store.getUserById(id3).then((u) => u ? { id: u.id, name: u.name, email: u.email } : null)
27649
28273
  },
27650
28274
  assistantStore: {
27651
28275
  getAssistantById: assistantStore.getAssistantById.bind(assistantStore),
@@ -27667,28 +28291,30 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27667
28291
  }
27668
28292
  });
27669
28293
  registerCapabilityBundleRoutes(app2, new CapabilityBundleController({
27670
- bundleStore: (0, import_core51.getStoreLattice)("default", "capabilityBundle").store,
28294
+ bundleStore: (0, import_core54.getStoreLattice)("default", "capabilityBundle").store,
27671
28295
  projectStore
27672
28296
  }, { authRequired: process.env.AUTH_REQUIRED === "true" }));
27673
- registerModelProviderRoutes(app2, new ModelProviderController({
27674
- providerStore: (0, import_core51.getStoreLattice)("default", "modelProvider").store,
28297
+ const modelProviderController = new ModelProviderController({
28298
+ providerStore: (0, import_core54.getStoreLattice)("default", "modelProvider").store,
27675
28299
  userTenantLinkStore,
27676
28300
  registry: {
27677
- register: (tenantId, modelKey2, config) => import_core51.modelLatticeManager.registerLatticeWithTenant(tenantId, modelKey2, config),
27678
- has: (tenantId, modelKey2) => import_core51.modelLatticeManager.hasLatticeWithTenant(tenantId, modelKey2),
27679
- remove: (tenantId, modelKey2) => import_core51.modelLatticeManager.removeLatticeWithTenant(tenantId, modelKey2),
27680
- listRegistered: (tenantId) => import_core51.modelLatticeManager.getLatticeKeysByTenant(tenantId)
27681
- }
27682
- }, { authRequired: process.env.AUTH_REQUIRED === "true" }));
28301
+ register: (tenantId, modelKey2, config) => import_core54.modelLatticeManager.registerLatticeWithTenant(tenantId, modelKey2, config),
28302
+ has: (tenantId, modelKey2) => import_core54.modelLatticeManager.hasLatticeWithTenant(tenantId, modelKey2),
28303
+ remove: (tenantId, modelKey2) => import_core54.modelLatticeManager.removeLatticeWithTenant(tenantId, modelKey2),
28304
+ listRegistered: (tenantId) => import_core54.modelLatticeManager.getLatticeKeysByTenant(tenantId)
28305
+ }
28306
+ }, { authRequired: process.env.AUTH_REQUIRED === "true" });
28307
+ registerModelProviderRoutes(app2, modelProviderController);
28308
+ void modelProviderController.hydrateRegistry().catch(() => void 0);
27683
28309
  registerAgentWebAppRoutes(app2, {
27684
28310
  service: new AgentWebAppService({
27685
28311
  webAppStore,
27686
28312
  assistantStore,
27687
28313
  projectStore,
27688
- modelRegistry: { has: (key) => import_core51.modelLatticeManager.hasLattice(key) },
28314
+ modelRegistry: { has: (key) => import_core54.modelLatticeManager.hasLattice(key) },
27689
28315
  configuredAgentResolver: createConfiguredAgentResolver({
27690
- hydrate: (tenantId) => import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
27691
- has: (tenantId, assistantId) => import_core51.agentLatticeManager.hasAgentLatticeWithTenant(tenantId, assistantId)
28316
+ hydrate: (tenantId) => import_core54.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
28317
+ has: (tenantId, assistantId) => import_core54.agentLatticeManager.hasAgentLatticeWithTenant(tenantId, assistantId)
27692
28318
  })
27693
28319
  })
27694
28320
  });
@@ -27725,28 +28351,28 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27725
28351
  },
27726
28352
  tasks: taskStore,
27727
28353
  workItems: taskWorkItemStore,
27728
- projectMutationMutex: import_core51.projectMutationMutex,
28354
+ projectMutationMutex: import_core54.projectMutationMutex,
27729
28355
  ...projectTaskComposition ? { commands: taskCommandService } : {}
27730
28356
  }),
27731
28357
  service: new AgentWebAppRuntimeService({
27732
28358
  webAppStore,
27733
- threadStore: (0, import_core51.getStoreLattice)("default", "thread").store,
28359
+ threadStore: (0, import_core54.getStoreLattice)("default", "thread").store,
27734
28360
  projectStore,
27735
28361
  assistantResolver: {
27736
28362
  resolve: async (tenantId, assistantId) => {
27737
28363
  const stored = await assistantStore.getAssistantById(tenantId, assistantId);
27738
28364
  if (stored) return { id: stored.id, name: stored.name, description: stored.description };
27739
- await import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId);
27740
- const config = import_core51.agentLatticeManager.getAgentConfigWithTenant(tenantId, assistantId);
28365
+ await import_core54.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId);
28366
+ const config = import_core54.agentLatticeManager.getAgentConfigWithTenant(tenantId, assistantId);
27741
28367
  return config ? { id: assistantId, name: config.name, description: config.description } : null;
27742
28368
  }
27743
28369
  },
27744
28370
  modelRegistry: {
27745
- has: (key) => import_core51.modelLatticeManager.hasLattice(key),
28371
+ has: (key) => import_core54.modelLatticeManager.hasLattice(key),
27746
28372
  label: (key) => key
27747
28373
  },
27748
28374
  messageReader: {
27749
- getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core51.agentInstanceManager.getAgentForRead({
28375
+ getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core54.agentInstanceManager.getAgentForRead({
27750
28376
  tenant_id: tenantId,
27751
28377
  assistant_id: assistantId,
27752
28378
  thread_id: threadId,
@@ -27755,7 +28381,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27755
28381
  }).getCurrentMessages()
27756
28382
  },
27757
28383
  agentResolver: {
27758
- getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core51.agentInstanceManager.getAgent({
28384
+ getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core54.agentInstanceManager.getAgent({
27759
28385
  tenant_id: tenantId,
27760
28386
  assistant_id: assistantId,
27761
28387
  thread_id: threadId,
@@ -27773,8 +28399,8 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27773
28399
  if (webApp.tenantId !== preview.tenantId) return false;
27774
28400
  const assistant = await assistantStore.getAssistantById(preview.tenantId, webApp.assistantId);
27775
28401
  if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === preview.userId;
27776
- await import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(preview.tenantId);
27777
- return import_core51.agentLatticeManager.hasAgentLatticeWithTenant(preview.tenantId, webApp.assistantId);
28402
+ await import_core54.agentLatticeManager.initializeStoredAssistantsForTenant(preview.tenantId);
28403
+ return import_core54.agentLatticeManager.hasAgentLatticeWithTenant(preview.tenantId, webApp.assistantId);
27778
28404
  }
27779
28405
  })
27780
28406
  });
@@ -27831,14 +28457,14 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27831
28457
  app2.post("/api/personal-assistant", createPersonalAssistant);
27832
28458
  app2.get("/api/personal-assistant", getPersonalAssistant);
27833
28459
  app2.delete("/api/personal-assistant", deletePersonalAssistant);
27834
- import_core51.executionAuthorityRegistry.register({
28460
+ import_core54.executionAuthorityRegistry.register({
27835
28461
  id: "project-lifecycle",
27836
28462
  matches: isProjectLifecycleTask,
27837
28463
  code: "PROJECT_TASK_EXECUTION_MANAGED",
27838
28464
  message: "Project tasks are executed by the durable Project queue runtime",
27839
28465
  onManagedStart: async (task, options2) => {
27840
28466
  if (!projectTaskComposition && options2?.agentId && options2.agentId !== task.ownerId) {
27841
- throw new import_core51.ManagedTaskStartError(
28467
+ throw new import_core54.ManagedTaskStartError(
27842
28468
  "PROJECT_TASK_RUNTIME_UNAVAILABLE",
27843
28469
  "Project task ownership transfer requires the Project task runtime.",
27844
28470
  { hint: "Project task execution is disabled; enable the Project task runtime or dispatch to the current owner." }
@@ -27851,17 +28477,21 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27851
28477
  threads: taskThreadService,
27852
28478
  reassignmentAuthorizer,
27853
28479
  lifecycle: taskLifecycle,
27854
- mutex: import_core51.projectMutationMutex
28480
+ mutex: import_core54.projectMutationMutex
27855
28481
  }) : void 0;
27856
28482
  const admitted = transferred ?? task;
27857
28483
  if (!admitted.workspaceId || !admitted.projectId) return;
27858
- void import_core51.projectTaskAdmissionNotifier.publish({
27859
- tenantId: admitted.tenantId,
27860
- workspaceId: admitted.workspaceId,
27861
- projectId: admitted.projectId,
27862
- taskId: admitted.id,
27863
- ownerId: admitted.ownerId,
27864
- updatedAt: admitted.updatedAt.toISOString()
28484
+ const markerUpdated = await taskStore.update(admitted.tenantId, admitted.id, {
28485
+ context: withExecutionRequest(admitted.context, (/* @__PURE__ */ new Date()).toISOString())
28486
+ }).catch(() => null);
28487
+ const eventTask = markerUpdated ?? admitted;
28488
+ void import_core54.projectTaskAdmissionNotifier.publish({
28489
+ tenantId: eventTask.tenantId,
28490
+ workspaceId: eventTask.workspaceId,
28491
+ projectId: eventTask.projectId,
28492
+ taskId: eventTask.id,
28493
+ ownerId: eventTask.ownerId,
28494
+ updatedAt: eventTask.updatedAt.toISOString()
27865
28495
  });
27866
28496
  }
27867
28497
  });
@@ -27869,7 +28499,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27869
28499
  access: accessService,
27870
28500
  tasks: taskStore,
27871
28501
  workItems: taskWorkItemStore,
27872
- projectMutationMutex: import_core51.projectMutationMutex,
28502
+ projectMutationMutex: import_core54.projectMutationMutex,
27873
28503
  commands: taskCommandService,
27874
28504
  readableProjects: async ({ tenantId, userId, workspaceId }) => {
27875
28505
  const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
@@ -28151,9 +28781,9 @@ function tryCreateProjectTaskRuntime(input) {
28151
28781
  let stores;
28152
28782
  try {
28153
28783
  stores = {
28154
- tenants: (0, import_core51.getStoreLattice)("default", "tenant").store,
28155
- workspaces: (0, import_core51.getStoreLattice)("default", "workspace").store,
28156
- queue: (0, import_core51.getStoreLattice)("default", "threadMessageQueue").store
28784
+ tenants: (0, import_core54.getStoreLattice)("default", "tenant").store,
28785
+ workspaces: (0, import_core54.getStoreLattice)("default", "workspace").store,
28786
+ queue: (0, import_core54.getStoreLattice)("default", "threadMessageQueue").store
28157
28787
  };
28158
28788
  const missing = missingRuntimeMethod(input, stores);
28159
28789
  if (missing) return { reason: missing };
@@ -28185,7 +28815,7 @@ function createRuntime(input, stores, log2) {
28185
28815
  const controller = new AbortController();
28186
28816
  const mutex = new ProjectTaskExecutionMutex();
28187
28817
  const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: import_node_crypto26.randomUUID });
28188
- const lifecycle = (0, import_core51.createTaskLifecycleService)({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
28818
+ const lifecycle = (0, import_core54.createTaskLifecycleService)({ taskStore: input.taskStore, workItemStore: input.taskWorkItemStore });
28189
28819
  const projection = new RoomProjectionService({
28190
28820
  messages: input.projectRoomMessageStore,
28191
28821
  events: input.taskWorkItemStore,
@@ -28195,7 +28825,7 @@ function createRuntime(input, stores, log2) {
28195
28825
  realtime: input.realtime,
28196
28826
  idFactory: import_node_crypto26.randomUUID
28197
28827
  });
28198
- const agentFor = (task, thread) => import_core51.agentInstanceManager.getAgent({
28828
+ const agentFor = (task, thread) => import_core54.agentInstanceManager.getAgent({
28199
28829
  tenant_id: task.tenantId,
28200
28830
  assistant_id: task.ownerId,
28201
28831
  thread_id: thread.id,
@@ -28209,7 +28839,7 @@ function createRuntime(input, stores, log2) {
28209
28839
  bots: input.projectBotMembershipStore,
28210
28840
  threads,
28211
28841
  lifecycle,
28212
- dispatcher: (0, import_core51.createTrustedAgentMessageDispatcher)((scope) => import_core51.agentInstanceManager.getAgent(scope)),
28842
+ dispatcher: (0, import_core54.createTrustedAgentMessageDispatcher)((scope) => import_core54.agentInstanceManager.getAgent(scope)),
28213
28843
  queue: stores.queue,
28214
28844
  agentFor
28215
28845
  }, {
@@ -28236,8 +28866,8 @@ function createRuntime(input, stores, log2) {
28236
28866
  const taskLifecycleListener = ((event) => {
28237
28867
  void observer.listener(event);
28238
28868
  });
28239
- const unsubscribe = import_core51.taskLifecycleNotifier.subscribe(taskLifecycleListener);
28240
- const unsubscribeAdmission = import_core51.projectTaskAdmissionNotifier.subscribe((event, signal) => {
28869
+ const unsubscribe = import_core54.taskLifecycleNotifier.subscribe(taskLifecycleListener);
28870
+ const unsubscribeAdmission = import_core54.projectTaskAdmissionNotifier.subscribe((event, signal) => {
28241
28871
  observer.admit(event, signal);
28242
28872
  });
28243
28873
  let reconciliationWork;
@@ -28384,12 +29014,12 @@ function registerResourceRoutes(app2, controller) {
28384
29014
  }
28385
29015
 
28386
29016
  // src/export_registrations/index.ts
28387
- var import_core62 = require("@axiom-lattice/core");
29017
+ var import_core65 = require("@axiom-lattice/core");
28388
29018
 
28389
29019
  // src/export_registrations/skill.registration.ts
28390
- var import_core52 = require("@axiom-lattice/core");
29020
+ var import_core55 = require("@axiom-lattice/core");
28391
29021
  function getStore(context) {
28392
- return new import_core52.SandboxSkillStore({
29022
+ return new import_core55.SandboxSkillStore({
28393
29023
  workspaceId: context?.workspaceId,
28394
29024
  projectId: context?.projectId
28395
29025
  });
@@ -28407,7 +29037,7 @@ var skillRegistration = {
28407
29037
  async listForExport(tenantId, context) {
28408
29038
  const store2 = getStore(context);
28409
29039
  const skills = await store2.getAllSkills(tenantId, toStoreContext(context));
28410
- return skills.filter((s) => !(0, import_core52.isBuiltInSkill)(s.id)).map((s) => ({
29040
+ return skills.filter((s) => !(0, import_core55.isBuiltInSkill)(s.id)).map((s) => ({
28411
29041
  _exportId: "",
28412
29042
  data: {
28413
29043
  id: s.id,
@@ -28472,7 +29102,7 @@ var skillRegistration = {
28472
29102
  const insertions = [];
28473
29103
  for (const entity of entities) {
28474
29104
  const id3 = entity.data.id;
28475
- if ((0, import_core52.isBuiltInSkill)(id3)) continue;
29105
+ if ((0, import_core55.isBuiltInSkill)(id3)) continue;
28476
29106
  if (existingIds.has(id3)) {
28477
29107
  conflicts.push({
28478
29108
  _exportId: entity._exportId,
@@ -28531,9 +29161,9 @@ var skillRegistration = {
28531
29161
  };
28532
29162
 
28533
29163
  // src/export_registrations/agent.registration.ts
28534
- var import_core53 = require("@axiom-lattice/core");
29164
+ var import_core56 = require("@axiom-lattice/core");
28535
29165
  function getStore2() {
28536
- return (0, import_core53.getStoreLattice)("default", "assistant").store;
29166
+ return (0, import_core56.getStoreLattice)("default", "assistant").store;
28537
29167
  }
28538
29168
  var agentRegistration = {
28539
29169
  entityType: "agent",
@@ -28613,9 +29243,9 @@ var agentRegistration = {
28613
29243
  };
28614
29244
 
28615
29245
  // src/export_registrations/database-config.registration.ts
28616
- var import_core54 = require("@axiom-lattice/core");
29246
+ var import_core57 = require("@axiom-lattice/core");
28617
29247
  function getStore3() {
28618
- return (0, import_core54.getStoreLattice)("default", "database").store;
29248
+ return (0, import_core57.getStoreLattice)("default", "database").store;
28619
29249
  }
28620
29250
  var databaseConfigRegistration = {
28621
29251
  entityType: "database_config",
@@ -28695,9 +29325,9 @@ var databaseConfigRegistration = {
28695
29325
  };
28696
29326
 
28697
29327
  // src/export_registrations/metrics-config.registration.ts
28698
- var import_core55 = require("@axiom-lattice/core");
29328
+ var import_core58 = require("@axiom-lattice/core");
28699
29329
  function getStore4() {
28700
- return (0, import_core55.getStoreLattice)("default", "metrics").store;
29330
+ return (0, import_core58.getStoreLattice)("default", "metrics").store;
28701
29331
  }
28702
29332
  var metricsConfigRegistration = {
28703
29333
  entityType: "metrics_config",
@@ -28777,9 +29407,9 @@ var metricsConfigRegistration = {
28777
29407
  };
28778
29408
 
28779
29409
  // src/export_registrations/mcp-config.registration.ts
28780
- var import_core56 = require("@axiom-lattice/core");
29410
+ var import_core59 = require("@axiom-lattice/core");
28781
29411
  function getStore5() {
28782
- return (0, import_core56.getStoreLattice)("default", "mcp").store;
29412
+ return (0, import_core59.getStoreLattice)("default", "mcp").store;
28783
29413
  }
28784
29414
  var mcpConfigRegistration = {
28785
29415
  entityType: "mcp_config",
@@ -28862,9 +29492,9 @@ var mcpConfigRegistration = {
28862
29492
  };
28863
29493
 
28864
29494
  // src/export_registrations/connection.registration.ts
28865
- var import_core57 = require("@axiom-lattice/core");
29495
+ var import_core60 = require("@axiom-lattice/core");
28866
29496
  function getStore6() {
28867
- return (0, import_core57.getStoreLattice)("default", "connection").store;
29497
+ return (0, import_core60.getStoreLattice)("default", "connection").store;
28868
29498
  }
28869
29499
  async function listAllConnections(tenantId) {
28870
29500
  const store2 = getStore6();
@@ -28946,9 +29576,9 @@ var connectionRegistration = {
28946
29576
  };
28947
29577
 
28948
29578
  // src/export_registrations/collection.registration.ts
28949
- var import_core58 = require("@axiom-lattice/core");
29579
+ var import_core61 = require("@axiom-lattice/core");
28950
29580
  function getStore7() {
28951
- return (0, import_core58.getStoreLattice)("default", "collection").store;
29581
+ return (0, import_core61.getStoreLattice)("default", "collection").store;
28952
29582
  }
28953
29583
  var collectionRegistration = {
28954
29584
  entityType: "collection",
@@ -29019,7 +29649,7 @@ var collectionRegistration = {
29019
29649
  };
29020
29650
 
29021
29651
  // src/export_registrations/channel-installation.registration.ts
29022
- var import_core59 = require("@axiom-lattice/core");
29652
+ var import_core62 = require("@axiom-lattice/core");
29023
29653
  var import_crypto11 = require("crypto");
29024
29654
  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;
29025
29655
  var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
@@ -29053,7 +29683,7 @@ function validateImportData(data) {
29053
29683
  };
29054
29684
  }
29055
29685
  function getStore8() {
29056
- return (0, import_core59.getStoreLattice)("default", "channelInstallation").store;
29686
+ return (0, import_core62.getStoreLattice)("default", "channelInstallation").store;
29057
29687
  }
29058
29688
  function getOverwriteError(tenantId, incomingChannel, existing) {
29059
29689
  if (!existing) return "Channel installation overwrite target must exist";
@@ -29187,9 +29817,9 @@ var channelInstallationRegistration = {
29187
29817
  };
29188
29818
 
29189
29819
  // src/export_registrations/menu.registration.ts
29190
- var import_core60 = require("@axiom-lattice/core");
29820
+ var import_core63 = require("@axiom-lattice/core");
29191
29821
  function getStore9() {
29192
- return (0, import_core60.getStoreLattice)("default", "menu").store;
29822
+ return (0, import_core63.getStoreLattice)("default", "menu").store;
29193
29823
  }
29194
29824
  var menuRegistration = {
29195
29825
  entityType: "menu",
@@ -29267,9 +29897,9 @@ var menuRegistration = {
29267
29897
  };
29268
29898
 
29269
29899
  // src/export_registrations/eval.registration.ts
29270
- var import_core61 = require("@axiom-lattice/core");
29900
+ var import_core64 = require("@axiom-lattice/core");
29271
29901
  function getStore10() {
29272
- return (0, import_core61.getStoreLattice)("default", "eval").store;
29902
+ return (0, import_core64.getStoreLattice)("default", "eval").store;
29273
29903
  }
29274
29904
  var evalRegistration = {
29275
29905
  entityType: "eval",
@@ -29595,7 +30225,7 @@ var evalRegistration = {
29595
30225
 
29596
30226
  // src/export_registrations/index.ts
29597
30227
  function registerAllBuiltinEntities() {
29598
- const registry = import_core62.ExportableEntityRegistry.getInstance();
30228
+ const registry = import_core65.ExportableEntityRegistry.getInstance();
29599
30229
  registry.register(skillRegistration);
29600
30230
  registry.register(agentRegistration);
29601
30231
  registry.register(databaseConfigRegistration);
@@ -29726,7 +30356,7 @@ function createAuditLoggerMiddleware() {
29726
30356
  }
29727
30357
 
29728
30358
  // src/index.ts
29729
- var import_core84 = require("@axiom-lattice/core");
30359
+ var import_core91 = require("@axiom-lattice/core");
29730
30360
 
29731
30361
  // src/swagger.ts
29732
30362
  var import_swagger = __toESM(require("@fastify/swagger"));
@@ -29792,7 +30422,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
29792
30422
 
29793
30423
  // src/services/agent_task_consumer.ts
29794
30424
  var import_node_crypto27 = require("crypto");
29795
- var import_core63 = require("@axiom-lattice/core");
30425
+ var import_core66 = require("@axiom-lattice/core");
29796
30426
  var import_protocols31 = require("@axiom-lattice/protocols");
29797
30427
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
29798
30428
  const {
@@ -29812,7 +30442,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
29812
30442
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
29813
30443
  );
29814
30444
  const scope = (0, import_protocols31.resolveRunScope)(runConfig);
29815
- const agent = import_core63.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: scope.workspaceId, project_id: scope.projectId, custom_run_config: runConfig });
30445
+ const agent = import_core66.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: scope.workspaceId, project_id: scope.projectId, custom_run_config: runConfig });
29816
30446
  if (callback_event) {
29817
30447
  const callbackEvent = callback_event;
29818
30448
  const messageId = typeof input?.id === "string" && input.id !== "" ? input.id : (0, import_node_crypto27.randomUUID)();
@@ -29833,13 +30463,13 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
29833
30463
  const onCompleted = (evt) => {
29834
30464
  if (!belongsToMessage(evt)) return;
29835
30465
  settle(() => {
29836
- import_core63.eventBus.publish(callbackEvent, {
30466
+ import_core66.eventBus.publish(callbackEvent, {
29837
30467
  success: true,
29838
30468
  state: evt.state
29839
30469
  });
29840
30470
  if (main_thread_id && main_tenant_id) {
29841
30471
  try {
29842
- const mainAgent = import_core63.agentInstanceManager.getAgent({
30472
+ const mainAgent = import_core66.agentInstanceManager.getAgent({
29843
30473
  assistant_id: main_assistant_id ?? assistant_id,
29844
30474
  thread_id: main_thread_id,
29845
30475
  tenant_id: main_tenant_id,
@@ -29872,7 +30502,7 @@ ${summary}`
29872
30502
  const onInterrupted = (evt) => {
29873
30503
  if (!belongsToMessage(evt)) return;
29874
30504
  settle(() => {
29875
- import_core63.eventBus.publish(callbackEvent, {
30505
+ import_core66.eventBus.publish(callbackEvent, {
29876
30506
  success: true,
29877
30507
  state: evt.state
29878
30508
  });
@@ -29881,7 +30511,7 @@ ${summary}`
29881
30511
  const onFailed = (evt) => {
29882
30512
  if (!belongsToMessage(evt)) return;
29883
30513
  settle(() => {
29884
- import_core63.eventBus.publish(callbackEvent, {
30514
+ import_core66.eventBus.publish(callbackEvent, {
29885
30515
  success: false,
29886
30516
  error: evt?.error instanceof Error ? evt.error.message : typeof evt?.error === "string" ? evt.error : "Agent execution failed"
29887
30517
  });
@@ -29896,13 +30526,13 @@ ${summary}`
29896
30526
  agent.subscribe("message:interrupted", onInterrupted);
29897
30527
  agent.subscribe("message:failed", onFailed);
29898
30528
  try {
29899
- await agent.addMessage({ input: { ...input, id: messageId }, command, custom_run_config: runConfig }, import_core63.QueueMode.STEER);
30529
+ await agent.addMessage({ input: { ...input, id: messageId }, command, custom_run_config: runConfig }, import_core66.QueueMode.STEER);
29900
30530
  } catch (error) {
29901
30531
  dispose();
29902
30532
  throw error;
29903
30533
  }
29904
30534
  } else {
29905
- await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core63.QueueMode.STEER);
30535
+ await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core66.QueueMode.STEER);
29906
30536
  }
29907
30537
  return true;
29908
30538
  } catch (error) {
@@ -29921,7 +30551,7 @@ ${summary}`
29921
30551
  return handleAgentTask(taskRequest, nextRetryCount);
29922
30552
  }
29923
30553
  if (callback_event) {
29924
- import_core63.eventBus.publish(callback_event, {
30554
+ import_core66.eventBus.publish(callback_event, {
29925
30555
  success: false,
29926
30556
  error: error instanceof Error ? error.message : String(error)
29927
30557
  });
@@ -29958,7 +30588,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
29958
30588
  * 初始化事件监听和队列轮询
29959
30589
  */
29960
30590
  initialize() {
29961
- import_core63.eventBus.subscribe(import_core63.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
30591
+ import_core66.eventBus.subscribe(import_core66.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
29962
30592
  this.startPollingQueue();
29963
30593
  console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
29964
30594
  }
@@ -30077,7 +30707,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
30077
30707
  handleAgentTask(taskRequest).catch((error) => {
30078
30708
  console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
30079
30709
  if (taskRequest.callback_event) {
30080
- import_core63.eventBus.publish(taskRequest.callback_event, {
30710
+ import_core66.eventBus.publish(taskRequest.callback_event, {
30081
30711
  success: false,
30082
30712
  error: error instanceof Error ? error.message : String(error)
30083
30713
  });
@@ -30092,13 +30722,13 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
30092
30722
  var AgentTaskConsumer = _AgentTaskConsumer;
30093
30723
 
30094
30724
  // src/index.ts
30095
- var import_core85 = require("@axiom-lattice/core");
30725
+ var import_core92 = require("@axiom-lattice/core");
30096
30726
 
30097
30727
  // src/channels/ChannelInfrastructure.ts
30098
- var import_core65 = require("@axiom-lattice/core");
30728
+ var import_core68 = require("@axiom-lattice/core");
30099
30729
 
30100
30730
  // src/router/MessageRouter.ts
30101
- var import_core64 = require("@axiom-lattice/core");
30731
+ var import_core67 = require("@axiom-lattice/core");
30102
30732
  var import_protocols32 = require("@axiom-lattice/protocols");
30103
30733
  var import_crypto12 = require("crypto");
30104
30734
  var BindingNotFoundError = class extends Error {
@@ -30384,7 +31014,7 @@ var MessageRouter = class {
30384
31014
  channel: message.channel,
30385
31015
  adapterChannel: adapter.channel
30386
31016
  }, "Thread resolved by adapter strategy");
30387
- const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
31017
+ const threadStore = (0, import_core67.getStoreLattice)("default", "thread").store;
30388
31018
  try {
30389
31019
  await threadStore.createThread(
30390
31020
  tenantId,
@@ -30421,7 +31051,7 @@ var MessageRouter = class {
30421
31051
  }
30422
31052
  }
30423
31053
  if (!threadId) {
30424
- const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
31054
+ const threadStore = (0, import_core67.getStoreLattice)("default", "thread").store;
30425
31055
  const newThreadId = (0, import_crypto12.randomUUID)();
30426
31056
  console.log({
30427
31057
  event: "dispatch:thread:create",
@@ -30461,7 +31091,7 @@ var MessageRouter = class {
30461
31091
  senderId: message.sender.id,
30462
31092
  contentLength: message.content.text.length
30463
31093
  }, "Dispatching to agent");
30464
- const agent = import_core64.agentInstanceManager.getAgent({
31094
+ const agent = import_core67.agentInstanceManager.getAgent({
30465
31095
  tenant_id: tenantId,
30466
31096
  assistant_id: agentId,
30467
31097
  thread_id: threadId,
@@ -30734,7 +31364,7 @@ function createChannelInfrastructure(deps) {
30734
31364
  onMessageCommitted: deps.onMessageCommitted ?? (deps.realtime ? (message) => deps.realtime?.messageCreated(message) : void 0),
30735
31365
  onCallbackFailure: (event) => deps.warn("project-room:message-commit-callback-failed", event)
30736
31366
  };
30737
- deps.adapterRegistry.register(createRoomChannelAdapter(new import_core65.RoomAgentMessageService(writerDeps)));
31367
+ deps.adapterRegistry.register(createRoomChannelAdapter(new import_core68.RoomAgentMessageService(writerDeps)));
30738
31368
  } catch (error) {
30739
31369
  deps.warn("Room channel unavailable", { error: error instanceof Error ? error.message : String(error) });
30740
31370
  }
@@ -30754,11 +31384,168 @@ async function prepareProjectTaskRuntime(runtime, log2) {
30754
31384
  }
30755
31385
  }
30756
31386
 
31387
+ // src/services/multiReplicaReadiness.ts
31388
+ var import_core69 = require("@axiom-lattice/core");
31389
+ var SHARED_STORE_KINDS = ["lease", "outbox", "threadMessageQueue", "task", "taskWorkItem"];
31390
+ function storeClassName(kind) {
31391
+ try {
31392
+ return (0, import_core69.getStoreLattice)("default", kind).store.constructor?.name;
31393
+ } catch {
31394
+ return void 0;
31395
+ }
31396
+ }
31397
+ function warnMultiReplicaReadiness(logger8) {
31398
+ if (typeof process.env.AXIOM_REPLICA_ID !== "string" || process.env.AXIOM_REPLICA_ID.trim() === "") return;
31399
+ for (const kind of SHARED_STORE_KINDS) {
31400
+ const name = storeClassName(kind);
31401
+ if (name === void 0 || !name.startsWith("InMemory")) continue;
31402
+ logger8.warn(`Multi-replica: store '${kind}' is ${name} (not shared); configure a shared store`, {
31403
+ phase: "multi_replica_readiness",
31404
+ store: kind
31405
+ });
31406
+ }
31407
+ if (!process.env.AUTH_TOKEN_SECRET?.trim() && !process.env.JWT_SECRET?.trim()) {
31408
+ logger8.warn(
31409
+ "Multi-replica: AUTH_TOKEN_SECRET/JWT_SECRET unset; console sessions are signed per-process and will not verify across replicas",
31410
+ { phase: "multi_replica_readiness", env: "AUTH_TOKEN_SECRET" }
31411
+ );
31412
+ }
31413
+ if (!process.env.LATTICE_ENCRYPTION_KEY?.trim()) {
31414
+ logger8.warn(
31415
+ "Multi-replica: LATTICE_ENCRYPTION_KEY unset; stored credentials use the shared dev default and may not decrypt across differing keys",
31416
+ { phase: "multi_replica_readiness", env: "LATTICE_ENCRYPTION_KEY" }
31417
+ );
31418
+ }
31419
+ logger8.warn(
31420
+ "Multi-replica: agent run SSE and resume_stream are process-local; route a thread's follow-up runs to the replica streaming it (sticky routing) or share the stream/queue state",
31421
+ { phase: "multi_replica_readiness", feature: "agent-run-sse" }
31422
+ );
31423
+ }
31424
+
31425
+ // src/services/OutboxRetentionJob.ts
31426
+ var HOUR_MS = 60 * 60 * 1e3;
31427
+ var DEFAULT_INTERVAL_MS2 = 6 * HOUR_MS;
31428
+ var DEFAULT_INITIAL_DELAY_MS2 = 6e4;
31429
+ var AXIOM_OUTBOX_RETENTION_HOURS_ENV = "AXIOM_OUTBOX_RETENTION_HOURS";
31430
+ var DEFAULT_RETENTION_HOURS = 24;
31431
+ function parseRetentionHours(value) {
31432
+ if (value === void 0 || value === null) return DEFAULT_RETENTION_HOURS;
31433
+ if (typeof value !== "string") return void 0;
31434
+ const trimmed = value.trim();
31435
+ if (trimmed.length === 0) return DEFAULT_RETENTION_HOURS;
31436
+ const parsed = Number(trimmed);
31437
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
31438
+ }
31439
+ var OutboxRetentionJob = class {
31440
+ /** @param deps Outbox slice, logger, and optional retention/interval bounds. */
31441
+ constructor(deps) {
31442
+ this.deps = deps;
31443
+ this.running = false;
31444
+ this.stopped = false;
31445
+ }
31446
+ /** Schedules one delayed sweep plus the recurring interval (both unref'd). */
31447
+ start() {
31448
+ if (this.stopped || this.deps.retentionMs === void 0) return;
31449
+ if (this.initial !== void 0 || this.interval !== void 0) return;
31450
+ this.initial = setTimeout(() => {
31451
+ this.initial = void 0;
31452
+ void this.runOnce();
31453
+ }, this.deps.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS2);
31454
+ this.initial.unref?.();
31455
+ this.interval = setInterval(() => void this.runOnce(), this.deps.intervalMs ?? DEFAULT_INTERVAL_MS2);
31456
+ this.interval.unref?.();
31457
+ }
31458
+ /**
31459
+ * Runs one age-based prune.
31460
+ *
31461
+ * @returns The number of rows removed (0 when disabled, overlapping, or on failure).
31462
+ */
31463
+ async runOnce() {
31464
+ const retentionMs = this.deps.retentionMs;
31465
+ if (this.stopped || retentionMs === void 0 || this.running) return 0;
31466
+ this.running = true;
31467
+ try {
31468
+ const removed = await this.deps.outbox.pruneOlderThan(retentionMs);
31469
+ if (removed > 0) this.deps.log.info("outbox pruned", { removed });
31470
+ return removed;
31471
+ } catch (error) {
31472
+ this.deps.log.warn("outbox prune failed", {
31473
+ error: error instanceof Error ? error.message : String(error)
31474
+ });
31475
+ return 0;
31476
+ } finally {
31477
+ this.running = false;
31478
+ }
31479
+ }
31480
+ /** Stops the timers; an in-flight sweep finishes on its own. */
31481
+ stop() {
31482
+ this.stopped = true;
31483
+ if (this.initial !== void 0) {
31484
+ clearTimeout(this.initial);
31485
+ this.initial = void 0;
31486
+ }
31487
+ if (this.interval !== void 0) {
31488
+ clearInterval(this.interval);
31489
+ this.interval = void 0;
31490
+ }
31491
+ }
31492
+ };
31493
+ function createOutboxRetentionJob(deps) {
31494
+ const retentionMs = deps.retentionMs ?? resolveRetentionMs(process.env[AXIOM_OUTBOX_RETENTION_HOURS_ENV]);
31495
+ return new OutboxRetentionJob({ ...deps, retentionMs });
31496
+ }
31497
+ function resolveRetentionMs(value) {
31498
+ const hours = parseRetentionHours(value);
31499
+ return hours === void 0 ? void 0 : hours * HOUR_MS;
31500
+ }
31501
+
31502
+ // src/services/collaboration/roomEventSync.ts
31503
+ var ROOM_EVENT_TOPIC = "room-event";
31504
+ async function publishRoomEvent(outbox, owner, scope, serialized, sourceKey) {
31505
+ try {
31506
+ await outbox.append({
31507
+ topic: ROOM_EVENT_TOPIC,
31508
+ payload: { scope, serialized, sourceKey },
31509
+ originReplicaId: owner
31510
+ });
31511
+ } catch {
31512
+ }
31513
+ }
31514
+ function ingestRoomEvent(payload, broker) {
31515
+ const record = payload;
31516
+ const serialized = record?.serialized;
31517
+ if (typeof serialized !== "string" || serialized.length === 0) return false;
31518
+ const sourceKey = record?.sourceKey;
31519
+ if (typeof sourceKey === "string" && sourceKey.length > 0) {
31520
+ return broker.ingestForeignEvent(serialized, sourceKey);
31521
+ }
31522
+ return broker.ingestForeignEvent(serialized);
31523
+ }
31524
+
31525
+ // src/services/assistantRegistrySync.ts
31526
+ var ASSISTANT_CHANGE_EVENTS = ["assistant:created", "assistant:updated", "assistant:deleted"];
31527
+ var ASSISTANT_CHANGED_TOPIC = "assistant-changed";
31528
+ function bridgeAssistantEvents(subscribe, outbox, owner) {
31529
+ for (const event of ASSISTANT_CHANGE_EVENTS) {
31530
+ subscribe(event, (data) => {
31531
+ const tenantId = data?.tenantId;
31532
+ if (typeof tenantId !== "string" || tenantId.length === 0) return;
31533
+ void outbox.append({ topic: ASSISTANT_CHANGED_TOPIC, payload: { tenantId }, originReplicaId: owner }).catch(() => void 0);
31534
+ });
31535
+ }
31536
+ }
31537
+ async function handleAssistantChanged(payload, reload) {
31538
+ const tenantId = payload?.tenantId;
31539
+ if (typeof tenantId !== "string" || tenantId.length === 0) return;
31540
+ await reload(tenantId);
31541
+ }
31542
+
30757
31543
  // src/index.ts
30758
31544
  var import_protocols40 = require("@axiom-lattice/protocols");
30759
31545
  init_TopCallerRecovery();
30760
31546
  init_OrphanRecoveryRuntime();
30761
31547
  init_RecoveryCallerDeps();
31548
+ init_taskRunLiveness();
30762
31549
  process.on("unhandledRejection", (reason, promise) => {
30763
31550
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
30764
31551
  });
@@ -30772,11 +31559,11 @@ var DEFAULT_LOGGER_CONFIG = {
30772
31559
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
30773
31560
  var logger7 = loggerLattice.client;
30774
31561
  function initializeLogger(config) {
30775
- if (import_core85.loggerLatticeManager.hasLattice("default")) {
30776
- import_core85.loggerLatticeManager.removeLattice("default");
31562
+ if (import_core92.loggerLatticeManager.hasLattice("default")) {
31563
+ import_core92.loggerLatticeManager.removeLattice("default");
30777
31564
  }
30778
- (0, import_core85.registerLoggerLattice)("default", config);
30779
- return (0, import_core85.getLoggerLattice)("default");
31565
+ (0, import_core92.registerLoggerLattice)("default", config);
31566
+ return (0, import_core92.getLoggerLattice)("default");
30780
31567
  }
30781
31568
  var app = (0, import_fastify.default)({
30782
31569
  logger: false,
@@ -30837,7 +31624,7 @@ app.register(import_cors.default, {
30837
31624
  origin: true,
30838
31625
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
30839
31626
  allowedHeaders: "*",
30840
- exposedHeaders: ["Content-Type", "Content-Disposition"],
31627
+ exposedHeaders: ["Content-Type", "Content-Disposition", import_protocols40.REPLICA_HEADER, import_protocols40.AFFINITY_ECHO_HEADER],
30841
31628
  credentials: true
30842
31629
  });
30843
31630
  app.register(import_sensible.default);
@@ -30873,7 +31660,7 @@ app.setErrorHandler((error, request, reply) => {
30873
31660
  });
30874
31661
  function getConfiguredSandboxProvider() {
30875
31662
  const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
30876
- return (0, import_core85.createSandboxProvider)({
31663
+ return (0, import_core92.createSandboxProvider)({
30877
31664
  type: sandboxProviderType,
30878
31665
  remoteBaseURL: process.env.SANDBOX_BASE_URL,
30879
31666
  microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
@@ -30889,7 +31676,7 @@ function getConfiguredSandboxProvider() {
30889
31676
  }
30890
31677
  async function restoreMcpConnections() {
30891
31678
  try {
30892
- const storeLattice = (0, import_core85.getStoreLattice)("default", "mcp");
31679
+ const storeLattice = (0, import_core92.getStoreLattice)("default", "mcp");
30893
31680
  const store2 = storeLattice.store;
30894
31681
  if (!store2) {
30895
31682
  logger7.info("MCP store not configured, skipping connection restoration");
@@ -30900,25 +31687,16 @@ async function restoreMcpConnections() {
30900
31687
  );
30901
31688
  if (configs.length === 0) return;
30902
31689
  logger7.info(`Restoring ${configs.length} MCP server connections...`);
30903
- for (const config of configs) {
30904
- try {
30905
- const connection = (0, import_core85.convertToConnection)(config.config);
30906
- import_core85.mcpManager.registerServer(config.tenantId, config.key, connection, {
30907
- timeoutMs: config.config.timeout,
30908
- maxAttempts: config.config.retryAttempts,
30909
- selectedTools: config.selectedTools
30910
- });
30911
- } catch (err) {
30912
- logger7.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
30913
- }
30914
- }
30915
- await import_core85.mcpManager.connectAll();
31690
+ const deps = {
31691
+ store: store2,
31692
+ manager: import_core92.mcpManager,
31693
+ convertToConnection: import_core92.convertToConnection
31694
+ };
30916
31695
  const results = await Promise.allSettled(
30917
31696
  configs.map(
30918
- (config) => import_core85.mcpManager.registerToolsToToolLattice(
30919
- config.tenantId,
30920
- config.key,
30921
- config.selectedTools
31697
+ (config) => reconcileMcpConfigChange(
31698
+ { tenantId: config.tenantId, serverKey: config.key },
31699
+ deps
30922
31700
  )
30923
31701
  )
30924
31702
  );
@@ -30926,7 +31704,7 @@ async function restoreMcpConnections() {
30926
31704
  if (result.status === "rejected") {
30927
31705
  const config = configs[index];
30928
31706
  logger7.warn(
30929
- `Failed to register tools for MCP server "${config.tenantId}/${config.key}"`,
31707
+ `Failed to restore MCP server "${config.tenantId}/${config.key}"`,
30930
31708
  { error: result.reason }
30931
31709
  );
30932
31710
  }
@@ -30953,9 +31731,9 @@ var start = async (config) => {
30953
31731
  let projectRoomRealtimeRuntime;
30954
31732
  if (projectRoomRealtimeMode === "single-gateway") {
30955
31733
  projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
30956
- tasks: (0, import_core85.getStoreLattice)("default", "task").store,
30957
- rooms: (0, import_core85.getStoreLattice)("default", "projectRoom").store,
30958
- memberships: (0, import_core85.getStoreLattice)("default", "projectBotMembership").store,
31734
+ tasks: (0, import_core92.getStoreLattice)("default", "task").store,
31735
+ rooms: (0, import_core92.getStoreLattice)("default", "projectRoom").store,
31736
+ memberships: (0, import_core92.getStoreLattice)("default", "projectBotMembership").store,
30959
31737
  log: { warn: (message, context) => logger7.warn(message, context) }
30960
31738
  });
30961
31739
  }
@@ -30969,7 +31747,7 @@ var start = async (config) => {
30969
31747
  const infrastructure = createChannelInfrastructure({
30970
31748
  adapterRegistry,
30971
31749
  getStore: ((kind) => getStore11("default", kind).store),
30972
- trustedDispatcher: (0, import_core85.createTrustedAgentMessageDispatcher)((scope) => import_core85.agentInstanceManager.getAgent(scope)),
31750
+ trustedDispatcher: (0, import_core92.createTrustedAgentMessageDispatcher)((scope) => import_core92.agentInstanceManager.getAgent(scope)),
30973
31751
  middlewares: [
30974
31752
  createDeduplicationMiddleware(),
30975
31753
  createRateLimitMiddleware(),
@@ -30980,7 +31758,7 @@ var start = async (config) => {
30980
31758
  });
30981
31759
  const bindingStore = infrastructure.bindingRegistry;
30982
31760
  const installationStore = infrastructure.installationStore;
30983
- (0, import_core84.setBindingRegistry)(bindingStore);
31761
+ (0, import_core91.setBindingRegistry)(bindingStore);
30984
31762
  const router = infrastructure.router;
30985
31763
  app.addHook("onClose", async () => router.dispose());
30986
31764
  channelDeps = {
@@ -30996,10 +31774,10 @@ var start = async (config) => {
30996
31774
  error: err instanceof Error ? err.message : String(err)
30997
31775
  });
30998
31776
  }
30999
- (0, import_core84.setEvalRunService)(evalRunner);
31777
+ (0, import_core91.setEvalRunService)(evalRunner);
31000
31778
  try {
31001
- const menuStore = (0, import_core85.getStoreLattice)("default", "menu").store;
31002
- (0, import_core84.setMenuRegistry)(menuStore);
31779
+ const menuStore = (0, import_core92.getStoreLattice)("default", "menu").store;
31780
+ (0, import_core91.setMenuRegistry)(menuStore);
31003
31781
  logger7.info("Menu registry initialized");
31004
31782
  } catch {
31005
31783
  }
@@ -31016,10 +31794,10 @@ var start = async (config) => {
31016
31794
  const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await Promise.resolve().then(() => (init_A2AAuthService(), A2AAuthService_exports));
31017
31795
  const { registerA2AStandardRoutes: registerA2AStandardRoutes2 } = await Promise.resolve().then(() => (init_a2a_standard(), a2a_standard_exports));
31018
31796
  const a2a = await Promise.resolve().then(() => (init_a2a(), a2a_exports));
31019
- const a2aKeyStore = (0, import_core85.getStoreLattice)("default", "a2aApiKey").store;
31020
- const assistantStore = (0, import_core85.getStoreLattice)("default", "assistant").store;
31021
- const projectStore = (0, import_core85.getStoreLattice)("default", "project").store;
31022
- const taskStore = (0, import_core85.getStoreLattice)("default", "task").store;
31797
+ const a2aKeyStore = (0, import_core92.getStoreLattice)("default", "a2aApiKey").store;
31798
+ const assistantStore = (0, import_core92.getStoreLattice)("default", "assistant").store;
31799
+ const projectStore = (0, import_core92.getStoreLattice)("default", "project").store;
31800
+ const taskStore = (0, import_core92.getStoreLattice)("default", "task").store;
31023
31801
  const authService = new A2AAuthService2({
31024
31802
  keyStore: a2aKeyStore,
31025
31803
  assistantStore,
@@ -31058,8 +31836,8 @@ var start = async (config) => {
31058
31836
  const { CollectionService: CollectionService2 } = await Promise.resolve().then(() => (init_collection_service(), collection_service_exports));
31059
31837
  openLattice2.register(createKbProvider2(new CollectionService2()));
31060
31838
  openLattice2.register(createOpenSandboxProvider2());
31061
- const threadStore = (0, import_core85.getStoreLattice)("default", "thread").store;
31062
- const assistantStore = (0, import_core85.getStoreLattice)("default", "assistant").store;
31839
+ const threadStore = (0, import_core92.getStoreLattice)("default", "thread").store;
31840
+ const assistantStore = (0, import_core92.getStoreLattice)("default", "assistant").store;
31063
31841
  openLattice2.register(
31064
31842
  createAgentProvider2({
31065
31843
  threadStore,
@@ -31082,18 +31860,29 @@ var start = async (config) => {
31082
31860
  error: err instanceof Error ? err.message : String(err)
31083
31861
  });
31084
31862
  }
31085
- if (!import_core85.sandboxLatticeManager.hasLattice("default")) {
31086
- import_core85.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
31863
+ try {
31864
+ const { registerGatewaySandboxOpenCredentialProvider: registerGatewaySandboxOpenCredentialProvider2 } = await Promise.resolve().then(() => (init_sandboxOpenCredentialProvider(), sandboxOpenCredentialProvider_exports));
31865
+ registerGatewaySandboxOpenCredentialProvider2((error) => {
31866
+ logger7.warn("sandbox open-credential provider failed", { error });
31867
+ });
31868
+ logger7.info("Sandbox open-credential provider registered");
31869
+ } catch (err) {
31870
+ logger7.warn("Sandbox open-credential provider unavailable", {
31871
+ error: err instanceof Error ? err.message : String(err)
31872
+ });
31873
+ }
31874
+ if (!import_core92.sandboxLatticeManager.hasLattice("default")) {
31875
+ import_core92.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
31087
31876
  logger7.info("Registered sandbox manager from env configuration");
31088
31877
  }
31089
31878
  try {
31090
31879
  const { ResourceController: ResourceController2 } = await Promise.resolve().then(() => (init_resources(), resources_exports));
31091
- const sharedResourceStore = (0, import_core85.getStoreLattice)("default", "sharedResource").store;
31092
- const cache = new import_core85.TokenCache();
31880
+ const sharedResourceStore = (0, import_core92.getStoreLattice)("default", "sharedResource").store;
31881
+ const cache2 = new import_core92.TokenCache();
31093
31882
  const resourceController = new ResourceController2({
31094
31883
  store: sharedResourceStore,
31095
- sandboxManager: import_core85.sandboxLatticeManager,
31096
- cache,
31884
+ sandboxManager: import_core92.sandboxLatticeManager,
31885
+ cache: cache2,
31097
31886
  logger: logger7
31098
31887
  });
31099
31888
  registerResourceRoutes(app, resourceController);
@@ -31117,30 +31906,98 @@ var start = async (config) => {
31117
31906
  }
31118
31907
  }
31119
31908
  const target_port = config?.port || Number(process.env.PORT) || 4001;
31120
- if (routes.projectTaskRuntime) {
31121
- await prepareProjectTaskRuntime(routes.projectTaskRuntime, logger7);
31909
+ (0, import_core92.configureCoordinationLeaseStore)((0, import_core92.getStoreLattice)("default", "lease").store);
31910
+ const outboxStore = (0, import_core92.getStoreLattice)("default", "outbox").store;
31911
+ import_core92.taskLifecycleNotifier.setOutbox(outboxStore);
31912
+ import_core92.projectTaskAdmissionNotifier.setOutbox(outboxStore);
31913
+ (0, import_core92.configurePluginCatalogSync)({ outbox: outboxStore, owner: (0, import_core92.getReplicaId)() });
31914
+ bridgeAssistantEvents(
31915
+ (event, cb) => import_core92.eventBus.subscribe(event, cb),
31916
+ outboxStore,
31917
+ (0, import_core92.getReplicaId)()
31918
+ );
31919
+ if (projectRoomRealtimeRuntime) {
31920
+ projectRoomRealtimeRuntime.broker.setBroadcastHook((scope, serialized, sourceKey) => {
31921
+ void publishRoomEvent(outboxStore, (0, import_core92.getReplicaId)(), scope, serialized, sourceKey);
31922
+ });
31122
31923
  }
31123
- if (process.env.AXIOM_TASK_ORPHAN_RECOVERY === "true") {
31124
- try {
31125
- const { createOrphanRecoveryRuntime: createOrphanRecoveryRuntime2 } = await Promise.resolve().then(() => (init_OrphanRecoveryRuntime(), OrphanRecoveryRuntime_exports));
31126
- const { createRecoveryCallerDeps: createRecoveryCallerDeps2 } = await Promise.resolve().then(() => (init_RecoveryCallerDeps(), RecoveryCallerDeps_exports));
31127
- const taskStore = (0, import_core85.getStoreLattice)("default", "task").store;
31128
- const workItemStore = (0, import_core85.getStoreLattice)("default", "taskWorkItem").store;
31129
- const tenantStore = (0, import_core85.getStoreLattice)("default", "tenant").store;
31130
- const runtime = createOrphanRecoveryRuntime2({
31131
- taskStore,
31132
- workItemStore,
31133
- tenantStore,
31134
- ...createRecoveryCallerDeps2(),
31135
- log: logger7
31136
- });
31137
- await runtime.recoverAll();
31138
- } catch (err) {
31139
- logger7.warn("Task orphan recovery failed", { error: err instanceof Error ? err.message : String(err) });
31924
+ const outboxPoller = new import_core92.OutboxPoller({
31925
+ store: outboxStore,
31926
+ owner: (0, import_core92.getReplicaId)(),
31927
+ handlers: {
31928
+ "task-lifecycle": (payload) => import_core92.taskLifecycleNotifier.deliverFromOutbox(payload),
31929
+ "project-task-admission": (payload) => import_core92.projectTaskAdmissionNotifier.deliverFromOutbox(payload),
31930
+ [ROOM_EVENT_TOPIC]: async (payload) => {
31931
+ if (projectRoomRealtimeRuntime) ingestRoomEvent(payload, projectRoomRealtimeRuntime.broker);
31932
+ },
31933
+ "assistant-changed": (payload) => handleAssistantChanged(payload, (tenantId) => import_core92.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId)),
31934
+ [MCP_CONFIG_CHANGED_TOPIC]: (payload) => reconcileMcpConfigChange(payload, {
31935
+ store: (0, import_core92.getStoreLattice)("default", "mcp").store,
31936
+ manager: import_core92.mcpManager,
31937
+ convertToConnection: import_core92.convertToConnection
31938
+ }),
31939
+ [import_core92.PLUGIN_CATALOG_CHANGED_TOPIC]: async (payload) => {
31940
+ const tenantId = payload?.tenantId;
31941
+ if (typeof tenantId === "string" && tenantId.length > 0) (0, import_core92.getSandboxPluginCatalog)().invalidate(tenantId);
31942
+ }
31943
+ },
31944
+ log: logger7
31945
+ });
31946
+ try {
31947
+ await outboxPoller.start();
31948
+ } catch (err) {
31949
+ logger7.warn("Outbox poller failed to start", { error: err instanceof Error ? err.message : String(err) });
31950
+ }
31951
+ const leaseStore = (0, import_core92.getStoreLattice)("default", "lease").store;
31952
+ if (leaseStore.constructor?.name === "InMemoryLeaseStore") {
31953
+ logger7.warn(
31954
+ "Startup recovery leader gate uses a non-distributed lease store (per-process only); configure a shared 'lease' store for multi-replica deployments",
31955
+ { phase: "startup_recovery" }
31956
+ );
31957
+ }
31958
+ warnMultiReplicaReadiness(logger7);
31959
+ const startup = await (0, import_core92.runAsLeader)(leaseStore, "startup-recovery", async () => {
31960
+ if (routes.projectTaskRuntime) {
31961
+ await prepareProjectTaskRuntime(routes.projectTaskRuntime, logger7);
31140
31962
  }
31963
+ if (process.env.AXIOM_TASK_ORPHAN_RECOVERY === "true") {
31964
+ try {
31965
+ const { createOrphanRecoveryRuntime: createOrphanRecoveryRuntime2 } = await Promise.resolve().then(() => (init_OrphanRecoveryRuntime(), OrphanRecoveryRuntime_exports));
31966
+ const { createRecoveryCallerDeps: createRecoveryCallerDeps2 } = await Promise.resolve().then(() => (init_RecoveryCallerDeps(), RecoveryCallerDeps_exports));
31967
+ const runtime = createOrphanRecoveryRuntime2({
31968
+ taskStore: (0, import_core92.getStoreLattice)("default", "task").store,
31969
+ workItemStore: (0, import_core92.getStoreLattice)("default", "taskWorkItem").store,
31970
+ tenantStore: (0, import_core92.getStoreLattice)("default", "tenant").store,
31971
+ ...createRecoveryCallerDeps2(),
31972
+ log: logger7
31973
+ });
31974
+ await runtime.recoverAll();
31975
+ } catch (err) {
31976
+ logger7.warn("Task orphan recovery failed", { error: err instanceof Error ? err.message : String(err) });
31977
+ }
31978
+ }
31979
+ if (import_core92.scheduleLatticeManager.hasLattice("default")) {
31980
+ try {
31981
+ const restored = await import_core92.scheduleLatticeManager.getScheduleLattice("default").restore();
31982
+ logger7.info("Scheduled tasks restored", { phase: "startup_recovery", restored });
31983
+ } catch (err) {
31984
+ logger7.warn("Scheduled task restore failed", {
31985
+ phase: "startup_recovery",
31986
+ error: err instanceof Error ? err.message : String(err)
31987
+ });
31988
+ }
31989
+ }
31990
+ }, { hold: true, ttlMs: 5 * 6e4 });
31991
+ if (!startup.ran) {
31992
+ logger7.info("Startup recovery skipped (another replica leads)", { phase: "startup_recovery" });
31141
31993
  }
31142
31994
  await app.listen({ port: target_port, host: "0.0.0.0" });
31143
31995
  logger7.info(`Lattice Gateway is running on port: ${target_port}`);
31996
+ const outboxRetention = createOutboxRetentionJob({
31997
+ outbox: outboxStore,
31998
+ log: logger7
31999
+ });
32000
+ outboxRetention.start();
31144
32001
  const queueServiceConfig = config?.queueServiceConfig;
31145
32002
  if (queueServiceConfig) {
31146
32003
  setQueueServiceType(queueServiceConfig.type);
@@ -31167,8 +32024,13 @@ var LatticeGateway = {
31167
32024
  // Annotate the CommonJS export names for ESM import in node:
31168
32025
  0 && (module.exports = {
31169
32026
  LatticeGateway,
32027
+ ProjectRoomEventBroker,
32028
+ ROOM_EVENT_TOPIC,
31170
32029
  createOrphanRecoveryRuntime,
31171
32030
  createRecoveryCallerDeps,
32031
+ createTaskRunLiveness,
32032
+ ingestRoomEvent,
32033
+ publishRoomEvent,
31172
32034
  recoverTask,
31173
32035
  recoverTopCaller,
31174
32036
  resolveTopCaller,