@axiom-lattice/gateway 4.6.1 → 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 (64) hide show
  1. package/dist/OrphanRecoveryRuntime-BEFILJMU.mjs +8 -0
  2. package/dist/RecoveryCallerDeps-6SJUYJ5S.mjs +8 -0
  3. package/dist/RecoveryCallerDeps-6SJUYJ5S.mjs.map +1 -0
  4. package/dist/{a2a-standard-PIOKEHPS.mjs → a2a-standard-PBYPNHOA.mjs} +59 -21
  5. package/dist/a2a-standard-PBYPNHOA.mjs.map +1 -0
  6. package/dist/{chunk-2POW27EI.mjs → chunk-7CG57LEF.mjs} +2 -2
  7. package/dist/{chunk-WKNZ7GBC.mjs → chunk-EXPERMZR.mjs} +1 -1
  8. package/dist/chunk-EXPERMZR.mjs.map +1 -0
  9. package/dist/{chunk-52SUWMOX.mjs → chunk-JUA3VJE7.mjs} +3 -2
  10. package/dist/chunk-JUA3VJE7.mjs.map +1 -0
  11. package/dist/chunk-NOXGKYUV.mjs +37 -0
  12. package/dist/chunk-NOXGKYUV.mjs.map +1 -0
  13. package/dist/{chunk-526UYVNQ.mjs → chunk-NPHK4SZ6.mjs} +4 -4
  14. package/dist/{chunk-TJAUXMYJ.mjs → chunk-URXZPI2F.mjs} +8 -1
  15. package/dist/chunk-URXZPI2F.mjs.map +1 -0
  16. package/dist/chunk-ZBTG3T7K.mjs +324 -0
  17. package/dist/chunk-ZBTG3T7K.mjs.map +1 -0
  18. package/dist/embed/{embed-page-AgentDetailSideApp-7NYLPLAX.js → embed-page-AgentDetailSideApp-NTLAFO5C.js} +1 -1
  19. package/dist/embed/{embed-page-AgentGovernanceWorkbench-LNGNSI7X.js → embed-page-AgentGovernanceWorkbench-QPVBOMLH.js} +1 -1
  20. package/dist/embed/{embed-page-MessageGraph-E6DZDT65.js → embed-page-MessageGraph-JAJGMYKH.js} +1 -1
  21. package/dist/embed/{embed-page-ReactChart-3VE7F5VJ.js → embed-page-ReactChart-WUG6TEAZ.js} +1 -1
  22. package/dist/embed/{embed-page-SkillFlow-XMMHEHQL.js → embed-page-SkillFlow-TSBZ4V2P.js} +1 -1
  23. package/dist/embed/{embed-page-TasksSideApp-QM5TZ7EG.js → embed-page-TasksSideApp-TJI22YGD.js} +1 -1
  24. package/dist/embed/{embed-page-TeamWorkspace-E7RFY3ZY.js → embed-page-TeamWorkspace-OJJLN5VI.js} +1 -1
  25. package/dist/embed/{embed-page-TopologyRuntimeView-DLPDUB7Z.js → embed-page-TopologyRuntimeView-ZFU4N6TV.js} +1 -1
  26. package/dist/embed/{embed-page-WorkflowAutomationView-FKSVOHHB.js → embed-page-WorkflowAutomationView-ALXV7VQQ.js} +1 -1
  27. package/dist/embed/{embed-page-chunk-I7PWCKAG.js → embed-page-chunk-DZQBVJ7Z.js} +1 -1
  28. package/dist/embed/{embed-page-chunk-WWTU55RX.js → embed-page-chunk-IPIJ2SG6.js} +1 -1
  29. package/dist/embed/{embed-page-chunk-KN7D66KL.js → embed-page-chunk-N22EGQAJ.js} +1 -1
  30. package/dist/embed/{embed-page-chunk-EXCJ42IB.js → embed-page-chunk-O72XBKQH.js} +2 -2
  31. package/dist/embed/{embed-page-chunk-U276WBDI.js → embed-page-chunk-QMVIL4BJ.js} +190 -185
  32. package/dist/embed/{embed-page-chunk-H6TK3FAR.js → embed-page-chunk-UT3WYBZH.js} +1 -1
  33. package/dist/embed/embed-page-chunk-YPNSOZYW.js +8 -0
  34. package/dist/embed/embed-page.js +1 -1
  35. package/dist/index.d.mts +362 -10
  36. package/dist/index.d.ts +362 -10
  37. package/dist/index.js +1693 -450
  38. package/dist/index.js.map +1 -1
  39. package/dist/index.mjs +714 -154
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/{open-AMEMJOFJ.mjs → open-UVKMONVE.mjs} +10 -9
  42. package/dist/open-UVKMONVE.mjs.map +1 -0
  43. package/dist/{open-exposure-I44Q4M3T.mjs → open-exposure-VU227IO3.mjs} +5 -5
  44. package/dist/{open-files-EJ6O2SGX.mjs → open-files-X3HXVEEM.mjs} +6 -6
  45. package/dist/{open-session-LBTCU3AE.mjs → open-session-2Y7XJJW2.mjs} +6 -6
  46. package/dist/{resources-DXPXAXEZ.mjs → resources-2L3KSUUU.mjs} +2 -2
  47. package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs +275 -0
  48. package/dist/sandboxOpenCredentialProvider-USIU7Z7H.mjs.map +1 -0
  49. package/dist/{sandboxProvider-I3P66VXT.mjs → sandboxProvider-DUYWHYJN.mjs} +2 -2
  50. package/dist/sandboxProvider-DUYWHYJN.mjs.map +1 -0
  51. package/package.json +14 -8
  52. package/dist/a2a-standard-PIOKEHPS.mjs.map +0 -1
  53. package/dist/chunk-52SUWMOX.mjs.map +0 -1
  54. package/dist/chunk-TJAUXMYJ.mjs.map +0 -1
  55. package/dist/chunk-WKNZ7GBC.mjs.map +0 -1
  56. package/dist/embed/embed-page-chunk-HEMNMC4X.js +0 -7
  57. package/dist/open-AMEMJOFJ.mjs.map +0 -1
  58. /package/dist/{sandboxProvider-I3P66VXT.mjs.map → OrphanRecoveryRuntime-BEFILJMU.mjs.map} +0 -0
  59. /package/dist/{chunk-2POW27EI.mjs.map → chunk-7CG57LEF.mjs.map} +0 -0
  60. /package/dist/{chunk-526UYVNQ.mjs.map → chunk-NPHK4SZ6.mjs.map} +0 -0
  61. /package/dist/{open-exposure-I44Q4M3T.mjs.map → open-exposure-VU227IO3.mjs.map} +0 -0
  62. /package/dist/{open-files-EJ6O2SGX.mjs.map → open-files-X3HXVEEM.mjs.map} +0 -0
  63. /package/dist/{open-session-LBTCU3AE.mjs.map → open-session-2Y7XJJW2.mjs.map} +0 -0
  64. /package/dist/{resources-DXPXAXEZ.mjs.map → resources-2L3KSUUU.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -874,6 +874,44 @@ var init_wechat_client = __esm({
874
874
  }
875
875
  });
876
876
 
877
+ // src/services/collaboration/projectTaskScope.ts
878
+ function isProjectLifecycleTask(task) {
879
+ const source = task.context?.source;
880
+ return typeof task.workspaceId === "string" && task.workspaceId.length > 0 && typeof task.projectId === "string" && task.projectId.length > 0 && (source === "project_room" || source === "project_task" || source === "user");
881
+ }
882
+ function isRosterExemptProject(project) {
883
+ return project?.kind === "public";
884
+ }
885
+ function resolveTaskOwnerMembership(task, project, room, membership) {
886
+ const active = membership && membership.status === "active" && membership.tenantId === task.tenantId && membership.workspaceId === task.workspaceId && membership.projectId === task.projectId && membership.assistantId === task.ownerId && membership.roomId === room.id;
887
+ if (active) return membership;
888
+ if (!isRosterExemptProject(project)) return void 0;
889
+ return syntheticPublicProjectOwnerMembership(task, room);
890
+ }
891
+ function syntheticPublicProjectOwnerMembership(task, room) {
892
+ const threadId = typeof task.context?.thread_id === "string" && task.context.thread_id.length > 0 ? task.context.thread_id : task.ownerId;
893
+ return {
894
+ id: `public-project-owner:${task.projectId}:${task.ownerId}`,
895
+ tenantId: task.tenantId,
896
+ workspaceId: task.workspaceId,
897
+ projectId: task.projectId,
898
+ roomId: room.id,
899
+ assistantId: task.ownerId,
900
+ role: "specialist",
901
+ title: task.ownerId,
902
+ mentionName: task.ownerId,
903
+ status: "active",
904
+ roomThreadId: threadId,
905
+ joinedAt: task.createdAt,
906
+ updatedAt: task.updatedAt
907
+ };
908
+ }
909
+ var init_projectTaskScope = __esm({
910
+ "src/services/collaboration/projectTaskScope.ts"() {
911
+ "use strict";
912
+ }
913
+ });
914
+
877
915
  // src/services/files/task-file-refs.ts
878
916
  function formatFileRefsSection(refs) {
879
917
  const lines = refs.map((ref) => {
@@ -889,6 +927,336 @@ var init_task_file_refs = __esm({
889
927
  }
890
928
  });
891
929
 
930
+ // src/services/collaboration/TopCallerRecovery.ts
931
+ function hasPendingInterrupts(state) {
932
+ return (state.tasks ?? []).some((t2) => (t2.interrupts?.length ?? 0) > 0);
933
+ }
934
+ function pendingTaskCalls(state) {
935
+ const messages = state.values?.messages ?? [];
936
+ const resolved = new Set(
937
+ messages.filter((m) => m.tool_call_id).map((m) => m.tool_call_id)
938
+ );
939
+ const calls = [];
940
+ for (const m of messages) {
941
+ const typed = m;
942
+ if (typeof typed.getType !== "function" || typed.getType() !== "ai") continue;
943
+ const toolCalls = m.tool_calls;
944
+ if (!Array.isArray(toolCalls)) continue;
945
+ for (const c2 of toolCalls) {
946
+ if (c2.name !== "task" || !c2.id || resolved.has(c2.id)) continue;
947
+ const taskId = typeof c2.args?.taskId === "string" ? c2.args.taskId : void 0;
948
+ calls.push({ id: c2.id, ...taskId ? { taskId } : {} });
949
+ }
950
+ }
951
+ return calls;
952
+ }
953
+ function outcome(task) {
954
+ if (task.status === "completed") {
955
+ return JSON.stringify({ success: true, taskId: task.id, result: task.result ?? "" });
956
+ }
957
+ if (task.status === "failed") {
958
+ return JSON.stringify({ success: false, code: "TASK_FAILED", retryable: false, taskId: task.id, error: task.failureReason ?? "" });
959
+ }
960
+ return JSON.stringify({
961
+ success: false,
962
+ code: "TASK_INTERRUPTED",
963
+ retryable: false,
964
+ taskId: task.id,
965
+ error: "Run ended before completion; the task is interrupted and awaiting a decision."
966
+ });
967
+ }
968
+ async function backfillTopCallerGroup(tasks, top, deps) {
969
+ const registry = deps.registry ?? import_core70.taskRunRegistry;
970
+ const results = [];
971
+ const eligible = [];
972
+ for (const task of tasks) {
973
+ if (!task.context?.caller_thread_id) {
974
+ results.push({ id: task.id, reason: "skipped:no_caller_link" });
975
+ continue;
976
+ }
977
+ if (registry.isLive(task.id) || deps.isTaskLive && await deps.isTaskLive(task)) {
978
+ results.push({ id: task.id, reason: "skipped:live" });
979
+ continue;
980
+ }
981
+ eligible.push(task);
982
+ }
983
+ if (eligible.length === 0) return results;
984
+ const target = await deps.callerFor(top, eligible[0].tenantId);
985
+ if (!target) {
986
+ return [...results, ...eligible.map((task) => ({ id: task.id, reason: "skipped:no_caller_client" }))];
987
+ }
988
+ const state = await target.client.getState(target.config);
989
+ if (hasPendingInterrupts(state)) {
990
+ return [...results, ...eligible.map((task) => ({ id: task.id, reason: "skipped:hitl" }))];
991
+ }
992
+ const nodeName = (state.tasks ?? [])[0]?.name;
993
+ if (!nodeName) {
994
+ return [...results, ...eligible.map((task) => ({ id: task.id, reason: "skipped:no_pending_node" }))];
995
+ }
996
+ const calls = pendingTaskCalls(state);
997
+ const matched = [];
998
+ for (const task of eligible) {
999
+ const call = calls.find((candidate) => candidate.taskId === task.id);
1000
+ if (!call) {
1001
+ results.push({ id: task.id, reason: "skipped:no_pending_task_call" });
1002
+ continue;
1003
+ }
1004
+ matched.push({ task, call });
1005
+ }
1006
+ if (matched.length === 0) return results;
1007
+ await target.client.updateState(target.config, {
1008
+ messages: matched.map(({ task, call }) => new import_messages.ToolMessage({ content: outcome(task), tool_call_id: call.id, name: "task" }))
1009
+ }, nodeName);
1010
+ await deps.resume(target.client, target.config, target.agent);
1011
+ return [...results, ...matched.map(({ task }) => ({ id: task.id, reason: "recovered" }))];
1012
+ }
1013
+ async function recoverTopCaller(task, top, deps) {
1014
+ if (!task.context?.caller_thread_id) return "skipped:no_caller_link";
1015
+ if (task.context.caller_thread_id !== top.threadId) return "skipped:middle";
1016
+ const [first] = await backfillTopCallerGroup([task], top, deps);
1017
+ return first?.reason ?? "skipped:no_pending_task_call";
1018
+ }
1019
+ async function recoverTask(task, deps) {
1020
+ if (task.status !== "in_progress") {
1021
+ return { kind: "skipped", reason: "not_in_progress" };
1022
+ }
1023
+ const registry = deps.registry ?? import_core70.taskRunRegistry;
1024
+ if (registry.isLive(task.id) || deps.isTaskLive && await deps.isTaskLive(task)) {
1025
+ return { kind: "skipped", reason: "live" };
1026
+ }
1027
+ const { convergedTasks } = await (0, import_core70.reconcileStaleTaskRuns)({
1028
+ tasks: [task],
1029
+ actor: "system:on-demand-recovery",
1030
+ lifecycle: deps.lifecycle,
1031
+ ...deps.registry ? { registry: deps.registry } : {},
1032
+ ...deps.isTaskLive ? { isTaskLive: deps.isTaskLive } : {}
1033
+ });
1034
+ const converged = convergedTasks[0];
1035
+ if (!converged) {
1036
+ return { kind: "skipped", reason: "convergence_failed" };
1037
+ }
1038
+ const top = resolveTopCaller(converged, deps.orphans ?? [converged]);
1039
+ if (!top) {
1040
+ return { kind: "skipped", reason: "no_top_caller" };
1041
+ }
1042
+ if (converged.context?.caller_thread_id !== top.threadId) {
1043
+ return { kind: "skipped", reason: "middle" };
1044
+ }
1045
+ const reason = await recoverTopCaller(converged, top, {
1046
+ callerFor: deps.callerFor,
1047
+ resume: deps.resume,
1048
+ ...deps.registry ? { registry: deps.registry } : {},
1049
+ ...deps.isTaskLive ? { isTaskLive: deps.isTaskLive } : {}
1050
+ });
1051
+ if (reason === "recovered") return { kind: "recovered" };
1052
+ if (reason === "skipped:no_pending_task_call") return { kind: "converged_no_call" };
1053
+ return { kind: "skipped", reason: reason.replace(/^skipped:/, "") };
1054
+ }
1055
+ function resolveTopCaller(task, orphans) {
1056
+ const tenantId = task.tenantId;
1057
+ const runThreadToTask = new Map(
1058
+ orphans.map((orphan) => [`${orphan.tenantId}:${orphan.context?.thread_id}`, orphan])
1059
+ );
1060
+ let threadId = task.context?.caller_thread_id;
1061
+ let assistantId = task.context?.caller_assistant_id;
1062
+ const seen = /* @__PURE__ */ new Set();
1063
+ while (threadId) {
1064
+ const scopeKey3 = `${tenantId}:${threadId}`;
1065
+ if (!runThreadToTask.has(scopeKey3) || seen.has(scopeKey3)) break;
1066
+ seen.add(scopeKey3);
1067
+ const parent = runThreadToTask.get(scopeKey3);
1068
+ threadId = parent.context?.caller_thread_id;
1069
+ assistantId = parent.context?.caller_assistant_id;
1070
+ }
1071
+ return threadId ? { threadId, ...assistantId ? { assistantId } : {} } : void 0;
1072
+ }
1073
+ async function runOrphanRecovery(deps) {
1074
+ let convergedTasks;
1075
+ try {
1076
+ ({ convergedTasks } = await deps.reconcile());
1077
+ } catch (error) {
1078
+ deps.log.warn("Task orphan recovery reconcile failed", {
1079
+ error: error instanceof Error ? error.message : String(error)
1080
+ });
1081
+ return { recovered: [], skipped: ["reconcile:error"] };
1082
+ }
1083
+ const orphans = convergedTasks;
1084
+ const recovered = [];
1085
+ const skipped = [];
1086
+ const groups = /* @__PURE__ */ new Map();
1087
+ for (const task of orphans) {
1088
+ const top = resolveTopCaller(task, orphans);
1089
+ if (!top) {
1090
+ skipped.push(`${task.id}:skipped:no_top_caller`);
1091
+ continue;
1092
+ }
1093
+ if (task.context?.caller_thread_id !== top.threadId) {
1094
+ skipped.push(`${task.id}:skipped:middle`);
1095
+ continue;
1096
+ }
1097
+ const key = `${task.tenantId}:${top.threadId}`;
1098
+ const group = groups.get(key);
1099
+ if (group) group.tasks.push(task);
1100
+ else groups.set(key, { top, tasks: [task] });
1101
+ }
1102
+ for (const { top, tasks } of groups.values()) {
1103
+ try {
1104
+ const results = await backfillTopCallerGroup(tasks, top, {
1105
+ callerFor: deps.callerFor,
1106
+ resume: deps.resume,
1107
+ ...deps.isTaskLive ? { isTaskLive: deps.isTaskLive } : {}
1108
+ });
1109
+ for (const { id: id3, reason } of results) {
1110
+ (reason === "recovered" ? recovered : skipped).push(`${id3}:${reason}`);
1111
+ }
1112
+ } catch (error) {
1113
+ for (const task of tasks) skipped.push(`${task.id}:error`);
1114
+ deps.log.warn("Task orphan recovery group failed", {
1115
+ threadId: top.threadId,
1116
+ error: error instanceof Error ? error.message : String(error)
1117
+ });
1118
+ }
1119
+ }
1120
+ deps.log.info("Task orphan recovery complete", { recovered, skipped });
1121
+ return { recovered, skipped };
1122
+ }
1123
+ var import_messages, import_core70;
1124
+ var init_TopCallerRecovery = __esm({
1125
+ "src/services/collaboration/TopCallerRecovery.ts"() {
1126
+ "use strict";
1127
+ import_messages = require("@langchain/core/messages");
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");
1158
+ }
1159
+ });
1160
+
1161
+ // src/services/collaboration/OrphanRecoveryRuntime.ts
1162
+ var OrphanRecoveryRuntime_exports = {};
1163
+ __export(OrphanRecoveryRuntime_exports, {
1164
+ createOrphanRecoveryRuntime: () => createOrphanRecoveryRuntime
1165
+ });
1166
+ function createOrphanRecoveryRuntime(deps) {
1167
+ const lifecycle = deps.lifecycle ?? (0, import_core72.createTaskLifecycleService)({
1168
+ taskStore: deps.taskStore,
1169
+ workItemStore: deps.workItemStore
1170
+ });
1171
+ return {
1172
+ async recoverAll() {
1173
+ const isTaskLive = deps.isTaskLive ?? createTaskRunLiveness();
1174
+ return runOrphanRecovery({
1175
+ reconcile: () => (0, import_core72.reconcileAllTenantTaskRuns)({
1176
+ actor: "system:orphan-recovery",
1177
+ tenantStore: {
1178
+ listTenants: async () => (await deps.tenantStore.getAllTenants()).map((tenant2) => ({ id: tenant2.id }))
1179
+ },
1180
+ taskStore: deps.taskStore,
1181
+ lifecycle,
1182
+ include: (t2) => !isProjectLifecycleTask(t2),
1183
+ ...isTaskLive ? { isTaskLive } : {}
1184
+ }),
1185
+ callerFor: deps.callerFor,
1186
+ resume: deps.resume,
1187
+ ...isTaskLive ? { isTaskLive } : {},
1188
+ log: deps.log
1189
+ });
1190
+ },
1191
+ async recoverTask(input) {
1192
+ const task = await deps.taskStore.getById(input.tenantId, input.taskId);
1193
+ if (!task) return { kind: "skipped", reason: "not_found" };
1194
+ if (isProjectLifecycleTask(task)) {
1195
+ return { kind: "skipped", reason: "project_task" };
1196
+ }
1197
+ const orphans = await deps.taskStore.list({ tenantId: input.tenantId, status: "in_progress" });
1198
+ const isTaskLive = deps.isTaskLive ?? createTaskRunLiveness();
1199
+ return recoverTask(task, {
1200
+ lifecycle,
1201
+ registry: import_core72.taskRunRegistry,
1202
+ orphans,
1203
+ callerFor: deps.callerFor,
1204
+ resume: deps.resume,
1205
+ ...isTaskLive ? { isTaskLive } : {}
1206
+ });
1207
+ }
1208
+ };
1209
+ }
1210
+ var import_core72;
1211
+ var init_OrphanRecoveryRuntime = __esm({
1212
+ "src/services/collaboration/OrphanRecoveryRuntime.ts"() {
1213
+ "use strict";
1214
+ import_core72 = require("@axiom-lattice/core");
1215
+ init_TopCallerRecovery();
1216
+ init_projectTaskScope();
1217
+ init_taskRunLiveness();
1218
+ }
1219
+ });
1220
+
1221
+ // src/services/collaboration/RecoveryCallerDeps.ts
1222
+ var RecoveryCallerDeps_exports = {};
1223
+ __export(RecoveryCallerDeps_exports, {
1224
+ createRecoveryCallerDeps: () => createRecoveryCallerDeps
1225
+ });
1226
+ function createRecoveryCallerDeps() {
1227
+ return {
1228
+ callerFor: async (top, tenantId) => {
1229
+ if (!top.assistantId) return void 0;
1230
+ const load = import_core73.getAgentClientAsync ?? import_core73.getAgentClient;
1231
+ const client = await load(tenantId, top.assistantId);
1232
+ const config = { configurable: { thread_id: top.threadId, runConfig: { tenantId, assistant_id: top.assistantId } } };
1233
+ let agent;
1234
+ try {
1235
+ agent = import_core73.agentInstanceManager.getAgent({ assistant_id: top.assistantId, thread_id: top.threadId, tenant_id: tenantId });
1236
+ } catch {
1237
+ }
1238
+ return agent ? { client, config, agent } : { client, config };
1239
+ },
1240
+ resume: async (client, config, agent) => {
1241
+ if (agent && typeof agent.resumeTask === "function") {
1242
+ try {
1243
+ await agent.resumeTask();
1244
+ return;
1245
+ } catch {
1246
+ }
1247
+ }
1248
+ await client.invoke(null, config);
1249
+ }
1250
+ };
1251
+ }
1252
+ var import_core73;
1253
+ var init_RecoveryCallerDeps = __esm({
1254
+ "src/services/collaboration/RecoveryCallerDeps.ts"() {
1255
+ "use strict";
1256
+ import_core73 = require("@axiom-lattice/core");
1257
+ }
1258
+ });
1259
+
892
1260
  // src/channels/wechat/context-store.ts
893
1261
  function makeKey(installationId, senderId) {
894
1262
  return `${installationId}::${senderId}`;
@@ -1179,11 +1547,11 @@ function devModeDefaults() {
1179
1547
  const projectId = process.env[DEFAULT_PROJECT_ENV] || void 0;
1180
1548
  return { tenantId, projectId };
1181
1549
  }
1182
- var import_protocols33, A2AAuthError, DEFAULT_TENANT_ENV, DEFAULT_TENANT_ID, DEFAULT_PROJECT_ENV, BEARER_SCHEME_RE, A2AAuthService;
1550
+ var import_protocols34, A2AAuthError, DEFAULT_TENANT_ENV, DEFAULT_TENANT_ID, DEFAULT_PROJECT_ENV, BEARER_SCHEME_RE, A2AAuthService;
1183
1551
  var init_A2AAuthService = __esm({
1184
1552
  "src/services/a2a/A2AAuthService.ts"() {
1185
1553
  "use strict";
1186
- import_protocols33 = require("@axiom-lattice/protocols");
1554
+ import_protocols34 = require("@axiom-lattice/protocols");
1187
1555
  A2AAuthError = class extends Error {
1188
1556
  constructor(message, statusCode) {
1189
1557
  super(message);
@@ -1238,7 +1606,7 @@ var init_A2AAuthService = __esm({
1238
1606
  const entry = merged.get(token);
1239
1607
  if (!entry) return { authenticated: false };
1240
1608
  const hasExplicitGrants = Array.isArray(entry.grants) && entry.grants.length > 0;
1241
- const agentGrant = (0, import_protocols33.effectiveGrants)(entry).find((g) => g.domain === "agent");
1609
+ const agentGrant = (0, import_protocols34.effectiveGrants)(entry).find((g) => g.domain === "agent");
1242
1610
  const assistantIds = hasExplicitGrants ? agentGrant?.items ?? [] : entry.assistantIds;
1243
1611
  return {
1244
1612
  authenticated: true,
@@ -1475,13 +1843,13 @@ function isA2ATaskSnapshot(value) {
1475
1843
  function snapshotSequence(value) {
1476
1844
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1477
1845
  }
1478
- var import_node_crypto28, import_node_util, import_core66, 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;
1479
1847
  var init_A2ATaskStoreAdapter = __esm({
1480
1848
  "src/services/a2a/A2ATaskStoreAdapter.ts"() {
1481
1849
  "use strict";
1482
1850
  import_node_crypto28 = require("crypto");
1483
1851
  import_node_util = require("util");
1484
- import_core66 = require("@axiom-lattice/core");
1852
+ import_core74 = require("@axiom-lattice/core");
1485
1853
  TASK_STATUS_TO_A2A_STATE = {
1486
1854
  pending: "submitted",
1487
1855
  in_progress: "working",
@@ -1507,7 +1875,7 @@ var init_A2ATaskStoreAdapter = __esm({
1507
1875
  this.workItemStore = options.workItemStore;
1508
1876
  this.lifecycle = options.lifecycle;
1509
1877
  this.scope = options.scope;
1510
- this.runBridge = (0, import_core66.createTaskRunBridge)({
1878
+ this.runBridge = (0, import_core74.createTaskRunBridge)({
1511
1879
  lifecycle: this.lifecycle,
1512
1880
  taskStore: this.taskStore,
1513
1881
  workItemStore: this.workItemStore
@@ -1858,13 +2226,13 @@ function assistantHasGovernedTask(assistant) {
1858
2226
  const allowedTools = taskMiddleware?.allowedTools;
1859
2227
  return !!taskMiddleware && (allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task")));
1860
2228
  }
1861
- var import_uuid11, import_core67, import_protocols34, _AxiomAgentExecutor, AxiomAgentExecutor;
2229
+ var import_uuid11, import_core75, import_protocols35, _AxiomAgentExecutor, AxiomAgentExecutor;
1862
2230
  var init_AxiomAgentExecutor = __esm({
1863
2231
  "src/services/a2a/AxiomAgentExecutor.ts"() {
1864
2232
  "use strict";
1865
2233
  import_uuid11 = require("uuid");
1866
- import_core67 = require("@axiom-lattice/core");
1867
- import_protocols34 = require("@axiom-lattice/protocols");
2234
+ import_core75 = require("@axiom-lattice/core");
2235
+ import_protocols35 = require("@axiom-lattice/protocols");
1868
2236
  init_a2aFileRefs();
1869
2237
  init_task_file_refs();
1870
2238
  _AxiomAgentExecutor = class _AxiomAgentExecutor {
@@ -1897,16 +2265,31 @@ var init_AxiomAgentExecutor = __esm({
1897
2265
  status: { state: "working", timestamp: isoNow() },
1898
2266
  metadata: { threadId, dependencies, inputFiles }
1899
2267
  });
1900
- const publishFailed = (err) => {
1901
- bus.publish({
1902
- kind: "status-update",
1903
- taskId: ctx.taskId,
1904
- contextId: ctx.contextId,
2268
+ const failTask = async (baseTask, err) => {
2269
+ const failedTask = {
2270
+ ...baseTask,
1905
2271
  status: {
1906
2272
  state: "failed",
1907
2273
  message: agentMessage(err instanceof Error ? err.message : String(err)),
1908
2274
  timestamp: isoNow()
1909
- },
2275
+ }
2276
+ };
2277
+ if (this.taskStore) {
2278
+ try {
2279
+ await this.taskStore.save(failedTask);
2280
+ } catch (saveError) {
2281
+ console.warn({
2282
+ event: "a2a:settle:save_failed",
2283
+ taskId: ctx.taskId,
2284
+ error: saveError instanceof Error ? saveError.message : String(saveError)
2285
+ });
2286
+ }
2287
+ }
2288
+ bus.publish({
2289
+ kind: "status-update",
2290
+ taskId: ctx.taskId,
2291
+ contextId: ctx.contextId,
2292
+ status: failedTask.status,
1910
2293
  final: true
1911
2294
  });
1912
2295
  };
@@ -1914,7 +2297,7 @@ var init_AxiomAgentExecutor = __esm({
1914
2297
  if (!this.taskStore) {
1915
2298
  const initialTask2 = createTask([]);
1916
2299
  bus.publish(initialTask2);
1917
- publishFailed(new Error("A2A execution requires a canonical task store"));
2300
+ await failTask(initialTask2, new Error("A2A execution requires a canonical task store"));
1918
2301
  return;
1919
2302
  }
1920
2303
  const governed = this.hasGovernedTaskCapability();
@@ -1938,14 +2321,14 @@ var init_AxiomAgentExecutor = __esm({
1938
2321
  const initialTask2 = createTask([]);
1939
2322
  if (this.taskStore) await this.taskStore.save(initialTask2);
1940
2323
  bus.publish(initialTask2);
1941
- publishFailed(err);
2324
+ await failTask(initialTask2, err);
1942
2325
  return;
1943
2326
  }
1944
2327
  const initialTask = createTask(inputFiles);
1945
2328
  await this.taskStore.save(initialTask);
1946
2329
  bus.publish(initialTask);
1947
2330
  try {
1948
- const agent = import_core67.agentInstanceManager.getAgent(this.agentParams(threadId));
2331
+ const agent = import_core75.agentInstanceManager.getAgent(this.agentParams(threadId));
1949
2332
  const requestText = inputFiles.length > 0 ? `${text}
1950
2333
 
1951
2334
  ${formatFileRefsSection(inputFiles)}` : text;
@@ -1963,7 +2346,7 @@ ${formatFileRefsSection(inputFiles)}` : text;
1963
2346
  custom_run_config: { taskId: ctx.taskId, execution: { taskId: ctx.taskId } }
1964
2347
  });
1965
2348
  const stream = agent.chunkStream(messageId, [
1966
- import_protocols34.MessageChunkTypes.MESSAGE_COMPLETED
2349
+ import_protocols35.MessageChunkTypes.MESSAGE_COMPLETED
1967
2350
  ]);
1968
2351
  let outputText = "";
1969
2352
  let interrupted = false;
@@ -1971,14 +2354,14 @@ ${formatFileRefsSection(inputFiles)}` : text;
1971
2354
  if (execution.canceled) return;
1972
2355
  const chunkType = chunk.type;
1973
2356
  const chunkText = chunk.data?.content ?? "";
1974
- if (chunkType === import_protocols34.MessageChunkTypes.INTERRUPT) {
2357
+ if (chunkType === import_protocols35.MessageChunkTypes.INTERRUPT) {
1975
2358
  interrupted = true;
1976
2359
  break;
1977
2360
  }
1978
- if (chunkType === import_protocols34.MessageChunkTypes.AI && chunkText) {
2361
+ if (chunkType === import_protocols35.MessageChunkTypes.AI && chunkText) {
1979
2362
  outputText += chunkText;
1980
2363
  }
1981
- if (chunkType === import_protocols34.MessageChunkTypes.AI || chunkType === import_protocols34.MessageChunkTypes.TOOL) {
2364
+ if (chunkType === import_protocols35.MessageChunkTypes.AI || chunkType === import_protocols35.MessageChunkTypes.TOOL) {
1982
2365
  if (chunkText) {
1983
2366
  bus.publish({
1984
2367
  kind: "status-update",
@@ -2081,19 +2464,12 @@ ${formatFileRefsSection(inputFiles)}` : text;
2081
2464
  });
2082
2465
  return;
2083
2466
  }
2084
- bus.publish({
2085
- kind: "status-update",
2086
- taskId: ctx.taskId,
2087
- contextId: ctx.contextId,
2088
- status: {
2089
- state: "failed",
2090
- message: agentMessage("Agent did not complete governed task lifecycle"),
2091
- timestamp: isoNow()
2092
- },
2093
- final: true
2094
- });
2467
+ await failTask(
2468
+ createTask(inputFiles),
2469
+ new Error("Agent did not complete governed task lifecycle")
2470
+ );
2095
2471
  } catch (err) {
2096
- publishFailed(err);
2472
+ await failTask(createTask(inputFiles), err);
2097
2473
  }
2098
2474
  } finally {
2099
2475
  active.delete(execution);
@@ -2140,7 +2516,7 @@ ${formatFileRefsSection(inputFiles)}` : text;
2140
2516
  const threadId = task ? asString2(task.metadata?.threadId) : void 0;
2141
2517
  if (threadId) {
2142
2518
  try {
2143
- const agent = import_core67.agentInstanceManager.getAgent(this.agentParams(threadId));
2519
+ const agent = import_core75.agentInstanceManager.getAgent(this.agentParams(threadId));
2144
2520
  await agent.abort();
2145
2521
  } catch (err) {
2146
2522
  console.warn({
@@ -2151,6 +2527,26 @@ ${formatFileRefsSection(inputFiles)}` : text;
2151
2527
  });
2152
2528
  }
2153
2529
  }
2530
+ if (this.taskStore) {
2531
+ const canceledTask = {
2532
+ ...task ?? {
2533
+ kind: "task",
2534
+ id: taskId,
2535
+ contextId: "",
2536
+ status: { state: "canceled" }
2537
+ },
2538
+ status: { state: "canceled", timestamp: isoNow() }
2539
+ };
2540
+ try {
2541
+ await this.taskStore.save(canceledTask);
2542
+ } catch (saveError) {
2543
+ console.warn({
2544
+ event: "a2a:settle:save_failed",
2545
+ taskId,
2546
+ error: saveError instanceof Error ? saveError.message : String(saveError)
2547
+ });
2548
+ }
2549
+ }
2154
2550
  bus.publish({
2155
2551
  kind: "status-update",
2156
2552
  taskId,
@@ -2494,10 +2890,10 @@ function isAsyncGenerator(value) {
2494
2890
  }
2495
2891
  function registerA2AStandardRoutes(app2, deps) {
2496
2892
  const authService = deps.authService;
2497
- const resolveTasks = deps.getTaskStoreLattice ?? (() => (0, import_core68.getStoreLattice)("default", "task").store);
2498
- const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => (0, import_core68.getStoreLattice)("default", "taskWorkItem").store);
2499
- const getAssistantStore2 = deps.getAssistantStore ?? (() => (0, import_core68.getStoreLattice)("default", "assistant").store);
2500
- const getProjectStore2 = deps.getProjectStore ?? (() => (0, import_core68.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);
2501
2897
  const runtimeRegistry = deps.runtimeRegistry ?? new A2ARuntimeRegistry();
2502
2898
  app2.addHook("onClose", async () => {
2503
2899
  runtimeRegistry.close();
@@ -2580,7 +2976,7 @@ function registerA2AStandardRoutes(app2, deps) {
2580
2976
  const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
2581
2977
  taskStore: internalTasks,
2582
2978
  workItemStore: workItems,
2583
- lifecycle: (0, import_core68.createTaskLifecycleService)({
2979
+ lifecycle: (0, import_core76.createTaskLifecycleService)({
2584
2980
  taskStore: internalTasks,
2585
2981
  workItemStore: workItems
2586
2982
  }),
@@ -2699,7 +3095,7 @@ data: ${JSON.stringify(errorResponse)}
2699
3095
  const taskStore = assistantHasGovernedTask(assistant) ? new A2ATaskStoreAdapter({
2700
3096
  taskStore: internalTasks,
2701
3097
  workItemStore: workItems,
2702
- lifecycle: (0, import_core68.createTaskLifecycleService)({
3098
+ lifecycle: (0, import_core76.createTaskLifecycleService)({
2703
3099
  taskStore: internalTasks,
2704
3100
  workItemStore: workItems
2705
3101
  }),
@@ -2731,6 +3127,16 @@ data: ${JSON.stringify(data)}
2731
3127
  await executor.execute(requestContext, bus);
2732
3128
  } catch (err) {
2733
3129
  request.log.error({ err }, "a2a:test-call:execution");
3130
+ try {
3131
+ await taskStore.save({
3132
+ kind: "task",
3133
+ id: taskId,
3134
+ contextId,
3135
+ status: { state: "failed", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
3136
+ });
3137
+ } catch (saveError) {
3138
+ request.log.error({ err: saveError }, "a2a:test-call:settle");
3139
+ }
2734
3140
  sendEvent("status-update", {
2735
3141
  kind: "status-update",
2736
3142
  taskId,
@@ -2744,13 +3150,13 @@ data: ${JSON.stringify(data)}
2744
3150
  }
2745
3151
  );
2746
3152
  }
2747
- var import_uuid12, import_server2, import_core68, SSE_HEADERS;
3153
+ var import_uuid12, import_server2, import_core76, SSE_HEADERS;
2748
3154
  var init_a2a_standard = __esm({
2749
3155
  "src/routes/a2a-standard.ts"() {
2750
3156
  "use strict";
2751
3157
  import_uuid12 = require("uuid");
2752
3158
  import_server2 = require("@a2a-js/sdk/server");
2753
- import_core68 = require("@axiom-lattice/core");
3159
+ import_core76 = require("@axiom-lattice/core");
2754
3160
  init_A2AAuthService();
2755
3161
  init_A2AAgentCardBuilder();
2756
3162
  init_A2ATaskStoreAdapter();
@@ -2958,11 +3364,11 @@ var init_a2a = __esm({
2958
3364
  });
2959
3365
 
2960
3366
  // src/services/open/openAuthService.ts
2961
- var import_protocols35, OpenAuthService;
3367
+ var import_protocols36, OpenAuthService;
2962
3368
  var init_openAuthService = __esm({
2963
3369
  "src/services/open/openAuthService.ts"() {
2964
3370
  "use strict";
2965
- import_protocols35 = require("@axiom-lattice/protocols");
3371
+ import_protocols36 = require("@axiom-lattice/protocols");
2966
3372
  OpenAuthService = class {
2967
3373
  constructor(keyStore) {
2968
3374
  this.keyStore = keyStore;
@@ -2976,7 +3382,7 @@ var init_openAuthService = __esm({
2976
3382
  kind: "api_key",
2977
3383
  tenantId: record.tenantId,
2978
3384
  projectId: record.projectId,
2979
- grants: (0, import_protocols35.effectiveGrants)(record)
3385
+ grants: (0, import_protocols36.effectiveGrants)(record)
2980
3386
  };
2981
3387
  }
2982
3388
  };
@@ -2984,11 +3390,11 @@ var init_openAuthService = __esm({
2984
3390
  });
2985
3391
 
2986
3392
  // src/services/open/openCredentialService.ts
2987
- var import_protocols36, OpenCredentialService;
3393
+ var import_protocols37, OpenCredentialService;
2988
3394
  var init_openCredentialService = __esm({
2989
3395
  "src/services/open/openCredentialService.ts"() {
2990
3396
  "use strict";
2991
- import_protocols36 = require("@axiom-lattice/protocols");
3397
+ import_protocols37 = require("@axiom-lattice/protocols");
2992
3398
  init_openToken();
2993
3399
  init_openAuthService();
2994
3400
  OpenCredentialService = class {
@@ -3008,6 +3414,7 @@ var init_openCredentialService = __esm({
3008
3414
  projectId: token.projectId,
3009
3415
  workspaceId: token.workspaceId,
3010
3416
  sandboxName: token.sandboxName,
3417
+ depth: token.depth,
3011
3418
  grants: token.grants
3012
3419
  };
3013
3420
  }
@@ -3501,8 +3908,8 @@ function exposedDeclarations(plugin) {
3501
3908
  return (plugin.meta.tools ?? []).filter((t2) => t2.expose);
3502
3909
  }
3503
3910
  async function registerExposedPluginTools() {
3504
- for (const key of import_core69.PluginRegistry.list()) {
3505
- const plugin = import_core69.PluginRegistry.get(key);
3911
+ for (const key of import_core77.PluginRegistry.list()) {
3912
+ const plugin = import_core77.PluginRegistry.get(key);
3506
3913
  if (!plugin?.middleware) continue;
3507
3914
  const exposed = exposedDeclarations(plugin);
3508
3915
  if (exposed.length === 0) continue;
@@ -3537,8 +3944,8 @@ async function registerExposedPluginTools() {
3537
3944
  }
3538
3945
  function collectExposedPluginUiResources(grants) {
3539
3946
  const resources = /* @__PURE__ */ new Map();
3540
- for (const type of import_core69.PluginRegistry.list()) {
3541
- const plugin = import_core69.PluginRegistry.get(type);
3947
+ for (const type of import_core77.PluginRegistry.list()) {
3948
+ const plugin = import_core77.PluginRegistry.get(type);
3542
3949
  if (!plugin?.middleware) continue;
3543
3950
  const exposedNames = new Set(exposedDeclarations(plugin).map((d) => d.name));
3544
3951
  if (exposedNames.size === 0) continue;
@@ -3562,11 +3969,11 @@ function collectExposedPluginUiResources(grants) {
3562
3969
  }
3563
3970
  return resources;
3564
3971
  }
3565
- var import_core69;
3972
+ var import_core77;
3566
3973
  var init_registerExposedPlugins = __esm({
3567
3974
  "src/services/open/registerExposedPlugins.ts"() {
3568
3975
  "use strict";
3569
- import_core69 = require("@axiom-lattice/core");
3976
+ import_core77 = require("@axiom-lattice/core");
3570
3977
  init_openLattice();
3571
3978
  init_pluginExposeAdapter();
3572
3979
  }
@@ -3581,14 +3988,14 @@ function toText(result) {
3581
3988
  }
3582
3989
  function defaultCatalog() {
3583
3990
  return {
3584
- getValidatedManifest: (tenantId, type) => (0, import_core70.getSandboxPluginCatalog)().getValidatedManifest(tenantId, type),
3585
- getExecutor: () => (0, import_core70.getSandboxPluginCatalog)().getExecutor(),
3586
- readPluginFile: (tenantId, type, relPath) => (0, import_core70.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)
3587
3994
  };
3588
3995
  }
3589
3996
  async function defaultResolveConnections(pluginType, ctx) {
3590
3997
  try {
3591
- return await (0, import_core70.resolveSandboxPluginConnections)(
3998
+ return await (0, import_core78.resolveSandboxPluginConnections)(
3592
3999
  pluginType,
3593
4000
  { connectAll: true },
3594
4001
  {
@@ -3617,7 +4024,7 @@ function mergeOpenCatalog(globalCatalog, tenantTools) {
3617
4024
  async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
3618
4025
  const catalog = deps.catalog ?? defaultCatalog();
3619
4026
  const listPlugins = deps.listPlugins ?? (async (tenant2) => {
3620
- const snapshot = await (0, import_core70.getSandboxPluginCatalog)().ensureLoaded(tenant2);
4027
+ const snapshot = await (0, import_core78.getSandboxPluginCatalog)().ensureLoaded(tenant2);
3621
4028
  return { plugins: snapshot.plugins, diagnostics: snapshot.diagnostics };
3622
4029
  });
3623
4030
  const resolveConnections = deps.resolveConnections ?? defaultResolveConnections;
@@ -3659,7 +4066,7 @@ async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
3659
4066
  domain,
3660
4067
  action: tool.name,
3661
4068
  description: tool.description,
3662
- schema: (0, import_core70.jsonSchemaToZod)(tool.schema),
4069
+ schema: (0, import_core78.jsonSchemaToZod)(tool.schema),
3663
4070
  annotations: {
3664
4071
  readOnlyHint: tool.readOnly ?? false,
3665
4072
  destructiveHint: tool.destructive ?? false
@@ -3710,11 +4117,11 @@ async function buildTenantOpenPlugins(tenantId, grants, deps = {}) {
3710
4117
  }
3711
4118
  };
3712
4119
  }
3713
- var import_core70;
4120
+ var import_core78;
3714
4121
  var init_tenantOpenPlugins = __esm({
3715
4122
  "src/services/open/tenantOpenPlugins.ts"() {
3716
4123
  "use strict";
3717
- import_core70 = require("@axiom-lattice/core");
4124
+ import_core78 = require("@axiom-lattice/core");
3718
4125
  init_openLattice();
3719
4126
  init_pluginToolExecution();
3720
4127
  }
@@ -3751,13 +4158,13 @@ function emptyTenantPlugins() {
3751
4158
  }
3752
4159
  function registerOpenRoutes(app2, deps = {}) {
3753
4160
  const openAuth = deps.openAuth ?? new OpenCredentialService(
3754
- (0, import_core71.getStoreLattice)("default", "a2aApiKey").store,
3755
- (0, import_core71.getStoreLattice)("default", "project").store
4161
+ (0, import_core79.getStoreLattice)("default", "a2aApiKey").store,
4162
+ (0, import_core79.getStoreLattice)("default", "project").store
3756
4163
  );
3757
4164
  const grantsSource = deps.grantsSource ?? ((ctx) => ctx.grants);
3758
4165
  const audit = deps.audit ?? (() => {
3759
4166
  try {
3760
- const store2 = (0, import_core71.getStoreLattice)("default", "openAudit").store;
4167
+ const store2 = (0, import_core79.getStoreLattice)("default", "openAudit").store;
3761
4168
  return new OpenAuditService(store2);
3762
4169
  } catch {
3763
4170
  return void 0;
@@ -3843,7 +4250,8 @@ function registerOpenRoutes(app2, deps = {}) {
3843
4250
  const isTenantRoute = tenant2.hasTool(toolName) && !globalToolNames.has(toolName);
3844
4251
  const runConfig = {
3845
4252
  sandboxName: context.sandboxName,
3846
- credentialId: context.credentialId
4253
+ credentialId: context.credentialId,
4254
+ depth: context.depth
3847
4255
  };
3848
4256
  const result = isTenantRoute ? await tenant2.invoke(toolName, args, {
3849
4257
  tenantId: context.tenantId,
@@ -3911,12 +4319,12 @@ function registerOpenRoutes(app2, deps = {}) {
3911
4319
  }
3912
4320
  );
3913
4321
  }
3914
- var import_types, import_core71, RPC_ERROR;
4322
+ var import_types, import_core79, RPC_ERROR;
3915
4323
  var init_open = __esm({
3916
4324
  "src/routes/open.ts"() {
3917
4325
  "use strict";
3918
4326
  import_types = require("@modelcontextprotocol/sdk/types.js");
3919
- import_core71 = require("@axiom-lattice/core");
4327
+ import_core79 = require("@axiom-lattice/core");
3920
4328
  init_openCredentialService();
3921
4329
  init_openAuditService();
3922
4330
  init_openRateLimiter();
@@ -3949,7 +4357,7 @@ function registerOpenSessionRoutes(app2) {
3949
4357
  if (typeof shareToken !== "string" || shareToken.length === 0) {
3950
4358
  return reply.status(400).send({ error: "SHARE_TOKEN_REQUIRED" });
3951
4359
  }
3952
- const store2 = (0, import_core72.getStoreLattice)("default", "sharedResource").store;
4360
+ const store2 = (0, import_core80.getStoreLattice)("default", "sharedResource").store;
3953
4361
  const record = await store2.findByToken(shareToken);
3954
4362
  if (!record || record.revoked) {
3955
4363
  return reply.status(404).send({ error: "SHARE_NOT_FOUND" });
@@ -3974,11 +4382,11 @@ function registerOpenSessionRoutes(app2) {
3974
4382
  }
3975
4383
  );
3976
4384
  }
3977
- var import_core72;
4385
+ var import_core80;
3978
4386
  var init_open_session = __esm({
3979
4387
  "src/routes/open-session.ts"() {
3980
4388
  "use strict";
3981
- import_core72 = require("@axiom-lattice/core");
4389
+ import_core80 = require("@axiom-lattice/core");
3982
4390
  init_openToken();
3983
4391
  init_previewToken();
3984
4392
  init_openLattice();
@@ -3987,13 +4395,13 @@ var init_open_session = __esm({
3987
4395
 
3988
4396
  // src/services/open/openCatalogService.ts
3989
4397
  async function defaultListTenantPlugins(tenantId) {
3990
- return (await (0, import_core73.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
4398
+ return (await (0, import_core81.getSandboxPluginCatalog)().ensureLoaded(tenantId)).plugins;
3991
4399
  }
3992
- var import_core73, OpenCatalogService;
4400
+ var import_core81, OpenCatalogService;
3993
4401
  var init_openCatalogService = __esm({
3994
4402
  "src/services/open/openCatalogService.ts"() {
3995
4403
  "use strict";
3996
- import_core73 = require("@axiom-lattice/core");
4404
+ import_core81 = require("@axiom-lattice/core");
3997
4405
  init_openLattice();
3998
4406
  OpenCatalogService = class {
3999
4407
  constructor(assistantStore, lattice = openLattice, listTenantPlugins2 = defaultListTenantPlugins) {
@@ -4068,17 +4476,17 @@ function registerOpenCatalogRoutes(app2) {
4068
4476
  if (!tenantId) {
4069
4477
  return reply.status(401).send({ success: false, error: "Unauthorized" });
4070
4478
  }
4071
- const assistantStore = (0, import_core74.getStoreLattice)("default", "assistant").store;
4479
+ const assistantStore = (0, import_core82.getStoreLattice)("default", "assistant").store;
4072
4480
  const service2 = new OpenCatalogService(assistantStore);
4073
4481
  const catalog = await service2.buildForTenant(tenantId);
4074
4482
  return reply.status(200).send({ success: true, data: catalog });
4075
4483
  });
4076
4484
  }
4077
- var import_core74;
4485
+ var import_core82;
4078
4486
  var init_open_catalog = __esm({
4079
4487
  "src/routes/open-catalog.ts"() {
4080
4488
  "use strict";
4081
- import_core74 = require("@axiom-lattice/core");
4489
+ import_core82 = require("@axiom-lattice/core");
4082
4490
  init_openCatalogService();
4083
4491
  }
4084
4492
  });
@@ -4090,7 +4498,14 @@ __export(sandboxProvider_exports, {
4090
4498
  resolveContextSandbox: () => resolveContextSandbox
4091
4499
  });
4092
4500
  async function resolveContextSandbox(ctx) {
4093
- return import_core75.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({
4094
4509
  assistant_id: "",
4095
4510
  thread_id: "",
4096
4511
  tenantId: ctx.tenantId,
@@ -4188,12 +4603,12 @@ ${truncated}`
4188
4603
  }
4189
4604
  };
4190
4605
  }
4191
- var import_zod21, import_core75, MAX_OUTPUT_BYTES;
4606
+ var import_zod21, import_core83, MAX_OUTPUT_BYTES;
4192
4607
  var init_sandboxProvider = __esm({
4193
4608
  "src/services/open/providers/sandboxProvider.ts"() {
4194
4609
  "use strict";
4195
4610
  import_zod21 = require("zod");
4196
- import_core75 = require("@axiom-lattice/core");
4611
+ import_core83 = require("@axiom-lattice/core");
4197
4612
  MAX_OUTPUT_BYTES = 64e3;
4198
4613
  }
4199
4614
  });
@@ -4215,12 +4630,12 @@ function safePath(raw) {
4215
4630
  }
4216
4631
  function registerOpenFilesRoutes(app2, deps = {}) {
4217
4632
  const openAuth = deps.openAuth ?? new OpenCredentialService(
4218
- (0, import_core76.getStoreLattice)("default", "a2aApiKey").store,
4219
- (0, import_core76.getStoreLattice)("default", "project").store
4633
+ (0, import_core84.getStoreLattice)("default", "a2aApiKey").store,
4634
+ (0, import_core84.getStoreLattice)("default", "project").store
4220
4635
  );
4221
4636
  const audit = deps.audit ?? (() => {
4222
4637
  try {
4223
- const store2 = (0, import_core76.getStoreLattice)("default", "openAudit").store;
4638
+ const store2 = (0, import_core84.getStoreLattice)("default", "openAudit").store;
4224
4639
  return new OpenAuditService(store2);
4225
4640
  } catch {
4226
4641
  return void 0;
@@ -4330,11 +4745,11 @@ function registerOpenFilesRoutes(app2, deps = {}) {
4330
4745
  }
4331
4746
  });
4332
4747
  }
4333
- var import_core76;
4748
+ var import_core84;
4334
4749
  var init_open_files = __esm({
4335
4750
  "src/routes/open-files.ts"() {
4336
4751
  "use strict";
4337
- import_core76 = require("@axiom-lattice/core");
4752
+ import_core84 = require("@axiom-lattice/core");
4338
4753
  init_openCredentialService();
4339
4754
  init_openLattice();
4340
4755
  init_sandboxProvider();
@@ -4356,7 +4771,7 @@ function registerOpenAuditRoutes(app2) {
4356
4771
  return reply.status(401).send({ success: false, error: "Unauthorized" });
4357
4772
  }
4358
4773
  const status = request.query.status === "ok" || request.query.status === "error" ? request.query.status : void 0;
4359
- const store2 = (0, import_core77.getStoreLattice)("default", "openAudit").store;
4774
+ const store2 = (0, import_core85.getStoreLattice)("default", "openAudit").store;
4360
4775
  const records = await store2.query({
4361
4776
  tenantId,
4362
4777
  credentialId: request.query.credentialId,
@@ -4371,11 +4786,11 @@ function registerOpenAuditRoutes(app2) {
4371
4786
  }
4372
4787
  );
4373
4788
  }
4374
- var import_core77;
4789
+ var import_core85;
4375
4790
  var init_open_audit = __esm({
4376
4791
  "src/routes/open-audit.ts"() {
4377
4792
  "use strict";
4378
- import_core77 = require("@axiom-lattice/core");
4793
+ import_core85 = require("@axiom-lattice/core");
4379
4794
  }
4380
4795
  });
4381
4796
 
@@ -4392,12 +4807,12 @@ function registerOpenExposureRoutes(app2, deps = {}) {
4392
4807
  if (!tenantId) {
4393
4808
  return reply.status(401).send({ success: false, error: "Unauthorized" });
4394
4809
  }
4395
- const keyStore = (0, import_core78.getStoreLattice)("default", "a2aApiKey").store;
4810
+ const keyStore = (0, import_core86.getStoreLattice)("default", "a2aApiKey").store;
4396
4811
  const record = await keyStore.findById(request.params.id);
4397
4812
  if (!record || record.tenantId !== tenantId) {
4398
4813
  return reply.status(404).send({ success: false, error: "Key not found" });
4399
4814
  }
4400
- const grants = (0, import_protocols37.effectiveGrants)(record);
4815
+ const grants = (0, import_protocols38.effectiveGrants)(record);
4401
4816
  const forwardedHost = request.headers["x-forwarded-host"];
4402
4817
  const host = (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ?? (Array.isArray(request.headers.host) ? request.headers.host[0] : request.headers.host) ?? request.hostname;
4403
4818
  const baseUrl = `${request.protocol}://${host}`;
@@ -4426,7 +4841,7 @@ function registerOpenExposureRoutes(app2, deps = {}) {
4426
4841
  hasUi: Boolean(t2.ui)
4427
4842
  }));
4428
4843
  const agentGrant = grants.find((g) => g.domain === "agent");
4429
- const assistantStore = (0, import_core78.getStoreLattice)("default", "assistant").store;
4844
+ const assistantStore = (0, import_core86.getStoreLattice)("default", "assistant").store;
4430
4845
  const agents = [];
4431
4846
  for (const assistantId of agentGrant?.items ?? []) {
4432
4847
  const assistant = await assistantStore.getAssistantById(
@@ -4461,12 +4876,12 @@ function registerOpenExposureRoutes(app2, deps = {}) {
4461
4876
  }
4462
4877
  );
4463
4878
  }
4464
- var import_core78, import_protocols37;
4879
+ var import_core86, import_protocols38;
4465
4880
  var init_open_exposure = __esm({
4466
4881
  "src/routes/open-exposure.ts"() {
4467
4882
  "use strict";
4468
- import_core78 = require("@axiom-lattice/core");
4469
- import_protocols37 = require("@axiom-lattice/protocols");
4883
+ import_core86 = require("@axiom-lattice/core");
4884
+ import_protocols38 = require("@axiom-lattice/protocols");
4470
4885
  init_openLattice();
4471
4886
  init_tenantOpenPlugins();
4472
4887
  init_A2AAgentCardBuilder();
@@ -4585,7 +5000,7 @@ function createAgentProvider(deps = {}) {
4585
5000
  } catch {
4586
5001
  }
4587
5002
  }
4588
- const agent = import_core79.agentInstanceManager.getAgent({
5003
+ const agent = import_core87.agentInstanceManager.getAgent({
4589
5004
  tenant_id: ctx.tenantId,
4590
5005
  assistant_id: assistantId,
4591
5006
  thread_id: threadId,
@@ -4599,8 +5014,8 @@ function createAgentProvider(deps = {}) {
4599
5014
  const stream = agent.chunkStream(messageId, [...STOP_TYPES]);
4600
5015
  for await (const chunk of stream) {
4601
5016
  const text = chunk.data?.content;
4602
- if (chunk.type === import_protocols38.MessageChunkTypes.INTERRUPT) break;
4603
- if (chunk.type === import_protocols38.MessageChunkTypes.AI && text) {
5017
+ if (chunk.type === import_protocols39.MessageChunkTypes.INTERRUPT) break;
5018
+ if (chunk.type === import_protocols39.MessageChunkTypes.AI && text) {
4604
5019
  parts.push(text);
4605
5020
  }
4606
5021
  }
@@ -4610,14 +5025,289 @@ function createAgentProvider(deps = {}) {
4610
5025
  }
4611
5026
  };
4612
5027
  }
4613
- var import_zod23, import_core79, import_protocols38, STOP_TYPES;
5028
+ var import_zod23, import_core87, import_protocols39, STOP_TYPES;
4614
5029
  var init_agentProvider = __esm({
4615
5030
  "src/services/open/providers/agentProvider.ts"() {
4616
5031
  "use strict";
4617
5032
  import_zod23 = require("zod");
4618
- import_core79 = require("@axiom-lattice/core");
4619
- import_protocols38 = require("@axiom-lattice/protocols");
4620
- STOP_TYPES = [import_protocols38.MessageChunkTypes.MESSAGE_COMPLETED];
5033
+ import_core87 = require("@axiom-lattice/core");
5034
+ import_protocols39 = require("@axiom-lattice/protocols");
5035
+ STOP_TYPES = [import_protocols39.MessageChunkTypes.MESSAGE_COMPLETED];
5036
+ }
5037
+ });
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;
4621
5311
  }
4622
5312
  });
4623
5313
 
@@ -4647,12 +5337,12 @@ var resources_exports = {};
4647
5337
  __export(resources_exports, {
4648
5338
  ResourceController: () => ResourceController
4649
5339
  });
4650
- var import_bcryptjs, import_core80, ResourceController;
5340
+ var import_bcryptjs, import_core90, ResourceController;
4651
5341
  var init_resources = __esm({
4652
5342
  "src/controllers/resources.ts"() {
4653
5343
  "use strict";
4654
5344
  import_bcryptjs = __toESM(require("bcryptjs"));
4655
- import_core80 = require("@axiom-lattice/core");
5345
+ import_core90 = require("@axiom-lattice/core");
4656
5346
  init_mime();
4657
5347
  init_http_range();
4658
5348
  init_sandboxGrants();
@@ -4669,14 +5359,14 @@ var init_resources = __esm({
4669
5359
  if (!workspaceId || !projectId) {
4670
5360
  return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
4671
5361
  }
4672
- const payload = (0, import_core80.createSharePayload)(
5362
+ const payload = (0, import_core90.createSharePayload)(
4673
5363
  tenantId,
4674
5364
  workspaceId,
4675
5365
  projectId,
4676
5366
  userId,
4677
5367
  body
4678
5368
  );
4679
- const token = (0, import_core80.generateToken)();
5369
+ const token = (0, import_core90.generateToken)();
4680
5370
  try {
4681
5371
  await this.deps.store.create({ ...payload, token });
4682
5372
  this.deps.logger.info(
@@ -4828,7 +5518,7 @@ var init_resources = __esm({
4828
5518
  resourcePath: subPath
4829
5519
  }, subPath, ext, request, reply);
4830
5520
  }
4831
- const volume = (0, import_core80.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
5521
+ const volume = (0, import_core90.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
4832
5522
  return this._resolveAndServe(
4833
5523
  volume,
4834
5524
  subPath,
@@ -5039,7 +5729,18 @@ var init_resources = __esm({
5039
5729
  // src/index.ts
5040
5730
  var index_exports = {};
5041
5731
  __export(index_exports, {
5042
- LatticeGateway: () => LatticeGateway
5732
+ LatticeGateway: () => LatticeGateway,
5733
+ ProjectRoomEventBroker: () => ProjectRoomEventBroker,
5734
+ ROOM_EVENT_TOPIC: () => ROOM_EVENT_TOPIC,
5735
+ createOrphanRecoveryRuntime: () => createOrphanRecoveryRuntime,
5736
+ createRecoveryCallerDeps: () => createRecoveryCallerDeps,
5737
+ createTaskRunLiveness: () => createTaskRunLiveness,
5738
+ ingestRoomEvent: () => ingestRoomEvent,
5739
+ publishRoomEvent: () => publishRoomEvent,
5740
+ recoverTask: () => recoverTask,
5741
+ recoverTopCaller: () => recoverTopCaller,
5742
+ resolveTopCaller: () => resolveTopCaller,
5743
+ runOrphanRecovery: () => runOrphanRecovery
5043
5744
  });
5044
5745
  module.exports = __toCommonJS(index_exports);
5045
5746
  var import_config2 = require("dotenv/config");
@@ -5715,7 +6416,7 @@ var getAgentGraph = async (request, reply) => {
5715
6416
  try {
5716
6417
  const { assistantId } = request.params;
5717
6418
  const tenant_id = getTenantId(request);
5718
- const agent = import_core.agentInstanceManager.getAgent({ assistant_id: assistantId, tenant_id, thread_id: "" });
6419
+ const agent = import_core.agentInstanceManager.getAgentForRead({ assistant_id: assistantId, tenant_id, thread_id: "" });
5719
6420
  const imageData = await agent.get_draw_graph();
5720
6421
  reply.header("Content-Type", "application/json").send({
5721
6422
  image: imageData
@@ -5852,6 +6553,21 @@ var import_uuid = require("uuid");
5852
6553
  var import_core3 = require("@axiom-lattice/core");
5853
6554
  var import_protocols2 = require("@axiom-lattice/protocols");
5854
6555
 
6556
+ // src/utils/agentScope.ts
6557
+ function headerValue(value) {
6558
+ if (Array.isArray(value)) return value[0];
6559
+ return value;
6560
+ }
6561
+ function resolveAgentScope(request) {
6562
+ const workspace = headerValue(request.headers["x-workspace-id"]);
6563
+ const project = headerValue(request.headers["x-project-id"]);
6564
+ return {
6565
+ tenant_id: headerValue(request.headers["x-tenant-id"]),
6566
+ workspace_id: workspace || "default",
6567
+ project_id: project || void 0
6568
+ };
6569
+ }
6570
+
5855
6571
  // src/services/agents/ScopedAgentExecution.ts
5856
6572
  async function executeScopedAgentMessage(agent, request, stopTypes, options) {
5857
6573
  const { messageId } = options === void 0 ? await agent.addMessage(request) : await agent.addMessage(request, options.queueMode);
@@ -5935,6 +6651,24 @@ async function writeSse(raw, value, closed) {
5935
6651
  }
5936
6652
 
5937
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
+ }
5938
6672
  function getUserId2(request) {
5939
6673
  const authUser = request.user;
5940
6674
  if (authUser?.id) return authUser.id;
@@ -5953,9 +6687,7 @@ var createRun = async (request, reply) => {
5953
6687
  mode,
5954
6688
  ...input
5955
6689
  } = request.body;
5956
- const tenant_id = request.headers["x-tenant-id"];
5957
- const workspace_id = request.headers["x-workspace-id"];
5958
- const project_id = request.headers["x-project-id"];
6690
+ const { tenant_id, workspace_id, project_id } = resolveAgentScope(request);
5959
6691
  const x_request_id = request.headers["x-request-id"] || (0, import_uuid.v4)();
5960
6692
  const user_id = getUserId2(request);
5961
6693
  const mergedConfig = {
@@ -5963,6 +6695,7 @@ var createRun = async (request, reply) => {
5963
6695
  ...user_id && { user_id }
5964
6696
  };
5965
6697
  if (!assistant_id) {
6698
+ applyAffinityEcho(request, reply);
5966
6699
  reply.status(400).send({
5967
6700
  success: false,
5968
6701
  error: "\u52A9\u624BID\u662F\u5FC5\u9700\u7684"
@@ -5984,6 +6717,8 @@ var createRun = async (request, reply) => {
5984
6717
  command,
5985
6718
  custom_run_config: mergedConfig
5986
6719
  }, mode);
6720
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6721
+ applyAffinityEcho(request, reply);
5987
6722
  reply.status(202).send({
5988
6723
  success: true,
5989
6724
  messageId: result.messageId,
@@ -5997,7 +6732,10 @@ var createRun = async (request, reply) => {
5997
6732
  "Content-Type": "text/event-stream",
5998
6733
  "Cache-Control": "no-cache",
5999
6734
  Connection: "keep-alive",
6000
- "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])
6001
6739
  });
6002
6740
  try {
6003
6741
  const messageInput = message_id ? { ...input, id: message_id } : input;
@@ -6008,6 +6746,18 @@ var createRun = async (request, reply) => {
6008
6746
  }, [import_protocols2.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols2.MessageChunkTypes.INTERRUPT, import_protocols2.MessageChunkTypes.MESSAGE_FAILED], {
6009
6747
  queueMode: mode
6010
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
+ }
6011
6761
  await pipeAsyncIterableToSse(reply.raw, result.stream, {
6012
6762
  onError: (error) => [{
6013
6763
  type: "error",
@@ -6039,12 +6789,15 @@ var createRun = async (request, reply) => {
6039
6789
  command,
6040
6790
  custom_run_config: mergedConfig
6041
6791
  });
6792
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6793
+ applyAffinityEcho(request, reply);
6042
6794
  reply.status(200).send({
6043
6795
  success: true,
6044
6796
  ...result
6045
6797
  });
6046
6798
  }
6047
6799
  } catch (error) {
6800
+ applyAffinityEcho(request, reply);
6048
6801
  reply.status(500).send({
6049
6802
  success: false,
6050
6803
  error: `\u521B\u5EFA\u8FD0\u884C\u65F6\u53D1\u751F\u9519\u8BEF: ${error.message}`
@@ -6054,9 +6807,7 @@ var createRun = async (request, reply) => {
6054
6807
  var resumeStream = async (request, reply) => {
6055
6808
  try {
6056
6809
  const { thread_id, message_id, assistant_id, known_content, poll_interval } = request.body;
6057
- const tenant_id = request.headers["x-tenant-id"];
6058
- const workspace_id = request.headers["x-workspace-id"];
6059
- const project_id = request.headers["x-project-id"];
6810
+ const { tenant_id, workspace_id, project_id } = resolveAgentScope(request);
6060
6811
  if (!thread_id || !message_id || !assistant_id || known_content === void 0) {
6061
6812
  reply.status(400).send({
6062
6813
  success: false,
@@ -6069,9 +6820,20 @@ var resumeStream = async (request, reply) => {
6069
6820
  "Content-Type": "text/event-stream",
6070
6821
  "Cache-Control": "no-cache",
6071
6822
  Connection: "keep-alive",
6072
- "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])
6073
6827
  });
6074
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
+ }
6075
6837
  const agent = import_core3.agentInstanceManager.getAgent({
6076
6838
  assistant_id,
6077
6839
  thread_id,
@@ -6114,9 +6876,7 @@ var resumeStream = async (request, reply) => {
6114
6876
  var abortRun = async (request, reply) => {
6115
6877
  try {
6116
6878
  const { assistantId, threadId } = request.params;
6117
- const tenant_id = request.headers["x-tenant-id"];
6118
- const workspace_id = request.headers["x-workspace-id"] || "default";
6119
- const project_id = request.headers["x-project-id"] || "default";
6879
+ const { tenant_id, workspace_id, project_id } = resolveAgentScope(request);
6120
6880
  const agent = import_core3.agentInstanceManager.getAgent({
6121
6881
  assistant_id: assistantId,
6122
6882
  thread_id: threadId,
@@ -6124,12 +6884,22 @@ var abortRun = async (request, reply) => {
6124
6884
  workspace_id,
6125
6885
  project_id
6126
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
+ }
6127
6894
  await agent.abort();
6895
+ reply.header(import_protocols2.REPLICA_HEADER, (0, import_core3.getReplicaId)());
6896
+ applyAffinityEcho(request, reply);
6128
6897
  reply.status(200).send({
6129
6898
  success: true,
6130
6899
  message: "Agent aborted"
6131
6900
  });
6132
6901
  } catch (error) {
6902
+ applyAffinityEcho(request, reply);
6133
6903
  reply.status(500).send({
6134
6904
  success: false,
6135
6905
  error: `Abort failed: ${error.message}`
@@ -6139,9 +6909,7 @@ var abortRun = async (request, reply) => {
6139
6909
  var recoverRun = async (request, reply) => {
6140
6910
  try {
6141
6911
  const { assistantId, threadId } = request.params;
6142
- const tenant_id = request.headers["x-tenant-id"];
6143
- const workspace_id = request.headers["x-workspace-id"] || "default";
6144
- const project_id = request.headers["x-project-id"];
6912
+ const { tenant_id, workspace_id, project_id } = resolveAgentScope(request);
6145
6913
  const agent = import_core3.agentInstanceManager.getAgent({
6146
6914
  assistant_id: assistantId,
6147
6915
  thread_id: threadId,
@@ -6149,13 +6917,17 @@ var recoverRun = async (request, reply) => {
6149
6917
  workspace_id,
6150
6918
  project_id
6151
6919
  });
6152
- 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);
6153
6923
  reply.status(200).send({
6154
6924
  success: true,
6155
6925
  threadId,
6156
- status
6926
+ status,
6927
+ ...leaseContended ? { code: import_protocols2.AFFINITY_MISS_CODE } : {}
6157
6928
  });
6158
6929
  } catch (error) {
6930
+ applyAffinityEcho(request, reply);
6159
6931
  reply.status(500).send({
6160
6932
  success: false,
6161
6933
  error: `Recover failed: ${error.message}`
@@ -6229,7 +7001,7 @@ var getAllMemoryItems = async (request, reply) => {
6229
7001
  });
6230
7002
  return;
6231
7003
  }
6232
- const agent = import_core4.agentInstanceManager.getAgent({ assistant_id: assistantId, tenant_id, thread_id });
7004
+ const agent = import_core4.agentInstanceManager.getAgentForRead({ assistant_id: assistantId, tenant_id, thread_id });
6233
7005
  const result = await agent.getCurrentMessages();
6234
7006
  if (!result) {
6235
7007
  reply.status(500).send(result);
@@ -6261,7 +7033,7 @@ var getAgentState = async (request, reply) => {
6261
7033
  return;
6262
7034
  }
6263
7035
  const tenant_id = request.headers["x-tenant-id"];
6264
- const agent = import_core4.agentInstanceManager.getAgent({ assistant_id: assistantId, tenant_id, thread_id });
7036
+ const agent = import_core4.agentInstanceManager.getAgentForRead({ assistant_id: assistantId, tenant_id, thread_id });
6265
7037
  const result = await agent.getCurrentState();
6266
7038
  const pendingMessages = await agent.getPendingMessages();
6267
7039
  if (!result) {
@@ -8072,7 +8844,7 @@ async function getInboxItems(request, reply) {
8072
8844
  store2.getRunSteps(r.id).catch(() => []),
8073
8845
  (async () => {
8074
8846
  try {
8075
- return import_core15.agentInstanceManager.getAgent({
8847
+ return import_core15.agentInstanceManager.getAgentForRead({
8076
8848
  assistant_id: r.assistantId,
8077
8849
  thread_id: r.threadId,
8078
8850
  tenant_id: r.tenantId
@@ -10231,9 +11003,9 @@ function isRoomPresenceMetadata(metadata, expected) {
10231
11003
  // src/services/collaboration/RoomCoordinatorEnsurer.ts
10232
11004
  var RoomCoordinatorEnsureError = class extends Error {
10233
11005
  /** Creates a typed ensure failure with the underlying store outcome. */
10234
- constructor(outcome) {
10235
- super(`Room coordinator could not be ensured: ${outcome}`);
10236
- this.outcome = outcome;
11006
+ constructor(outcome2) {
11007
+ super(`Room coordinator could not be ensured: ${outcome2}`);
11008
+ this.outcome = outcome2;
10237
11009
  this.name = "RoomCoordinatorEnsureError";
10238
11010
  }
10239
11011
  };
@@ -12720,6 +13492,43 @@ function connectionRoutes(fastify2) {
12720
13492
  // src/controllers/mcp-configs.ts
12721
13493
  var import_core33 = require("@axiom-lattice/core");
12722
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
12723
13532
  function getTenantId11(request) {
12724
13533
  const userTenantId = request.user?.tenantId;
12725
13534
  if (userTenantId) {
@@ -12727,6 +13536,14 @@ function getTenantId11(request) {
12727
13536
  }
12728
13537
  return request.headers["x-tenant-id"] || "default";
12729
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
+ }
12730
13547
  async function getMcpServerConfigList(request, reply) {
12731
13548
  const tenantId = getTenantId11(request);
12732
13549
  try {
@@ -12804,6 +13621,7 @@ async function createMcpServerConfig(request, reply) {
12804
13621
  await store2.updateConfig(tenantId, id3, { status: "error" });
12805
13622
  config.status = "error";
12806
13623
  }
13624
+ broadcastMcpConfigChanged(tenantId, config.key);
12807
13625
  reply.code(201);
12808
13626
  return {
12809
13627
  success: true,
@@ -12833,7 +13651,9 @@ async function updateMcpServerConfig(request, reply) {
12833
13651
  message: "MCP server configuration not found"
12834
13652
  };
12835
13653
  }
12836
- 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;
12837
13657
  const updated = await store2.updateConfig(tenantId, existing.id, updates);
12838
13658
  if (!updated) {
12839
13659
  return {
@@ -12841,7 +13661,7 @@ async function updateMcpServerConfig(request, reply) {
12841
13661
  message: "Failed to update MCP server configuration"
12842
13662
  };
12843
13663
  }
12844
- if (shouldReconnect) {
13664
+ if (configChanged) {
12845
13665
  try {
12846
13666
  const connection = (0, import_core33.convertToConnection)(updated.config);
12847
13667
  import_core33.mcpManager.registerServer(tenantId, key, connection, {
@@ -12853,11 +13673,27 @@ async function updateMcpServerConfig(request, reply) {
12853
13673
  await store2.updateConfig(tenantId, existing.id, { status: "connected" });
12854
13674
  updated.status = "connected";
12855
13675
  } catch (error) {
12856
- console.warn("Failed to reconnect MCP server:", error);
12857
- await store2.updateConfig(tenantId, existing.id, { status: "error" });
12858
- updated.status = "error";
13676
+ console.warn("Failed to reconnect MCP server:", error);
13677
+ await store2.updateConfig(tenantId, existing.id, { status: "error" });
13678
+ updated.status = "error";
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);
12859
13692
  }
12860
13693
  }
13694
+ if (configChanged || toolsChanged || statusChanged) {
13695
+ broadcastMcpConfigChanged(tenantId, key);
13696
+ }
12861
13697
  return {
12862
13698
  success: true,
12863
13699
  message: "MCP server configuration updated successfully",
@@ -12906,6 +13742,7 @@ async function deleteMcpServerConfig(request, reply) {
12906
13742
  message: "Failed to delete MCP server configuration"
12907
13743
  };
12908
13744
  }
13745
+ broadcastMcpConfigChanged(tenantId, configKey);
12909
13746
  return {
12910
13747
  success: true,
12911
13748
  message: "MCP server configuration deleted successfully"
@@ -12980,12 +13817,17 @@ async function listMcpServerTools(request, reply) {
12980
13817
  message: "MCP server configuration not found"
12981
13818
  };
12982
13819
  }
12983
- if (!import_core33.mcpManager.isServerConnected(tenantId, key)) {
13820
+ const wasConnected = import_core33.mcpManager.isServerConnected(tenantId, key);
13821
+ if (!wasConnected) {
12984
13822
  await connectAndRegisterTools(config);
12985
13823
  }
12986
- if (config.status !== "connected") {
13824
+ const statusStale = config.status !== "connected";
13825
+ if (statusStale) {
12987
13826
  await store2.updateConfig(tenantId, config.id, { status: "connected" });
12988
13827
  }
13828
+ if (!wasConnected || statusStale) {
13829
+ broadcastMcpConfigChanged(tenantId, key);
13830
+ }
12989
13831
  const tools = await import_core33.mcpManager.getServerTools(tenantId, key);
12990
13832
  return {
12991
13833
  success: true,
@@ -13020,6 +13862,7 @@ async function connectMcpServer(request, reply) {
13020
13862
  const updated = await store2.updateConfig(tenantId, config.id, {
13021
13863
  status: "connected"
13022
13864
  });
13865
+ broadcastMcpConfigChanged(tenantId, key);
13023
13866
  return {
13024
13867
  success: true,
13025
13868
  message: "MCP server connected successfully",
@@ -13033,6 +13876,7 @@ async function connectMcpServer(request, reply) {
13033
13876
  if (config) {
13034
13877
  await store2.updateConfig(tenantId, config.id, { status: "error" });
13035
13878
  }
13879
+ broadcastMcpConfigChanged(tenantId, key);
13036
13880
  return {
13037
13881
  success: false,
13038
13882
  message: `Failed to connect MCP server: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -13059,6 +13903,7 @@ async function disconnectMcpServer(request, reply) {
13059
13903
  const updated = await store2.updateConfig(tenantId, config.id, {
13060
13904
  status: "disconnected"
13061
13905
  });
13906
+ broadcastMcpConfigChanged(tenantId, key);
13062
13907
  return {
13063
13908
  success: true,
13064
13909
  message: "MCP server disconnected successfully",
@@ -15309,7 +16154,7 @@ function verifyLarkParsedBody(body, config) {
15309
16154
  }
15310
16155
  return extractVerificationToken(body) === config.verificationToken;
15311
16156
  }
15312
- function headerValue(request, name) {
16157
+ function headerValue2(request, name) {
15313
16158
  const value = request.headers[name];
15314
16159
  return Array.isArray(value) ? value[0] : value;
15315
16160
  }
@@ -15317,9 +16162,9 @@ function verifyLarkRequestSignature(request, config) {
15317
16162
  if (!config.encryptKey) {
15318
16163
  return true;
15319
16164
  }
15320
- const timestamp = headerValue(request, "x-lark-request-timestamp");
15321
- const nonce = headerValue(request, "x-lark-request-nonce");
15322
- const signature = headerValue(request, "x-lark-signature");
16165
+ const timestamp = headerValue2(request, "x-lark-request-timestamp");
16166
+ const nonce = headerValue2(request, "x-lark-request-nonce");
16167
+ const signature = headerValue2(request, "x-lark-signature");
15323
16168
  if (!timestamp || !nonce || !signature) {
15324
16169
  return false;
15325
16170
  }
@@ -15577,8 +16422,8 @@ function snapshotPlainObject(value) {
15577
16422
  }
15578
16423
  }
15579
16424
  async function getInstallationStore() {
15580
- const { getStoreLattice: getStoreLattice37 } = await import("@axiom-lattice/core");
15581
- const store2 = getStoreLattice37("default", "channelInstallation").store;
16425
+ const { getStoreLattice: getStoreLattice39 } = await import("@axiom-lattice/core");
16426
+ const store2 = getStoreLattice39("default", "channelInstallation").store;
15582
16427
  if (store2) return store2;
15583
16428
  const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
15584
16429
  const databaseUrl = process.env.DATABASE_URL;
@@ -16720,7 +17565,7 @@ function registerTaskRoutes(app2, controller) {
16720
17565
  }
16721
17566
 
16722
17567
  // src/routes/index.ts
16723
- var import_core51 = require("@axiom-lattice/core");
17568
+ var import_core54 = require("@axiom-lattice/core");
16724
17569
 
16725
17570
  // src/controllers/tasks.ts
16726
17571
  var import_protocols13 = require("@axiom-lattice/protocols");
@@ -16929,40 +17774,8 @@ function isTaskFileRef(value) {
16929
17774
  return !!row && typeof row.uri === "string" && !!row.uri && (row.name === void 0 || typeof row.name === "string") && (row.mimeType === void 0 || typeof row.mimeType === "string") && (row.addedBy === void 0 || row.addedBy === "user" || row.addedBy === "agent");
16930
17775
  }
16931
17776
 
16932
- // src/services/collaboration/projectTaskScope.ts
16933
- function isProjectLifecycleTask(task) {
16934
- const source = task.context?.source;
16935
- return typeof task.workspaceId === "string" && task.workspaceId.length > 0 && typeof task.projectId === "string" && task.projectId.length > 0 && (source === "project_room" || source === "project_task" || source === "user");
16936
- }
16937
- function isRosterExemptProject(project) {
16938
- return project?.kind === "public";
16939
- }
16940
- function resolveTaskOwnerMembership(task, project, room, membership) {
16941
- const active = membership && membership.status === "active" && membership.tenantId === task.tenantId && membership.workspaceId === task.workspaceId && membership.projectId === task.projectId && membership.assistantId === task.ownerId && membership.roomId === room.id;
16942
- if (active) return membership;
16943
- if (!isRosterExemptProject(project)) return void 0;
16944
- return syntheticPublicProjectOwnerMembership(task, room);
16945
- }
16946
- function syntheticPublicProjectOwnerMembership(task, room) {
16947
- const threadId = typeof task.context?.thread_id === "string" && task.context.thread_id.length > 0 ? task.context.thread_id : task.ownerId;
16948
- return {
16949
- id: `public-project-owner:${task.projectId}:${task.ownerId}`,
16950
- tenantId: task.tenantId,
16951
- workspaceId: task.workspaceId,
16952
- projectId: task.projectId,
16953
- roomId: room.id,
16954
- assistantId: task.ownerId,
16955
- role: "specialist",
16956
- title: task.ownerId,
16957
- mentionName: task.ownerId,
16958
- status: "active",
16959
- roomThreadId: threadId,
16960
- joinedAt: task.createdAt,
16961
- updatedAt: task.updatedAt
16962
- };
16963
- }
16964
-
16965
17777
  // src/services/collaboration/ProjectTaskThreadService.ts
17778
+ init_projectTaskScope();
16966
17779
  var ProjectTaskThreadError = class extends Error {
16967
17780
  /**
16968
17781
  * @param code Stable machine-readable failure code.
@@ -17078,6 +17891,7 @@ var import_node_crypto9 = require("crypto");
17078
17891
  var import_node_crypto8 = require("crypto");
17079
17892
  var import_core44 = require("@axiom-lattice/core");
17080
17893
  var import_protocols12 = require("@axiom-lattice/protocols");
17894
+ init_projectTaskScope();
17081
17895
 
17082
17896
  // src/router/roomInterrupt.ts
17083
17897
  var CLARIFY_FENCE = /^```clarify\n([\s\S]*?)\n```$/;
@@ -17330,18 +18144,22 @@ var ProjectTaskExecutionService = class {
17330
18144
  input.inputMessageId,
17331
18145
  true
17332
18146
  );
18147
+ if (resumed.leaseContended) {
18148
+ settlement.cleanup();
18149
+ return { kind: "deferred", task: input.task, thread: input.thread, messageId: input.inputMessageId };
18150
+ }
17333
18151
  if (resumed.status === import_core44.ThreadStatus.INTERRUPTED && settlement.tryClaim()) {
17334
18152
  settlement.cleanup();
17335
- const outcome = await this.settleInterruptedEvent(input.task, input.thread, input.inputMessageId);
17336
- settlement.resolve(outcome);
17337
- return outcome;
18153
+ const outcome2 = await this.settleInterruptedEvent(input.task, input.thread, input.inputMessageId);
18154
+ settlement.resolve(outcome2);
18155
+ return outcome2;
17338
18156
  }
17339
18157
  if ((resumed.status === import_core44.ThreadStatus.IDLE || resumed.status === import_core44.ThreadStatus.ERROR) && settlement.tryClaim()) {
17340
18158
  settlement.cleanup();
17341
18159
  const reason = resumed.status === import_core44.ThreadStatus.IDLE ? "Project task execution ended without lifecycle transition" : "Recovered Agent execution is in error state";
17342
- const outcome = await this.settleEndedRecovery(input, reason);
17343
- settlement.resolve(outcome);
17344
- return outcome;
18160
+ const outcome2 = await this.settleEndedRecovery(input, reason);
18161
+ settlement.resolve(outcome2);
18162
+ return outcome2;
17345
18163
  }
17346
18164
  return await this.awaitSettlementCancellation(
17347
18165
  settlement,
@@ -17353,7 +18171,7 @@ var ProjectTaskExecutionService = class {
17353
18171
  } catch (error) {
17354
18172
  if (!settlement.tryClaim()) return settlement.promise;
17355
18173
  settlement.cleanup();
17356
- const outcome = {
18174
+ const outcome2 = {
17357
18175
  kind: "failed",
17358
18176
  task: input.task,
17359
18177
  thread: input.thread,
@@ -17362,8 +18180,8 @@ var ProjectTaskExecutionService = class {
17362
18180
  cause: "recovery_ended",
17363
18181
  lifecycleHandled: false
17364
18182
  };
17365
- settlement.resolve(outcome);
17366
- return outcome;
18183
+ settlement.resolve(outcome2);
18184
+ return outcome2;
17367
18185
  }
17368
18186
  }
17369
18187
  /** Classifies one exact Task Thread queue fact with transition-safe repeated reads. */
@@ -17581,9 +18399,9 @@ var ProjectTaskExecutionService = class {
17581
18399
  async claimDispatchFailure(settlement, task, thread, messageId, error) {
17582
18400
  if (!settlement.tryClaim()) return settlement.promise;
17583
18401
  settlement.cleanup();
17584
- const outcome = await this.failSettledTaskAwaited(task, thread, messageId, error);
17585
- settlement.resolve(outcome);
17586
- return outcome;
18402
+ const outcome2 = await this.failSettledTaskAwaited(task, thread, messageId, error);
18403
+ settlement.resolve(outcome2);
18404
+ return outcome2;
17587
18405
  }
17588
18406
  armSettlement(task, thread, messageId) {
17589
18407
  let claimed = false;
@@ -17674,9 +18492,9 @@ var ProjectTaskExecutionService = class {
17674
18492
  } catch {
17675
18493
  }
17676
18494
  }
17677
- const outcome = await this.compensateCancellation(task, thread, messageId, false);
17678
- settlement.resolve(outcome);
17679
- return outcome;
18495
+ const outcome2 = await this.compensateCancellation(task, thread, messageId, false);
18496
+ settlement.resolve(outcome2);
18497
+ return outcome2;
17680
18498
  }
17681
18499
  compensateCancellation(task, thread, messageId, abortAgent) {
17682
18500
  if (abortAgent) {
@@ -19018,6 +19836,7 @@ var import_zod6 = require("zod");
19018
19836
  // src/services/agent-web-app/AgentWebAppRuntimeService.ts
19019
19837
  var import_node_crypto10 = require("crypto");
19020
19838
  var import_protocols14 = require("@axiom-lattice/protocols");
19839
+ var import_core46 = require("@axiom-lattice/core");
19021
19840
  init_task_file_refs();
19022
19841
  function isRecord2(value) {
19023
19842
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -19180,6 +19999,20 @@ var AgentWebAppRuntimeService = class {
19180
19999
  async resumeStream(webAppId, userId, threadId, messageId, selection) {
19181
20000
  const runtime = await this.resolveRuntime(webAppId, selection);
19182
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
+ }
19183
20016
  const agent = this.resolveAgent(runtime, threadId);
19184
20017
  return { stream: agent.chunkStream(messageId, []) };
19185
20018
  }
@@ -19525,6 +20358,7 @@ async function verifyWebAppToken(token, issuer, options = {}) {
19525
20358
 
19526
20359
  // src/routes/agent-web-app-runtime.ts
19527
20360
  var import_protocols15 = require("@axiom-lattice/protocols");
20361
+ var import_core47 = require("@axiom-lattice/core");
19528
20362
  var idSchema = import_zod6.z.string().min(1).max(128);
19529
20363
  var paramsSchema = import_zod6.z.object({ webAppId: idSchema }).strict();
19530
20364
  var threadParamsSchema = import_zod6.z.object({ webAppId: idSchema, threadId: idSchema }).strict();
@@ -19673,7 +20507,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
19673
20507
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
19674
20508
  const input = streamBodySchema.parse(request.body);
19675
20509
  const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
19676
- beginSse(reply);
20510
+ beginSse(reply, request);
19677
20511
  await pipeNativeWebAppStream(reply.raw, result.stream);
19678
20512
  } catch (error) {
19679
20513
  return normalizeError2(reply, error);
@@ -19685,7 +20519,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
19685
20519
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
19686
20520
  const input = streamBodySchema.parse(request.body);
19687
20521
  const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
19688
- beginSse(reply);
20522
+ beginSse(reply, request);
19689
20523
  await pipeAgentWebAppStream(reply.raw, result.stream, result);
19690
20524
  } catch (error) {
19691
20525
  return normalizeError2(reply, error);
@@ -19697,7 +20531,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
19697
20531
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
19698
20532
  const { messageId, projectId, modelKey: modelKey2 } = resumeSchema.parse(request.body);
19699
20533
  const result = await deps.service.resumeStream(webAppId, identity.userId, threadId, messageId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview));
19700
- beginSse(reply);
20534
+ beginSse(reply, request);
19701
20535
  await pipeNativeWebAppStream(reply.raw, result.stream);
19702
20536
  } catch (error) {
19703
20537
  return normalizeError2(reply, error);
@@ -19730,7 +20564,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
19730
20564
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
19731
20565
  const { projectId, modelKey: modelKey2, message, response } = resumeBodySchema.parse(request.body);
19732
20566
  const result = await deps.service.resumeInterrupt(webAppId, identity.userId, threadId, interruptId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview), { message, response });
19733
- beginSse(reply);
20567
+ beginSse(reply, request);
19734
20568
  await pipeAgentWebAppStream(reply.raw, result.stream, result);
19735
20569
  } catch (error) {
19736
20570
  return normalizeError2(reply, error);
@@ -19958,6 +20792,10 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
19958
20792
  if (!threadId) throw new AgentWebAppRuntimeError("INVALID_REQUEST", 400, "thread_id is required");
19959
20793
  const content = body.messages.map((message) => message.content).join("\n");
19960
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
+ }
19961
20799
  return reply.code(202).send({ success: true, messageId, queued: true });
19962
20800
  } catch (error) {
19963
20801
  return normalizeError2(reply, error);
@@ -19978,7 +20816,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
19978
20816
  const identity = await resolveRuntimeIdentity(request, webAppId, identities);
19979
20817
  const body = resumeStreamSchema2.parse(request.body ?? {});
19980
20818
  const result = await deps.service.resumeStream(webAppId, identity.userId, body.thread_id, body.message_id, { projectId: body.projectId, modelKey: body.modelKey });
19981
- beginSse(reply);
20819
+ beginSse(reply, request);
19982
20820
  await pipeNativeWebAppStream(reply.raw, result.stream);
19983
20821
  } catch (error) {
19984
20822
  return normalizeError2(reply, error);
@@ -20031,14 +20869,17 @@ async function pipeNativeWebAppStream(raw, stream) {
20031
20869
  }]
20032
20870
  });
20033
20871
  }
20034
- function beginSse(reply) {
20872
+ function beginSse(reply, request) {
20035
20873
  reply.hijack();
20036
20874
  reply.raw.writeHead(200, {
20037
20875
  "Content-Type": "text/event-stream; charset=utf-8",
20038
20876
  "Cache-Control": "no-cache, no-transform",
20039
20877
  Connection: "keep-alive",
20040
20878
  "X-Accel-Buffering": "no",
20041
- "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])
20042
20883
  });
20043
20884
  }
20044
20885
  async function handle2(reply, operation) {
@@ -20274,7 +21115,7 @@ var import_zod9 = require("zod");
20274
21115
 
20275
21116
  // src/services/capability/CapabilityBundleService.ts
20276
21117
  var import_zod8 = require("zod");
20277
- var import_core46 = require("@axiom-lattice/core");
21118
+ var import_core48 = require("@axiom-lattice/core");
20278
21119
  var CapabilityBundleServiceError = class extends Error {
20279
21120
  constructor(code, message, statusCode) {
20280
21121
  super(message);
@@ -20315,7 +21156,7 @@ function projectBundleIds(config) {
20315
21156
  return [...value];
20316
21157
  }
20317
21158
  function issue(error) {
20318
- 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 };
20319
21160
  return { code: "CAPABILITY_BUNDLE_CONFIGURATION_ERROR", message: error instanceof Error ? error.message : "Invalid capability bundle configuration" };
20320
21161
  }
20321
21162
  var CapabilityBundleService = class {
@@ -20328,7 +21169,7 @@ var CapabilityBundleService = class {
20328
21169
  const errors = [];
20329
21170
  for (const value of values) {
20330
21171
  try {
20331
- (0, import_core46.validateCapabilityBundleInput)(value);
21172
+ (0, import_core48.validateCapabilityBundleInput)(value);
20332
21173
  bundles.push(value);
20333
21174
  } catch {
20334
21175
  errors.push({ bundleId: value.id, code: "INVALID_BUNDLE_CONFIG", message: "Capability bundle record is invalid" });
@@ -20340,7 +21181,7 @@ var CapabilityBundleService = class {
20340
21181
  const value = await this.deps.bundleStore.getById(tenantId, id3);
20341
21182
  if (!value) throw new CapabilityBundleServiceError("BUNDLE_NOT_FOUND", "Capability bundle not found", 404);
20342
21183
  try {
20343
- (0, import_core46.validateCapabilityBundleInput)(value);
21184
+ (0, import_core48.validateCapabilityBundleInput)(value);
20344
21185
  } catch {
20345
21186
  throw new CapabilityBundleServiceError("INVALID_BUNDLE_CONFIG", "Capability bundle record is invalid", 422);
20346
21187
  }
@@ -20348,7 +21189,7 @@ var CapabilityBundleService = class {
20348
21189
  }
20349
21190
  async create(tenantId, input) {
20350
21191
  rejectStableKey(input);
20351
- (0, import_core46.validateCapabilityBundleInput)(input);
21192
+ (0, import_core48.validateCapabilityBundleInput)(input);
20352
21193
  const value = input;
20353
21194
  const name = value.name.trim();
20354
21195
  const base = slugify(name);
@@ -20359,10 +21200,10 @@ var CapabilityBundleService = class {
20359
21200
  let suffix = 2;
20360
21201
  while (keys2.has(key)) key = `${base}-${suffix++}`;
20361
21202
  const normalized = { ...value, key, name };
20362
- (0, import_core46.validateCapabilityBundleInput)(normalized);
21203
+ (0, import_core48.validateCapabilityBundleInput)(normalized);
20363
21204
  try {
20364
21205
  const result = await this.deps.bundleStore.create(tenantId, normalized);
20365
- (0, import_core46.validateCapabilityBundleInput)(result);
21206
+ (0, import_core48.validateCapabilityBundleInput)(result);
20366
21207
  return result;
20367
21208
  } catch (error) {
20368
21209
  if (!isDuplicate(error)) throw error;
@@ -20373,16 +21214,16 @@ var CapabilityBundleService = class {
20373
21214
  }
20374
21215
  async update(tenantId, id3, input) {
20375
21216
  rejectStableKey(input);
20376
- (0, import_core46.validateCapabilityBundleInput)(input, { partial: true });
21217
+ (0, import_core48.validateCapabilityBundleInput)(input, { partial: true });
20377
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);
20378
21219
  const value = input;
20379
21220
  const normalized = { ...value, ...value.name === void 0 ? {} : { name: value.name.trim() }, ...Object.prototype.hasOwnProperty.call(value, "description") ? { description: value.description?.trim() || void 0 } : {} };
20380
- (0, import_core46.validateCapabilityBundleInput)(normalized, { partial: true });
21221
+ (0, import_core48.validateCapabilityBundleInput)(normalized, { partial: true });
20381
21222
  try {
20382
21223
  const result = await this.deps.bundleStore.update(tenantId, id3, normalized);
20383
21224
  if (!result) throw new CapabilityBundleServiceError("BUNDLE_NOT_FOUND", "Capability bundle not found", 404);
20384
21225
  if ("status" in result && result.status === "conflict") throw new CapabilityBundleServiceError("BUNDLE_CONFLICT", "Capability bundle changed after it was loaded; reload before saving", 409);
20385
- (0, import_core46.validateCapabilityBundleInput)(result);
21226
+ (0, import_core48.validateCapabilityBundleInput)(result);
20386
21227
  return result;
20387
21228
  } catch (error) {
20388
21229
  if (error instanceof CapabilityBundleServiceError) throw error;
@@ -20417,8 +21258,8 @@ var CapabilityBundleService = class {
20417
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: "" };
20418
21259
  try {
20419
21260
  const ordered = ids.map((id3) => byId.get(id3));
20420
- ordered.forEach((bundle) => (0, import_core46.validateCapabilityBundleInput)(bundle));
20421
- return (0, import_core46.mergeCapabilityBundles)(ordered);
21261
+ ordered.forEach((bundle) => (0, import_core48.validateCapabilityBundleInput)(bundle));
21262
+ return (0, import_core48.mergeCapabilityBundles)(ordered);
20422
21263
  } catch (error) {
20423
21264
  return { bundleIds: ids, capabilities: [], overrides: [], sources: [], warnings: [], errors: [issue(error)], revision: "" };
20424
21265
  }
@@ -20451,7 +21292,7 @@ var CapabilityBundleService = class {
20451
21292
  return ids.map((id3) => {
20452
21293
  const value = byId.get(id3);
20453
21294
  if (!value) throw new CapabilityBundleServiceError("BUNDLE_NOT_FOUND", "Capability bundle not found", 404);
20454
- (0, import_core46.validateCapabilityBundleInput)(value);
21295
+ (0, import_core48.validateCapabilityBundleInput)(value);
20455
21296
  return value;
20456
21297
  });
20457
21298
  }
@@ -20466,7 +21307,7 @@ var CapabilityBundleService = class {
20466
21307
  continue;
20467
21308
  }
20468
21309
  try {
20469
- (0, import_core46.validateCapabilityBundleInput)(bundle);
21310
+ (0, import_core48.validateCapabilityBundleInput)(bundle);
20470
21311
  valid.push(bundle);
20471
21312
  } catch (error) {
20472
21313
  errors.push({ ...issue(error), bundleId: id3 });
@@ -20474,7 +21315,7 @@ var CapabilityBundleService = class {
20474
21315
  }
20475
21316
  let preview;
20476
21317
  try {
20477
- preview = (0, import_core46.mergeCapabilityBundles)(valid);
21318
+ preview = (0, import_core48.mergeCapabilityBundles)(valid);
20478
21319
  } catch (error) {
20479
21320
  preview = { bundleIds: ids, capabilities: [], overrides: [], sources: [], warnings: [], errors: [issue(error)], revision: "" };
20480
21321
  }
@@ -20626,7 +21467,7 @@ var import_zod11 = require("zod");
20626
21467
  // src/services/model-provider/ModelProviderService.ts
20627
21468
  var import_node_crypto14 = require("crypto");
20628
21469
  var import_protocols16 = require("@axiom-lattice/protocols");
20629
- var import_core47 = require("@axiom-lattice/core");
21470
+ var import_core49 = require("@axiom-lattice/core");
20630
21471
 
20631
21472
  // src/services/model-provider/errors.ts
20632
21473
  var ModelProviderServiceError = class extends Error {
@@ -20639,6 +21480,17 @@ var ModelProviderServiceError = class extends Error {
20639
21480
  this.name = "ModelProviderServiceError";
20640
21481
  }
20641
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
+ }
20642
21494
 
20643
21495
  // src/services/model-provider/discovery.ts
20644
21496
  var MAX_DISCOVERED_MODELS = 1e3;
@@ -20914,6 +21766,33 @@ var ModelProviderService = class {
20914
21766
  models
20915
21767
  };
20916
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
+ }
20917
21796
  /**
20918
21797
  * Create a provider configuration. Does not run discovery.
20919
21798
  */
@@ -21153,7 +22032,7 @@ var ModelProviderService = class {
21153
22032
  models
21154
22033
  };
21155
22034
  } catch (error) {
21156
- if (error instanceof ModelProviderServiceError) {
22035
+ if (error instanceof ModelProviderServiceError && isUpstreamErrorCode(error.code)) {
21157
22036
  return {
21158
22037
  reachable: false,
21159
22038
  latencyMs: Date.now() - startedAt,
@@ -21392,9 +22271,9 @@ var ModelProviderService = class {
21392
22271
  */
21393
22272
  async assertSafeUrl(baseURL) {
21394
22273
  try {
21395
- await (0, import_core47.assertSafeUpstreamUrl)(baseURL);
22274
+ await (0, import_core49.assertSafeUpstreamUrl)(baseURL);
21396
22275
  } catch (error) {
21397
- if (error instanceof import_core47.UnsafeUpstreamUrlError) {
22276
+ if (error instanceof import_core49.UnsafeUpstreamUrlError) {
21398
22277
  throw new ModelProviderServiceError(
21399
22278
  unsafeReasonToErrorCode[error.reason],
21400
22279
  400,
@@ -21570,6 +22449,10 @@ var ModelProviderController = class {
21570
22449
  return sendError2(reply, error);
21571
22450
  }
21572
22451
  }
22452
+ /** Hydrates the runtime model registry from persisted providers (startup). */
22453
+ async hydrateRegistry() {
22454
+ return this.service.hydrateRegistry();
22455
+ }
21573
22456
  /** GET /api/model-providers */
21574
22457
  async listProviders(request, reply) {
21575
22458
  try {
@@ -22413,8 +23296,8 @@ var RoomMessageService = class {
22413
23296
  const resolved = await promise;
22414
23297
  return this.resultForCaller(resolved, resolved.replayed);
22415
23298
  }
22416
- const outcome = await this.createAndDispatchOutcome(actor3, normalized, room);
22417
- return this.resultForCaller(outcome, outcome.replayed);
23299
+ const outcome2 = await this.createAndDispatchOutcome(actor3, normalized, room);
23300
+ return this.resultForCaller(outcome2, outcome2.replayed);
22418
23301
  }
22419
23302
  async createAndDispatchOutcome(actor3, input, room) {
22420
23303
  const persisted = await this.persistNormalizedHumanMessage(actor3, input, room);
@@ -22463,8 +23346,8 @@ var RoomMessageService = class {
22463
23346
  }
22464
23347
  throw new RoomMessageServiceError("ROOM_DISPATCH_OVERLOADED", 429, "Room dispatch capacity is full", true);
22465
23348
  }
22466
- resultForCaller(outcome, replayed) {
22467
- return { message: cloneRoomMessage(outcome.message), dispatch: outcome.dispatch.map((entry) => ({ ...entry })), replayed };
23349
+ resultForCaller(outcome2, replayed) {
23350
+ return { message: cloneRoomMessage(outcome2.message), dispatch: outcome2.dispatch.map((entry) => ({ ...entry })), replayed };
22468
23351
  }
22469
23352
  /** Retries an existing human message using the current roster without creating a copy. */
22470
23353
  async retry(actor3, messageId) {
@@ -22856,7 +23739,7 @@ async function execute(reply, operation) {
22856
23739
  // src/routes/cospace.ts
22857
23740
  var import_node_crypto20 = require("crypto");
22858
23741
  var import_zod15 = require("zod");
22859
- var import_core48 = require("@axiom-lattice/core");
23742
+ var import_core50 = require("@axiom-lattice/core");
22860
23743
  var import_protocols19 = require("@axiom-lattice/protocols");
22861
23744
  var CoSpaceAuthError = class extends Error {
22862
23745
  constructor() {
@@ -23041,14 +23924,14 @@ function registerCoSpaceRoutes(app2, deps) {
23041
23924
  }
23042
23925
  let projectId;
23043
23926
  if (body.type === "human") {
23044
- projectId = (0, import_core48.generateDirectHumanProjectId)(
23927
+ projectId = (0, import_core50.generateDirectHumanProjectId)(
23045
23928
  actor3.tenantId,
23046
23929
  params.workspaceId,
23047
23930
  actor3.userId,
23048
23931
  body.targetUserId
23049
23932
  );
23050
23933
  } else {
23051
- projectId = (0, import_core48.generateDirectBotProjectId)(
23934
+ projectId = (0, import_core50.generateDirectBotProjectId)(
23052
23935
  actor3.tenantId,
23053
23936
  params.workspaceId,
23054
23937
  actor3.userId,
@@ -23255,7 +24138,7 @@ function registerCoSpaceRoutes(app2, deps) {
23255
24138
  );
23256
24139
  const bots = [];
23257
24140
  for (const assistant of assistants) {
23258
- const directProjectId = (0, import_core48.generateDirectBotProjectId)(
24141
+ const directProjectId = (0, import_core50.generateDirectBotProjectId)(
23259
24142
  actor3.tenantId,
23260
24143
  params.workspaceId,
23261
24144
  actor3.userId,
@@ -23317,7 +24200,7 @@ function registerCoSpaceRoutes(app2, deps) {
23317
24200
  if (link.userId === actor3.userId) continue;
23318
24201
  const user = await deps.userStore.getUserById(link.userId);
23319
24202
  if (!user) continue;
23320
- const directProjectId = (0, import_core48.generateDirectHumanProjectId)(
24203
+ const directProjectId = (0, import_core50.generateDirectHumanProjectId)(
23321
24204
  actor3.tenantId,
23322
24205
  params.workspaceId,
23323
24206
  actor3.userId,
@@ -23843,6 +24726,7 @@ function exactValues(value, required, optional = []) {
23843
24726
  // src/services/collaboration/CoordinatorWakeNotifier.ts
23844
24727
  var import_node_crypto21 = require("crypto");
23845
24728
  var import_protocols21 = require("@axiom-lattice/protocols");
24729
+ init_projectTaskScope();
23846
24730
  var SYNTHETIC_WAKE_AUTHOR = "coordinator-wake";
23847
24731
  var CoordinatorWakeNotifier = class {
23848
24732
  /** Creates a bounded Project Task lifecycle observer that wakes the Room Coordinator. */
@@ -23900,10 +24784,10 @@ var CoordinatorWakeNotifier = class {
23900
24784
  timer = setTimeout(() => resolve4("timeout"), timeoutMs);
23901
24785
  timer.unref?.();
23902
24786
  });
23903
- const outcome = await Promise.race([work.then(() => "drained"), timeout]);
24787
+ const outcome2 = await Promise.race([work.then(() => "drained"), timeout]);
23904
24788
  if (timer) clearTimeout(timer);
23905
24789
  this.closed = true;
23906
- if (outcome === "timeout") this.queues.clear();
24790
+ if (outcome2 === "timeout") this.queues.clear();
23907
24791
  }
23908
24792
  /** Closes admission and drains accepted work. */
23909
24793
  async dispose() {
@@ -24133,6 +25017,8 @@ function createRoomMessageRetentionJob(input) {
24133
25017
 
24134
25018
  // src/services/collaboration/ProjectTaskExecutionMutex.ts
24135
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 };
24136
25022
  function machineKey(scope) {
24137
25023
  const snapshot = (0, import_protocols22.snapshotExactRecord)(scope, ["tenantId", "assistantId", "projectId"]);
24138
25024
  if (!snapshot) throw new Error("Invalid Project task machine scope");
@@ -24156,9 +25042,12 @@ var ProjectTaskExecutionMutex = class {
24156
25042
  */
24157
25043
  async runExclusive(scope, operation, signal) {
24158
25044
  const key = machineKey(scope);
25045
+ const leaseKey = `project-task-machine:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.assistantId)}:${encodeURIComponent(scope.projectId)}`;
24159
25046
  await this.acquire(key, signal);
24160
25047
  try {
24161
- 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);
24162
25051
  } finally {
24163
25052
  this.release(key);
24164
25053
  }
@@ -24211,8 +25100,23 @@ var ProjectTaskExecutionMutex = class {
24211
25100
 
24212
25101
  // src/services/collaboration/ProjectTaskObserver.ts
24213
25102
  var import_node_crypto22 = require("crypto");
24214
- var import_core49 = require("@axiom-lattice/core");
25103
+ var import_core52 = require("@axiom-lattice/core");
24215
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
25119
+ init_projectTaskScope();
24216
25120
  var SYSTEM_ACTOR2 = "system";
24217
25121
  var TERMINAL_ACTIONS = ["completed", "failed", "interrupted", "cancelled"];
24218
25122
  var ProjectTaskObserver = class {
@@ -24233,7 +25137,7 @@ var ProjectTaskObserver = class {
24233
25137
  throw new Error(`Invalid Project task observer bound: ${name}`);
24234
25138
  }
24235
25139
  }
24236
- this.runBridge = (0, import_core49.createTaskRunBridge)({ lifecycle: this.deps.lifecycle });
25140
+ this.runBridge = (0, import_core52.createTaskRunBridge)({ lifecycle: this.deps.lifecycle });
24237
25141
  }
24238
25142
  /** Fire-and-forget notifier adapter that never waits for observer work or machine acquisition. */
24239
25143
  async listener(event) {
@@ -24412,11 +25316,11 @@ var ProjectTaskObserver = class {
24412
25316
  try {
24413
25317
  await this.requireActiveOwner(task);
24414
25318
  } catch (error) {
24415
- await this.convergeInterrupted(task, `No active executor: ${errorMessage3(error)}`);
25319
+ await this.convergeFailed(task, `No active executor: ${errorMessage3(error)}`);
24416
25320
  return;
24417
25321
  }
24418
25322
  }
24419
- 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) {
24420
25324
  await this.runStart(task, signal);
24421
25325
  }
24422
25326
  return;
@@ -24511,7 +25415,7 @@ var ProjectTaskObserver = class {
24511
25415
  const classification = await this.deps.execution.classifyRecovery(current, ensured.thread);
24512
25416
  if (signal?.aborted || this.disposed) return;
24513
25417
  if (classification.kind === "invalid") {
24514
- await this.convergeInterrupted(
25418
+ await this.convergeFailed(
24515
25419
  current,
24516
25420
  `Task execution state is not recoverable: ${classification.reason}`
24517
25421
  );
@@ -24532,40 +25436,40 @@ var ProjectTaskObserver = class {
24532
25436
  }
24533
25437
  }, signal);
24534
25438
  }
24535
- async mapOutcome(outcome) {
24536
- if (outcome.kind === "aborted") return;
24537
- const current = snapshotTaskItem(await this.deps.tasks.getById(outcome.task.tenantId, outcome.task.id));
24538
- if (!current || current.ownerId !== outcome.thread.assistantId || current.context?.thread_id !== outcome.thread.id) return;
25439
+ async mapOutcome(outcome2) {
25440
+ if (outcome2.kind === "aborted" || outcome2.kind === "deferred") return;
25441
+ const current = snapshotTaskItem(await this.deps.tasks.getById(outcome2.task.tenantId, outcome2.task.id));
25442
+ if (!current || current.ownerId !== outcome2.thread.assistantId || current.context?.thread_id !== outcome2.thread.id) return;
24539
25443
  if (isTerminal(current.status) || current.status === "review") return;
24540
- if (outcome.kind === "completed") {
25444
+ if (outcome2.kind === "completed") {
24541
25445
  const result2 = await this.runBridge.settleRun({
24542
25446
  tenantId: current.tenantId,
24543
25447
  task: current,
24544
25448
  actor: SYSTEM_ACTOR2,
24545
- threadId: outcome.thread.id,
25449
+ threadId: outcome2.thread.id,
24546
25450
  outcome: { kind: "completed" }
24547
25451
  });
24548
25452
  await this.checkLifecycle(current, "outcome-completed", result2);
24549
25453
  return;
24550
25454
  }
24551
- if (outcome.kind === "cancelled") {
25455
+ if (outcome2.kind === "cancelled") {
24552
25456
  const result2 = await this.runBridge.settleRun({
24553
25457
  tenantId: current.tenantId,
24554
25458
  task: current,
24555
25459
  actor: SYSTEM_ACTOR2,
24556
- threadId: outcome.thread.id,
25460
+ threadId: outcome2.thread.id,
24557
25461
  outcome: { kind: "cancelled" }
24558
25462
  });
24559
25463
  await this.checkLifecycle(current, "outcome-cancelled", result2);
24560
25464
  return;
24561
25465
  }
24562
- if (outcome.kind === "failed" && outcome.lifecycleHandled) return;
24563
- if (outcome.kind === "interrupted") {
25466
+ if (outcome2.kind === "failed" && outcome2.lifecycleHandled) return;
25467
+ if (outcome2.kind === "interrupted") {
24564
25468
  const result2 = await this.runBridge.settleRun({
24565
25469
  tenantId: current.tenantId,
24566
25470
  task: current,
24567
25471
  actor: SYSTEM_ACTOR2,
24568
- threadId: outcome.thread.id,
25472
+ threadId: outcome2.thread.id,
24569
25473
  outcome: { kind: "interrupted" }
24570
25474
  });
24571
25475
  await this.checkLifecycle(current, "outcome-interrupted", result2);
@@ -24575,10 +25479,10 @@ var ProjectTaskObserver = class {
24575
25479
  tenantId: current.tenantId,
24576
25480
  task: current,
24577
25481
  actor: SYSTEM_ACTOR2,
24578
- threadId: outcome.thread.id,
24579
- outcome: { kind: "failed", lifecycleHandled: false, error: outcome.error }
25482
+ threadId: outcome2.thread.id,
25483
+ outcome: { kind: "failed", lifecycleHandled: false, error: outcome2.error }
24580
25484
  });
24581
- await this.checkLifecycle(current, `outcome-${outcome.cause}`, result);
25485
+ await this.checkLifecycle(current, `outcome-${outcome2.cause}`, result);
24582
25486
  }
24583
25487
  async collectDependentIds(task) {
24584
25488
  const rows = /* @__PURE__ */ new Map();
@@ -24778,25 +25682,26 @@ var ProjectTaskObserver = class {
24778
25682
  }
24779
25683
  }
24780
25684
  /**
24781
- * Converges an unexecutable task to a durable interruption.
25685
+ * Converges a mechanically unexecutable task to a durable failure.
24782
25686
  *
24783
25687
  * @param task Exact task snapshot that cannot make progress through automation.
24784
- * @param summary Human-readable reason recorded with the interruption.
25688
+ * @param failureReason Human-readable reason recorded with the failure.
24785
25689
  * @remarks Never leaves a task silently pending behind a warning; an `in_progress`
24786
25690
  * task with no dispatched queue rows is intentionally left untouched by recovery.
24787
- * The interruption is visible and resumable once an executor is available.
25691
+ * A no-executor/unrecoverable state is a mechanical failure (`failed`), not HITL
25692
+ * (ADR-143); `failed` remains retryable.
24788
25693
  */
24789
- async convergeInterrupted(task, summary) {
25694
+ async convergeFailed(task, failureReason) {
24790
25695
  if (this.disposed) return;
24791
25696
  try {
24792
25697
  const result = await this.runBridge.settleRun({
24793
25698
  tenantId: task.tenantId,
24794
25699
  task,
24795
25700
  actor: SYSTEM_ACTOR2,
24796
- outcome: { kind: "interrupted", summary }
25701
+ outcome: { kind: "failed", lifecycleHandled: false, error: failureReason }
24797
25702
  });
24798
25703
  if (!result.success && result.code !== "TASK_STATUS_CONFLICT") {
24799
- await this.safeWarning(task, "converge", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? summary}`);
25704
+ await this.safeWarning(task, "converge", `${result.code ?? "LIFECYCLE_REJECTED"}: ${result.error ?? failureReason}`);
24800
25705
  }
24801
25706
  } catch (error) {
24802
25707
  await this.safeWarning(task, "converge", error);
@@ -25074,6 +25979,7 @@ function combineSignals(left, right) {
25074
25979
  // src/services/collaboration/RoomProjectionService.ts
25075
25980
  var import_node_crypto23 = require("crypto");
25076
25981
  var import_protocols24 = require("@axiom-lattice/protocols");
25982
+ init_projectTaskScope();
25077
25983
  var ROOM_PROJECTABLE_ACTIONS = [
25078
25984
  "interrupted",
25079
25985
  "failed",
@@ -25443,8 +26349,11 @@ function isRecord3(value) {
25443
26349
  return typeof value === "object" && value !== null && !Array.isArray(value);
25444
26350
  }
25445
26351
 
26352
+ // src/routes/index.ts
26353
+ init_projectTaskScope();
26354
+
25446
26355
  // src/services/collaboration/ProjectRoomRealtimeRuntime.ts
25447
- var import_core50 = require("@axiom-lattice/core");
26356
+ var import_core53 = require("@axiom-lattice/core");
25448
26357
 
25449
26358
  // src/services/collaboration/ProjectRoomEventBroker.ts
25450
26359
  var import_node_crypto24 = require("crypto");
@@ -25454,6 +26363,7 @@ var DEFAULT_MAX_EVENT_AGE_MS = 10 * 60 * 1e3;
25454
26363
  var DEFAULT_MAX_ROOMS = 1e3;
25455
26364
  var DEFAULT_MAX_GLOBAL_EVENTS = 1e5;
25456
26365
  var DEFAULT_MAX_GLOBAL_BYTES = 64 * 1024 * 1024;
26366
+ var DEFAULT_SOURCE_KEY_LIMIT = 2e3;
25457
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}$/;
25458
26368
  var ProjectRoomBrokerResourceCapacityError = class extends Error {
25459
26369
  constructor(resource) {
@@ -25467,6 +26377,7 @@ var ProjectRoomEventBroker = class {
25467
26377
  constructor(options = {}) {
25468
26378
  this.rooms = /* @__PURE__ */ new Map();
25469
26379
  this.sequenceScopes = /* @__PURE__ */ new Map();
26380
+ this.seenSourceKeys = /* @__PURE__ */ new Map();
25470
26381
  this.eventCount = 0;
25471
26382
  this.byteCount = 0;
25472
26383
  this.subscriberCount = 0;
@@ -25494,6 +26405,10 @@ var ProjectRoomEventBroker = class {
25494
26405
  options.maxGlobalBytes ?? DEFAULT_MAX_GLOBAL_BYTES,
25495
26406
  "maxGlobalBytes"
25496
26407
  );
26408
+ this.sourceKeyLimit = positiveSafeInteger(
26409
+ options.sourceKeyLimit ?? DEFAULT_SOURCE_KEY_LIMIT,
26410
+ "sourceKeyLimit"
26411
+ );
25497
26412
  this.sequence = nonnegativeSafeInteger(options.initialSequence ?? 0, "initialSequence");
25498
26413
  this.log = options.log ?? { warn: () => void 0 };
25499
26414
  }
@@ -25505,8 +26420,22 @@ var ProjectRoomEventBroker = class {
25505
26420
  get retainedBytes() {
25506
26421
  return this.byteCount;
25507
26422
  }
25508
- /** Atomically stores an immutable event before queueing detached listener callbacks. */
25509
- 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) {
25510
26439
  this.assertOpen();
25511
26440
  const canonicalScope = snapshotScope(scope);
25512
26441
  const key = scopeKey2(canonicalScope);
@@ -25541,9 +26470,52 @@ var ProjectRoomEventBroker = class {
25541
26470
  this.sequence = nextSequence;
25542
26471
  this.eventCount += 1;
25543
26472
  this.byteCount += bytes;
26473
+ if (sourceKey !== void 0) this.rememberSourceKey(sourceKey);
25544
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
+ }
25545
26481
  return parseScopedEvent(serialized);
25546
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
+ }
25547
26519
  /** Atomically resolves replay and registers a distinct subscription for live events. */
25548
26520
  subscribe(scope, afterEventId, listener) {
25549
26521
  this.assertOpen();
@@ -25583,6 +26555,7 @@ var ProjectRoomEventBroker = class {
25583
26555
  }
25584
26556
  this.rooms.clear();
25585
26557
  this.sequenceScopes.clear();
26558
+ this.seenSourceKeys.clear();
25586
26559
  this.eventCount = 0;
25587
26560
  this.byteCount = 0;
25588
26561
  this.subscriberCount = 0;
@@ -25717,6 +26690,14 @@ var ProjectRoomEventBroker = class {
25717
26690
  this.usageClock += 1;
25718
26691
  room.lastUsed = this.usageClock;
25719
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
+ }
25720
26701
  };
25721
26702
  function positiveSafeInteger(value, name) {
25722
26703
  if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError(`${name} must be a positive safe integer`);
@@ -25908,7 +26889,7 @@ var BrokerProjectRoomRealtimePublisher = class {
25908
26889
  if (!event) throw new Error("Invalid Project Room realtime projection");
25909
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");
25910
26891
  if (this.sourceKeys.has(dedupeKey)) return;
25911
- this.deps.broker.publish(event.scope, event.draft);
26892
+ this.deps.broker.publish(event.scope, event.draft, event.sourceKey);
25912
26893
  this.sourceKeys.set(dedupeKey, true);
25913
26894
  while (this.sourceKeys.size > this.dedupeLimit) {
25914
26895
  const oldest = this.sourceKeys.keys().next().value;
@@ -25961,6 +26942,7 @@ function safeErrorCode(error) {
25961
26942
 
25962
26943
  // src/services/collaboration/ProjectRoomTaskEventSubscriber.ts
25963
26944
  var import_protocols27 = require("@axiom-lattice/protocols");
26945
+ init_projectTaskScope();
25964
26946
  var ProjectRoomTaskEventSubscriber = class {
25965
26947
  /** Creates a bounded Project task lifecycle observer. */
25966
26948
  constructor(deps) {
@@ -26015,10 +26997,10 @@ var ProjectRoomTaskEventSubscriber = class {
26015
26997
  timer = setTimeout(() => resolve4("timeout"), timeoutMs);
26016
26998
  timer.unref?.();
26017
26999
  });
26018
- const outcome = await Promise.race([work.then(() => "drained"), timeout]);
27000
+ const outcome2 = await Promise.race([work.then(() => "drained"), timeout]);
26019
27001
  if (timer) clearTimeout(timer);
26020
27002
  this.closed = true;
26021
- if (outcome === "timeout") this.queues.clear();
27003
+ if (outcome2 === "timeout") this.queues.clear();
26022
27004
  }
26023
27005
  /** Closes admission and drains accepted work. */
26024
27006
  async dispose() {
@@ -26146,7 +27128,7 @@ function createProjectRoomRealtimeRuntime(deps) {
26146
27128
  broker,
26147
27129
  publisher,
26148
27130
  subscriber,
26149
- subscribe: (listener) => import_core50.taskLifecycleNotifier.subscribe(listener),
27131
+ subscribe: (listener) => import_core53.taskLifecycleNotifier.subscribe(listener),
26150
27132
  log: deps.log,
26151
27133
  shutdownTimeoutMs: deps.shutdownTimeoutMs
26152
27134
  });
@@ -26884,35 +27866,35 @@ function hookErrorMessage(error) {
26884
27866
  function wrapReassignmentThreadError(error) {
26885
27867
  if (error instanceof ProjectTaskThreadError) {
26886
27868
  if (error.code === "PROJECT_TASK_THREAD_SCOPE_INVALID") {
26887
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
27869
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", error.message, {
26888
27870
  hint: "Project task, room, or roster scope changed; re-read the task and retry."
26889
27871
  });
26890
27872
  }
26891
27873
  if (error.code === "PROJECT_TASK_THREAD_OWNER_INVALID") {
26892
- return new import_core51.ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
27874
+ return new import_core54.ManagedTaskStartError("TASK_REASSIGN_NO_CHANGE", error.message, {
26893
27875
  hint: "The target already owns this task; re-read before dispatching."
26894
27876
  });
26895
27877
  }
26896
- return new import_core51.ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
27878
+ return new import_core54.ManagedTaskStartError("TASK_THREAD_CONFLICT", error.message, {
26897
27879
  hint: "Task Thread changed concurrently; re-read the task and retry."
26898
27880
  });
26899
27881
  }
26900
- return new import_core51.ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
27882
+ return new import_core54.ManagedTaskStartError("TASK_EXECUTION_ENQUEUE_FAILED", hookErrorMessage(error), {
26901
27883
  hint: "Re-read the task and retry the dispatch."
26902
27884
  });
26903
27885
  }
26904
27886
  function wrapReassignmentAuthorizationError(error) {
26905
- if (error instanceof import_core51.ProjectTaskOwnerPolicyError) {
27887
+ if (error instanceof import_core54.ProjectTaskOwnerPolicyError) {
26906
27888
  if (error.code === "PROJECT_TASK_CALLER_INACTIVE") {
26907
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
27889
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_CALLER_INACTIVE", error.message, {
26908
27890
  hint: "Dispatch from an active Project Room member bot."
26909
27891
  });
26910
27892
  }
26911
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
27893
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_OWNER_INACTIVE", error.message, {
26912
27894
  hint: "Dispatch to an active bot in this Project's roster."
26913
27895
  });
26914
27896
  }
26915
- return new import_core51.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
27897
+ return new import_core54.ManagedTaskStartError("PROJECT_TASK_CONTEXT_INVALID", hookErrorMessage(error), {
26916
27898
  hint: "Re-read the task and roster, then retry the dispatch."
26917
27899
  });
26918
27900
  }
@@ -26941,43 +27923,43 @@ async function transferGovernedDispatchOwner(task, options, deps) {
26941
27923
  const agentId = options?.agentId;
26942
27924
  if (!agentId || agentId === task.ownerId) return void 0;
26943
27925
  if (!task.workspaceId || !task.projectId) {
26944
- 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.", {
26945
27927
  hint: "Dispatch governed tasks from a trusted Project run; ordinary tasks execute immediately."
26946
27928
  });
26947
27929
  }
26948
27930
  return deps.mutex.runExclusive({ tenantId: task.tenantId, projectId: task.projectId }, async () => {
26949
27931
  const current = await deps.tasks.getById(task.tenantId, task.id);
26950
27932
  if (!current || !isProjectLifecycleTask(current) || current.workspaceId !== task.workspaceId || current.projectId !== task.projectId) {
26951
- 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.", {
26952
27934
  hint: "Re-read the task and confirm it still belongs to this Project before dispatching."
26953
27935
  });
26954
27936
  }
26955
27937
  if (current.ownerType !== "agent") {
26956
- 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.", {
26957
27939
  hint: "Assign the task to an Agent before dispatching."
26958
27940
  });
26959
27941
  }
26960
27942
  if (current.status !== "pending") {
26961
- 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.", {
26962
27944
  hint: `This task is '${current.status}'. Resolve it or create a new task before dispatching.`
26963
27945
  });
26964
27946
  }
26965
27947
  if (agentId === current.ownerId) return current;
26966
27948
  const room = await deps.rooms.getMainRoom(current.tenantId, current.projectId);
26967
27949
  if (!room || room.tenantId !== current.tenantId || room.workspaceId !== current.workspaceId || room.projectId !== current.projectId || room.type !== "main") {
26968
- 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.", {
26969
27951
  hint: "Open or repair the Project Room before dispatching."
26970
27952
  });
26971
27953
  }
26972
27954
  const target = await deps.botMemberships.findByAssistant(current.tenantId, current.projectId, agentId);
26973
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) {
26974
- 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.", {
26975
27957
  hint: "Dispatch to an active bot in this Project's roster."
26976
27958
  });
26977
27959
  }
26978
27960
  const caller = options?.callerMembershipId ? await deps.botMemberships.findById(current.tenantId, options.callerMembershipId) : null;
26979
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) {
26980
- 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.", {
26981
27963
  hint: "Dispatch from an active Project Room member bot."
26982
27964
  });
26983
27965
  }
@@ -27012,7 +27994,7 @@ async function transferGovernedDispatchOwner(task, options, deps) {
27012
27994
  actor: `agent:${caller.assistantId}`
27013
27995
  });
27014
27996
  if (!result.success) {
27015
- throw new import_core51.ManagedTaskStartError(result.code, result.error, {
27997
+ throw new import_core54.ManagedTaskStartError(result.code, result.error, {
27016
27998
  hint: result.hint ?? reassignmentFailureHint(result.code),
27017
27999
  ...result.retryable !== void 0 ? { retryable: result.retryable } : {}
27018
28000
  });
@@ -27021,19 +28003,19 @@ async function transferGovernedDispatchOwner(task, options, deps) {
27021
28003
  });
27022
28004
  }
27023
28005
  var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27024
- const webAppStore = (0, import_core51.getStoreLattice)("default", "agentWebApp").store;
27025
- const assistantStore = (0, import_core51.getStoreLattice)("default", "assistant").store;
27026
- const projectStore = (0, import_core51.getStoreLattice)("default", "project").store;
27027
- const projectMembershipStore = (0, import_core51.getStoreLattice)("default", "projectMembership").store;
27028
- const userTenantLinkStore = (0, import_core51.getStoreLattice)("default", "userTenantLink").store;
27029
- const projectRoomStore = (0, import_core51.getStoreLattice)("default", "projectRoom").store;
27030
- const projectBotMembershipStore = (0, import_core51.getStoreLattice)("default", "projectBotMembership").store;
27031
- const projectRoomMessageStore = (0, import_core51.getStoreLattice)("default", "projectRoomMessage").store;
27032
- const projectRoomReadStateStore = (0, import_core51.getStoreLattice)("default", "projectRoomReadState").store;
27033
- const userPushSubscriptionStore = (0, import_core51.getStoreLattice)("default", "userPushSubscription").store;
27034
- const threadStore = (0, import_core51.getStoreLattice)("default", "thread").store;
27035
- const taskStore = (0, import_core51.getStoreLattice)("default", "task").store;
27036
- 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;
27037
28019
  const projectTaskEnabled = options.projectTaskEnabled ?? process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false";
27038
28020
  const accessService = new ProjectAccessService({ projectStore, membershipStore: projectMembershipStore, userTenantLinkStore });
27039
28021
  const realtimeMode = resolveProjectRoomRealtimeMode();
@@ -27099,7 +28081,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27099
28081
  updateAssistant: assistantStore.updateAssistant.bind(assistantStore)
27100
28082
  });
27101
28083
  const resolveCoordinatorMentionName = async (tenantId) => {
27102
- 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);
27103
28085
  const value = tenant2?.metadata?.coordinatorMentionName;
27104
28086
  return typeof value === "string" && value.length > 0 ? value : void 0;
27105
28087
  };
@@ -27126,7 +28108,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27126
28108
  return membership !== null && membership.projectId === projectId ? membership : null;
27127
28109
  },
27128
28110
  getThreadStatus: async ({ tenantId, assistantId, threadId, workspaceId, projectId }) => {
27129
- const agent = import_core51.agentInstanceManager.getAgent({
28111
+ const agent = import_core54.agentInstanceManager.getAgent({
27130
28112
  tenant_id: tenantId,
27131
28113
  assistant_id: assistantId,
27132
28114
  thread_id: threadId,
@@ -27135,9 +28117,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27135
28117
  });
27136
28118
  return String(await agent.getRunStatus());
27137
28119
  },
27138
- dispatcher: (0, import_core51.createTrustedAgentMessageDispatcher)((scope) => import_core51.agentInstanceManager.getAgent(scope))
28120
+ dispatcher: (0, import_core54.createTrustedAgentMessageDispatcher)((scope) => import_core54.agentInstanceManager.getAgent(scope))
27139
28121
  });
27140
- 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 });
27141
28123
  const rosterService = new ProjectRosterService({
27142
28124
  accessService,
27143
28125
  roomService,
@@ -27148,7 +28130,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27148
28130
  assistantStore,
27149
28131
  taskStore,
27150
28132
  bindingLifecycle,
27151
- projectMutationMutex: import_core51.projectMutationMutex,
28133
+ projectMutationMutex: import_core54.projectMutationMutex,
27152
28134
  realtime,
27153
28135
  onRealtimeFailure: realtime && channelDeps?.warn ? ({ code, membershipId }) => channelDeps.warn?.("project-room:roster-realtime-failed", { code, membershipId }) : void 0
27154
28136
  });
@@ -27204,9 +28186,9 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27204
28186
  }
27205
28187
  if (projectTaskComposition) app2.addHook("onClose", async () => projectTaskComposition.lease.release());
27206
28188
  const taskThreadService = projectTaskComposition?.lease.runtime.threads ?? new ProjectTaskThreadService({ threads: threadStore, idFactory: import_node_crypto26.randomUUID });
27207
- const taskLifecycle = projectTaskComposition?.lease.runtime.lifecycle ?? (0, import_core51.createTaskLifecycleService)({ taskStore, workItemStore: taskWorkItemStore });
27208
- const reassignmentAuthorizer = (0, import_core51.createProjectTaskReassignmentAuthorizer)(
27209
- 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),
27210
28192
  (input) => validateReassignmentThread(threadStore, input)
27211
28193
  );
27212
28194
  const taskCommandService = new TaskCommandService({
@@ -27215,7 +28197,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27215
28197
  lifecycle: taskLifecycle,
27216
28198
  execution: projectTaskComposition?.lease.runtime.execution,
27217
28199
  threads: taskThreadService,
27218
- agents: { getAgent: (params) => import_core51.agentInstanceManager.getAgent(params) },
28200
+ agents: { getAgent: (params) => import_core54.agentInstanceManager.getAgent(params) },
27219
28201
  taskScope: (task) => resolveCommandTaskScope(task, {
27220
28202
  rooms: projectRoomStore,
27221
28203
  bots: projectBotMembershipStore,
@@ -27253,7 +28235,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27253
28235
  readStateService,
27254
28236
  interruptService: roomInterruptService,
27255
28237
  userDirectory: {
27256
- 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)
27257
28239
  }
27258
28240
  });
27259
28241
  registerProjectRosterRoutes(app2, { service: rosterService });
@@ -27264,7 +28246,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27264
28246
  createProject: projectStore.createProject.bind(projectStore)
27265
28247
  },
27266
28248
  workspaceStore: {
27267
- 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)
27268
28250
  },
27269
28251
  membershipStore: {
27270
28252
  list: projectMembershipStore.list.bind(projectMembershipStore),
@@ -27287,7 +28269,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27287
28269
  getUsersByTenant: userTenantLinkStore.getUsersByTenant.bind(userTenantLinkStore)
27288
28270
  },
27289
28271
  userStore: {
27290
- 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)
27291
28273
  },
27292
28274
  assistantStore: {
27293
28275
  getAssistantById: assistantStore.getAssistantById.bind(assistantStore),
@@ -27309,28 +28291,30 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27309
28291
  }
27310
28292
  });
27311
28293
  registerCapabilityBundleRoutes(app2, new CapabilityBundleController({
27312
- bundleStore: (0, import_core51.getStoreLattice)("default", "capabilityBundle").store,
28294
+ bundleStore: (0, import_core54.getStoreLattice)("default", "capabilityBundle").store,
27313
28295
  projectStore
27314
28296
  }, { authRequired: process.env.AUTH_REQUIRED === "true" }));
27315
- registerModelProviderRoutes(app2, new ModelProviderController({
27316
- providerStore: (0, import_core51.getStoreLattice)("default", "modelProvider").store,
28297
+ const modelProviderController = new ModelProviderController({
28298
+ providerStore: (0, import_core54.getStoreLattice)("default", "modelProvider").store,
27317
28299
  userTenantLinkStore,
27318
28300
  registry: {
27319
- register: (tenantId, modelKey2, config) => import_core51.modelLatticeManager.registerLatticeWithTenant(tenantId, modelKey2, config),
27320
- has: (tenantId, modelKey2) => import_core51.modelLatticeManager.hasLatticeWithTenant(tenantId, modelKey2),
27321
- remove: (tenantId, modelKey2) => import_core51.modelLatticeManager.removeLatticeWithTenant(tenantId, modelKey2),
27322
- listRegistered: (tenantId) => import_core51.modelLatticeManager.getLatticeKeysByTenant(tenantId)
27323
- }
27324
- }, { 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);
27325
28309
  registerAgentWebAppRoutes(app2, {
27326
28310
  service: new AgentWebAppService({
27327
28311
  webAppStore,
27328
28312
  assistantStore,
27329
28313
  projectStore,
27330
- modelRegistry: { has: (key) => import_core51.modelLatticeManager.hasLattice(key) },
28314
+ modelRegistry: { has: (key) => import_core54.modelLatticeManager.hasLattice(key) },
27331
28315
  configuredAgentResolver: createConfiguredAgentResolver({
27332
- hydrate: (tenantId) => import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
27333
- 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)
27334
28318
  })
27335
28319
  })
27336
28320
  });
@@ -27367,28 +28351,28 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27367
28351
  },
27368
28352
  tasks: taskStore,
27369
28353
  workItems: taskWorkItemStore,
27370
- projectMutationMutex: import_core51.projectMutationMutex,
28354
+ projectMutationMutex: import_core54.projectMutationMutex,
27371
28355
  ...projectTaskComposition ? { commands: taskCommandService } : {}
27372
28356
  }),
27373
28357
  service: new AgentWebAppRuntimeService({
27374
28358
  webAppStore,
27375
- threadStore: (0, import_core51.getStoreLattice)("default", "thread").store,
28359
+ threadStore: (0, import_core54.getStoreLattice)("default", "thread").store,
27376
28360
  projectStore,
27377
28361
  assistantResolver: {
27378
28362
  resolve: async (tenantId, assistantId) => {
27379
28363
  const stored = await assistantStore.getAssistantById(tenantId, assistantId);
27380
28364
  if (stored) return { id: stored.id, name: stored.name, description: stored.description };
27381
- await import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId);
27382
- const config = import_core51.agentLatticeManager.getAgentConfigWithTenant(tenantId, assistantId);
28365
+ await import_core54.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId);
28366
+ const config = import_core54.agentLatticeManager.getAgentConfigWithTenant(tenantId, assistantId);
27383
28367
  return config ? { id: assistantId, name: config.name, description: config.description } : null;
27384
28368
  }
27385
28369
  },
27386
28370
  modelRegistry: {
27387
- has: (key) => import_core51.modelLatticeManager.hasLattice(key),
28371
+ has: (key) => import_core54.modelLatticeManager.hasLattice(key),
27388
28372
  label: (key) => key
27389
28373
  },
27390
28374
  messageReader: {
27391
- getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core51.agentInstanceManager.getAgent({
28375
+ getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core54.agentInstanceManager.getAgentForRead({
27392
28376
  tenant_id: tenantId,
27393
28377
  assistant_id: assistantId,
27394
28378
  thread_id: threadId,
@@ -27397,7 +28381,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27397
28381
  }).getCurrentMessages()
27398
28382
  },
27399
28383
  agentResolver: {
27400
- getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core51.agentInstanceManager.getAgent({
28384
+ getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core54.agentInstanceManager.getAgent({
27401
28385
  tenant_id: tenantId,
27402
28386
  assistant_id: assistantId,
27403
28387
  thread_id: threadId,
@@ -27415,8 +28399,8 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27415
28399
  if (webApp.tenantId !== preview.tenantId) return false;
27416
28400
  const assistant = await assistantStore.getAssistantById(preview.tenantId, webApp.assistantId);
27417
28401
  if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === preview.userId;
27418
- await import_core51.agentLatticeManager.initializeStoredAssistantsForTenant(preview.tenantId);
27419
- 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);
27420
28404
  }
27421
28405
  })
27422
28406
  });
@@ -27473,14 +28457,14 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27473
28457
  app2.post("/api/personal-assistant", createPersonalAssistant);
27474
28458
  app2.get("/api/personal-assistant", getPersonalAssistant);
27475
28459
  app2.delete("/api/personal-assistant", deletePersonalAssistant);
27476
- import_core51.executionAuthorityRegistry.register({
28460
+ import_core54.executionAuthorityRegistry.register({
27477
28461
  id: "project-lifecycle",
27478
28462
  matches: isProjectLifecycleTask,
27479
28463
  code: "PROJECT_TASK_EXECUTION_MANAGED",
27480
28464
  message: "Project tasks are executed by the durable Project queue runtime",
27481
28465
  onManagedStart: async (task, options2) => {
27482
28466
  if (!projectTaskComposition && options2?.agentId && options2.agentId !== task.ownerId) {
27483
- throw new import_core51.ManagedTaskStartError(
28467
+ throw new import_core54.ManagedTaskStartError(
27484
28468
  "PROJECT_TASK_RUNTIME_UNAVAILABLE",
27485
28469
  "Project task ownership transfer requires the Project task runtime.",
27486
28470
  { hint: "Project task execution is disabled; enable the Project task runtime or dispatch to the current owner." }
@@ -27493,17 +28477,21 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27493
28477
  threads: taskThreadService,
27494
28478
  reassignmentAuthorizer,
27495
28479
  lifecycle: taskLifecycle,
27496
- mutex: import_core51.projectMutationMutex
28480
+ mutex: import_core54.projectMutationMutex
27497
28481
  }) : void 0;
27498
28482
  const admitted = transferred ?? task;
27499
28483
  if (!admitted.workspaceId || !admitted.projectId) return;
27500
- void import_core51.projectTaskAdmissionNotifier.publish({
27501
- tenantId: admitted.tenantId,
27502
- workspaceId: admitted.workspaceId,
27503
- projectId: admitted.projectId,
27504
- taskId: admitted.id,
27505
- ownerId: admitted.ownerId,
27506
- 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()
27507
28495
  });
27508
28496
  }
27509
28497
  });
@@ -27511,7 +28499,7 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
27511
28499
  access: accessService,
27512
28500
  tasks: taskStore,
27513
28501
  workItems: taskWorkItemStore,
27514
- projectMutationMutex: import_core51.projectMutationMutex,
28502
+ projectMutationMutex: import_core54.projectMutationMutex,
27515
28503
  commands: taskCommandService,
27516
28504
  readableProjects: async ({ tenantId, userId, workspaceId }) => {
27517
28505
  const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
@@ -27793,9 +28781,9 @@ function tryCreateProjectTaskRuntime(input) {
27793
28781
  let stores;
27794
28782
  try {
27795
28783
  stores = {
27796
- tenants: (0, import_core51.getStoreLattice)("default", "tenant").store,
27797
- workspaces: (0, import_core51.getStoreLattice)("default", "workspace").store,
27798
- 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
27799
28787
  };
27800
28788
  const missing = missingRuntimeMethod(input, stores);
27801
28789
  if (missing) return { reason: missing };
@@ -27827,7 +28815,7 @@ function createRuntime(input, stores, log2) {
27827
28815
  const controller = new AbortController();
27828
28816
  const mutex = new ProjectTaskExecutionMutex();
27829
28817
  const threads = new ProjectTaskThreadService({ threads: input.threadStore, idFactory: import_node_crypto26.randomUUID });
27830
- 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 });
27831
28819
  const projection = new RoomProjectionService({
27832
28820
  messages: input.projectRoomMessageStore,
27833
28821
  events: input.taskWorkItemStore,
@@ -27837,7 +28825,7 @@ function createRuntime(input, stores, log2) {
27837
28825
  realtime: input.realtime,
27838
28826
  idFactory: import_node_crypto26.randomUUID
27839
28827
  });
27840
- const agentFor = (task, thread) => import_core51.agentInstanceManager.getAgent({
28828
+ const agentFor = (task, thread) => import_core54.agentInstanceManager.getAgent({
27841
28829
  tenant_id: task.tenantId,
27842
28830
  assistant_id: task.ownerId,
27843
28831
  thread_id: thread.id,
@@ -27851,7 +28839,7 @@ function createRuntime(input, stores, log2) {
27851
28839
  bots: input.projectBotMembershipStore,
27852
28840
  threads,
27853
28841
  lifecycle,
27854
- dispatcher: (0, import_core51.createTrustedAgentMessageDispatcher)((scope) => import_core51.agentInstanceManager.getAgent(scope)),
28842
+ dispatcher: (0, import_core54.createTrustedAgentMessageDispatcher)((scope) => import_core54.agentInstanceManager.getAgent(scope)),
27855
28843
  queue: stores.queue,
27856
28844
  agentFor
27857
28845
  }, {
@@ -27878,8 +28866,8 @@ function createRuntime(input, stores, log2) {
27878
28866
  const taskLifecycleListener = ((event) => {
27879
28867
  void observer.listener(event);
27880
28868
  });
27881
- const unsubscribe = import_core51.taskLifecycleNotifier.subscribe(taskLifecycleListener);
27882
- 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) => {
27883
28871
  observer.admit(event, signal);
27884
28872
  });
27885
28873
  let reconciliationWork;
@@ -28026,12 +29014,12 @@ function registerResourceRoutes(app2, controller) {
28026
29014
  }
28027
29015
 
28028
29016
  // src/export_registrations/index.ts
28029
- var import_core62 = require("@axiom-lattice/core");
29017
+ var import_core65 = require("@axiom-lattice/core");
28030
29018
 
28031
29019
  // src/export_registrations/skill.registration.ts
28032
- var import_core52 = require("@axiom-lattice/core");
29020
+ var import_core55 = require("@axiom-lattice/core");
28033
29021
  function getStore(context) {
28034
- return new import_core52.SandboxSkillStore({
29022
+ return new import_core55.SandboxSkillStore({
28035
29023
  workspaceId: context?.workspaceId,
28036
29024
  projectId: context?.projectId
28037
29025
  });
@@ -28049,7 +29037,7 @@ var skillRegistration = {
28049
29037
  async listForExport(tenantId, context) {
28050
29038
  const store2 = getStore(context);
28051
29039
  const skills = await store2.getAllSkills(tenantId, toStoreContext(context));
28052
- 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) => ({
28053
29041
  _exportId: "",
28054
29042
  data: {
28055
29043
  id: s.id,
@@ -28114,7 +29102,7 @@ var skillRegistration = {
28114
29102
  const insertions = [];
28115
29103
  for (const entity of entities) {
28116
29104
  const id3 = entity.data.id;
28117
- if ((0, import_core52.isBuiltInSkill)(id3)) continue;
29105
+ if ((0, import_core55.isBuiltInSkill)(id3)) continue;
28118
29106
  if (existingIds.has(id3)) {
28119
29107
  conflicts.push({
28120
29108
  _exportId: entity._exportId,
@@ -28173,9 +29161,9 @@ var skillRegistration = {
28173
29161
  };
28174
29162
 
28175
29163
  // src/export_registrations/agent.registration.ts
28176
- var import_core53 = require("@axiom-lattice/core");
29164
+ var import_core56 = require("@axiom-lattice/core");
28177
29165
  function getStore2() {
28178
- return (0, import_core53.getStoreLattice)("default", "assistant").store;
29166
+ return (0, import_core56.getStoreLattice)("default", "assistant").store;
28179
29167
  }
28180
29168
  var agentRegistration = {
28181
29169
  entityType: "agent",
@@ -28255,9 +29243,9 @@ var agentRegistration = {
28255
29243
  };
28256
29244
 
28257
29245
  // src/export_registrations/database-config.registration.ts
28258
- var import_core54 = require("@axiom-lattice/core");
29246
+ var import_core57 = require("@axiom-lattice/core");
28259
29247
  function getStore3() {
28260
- return (0, import_core54.getStoreLattice)("default", "database").store;
29248
+ return (0, import_core57.getStoreLattice)("default", "database").store;
28261
29249
  }
28262
29250
  var databaseConfigRegistration = {
28263
29251
  entityType: "database_config",
@@ -28337,9 +29325,9 @@ var databaseConfigRegistration = {
28337
29325
  };
28338
29326
 
28339
29327
  // src/export_registrations/metrics-config.registration.ts
28340
- var import_core55 = require("@axiom-lattice/core");
29328
+ var import_core58 = require("@axiom-lattice/core");
28341
29329
  function getStore4() {
28342
- return (0, import_core55.getStoreLattice)("default", "metrics").store;
29330
+ return (0, import_core58.getStoreLattice)("default", "metrics").store;
28343
29331
  }
28344
29332
  var metricsConfigRegistration = {
28345
29333
  entityType: "metrics_config",
@@ -28419,9 +29407,9 @@ var metricsConfigRegistration = {
28419
29407
  };
28420
29408
 
28421
29409
  // src/export_registrations/mcp-config.registration.ts
28422
- var import_core56 = require("@axiom-lattice/core");
29410
+ var import_core59 = require("@axiom-lattice/core");
28423
29411
  function getStore5() {
28424
- return (0, import_core56.getStoreLattice)("default", "mcp").store;
29412
+ return (0, import_core59.getStoreLattice)("default", "mcp").store;
28425
29413
  }
28426
29414
  var mcpConfigRegistration = {
28427
29415
  entityType: "mcp_config",
@@ -28504,9 +29492,9 @@ var mcpConfigRegistration = {
28504
29492
  };
28505
29493
 
28506
29494
  // src/export_registrations/connection.registration.ts
28507
- var import_core57 = require("@axiom-lattice/core");
29495
+ var import_core60 = require("@axiom-lattice/core");
28508
29496
  function getStore6() {
28509
- return (0, import_core57.getStoreLattice)("default", "connection").store;
29497
+ return (0, import_core60.getStoreLattice)("default", "connection").store;
28510
29498
  }
28511
29499
  async function listAllConnections(tenantId) {
28512
29500
  const store2 = getStore6();
@@ -28588,9 +29576,9 @@ var connectionRegistration = {
28588
29576
  };
28589
29577
 
28590
29578
  // src/export_registrations/collection.registration.ts
28591
- var import_core58 = require("@axiom-lattice/core");
29579
+ var import_core61 = require("@axiom-lattice/core");
28592
29580
  function getStore7() {
28593
- return (0, import_core58.getStoreLattice)("default", "collection").store;
29581
+ return (0, import_core61.getStoreLattice)("default", "collection").store;
28594
29582
  }
28595
29583
  var collectionRegistration = {
28596
29584
  entityType: "collection",
@@ -28661,7 +29649,7 @@ var collectionRegistration = {
28661
29649
  };
28662
29650
 
28663
29651
  // src/export_registrations/channel-installation.registration.ts
28664
- var import_core59 = require("@axiom-lattice/core");
29652
+ var import_core62 = require("@axiom-lattice/core");
28665
29653
  var import_crypto11 = require("crypto");
28666
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;
28667
29655
  var PUBLIC_CHANNELS = /* @__PURE__ */ new Set(["lark", "email", "slack", "wechat"]);
@@ -28695,7 +29683,7 @@ function validateImportData(data) {
28695
29683
  };
28696
29684
  }
28697
29685
  function getStore8() {
28698
- return (0, import_core59.getStoreLattice)("default", "channelInstallation").store;
29686
+ return (0, import_core62.getStoreLattice)("default", "channelInstallation").store;
28699
29687
  }
28700
29688
  function getOverwriteError(tenantId, incomingChannel, existing) {
28701
29689
  if (!existing) return "Channel installation overwrite target must exist";
@@ -28829,9 +29817,9 @@ var channelInstallationRegistration = {
28829
29817
  };
28830
29818
 
28831
29819
  // src/export_registrations/menu.registration.ts
28832
- var import_core60 = require("@axiom-lattice/core");
29820
+ var import_core63 = require("@axiom-lattice/core");
28833
29821
  function getStore9() {
28834
- return (0, import_core60.getStoreLattice)("default", "menu").store;
29822
+ return (0, import_core63.getStoreLattice)("default", "menu").store;
28835
29823
  }
28836
29824
  var menuRegistration = {
28837
29825
  entityType: "menu",
@@ -28909,9 +29897,9 @@ var menuRegistration = {
28909
29897
  };
28910
29898
 
28911
29899
  // src/export_registrations/eval.registration.ts
28912
- var import_core61 = require("@axiom-lattice/core");
29900
+ var import_core64 = require("@axiom-lattice/core");
28913
29901
  function getStore10() {
28914
- return (0, import_core61.getStoreLattice)("default", "eval").store;
29902
+ return (0, import_core64.getStoreLattice)("default", "eval").store;
28915
29903
  }
28916
29904
  var evalRegistration = {
28917
29905
  entityType: "eval",
@@ -29237,7 +30225,7 @@ var evalRegistration = {
29237
30225
 
29238
30226
  // src/export_registrations/index.ts
29239
30227
  function registerAllBuiltinEntities() {
29240
- const registry = import_core62.ExportableEntityRegistry.getInstance();
30228
+ const registry = import_core65.ExportableEntityRegistry.getInstance();
29241
30229
  registry.register(skillRegistration);
29242
30230
  registry.register(agentRegistration);
29243
30231
  registry.register(databaseConfigRegistration);
@@ -29368,7 +30356,7 @@ function createAuditLoggerMiddleware() {
29368
30356
  }
29369
30357
 
29370
30358
  // src/index.ts
29371
- var import_core81 = require("@axiom-lattice/core");
30359
+ var import_core91 = require("@axiom-lattice/core");
29372
30360
 
29373
30361
  // src/swagger.ts
29374
30362
  var import_swagger = __toESM(require("@fastify/swagger"));
@@ -29434,7 +30422,8 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
29434
30422
 
29435
30423
  // src/services/agent_task_consumer.ts
29436
30424
  var import_node_crypto27 = require("crypto");
29437
- var import_core63 = require("@axiom-lattice/core");
30425
+ var import_core66 = require("@axiom-lattice/core");
30426
+ var import_protocols31 = require("@axiom-lattice/protocols");
29438
30427
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
29439
30428
  const {
29440
30429
  assistant_id,
@@ -29452,7 +30441,8 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
29452
30441
  console.log(
29453
30442
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
29454
30443
  );
29455
- const agent = import_core63.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
30444
+ const scope = (0, import_protocols31.resolveRunScope)(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 });
29456
30446
  if (callback_event) {
29457
30447
  const callbackEvent = callback_event;
29458
30448
  const messageId = typeof input?.id === "string" && input.id !== "" ? input.id : (0, import_node_crypto27.randomUUID)();
@@ -29473,18 +30463,18 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
29473
30463
  const onCompleted = (evt) => {
29474
30464
  if (!belongsToMessage(evt)) return;
29475
30465
  settle(() => {
29476
- import_core63.eventBus.publish(callbackEvent, {
30466
+ import_core66.eventBus.publish(callbackEvent, {
29477
30467
  success: true,
29478
30468
  state: evt.state
29479
30469
  });
29480
30470
  if (main_thread_id && main_tenant_id) {
29481
30471
  try {
29482
- const mainAgent = import_core63.agentInstanceManager.getAgent({
30472
+ const mainAgent = import_core66.agentInstanceManager.getAgent({
29483
30473
  assistant_id: main_assistant_id ?? assistant_id,
29484
30474
  thread_id: main_thread_id,
29485
30475
  tenant_id: main_tenant_id,
29486
- workspace_id: runConfig?.workspaceId,
29487
- project_id: runConfig?.projectId
30476
+ workspace_id: scope.workspaceId,
30477
+ project_id: scope.projectId
29488
30478
  });
29489
30479
  if (mainAgent) {
29490
30480
  const messages = evt.state?.values?.messages;
@@ -29512,7 +30502,7 @@ ${summary}`
29512
30502
  const onInterrupted = (evt) => {
29513
30503
  if (!belongsToMessage(evt)) return;
29514
30504
  settle(() => {
29515
- import_core63.eventBus.publish(callbackEvent, {
30505
+ import_core66.eventBus.publish(callbackEvent, {
29516
30506
  success: true,
29517
30507
  state: evt.state
29518
30508
  });
@@ -29521,7 +30511,7 @@ ${summary}`
29521
30511
  const onFailed = (evt) => {
29522
30512
  if (!belongsToMessage(evt)) return;
29523
30513
  settle(() => {
29524
- import_core63.eventBus.publish(callbackEvent, {
30514
+ import_core66.eventBus.publish(callbackEvent, {
29525
30515
  success: false,
29526
30516
  error: evt?.error instanceof Error ? evt.error.message : typeof evt?.error === "string" ? evt.error : "Agent execution failed"
29527
30517
  });
@@ -29536,13 +30526,13 @@ ${summary}`
29536
30526
  agent.subscribe("message:interrupted", onInterrupted);
29537
30527
  agent.subscribe("message:failed", onFailed);
29538
30528
  try {
29539
- 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);
29540
30530
  } catch (error) {
29541
30531
  dispose();
29542
30532
  throw error;
29543
30533
  }
29544
30534
  } else {
29545
- 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);
29546
30536
  }
29547
30537
  return true;
29548
30538
  } catch (error) {
@@ -29561,7 +30551,7 @@ ${summary}`
29561
30551
  return handleAgentTask(taskRequest, nextRetryCount);
29562
30552
  }
29563
30553
  if (callback_event) {
29564
- import_core63.eventBus.publish(callback_event, {
30554
+ import_core66.eventBus.publish(callback_event, {
29565
30555
  success: false,
29566
30556
  error: error instanceof Error ? error.message : String(error)
29567
30557
  });
@@ -29598,7 +30588,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
29598
30588
  * 初始化事件监听和队列轮询
29599
30589
  */
29600
30590
  initialize() {
29601
- 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));
29602
30592
  this.startPollingQueue();
29603
30593
  console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
29604
30594
  }
@@ -29717,7 +30707,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
29717
30707
  handleAgentTask(taskRequest).catch((error) => {
29718
30708
  console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
29719
30709
  if (taskRequest.callback_event) {
29720
- import_core63.eventBus.publish(taskRequest.callback_event, {
30710
+ import_core66.eventBus.publish(taskRequest.callback_event, {
29721
30711
  success: false,
29722
30712
  error: error instanceof Error ? error.message : String(error)
29723
30713
  });
@@ -29732,14 +30722,14 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
29732
30722
  var AgentTaskConsumer = _AgentTaskConsumer;
29733
30723
 
29734
30724
  // src/index.ts
29735
- var import_core82 = require("@axiom-lattice/core");
30725
+ var import_core92 = require("@axiom-lattice/core");
29736
30726
 
29737
30727
  // src/channels/ChannelInfrastructure.ts
29738
- var import_core65 = require("@axiom-lattice/core");
30728
+ var import_core68 = require("@axiom-lattice/core");
29739
30729
 
29740
30730
  // src/router/MessageRouter.ts
29741
- var import_core64 = require("@axiom-lattice/core");
29742
- var import_protocols31 = require("@axiom-lattice/protocols");
30731
+ var import_core67 = require("@axiom-lattice/core");
30732
+ var import_protocols32 = require("@axiom-lattice/protocols");
29743
30733
  var import_crypto12 = require("crypto");
29744
30734
  var BindingNotFoundError = class extends Error {
29745
30735
  constructor(message) {
@@ -29774,10 +30764,10 @@ function clarifyText(clarify) {
29774
30764
  return ["```clarify", JSON.stringify({ questions: clarify.questions }), "```"].join("\n");
29775
30765
  }
29776
30766
  function exactDataValues(value, required, optional = []) {
29777
- return (0, import_protocols31.snapshotExactRecord)(value, required, optional);
30767
+ return (0, import_protocols32.snapshotExactRecord)(value, required, optional);
29778
30768
  }
29779
30769
  function uniqueNonemptyStrings(value) {
29780
- const snapshot = (0, import_protocols31.snapshotExactArray)(value);
30770
+ const snapshot = (0, import_protocols32.snapshotExactArray)(value);
29781
30771
  if (!snapshot || snapshot.length === 0 || snapshot.some((entry) => typeof entry !== "string" || entry.length === 0)) return void 0;
29782
30772
  const values = snapshot;
29783
30773
  return new Set(values).size === values.length ? values : void 0;
@@ -30024,7 +31014,7 @@ var MessageRouter = class {
30024
31014
  channel: message.channel,
30025
31015
  adapterChannel: adapter.channel
30026
31016
  }, "Thread resolved by adapter strategy");
30027
- const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
31017
+ const threadStore = (0, import_core67.getStoreLattice)("default", "thread").store;
30028
31018
  try {
30029
31019
  await threadStore.createThread(
30030
31020
  tenantId,
@@ -30061,7 +31051,7 @@ var MessageRouter = class {
30061
31051
  }
30062
31052
  }
30063
31053
  if (!threadId) {
30064
- const threadStore = (0, import_core64.getStoreLattice)("default", "thread").store;
31054
+ const threadStore = (0, import_core67.getStoreLattice)("default", "thread").store;
30065
31055
  const newThreadId = (0, import_crypto12.randomUUID)();
30066
31056
  console.log({
30067
31057
  event: "dispatch:thread:create",
@@ -30101,7 +31091,7 @@ var MessageRouter = class {
30101
31091
  senderId: message.sender.id,
30102
31092
  contentLength: message.content.text.length
30103
31093
  }, "Dispatching to agent");
30104
- const agent = import_core64.agentInstanceManager.getAgent({
31094
+ const agent = import_core67.agentInstanceManager.getAgent({
30105
31095
  tenant_id: tenantId,
30106
31096
  assistant_id: agentId,
30107
31097
  thread_id: threadId,
@@ -30289,7 +31279,7 @@ var MessageRouter = class {
30289
31279
 
30290
31280
  // src/channels/room/RoomChannelAdapter.ts
30291
31281
  var import_zod17 = require("zod");
30292
- var import_protocols32 = require("@axiom-lattice/protocols");
31282
+ var import_protocols33 = require("@axiom-lattice/protocols");
30293
31283
  var roomConfigSchema = import_zod17.z.object({}).strict();
30294
31284
  function createRoomChannelAdapter(writer) {
30295
31285
  return {
@@ -30350,7 +31340,7 @@ function exactStrings(value, keys2) {
30350
31340
  function exactValues2(value, required, optional = []) {
30351
31341
  try {
30352
31342
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
30353
- return (0, import_protocols32.snapshotExactRecord)(value, required, optional);
31343
+ return (0, import_protocols33.snapshotExactRecord)(value, required, optional);
30354
31344
  } catch {
30355
31345
  return void 0;
30356
31346
  }
@@ -30374,7 +31364,7 @@ function createChannelInfrastructure(deps) {
30374
31364
  onMessageCommitted: deps.onMessageCommitted ?? (deps.realtime ? (message) => deps.realtime?.messageCreated(message) : void 0),
30375
31365
  onCallbackFailure: (event) => deps.warn("project-room:message-commit-callback-failed", event)
30376
31366
  };
30377
- deps.adapterRegistry.register(createRoomChannelAdapter(new import_core65.RoomAgentMessageService(writerDeps)));
31367
+ deps.adapterRegistry.register(createRoomChannelAdapter(new import_core68.RoomAgentMessageService(writerDeps)));
30378
31368
  } catch (error) {
30379
31369
  deps.warn("Room channel unavailable", { error: error instanceof Error ? error.message : String(error) });
30380
31370
  }
@@ -30394,26 +31384,186 @@ async function prepareProjectTaskRuntime(runtime, log2) {
30394
31384
  }
30395
31385
  }
30396
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
+
30397
31543
  // src/index.ts
30398
- var import_protocols39 = require("@axiom-lattice/protocols");
31544
+ var import_protocols40 = require("@axiom-lattice/protocols");
31545
+ init_TopCallerRecovery();
31546
+ init_OrphanRecoveryRuntime();
31547
+ init_RecoveryCallerDeps();
31548
+ init_taskRunLiveness();
30399
31549
  process.on("unhandledRejection", (reason, promise) => {
30400
31550
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
30401
31551
  });
30402
31552
  var DEFAULT_LOGGER_CONFIG = {
30403
31553
  name: "default",
30404
31554
  description: "Default logger for lattice-gateway service",
30405
- type: import_protocols39.LoggerType.PINO,
31555
+ type: import_protocols40.LoggerType.PINO,
30406
31556
  serviceName: "lattice/gateway",
30407
31557
  loggerName: "lattice/gateway"
30408
31558
  };
30409
31559
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
30410
31560
  var logger7 = loggerLattice.client;
30411
31561
  function initializeLogger(config) {
30412
- if (import_core82.loggerLatticeManager.hasLattice("default")) {
30413
- import_core82.loggerLatticeManager.removeLattice("default");
31562
+ if (import_core92.loggerLatticeManager.hasLattice("default")) {
31563
+ import_core92.loggerLatticeManager.removeLattice("default");
30414
31564
  }
30415
- (0, import_core82.registerLoggerLattice)("default", config);
30416
- return (0, import_core82.getLoggerLattice)("default");
31565
+ (0, import_core92.registerLoggerLattice)("default", config);
31566
+ return (0, import_core92.getLoggerLattice)("default");
30417
31567
  }
30418
31568
  var app = (0, import_fastify.default)({
30419
31569
  logger: false,
@@ -30474,7 +31624,7 @@ app.register(import_cors.default, {
30474
31624
  origin: true,
30475
31625
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
30476
31626
  allowedHeaders: "*",
30477
- exposedHeaders: ["Content-Type", "Content-Disposition"],
31627
+ exposedHeaders: ["Content-Type", "Content-Disposition", import_protocols40.REPLICA_HEADER, import_protocols40.AFFINITY_ECHO_HEADER],
30478
31628
  credentials: true
30479
31629
  });
30480
31630
  app.register(import_sensible.default);
@@ -30510,7 +31660,7 @@ app.setErrorHandler((error, request, reply) => {
30510
31660
  });
30511
31661
  function getConfiguredSandboxProvider() {
30512
31662
  const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
30513
- return (0, import_core82.createSandboxProvider)({
31663
+ return (0, import_core92.createSandboxProvider)({
30514
31664
  type: sandboxProviderType,
30515
31665
  remoteBaseURL: process.env.SANDBOX_BASE_URL,
30516
31666
  microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
@@ -30526,7 +31676,7 @@ function getConfiguredSandboxProvider() {
30526
31676
  }
30527
31677
  async function restoreMcpConnections() {
30528
31678
  try {
30529
- const storeLattice = (0, import_core82.getStoreLattice)("default", "mcp");
31679
+ const storeLattice = (0, import_core92.getStoreLattice)("default", "mcp");
30530
31680
  const store2 = storeLattice.store;
30531
31681
  if (!store2) {
30532
31682
  logger7.info("MCP store not configured, skipping connection restoration");
@@ -30537,25 +31687,16 @@ async function restoreMcpConnections() {
30537
31687
  );
30538
31688
  if (configs.length === 0) return;
30539
31689
  logger7.info(`Restoring ${configs.length} MCP server connections...`);
30540
- for (const config of configs) {
30541
- try {
30542
- const connection = (0, import_core82.convertToConnection)(config.config);
30543
- import_core82.mcpManager.registerServer(config.tenantId, config.key, connection, {
30544
- timeoutMs: config.config.timeout,
30545
- maxAttempts: config.config.retryAttempts,
30546
- selectedTools: config.selectedTools
30547
- });
30548
- } catch (err) {
30549
- logger7.warn(`Failed to prepare MCP server "${config.tenantId}/${config.key}"`, { error: err });
30550
- }
30551
- }
30552
- await import_core82.mcpManager.connectAll();
31690
+ const deps = {
31691
+ store: store2,
31692
+ manager: import_core92.mcpManager,
31693
+ convertToConnection: import_core92.convertToConnection
31694
+ };
30553
31695
  const results = await Promise.allSettled(
30554
31696
  configs.map(
30555
- (config) => import_core82.mcpManager.registerToolsToToolLattice(
30556
- config.tenantId,
30557
- config.key,
30558
- config.selectedTools
31697
+ (config) => reconcileMcpConfigChange(
31698
+ { tenantId: config.tenantId, serverKey: config.key },
31699
+ deps
30559
31700
  )
30560
31701
  )
30561
31702
  );
@@ -30563,7 +31704,7 @@ async function restoreMcpConnections() {
30563
31704
  if (result.status === "rejected") {
30564
31705
  const config = configs[index];
30565
31706
  logger7.warn(
30566
- `Failed to register tools for MCP server "${config.tenantId}/${config.key}"`,
31707
+ `Failed to restore MCP server "${config.tenantId}/${config.key}"`,
30567
31708
  { error: result.reason }
30568
31709
  );
30569
31710
  }
@@ -30590,9 +31731,9 @@ var start = async (config) => {
30590
31731
  let projectRoomRealtimeRuntime;
30591
31732
  if (projectRoomRealtimeMode === "single-gateway") {
30592
31733
  projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
30593
- tasks: (0, import_core82.getStoreLattice)("default", "task").store,
30594
- rooms: (0, import_core82.getStoreLattice)("default", "projectRoom").store,
30595
- memberships: (0, import_core82.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,
30596
31737
  log: { warn: (message, context) => logger7.warn(message, context) }
30597
31738
  });
30598
31739
  }
@@ -30606,7 +31747,7 @@ var start = async (config) => {
30606
31747
  const infrastructure = createChannelInfrastructure({
30607
31748
  adapterRegistry,
30608
31749
  getStore: ((kind) => getStore11("default", kind).store),
30609
- trustedDispatcher: (0, import_core82.createTrustedAgentMessageDispatcher)((scope) => import_core82.agentInstanceManager.getAgent(scope)),
31750
+ trustedDispatcher: (0, import_core92.createTrustedAgentMessageDispatcher)((scope) => import_core92.agentInstanceManager.getAgent(scope)),
30610
31751
  middlewares: [
30611
31752
  createDeduplicationMiddleware(),
30612
31753
  createRateLimitMiddleware(),
@@ -30617,7 +31758,7 @@ var start = async (config) => {
30617
31758
  });
30618
31759
  const bindingStore = infrastructure.bindingRegistry;
30619
31760
  const installationStore = infrastructure.installationStore;
30620
- (0, import_core81.setBindingRegistry)(bindingStore);
31761
+ (0, import_core91.setBindingRegistry)(bindingStore);
30621
31762
  const router = infrastructure.router;
30622
31763
  app.addHook("onClose", async () => router.dispose());
30623
31764
  channelDeps = {
@@ -30633,10 +31774,10 @@ var start = async (config) => {
30633
31774
  error: err instanceof Error ? err.message : String(err)
30634
31775
  });
30635
31776
  }
30636
- (0, import_core81.setEvalRunService)(evalRunner);
31777
+ (0, import_core91.setEvalRunService)(evalRunner);
30637
31778
  try {
30638
- const menuStore = (0, import_core82.getStoreLattice)("default", "menu").store;
30639
- (0, import_core81.setMenuRegistry)(menuStore);
31779
+ const menuStore = (0, import_core92.getStoreLattice)("default", "menu").store;
31780
+ (0, import_core91.setMenuRegistry)(menuStore);
30640
31781
  logger7.info("Menu registry initialized");
30641
31782
  } catch {
30642
31783
  }
@@ -30653,10 +31794,10 @@ var start = async (config) => {
30653
31794
  const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await Promise.resolve().then(() => (init_A2AAuthService(), A2AAuthService_exports));
30654
31795
  const { registerA2AStandardRoutes: registerA2AStandardRoutes2 } = await Promise.resolve().then(() => (init_a2a_standard(), a2a_standard_exports));
30655
31796
  const a2a = await Promise.resolve().then(() => (init_a2a(), a2a_exports));
30656
- const a2aKeyStore = (0, import_core82.getStoreLattice)("default", "a2aApiKey").store;
30657
- const assistantStore = (0, import_core82.getStoreLattice)("default", "assistant").store;
30658
- const projectStore = (0, import_core82.getStoreLattice)("default", "project").store;
30659
- const taskStore = (0, import_core82.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;
30660
31801
  const authService = new A2AAuthService2({
30661
31802
  keyStore: a2aKeyStore,
30662
31803
  assistantStore,
@@ -30695,8 +31836,8 @@ var start = async (config) => {
30695
31836
  const { CollectionService: CollectionService2 } = await Promise.resolve().then(() => (init_collection_service(), collection_service_exports));
30696
31837
  openLattice2.register(createKbProvider2(new CollectionService2()));
30697
31838
  openLattice2.register(createOpenSandboxProvider2());
30698
- const threadStore = (0, import_core82.getStoreLattice)("default", "thread").store;
30699
- const assistantStore = (0, import_core82.getStoreLattice)("default", "assistant").store;
31839
+ const threadStore = (0, import_core92.getStoreLattice)("default", "thread").store;
31840
+ const assistantStore = (0, import_core92.getStoreLattice)("default", "assistant").store;
30700
31841
  openLattice2.register(
30701
31842
  createAgentProvider2({
30702
31843
  threadStore,
@@ -30719,18 +31860,29 @@ var start = async (config) => {
30719
31860
  error: err instanceof Error ? err.message : String(err)
30720
31861
  });
30721
31862
  }
30722
- if (!import_core82.sandboxLatticeManager.hasLattice("default")) {
30723
- import_core82.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());
30724
31876
  logger7.info("Registered sandbox manager from env configuration");
30725
31877
  }
30726
31878
  try {
30727
31879
  const { ResourceController: ResourceController2 } = await Promise.resolve().then(() => (init_resources(), resources_exports));
30728
- const sharedResourceStore = (0, import_core82.getStoreLattice)("default", "sharedResource").store;
30729
- const cache = new import_core82.TokenCache();
31880
+ const sharedResourceStore = (0, import_core92.getStoreLattice)("default", "sharedResource").store;
31881
+ const cache2 = new import_core92.TokenCache();
30730
31882
  const resourceController = new ResourceController2({
30731
31883
  store: sharedResourceStore,
30732
- sandboxManager: import_core82.sandboxLatticeManager,
30733
- cache,
31884
+ sandboxManager: import_core92.sandboxLatticeManager,
31885
+ cache: cache2,
30734
31886
  logger: logger7
30735
31887
  });
30736
31888
  registerResourceRoutes(app, resourceController);
@@ -30754,11 +31906,98 @@ var start = async (config) => {
30754
31906
  }
30755
31907
  }
30756
31908
  const target_port = config?.port || Number(process.env.PORT) || 4001;
30757
- if (routes.projectTaskRuntime) {
30758
- 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
+ });
31923
+ }
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);
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" });
30759
31993
  }
30760
31994
  await app.listen({ port: target_port, host: "0.0.0.0" });
30761
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();
30762
32001
  const queueServiceConfig = config?.queueServiceConfig;
30763
32002
  if (queueServiceConfig) {
30764
32003
  setQueueServiceType(queueServiceConfig.type);
@@ -30767,13 +32006,6 @@ var start = async (config) => {
30767
32006
  agentTaskConsumer.startPollingQueue();
30768
32007
  }
30769
32008
  }
30770
- if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
30771
- import_core82.agentInstanceManager.restore().then((stats) => {
30772
- logger7.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
30773
- }).catch((error) => {
30774
- logger7.error("Agent recovery failed", { error });
30775
- });
30776
- }
30777
32009
  restoreMcpConnections().catch((error) => {
30778
32010
  logger7.error("MCP connection restoration failed", { error });
30779
32011
  });
@@ -30791,6 +32023,17 @@ var LatticeGateway = {
30791
32023
  };
30792
32024
  // Annotate the CommonJS export names for ESM import in node:
30793
32025
  0 && (module.exports = {
30794
- LatticeGateway
32026
+ LatticeGateway,
32027
+ ProjectRoomEventBroker,
32028
+ ROOM_EVENT_TOPIC,
32029
+ createOrphanRecoveryRuntime,
32030
+ createRecoveryCallerDeps,
32031
+ createTaskRunLiveness,
32032
+ ingestRoomEvent,
32033
+ publishRoomEvent,
32034
+ recoverTask,
32035
+ recoverTopCaller,
32036
+ resolveTopCaller,
32037
+ runOrphanRecovery
30795
32038
  });
30796
32039
  //# sourceMappingURL=index.js.map