@axiom-lattice/gateway 7.0.0 → 8.0.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 (92) hide show
  1. package/dist/{WechatChannelAdapter-7WHE5UGY.mjs → WechatChannelAdapter-QLPJJD6S.mjs} +55 -4
  2. package/dist/WechatChannelAdapter-QLPJJD6S.mjs.map +1 -0
  3. package/dist/{a2a-standard-DP53K7RC.mjs → a2a-standard-VL5I7B4W.mjs} +831 -59
  4. package/dist/a2a-standard-VL5I7B4W.mjs.map +1 -0
  5. package/dist/{chunk-FGSFOXIX.mjs → chunk-L6MBEEII.mjs} +2 -2
  6. package/dist/{chunk-J6B2VFJ7.mjs → chunk-W3YH4S6G.mjs} +2 -1
  7. package/dist/{chunk-MWWLJMYN.mjs → chunk-XUWD4VKY.mjs} +4 -2
  8. package/dist/chunk-XUWD4VKY.mjs.map +1 -0
  9. package/dist/embed/embed-page-AgentDetailSideApp-NK6VQBXS.js +1 -0
  10. package/dist/embed/embed-page-AgentGovernanceWorkbench-TKVV2GA6.js +1 -0
  11. package/dist/embed/embed-page-LangfuseDashboard-EYK2T2VK.js +1 -0
  12. package/dist/embed/embed-page-MessageGraph-P73B52OR.js +6 -0
  13. package/dist/embed/embed-page-ReactChart-LK6DUA4R.js +1 -0
  14. package/dist/embed/embed-page-SkillFlow-54RIT6M2.js +1 -0
  15. package/dist/embed/{embed-page-TasksSideApp-D7FFG2OC.js → embed-page-TasksSideApp-RK64OPAA.js} +1 -1
  16. package/dist/embed/embed-page-ThreadMaintenancePanel-PI3U76W3.js +1 -0
  17. package/dist/embed/{embed-page-TopologyRuntimeView-IS6HEO4F.js → embed-page-TopologyRuntimeView-X6QPRQE2.js} +1 -1
  18. package/dist/embed/{embed-page-WorkflowAutomationView-IDVSQTJB.js → embed-page-WorkflowAutomationView-3PE6YCRK.js} +1 -1
  19. package/dist/embed/{embed-page-chunk-NFMLKSPM.js → embed-page-chunk-3VNL5M2Q.js} +1 -1
  20. package/dist/embed/embed-page-chunk-767LVAGR.js +7 -0
  21. package/dist/embed/embed-page-chunk-A5GIM22E.js +117 -0
  22. package/dist/embed/{embed-page-chunk-SRMYHWL3.js → embed-page-chunk-AICFKU43.js} +1 -1
  23. package/dist/embed/{embed-page-chunk-XFV3N2LD.js → embed-page-chunk-BD7DUF2M.js} +1 -1
  24. package/dist/embed/embed-page-chunk-D7YTSCUO.js +1 -0
  25. package/dist/embed/{embed-page-chunk-N66H4DNH.js → embed-page-chunk-EB43T3BJ.js} +283 -194
  26. package/dist/embed/{embed-page-chunk-JFMBLJXY.js → embed-page-chunk-GYO2ELMI.js} +1 -1
  27. package/dist/embed/{embed-page-chunk-EYYKNTX7.js → embed-page-chunk-KF3HZ4TX.js} +1 -1
  28. package/dist/embed/embed-page-chunk-MEY3NEJZ.js +211 -0
  29. package/dist/embed/{embed-page-chunk-TVVAPR7I.js → embed-page-chunk-OIML5DX4.js} +1 -1
  30. package/dist/embed/{embed-page-chunk-FEMHLB6I.js → embed-page-chunk-OKZPPZMF.js} +1 -1
  31. package/dist/embed/embed-page-chunk-T2HKN7ZS.js +459 -0
  32. package/dist/embed/{embed-page-chunk-S2KVN3RB.js → embed-page-chunk-TSZQWEXD.js} +1 -1
  33. package/dist/embed/embed-page-chunk-US46Q3B7.js +15 -0
  34. package/dist/embed/{embed-page-chunk-BOEX26H7.js → embed-page-chunk-UV6IYDW5.js} +8 -8
  35. package/dist/embed/embed-page-chunk-WXMPVIXV.js +59 -0
  36. package/dist/embed/embed-page-chunk-YWJQVBZV.js +1 -0
  37. package/dist/embed/embed-page-code-highlighter-DD23LJY3.js +1 -0
  38. package/dist/embed/{embed-page-en-ETUCVCSC.js → embed-page-en-E35GE7TA.js} +1 -1
  39. package/dist/embed/embed-page-en-EKHHBS6L.js +1 -0
  40. package/dist/embed/embed-page-en-KAXIJJ4X.js +1 -0
  41. package/dist/embed/embed-page-en-OIICQSPX.js +3 -0
  42. package/dist/embed/embed-page-en-PQOH5CU4.js +1 -0
  43. package/dist/embed/embed-page-en-TMGUHZ6Y.js +1 -0
  44. package/dist/embed/embed-page-en-XJYWUJEL.js +1 -0
  45. package/dist/embed/{embed-page-en-2SWQHPAC.js → embed-page-en-ZCNNAMUD.js} +1 -1
  46. package/dist/embed/{embed-page-mermaid-KEBBKHQH.js → embed-page-mermaid-IN3A3YH2.js} +1 -1
  47. package/dist/embed/embed-page-zh-2UVQB4RP.js +3 -0
  48. package/dist/embed/embed-page-zh-6EBUEFKP.js +1 -0
  49. package/dist/embed/embed-page-zh-B3WBWQAV.js +1 -0
  50. package/dist/embed/embed-page-zh-KEMYJJLT.js +1 -0
  51. package/dist/embed/embed-page-zh-XO36BV2R.js +1 -0
  52. package/dist/embed/{embed-page-zh-PRMID3YQ.js → embed-page-zh-Z3URTIHW.js} +1 -1
  53. package/dist/embed/{embed-page-zh-D3IC6BGA.js → embed-page-zh-ZLK3GLM3.js} +1 -1
  54. package/dist/embed/embed-page.js +1 -1
  55. package/dist/index.d.mts +118 -3
  56. package/dist/index.d.ts +118 -3
  57. package/dist/index.js +3862 -1525
  58. package/dist/index.js.map +1 -1
  59. package/dist/index.mjs +2105 -598
  60. package/dist/index.mjs.map +1 -1
  61. package/dist/{open-NTH7AGD3.mjs → open-7ED27I5B.mjs} +3 -3
  62. package/dist/{open-exposure-AGNG4MC3.mjs → open-exposure-I3DN2X4E.mjs} +6 -4
  63. package/dist/{open-exposure-AGNG4MC3.mjs.map → open-exposure-I3DN2X4E.mjs.map} +1 -1
  64. package/package.json +13 -7
  65. package/dist/WechatChannelAdapter-7WHE5UGY.mjs.map +0 -1
  66. package/dist/a2a-standard-DP53K7RC.mjs.map +0 -1
  67. package/dist/chunk-MWWLJMYN.mjs.map +0 -1
  68. package/dist/embed/embed-page-AgentDetailSideApp-KNVDO3FZ.js +0 -1
  69. package/dist/embed/embed-page-AgentGovernanceWorkbench-QSKWVADP.js +0 -1
  70. package/dist/embed/embed-page-MessageGraph-VGXX4OWU.js +0 -6
  71. package/dist/embed/embed-page-ReactChart-PIISVBLU.js +0 -1
  72. package/dist/embed/embed-page-SkillFlow-JLI5MEZE.js +0 -1
  73. package/dist/embed/embed-page-ThreadMaintenancePanel-FSQVVIIG.js +0 -1
  74. package/dist/embed/embed-page-chunk-37LGEEJU.js +0 -21
  75. package/dist/embed/embed-page-chunk-436REIWY.js +0 -1
  76. package/dist/embed/embed-page-chunk-4W34QOXT.js +0 -517
  77. package/dist/embed/embed-page-chunk-BANVRQT7.js +0 -322
  78. package/dist/embed/embed-page-code-highlighter-66CSPTFK.js +0 -1
  79. package/dist/embed/embed-page-en-7IHJFS63.js +0 -1
  80. package/dist/embed/embed-page-en-7NF4GCC2.js +0 -1
  81. package/dist/embed/embed-page-en-FP6DWSG3.js +0 -1
  82. package/dist/embed/embed-page-en-GHTROC4H.js +0 -1
  83. package/dist/embed/embed-page-en-WUFULCUS.js +0 -1
  84. package/dist/embed/embed-page-en-XUMHP5CL.js +0 -1
  85. package/dist/embed/embed-page-zh-G75LDQU4.js +0 -1
  86. package/dist/embed/embed-page-zh-KJXKOUIY.js +0 -1
  87. package/dist/embed/embed-page-zh-RMSS6PXD.js +0 -1
  88. package/dist/embed/embed-page-zh-VH6J7XZO.js +0 -1
  89. package/dist/embed/embed-page-zh-Y5X5Y2DF.js +0 -1
  90. /package/dist/{chunk-FGSFOXIX.mjs.map → chunk-L6MBEEII.mjs.map} +0 -0
  91. /package/dist/{chunk-J6B2VFJ7.mjs.map → chunk-W3YH4S6G.mjs.map} +0 -0
  92. /package/dist/{open-NTH7AGD3.mjs.map → open-7ED27I5B.mjs.map} +0 -0
package/dist/index.mjs CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  import {
31
31
  PluginToolScopeError,
32
32
  invokePluginTool
33
- } from "./chunk-MWWLJMYN.mjs";
33
+ } from "./chunk-XUWD4VKY.mjs";
34
34
  import {
35
35
  buildPreviewTokenScript
36
36
  } from "./chunk-7CG57LEF.mjs";
@@ -2794,7 +2794,38 @@ async function threadOwnedBy(tenantId, assistantId, threadId) {
2794
2794
  try {
2795
2795
  const ledger = getStoreLattice5("default", "agentRun").store;
2796
2796
  const runs = await ledger.listRuns(tenantId, threadId, { limit: 1 });
2797
- return runs.some((run2) => run2.assistantId === assistantId);
2797
+ if (runs.some((run2) => run2.assistantId === assistantId)) return true;
2798
+ } catch {
2799
+ }
2800
+ try {
2801
+ const tasks = getStoreLattice5("default", "task").store;
2802
+ const matches = await tasks.list({ tenantId, threadId, limit: 1 });
2803
+ return matches.some((task) => task.ownerType === "agent" && task.ownerId === assistantId);
2804
+ } catch {
2805
+ return false;
2806
+ }
2807
+ }
2808
+ async function threadIsNewForTenant(tenantId, threadId) {
2809
+ try {
2810
+ const existing = await getStoreLattice5("default", "thread").store.getThreadById(
2811
+ tenantId,
2812
+ threadId
2813
+ );
2814
+ if (existing) return false;
2815
+ } catch {
2816
+ return false;
2817
+ }
2818
+ try {
2819
+ const ledger = getStoreLattice5("default", "agentRun").store;
2820
+ const runs = await ledger.listRuns(tenantId, threadId, { limit: 1 });
2821
+ if (runs.length > 0) return false;
2822
+ } catch {
2823
+ return false;
2824
+ }
2825
+ try {
2826
+ const tasks = getStoreLattice5("default", "task").store;
2827
+ const matches = await tasks.list({ tenantId, threadId, limit: 1 });
2828
+ return matches.length === 0;
2798
2829
  } catch {
2799
2830
  return false;
2800
2831
  }
@@ -2987,6 +3018,12 @@ var RUN_CREATE_UNAVAILABLE2 = "RUN_CREATE_UNAVAILABLE";
2987
3018
  var RUN_CREATE_LOCK_TIMEOUT = "RUN_CREATE_LOCK_TIMEOUT";
2988
3019
  var DEFAULT_WAIT_TIMEOUT_MS = 3e4;
2989
3020
  var DEFAULT_POLL_INTERVAL_MS = 200;
3021
+ function getUserId3(request) {
3022
+ const userId = request.user?.id;
3023
+ if (userId) return userId;
3024
+ const header2 = request.headers["x-user-id"];
3025
+ return typeof header2 === "string" && header2.length > 0 ? header2 : void 0;
3026
+ }
2990
3027
  var RUN_CREATE_LEASE = {
2991
3028
  ttlMs: 12e4,
2992
3029
  acquireTimeoutMs: 3e4,
@@ -3161,6 +3198,7 @@ function createRunLifecycleHandlers(deps = {}) {
3161
3198
  const resolveRouter = deps.getRunCancelRouter ?? getRunCancelRouter;
3162
3199
  const resolveLeaseStore = deps.getCoordinationLeaseStore ?? getConfiguredCoordinationLeaseStore;
3163
3200
  const ownsThread = deps.threadOwnedBy ?? threadOwnedBy;
3201
+ const isNewThread = deps.threadIsNew ?? threadIsNewForTenant;
3164
3202
  const wake = deps.wakeRunTaskQueue ?? wakeRunTaskQueue;
3165
3203
  const idFactory = deps.idFactory ?? (() => v42());
3166
3204
  const waitTimeoutMs = deps.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
@@ -3243,7 +3281,9 @@ function createRunLifecycleHandlers(deps = {}) {
3243
3281
  const tenantId = getTenantId4(request);
3244
3282
  const { assistantId, threadId } = request.params;
3245
3283
  if (!await ownsThread(tenantId, assistantId, threadId)) {
3246
- return notFound2(reply, THREAD_NOT_FOUND2, "Thread not found");
3284
+ if (!await isNewThread(tenantId, threadId)) {
3285
+ return notFound2(reply, THREAD_NOT_FOUND2, "Thread not found");
3286
+ }
3247
3287
  }
3248
3288
  const body = isRecord(request.body) ? request.body : {};
3249
3289
  const mode = body.mode;
@@ -3290,6 +3330,11 @@ function createRunLifecycleHandlers(deps = {}) {
3290
3330
  if (body.metadata !== void 0 && !isRecord(body.metadata)) {
3291
3331
  return invalidRequest(reply, "metadata must be an object");
3292
3332
  }
3333
+ const userId = getUserId3(request);
3334
+ const runConfig = userId ? {
3335
+ ...isRecord(body.config) ? body.config : {},
3336
+ user_id: userId
3337
+ } : isRecord(body.config) ? body.config : void 0;
3293
3338
  const maxActiveRunsForTenant = resolveRunTaskMaxActivePerTenant();
3294
3339
  const createPending = async () => {
3295
3340
  let threadRuns;
@@ -3333,7 +3378,7 @@ function createRunLifecycleHandlers(deps = {}) {
3333
3378
  projectId: resolveAgentScope(request).project_id,
3334
3379
  input: input ?? null,
3335
3380
  command: command ?? null,
3336
- config: body.config ?? null,
3381
+ config: runConfig ?? null,
3337
3382
  metadata: body.metadata ?? null,
3338
3383
  inputMessageIds: input ? extractAgentRunInputMessageIds2(input) : [],
3339
3384
  ...inheritedRevision === null ? {} : { capabilityRuntimeRevision: inheritedRevision },
@@ -4378,7 +4423,7 @@ async function handleConnection(socket, request) {
4378
4423
  socket.send(JSON.stringify(payload));
4379
4424
  return true;
4380
4425
  };
4381
- const sendError3 = (code, message, options = {}) => {
4426
+ const sendError4 = (code, message, options = {}) => {
4382
4427
  send({
4383
4428
  type: "error",
4384
4429
  ...options.subscriptionId === void 0 ? {} : { subscription_id: options.subscriptionId },
@@ -4411,7 +4456,7 @@ async function handleConnection(socket, request) {
4411
4456
  } catch (error) {
4412
4457
  if (!subscription.closed && !closed) {
4413
4458
  const frame = mapThreadEventError(error);
4414
- sendError3(frame.code, frame.message, { subscriptionId: subscription.subscriptionId });
4459
+ sendError4(frame.code, frame.message, { subscriptionId: subscription.subscriptionId });
4415
4460
  }
4416
4461
  } finally {
4417
4462
  await removeSubscription(subscription);
@@ -4419,7 +4464,7 @@ async function handleConnection(socket, request) {
4419
4464
  };
4420
4465
  const subscribe = (requestId, params) => {
4421
4466
  if (subscriptions.size >= MAX_SUBSCRIPTIONS) {
4422
- sendError3(
4467
+ sendError4(
4423
4468
  "SUBSCRIPTION_LIMIT",
4424
4469
  `at most ${MAX_SUBSCRIPTIONS} subscriptions are allowed`,
4425
4470
  { requestId }
@@ -4428,12 +4473,12 @@ async function handleConnection(socket, request) {
4428
4473
  }
4429
4474
  const normalized = validateThreadEventFilters(params ?? {});
4430
4475
  if (normalized.error || !normalized.filters) {
4431
- sendError3("INVALID_SUBSCRIPTION", normalized.error ?? "invalid subscription params", { requestId });
4476
+ sendError4("INVALID_SUBSCRIPTION", normalized.error ?? "invalid subscription params", { requestId });
4432
4477
  return;
4433
4478
  }
4434
4479
  const since = parseSinceCursor(isRecord4(params) ? params.since : void 0);
4435
4480
  if (since.error) {
4436
- sendError3("INVALID_SUBSCRIPTION", since.error, { requestId });
4481
+ sendError4("INVALID_SUBSCRIPTION", since.error, { requestId });
4437
4482
  return;
4438
4483
  }
4439
4484
  const subscriptionId = randomUUID5();
@@ -4464,16 +4509,16 @@ async function handleConnection(socket, request) {
4464
4509
  try {
4465
4510
  message = JSON.parse(String(raw));
4466
4511
  } catch {
4467
- sendError3("INVALID_REQUEST", "message must be valid JSON");
4512
+ sendError4("INVALID_REQUEST", "message must be valid JSON");
4468
4513
  return;
4469
4514
  }
4470
4515
  if (!isRecord4(message)) {
4471
- sendError3("INVALID_REQUEST", "message must be an object");
4516
+ sendError4("INVALID_REQUEST", "message must be an object");
4472
4517
  return;
4473
4518
  }
4474
4519
  if (message.type === "subscription.subscribe") {
4475
4520
  if (typeof message.id !== "string" || message.id.length === 0) {
4476
- sendError3("INVALID_REQUEST", "subscription id is required");
4521
+ sendError4("INVALID_REQUEST", "subscription id is required");
4477
4522
  return;
4478
4523
  }
4479
4524
  subscribe(message.id, message.params);
@@ -4481,13 +4526,13 @@ async function handleConnection(socket, request) {
4481
4526
  }
4482
4527
  if (message.type === "subscription.unsubscribe") {
4483
4528
  if (typeof message.id !== "string" || message.id.length === 0) {
4484
- sendError3("INVALID_REQUEST", "subscription id is required");
4529
+ sendError4("INVALID_REQUEST", "subscription id is required");
4485
4530
  return;
4486
4531
  }
4487
4532
  unsubscribe(message.id);
4488
4533
  return;
4489
4534
  }
4490
- sendError3("UNKNOWN_MESSAGE_TYPE", "unknown message type");
4535
+ sendError4("UNKNOWN_MESSAGE_TYPE", "unknown message type");
4491
4536
  };
4492
4537
  cleanup = () => {
4493
4538
  if (closed) return;
@@ -4864,6 +4909,16 @@ function getTenantId7(request) {
4864
4909
  const header2 = request.headers["x-tenant-id"];
4865
4910
  return typeof header2 === "string" && header2.length > 0 ? header2 : "default";
4866
4911
  }
4912
+ function getUserId4(request) {
4913
+ const userId = request.user?.id;
4914
+ if (userId) return userId;
4915
+ const header2 = request.headers["x-user-id"];
4916
+ return typeof header2 === "string" && header2.length > 0 ? header2 : void 0;
4917
+ }
4918
+ function withUserId(config, userId) {
4919
+ if (!userId) return config;
4920
+ return { ...config ?? {}, user_id: userId };
4921
+ }
4867
4922
  function headerValue3(request, name) {
4868
4923
  const value = request.headers[name];
4869
4924
  return typeof value === "string" ? value : void 0;
@@ -4937,7 +4992,10 @@ async function executeThreadCommand(request, reply) {
4937
4992
  }
4938
4993
  const mode = parseMode(args.multitask_strategy);
4939
4994
  if (!mode) return badRequest2(reply, "invalid multitask_strategy");
4940
- const customRunConfig = args.config === void 0 ? void 0 : args.config;
4995
+ const customRunConfig = withUserId(
4996
+ args.config === void 0 ? void 0 : args.config,
4997
+ getUserId4(request)
4998
+ );
4941
4999
  const agent2 = agentInstanceManager6.getAgent({
4942
5000
  assistant_id: thread.assistantId,
4943
5001
  thread_id: threadId,
@@ -4992,6 +5050,10 @@ async function executeThreadCommand(request, reply) {
4992
5050
  );
4993
5051
  }
4994
5052
  };
5053
+ const customRunConfig = withUserId(
5054
+ args.config === void 0 ? void 0 : args.config,
5055
+ getUserId4(request)
5056
+ );
4995
5057
  try {
4996
5058
  const agent2 = agentInstanceManager6.getAgent({
4997
5059
  assistant_id: thread.assistantId,
@@ -4999,7 +5061,7 @@ async function executeThreadCommand(request, reply) {
4999
5061
  tenant_id: tenantId,
5000
5062
  workspace_id: workspaceId,
5001
5063
  project_id: projectId,
5002
- custom_run_config: args.config
5064
+ custom_run_config: customRunConfig
5003
5065
  });
5004
5066
  let interrupt;
5005
5067
  try {
@@ -5033,7 +5095,7 @@ async function executeThreadCommand(request, reply) {
5033
5095
  input: { id: messageId, message: answerText },
5034
5096
  command: resume.command,
5035
5097
  expected_interrupt_id: interruptId,
5036
- ...args.config === void 0 ? {} : { custom_run_config: args.config }
5098
+ ...customRunConfig === void 0 ? {} : { custom_run_config: customRunConfig }
5037
5099
  }, QueueMode2.FOLLOWUP);
5038
5100
  } catch (error) {
5039
5101
  await releaseReservation();
@@ -5511,7 +5573,12 @@ function readCronMetadata(metadata) {
5511
5573
  const cron = isRecord7(metadata) && isRecord7(metadata.cron) ? metadata.cron : {};
5512
5574
  const multitaskStrategy = cron.multitaskStrategy === "interrupt" ? "interrupt" : "enqueue";
5513
5575
  const onRunCompleted = cron.onRunCompleted === "delete" ? "delete" : "keep";
5514
- return { multitaskStrategy, onRunCompleted };
5576
+ return {
5577
+ multitaskStrategy,
5578
+ onRunCompleted,
5579
+ ...typeof cron.workspaceId === "string" && cron.workspaceId.length > 0 ? { workspaceId: cron.workspaceId } : {},
5580
+ ...typeof cron.projectId === "string" && cron.projectId.length > 0 ? { projectId: cron.projectId } : {}
5581
+ };
5515
5582
  }
5516
5583
  function queueModeFor(strategy) {
5517
5584
  return strategy === "interrupt" ? QueueMode3.STEER : QueueMode3.FOLLOWUP;
@@ -5597,6 +5664,10 @@ function createAgentCronHandler(deps) {
5597
5664
  const parts = payloadParts(payload);
5598
5665
  const cron = readCronMetadata(taskInfo.metadata);
5599
5666
  const mode = queueModeFor(cron.multitaskStrategy);
5667
+ const scope = {
5668
+ ...cron.workspaceId === void 0 ? {} : { workspace_id: cron.workspaceId },
5669
+ ...cron.projectId === void 0 ? {} : { project_id: cron.projectId }
5670
+ };
5600
5671
  let threadId = taskInfo.threadId;
5601
5672
  let createdThreadId;
5602
5673
  try {
@@ -5605,18 +5676,25 @@ function createAgentCronHandler(deps) {
5605
5676
  agent = deps.getAgent({
5606
5677
  tenant_id: taskInfo.tenantId,
5607
5678
  assistant_id: assistantId,
5608
- thread_id: threadId
5679
+ thread_id: threadId,
5680
+ ...scope
5609
5681
  });
5610
5682
  } else {
5611
5683
  createdThreadId = idFactory();
5612
5684
  threadId = createdThreadId;
5613
5685
  await deps.createThread(taskInfo.tenantId, assistantId, threadId, {
5614
- metadata: { tenantId: taskInfo.tenantId, cronId: taskInfo.taskId }
5686
+ metadata: {
5687
+ tenantId: taskInfo.tenantId,
5688
+ cronId: taskInfo.taskId,
5689
+ ...cron.workspaceId === void 0 ? {} : { workspaceId: cron.workspaceId },
5690
+ ...cron.projectId === void 0 ? {} : { projectId: cron.projectId }
5691
+ }
5615
5692
  });
5616
5693
  agent = deps.getAgent({
5617
5694
  tenant_id: taskInfo.tenantId,
5618
5695
  assistant_id: assistantId,
5619
- thread_id: threadId
5696
+ thread_id: threadId,
5697
+ ...scope
5620
5698
  });
5621
5699
  if (cron.onRunCompleted === "delete") {
5622
5700
  const cleanupThreadId = threadId;
@@ -5685,6 +5763,8 @@ function toDto(task) {
5685
5763
  cron_id: task.taskId,
5686
5764
  ...task.threadId === void 0 ? {} : { thread_id: task.threadId },
5687
5765
  assistant_id: task.assistantId ?? "",
5766
+ ...cron.workspaceId === void 0 ? {} : { workspace_id: cron.workspaceId },
5767
+ ...cron.projectId === void 0 ? {} : { project_id: cron.projectId },
5688
5768
  schedule: task.cronExpression ?? "",
5689
5769
  timezone: task.timezone ?? "UTC",
5690
5770
  ...task.expiresAt === void 0 ? {} : { end_time: new Date(task.expiresAt).toISOString() },
@@ -5712,6 +5792,70 @@ function failure2(request, reply, error) {
5712
5792
  request.log.error({ err: error }, "agent cron operation failed");
5713
5793
  return reply.status(500).send({ success: false, error: "Agent cron operation failed" });
5714
5794
  }
5795
+ function parseScopeId(value) {
5796
+ if (value === void 0 || value === null) return void 0;
5797
+ if (typeof value !== "string") return null;
5798
+ const trimmed = value.trim();
5799
+ return trimmed.length === 0 ? void 0 : trimmed;
5800
+ }
5801
+ async function resolveCronScope(body, tenantId) {
5802
+ const projectId = parseScopeId(body.project_id);
5803
+ if (projectId === null) {
5804
+ return { ok: false, status: 400, error: "project_id must be a string" };
5805
+ }
5806
+ const workspaceId = parseScopeId(body.workspace_id);
5807
+ if (workspaceId === null) {
5808
+ return { ok: false, status: 400, error: "workspace_id must be a string" };
5809
+ }
5810
+ if (projectId !== void 0) {
5811
+ try {
5812
+ const projectStore = getStoreLattice10("default", "project").store;
5813
+ const project = await projectStore.getProjectById(tenantId, projectId);
5814
+ if (!project || project.tenantId !== tenantId) {
5815
+ return { ok: false, status: 404, error: "Project not found" };
5816
+ }
5817
+ if (workspaceId !== void 0 && workspaceId !== project.workspaceId) {
5818
+ return {
5819
+ ok: false,
5820
+ status: 400,
5821
+ error: "project_id does not belong to workspace_id"
5822
+ };
5823
+ }
5824
+ return {
5825
+ ok: true,
5826
+ scope: {
5827
+ projectId,
5828
+ ...project.workspaceId ? { workspaceId: project.workspaceId } : workspaceId === void 0 ? {} : { workspaceId }
5829
+ }
5830
+ };
5831
+ } catch {
5832
+ return { ok: false, status: 404, error: "Project not found" };
5833
+ }
5834
+ }
5835
+ if (workspaceId !== void 0) {
5836
+ try {
5837
+ const workspaceStore = getStoreLattice10("default", "workspace").store;
5838
+ const workspace = await workspaceStore.getWorkspaceById(tenantId, workspaceId);
5839
+ if (!workspace || workspace.tenantId !== tenantId) {
5840
+ return { ok: false, status: 404, error: "Workspace not found" };
5841
+ }
5842
+ return { ok: true, scope: { workspaceId } };
5843
+ } catch {
5844
+ return { ok: false, status: 404, error: "Workspace not found" };
5845
+ }
5846
+ }
5847
+ return { ok: true, scope: {} };
5848
+ }
5849
+ function cronMetadata(strategy, scope, completion) {
5850
+ return {
5851
+ cron: {
5852
+ multitaskStrategy: strategy,
5853
+ ...completion === void 0 ? {} : { onRunCompleted: completion },
5854
+ ...scope.workspaceId === void 0 ? {} : { workspaceId: scope.workspaceId },
5855
+ ...scope.projectId === void 0 ? {} : { projectId: scope.projectId }
5856
+ }
5857
+ };
5858
+ }
5715
5859
  function validCron(value) {
5716
5860
  if (typeof value !== "string" || value.trim() === "") return void 0;
5717
5861
  try {
@@ -5780,6 +5924,13 @@ async function createThreadCron(request, reply) {
5780
5924
  return badRequest4(reply, "enabled must be a boolean");
5781
5925
  }
5782
5926
  const tenantId = getTenantId10(request);
5927
+ const scopeResolution = await resolveCronScope(body, tenantId);
5928
+ if (!scopeResolution.ok) {
5929
+ return reply.status(scopeResolution.status).send({
5930
+ success: false,
5931
+ error: scopeResolution.error
5932
+ });
5933
+ }
5783
5934
  let thread;
5784
5935
  try {
5785
5936
  thread = await getStoreLattice10("default", "thread").store.getThreadById(
@@ -5802,7 +5953,7 @@ async function createThreadCron(request, reply) {
5802
5953
  tenantId,
5803
5954
  assistantId: thread.assistantId,
5804
5955
  threadId: thread.id,
5805
- metadata: { cron: { multitaskStrategy: strategy } }
5956
+ metadata: cronMetadata(strategy, scopeResolution.scope)
5806
5957
  });
5807
5958
  if (!scheduled) return failure2(request, reply, new Error("scheduleCron rejected the task"));
5808
5959
  if (body.enabled === false) {
@@ -5857,6 +6008,13 @@ async function createStatelessCron(request, reply) {
5857
6008
  } catch (error) {
5858
6009
  return failure2(request, reply, error);
5859
6010
  }
6011
+ const scopeResolution = await resolveCronScope(body, tenantId);
6012
+ if (!scopeResolution.ok) {
6013
+ return reply.status(scopeResolution.status).send({
6014
+ success: false,
6015
+ error: scopeResolution.error
6016
+ });
6017
+ }
5860
6018
  const lattice = schedule();
5861
6019
  if (!lattice || !storageOf(lattice)) return unavailable2(reply);
5862
6020
  const cronId = randomUUID10();
@@ -5868,7 +6026,7 @@ async function createStatelessCron(request, reply) {
5868
6026
  maxRetries: 2,
5869
6027
  tenantId,
5870
6028
  assistantId: body.assistant_id,
5871
- metadata: { cron: { multitaskStrategy: strategy, onRunCompleted: completion } }
6029
+ metadata: cronMetadata(strategy, scopeResolution.scope, completion)
5872
6030
  });
5873
6031
  if (!scheduled) return failure2(request, reply, new Error("scheduleCron rejected the task"));
5874
6032
  if (body.enabled === false) {
@@ -6376,9 +6534,10 @@ function isSharedStore(kind) {
6376
6534
  }
6377
6535
  function computeMultiReplicaReadiness() {
6378
6536
  const multiReplicaDeclared = isMultiReplicaDeclared();
6537
+ const strict = process.env.AXIOM_READINESS_STRICT === "true";
6379
6538
  const warnings = [];
6380
6539
  if (!multiReplicaDeclared) {
6381
- return { multiReplicaDeclared, ok: true, warnings };
6540
+ return { multiReplicaDeclared, ok: true, strict, blocking: false, warnings };
6382
6541
  }
6383
6542
  for (const kind of SHARED_STORE_KINDS) {
6384
6543
  const name = storeClassName(kind);
@@ -6424,16 +6583,35 @@ function computeMultiReplicaReadiness() {
6424
6583
  );
6425
6584
  }
6426
6585
  }
6427
- return { multiReplicaDeclared, ok: warnings.length === 0, warnings };
6586
+ const ok = warnings.length === 0;
6587
+ return {
6588
+ multiReplicaDeclared,
6589
+ ok,
6590
+ strict,
6591
+ blocking: strict && multiReplicaDeclared && !ok,
6592
+ warnings
6593
+ };
6428
6594
  }
6429
- function warnMultiReplicaReadiness(logger8) {
6595
+ var MultiReplicaReadinessError = class extends Error {
6596
+ constructor(warnings) {
6597
+ super(`Multi-replica readiness blocked startup: ${warnings.join(" | ")}`);
6598
+ this.warnings = warnings;
6599
+ this.code = "MULTI_REPLICA_READINESS_BLOCKED";
6600
+ this.name = "MultiReplicaReadinessError";
6601
+ }
6602
+ };
6603
+ function assertMultiReplicaReadiness() {
6604
+ const { blocking, warnings } = computeMultiReplicaReadiness();
6605
+ if (blocking) throw new MultiReplicaReadinessError(warnings);
6606
+ }
6607
+ function warnMultiReplicaReadiness(logger9) {
6430
6608
  const { multiReplicaDeclared, warnings } = computeMultiReplicaReadiness();
6431
6609
  if (!multiReplicaDeclared) return;
6432
6610
  for (const message of warnings) {
6433
- logger8.warn(message, { phase: "multi_replica_readiness" });
6611
+ logger9.warn(message, { phase: "multi_replica_readiness" });
6434
6612
  }
6435
6613
  if (process.env.CHUNK_SOURCE?.trim() !== "redis") {
6436
- logger8.warn(
6614
+ logger9.warn(
6437
6615
  "Multi-replica: CHUNK_SOURCE is not 'redis'; agent run SSE and resume_stream are process-local and require thread affinity/sticky routing (set CHUNK_SOURCE=redis with REDIS_URL to share streams)",
6438
6616
  { phase: "multi_replica_readiness", feature: "agent-run-sse" }
6439
6617
  );
@@ -6527,6 +6705,159 @@ async function getInfo(request, reply) {
6527
6705
  }
6528
6706
  }
6529
6707
 
6708
+ // src/controllers/observability.ts
6709
+ import {
6710
+ ObservabilityDashboardUnavailableError,
6711
+ ObservabilityDashboardUnsupportedError,
6712
+ ObservabilityLatticeManager
6713
+ } from "@axiom-lattice/core";
6714
+
6715
+ // src/controllers/tenant-scope.ts
6716
+ var TenantScopeError = class extends Error {
6717
+ /** Creates a tenant scope error. */
6718
+ constructor(code, statusCode, message) {
6719
+ super(message);
6720
+ this.code = code;
6721
+ this.statusCode = statusCode;
6722
+ this.name = "TenantScopeError";
6723
+ }
6724
+ };
6725
+ function resolveTenantScope(request, authRequired2) {
6726
+ const authenticated = request.user?.tenantId;
6727
+ const header2 = request.headers["x-tenant-id"];
6728
+ const requested = Array.isArray(header2) ? header2[0] : header2;
6729
+ if (authenticated) {
6730
+ if (requested && requested !== authenticated) {
6731
+ throw new TenantScopeError(
6732
+ "TENANT_ACCESS_DENIED",
6733
+ 403,
6734
+ "Tenant access denied"
6735
+ );
6736
+ }
6737
+ return { tenantId: authenticated, userId: request.user?.id };
6738
+ }
6739
+ if (authRequired2) {
6740
+ throw new TenantScopeError(
6741
+ "UNAUTHENTICATED",
6742
+ 401,
6743
+ "Console session required"
6744
+ );
6745
+ }
6746
+ return { tenantId: requested || "default" };
6747
+ }
6748
+
6749
+ // src/controllers/observability.ts
6750
+ var logger = new Logger({ serviceName: "lattice/gateway/observability" });
6751
+ var DASHBOARD_RANGES = /* @__PURE__ */ new Set([
6752
+ "1h",
6753
+ "24h",
6754
+ "7d",
6755
+ "30d"
6756
+ ]);
6757
+ function buildObservabilityInfo(manager = ObservabilityLatticeManager.getInstance()) {
6758
+ const provider = manager.getDefault();
6759
+ if (!provider) return { enabled: false, dashboard: false };
6760
+ const url = provider.getTraceListUrl?.();
6761
+ return {
6762
+ enabled: true,
6763
+ dashboard: typeof provider.getDashboard === "function",
6764
+ provider: provider.type,
6765
+ ...url ? { url } : {}
6766
+ };
6767
+ }
6768
+ async function getObservability(_request, reply) {
6769
+ return reply.send({ success: true, data: buildObservabilityInfo() });
6770
+ }
6771
+ function sendError(reply, status, code, message) {
6772
+ return reply.status(status).send({
6773
+ success: false,
6774
+ error: { code, message, retryable: false }
6775
+ });
6776
+ }
6777
+ async function getObservabilityDashboard(request, reply) {
6778
+ const provider = ObservabilityLatticeManager.getInstance().getDefault();
6779
+ if (!provider) {
6780
+ return sendError(
6781
+ reply,
6782
+ 404,
6783
+ "OBSERVABILITY_DISABLED",
6784
+ "Observability is not enabled"
6785
+ );
6786
+ }
6787
+ if (typeof provider.getDashboard !== "function") {
6788
+ return sendError(
6789
+ reply,
6790
+ 501,
6791
+ "OBSERVABILITY_DASHBOARD_UNSUPPORTED",
6792
+ "Dashboard is not supported by this provider"
6793
+ );
6794
+ }
6795
+ const range = request.query.range ?? "24h";
6796
+ if (!DASHBOARD_RANGES.has(range)) {
6797
+ return sendError(reply, 400, "INVALID_RANGE", "Invalid dashboard range");
6798
+ }
6799
+ let tenantId;
6800
+ try {
6801
+ tenantId = resolveTenantScope(
6802
+ request,
6803
+ process.env.AUTH_REQUIRED === "true"
6804
+ ).tenantId;
6805
+ } catch (error) {
6806
+ if (error instanceof TenantScopeError) {
6807
+ return sendError(reply, error.statusCode, error.code, error.message);
6808
+ }
6809
+ return sendError(
6810
+ reply,
6811
+ 401,
6812
+ "UNAUTHENTICATED",
6813
+ "Console session required"
6814
+ );
6815
+ }
6816
+ try {
6817
+ const result = await provider.getDashboard({
6818
+ range,
6819
+ tenantId
6820
+ });
6821
+ return reply.send({
6822
+ success: true,
6823
+ data: {
6824
+ provider: provider.type,
6825
+ range,
6826
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6827
+ version: result.version,
6828
+ ...result.warnings ? { warnings: result.warnings } : {},
6829
+ payload: result.payload
6830
+ }
6831
+ });
6832
+ } catch (error) {
6833
+ if (error instanceof ObservabilityDashboardUnsupportedError) {
6834
+ return sendError(
6835
+ reply,
6836
+ 501,
6837
+ "OBSERVABILITY_DASHBOARD_UNSUPPORTED",
6838
+ error.message
6839
+ );
6840
+ }
6841
+ if (error instanceof ObservabilityDashboardUnavailableError) {
6842
+ return sendError(
6843
+ reply,
6844
+ 502,
6845
+ "OBSERVABILITY_DASHBOARD_UNAVAILABLE",
6846
+ error.message
6847
+ );
6848
+ }
6849
+ logger.warn("observability dashboard failed", {
6850
+ error: error instanceof Error ? error.message : String(error)
6851
+ });
6852
+ return sendError(
6853
+ reply,
6854
+ 502,
6855
+ "OBSERVABILITY_DASHBOARD_FAILED",
6856
+ "Failed to load dashboard"
6857
+ );
6858
+ }
6859
+ }
6860
+
6530
6861
  // src/controllers/system-metrics.ts
6531
6862
  import {
6532
6863
  getReplicaId as getReplicaId4,
@@ -7874,8 +8205,92 @@ async function executeSqlQuery(client, body, reply) {
7874
8205
  }
7875
8206
 
7876
8207
  // src/controllers/workflow-tracking.ts
7877
- import { getStoreLattice as getStoreLattice14, agentInstanceManager as agentInstanceManager7, ThreadStatus as ThreadStatus2, abortWorkflowRun as signalAbort } from "@axiom-lattice/core";
7878
- import { MessageChunkTypes as MessageChunkTypes3 } from "@axiom-lattice/protocols";
8208
+ import {
8209
+ getStoreLattice as getStoreLattice14,
8210
+ agentInstanceManager as agentInstanceManager7,
8211
+ ThreadStatus as ThreadStatus2,
8212
+ abortWorkflowRun as signalAbort
8213
+ } from "@axiom-lattice/core";
8214
+
8215
+ // src/router/roomInterrupt.ts
8216
+ var CLARIFY_FENCE = /^```clarify\n([\s\S]*?)\n```$/;
8217
+ function readClarifyValue(value) {
8218
+ if (typeof value !== "string") return void 0;
8219
+ const match = CLARIFY_FENCE.exec(value.trim());
8220
+ if (!match) return void 0;
8221
+ try {
8222
+ const parsed = JSON.parse(match[1]);
8223
+ if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.questions)) {
8224
+ return parsed;
8225
+ }
8226
+ } catch {
8227
+ }
8228
+ return void 0;
8229
+ }
8230
+ function rawInterruptId(interrupt, taskId, taskIndex, index) {
8231
+ const record = typeof interrupt === "object" && interrupt !== null ? interrupt : void 0;
8232
+ const rawId = record?.id;
8233
+ if (typeof rawId === "string" || typeof rawId === "number") return String(rawId);
8234
+ return `${taskId ?? `task-${taskIndex}`}:${index}`;
8235
+ }
8236
+ function pendingInterrupts(state) {
8237
+ if (typeof state !== "object" || state === null) return [];
8238
+ const tasks = state.tasks;
8239
+ if (!Array.isArray(tasks)) return [];
8240
+ const result = [];
8241
+ tasks.forEach((task, taskIndex) => {
8242
+ const record = typeof task === "object" && task !== null ? task : void 0;
8243
+ const rawTaskId = record?.id;
8244
+ const taskId = typeof rawTaskId === "string" || typeof rawTaskId === "number" ? String(rawTaskId) : void 0;
8245
+ const interrupts = record?.interrupts;
8246
+ if (!Array.isArray(interrupts)) return;
8247
+ interrupts.forEach((interrupt, index) => {
8248
+ const value = typeof interrupt === "object" && interrupt !== null ? interrupt.value : void 0;
8249
+ const clarify = readClarifyValue(value);
8250
+ result.push({
8251
+ interruptId: rawInterruptId(interrupt, taskId, taskIndex, index),
8252
+ ...clarify === void 0 ? {} : { clarify }
8253
+ });
8254
+ });
8255
+ });
8256
+ return result;
8257
+ }
8258
+ function readPendingInterruptTarget(state) {
8259
+ const interrupts = pendingInterrupts(state);
8260
+ if (interrupts.length === 0) return void 0;
8261
+ const ids = new Set(interrupts.map((interrupt) => interrupt.interruptId));
8262
+ if (ids.size !== interrupts.length) return { conflict: true };
8263
+ const clarify = interrupts.find((interrupt) => interrupt.clarify !== void 0);
8264
+ if (clarify !== void 0) {
8265
+ if (!isOfficialInterruptId(clarify.interruptId) && interrupts.length > 1) return { conflict: true };
8266
+ return { target: clarify };
8267
+ }
8268
+ return interrupts.length === 1 ? { target: interrupts[0] } : { conflict: true };
8269
+ }
8270
+ function readExplicitInterruptTarget(state, interruptId) {
8271
+ const interrupts = pendingInterrupts(state);
8272
+ const matches = interrupts.filter((interrupt) => interrupt.interruptId === interruptId);
8273
+ if (matches.length > 1) return { conflict: true };
8274
+ const target = matches[0];
8275
+ if (target === void 0) return void 0;
8276
+ if (!isOfficialInterruptId(interruptId) && interrupts.length > 1) return { conflict: true };
8277
+ return { target };
8278
+ }
8279
+ function readClarifyInterruptTarget(state) {
8280
+ const match = readPendingInterruptTarget(state);
8281
+ return match !== void 0 && !("conflict" in match) && match.target.clarify !== void 0 ? match.target : void 0;
8282
+ }
8283
+ function readClarifyInterrupt(state) {
8284
+ return pendingInterrupts(state).find((interrupt) => interrupt.clarify !== void 0)?.clarify;
8285
+ }
8286
+ function buildClarifyResumeCommand(text, interruptId) {
8287
+ return resumeMap(interruptId, { data: { answers: [{ questionIndex: 0, otherText: text }] } });
8288
+ }
8289
+ function buildClarifySkipCommand(interruptId) {
8290
+ return resumeMap(interruptId, { data: { answers: [] } });
8291
+ }
8292
+
8293
+ // src/controllers/workflow-tracking.ts
7879
8294
  function getTenantId14(request) {
7880
8295
  const userTenantId = request.user?.tenantId;
7881
8296
  if (userTenantId) return userTenantId;
@@ -8202,19 +8617,19 @@ async function deleteWorkflowRun(request, reply) {
8202
8617
  return reply.status(404).send({ success: false, message: "Workflow run not found" });
8203
8618
  }
8204
8619
  if (run2.status === "running") {
8205
- try {
8206
- const workspace_id = request.headers["x-workspace-id"];
8207
- const project_id = request.headers["x-project-id"];
8208
- const agent = agentInstanceManager7.getAgent({
8209
- assistant_id: run2.assistantId,
8210
- thread_id: run2.threadId,
8211
- tenant_id: tenantId,
8212
- workspace_id,
8213
- project_id
8620
+ const result = await requestWorkflowCancel(request, tenantId, run2);
8621
+ if (result === "unavailable") {
8622
+ return reply.status(503).send({
8623
+ success: false,
8624
+ message: "Workflow cancellation is unavailable on this replica"
8214
8625
  });
8215
- await agent.abort();
8216
- } catch (err) {
8217
- request.log.warn({ runId, error: err.message }, "Failed to abort agent, deleting tracking records anyway");
8626
+ }
8627
+ if (result === "accepted" || result === "not_found") {
8628
+ await store2.updateWorkflowRun(runId, {
8629
+ status: "cancelled",
8630
+ errorMessage: "Deleted by user",
8631
+ completedAt: /* @__PURE__ */ new Date()
8632
+ }).catch(() => void 0);
8218
8633
  }
8219
8634
  }
8220
8635
  await store2.deleteWorkflowRun(runId);
@@ -8307,29 +8722,62 @@ async function replyInboxTask(request, reply) {
8307
8722
  data: { runId: run2.id, assistantId: run2.assistantId, threadId: run2.threadId, status: runStatus }
8308
8723
  });
8309
8724
  }
8310
- agent.addMessage({
8311
- input: { message: "Clarification answers submitted" },
8312
- command: {
8313
- resume: {
8314
- action: "submit",
8315
- data: { answers },
8316
- message: "Clarification answers submitted"
8317
- }
8725
+ const currentState = await agent.getCurrentState();
8726
+ const interruptMatch = readPendingInterruptTarget(currentState);
8727
+ if (!interruptMatch || "conflict" in interruptMatch) {
8728
+ return reply.status(409).send({
8729
+ success: false,
8730
+ message: "Cannot resume: pending interrupt is missing or ambiguous",
8731
+ data: { runId: run2.id, assistantId: run2.assistantId, threadId: run2.threadId }
8732
+ });
8733
+ }
8734
+ const interruptTarget = interruptMatch.target;
8735
+ const resume = interruptTarget.clarify ? clarifyResume({ answers }, interruptTarget.interruptId) : {
8736
+ command: resumeMap(interruptTarget.interruptId, {
8737
+ action: "submit",
8738
+ data: { answers },
8739
+ message: "Clarification answers submitted"
8740
+ })
8741
+ };
8742
+ if (!resume.command) {
8743
+ return reply.status(400).send({
8744
+ success: false,
8745
+ message: "error" in resume ? resume.error ?? "Invalid resume answers" : "Invalid resume answers"
8746
+ });
8747
+ }
8748
+ let queuedStatus = 200;
8749
+ let runPayload;
8750
+ const syntheticReply = {
8751
+ status(code) {
8752
+ queuedStatus = code;
8753
+ return this;
8754
+ },
8755
+ send(payload) {
8756
+ runPayload = payload;
8757
+ return this;
8758
+ },
8759
+ header() {
8760
+ return this;
8318
8761
  }
8319
- }).then((result) => {
8320
- const stream = agent.chunkStream(
8321
- result.messageId,
8322
- [MessageChunkTypes3.MESSAGE_COMPLETED]
8323
- );
8324
- (async () => {
8325
- for await (const _ of stream) {
8762
+ };
8763
+ await createRun(
8764
+ {
8765
+ ...request,
8766
+ body: {
8767
+ assistant_id: run2.assistantId,
8768
+ thread_id: run2.threadId,
8769
+ background: true,
8770
+ command: resume.command
8326
8771
  }
8327
- })().catch((error) => {
8328
- request.log.error(error, "Background chunk stream error");
8772
+ },
8773
+ syntheticReply
8774
+ );
8775
+ if (queuedStatus >= 400) {
8776
+ return reply.status(queuedStatus).send(runPayload ?? {
8777
+ success: false,
8778
+ message: "Failed to enqueue workflow resume"
8329
8779
  });
8330
- }).catch((error) => {
8331
- request.log.error(error, "Background resume failed");
8332
- });
8780
+ }
8333
8781
  return reply.status(200).send({
8334
8782
  success: true,
8335
8783
  message: "Resume request submitted successfully",
@@ -8348,6 +8796,60 @@ async function replyInboxTask(request, reply) {
8348
8796
  });
8349
8797
  }
8350
8798
  }
8799
+ async function requestWorkflowCancel(request, tenantId, run2) {
8800
+ let agentRun;
8801
+ try {
8802
+ const ledger = getStoreLattice14("default", "agentRun").store;
8803
+ agentRun = await ledger.findRunning(tenantId, run2.threadId);
8804
+ } catch {
8805
+ }
8806
+ if (agentRun) {
8807
+ const router = getRunCancelRouter();
8808
+ if (router) {
8809
+ try {
8810
+ const result = await router.requestCancelById({
8811
+ tenantId,
8812
+ assistantId: run2.assistantId,
8813
+ threadId: run2.threadId,
8814
+ runId: agentRun.runId,
8815
+ reason: "abort"
8816
+ });
8817
+ if (result === "not_found") return "not_found";
8818
+ if (result === "terminal") return "terminal";
8819
+ signalAbort(run2.id);
8820
+ return "accepted";
8821
+ } catch {
8822
+ return "unavailable";
8823
+ }
8824
+ }
8825
+ try {
8826
+ const ledger = getStoreLattice14("default", "agentRun").store;
8827
+ const result = await ledger.requestCancel(tenantId, agentRun.runId);
8828
+ if (result === "not_found") return "not_found";
8829
+ if (result === "terminal") return "terminal";
8830
+ signalAbort(run2.id);
8831
+ return "accepted";
8832
+ } catch {
8833
+ return "unavailable";
8834
+ }
8835
+ }
8836
+ try {
8837
+ const workspace_id = request.headers["x-workspace-id"];
8838
+ const project_id = request.headers["x-project-id"];
8839
+ const agent = agentInstanceManager7.getAgent({
8840
+ assistant_id: run2.assistantId,
8841
+ thread_id: run2.threadId,
8842
+ tenant_id: tenantId,
8843
+ workspace_id,
8844
+ project_id
8845
+ });
8846
+ await agent.abort();
8847
+ signalAbort(run2.id);
8848
+ return "accepted";
8849
+ } catch {
8850
+ return "unavailable";
8851
+ }
8852
+ }
8351
8853
  async function abortWorkflowRun(request, reply) {
8352
8854
  try {
8353
8855
  const { runId } = request.params;
@@ -8366,23 +8868,24 @@ async function abortWorkflowRun(request, reply) {
8366
8868
  message: "Workflow run not found"
8367
8869
  });
8368
8870
  }
8871
+ const result = await requestWorkflowCancel(request, tenantId, run2);
8872
+ if (result === "not_found" || result === "terminal") {
8873
+ return reply.status(409).send({
8874
+ success: false,
8875
+ message: result === "terminal" ? "Workflow run is already finished" : "Workflow run is not running"
8876
+ });
8877
+ }
8878
+ if (result === "unavailable") {
8879
+ return reply.status(503).send({
8880
+ success: false,
8881
+ message: "Workflow cancellation is unavailable on this replica"
8882
+ });
8883
+ }
8369
8884
  await store2.updateWorkflowRun(runId, {
8370
8885
  status: "cancelled",
8371
8886
  errorMessage: "Aborted by user",
8372
8887
  completedAt: /* @__PURE__ */ new Date()
8373
- }).catch(() => {
8374
- });
8375
- const workspace_id = request.headers["x-workspace-id"];
8376
- const project_id = request.headers["x-project-id"];
8377
- const agent = agentInstanceManager7.getAgent({
8378
- assistant_id: run2.assistantId,
8379
- thread_id: run2.threadId,
8380
- tenant_id: tenantId,
8381
- workspace_id,
8382
- project_id
8383
- });
8384
- await agent.abort();
8385
- signalAbort(runId);
8888
+ }).catch(() => void 0);
8386
8889
  return reply.status(200).send({
8387
8890
  success: true,
8388
8891
  message: "Workflow aborted",
@@ -8408,7 +8911,7 @@ function getTenantId15(request) {
8408
8911
  if (userTenantId) return userTenantId;
8409
8912
  return request.headers["x-tenant-id"] || "default";
8410
8913
  }
8411
- function getUserId3(request) {
8914
+ function getUserId5(request) {
8412
8915
  const userId = request.user?.id || request.headers["x-user-id"];
8413
8916
  if (!userId) throw new Error("User ID is required");
8414
8917
  return userId;
@@ -8431,7 +8934,7 @@ async function updateLinkMeta(userId, tenantId, meta) {
8431
8934
  }
8432
8935
  async function createPersonalAssistant(request, reply) {
8433
8936
  const tenantId = getTenantId15(request);
8434
- const userId = getUserId3(request);
8937
+ const userId = getUserId5(request);
8435
8938
  const { name, personality } = request.body;
8436
8939
  if (!name || !personality) {
8437
8940
  return reply.status(400).send({ success: false, message: "name and personality are required" });
@@ -8468,7 +8971,7 @@ async function createPersonalAssistant(request, reply) {
8468
8971
  }
8469
8972
  async function getPersonalAssistant(request, reply) {
8470
8973
  const tenantId = getTenantId15(request);
8471
- const userId = getUserId3(request);
8974
+ const userId = getUserId5(request);
8472
8975
  const store2 = getAssistantStore();
8473
8976
  const assistant = await store2.getByOwner(tenantId, userId);
8474
8977
  if (!assistant) {
@@ -8485,7 +8988,7 @@ async function getPersonalAssistant(request, reply) {
8485
8988
  }
8486
8989
  async function deletePersonalAssistant(request, reply) {
8487
8990
  const tenantId = getTenantId15(request);
8488
- const userId = getUserId3(request);
8991
+ const userId = getUserId5(request);
8489
8992
  const store2 = getAssistantStore();
8490
8993
  const assistant = await store2.getByOwner(tenantId, userId);
8491
8994
  if (!assistant) {
@@ -9162,7 +9665,7 @@ var ExportImportService = class {
9162
9665
  // src/controllers/export-import.ts
9163
9666
  import { ExportableEntityRegistry as ExportableEntityRegistry2 } from "@axiom-lattice/core";
9164
9667
  var service = new ExportImportService();
9165
- var TenantScopeError = class extends Error {
9668
+ var TenantScopeError2 = class extends Error {
9166
9669
  constructor() {
9167
9670
  super(...arguments);
9168
9671
  this.statusCode = 403;
@@ -9174,12 +9677,12 @@ function resolveTenantId(request, routeTenantId) {
9174
9677
  const headerTenantId = Array.isArray(header2) ? header2[0] : header2;
9175
9678
  if (authenticatedTenantId) {
9176
9679
  if (headerTenantId && headerTenantId !== authenticatedTenantId) {
9177
- throw new TenantScopeError(
9680
+ throw new TenantScopeError2(
9178
9681
  `Tenant scope mismatch: header tenant "${headerTenantId}" does not match authenticated tenant "${authenticatedTenantId}"`
9179
9682
  );
9180
9683
  }
9181
9684
  if (routeTenantId && routeTenantId !== authenticatedTenantId) {
9182
- throw new TenantScopeError(
9685
+ throw new TenantScopeError2(
9183
9686
  `Tenant scope mismatch: route tenant "${routeTenantId}" does not match authenticated tenant "${authenticatedTenantId}"`
9184
9687
  );
9185
9688
  }
@@ -13033,7 +13536,9 @@ import {
13033
13536
  } from "@axiom-lattice/protocols";
13034
13537
  import {
13035
13538
  getSandboxPluginCatalog as getSandboxPluginCatalog2,
13036
- mcpManager as mcpManager2
13539
+ mcpManager as mcpManager2,
13540
+ resolvePluginConnections,
13541
+ verifyMcpUiScope
13037
13542
  } from "@axiom-lattice/core";
13038
13543
 
13039
13544
  // src/services/mcp-ui/UiResourceResolver.ts
@@ -13303,6 +13808,22 @@ var PluginMethodNotSupportedError = class extends Error {
13303
13808
  this.name = "PluginMethodNotSupportedError";
13304
13809
  }
13305
13810
  };
13811
+ var McpAppScopeError = class extends Error {
13812
+ /**
13813
+ * @param status - HTTP status to return.
13814
+ * @param code - Stable machine-readable denial code.
13815
+ */
13816
+ constructor(status, code) {
13817
+ super(code);
13818
+ this.status = status;
13819
+ this.code = code;
13820
+ this.name = "McpAppScopeError";
13821
+ }
13822
+ };
13823
+ function denyScope(status, code, detail) {
13824
+ console.warn(JSON.stringify({ event: "mcp-apps:scope-denied", ...detail, code }));
13825
+ return new McpAppScopeError(status, code);
13826
+ }
13306
13827
  var ALLOWED_METHODS = /* @__PURE__ */ new Set(["tools/call", "tools/list", "resources/read"]);
13307
13828
  function asRecord(value) {
13308
13829
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
@@ -13369,6 +13890,9 @@ function makeMcpAppHandlers(deps) {
13369
13890
  }
13370
13891
  return { status: 200, body: { tools: await deps.listTools(tenantId, ref) } };
13371
13892
  } catch (error) {
13893
+ if (error instanceof McpAppScopeError) {
13894
+ return { status: error.status, body: { error: error.code } };
13895
+ }
13372
13896
  if (error instanceof PluginToolScopeError) {
13373
13897
  return { status: 403, body: { error: "PLUGIN_TOOL_SCOPE" } };
13374
13898
  }
@@ -13412,8 +13936,84 @@ async function fetchPluginUiResource(backend, tenantId, pluginType, resource) {
13412
13936
  prefersBorder: tool.ui.prefersBorder
13413
13937
  };
13414
13938
  }
13415
- async function callPluginTool(backend, tenantId, ref, name, args, context) {
13416
- return invokePluginTool(backend, tenantId, ref.pluginType, name, args, context);
13939
+ async function callPluginTool(backend, tenantId, ref, name, args, context, deps) {
13940
+ const scope = ref.scope ? (deps?.verifyScope ?? verifyMcpUiScope)(ref.scope) : null;
13941
+ if (ref.scope && !scope) {
13942
+ throw denyScope(403, "SCOPE_INVALID", {
13943
+ tenantId,
13944
+ pluginType: ref.pluginType,
13945
+ tool: name
13946
+ });
13947
+ }
13948
+ if (scope) {
13949
+ const mismatched = scope.tid !== tenantId || scope.plugin !== ref.pluginType || scope.resource !== ref.resource;
13950
+ if (mismatched) {
13951
+ throw denyScope(403, "SCOPE_MISMATCH", {
13952
+ tenantId,
13953
+ pluginType: ref.pluginType,
13954
+ tool: name,
13955
+ jti: scope.jti,
13956
+ threadId: scope.thid
13957
+ });
13958
+ }
13959
+ }
13960
+ const manifest = await backend.catalog.getValidatedManifest(tenantId, ref.pluginType);
13961
+ const rawConnectionKey = args.connectionKey;
13962
+ const explicit = typeof rawConnectionKey === "string" ? rawConnectionKey : void 0;
13963
+ const scoped = scope?.ck;
13964
+ const connectionRelevant = Boolean(manifest?.connection) || scoped !== void 0;
13965
+ if (connectionRelevant && (explicit !== void 0 ? explicit !== scoped : rawConnectionKey !== void 0)) {
13966
+ throw denyScope(403, "CONNECTION_NOT_ALLOWED", {
13967
+ tenantId,
13968
+ pluginType: ref.pluginType,
13969
+ tool: name,
13970
+ jti: scope?.jti,
13971
+ threadId: scope?.thid
13972
+ });
13973
+ }
13974
+ let connection;
13975
+ let connectionKey;
13976
+ if (scoped !== void 0) {
13977
+ const resolveConnections = deps?.resolveConnections ?? resolvePluginConnections;
13978
+ const resolved = await resolveConnections(
13979
+ ref.pluginType,
13980
+ { connections: [scoped] },
13981
+ { tenantId }
13982
+ );
13983
+ if (resolved.length === 0) {
13984
+ throw denyScope(403, "CONNECTION_NOT_ALLOWED", {
13985
+ tenantId,
13986
+ pluginType: ref.pluginType,
13987
+ tool: name,
13988
+ jti: scope?.jti,
13989
+ threadId: scope?.thid
13990
+ });
13991
+ }
13992
+ connection = resolved[0].config;
13993
+ connectionKey = resolved[0].key;
13994
+ }
13995
+ if (manifest?.connection && !connectionKey) {
13996
+ throw denyScope(409, "CONNECTION_REQUIRED", {
13997
+ tenantId,
13998
+ pluginType: ref.pluginType,
13999
+ tool: name,
14000
+ jti: scope?.jti,
14001
+ threadId: scope?.thid
14002
+ });
14003
+ }
14004
+ const identity = scope ? {
14005
+ workspaceId: scope.wid,
14006
+ projectId: scope.pid,
14007
+ threadId: scope.thid,
14008
+ assistantId: scope.aid
14009
+ } : {
14010
+ workspaceId: context?.workspaceId,
14011
+ projectId: context?.projectId
14012
+ };
14013
+ return invokePluginTool(backend, tenantId, ref.pluginType, name, args, {
14014
+ ...identity,
14015
+ ...connection === void 0 ? {} : { connection }
14016
+ });
13417
14017
  }
13418
14018
  function createDefaultPluginBackend() {
13419
14019
  return {
@@ -13461,8 +14061,8 @@ function runContextOf(request) {
13461
14061
  projectId: firstHeader(request, "x-project-id")
13462
14062
  };
13463
14063
  }
13464
- function registerMcpAppRoutes(fastify2) {
13465
- const handlers = makeMcpAppHandlers(resolveMcpAppDeps());
14064
+ function registerMcpAppRoutes(fastify2, deps = resolveMcpAppDeps()) {
14065
+ const handlers = makeMcpAppHandlers(deps);
13466
14066
  fastify2.get("/mcp-apps/sandbox.html", async (_request, reply) => {
13467
14067
  return reply.header("content-type", SANDBOX_HTML_CONTENT_TYPE).header("x-content-type-options", "nosniff").header("cache-control", "no-store").send(SANDBOX_HTML);
13468
14068
  });
@@ -13493,6 +14093,7 @@ import { v4 as uuidv43 } from "uuid";
13493
14093
  // src/services/eval-runner.ts
13494
14094
  import { EventEmitter } from "events";
13495
14095
  import {
14096
+ getReplicaId as getReplicaId6,
13496
14097
  getStoreLattice as getStoreLattice20,
13497
14098
  hasModelLatticeForTenant,
13498
14099
  listModelLatticesForTenant,
@@ -13500,6 +14101,8 @@ import {
13500
14101
  } from "@axiom-lattice/core";
13501
14102
  import { v4 as uuidv42 } from "uuid";
13502
14103
  var TERMINAL_PERSIST_ATTEMPTS = 2;
14104
+ var EVAL_EXECUTION_LEASE = "eval:execution";
14105
+ var EVAL_LEASE_TTL_MS = 6e4;
13503
14106
  function mapLogs(logs) {
13504
14107
  return logs.map((l) => ({
13505
14108
  timestamp: l.ts,
@@ -13534,14 +14137,48 @@ var EvalRunner = class {
13534
14137
  }
13535
14138
  const leaseToken = Symbol(leaseKey);
13536
14139
  this.projectLeases.set(leaseKey, leaseToken);
14140
+ let executionLease;
13537
14141
  try {
13538
- return await this.startLeasedRun(tenantId, projectId, suiteIds, caseIds, runConfig, taskId, leaseKey, leaseToken);
14142
+ executionLease = await this.acquireExecutionLease();
14143
+ return await this.startLeasedRun(
14144
+ tenantId,
14145
+ projectId,
14146
+ suiteIds,
14147
+ caseIds,
14148
+ runConfig,
14149
+ taskId,
14150
+ leaseKey,
14151
+ leaseToken,
14152
+ executionLease.release
14153
+ );
13539
14154
  } catch (error) {
13540
14155
  this.releaseProjectLease(leaseKey, leaseToken);
14156
+ await executionLease?.release();
13541
14157
  throw error;
13542
14158
  }
13543
14159
  }
13544
- async startLeasedRun(tenantId, projectId, suiteIds, caseIds, runConfig, taskId, leaseKey, leaseToken) {
14160
+ /** One eval at a time across replicas; the executing worker holds this lease. */
14161
+ async acquireExecutionLease() {
14162
+ const store2 = this.getLeaseStore();
14163
+ const holder = `${getReplicaId6()}#${uuidv42()}`;
14164
+ if (!await store2.tryAcquire(EVAL_EXECUTION_LEASE, holder, EVAL_LEASE_TTL_MS)) {
14165
+ throw new Error("An evaluation run is already in progress");
14166
+ }
14167
+ const heartbeat = setInterval(() => {
14168
+ void store2.renew(EVAL_EXECUTION_LEASE, holder, EVAL_LEASE_TTL_MS).catch(() => void 0);
14169
+ }, Math.max(1e3, Math.floor(EVAL_LEASE_TTL_MS / 3)));
14170
+ heartbeat.unref?.();
14171
+ return {
14172
+ release: async () => {
14173
+ clearInterval(heartbeat);
14174
+ await store2.release(EVAL_EXECUTION_LEASE, holder).catch(() => void 0);
14175
+ }
14176
+ };
14177
+ }
14178
+ getLeaseStore() {
14179
+ return getStoreLattice20("default", "lease").store;
14180
+ }
14181
+ async startLeasedRun(tenantId, projectId, suiteIds, caseIds, runConfig, taskId, leaseKey, leaseToken, releaseExecutionLease) {
13545
14182
  const store2 = this.getEvalStore();
13546
14183
  const project = await store2.getProjectById(tenantId, projectId);
13547
14184
  if (!project) throw new Error("Project not found");
@@ -13719,6 +14356,12 @@ var EvalRunner = class {
13719
14356
  });
13720
14357
  };
13721
14358
  const runPromise = (async () => {
14359
+ const cancelPoll = setInterval(() => {
14360
+ void Promise.resolve(this.getEvalStore().getRunById(tenantId, runId)).then((latest) => {
14361
+ if (latest?.cancelRequested) abortController.abort();
14362
+ }).catch(() => void 0);
14363
+ }, 1e3);
14364
+ cancelPoll.unref?.();
13722
14365
  try {
13723
14366
  const evalProject = new LatticeEvalProject(projectConfig, onCaseComplete);
13724
14367
  const calibration = await evalProject.calibrateJudge();
@@ -13744,10 +14387,11 @@ var EvalRunner = class {
13744
14387
  });
13745
14388
  return { report };
13746
14389
  } catch (err) {
13747
- const errorMsg = err.message;
14390
+ const latest = await Promise.resolve(this.getEvalStore().getRunById(tenantId, runId)).catch(() => null);
14391
+ const errorMsg = latest?.cancelRequested ? "Run aborted" : err.message;
13748
14392
  try {
13749
14393
  await persistTerminal({
13750
- status: "failed",
14394
+ status: latest?.cancelRequested ? "aborted" : "failed",
13751
14395
  error: errorMsg,
13752
14396
  completedAt: /* @__PURE__ */ new Date()
13753
14397
  }, {
@@ -13759,11 +14403,13 @@ var EvalRunner = class {
13759
14403
  }
13760
14404
  throw err;
13761
14405
  } finally {
14406
+ clearInterval(cancelPoll);
13762
14407
  terminal.workerSettled = true;
13763
14408
  if (terminal.persisted) {
13764
14409
  this.runs.delete(runId);
13765
14410
  this.releaseProjectLease(leaseKey, leaseToken);
13766
14411
  }
14412
+ await releaseExecutionLease();
13767
14413
  }
13768
14414
  })();
13769
14415
  this.runs.set(runId, { runId, projectId, tenantId, abortController, promise: runPromise, terminal, leaseKey, leaseToken, taskId });
@@ -13773,10 +14419,16 @@ var EvalRunner = class {
13773
14419
  }
13774
14420
  async abortRun(runId, tenantId) {
13775
14421
  const ctx = this.runs.get(runId);
13776
- if (!ctx || tenantId !== void 0 && ctx.tenantId !== tenantId) return false;
14422
+ if (ctx && tenantId !== void 0 && ctx.tenantId !== tenantId) return false;
14423
+ if (!ctx) {
14424
+ if (!tenantId) return false;
14425
+ const store3 = this.getEvalStore();
14426
+ return store3.requestCancel ? await Promise.resolve(store3.requestCancel(tenantId, runId)).catch(() => false) === true : false;
14427
+ }
13777
14428
  if (ctx.terminal.claiming || ctx.terminal.persisted) return false;
13778
- ctx.abortController.abort();
13779
14429
  const store2 = this.getEvalStore();
14430
+ void Promise.resolve(store2.requestCancel?.(ctx.tenantId, runId)).catch(() => false);
14431
+ ctx.abortController.abort();
13780
14432
  ctx.terminal.claiming = true;
13781
14433
  try {
13782
14434
  let lastError;
@@ -14253,8 +14905,8 @@ function registerEvalRoutes(app2) {
14253
14905
  const tenantId = getTenantId19(request);
14254
14906
  const { rid } = request.params;
14255
14907
  const store2 = getEvalStore();
14256
- const ownedRun = await store2.getRunById(tenantId, rid);
14257
- if (!ownedRun) return reply.status(404).send({ success: false, message: "Run not found" });
14908
+ const initial = await store2.getRunById(tenantId, rid);
14909
+ if (!initial) return reply.status(404).send({ success: false, message: "Run not found" });
14258
14910
  reply.hijack();
14259
14911
  reply.raw.writeHead(200, {
14260
14912
  "Content-Type": "text/event-stream",
@@ -14262,67 +14914,74 @@ function registerEvalRoutes(app2) {
14262
14914
  Connection: "keep-alive",
14263
14915
  "Access-Control-Allow-Origin": "*"
14264
14916
  });
14265
- const emitter = evalRunner.getEventEmitter();
14266
- const eventKey = `run:${rid}`;
14267
- let settled = false;
14917
+ let closed = false;
14918
+ let finished = false;
14919
+ const seen = /* @__PURE__ */ new Set();
14920
+ let timer;
14268
14921
  const cleanup = () => {
14269
- emitter.off(eventKey, handler);
14270
- request.raw.off("close", disconnect);
14271
- };
14272
- const disconnect = () => {
14273
- if (settled) return;
14274
- settled = true;
14275
- cleanup();
14922
+ if (closed) return;
14923
+ closed = true;
14924
+ if (timer) clearInterval(timer);
14925
+ request.raw.off("close", cleanup);
14276
14926
  };
14277
- const finish = (type, data) => {
14278
- if (settled) return;
14279
- settled = true;
14280
- cleanup();
14281
- reply.raw.write(`event: ${type}
14927
+ const write = (event, data) => {
14928
+ if (closed) return;
14929
+ reply.raw.write(`event: ${event}
14282
14930
  data: ${JSON.stringify(data)}
14283
14931
 
14284
14932
  `);
14933
+ };
14934
+ const finish = (event, data) => {
14935
+ if (finished) return;
14936
+ finished = true;
14937
+ write(event, data);
14938
+ cleanup();
14285
14939
  reply.raw.end();
14286
14940
  };
14287
- const handler = (event) => {
14288
- if (event.type === "completed" || event.type === "error") {
14289
- finish(event.type, event.data);
14941
+ const poll = async () => {
14942
+ if (closed || finished) return;
14943
+ let run2;
14944
+ let results;
14945
+ try {
14946
+ run2 = await store2.getRunById(tenantId, rid);
14947
+ if (!run2) {
14948
+ finish("error", { message: "Run not found" });
14949
+ return;
14950
+ }
14951
+ results = await store2.getResultsByRun(tenantId, rid);
14952
+ } catch (error) {
14290
14953
  return;
14291
14954
  }
14292
- if (!settled) reply.raw.write(`event: ${event.type}
14293
- data: ${JSON.stringify(event.data)}
14294
-
14295
- `);
14955
+ for (const result of results) {
14956
+ if (seen.has(result.id)) continue;
14957
+ seen.add(result.id);
14958
+ write("progress", {
14959
+ suiteName: result.suiteName,
14960
+ caseId: result.caseId,
14961
+ pass: result.pass,
14962
+ score: result.score,
14963
+ interrupted: result.interrupted ?? false,
14964
+ completed: run2.passedCases + run2.failedCases,
14965
+ total: run2.totalCases
14966
+ });
14967
+ }
14968
+ if (run2.status === "completed") {
14969
+ finish("completed", {
14970
+ passed: run2.passedCases,
14971
+ failed: run2.failedCases,
14972
+ avgScore: run2.avgScore
14973
+ });
14974
+ } else if (run2.status === "failed") {
14975
+ finish("error", { message: run2.error ?? "Evaluation failed" });
14976
+ } else if (run2.status === "aborted") {
14977
+ finish("error", { message: "Run aborted" });
14978
+ }
14296
14979
  };
14297
- emitter.on(eventKey, handler);
14298
- request.raw.on("close", disconnect);
14299
- let reconciledRun;
14300
- try {
14301
- reconciledRun = await store2.getRunById(tenantId, rid);
14302
- } catch (error) {
14303
- finish("error", { message: error.message });
14304
- return;
14305
- }
14306
- if (settled) return;
14307
- if (!reconciledRun) {
14308
- cleanup();
14309
- reply.raw.end();
14310
- return;
14311
- }
14312
- if (reconciledRun.status === "completed") {
14313
- finish("completed", {
14314
- passed: reconciledRun.passedCases,
14315
- failed: reconciledRun.failedCases,
14316
- avgScore: reconciledRun.avgScore
14317
- });
14318
- } else if (reconciledRun.status === "failed") {
14319
- finish("error", { message: reconciledRun.error ?? "Evaluation failed" });
14320
- } else if (reconciledRun.status === "aborted") {
14321
- finish("error", { message: "Run aborted" });
14322
- } else if (!evalRunner.isRunning(rid, tenantId)) {
14323
- cleanup();
14324
- reply.raw.end();
14325
- }
14980
+ request.raw.on("close", cleanup);
14981
+ await poll();
14982
+ if (!closed && !finished) timer = setInterval(() => {
14983
+ void poll();
14984
+ }, 1e3);
14326
14985
  });
14327
14986
  app2.post("/api/eval/runs/:rid/abort", async (request, reply) => {
14328
14987
  try {
@@ -14332,7 +14991,7 @@ data: ${JSON.stringify(event.data)}
14332
14991
  if (!run2) return reply.status(404).send({ success: false, message: "Run not found" });
14333
14992
  const aborted = await evalRunner.abortRun(rid, tenantId);
14334
14993
  if (!aborted) return reply.status(404).send({ success: false, message: "Run not found or not running" });
14335
- reply.send({ success: true, message: "Run aborted" });
14994
+ reply.status(202).send({ success: true, message: "Abort requested", data: { runId: rid } });
14336
14995
  } catch (err) {
14337
14996
  reply.status(500).send({ success: false, message: err.message });
14338
14997
  }
@@ -14945,10 +15604,10 @@ function bypassesConsoleAuth(request) {
14945
15604
  const path5 = request.url.split("?")[0];
14946
15605
  const method = request.method;
14947
15606
  if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path5) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path5) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/files\/(viewfile|downloadfile)$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/api\/assistants\/[^/]+\/threads\/[^/]+\/sandbox\/(viewfile|downloadfile)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+\/join$/.test(path5) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime(?:\/api)?\/assistants\/[^/]+\/threads\/[^/]+\/runs\/[^/]+\/cancel$/.test(path5) || method === "GET" && /^\/api\/web-apps\/[^/]+\/chat$/.test(path5) || method === "GET" && /^\/embed\/[^/]+$/.test(path5) || method === "GET" && path5 === "/mcp-apps/sandbox.html" || (method === "POST" || method === "DELETE") && path5 === "/open/mcp" || (method === "POST" || method === "GET") && (path5 === "/mcp" || path5 === "/mcp/") || method === "POST" && path5 === "/open/session" || (method === "GET" || method === "POST") && path5.startsWith("/open/sandbox/files/")) return true;
14948
- if (request.method === "GET" && (/^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path5) || /^\/a2a\/[^/]+\/\.well-known\/agent-card\.json$/.test(path5))) {
15607
+ if (request.method === "GET" && (/^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path5) || /^\/a2a\/(?:v1\/)?[^/]+\/\.well-known\/agent-card\.json$/.test(path5))) {
14949
15608
  return true;
14950
15609
  }
14951
- return request.method === "POST" && /^\/api\/a2a\/agents\/[^/]+\/jsonrpc$/.test(path5) || request.method === "POST" && /^\/a2a\/[^/]+$/.test(path5);
15610
+ return request.method === "POST" && /^\/api\/a2a\/agents\/[^/]+\/jsonrpc$/.test(path5) || request.method === "POST" && /^\/a2a\/(?:v1\/)?[^/]+$/.test(path5);
14952
15611
  }
14953
15612
  function registerConsoleAuthHook(app2, options) {
14954
15613
  app2.addHook("preHandler", async (request, reply) => {
@@ -15053,7 +15712,7 @@ function normalizeChatType(chatType) {
15053
15712
 
15054
15713
  // src/channels/lark/LarkChannelAdapter.ts
15055
15714
  import * as Lark from "@larksuiteoapi/node-sdk";
15056
- var logger = new Logger({ serviceName: "lattice/gateway/lark" });
15715
+ var logger2 = new Logger({ serviceName: "lattice/gateway/lark" });
15057
15716
  var activeConnections = /* @__PURE__ */ new Map();
15058
15717
  function parseTextContent(content) {
15059
15718
  if (!content) return null;
@@ -15141,21 +15800,21 @@ var larkChannelAdapter = {
15141
15800
  async connect(installation, deps) {
15142
15801
  const { id: installationId, tenantId, config } = installation;
15143
15802
  if (!config.appId || !config.appSecret) {
15144
- logger.warn("Lark installation missing credentials, skipping", { installationId });
15803
+ logger2.warn("Lark installation missing credentials, skipping", { installationId });
15145
15804
  return;
15146
15805
  }
15147
15806
  if (activeConnections.has(installationId)) {
15148
- logger.warn("Lark WS already connected for installation, skipping", { installationId });
15807
+ logger2.warn("Lark WS already connected for installation, skipping", { installationId });
15149
15808
  return;
15150
15809
  }
15151
- logger.info("Lark WS client starting", { installationId, tenantId });
15810
+ logger2.info("Lark WS client starting", { installationId, tenantId });
15152
15811
  const router = deps?.router;
15153
15812
  const eventDispatcher = new Lark.EventDispatcher({}).register({
15154
15813
  "im.message.receive_v1": async (data) => {
15155
15814
  try {
15156
15815
  const inbound = wsEventToInbound(data, installationId, tenantId);
15157
15816
  if (!inbound) return;
15158
- logger.info("Lark WS message received", {
15817
+ logger2.info("Lark WS message received", {
15159
15818
  installationId,
15160
15819
  senderId: inbound.sender.id,
15161
15820
  chatId: data.message?.chat_id
@@ -15163,14 +15822,14 @@ var larkChannelAdapter = {
15163
15822
  if (router) {
15164
15823
  const result = await router.dispatch(inbound);
15165
15824
  if (!result.success) {
15166
- logger.warn("Lark WS dispatch failed", {
15825
+ logger2.warn("Lark WS dispatch failed", {
15167
15826
  installationId,
15168
15827
  error: result.error?.message
15169
15828
  });
15170
15829
  }
15171
15830
  }
15172
15831
  } catch (err) {
15173
- logger.error("Lark WS event handler error", {
15832
+ logger2.error("Lark WS event handler error", {
15174
15833
  installationId,
15175
15834
  error: err instanceof Error ? err.message : String(err)
15176
15835
  });
@@ -15184,25 +15843,25 @@ var larkChannelAdapter = {
15184
15843
  });
15185
15844
  await client.start({ eventDispatcher });
15186
15845
  activeConnections.set(installationId, client);
15187
- logger.info("Lark WS client connected", { installationId });
15846
+ logger2.info("Lark WS client connected", { installationId });
15188
15847
  },
15189
15848
  async disconnect(installationId) {
15190
15849
  const client = activeConnections.get(installationId);
15191
15850
  if (!client) {
15192
- logger.warn("Lark WS not connected, nothing to disconnect", { installationId });
15851
+ logger2.warn("Lark WS not connected, nothing to disconnect", { installationId });
15193
15852
  return;
15194
15853
  }
15195
- logger.info("Lark WS client disconnecting", { installationId });
15854
+ logger2.info("Lark WS client disconnecting", { installationId });
15196
15855
  try {
15197
15856
  client.close({ force: true });
15198
15857
  } catch (err) {
15199
- logger.error("Lark WS client stop error", {
15858
+ logger2.error("Lark WS client stop error", {
15200
15859
  installationId,
15201
15860
  error: err instanceof Error ? err.message : String(err)
15202
15861
  });
15203
15862
  }
15204
15863
  activeConnections.delete(installationId);
15205
- logger.info("Lark WS client disconnected", { installationId });
15864
+ logger2.info("Lark WS client disconnected", { installationId });
15206
15865
  }
15207
15866
  };
15208
15867
 
@@ -15267,7 +15926,7 @@ function verifyLarkRequestSignature(request, config) {
15267
15926
  }
15268
15927
 
15269
15928
  // src/channels/lark/controller.ts
15270
- var logger2 = new Logger({ serviceName: "lattice/gateway/lark" });
15929
+ var logger3 = new Logger({ serviceName: "lattice/gateway/lark" });
15271
15930
  function createLarkEventHandler(deps) {
15272
15931
  return async function handleLarkEvent(request, reply) {
15273
15932
  const { installationId } = request.params;
@@ -15298,7 +15957,7 @@ function createLarkEventHandler(deps) {
15298
15957
  return;
15299
15958
  }
15300
15959
  deps.router.dispatch(inboundMessage).catch((error) => {
15301
- logger2.error("Lark message dispatch error", {
15960
+ logger3.error("Lark message dispatch error", {
15302
15961
  error: error instanceof Error ? error.message : String(error)
15303
15962
  });
15304
15963
  });
@@ -15319,21 +15978,9 @@ function registerLarkChannelRoutes(app2, deps) {
15319
15978
  }
15320
15979
 
15321
15980
  // src/channels/wechat/controller.ts
15322
- var qrSessions = /* @__PURE__ */ new Map();
15323
- var SESSION_TTL_MS = 5 * 60 * 1e3;
15324
- function cleanupExpiredSessions() {
15325
- const now = Date.now();
15326
- for (const [key, session] of qrSessions) {
15327
- if (now - session.createdAt > SESSION_TTL_MS) {
15328
- qrSessions.delete(key);
15329
- }
15330
- }
15331
- }
15332
15981
  async function handleGetQrCode(_request, reply) {
15333
15982
  try {
15334
- cleanupExpiredSessions();
15335
15983
  const { qrcode, qrcodeImgUrl } = await getQrCode();
15336
- qrSessions.set(qrcode, { qrcode, createdAt: Date.now() });
15337
15984
  reply.send({ success: true, data: { qrcode, qrcodeImgUrl } });
15338
15985
  } catch (err) {
15339
15986
  reply.status(500).send({
@@ -15348,11 +15995,6 @@ async function handleGetQrCodeStatus(request, reply) {
15348
15995
  reply.status(400).send({ success: false, message: "qrcode is required" });
15349
15996
  return;
15350
15997
  }
15351
- const session = qrSessions.get(qrcode);
15352
- if (!session) {
15353
- reply.status(404).send({ success: false, message: "Session not found or expired" });
15354
- return;
15355
- }
15356
15998
  try {
15357
15999
  const result = await getQrCodeStatus(qrcode);
15358
16000
  reply.send({ success: true, data: result });
@@ -15390,9 +16032,19 @@ import {
15390
16032
  import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
15391
16033
  var _adapterRegistry;
15392
16034
  var _router;
16035
+ var _onInstallationChanged;
15393
16036
  function setChannelControllerDeps(deps) {
15394
16037
  _adapterRegistry = deps.adapterRegistry;
15395
16038
  _router = deps.router;
16039
+ _onInstallationChanged = deps.onInstallationChanged;
16040
+ }
16041
+ async function notifyInstallationChanged(tenantId, installationId) {
16042
+ if (!_onInstallationChanged) return;
16043
+ try {
16044
+ await _onInstallationChanged({ tenantId, installationId });
16045
+ } catch (error) {
16046
+ console.error("Failed to notify channel installation change:", error);
16047
+ }
15396
16048
  }
15397
16049
  function getTenantId20(request) {
15398
16050
  const userTenantId = request.user?.tenantId;
@@ -15608,7 +16260,9 @@ async function createChannelInstallation(request, reply) {
15608
16260
  installationId,
15609
16261
  createInput
15610
16262
  );
15611
- if (body.enabled !== false && _adapterRegistry) {
16263
+ if (_onInstallationChanged) {
16264
+ await notifyInstallationChanged(tenantId, installationId);
16265
+ } else if (body.enabled !== false && _adapterRegistry) {
15612
16266
  const adapter = _adapterRegistry.get(body.channel);
15613
16267
  if (adapter?.connect) {
15614
16268
  try {
@@ -15663,6 +16317,19 @@ async function updateChannelInstallation(request, reply) {
15663
16317
  reply.code(400);
15664
16318
  return { success: false, message: "Invalid request body" };
15665
16319
  }
16320
+ if (_onInstallationChanged) {
16321
+ const installation2 = await store2.updateInstallation(tenantId, installationId, body);
16322
+ if (!installation2) {
16323
+ reply.code(404);
16324
+ return { success: false, message: "Channel installation not found" };
16325
+ }
16326
+ await notifyInstallationChanged(tenantId, installationId);
16327
+ return {
16328
+ success: true,
16329
+ message: "Channel installation updated successfully",
16330
+ data: installation2
16331
+ };
16332
+ }
15666
16333
  const adapter = _adapterRegistry?.get(existing.channel);
15667
16334
  const disconnect = adapter?.disconnect;
15668
16335
  const connect = adapter?.connect;
@@ -15782,7 +16449,7 @@ async function deleteChannelInstallation(request, reply) {
15782
16449
  message: "Access denied"
15783
16450
  };
15784
16451
  }
15785
- if (_adapterRegistry) {
16452
+ if (!_onInstallationChanged && _adapterRegistry) {
15786
16453
  const adapter = _adapterRegistry.get(existing.channel);
15787
16454
  if (adapter?.disconnect) {
15788
16455
  try {
@@ -15800,6 +16467,7 @@ async function deleteChannelInstallation(request, reply) {
15800
16467
  message: "Channel installation not found"
15801
16468
  };
15802
16469
  }
16470
+ await notifyInstallationChanged(tenantId, installationId);
15803
16471
  return {
15804
16472
  success: true,
15805
16473
  message: "Channel installation deleted successfully"
@@ -16318,42 +16986,6 @@ function registerA2ABridgeRoutes(app2) {
16318
16986
 
16319
16987
  // src/routes/stt.ts
16320
16988
  import { sttModelLatticeManager } from "@axiom-lattice/core";
16321
-
16322
- // src/controllers/tenant-scope.ts
16323
- var TenantScopeError2 = class extends Error {
16324
- /** Creates a tenant scope error. */
16325
- constructor(code, statusCode, message) {
16326
- super(message);
16327
- this.code = code;
16328
- this.statusCode = statusCode;
16329
- this.name = "TenantScopeError";
16330
- }
16331
- };
16332
- function resolveTenantScope(request, authRequired2) {
16333
- const authenticated = request.user?.tenantId;
16334
- const header2 = request.headers["x-tenant-id"];
16335
- const requested = Array.isArray(header2) ? header2[0] : header2;
16336
- if (authenticated) {
16337
- if (requested && requested !== authenticated) {
16338
- throw new TenantScopeError2(
16339
- "TENANT_ACCESS_DENIED",
16340
- 403,
16341
- "Tenant access denied"
16342
- );
16343
- }
16344
- return { tenantId: authenticated, userId: request.user?.id };
16345
- }
16346
- if (authRequired2) {
16347
- throw new TenantScopeError2(
16348
- "UNAUTHENTICATED",
16349
- 401,
16350
- "Console session required"
16351
- );
16352
- }
16353
- return { tenantId: requested || "default" };
16354
- }
16355
-
16356
- // src/routes/stt.ts
16357
16989
  function tenantIdOf(request) {
16358
16990
  try {
16359
16991
  return resolveTenantScope(request, false).tenantId;
@@ -16719,7 +17351,7 @@ import {
16719
17351
  createTaskLifecycleService,
16720
17352
  createTrustedAgentMessageDispatcher,
16721
17353
  executionAuthorityRegistry,
16722
- getReplicaId as getReplicaId9,
17354
+ getReplicaId as getReplicaId10,
16723
17355
  getStoreLattice as getStoreLattice28,
16724
17356
  hasModelLatticeForTenant as hasModelLatticeForTenant3,
16725
17357
  ManagedTaskStartError,
@@ -17393,6 +18025,8 @@ var ProjectTaskRunPlanner = class {
17393
18025
  throw asPlanError(error);
17394
18026
  }
17395
18027
  const inputId = (this.deps.idFactory ?? randomUUID20)();
18028
+ const actor3 = input.actor ?? SYSTEM_ACTOR;
18029
+ const userId = actor3.startsWith("user:") && actor3.length > "user:".length ? actor3.slice("user:".length) : void 0;
17396
18030
  const trustedContext = {
17397
18031
  projectTask: buildTrustedTaskContext(task, thread, inputId, rosterExempt)
17398
18032
  };
@@ -17404,12 +18038,16 @@ var ProjectTaskRunPlanner = class {
17404
18038
  projectId: task.projectId,
17405
18039
  assistantId: task.ownerId,
17406
18040
  threadId: thread.id,
17407
- config: { taskId: task.id, execution: { taskId: task.id } },
18041
+ config: {
18042
+ taskId: task.id,
18043
+ execution: { taskId: task.id },
18044
+ ...userId ? { user_id: userId } : {}
18045
+ },
17408
18046
  metadata: {
17409
18047
  taskId: task.id,
17410
18048
  projectId: task.projectId,
17411
18049
  workspaceId: task.workspaceId,
17412
- actor: input.actor ?? SYSTEM_ACTOR,
18050
+ actor: actor3,
17413
18051
  mode: input.mode
17414
18052
  }
17415
18053
  };
@@ -18375,7 +19013,7 @@ function getTenantId24(request) {
18375
19013
  if (typeof value !== "string" || !value) throw new HttpTaskError(400, "INVALID_TASK_INPUT");
18376
19014
  return value;
18377
19015
  }
18378
- function getUserId4(request) {
19016
+ function getUserId6(request) {
18379
19017
  const user = request.user;
18380
19018
  const value = user?.id ?? request.headers["x-user-id"];
18381
19019
  if (typeof value !== "string" || !value) throw new HttpTaskError(400, "INVALID_TASK_INPUT", "User ID is required");
@@ -18541,7 +19179,7 @@ function withErrors(handler) {
18541
19179
  }
18542
19180
  async function authorizeRead(request, task, authorize) {
18543
19181
  if (!task.projectId || task.projectId === "default") {
18544
- if (task.ownerType !== "user" || task.ownerId !== getUserId4(request)) throw notFound6();
19182
+ if (task.ownerType !== "user" || task.ownerId !== getUserId6(request)) throw notFound6();
18545
19183
  return;
18546
19184
  }
18547
19185
  let project;
@@ -18555,7 +19193,7 @@ async function authorizeRead(request, task, authorize) {
18555
19193
  }
18556
19194
  async function authorizeCommand(request, task, authorize, capability2) {
18557
19195
  if (!task.projectId || task.projectId === "default") {
18558
- if (task.ownerType !== "user" || task.ownerId !== getUserId4(request)) throw notFound6();
19196
+ if (task.ownerType !== "user" || task.ownerId !== getUserId6(request)) throw notFound6();
18559
19197
  return;
18560
19198
  }
18561
19199
  let project;
@@ -18568,7 +19206,7 @@ async function authorizeCommand(request, task, authorize, capability2) {
18568
19206
  if (task.workspaceId !== project.workspaceId) throw new HttpTaskError(404, "TASK_NOT_FOUND");
18569
19207
  }
18570
19208
  async function authorizeMutation(request, task, authorize) {
18571
- const capability2 = task.ownerType === "agent" || task.ownerId !== getUserId4(request) ? "manage" : "send";
19209
+ const capability2 = task.ownerType === "agent" || task.ownerId !== getUserId6(request) ? "manage" : "send";
18572
19210
  return authorizeCommand(request, task, authorize, capability2);
18573
19211
  }
18574
19212
  async function preauthorizeExplicitProject(request, authorize) {
@@ -18612,7 +19250,7 @@ function createTaskController(deps) {
18612
19250
  const mutationMutex = deps.projectMutationMutex ?? projectMutationMutex;
18613
19251
  const authorize = async (request, projectId, capability2) => deps.access.requireProjectAccess({
18614
19252
  tenantId: getTenantId24(request),
18615
- userId: getUserId4(request),
19253
+ userId: getUserId6(request),
18616
19254
  projectId,
18617
19255
  capability: capability2
18618
19256
  });
@@ -18630,7 +19268,7 @@ function createTaskController(deps) {
18630
19268
  };
18631
19269
  const list = withErrors(async (request) => {
18632
19270
  const tenantId = getTenantId24(request);
18633
- const userId = getUserId4(request);
19271
+ const userId = getUserId6(request);
18634
19272
  const query = exactObject(request.query, ["projectId", "status", "priority", "limit", "offset", "ownerType", "ownerId", "parentId", "metadata"]);
18635
19273
  const projectId = typeof query.projectId === "string" ? query.projectId : void 0;
18636
19274
  const limit = integer(query.limit, 1);
@@ -18709,7 +19347,7 @@ function createTaskController(deps) {
18709
19347
  const create = withErrors(async (request, reply) => {
18710
19348
  const body = snapshotCreate(request.body);
18711
19349
  const tenantId = getTenantId24(request);
18712
- const userId = getUserId4(request);
19350
+ const userId = getUserId6(request);
18713
19351
  const governed = governedCreate(body);
18714
19352
  if (governed) return reply.code(409).send({ success: false, code: governed });
18715
19353
  if (!body.projectId || body.projectId === "default") {
@@ -18785,7 +19423,7 @@ function createTaskController(deps) {
18785
19423
  await authorizeMutation(request, current3, authorize);
18786
19424
  return lockedTask(request, current3, async (locked) => {
18787
19425
  if (deps.commands) {
18788
- const actor3 = `user:${getUserId4(request)}`;
19426
+ const actor3 = `user:${getUserId6(request)}`;
18789
19427
  await deps.commands.run(locked, { type: "delete", actor: actor3 });
18790
19428
  return { success: true, message: "Task deleted" };
18791
19429
  }
@@ -18826,7 +19464,7 @@ function createTaskController(deps) {
18826
19464
  if (body.expectedUpdatedAt && body.expectedUpdatedAt !== locked.updatedAt.toISOString()) {
18827
19465
  throw new HttpTaskError(409, "TASK_STATUS_CONFLICT");
18828
19466
  }
18829
- const result = await deps.commands.run(locked, { ...body, actor: `user:${getUserId4(request)}` });
19467
+ const result = await deps.commands.run(locked, { ...body, actor: `user:${getUserId6(request)}` });
18830
19468
  return { success: true, data: result.task, warnings: result.warnings };
18831
19469
  }, capability2);
18832
19470
  });
@@ -19172,8 +19810,8 @@ import { z as z5, ZodError as ZodError2 } from "zod";
19172
19810
 
19173
19811
  // src/services/agent-web-app/AgentWebAppRuntimeService.ts
19174
19812
  import { createHash as createHash5, randomUUID as randomUUID22 } from "crypto";
19175
- import { MessageChunkTypes as MessageChunkTypes4, parseAgentWebAppGenUIBlock, buildAffinityMissChunk as buildAffinityMissChunk2 } from "@axiom-lattice/protocols";
19176
- import { getReplicaId as getReplicaId6, getThreadRunOwner as getThreadRunOwner2, normalizeAgentInterrupts as normalizeAgentInterrupts2 } from "@axiom-lattice/core";
19813
+ import { MessageChunkTypes as MessageChunkTypes3, parseAgentWebAppGenUIBlock, buildAffinityMissChunk as buildAffinityMissChunk2 } from "@axiom-lattice/protocols";
19814
+ import { getReplicaId as getReplicaId7, getThreadRunOwner as getThreadRunOwner2, normalizeAgentInterrupts as normalizeAgentInterrupts2 } from "@axiom-lattice/core";
19177
19815
  function isRecord9(value) {
19178
19816
  return typeof value === "object" && value !== null && !Array.isArray(value);
19179
19817
  }
@@ -19353,7 +19991,7 @@ var AgentWebAppRuntimeService = class {
19353
19991
  async enqueueMessage(webAppId, userId, threadId, content, selection) {
19354
19992
  const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
19355
19993
  const agent = this.resolveAgent(runtime, threadId);
19356
- const execution = await this.execute(agent, runtime, { input: { message: content } });
19994
+ const execution = await this.execute(agent, runtime, userId, { input: { message: content } });
19357
19995
  return { messageId: execution.messageId };
19358
19996
  }
19359
19997
  async removePendingMessage(webAppId, userId, threadId, messageId, selection) {
@@ -19372,7 +20010,7 @@ var AgentWebAppRuntimeService = class {
19372
20010
  assistantId: runtime.webApp.assistantId,
19373
20011
  threadId
19374
20012
  });
19375
- if (owner && owner !== getReplicaId6()) {
20013
+ if (owner && owner !== getReplicaId7()) {
19376
20014
  return {
19377
20015
  stream: (async function* () {
19378
20016
  yield buildAffinityMissChunk2(owner, threadId);
@@ -19400,7 +20038,7 @@ var AgentWebAppRuntimeService = class {
19400
20038
  const content = refs.length ? `${input.content}
19401
20039
 
19402
20040
  ${formatFileRefsSection(refs)}` : input.content;
19403
- const execution = await this.execute(agent, runtime, { input: { message: content } });
20041
+ const execution = await this.execute(agent, runtime, userId, { input: { message: content } });
19404
20042
  return { ...execution, allowInterrupts: runtime.webApp.features.hitl, allowGenUI: runtime.webApp.features.genUI };
19405
20043
  }
19406
20044
  async abort(webAppId, userId, threadId, selection) {
@@ -19503,17 +20141,21 @@ ${formatFileRefsSection(refs)}` : input.content;
19503
20141
  );
19504
20142
  if (matched.conflict) this.fail("INVALID_REQUEST", 409, "Interrupt is ambiguous");
19505
20143
  if (!matched.interrupt) this.fail("INVALID_REQUEST", 400, "Interrupt not found");
19506
- const execution = await this.execute(agent, runtime, {
20144
+ const execution = await this.execute(agent, runtime, userId, {
19507
20145
  input: { message: input.message },
19508
20146
  command: resumeMap(interruptId, input.response)
19509
20147
  });
19510
20148
  return { ...execution, allowInterrupts: true, allowGenUI: runtime.webApp.features.genUI };
19511
20149
  }
19512
- execute(agent, runtime, request) {
20150
+ execute(agent, runtime, userId, request) {
20151
+ const customRunConfig = {
20152
+ ...runtime.modelKey ? { modelConfig: { modelKey: runtime.modelKey } } : {},
20153
+ ...userId ? { user_id: userId } : {}
20154
+ };
19513
20155
  return executeScopedAgentMessage(agent, {
19514
20156
  ...request,
19515
- ...runtime.modelKey ? { custom_run_config: { modelConfig: { modelKey: runtime.modelKey } } } : {}
19516
- }, [MessageChunkTypes4.MESSAGE_COMPLETED, MessageChunkTypes4.INTERRUPT, MessageChunkTypes4.MESSAGE_FAILED]);
20157
+ ...Object.keys(customRunConfig).length > 0 ? { custom_run_config: customRunConfig } : {}
20158
+ }, [MessageChunkTypes3.MESSAGE_COMPLETED, MessageChunkTypes3.INTERRUPT, MessageChunkTypes3.MESSAGE_FAILED]);
19517
20159
  }
19518
20160
  async resolveRuntime(webAppId, selection) {
19519
20161
  const webApp = await this.deps.webAppStore.findById(webAppId);
@@ -19778,7 +20420,7 @@ async function verifyWebAppToken(token, issuer, options = {}) {
19778
20420
 
19779
20421
  // src/routes/agent-web-app-runtime.ts
19780
20422
  import { createAgentWebAppStreamProjectionContext, projectAgentWebAppChunk, AGENT_AFFINITY_HEADER as AGENT_AFFINITY_HEADER2, AFFINITY_ECHO_HEADER as AFFINITY_ECHO_HEADER2, affinityEchoHeaders as affinityEchoHeaders2, REPLICA_HEADER as REPLICA_HEADER2 } from "@axiom-lattice/protocols";
19781
- import { getReplicaId as getReplicaId7 } from "@axiom-lattice/core";
20423
+ import { getReplicaId as getReplicaId8 } from "@axiom-lattice/core";
19782
20424
  var idSchema = z5.string().min(1).max(128);
19783
20425
  var paramsSchema = z5.object({ webAppId: idSchema }).strict();
19784
20426
  var threadParamsSchema = z5.object({ webAppId: idSchema, threadId: idSchema }).strict();
@@ -19804,7 +20446,7 @@ var resumeBodySchema = z5.object({
19804
20446
  z5.object({ approved: z5.boolean(), feedback: z5.string().max(4096).optional() }).strict()
19805
20447
  ])
19806
20448
  }).strict();
19807
- var MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
20449
+ var MAX_ATTACHMENT_BYTES = 500 * 1024 * 1024;
19808
20450
  var stateQuerySchema = z5.object({
19809
20451
  threadId: idSchema,
19810
20452
  projectId: idSchema.optional(),
@@ -20355,7 +20997,7 @@ function registerAgentWebAppRuntimeRoutes(app2, deps) {
20355
20997
  if (!threadId) throw new AgentWebAppRuntimeError("INVALID_REQUEST", 400, "thread_id is required");
20356
20998
  const content = body.messages.map((message) => message.content).join("\n");
20357
20999
  const { messageId } = await deps.service.enqueueMessage(webAppId, identity.userId, threadId, content, { projectId: body.projectId, modelKey: body.modelKey });
20358
- reply.header(REPLICA_HEADER2, getReplicaId7());
21000
+ reply.header(REPLICA_HEADER2, getReplicaId8());
20359
21001
  for (const [name, value] of Object.entries(affinityEchoHeaders2(request.headers[AGENT_AFFINITY_HEADER2]))) {
20360
21002
  reply.header(name, value);
20361
21003
  }
@@ -20441,7 +21083,7 @@ function beginSse(reply, request) {
20441
21083
  "X-Accel-Buffering": "no",
20442
21084
  "Access-Control-Allow-Origin": "*",
20443
21085
  "Access-Control-Expose-Headers": `${REPLICA_HEADER2}, ${AFFINITY_ECHO_HEADER2}`,
20444
- [REPLICA_HEADER2]: getReplicaId7(),
21086
+ [REPLICA_HEADER2]: getReplicaId8(),
20445
21087
  ...affinityEchoHeaders2(request.headers[AGENT_AFFINITY_HEADER2])
20446
21088
  });
20447
21089
  }
@@ -20886,7 +21528,7 @@ var CapabilityBundleService = class {
20886
21528
  };
20887
21529
 
20888
21530
  // src/controllers/capability-bundles.ts
20889
- var logger3 = new Logger({ serviceName: "lattice/gateway/capability-bundles" });
21531
+ var logger4 = new Logger({ serviceName: "lattice/gateway/capability-bundles" });
20890
21532
  var capability = z8.unknown();
20891
21533
  var createSchema = z8.object({ name: z8.string(), description: z8.string().optional(), capabilities: z8.array(capability) }).strict();
20892
21534
  var updateSchema = createSchema.partial().extend({ expectedUpdatedAt: z8.string().optional() });
@@ -20904,14 +21546,14 @@ function tenant(request, authRequired2) {
20904
21546
  if (authRequired2) throw new CapabilityBundleServiceError("INVALID_INPUT", "Unauthorized", 401);
20905
21547
  return requested || "default";
20906
21548
  }
20907
- function sendError(reply, error) {
21549
+ function sendError2(reply, error) {
20908
21550
  if (error instanceof CapabilityBundleServiceError) return reply.status(error.statusCode).send({ success: false, code: error.code, message: error.message });
20909
21551
  if (error instanceof Error && error.name === "CapabilityBundleValidationError") {
20910
21552
  const validationError = error;
20911
21553
  const code = validationError.code === "CAPABILITY_BUNDLE_INELIGIBLE_CAPABILITY" ? "BUNDLE_PLUGIN_NOT_ELIGIBLE" : validationError.code === "CAPABILITY_BUNDLE_CREDENTIALS_FORBIDDEN" || validationError.code === "CAPABILITY_BUNDLE_INVALID_CONFIG" ? "INVALID_BUNDLE_CONFIG" : validationError.code;
20912
21554
  return reply.status(400).send({ success: false, code, message: error.message, capabilityType: validationError.capabilityType, fieldPath: validationError.fieldPath });
20913
21555
  }
20914
- logger3.error("Capability bundle controller error", { error: error instanceof Error ? error : new Error(String(error)) });
21556
+ logger4.error("Capability bundle controller error", { error: error instanceof Error ? error : new Error(String(error)) });
20915
21557
  return reply.status(500).send({ success: false, code: "INTERNAL_ERROR", message: "Internal server error" });
20916
21558
  }
20917
21559
  var CapabilityBundleController = class {
@@ -20924,7 +21566,7 @@ var CapabilityBundleController = class {
20924
21566
  const result = await this.service.list(tenant(request, this.authRequired));
20925
21567
  return reply.send({ success: true, data: result.bundles, errors: result.errors });
20926
21568
  } catch (error) {
20927
- return sendError(reply, error);
21569
+ return sendError2(reply, error);
20928
21570
  }
20929
21571
  }
20930
21572
  async getBundle(request, reply) {
@@ -20934,7 +21576,7 @@ var CapabilityBundleController = class {
20934
21576
  if (!parsed.success) return reply.status(400).send({ success: false, code: "INVALID_INPUT", message: "Invalid capability bundle ID" });
20935
21577
  return reply.send({ success: true, data: await this.service.get(tenantId, parsed.data.bundleId) });
20936
21578
  } catch (error) {
20937
- return sendError(reply, error);
21579
+ return sendError2(reply, error);
20938
21580
  }
20939
21581
  }
20940
21582
  async createBundle(request, reply) {
@@ -20944,7 +21586,7 @@ var CapabilityBundleController = class {
20944
21586
  if (!parsed.success) return reply.status(400).send({ success: false, code: "INVALID_INPUT", message: "Invalid capability bundle body" });
20945
21587
  return reply.status(201).send({ success: true, data: await this.service.create(tenantId, parsed.data) });
20946
21588
  } catch (error) {
20947
- return sendError(reply, error);
21589
+ return sendError2(reply, error);
20948
21590
  }
20949
21591
  }
20950
21592
  async updateBundle(request, reply) {
@@ -20955,7 +21597,7 @@ var CapabilityBundleController = class {
20955
21597
  if (!params.success || !parsed.success) return reply.status(400).send({ success: false, code: "INVALID_INPUT", message: "Invalid capability bundle request" });
20956
21598
  return reply.send({ success: true, data: await this.service.update(tenantId, params.data.bundleId, parsed.data) });
20957
21599
  } catch (error) {
20958
- return sendError(reply, error);
21600
+ return sendError2(reply, error);
20959
21601
  }
20960
21602
  }
20961
21603
  async deleteBundle(request, reply) {
@@ -20966,7 +21608,7 @@ var CapabilityBundleController = class {
20966
21608
  await this.service.remove(tenantId, parsed.data.bundleId);
20967
21609
  return reply.send({ success: true, data: null });
20968
21610
  } catch (error) {
20969
- return sendError(reply, error);
21611
+ return sendError2(reply, error);
20970
21612
  }
20971
21613
  }
20972
21614
  async updateProjectBundles(request, reply) {
@@ -20977,7 +21619,7 @@ var CapabilityBundleController = class {
20977
21619
  const result = await this.service.project(tenantId, request.params.projectId, parsed.data.bundleIds, true, parsed.data.expectedRevisions);
20978
21620
  return reply.send({ success: true, data: { project: result.project, persistedBundleIds: result.persistedBundleIds, bundles: result.bundles, preview: result.preview } });
20979
21621
  } catch (error) {
20980
- return sendError(reply, error);
21622
+ return sendError2(reply, error);
20981
21623
  }
20982
21624
  }
20983
21625
  async getProjectBundles(request, reply) {
@@ -20986,7 +21628,7 @@ var CapabilityBundleController = class {
20986
21628
  if (result.preview.errors.some((issue2) => issue2.code === "CAPABILITY_BUNDLE_CREDENTIALS_FORBIDDEN")) return reply.status(400).send({ success: false, code: "INVALID_BUNDLE_CONFIG", message: "Capability bundle record is invalid" });
20987
21629
  return reply.send({ success: true, data: { project: result.project, persistedBundleIds: result.persistedBundleIds, bundles: result.bundles, preview: result.preview } });
20988
21630
  } catch (error) {
20989
- return sendError(reply, error);
21631
+ return sendError2(reply, error);
20990
21632
  }
20991
21633
  }
20992
21634
  async getProjectPreview(request, reply) {
@@ -20994,7 +21636,7 @@ var CapabilityBundleController = class {
20994
21636
  const preview = await this.service.previewProject(tenant(request, this.authRequired), request.params.projectId);
20995
21637
  return reply.send({ success: true, data: preview });
20996
21638
  } catch (error) {
20997
- return sendError(reply, error);
21639
+ return sendError2(reply, error);
20998
21640
  }
20999
21641
  }
21000
21642
  async postProjectPreview(request, reply) {
@@ -21005,7 +21647,7 @@ var CapabilityBundleController = class {
21005
21647
  const result = await this.service.project(tenantId, request.params.projectId, parsed.data.bundleIds, false);
21006
21648
  return reply.send({ success: true, data: result.preview });
21007
21649
  } catch (error) {
21008
- return sendError(reply, error);
21650
+ return sendError2(reply, error);
21009
21651
  }
21010
21652
  }
21011
21653
  };
@@ -22151,8 +22793,8 @@ var probeProviderSchema = z9.object({
22151
22793
  }).strict();
22152
22794
 
22153
22795
  // src/controllers/model-providers.ts
22154
- var logger4 = new Logger({ serviceName: "lattice/gateway/model-providers" });
22155
- function sendError2(reply, error) {
22796
+ var logger5 = new Logger({ serviceName: "lattice/gateway/model-providers" });
22797
+ function sendError3(reply, error) {
22156
22798
  if (error instanceof ModelProviderServiceError) {
22157
22799
  return reply.status(error.statusCode).send({
22158
22800
  success: false,
@@ -22163,7 +22805,7 @@ function sendError2(reply, error) {
22163
22805
  }
22164
22806
  });
22165
22807
  }
22166
- if (error instanceof TenantScopeError2) {
22808
+ if (error instanceof TenantScopeError) {
22167
22809
  return reply.status(error.statusCode).send({
22168
22810
  success: false,
22169
22811
  error: {
@@ -22183,7 +22825,7 @@ function sendError2(reply, error) {
22183
22825
  }
22184
22826
  });
22185
22827
  }
22186
- logger4.error("Model provider controller error", {
22828
+ logger5.error("Model provider controller error", {
22187
22829
  error: error instanceof Error ? error : new Error(String(error))
22188
22830
  });
22189
22831
  return reply.status(500).send({
@@ -22207,7 +22849,7 @@ var ModelProviderController = class {
22207
22849
  const catalog = await this.service.catalog(scope.userId, scope.tenantId);
22208
22850
  return reply.send({ success: true, data: catalog });
22209
22851
  } catch (error) {
22210
- return sendError2(reply, error);
22852
+ return sendError3(reply, error);
22211
22853
  }
22212
22854
  }
22213
22855
  /** Hydrates the runtime model registry from persisted providers (startup). */
@@ -22228,7 +22870,7 @@ var ModelProviderController = class {
22228
22870
  data: { records, total: records.length }
22229
22871
  });
22230
22872
  } catch (error) {
22231
- return sendError2(reply, error);
22873
+ return sendError3(reply, error);
22232
22874
  }
22233
22875
  }
22234
22876
  /** GET /api/model-providers/:providerId */
@@ -22241,7 +22883,7 @@ var ModelProviderController = class {
22241
22883
  data: await this.service.get(scope.userId, scope.tenantId, providerId2)
22242
22884
  });
22243
22885
  } catch (error) {
22244
- return sendError2(reply, error);
22886
+ return sendError3(reply, error);
22245
22887
  }
22246
22888
  }
22247
22889
  /** POST /api/model-providers */
@@ -22254,7 +22896,7 @@ var ModelProviderController = class {
22254
22896
  data: await this.service.create(scope.userId, scope.tenantId, input)
22255
22897
  });
22256
22898
  } catch (error) {
22257
- return sendError2(reply, error);
22899
+ return sendError3(reply, error);
22258
22900
  }
22259
22901
  }
22260
22902
  /** PUT /api/model-providers/:providerId */
@@ -22273,7 +22915,7 @@ var ModelProviderController = class {
22273
22915
  )
22274
22916
  });
22275
22917
  } catch (error) {
22276
- return sendError2(reply, error);
22918
+ return sendError3(reply, error);
22277
22919
  }
22278
22920
  }
22279
22921
  /** DELETE /api/model-providers/:providerId */
@@ -22284,7 +22926,7 @@ var ModelProviderController = class {
22284
22926
  await this.service.remove(scope.userId, scope.tenantId, providerId2);
22285
22927
  return reply.send({ success: true, data: null });
22286
22928
  } catch (error) {
22287
- return sendError2(reply, error);
22929
+ return sendError3(reply, error);
22288
22930
  }
22289
22931
  }
22290
22932
  /** POST /api/model-providers/probe */
@@ -22297,7 +22939,7 @@ var ModelProviderController = class {
22297
22939
  data: await this.service.probe(scope.userId, scope.tenantId, input)
22298
22940
  });
22299
22941
  } catch (error) {
22300
- return sendError2(reply, error);
22942
+ return sendError3(reply, error);
22301
22943
  }
22302
22944
  }
22303
22945
  /** PUT /api/model-providers/:providerId/models */
@@ -22316,7 +22958,7 @@ var ModelProviderController = class {
22316
22958
  )
22317
22959
  });
22318
22960
  } catch (error) {
22319
- return sendError2(reply, error);
22961
+ return sendError3(reply, error);
22320
22962
  }
22321
22963
  }
22322
22964
  /** POST /api/model-providers/:providerId/discover */
@@ -22334,7 +22976,7 @@ var ModelProviderController = class {
22334
22976
  )
22335
22977
  });
22336
22978
  } catch (error) {
22337
- return sendError2(reply, error);
22979
+ return sendError3(reply, error);
22338
22980
  }
22339
22981
  }
22340
22982
  /** POST /api/model-providers/:providerId/models/enabled */
@@ -22354,7 +22996,7 @@ var ModelProviderController = class {
22354
22996
  )
22355
22997
  });
22356
22998
  } catch (error) {
22357
- return sendError2(reply, error);
22999
+ return sendError3(reply, error);
22358
23000
  }
22359
23001
  }
22360
23002
  };
@@ -23472,86 +24114,6 @@ import { toProjectRoomPublicMessage } from "@axiom-lattice/protocols";
23472
24114
 
23473
24115
  // src/services/collaboration/RoomInterruptService.ts
23474
24116
  import { randomUUID as randomUUID28 } from "crypto";
23475
-
23476
- // src/router/roomInterrupt.ts
23477
- var CLARIFY_FENCE = /^```clarify\n([\s\S]*?)\n```$/;
23478
- function readClarifyValue(value) {
23479
- if (typeof value !== "string") return void 0;
23480
- const match = CLARIFY_FENCE.exec(value.trim());
23481
- if (!match) return void 0;
23482
- try {
23483
- const parsed = JSON.parse(match[1]);
23484
- if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.questions)) {
23485
- return parsed;
23486
- }
23487
- } catch {
23488
- }
23489
- return void 0;
23490
- }
23491
- function rawInterruptId(interrupt, taskId, taskIndex, index) {
23492
- const record = typeof interrupt === "object" && interrupt !== null ? interrupt : void 0;
23493
- const rawId = record?.id;
23494
- if (typeof rawId === "string" || typeof rawId === "number") return String(rawId);
23495
- return `${taskId ?? `task-${taskIndex}`}:${index}`;
23496
- }
23497
- function pendingInterrupts(state) {
23498
- if (typeof state !== "object" || state === null) return [];
23499
- const tasks = state.tasks;
23500
- if (!Array.isArray(tasks)) return [];
23501
- const result = [];
23502
- tasks.forEach((task, taskIndex) => {
23503
- const record = typeof task === "object" && task !== null ? task : void 0;
23504
- const rawTaskId = record?.id;
23505
- const taskId = typeof rawTaskId === "string" || typeof rawTaskId === "number" ? String(rawTaskId) : void 0;
23506
- const interrupts = record?.interrupts;
23507
- if (!Array.isArray(interrupts)) return;
23508
- interrupts.forEach((interrupt, index) => {
23509
- const value = typeof interrupt === "object" && interrupt !== null ? interrupt.value : void 0;
23510
- const clarify = readClarifyValue(value);
23511
- result.push({
23512
- interruptId: rawInterruptId(interrupt, taskId, taskIndex, index),
23513
- ...clarify === void 0 ? {} : { clarify }
23514
- });
23515
- });
23516
- });
23517
- return result;
23518
- }
23519
- function readPendingInterruptTarget(state) {
23520
- const interrupts = pendingInterrupts(state);
23521
- if (interrupts.length === 0) return void 0;
23522
- const ids = new Set(interrupts.map((interrupt) => interrupt.interruptId));
23523
- if (ids.size !== interrupts.length) return { conflict: true };
23524
- const clarify = interrupts.find((interrupt) => interrupt.clarify !== void 0);
23525
- if (clarify !== void 0) {
23526
- if (!isOfficialInterruptId(clarify.interruptId) && interrupts.length > 1) return { conflict: true };
23527
- return { target: clarify };
23528
- }
23529
- return interrupts.length === 1 ? { target: interrupts[0] } : { conflict: true };
23530
- }
23531
- function readExplicitInterruptTarget(state, interruptId) {
23532
- const interrupts = pendingInterrupts(state);
23533
- const matches = interrupts.filter((interrupt) => interrupt.interruptId === interruptId);
23534
- if (matches.length > 1) return { conflict: true };
23535
- const target = matches[0];
23536
- if (target === void 0) return void 0;
23537
- if (!isOfficialInterruptId(interruptId) && interrupts.length > 1) return { conflict: true };
23538
- return { target };
23539
- }
23540
- function readClarifyInterruptTarget(state) {
23541
- const match = readPendingInterruptTarget(state);
23542
- return match !== void 0 && !("conflict" in match) && match.target.clarify !== void 0 ? match.target : void 0;
23543
- }
23544
- function readClarifyInterrupt(state) {
23545
- return pendingInterrupts(state).find((interrupt) => interrupt.clarify !== void 0)?.clarify;
23546
- }
23547
- function buildClarifyResumeCommand(text, interruptId) {
23548
- return resumeMap(interruptId, { data: { answers: [{ questionIndex: 0, otherText: text }] } });
23549
- }
23550
- function buildClarifySkipCommand(interruptId) {
23551
- return resumeMap(interruptId, { data: { answers: [] } });
23552
- }
23553
-
23554
- // src/services/collaboration/RoomInterruptService.ts
23555
24117
  var RESUMABLE_TASK_INTERRUPT_CODES = /* @__PURE__ */ new Set([
23556
24118
  "PROJECT_TASK_NOT_RESUMABLE",
23557
24119
  "PROJECT_TASK_NOT_READY",
@@ -25247,7 +25809,7 @@ function createRoomMessageRetentionJob(input) {
25247
25809
 
25248
25810
  // src/services/collaboration/ProjectTaskExecutionMutex.ts
25249
25811
  import { snapshotExactRecord as snapshotExactRecord8 } from "@axiom-lattice/protocols";
25250
- import { getCoordinationLeaseStore, getReplicaId as getReplicaId8, withLease as withLease2 } from "@axiom-lattice/core";
25812
+ import { getCoordinationLeaseStore, getReplicaId as getReplicaId9, withLease as withLease2 } from "@axiom-lattice/core";
25251
25813
  var MACHINE_LEASE_OPTIONS = { ttlMs: 6e4, acquireTimeoutMs: 9e4, retryDelayMs: 100 };
25252
25814
  function machineKey(scope) {
25253
25815
  const snapshot = snapshotExactRecord8(scope, ["tenantId", "assistantId", "projectId"]);
@@ -25277,7 +25839,7 @@ var ProjectTaskExecutionMutex = class {
25277
25839
  try {
25278
25840
  const store2 = getCoordinationLeaseStore();
25279
25841
  if (!store2) return await operation();
25280
- return await withLease2(store2, leaseKey, getReplicaId8(), operation, MACHINE_LEASE_OPTIONS);
25842
+ return await withLease2(store2, leaseKey, getReplicaId9(), operation, MACHINE_LEASE_OPTIONS);
25281
25843
  } finally {
25282
25844
  this.release(key);
25283
25845
  }
@@ -28556,7 +29118,7 @@ function broadcastModelProviderChanged(tenantId, providerId2) {
28556
29118
  if (!tenantId) return;
28557
29119
  try {
28558
29120
  const outbox = getStoreLattice28("default", "outbox").store;
28559
- void publishModelProviderChanged(outbox, getReplicaId9(), tenantId, providerId2);
29121
+ void publishModelProviderChanged(outbox, getReplicaId10(), tenantId, providerId2);
28560
29122
  } catch {
28561
29123
  }
28562
29124
  }
@@ -29173,6 +29735,11 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
29173
29735
  getHealth
29174
29736
  );
29175
29737
  app2.get("/info", getInfo);
29738
+ app2.get("/api/observability", getObservability);
29739
+ app2.get(
29740
+ "/api/observability/dashboard",
29741
+ getObservabilityDashboard
29742
+ );
29176
29743
  app2.get("/metrics", getSystemMetrics);
29177
29744
  app2.get(
29178
29745
  "/api/assistants/:assistantId/threads/:threadId/schedules",
@@ -30872,6 +31439,50 @@ var ChannelAdapterRegistry = class {
30872
31439
  }
30873
31440
  };
30874
31441
 
31442
+ // src/router/middlewares/channelSharedState.ts
31443
+ import { createClient as createClient2 } from "redis";
31444
+ var clientPromise;
31445
+ async function redis() {
31446
+ const url = process.env.REDIS_URL?.trim();
31447
+ if (!url) return void 0;
31448
+ if (!clientPromise) {
31449
+ clientPromise = (async () => {
31450
+ const client = createClient2({ url });
31451
+ client.on("error", () => void 0);
31452
+ await client.connect();
31453
+ return client;
31454
+ })();
31455
+ }
31456
+ try {
31457
+ const client = await clientPromise;
31458
+ return client.isOpen ? client : void 0;
31459
+ } catch {
31460
+ clientPromise = void 0;
31461
+ return void 0;
31462
+ }
31463
+ }
31464
+ async function claimDedupKey(key, ttlMs) {
31465
+ try {
31466
+ const client = await redis();
31467
+ if (!client) return void 0;
31468
+ const result = await client.set(key, "1", { NX: true, PX: ttlMs });
31469
+ return result === "OK";
31470
+ } catch {
31471
+ return void 0;
31472
+ }
31473
+ }
31474
+ async function incrementRateCounter(key, windowMs) {
31475
+ try {
31476
+ const client = await redis();
31477
+ if (!client) return void 0;
31478
+ const count = await client.incr(key);
31479
+ if (count === 1) await client.pExpire(key, windowMs);
31480
+ return count;
31481
+ } catch {
31482
+ return void 0;
31483
+ }
31484
+ }
31485
+
30875
31486
  // src/router/middlewares/deduplication.ts
30876
31487
  var processedMessages = /* @__PURE__ */ new Map();
30877
31488
  function createDeduplicationMiddleware(ttlMs = 5 * 60 * 1e3) {
@@ -30891,6 +31502,16 @@ function createDeduplicationMiddleware(ttlMs = 5 * 60 * 1e3) {
30891
31502
  }, "Duplicate message blocked by deduplication");
30892
31503
  return;
30893
31504
  }
31505
+ const sharedClaim = await claimDedupKey(`axiom:channel:dedup:${key}`, ttlMs);
31506
+ if (sharedClaim === false) {
31507
+ console.warn({
31508
+ event: "dedup:blocked",
31509
+ channel: msg.channel,
31510
+ senderId: msg.sender.id,
31511
+ messageId: msgId
31512
+ }, "Duplicate message blocked by deduplication");
31513
+ return;
31514
+ }
30894
31515
  processedMessages.set(key, now);
30895
31516
  }
30896
31517
  if (processedMessages.size > 1e4) {
@@ -30913,11 +31534,12 @@ function createRateLimitMiddleware(maxRequests = 10, windowMs = 60 * 1e3, maxEnt
30913
31534
  return async (ctx, next) => {
30914
31535
  const senderKey = `${ctx.inboundMessage.channel}:${ctx.inboundMessage.sender.id}`;
30915
31536
  const now = Date.now();
31537
+ const sharedCount = await incrementRateCounter(`axiom:channel:rate:${senderKey}`, windowMs);
30916
31538
  let counter = rateCounters.get(senderKey);
30917
31539
  if (!counter || now > counter.resetAt) {
30918
31540
  counter = { count: 0, resetAt: now + windowMs };
30919
31541
  }
30920
- counter.count++;
31542
+ counter.count = sharedCount ?? counter.count + 1;
30921
31543
  rateCounters.set(senderKey, counter);
30922
31544
  if (rateCounters.size > maxEntries) {
30923
31545
  const oldest = Array.from(rateCounters.entries()).sort((a, b) => a[1].resetAt - b[1].resetAt)[0];
@@ -30938,13 +31560,13 @@ function createRateLimitMiddleware(maxRequests = 10, windowMs = 60 * 1e3, maxEnt
30938
31560
  }
30939
31561
 
30940
31562
  // src/router/middlewares/auditLogger.ts
30941
- var logger5 = new Logger({ serviceName: "lattice/gateway/audit" });
31563
+ var logger6 = new Logger({ serviceName: "lattice/gateway/audit" });
30942
31564
  function createAuditLoggerMiddleware() {
30943
31565
  return async (ctx, next) => {
30944
31566
  const start2 = Date.now();
30945
31567
  try {
30946
31568
  await next();
30947
- logger5.info("message routed", {
31569
+ logger6.info("message routed", {
30948
31570
  event: "message:routed",
30949
31571
  channel: ctx.inboundMessage.channel,
30950
31572
  senderId: ctx.inboundMessage.sender.id,
@@ -30954,7 +31576,7 @@ function createAuditLoggerMiddleware() {
30954
31576
  status: "success"
30955
31577
  });
30956
31578
  } catch (error) {
30957
- logger5.error(
31579
+ logger6.error(
30958
31580
  error instanceof Error ? error.message : String(error),
30959
31581
  {
30960
31582
  event: "message:error",
@@ -31000,9 +31622,59 @@ function parseWecomTextFrame(frame) {
31000
31622
  }
31001
31623
 
31002
31624
  // src/channels/wecom/cardTool.ts
31003
- import { getToolLattice, registerToolLattice } from "@axiom-lattice/core";
31625
+ import { getStoreLattice as getStoreLattice38, getToolLattice, registerToolLattice } from "@axiom-lattice/core";
31004
31626
  import { z as z12 } from "zod";
31005
31627
  var CARD_TTL_MS = 10 * 6e4;
31628
+ var SHARED_NAMESPACE = ["channel-runtime", "wecom-card"];
31629
+ var SHARED_TTL_MINUTES = 10;
31630
+ function sharedStore() {
31631
+ try {
31632
+ return getStoreLattice38("default", "store").store;
31633
+ } catch {
31634
+ return null;
31635
+ }
31636
+ }
31637
+ async function putWecomCardForTenant(tenantId, key, card) {
31638
+ const accepted = putWecomCard(key, card);
31639
+ if (!accepted) return false;
31640
+ const store2 = sharedStore();
31641
+ if (!store2) return true;
31642
+ try {
31643
+ await store2.put(tenantId, {
31644
+ namespace: SHARED_NAMESPACE,
31645
+ key,
31646
+ value: { card },
31647
+ ttlMinutes: SHARED_TTL_MINUTES
31648
+ });
31649
+ } catch {
31650
+ }
31651
+ return true;
31652
+ }
31653
+ async function takeWecomCardForTenant(tenantId, key) {
31654
+ const store2 = sharedStore();
31655
+ if (store2) {
31656
+ try {
31657
+ const item = await store2.get(tenantId, SHARED_NAMESPACE, key);
31658
+ const card = item?.value?.card;
31659
+ if (card) {
31660
+ await store2.delete(tenantId, SHARED_NAMESPACE, key);
31661
+ takeWecomCard(key);
31662
+ return card;
31663
+ }
31664
+ } catch {
31665
+ }
31666
+ }
31667
+ return takeWecomCard(key);
31668
+ }
31669
+ async function discardWecomCardForTenant(tenantId, key) {
31670
+ discardWecomCard(key);
31671
+ const store2 = sharedStore();
31672
+ if (!store2) return;
31673
+ try {
31674
+ await store2.delete(tenantId, SHARED_NAMESPACE, key);
31675
+ } catch {
31676
+ }
31677
+ }
31006
31678
  var MAX_CARD_ENTRIES = 1e3;
31007
31679
  var cardEntries = /* @__PURE__ */ new Map();
31008
31680
  function pruneCards(now) {
@@ -31216,6 +31888,11 @@ function readReplyKey(exeConfig) {
31216
31888
  const key = configurable?.runConfig?.wecomReplyKey;
31217
31889
  return typeof key === "string" && key.length > 0 ? key : void 0;
31218
31890
  }
31891
+ function readTenantId(exeConfig) {
31892
+ const configurable = exeConfig?.configurable;
31893
+ const tenantId = configurable?.runConfig?.tenantId;
31894
+ return typeof tenantId === "string" && tenantId.length > 0 ? tenantId : "default";
31895
+ }
31219
31896
  function registerWecomCardTool() {
31220
31897
  if (getToolLattice("wecom_card")) return;
31221
31898
  registerToolLattice(
@@ -31232,7 +31909,7 @@ function registerWecomCardTool() {
31232
31909
  }
31233
31910
  try {
31234
31911
  const card = buildTemplateCard(input);
31235
- if (!putWecomCard(replyKey, card)) {
31912
+ if (!await putWecomCardForTenant(readTenantId(exeConfig), replyKey, card)) {
31236
31913
  return "ERROR: \u672C\u6761\u56DE\u590D\u5DF2\u7ED3\u675F\uFF0C\u65E0\u6CD5\u518D\u9644\u5E26\u5361\u7247\u3002\u8BF7\u628A\u7ED3\u679C\u5199\u5165\u6B63\u6587\u3002";
31237
31914
  }
31238
31915
  const title = card.main_title?.title;
@@ -31246,7 +31923,7 @@ function registerWecomCardTool() {
31246
31923
  }
31247
31924
 
31248
31925
  // src/channels/wecom/WecomChannelAdapter.ts
31249
- var logger6 = new Logger({ serviceName: "lattice/gateway/wecom" });
31926
+ var logger7 = new Logger({ serviceName: "lattice/gateway/wecom" });
31250
31927
  var wecomConfigSchema = z13.object({
31251
31928
  botId: z13.string(),
31252
31929
  secret: z13.string()
@@ -31338,12 +32015,12 @@ async function sendStreamFrame(connection, replyTarget, input) {
31338
32015
  }
31339
32016
  const frame = replyTarget.rawTarget?.frame;
31340
32017
  if (!frame) {
31341
- logger6.warn("WeCom reply target has no inbound frame, skipping", {
32018
+ logger7.warn("WeCom reply target has no inbound frame, skipping", {
31342
32019
  installationId: replyTarget.channelInstallationId
31343
32020
  });
31344
32021
  return;
31345
32022
  }
31346
- const report = (message, error) => logger6.warn(message, {
32023
+ const report = (message, error) => logger7.warn(message, {
31347
32024
  installationId: replyTarget.channelInstallationId,
31348
32025
  error: error instanceof Error ? error.message : String(error)
31349
32026
  });
@@ -31364,7 +32041,7 @@ async function sendStreamFrame(connection, replyTarget, input) {
31364
32041
  template_card: input.card
31365
32042
  }));
31366
32043
  rememberSentCard(input.card);
31367
- logger6.info("WeCom card sent", {
32044
+ logger7.info("WeCom card sent", {
31368
32045
  installationId: replyTarget.channelInstallationId,
31369
32046
  cardType: input.card.card_type
31370
32047
  });
@@ -31377,19 +32054,19 @@ async function sendEventReply(connection, replyTarget, input) {
31377
32054
  if (!input.done) return;
31378
32055
  const chatId = eventChatId(replyTarget);
31379
32056
  if (!chatId) {
31380
- logger6.warn("WeCom card event has no chat id, cannot reply", {
32057
+ logger7.warn("WeCom card event has no chat id, cannot reply", {
31381
32058
  installationId: replyTarget.channelInstallationId
31382
32059
  });
31383
32060
  return;
31384
32061
  }
31385
32062
  const sendMessage = connection.client.sendMessage;
31386
32063
  if (!sendMessage) {
31387
- logger6.warn("WeCom client does not support sendMessage, cannot reply to card event", {
32064
+ logger7.warn("WeCom client does not support sendMessage, cannot reply to card event", {
31388
32065
  installationId: replyTarget.channelInstallationId
31389
32066
  });
31390
32067
  return;
31391
32068
  }
31392
- const report = (message, error) => logger6.warn(message, {
32069
+ const report = (message, error) => logger7.warn(message, {
31393
32070
  installationId: replyTarget.channelInstallationId,
31394
32071
  error: error instanceof Error ? error.message : String(error)
31395
32072
  });
@@ -31400,7 +32077,7 @@ async function sendEventReply(connection, replyTarget, input) {
31400
32077
  template_card: input.card
31401
32078
  }));
31402
32079
  rememberSentCard(input.card);
31403
- logger6.info("WeCom card sent", {
32080
+ logger7.info("WeCom card sent", {
31404
32081
  installationId: replyTarget.channelInstallationId,
31405
32082
  cardType: input.card.card_type
31406
32083
  });
@@ -31496,14 +32173,14 @@ function handleTemplateCardEvent(rawFrame, installation, client, router) {
31496
32173
  const userId = body.from?.userid;
31497
32174
  const chatId = typeof body.chatid === "string" && body.chatid ? body.chatid : userId;
31498
32175
  if (!userId || !chatId) {
31499
- logger6.warn("WeCom card event missing user/chat id, skipping", {
32176
+ logger7.warn("WeCom card event missing user/chat id, skipping", {
31500
32177
  installationId: installation.id
31501
32178
  });
31502
32179
  return;
31503
32180
  }
31504
32181
  const { taskId, eventKey } = cardEventFields(event);
31505
32182
  if (!claimCardEvent(taskId, eventKey)) {
31506
- logger6.info("WeCom duplicate card event ignored", {
32183
+ logger7.info("WeCom duplicate card event ignored", {
31507
32184
  installationId: installation.id,
31508
32185
  taskId,
31509
32186
  eventKey
@@ -31515,24 +32192,24 @@ function handleTemplateCardEvent(rawFrame, installation, client, router) {
31515
32192
  const updated = acknowledgedCard(taskId, eventKey);
31516
32193
  try {
31517
32194
  void Promise.resolve(updateTemplateCard.call(client, rawFrame, updated)).catch((error) => {
31518
- logger6.warn("WeCom template card update failed", {
32195
+ logger7.warn("WeCom template card update failed", {
31519
32196
  installationId: installation.id,
31520
32197
  error: describeError(error)
31521
32198
  });
31522
32199
  });
31523
32200
  } catch (error) {
31524
- logger6.warn("WeCom template card update failed", {
32201
+ logger7.warn("WeCom template card update failed", {
31525
32202
  installationId: installation.id,
31526
32203
  error: describeError(error)
31527
32204
  });
31528
32205
  }
31529
32206
  } else {
31530
- logger6.warn("WeCom client does not support updateTemplateCard", {
32207
+ logger7.warn("WeCom client does not support updateTemplateCard", {
31531
32208
  installationId: installation.id
31532
32209
  });
31533
32210
  }
31534
32211
  if (!router) return;
31535
- logger6.info("WeCom card event dispatched", {
32212
+ logger7.info("WeCom card event dispatched", {
31536
32213
  installationId: installation.id,
31537
32214
  taskId,
31538
32215
  eventKey,
@@ -31540,13 +32217,13 @@ function handleTemplateCardEvent(rawFrame, installation, client, router) {
31540
32217
  });
31541
32218
  try {
31542
32219
  void router.dispatch(cardEventInbound(frame, installation, { chatId, userId })).catch((error) => {
31543
- logger6.error("WeCom card event dispatch error", {
32220
+ logger7.error("WeCom card event dispatch error", {
31544
32221
  installationId: installation.id,
31545
32222
  error: describeError(error)
31546
32223
  });
31547
32224
  });
31548
32225
  } catch (error) {
31549
- logger6.error("WeCom card event dispatch error", {
32226
+ logger7.error("WeCom card event dispatch error", {
31550
32227
  installationId: installation.id,
31551
32228
  error: describeError(error)
31552
32229
  });
@@ -31590,13 +32267,13 @@ var wecomChannelAdapter = {
31590
32267
  async sendReply(replyTarget, message, installation) {
31591
32268
  const connection = activeConnections2.get(installation.id);
31592
32269
  if (!connection) {
31593
- logger6.warn("WeCom bot not connected, cannot reply", {
32270
+ logger7.warn("WeCom bot not connected, cannot reply", {
31594
32271
  installationId: installation.id
31595
32272
  });
31596
32273
  return;
31597
32274
  }
31598
32275
  const key = replyKeyForTarget(replyTarget);
31599
- const card = takeWecomCard(key);
32276
+ const card = await takeWecomCardForTenant(installation.tenantId, key);
31600
32277
  await sendStreamFrame(connection, replyTarget, {
31601
32278
  text: message.text,
31602
32279
  done: true,
@@ -31614,7 +32291,7 @@ var wecomChannelAdapter = {
31614
32291
  async sendStreamChunk(replyTarget, chunk, installation) {
31615
32292
  const connection = activeConnections2.get(installation.id);
31616
32293
  if (!connection) {
31617
- logger6.warn("WeCom bot not connected, cannot stream reply", {
32294
+ logger7.warn("WeCom bot not connected, cannot stream reply", {
31618
32295
  installationId: installation.id
31619
32296
  });
31620
32297
  return;
@@ -31623,9 +32300,9 @@ var wecomChannelAdapter = {
31623
32300
  let card;
31624
32301
  if (chunk.done) {
31625
32302
  if (chunk.reason !== void 0 && chunk.reason !== "completed") {
31626
- discardWecomCard(key);
32303
+ await discardWecomCardForTenant(installation.tenantId, key);
31627
32304
  } else {
31628
- card = takeWecomCard(key);
32305
+ card = await takeWecomCardForTenant(installation.tenantId, key);
31629
32306
  }
31630
32307
  }
31631
32308
  await sendStreamFrame(connection, replyTarget, {
@@ -31646,11 +32323,11 @@ var wecomChannelAdapter = {
31646
32323
  async connect(installation, deps) {
31647
32324
  const { id: installationId, tenantId, config } = installation;
31648
32325
  if (!config.botId || !config.secret) {
31649
- logger6.warn("WeCom installation missing botId/secret, skipping", { installationId });
32326
+ logger7.warn("WeCom installation missing botId/secret, skipping", { installationId });
31650
32327
  return;
31651
32328
  }
31652
32329
  if (activeConnections2.has(installationId)) {
31653
- logger6.warn("WeCom long connection already running, skipping", { installationId });
32330
+ logger7.warn("WeCom long connection already running, skipping", { installationId });
31654
32331
  return;
31655
32332
  }
31656
32333
  const { router, wsClientFactory } = deps ?? {};
@@ -31661,7 +32338,7 @@ var wecomChannelAdapter = {
31661
32338
  if (!inbound || !router) return void 0;
31662
32339
  return router.dispatch(inbound);
31663
32340
  }).catch((error) => {
31664
- logger6.error("WeCom message dispatch error", {
32341
+ logger7.error("WeCom message dispatch error", {
31665
32342
  installationId,
31666
32343
  error: error instanceof Error ? error.message : String(error)
31667
32344
  });
@@ -31674,19 +32351,19 @@ var wecomChannelAdapter = {
31674
32351
  connection.attempts = 0;
31675
32352
  });
31676
32353
  client.on("event.disconnected_event", () => {
31677
- logger6.warn("WeCom connection kicked by a newer connection; will reconnect", {
32354
+ logger7.warn("WeCom connection kicked by a newer connection; will reconnect", {
31678
32355
  installationId
31679
32356
  });
31680
32357
  scheduleReconnect(installationId, connection);
31681
32358
  });
31682
32359
  activeConnections2.set(installationId, connection);
31683
32360
  client.connect();
31684
- logger6.info("WeCom long connection starting", { installationId, tenantId });
32361
+ logger7.info("WeCom long connection starting", { installationId, tenantId });
31685
32362
  },
31686
32363
  async disconnect(installationId) {
31687
32364
  const connection = activeConnections2.get(installationId);
31688
32365
  if (!connection) {
31689
- logger6.warn("WeCom long connection not running, nothing to disconnect", { installationId });
32366
+ logger7.warn("WeCom long connection not running, nothing to disconnect", { installationId });
31690
32367
  return;
31691
32368
  }
31692
32369
  connection.intentional = true;
@@ -31697,20 +32374,20 @@ var wecomChannelAdapter = {
31697
32374
  try {
31698
32375
  connection.client.disconnect();
31699
32376
  } catch (error) {
31700
- logger6.error("WeCom long connection stop error", {
32377
+ logger7.error("WeCom long connection stop error", {
31701
32378
  installationId,
31702
32379
  error: error instanceof Error ? error.message : String(error)
31703
32380
  });
31704
32381
  }
31705
32382
  activeConnections2.delete(installationId);
31706
- logger6.info("WeCom long connection disconnected", { installationId });
32383
+ logger7.info("WeCom long connection disconnected", { installationId });
31707
32384
  }
31708
32385
  };
31709
32386
  function scheduleReconnect(installationId, connection) {
31710
32387
  if (connection.intentional || connection.timer) return;
31711
32388
  const delay3 = Math.min(RECONNECT_BASE_MS * 2 ** connection.attempts, RECONNECT_MAX_MS);
31712
32389
  connection.attempts += 1;
31713
- logger6.info("WeCom reconnect scheduled", {
32390
+ logger7.info("WeCom reconnect scheduled", {
31714
32391
  installationId,
31715
32392
  delayMs: delay3,
31716
32393
  attempt: connection.attempts
@@ -31723,7 +32400,7 @@ function scheduleReconnect(installationId, connection) {
31723
32400
  try {
31724
32401
  connection.client.connect();
31725
32402
  } catch (error) {
31726
- logger6.error("WeCom reconnect failed", {
32403
+ logger7.error("WeCom reconnect failed", {
31727
32404
  installationId,
31728
32405
  error: error instanceof Error ? error.message : String(error)
31729
32406
  });
@@ -31870,19 +32547,17 @@ import {
31870
32547
  getLoggerLattice,
31871
32548
  loggerLatticeManager,
31872
32549
  sandboxLatticeManager as sandboxLatticeManager2,
31873
- getStoreLattice as getStoreLattice41,
32550
+ getStoreLattice as getStoreLattice42,
31874
32551
  agentInstanceManager as agentInstanceManager13,
31875
32552
  createSandboxProvider,
31876
32553
  TokenCache,
31877
32554
  mcpManager as mcpManager3,
31878
32555
  convertToConnection as convertToConnection2,
31879
32556
  createTrustedAgentMessageDispatcher as createTrustedAgentMessageDispatcher2,
31880
- connectAllChannels,
31881
- disconnectAllChannels,
31882
32557
  ChannelConnectionLeader,
31883
32558
  runAsLeader,
31884
32559
  configureCoordinationLeaseStore,
31885
- getReplicaId as getReplicaId11,
32560
+ getReplicaId as getReplicaId12,
31886
32561
  tryAcquireThreadRun as tryAcquireThreadRun5,
31887
32562
  taskLifecycleNotifier as taskLifecycleNotifier3,
31888
32563
  projectTaskAdmissionNotifier as projectTaskAdmissionNotifier2,
@@ -31895,6 +32570,7 @@ import {
31895
32570
  configureCollectionSync,
31896
32571
  COLLECTION_CHANGED_TOPIC,
31897
32572
  vectorStoreLatticeManager as vectorStoreLatticeManager2,
32573
+ observabilityLatticeManager,
31898
32574
  getSandboxPluginCatalog as getSandboxPluginCatalog3,
31899
32575
  getSandBoxManager as getSandBoxManager4,
31900
32576
  installPluginHost,
@@ -31906,7 +32582,7 @@ import {
31906
32582
  } from "@axiom-lattice/core";
31907
32583
 
31908
32584
  // src/services/chunk/redisAppendLog.ts
31909
- import { createClient as createClient2 } from "redis";
32585
+ import { createClient as createClient3 } from "redis";
31910
32586
  var VALUE_FIELD = "v";
31911
32587
  var START_ID = "0-0";
31912
32588
  var ChunkLogCorruptionError = class extends Error {
@@ -31923,7 +32599,7 @@ var ChunkLogCorruptionError = class extends Error {
31923
32599
  }
31924
32600
  };
31925
32601
  async function createRedisAppendLog(url, options = {}) {
31926
- const client = createClient2({
32602
+ const client = createClient3({
31927
32603
  url,
31928
32604
  disableOfflineQueue: true,
31929
32605
  socket: {
@@ -32113,7 +32789,7 @@ function readField(fields, name) {
32113
32789
  }
32114
32790
 
32115
32791
  // src/services/run-cancel/RedisRunCancelNotifier.ts
32116
- import { createClient as createClient3 } from "redis";
32792
+ import { createClient as createClient4 } from "redis";
32117
32793
  import {
32118
32794
  runCancelSignalSchema as runCancelSignalSchema2
32119
32795
  } from "@axiom-lattice/protocols";
@@ -32514,7 +33190,7 @@ function createRedisRunCancelNotifier(options) {
32514
33190
  });
32515
33191
  }
32516
33192
  function createNodeRedisClient2(url) {
32517
- const client = createClient3({
33193
+ const client = createClient4({
32518
33194
  url,
32519
33195
  disableOfflineQueue: true,
32520
33196
  socket: {
@@ -32570,13 +33246,13 @@ import { RoomAgentMessageService } from "@axiom-lattice/core";
32570
33246
 
32571
33247
  // src/router/MessageRouter.ts
32572
33248
  import {
32573
- getStoreLattice as getStoreLattice38,
33249
+ getStoreLattice as getStoreLattice39,
32574
33250
  agentInstanceManager as agentInstanceManager10
32575
33251
  } from "@axiom-lattice/core";
32576
33252
  import {
32577
33253
  snapshotExactArray as snapshotExactArray3,
32578
33254
  snapshotExactRecord as snapshotExactRecord15,
32579
- MessageChunkTypes as MessageChunkTypes5
33255
+ MessageChunkTypes as MessageChunkTypes4
32580
33256
  } from "@axiom-lattice/protocols";
32581
33257
  import { randomUUID as randomUUID35 } from "crypto";
32582
33258
 
@@ -32895,7 +33571,7 @@ var _MessageRouter = class _MessageRouter {
32895
33571
  channel: message.channel,
32896
33572
  adapterChannel: adapter.channel
32897
33573
  }, "Thread resolved by adapter strategy");
32898
- const threadStore = getStoreLattice38("default", "thread").store;
33574
+ const threadStore = getStoreLattice39("default", "thread").store;
32899
33575
  try {
32900
33576
  await threadStore.createThread(
32901
33577
  tenantId,
@@ -32932,7 +33608,7 @@ var _MessageRouter = class _MessageRouter {
32932
33608
  }
32933
33609
  }
32934
33610
  if (!threadId) {
32935
- const threadStore = getStoreLattice38("default", "thread").store;
33611
+ const threadStore = getStoreLattice39("default", "thread").store;
32936
33612
  const newThreadId = randomUUID35();
32937
33613
  console.log({
32938
33614
  event: "dispatch:thread:create",
@@ -33167,8 +33843,8 @@ var _MessageRouter = class _MessageRouter {
33167
33843
  ...metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {},
33168
33844
  channelRun: true
33169
33845
  };
33170
- const runLedger = getStoreLattice38("default", "agentRun").store;
33171
- const jobStore = getStoreLattice38("default", "channelReplyJob").store;
33846
+ const runLedger = getStoreLattice39("default", "agentRun").store;
33847
+ const jobStore = getStoreLattice39("default", "channelReplyJob").store;
33172
33848
  const runId = randomUUID35();
33173
33849
  await jobStore.create({
33174
33850
  runId,
@@ -33266,13 +33942,13 @@ var _MessageRouter = class _MessageRouter {
33266
33942
  for await (const chunk of stream) {
33267
33943
  if (state.finished) break;
33268
33944
  if (!started) {
33269
- if (chunk?.type === MessageChunkTypes5.HUMAN && chunk?.data?.id === inputMessageId) {
33945
+ if (chunk?.type === MessageChunkTypes4.HUMAN && chunk?.data?.id === inputMessageId) {
33270
33946
  started = true;
33271
33947
  }
33272
33948
  continue;
33273
33949
  }
33274
- if (chunk?.type === MessageChunkTypes5.MESSAGE_COMPLETED || chunk?.type === MessageChunkTypes5.MESSAGE_FAILED || chunk?.type === MessageChunkTypes5.INTERRUPT) {
33275
- if (chunk.type === MessageChunkTypes5.MESSAGE_COMPLETED) {
33950
+ if (chunk?.type === MessageChunkTypes4.MESSAGE_COMPLETED || chunk?.type === MessageChunkTypes4.MESSAGE_FAILED || chunk?.type === MessageChunkTypes4.INTERRUPT) {
33951
+ if (chunk.type === MessageChunkTypes4.MESSAGE_COMPLETED) {
33276
33952
  normalEnd = true;
33277
33953
  const grace = setTimeout(
33278
33954
  () => {
@@ -33281,14 +33957,14 @@ var _MessageRouter = class _MessageRouter {
33281
33957
  _MessageRouter.COMPLETION_GRACE_MS
33282
33958
  );
33283
33959
  grace.unref?.();
33284
- } else if (chunk.type === MessageChunkTypes5.MESSAGE_FAILED) {
33960
+ } else if (chunk.type === MessageChunkTypes4.MESSAGE_FAILED) {
33285
33961
  await closeWith("failed");
33286
33962
  } else {
33287
33963
  await closeWith("interrupted");
33288
33964
  }
33289
33965
  break;
33290
33966
  }
33291
- if (chunk?.type !== MessageChunkTypes5.AI) continue;
33967
+ if (chunk?.type !== MessageChunkTypes4.AI) continue;
33292
33968
  const text = typeof chunk.data?.content === "string" ? chunk.data.content : void 0;
33293
33969
  if (!text) continue;
33294
33970
  accumulated += text;
@@ -34589,11 +35265,11 @@ function readPluginDeploymentLimits(env = process.env) {
34589
35265
  };
34590
35266
  }
34591
35267
  function startPluginDeploymentRuntime(options) {
34592
- const { lease, holder, logger: logger8 } = options;
35268
+ const { lease, holder, logger: logger9 } = options;
34593
35269
  const forcedOn = process.env[PLUGIN_DEPLOYMENT_RUNTIME_FLAG] === "true" || process.env[PLUGIN_DEPLOYMENT_RUNTIME_FLAG] === "1";
34594
35270
  const effectiveServiceBaseUrl = options.wiring.serviceBaseUrl ?? process.env.MICROSANDBOX_SERVICE_BASE_URL;
34595
35271
  if (forcedOn && !effectiveServiceBaseUrl) {
34596
- logger8.warn(
35272
+ logger9.warn(
34597
35273
  `${PLUGIN_DEPLOYMENT_RUNTIME_FLAG} is enabled but MICROSANDBOX_SERVICE_BASE_URL is empty; skipping plugin deployment runtime`,
34598
35274
  { holder }
34599
35275
  );
@@ -34612,8 +35288,8 @@ function startPluginDeploymentRuntime(options) {
34612
35288
  };
34613
35289
  const service2 = createService(deps);
34614
35290
  register(service2);
34615
- logger8.info("plugin deployment runtime started", { holder });
34616
- const reconcile = runStartupReconcile(service2, reconcileDeadlineMs, logger8, holder);
35291
+ logger9.info("plugin deployment runtime started", { holder });
35292
+ const reconcile = runStartupReconcile(service2, reconcileDeadlineMs, logger9, holder);
34617
35293
  let held = false;
34618
35294
  let inFlight = false;
34619
35295
  const tick = async () => {
@@ -34621,18 +35297,18 @@ function startPluginDeploymentRuntime(options) {
34621
35297
  inFlight = true;
34622
35298
  try {
34623
35299
  if (held) {
34624
- held = await renewLease(lease, holder, logger8);
35300
+ held = await renewLease(lease, holder, logger9);
34625
35301
  if (!held) {
34626
- logger8.warn("plugin deployment lease lost; watchdog paused", { holder });
35302
+ logger9.warn("plugin deployment lease lost; watchdog paused", { holder });
34627
35303
  }
34628
35304
  } else {
34629
- held = await acquireLease(lease, holder, logger8);
35305
+ held = await acquireLease(lease, holder, logger9);
34630
35306
  if (held) {
34631
- logger8.info("plugin deployment lease acquired", { holder });
35307
+ logger9.info("plugin deployment lease acquired", { holder });
34632
35308
  }
34633
35309
  }
34634
35310
  if (!held) return;
34635
- await runWatchdog(service2, holder, logger8);
35311
+ await runWatchdog(service2, holder, logger9);
34636
35312
  } finally {
34637
35313
  inFlight = false;
34638
35314
  }
@@ -34648,7 +35324,7 @@ function startPluginDeploymentRuntime(options) {
34648
35324
  try {
34649
35325
  await lease.release(PLUGIN_DEPLOYMENT_LEASE_KEY, holder);
34650
35326
  } catch (error) {
34651
- logger8.warn("plugin deployment lease release failed", {
35327
+ logger9.warn("plugin deployment lease release failed", {
34652
35328
  holder,
34653
35329
  error: errorMessage8(error)
34654
35330
  });
@@ -34656,16 +35332,16 @@ function startPluginDeploymentRuntime(options) {
34656
35332
  held = false;
34657
35333
  }
34658
35334
  register(void 0);
34659
- logger8.info("plugin deployment runtime stopped", { holder });
35335
+ logger9.info("plugin deployment runtime stopped", { holder });
34660
35336
  }
34661
35337
  };
34662
35338
  }
34663
- async function runStartupReconcile(service2, deadlineMs, logger8, holder) {
35339
+ async function runStartupReconcile(service2, deadlineMs, logger9, holder) {
34664
35340
  try {
34665
35341
  await withDeadline(service2.startupReconcile(), deadlineMs);
34666
- logger8.info("plugin deployment startup reconcile complete", { holder });
35342
+ logger9.info("plugin deployment startup reconcile complete", { holder });
34667
35343
  } catch (error) {
34668
- logger8.warn("plugin deployment startup reconcile failed", {
35344
+ logger9.warn("plugin deployment startup reconcile failed", {
34669
35345
  holder,
34670
35346
  error: errorMessage8(error)
34671
35347
  });
@@ -34688,7 +35364,7 @@ async function withDeadline(work, deadlineMs) {
34688
35364
  if (timer !== void 0) clearTimeout(timer);
34689
35365
  }
34690
35366
  }
34691
- async function acquireLease(lease, holder, logger8) {
35367
+ async function acquireLease(lease, holder, logger9) {
34692
35368
  try {
34693
35369
  return await lease.tryAcquire(
34694
35370
  PLUGIN_DEPLOYMENT_LEASE_KEY,
@@ -34696,14 +35372,14 @@ async function acquireLease(lease, holder, logger8) {
34696
35372
  PLUGIN_DEPLOYMENT_LEASE_TTL_MS
34697
35373
  );
34698
35374
  } catch (error) {
34699
- logger8.warn("plugin deployment lease acquire failed", {
35375
+ logger9.warn("plugin deployment lease acquire failed", {
34700
35376
  holder,
34701
35377
  error: errorMessage8(error)
34702
35378
  });
34703
35379
  return false;
34704
35380
  }
34705
35381
  }
34706
- async function renewLease(lease, holder, logger8) {
35382
+ async function renewLease(lease, holder, logger9) {
34707
35383
  try {
34708
35384
  return await lease.renew(
34709
35385
  PLUGIN_DEPLOYMENT_LEASE_KEY,
@@ -34711,18 +35387,18 @@ async function renewLease(lease, holder, logger8) {
34711
35387
  PLUGIN_DEPLOYMENT_LEASE_TTL_MS
34712
35388
  );
34713
35389
  } catch (error) {
34714
- logger8.warn("plugin deployment lease renew failed", {
35390
+ logger9.warn("plugin deployment lease renew failed", {
34715
35391
  holder,
34716
35392
  error: errorMessage8(error)
34717
35393
  });
34718
35394
  return false;
34719
35395
  }
34720
35396
  }
34721
- async function runWatchdog(service2, holder, logger8) {
35397
+ async function runWatchdog(service2, holder, logger9) {
34722
35398
  try {
34723
35399
  await service2.healthWatchdog();
34724
35400
  } catch (error) {
34725
- logger8.warn("plugin deployment health watchdog failed", {
35401
+ logger9.warn("plugin deployment health watchdog failed", {
34726
35402
  holder,
34727
35403
  error: errorMessage8(error)
34728
35404
  });
@@ -34822,8 +35498,8 @@ function resolveRetentionMs(value) {
34822
35498
 
34823
35499
  // src/services/execution/threadTtlSweeper.ts
34824
35500
  import {
34825
- getReplicaId as getReplicaId10,
34826
- getStoreLattice as getStoreLattice39,
35501
+ getReplicaId as getReplicaId11,
35502
+ getStoreLattice as getStoreLattice40,
34827
35503
  tryAcquireThreadRun as tryAcquireThreadRun2
34828
35504
  } from "@axiom-lattice/core";
34829
35505
  function createThreadTtlSweeper(options) {
@@ -34841,7 +35517,7 @@ function createThreadTtlSweeper(options) {
34841
35517
  const result = { scanned: 0, pruned: 0, skipped: 0, errors: 0 };
34842
35518
  let expired;
34843
35519
  try {
34844
- expired = await getStoreLattice39("default", "thread").store.listExpiredThreads(now(), batchSize);
35520
+ expired = await getStoreLattice40("default", "thread").store.listExpiredThreads(now(), batchSize);
34845
35521
  } catch (error) {
34846
35522
  result.errors += 1;
34847
35523
  log2.warn("Thread TTL sweep failed to list expired threads", {
@@ -34854,11 +35530,11 @@ function createThreadTtlSweeper(options) {
34854
35530
  let lease;
34855
35531
  try {
34856
35532
  lease = await tryAcquireThreadRun2({
34857
- store: getStoreLattice39("default", "lease").store,
35533
+ store: getStoreLattice40("default", "lease").store,
34858
35534
  tenantId: thread.tenantId,
34859
35535
  assistantId: thread.assistantId,
34860
35536
  threadId: thread.id,
34861
- owner: getReplicaId10(),
35537
+ owner: getReplicaId11(),
34862
35538
  ttlMs: 6e4
34863
35539
  });
34864
35540
  } catch (error) {
@@ -34874,7 +35550,7 @@ function createThreadTtlSweeper(options) {
34874
35550
  continue;
34875
35551
  }
34876
35552
  try {
34877
- const queueStore = getStoreLattice39("default", "threadMessageQueue").store;
35553
+ const queueStore = getStoreLattice40("default", "threadMessageQueue").store;
34878
35554
  const pending2 = await queueStore.getPendingMessages(thread.id, queueScopeFromThread(thread));
34879
35555
  if (pending2.length > 0) {
34880
35556
  result.skipped += 1;
@@ -34951,7 +35627,7 @@ function createThreadTtlSweeper(options) {
34951
35627
  }
34952
35628
 
34953
35629
  // src/services/execution/storeTtlSweeper.ts
34954
- import { getStoreLattice as getStoreLattice40 } from "@axiom-lattice/core";
35630
+ import { getStoreLattice as getStoreLattice41 } from "@axiom-lattice/core";
34955
35631
  function parsePositiveInteger(value, fallback) {
34956
35632
  const parsed = Number(value);
34957
35633
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : fallback;
@@ -34974,7 +35650,7 @@ function createStoreTtlSweeper(options) {
34974
35650
  const performSweep = async () => {
34975
35651
  let store2;
34976
35652
  try {
34977
- store2 = getStoreLattice40("default", "store").store;
35653
+ store2 = getStoreLattice41("default", "store").store;
34978
35654
  } catch (error) {
34979
35655
  if (!warnedMissingStore) {
34980
35656
  warnedMissingStore = true;
@@ -36415,7 +37091,7 @@ function createAgentRunRetentionJob(input) {
36415
37091
  }
36416
37092
 
36417
37093
  // src/services/run-task/RedisRunTaskWake.ts
36418
- import { createClient as createClient4 } from "redis";
37094
+ import { createClient as createClient5 } from "redis";
36419
37095
  var RUN_TASK_WAKE_CHANNEL = "run-task:wake";
36420
37096
  var DEFAULT_OPERATION_TIMEOUT_MS = 2e3;
36421
37097
  var DEFAULT_RETRY_DELAYS_MS = [500, 2e3, 5e3, 1e4];
@@ -36808,7 +37484,7 @@ function createConfiguredRedisRunTaskWake(options) {
36808
37484
  return createRedisRunTaskWake({ ...options, url });
36809
37485
  }
36810
37486
  function createNodeRedisClient3(url) {
36811
- const client = createClient4({
37487
+ const client = createClient5({
36812
37488
  url,
36813
37489
  disableOfflineQueue: true,
36814
37490
  socket: {
@@ -36916,6 +37592,19 @@ function ingestRoomEvent(payload, broker) {
36916
37592
  return broker.ingestForeignEvent(serialized);
36917
37593
  }
36918
37594
 
37595
+ // src/services/channelInstallationSync.ts
37596
+ var CHANNEL_INSTALLATION_CHANGED_TOPIC = "channel-installation-changed";
37597
+ async function publishChannelInstallationChanged(outbox, owner, payload) {
37598
+ try {
37599
+ await outbox.append({
37600
+ topic: CHANNEL_INSTALLATION_CHANGED_TOPIC,
37601
+ payload,
37602
+ originReplicaId: owner
37603
+ });
37604
+ } catch {
37605
+ }
37606
+ }
37607
+
36919
37608
  // src/services/assistantRegistrySync.ts
36920
37609
  var ASSISTANT_CHANGE_EVENTS = ["assistant:created", "assistant:updated", "assistant:deleted"];
36921
37610
  var ASSISTANT_CHANGED_TOPIC = "assistant-changed";
@@ -37000,6 +37689,743 @@ import {
37000
37689
  REPLICA_HEADER as REPLICA_HEADER3,
37001
37690
  AFFINITY_ECHO_HEADER as AFFINITY_ECHO_HEADER3
37002
37691
  } from "@axiom-lattice/protocols";
37692
+
37693
+ // src/services/observability/LangfuseObservabilityProvider.ts
37694
+ import { Buffer as Buffer2 } from "buffer";
37695
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
37696
+ import { LangfuseSpanProcessor } from "@langfuse/otel";
37697
+ import { CallbackHandler } from "@langfuse/langchain";
37698
+ import {
37699
+ ObservabilityDashboardUnavailableError as ObservabilityDashboardUnavailableError2,
37700
+ ObservabilityDashboardUnsupportedError as ObservabilityDashboardUnsupportedError2
37701
+ } from "@axiom-lattice/core";
37702
+ import {
37703
+ LANGFUSE_OBSERVABILITY_DASHBOARD_VERSION
37704
+ } from "@axiom-lattice/protocols";
37705
+
37706
+ // src/services/observability/LangfuseDashboardQueries.ts
37707
+ var DASHBOARD_SECTIONS = [
37708
+ "traces",
37709
+ "agentTraces",
37710
+ "costTokens",
37711
+ "latency",
37712
+ "errors",
37713
+ "agents",
37714
+ "models",
37715
+ "tools",
37716
+ "toolCalls"
37717
+ ];
37718
+ var V1_METRICS_SECTIONS = /* @__PURE__ */ new Set([
37719
+ "traces",
37720
+ "agentTraces"
37721
+ ]);
37722
+ var RANGE_MS = {
37723
+ "1h": 60 * 60 * 1e3,
37724
+ "24h": 24 * 60 * 60 * 1e3,
37725
+ "7d": 7 * 24 * 60 * 60 * 1e3,
37726
+ "30d": 30 * 24 * 60 * 60 * 1e3
37727
+ };
37728
+ var GRANULARITY = {
37729
+ "1h": "minute",
37730
+ "24h": "hour",
37731
+ "7d": "hour",
37732
+ "30d": "day"
37733
+ };
37734
+ var ROOT_FILTER = {
37735
+ column: "isRootObservation",
37736
+ operator: "=",
37737
+ value: true,
37738
+ type: "boolean"
37739
+ };
37740
+ function typeFilter(value) {
37741
+ return {
37742
+ column: "type",
37743
+ operator: "any of",
37744
+ value: [value],
37745
+ type: "stringOptions"
37746
+ };
37747
+ }
37748
+ function buildDashboardQueries(input) {
37749
+ const now = input.now ?? /* @__PURE__ */ new Date();
37750
+ const fromTimestamp = new Date(
37751
+ now.getTime() - RANGE_MS[input.range]
37752
+ ).toISOString();
37753
+ const toTimestamp = now.toISOString();
37754
+ const filters = [
37755
+ {
37756
+ column: "metadata",
37757
+ operator: "=",
37758
+ value: input.tenantId,
37759
+ type: "stringObject",
37760
+ key: "x-tenant-id"
37761
+ }
37762
+ ];
37763
+ if (input.environment) {
37764
+ filters.push({
37765
+ column: "environment",
37766
+ operator: "=",
37767
+ value: input.environment,
37768
+ type: "string"
37769
+ });
37770
+ }
37771
+ const timeDimension = { granularity: GRANULARITY[input.range] };
37772
+ const window = { fromTimestamp, toTimestamp };
37773
+ return {
37774
+ traces: {
37775
+ view: "traces",
37776
+ metrics: [{ measure: "count", aggregation: "count" }],
37777
+ filters,
37778
+ timeDimension,
37779
+ ...window,
37780
+ config: { row_limit: 1e3 }
37781
+ },
37782
+ agentTraces: {
37783
+ view: "traces",
37784
+ metrics: [{ measure: "count", aggregation: "count" }],
37785
+ dimensions: [{ field: "tags" }, { field: "name" }],
37786
+ filters,
37787
+ orderBy: [{ field: "count_count", direction: "desc" }],
37788
+ ...window,
37789
+ config: { row_limit: 1e3 }
37790
+ },
37791
+ costTokens: {
37792
+ view: "observations",
37793
+ metrics: [
37794
+ { measure: "totalCost", aggregation: "sum" },
37795
+ { measure: "inputTokens", aggregation: "sum" },
37796
+ { measure: "outputTokens", aggregation: "sum" }
37797
+ ],
37798
+ dimensions: [{ field: "providedModelName" }],
37799
+ filters: [...filters, typeFilter("GENERATION")],
37800
+ timeDimension,
37801
+ ...window,
37802
+ config: { row_limit: 1e3 }
37803
+ },
37804
+ latency: {
37805
+ view: "observations",
37806
+ metrics: [
37807
+ { measure: "latency", aggregation: "p50" },
37808
+ { measure: "latency", aggregation: "p95" },
37809
+ { measure: "latency", aggregation: "p99" }
37810
+ ],
37811
+ filters: [...filters, ROOT_FILTER],
37812
+ timeDimension,
37813
+ ...window,
37814
+ config: { row_limit: 1e3 }
37815
+ },
37816
+ errors: {
37817
+ view: "observations",
37818
+ metrics: [{ measure: "count", aggregation: "count" }],
37819
+ dimensions: [{ field: "level" }],
37820
+ filters,
37821
+ timeDimension,
37822
+ ...window,
37823
+ config: { row_limit: 1e3 }
37824
+ },
37825
+ agents: {
37826
+ view: "observations",
37827
+ metrics: [
37828
+ { measure: "count", aggregation: "count" },
37829
+ { measure: "totalCost", aggregation: "sum" },
37830
+ { measure: "inputTokens", aggregation: "sum" },
37831
+ { measure: "outputTokens", aggregation: "sum" }
37832
+ ],
37833
+ dimensions: [{ field: "tags" }, { field: "level" }],
37834
+ filters,
37835
+ orderBy: [{ field: "count_count", direction: "desc" }],
37836
+ ...window,
37837
+ config: { row_limit: 1e3 }
37838
+ },
37839
+ models: {
37840
+ view: "observations",
37841
+ metrics: [
37842
+ { measure: "count", aggregation: "count" },
37843
+ { measure: "totalCost", aggregation: "sum" },
37844
+ { measure: "inputTokens", aggregation: "sum" },
37845
+ { measure: "outputTokens", aggregation: "sum" },
37846
+ { measure: "latency", aggregation: "p95" }
37847
+ ],
37848
+ dimensions: [{ field: "providedModelName" }],
37849
+ filters: [...filters, typeFilter("GENERATION")],
37850
+ orderBy: [{ field: "count_count", direction: "desc" }],
37851
+ ...window,
37852
+ config: { row_limit: 20 }
37853
+ },
37854
+ tools: {
37855
+ view: "observations",
37856
+ metrics: [{ measure: "count", aggregation: "count" }],
37857
+ dimensions: [{ field: "name" }],
37858
+ filters: [...filters, typeFilter("TOOL")],
37859
+ orderBy: [{ field: "count_count", direction: "desc" }],
37860
+ ...window,
37861
+ config: { row_limit: 20 }
37862
+ },
37863
+ toolCalls: {
37864
+ view: "observations",
37865
+ metrics: [{ measure: "count", aggregation: "count" }],
37866
+ filters: [...filters, typeFilter("TOOL")],
37867
+ timeDimension,
37868
+ ...window,
37869
+ config: { row_limit: 1e3 }
37870
+ }
37871
+ };
37872
+ }
37873
+ function rowsOf(data) {
37874
+ if (!data || typeof data !== "object") return [];
37875
+ const rows = data.data;
37876
+ if (!Array.isArray(rows)) return [];
37877
+ return rows.filter(
37878
+ (row) => row !== null && typeof row === "object" && !Array.isArray(row)
37879
+ );
37880
+ }
37881
+ function numberOf(row, key) {
37882
+ const value = row[key];
37883
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
37884
+ }
37885
+ function nullableNumber(row, key) {
37886
+ const value = row[key];
37887
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
37888
+ }
37889
+ function isoBucket(row) {
37890
+ const raw = row.time_dimension ?? row.timestamp;
37891
+ if (typeof raw !== "string" || raw.length === 0) return null;
37892
+ const parsed = new Date(raw);
37893
+ return Number.isFinite(parsed.getTime()) ? parsed.toISOString() : null;
37894
+ }
37895
+ function toSeries(data, key) {
37896
+ const points = [];
37897
+ for (const row of rowsOf(data)) {
37898
+ const bucket = isoBucket(row);
37899
+ if (!bucket) continue;
37900
+ points.push({ bucket, value: nullableNumber(row, key) });
37901
+ }
37902
+ return points.sort((a, b) => a.bucket.localeCompare(b.bucket));
37903
+ }
37904
+ function sumSeries(points) {
37905
+ return points.reduce((total, point) => total + (point.value ?? 0), 0);
37906
+ }
37907
+ function toAggregatedSeries(data, key) {
37908
+ const buckets = /* @__PURE__ */ new Map();
37909
+ for (const row of rowsOf(data)) {
37910
+ const bucket = isoBucket(row);
37911
+ if (!bucket) continue;
37912
+ buckets.set(bucket, (buckets.get(bucket) ?? 0) + (nullableNumber(row, key) ?? 0));
37913
+ }
37914
+ return [...buckets.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([bucket, value]) => ({ bucket, value }));
37915
+ }
37916
+ function parseCostByModel(data) {
37917
+ const byModel = /* @__PURE__ */ new Map();
37918
+ for (const row of rowsOf(data)) {
37919
+ const bucket = isoBucket(row);
37920
+ if (!bucket) continue;
37921
+ const model = typeof row.providedModelName === "string" && row.providedModelName ? row.providedModelName : "unknown";
37922
+ const points = byModel.get(model) ?? [];
37923
+ points.push({ bucket, value: nullableNumber(row, "sum_totalCost") });
37924
+ byModel.set(model, points);
37925
+ }
37926
+ return [...byModel.entries()].map(([model, points]) => ({
37927
+ model,
37928
+ points: points.sort((a, b) => a.bucket.localeCompare(b.bucket))
37929
+ })).filter((series) => sumSeries(series.points) !== 0).sort((a, b) => sumSeries(b.points) - sumSeries(a.points));
37930
+ }
37931
+ function parseLevelSeries(data) {
37932
+ const buckets = /* @__PURE__ */ new Map();
37933
+ for (const row of rowsOf(data)) {
37934
+ const bucket = isoBucket(row);
37935
+ if (!bucket) continue;
37936
+ const current3 = buckets.get(bucket) ?? { total: 0, errors: 0 };
37937
+ const count = numberOf(row, "count_count");
37938
+ current3.total += count;
37939
+ if (row.level === "ERROR") current3.errors += count;
37940
+ buckets.set(bucket, current3);
37941
+ }
37942
+ return [...buckets.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([bucket, value]) => ({
37943
+ bucket,
37944
+ value: value.total > 0 ? value.errors / value.total : 0
37945
+ }));
37946
+ }
37947
+ function parseModels(data) {
37948
+ return rowsOf(data).map((row) => ({
37949
+ model: typeof row.providedModelName === "string" && row.providedModelName ? row.providedModelName : "unknown",
37950
+ count: numberOf(row, "count_count"),
37951
+ costUsd: numberOf(row, "sum_totalCost"),
37952
+ inputTokens: numberOf(row, "sum_inputTokens"),
37953
+ outputTokens: numberOf(row, "sum_outputTokens"),
37954
+ p95LatencyMs: nullableNumber(row, "p95_latency")
37955
+ })).filter((row) => row.count > 0).sort((a, b) => b.count - a.count);
37956
+ }
37957
+ function assistantIdFromTags(tags) {
37958
+ if (!Array.isArray(tags)) return "unattributed";
37959
+ const assistantTag = tags.find(
37960
+ (tag) => typeof tag === "string" && tag.startsWith("assistant:")
37961
+ );
37962
+ if (!assistantTag) return "unattributed";
37963
+ const assistantId = assistantTag.slice("assistant:".length);
37964
+ return assistantId.length > 0 ? assistantId : "unattributed";
37965
+ }
37966
+ function parseAgentTraces(data) {
37967
+ const byAgent = /* @__PURE__ */ new Map();
37968
+ for (const row of rowsOf(data)) {
37969
+ const assistantId = assistantIdFromTags(row.tags);
37970
+ const current3 = byAgent.get(assistantId) ?? {
37971
+ calls: 0,
37972
+ names: /* @__PURE__ */ new Map()
37973
+ };
37974
+ const count = numberOf(row, "count_count");
37975
+ current3.calls += count;
37976
+ const name = typeof row.name === "string" ? row.name.trim() : "";
37977
+ if (name) {
37978
+ current3.names.set(name, (current3.names.get(name) ?? 0) + count);
37979
+ }
37980
+ byAgent.set(assistantId, current3);
37981
+ }
37982
+ return byAgent;
37983
+ }
37984
+ function parseAgents(observationData, traceData) {
37985
+ const observations = /* @__PURE__ */ new Map();
37986
+ for (const row of rowsOf(observationData)) {
37987
+ const assistantId = assistantIdFromTags(row.tags);
37988
+ const current3 = observations.get(assistantId) ?? {
37989
+ observations: 0,
37990
+ costUsd: 0,
37991
+ inputTokens: 0,
37992
+ outputTokens: 0,
37993
+ errorCount: 0
37994
+ };
37995
+ const count = numberOf(row, "count_count");
37996
+ current3.observations += count;
37997
+ current3.costUsd += numberOf(row, "sum_totalCost");
37998
+ current3.inputTokens += numberOf(row, "sum_inputTokens");
37999
+ current3.outputTokens += numberOf(row, "sum_outputTokens");
38000
+ if (row.level === "ERROR") current3.errorCount += count;
38001
+ observations.set(assistantId, current3);
38002
+ }
38003
+ const traces = parseAgentTraces(traceData);
38004
+ const ids = /* @__PURE__ */ new Set([...observations.keys(), ...traces.keys()]);
38005
+ return [...ids].map((assistantId) => {
38006
+ const observation = observations.get(assistantId) ?? {
38007
+ observations: 0,
38008
+ costUsd: 0,
38009
+ inputTokens: 0,
38010
+ outputTokens: 0,
38011
+ errorCount: 0
38012
+ };
38013
+ const trace = traces.get(assistantId);
38014
+ const bestName = trace ? [...trace.names.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] : void 0;
38015
+ return {
38016
+ assistantId,
38017
+ ...bestName ? { name: bestName } : {},
38018
+ calls: trace?.calls ?? 0,
38019
+ observations: observation.observations,
38020
+ costUsd: observation.costUsd,
38021
+ inputTokens: observation.inputTokens,
38022
+ outputTokens: observation.outputTokens,
38023
+ errorCount: observation.errorCount,
38024
+ errorRate: observation.observations > 0 ? observation.errorCount / observation.observations : 0
38025
+ };
38026
+ }).sort(
38027
+ (a, b) => b.calls - a.calls || b.observations - a.observations
38028
+ );
38029
+ }
38030
+ function parseTools(data) {
38031
+ return rowsOf(data).map((row) => ({
38032
+ tool: typeof row.name === "string" && row.name ? row.name : "unknown",
38033
+ count: numberOf(row, "count_count")
38034
+ })).filter((row) => row.count > 0);
38035
+ }
38036
+ function assembleLangfuseDashboard(input) {
38037
+ const warnings = DASHBOARD_SECTIONS.filter(
38038
+ (section) => input.responses[section] === void 0
38039
+ );
38040
+ const tracesSeries = toSeries(input.responses.traces, "count_count");
38041
+ const costSeries = toAggregatedSeries(
38042
+ input.responses.costTokens,
38043
+ "sum_totalCost"
38044
+ );
38045
+ const inputTokenSeries = toAggregatedSeries(
38046
+ input.responses.costTokens,
38047
+ "sum_inputTokens"
38048
+ );
38049
+ const outputTokenSeries = toAggregatedSeries(
38050
+ input.responses.costTokens,
38051
+ "sum_outputTokens"
38052
+ );
38053
+ const latencyP50 = toSeries(input.responses.latency, "p50_latency");
38054
+ const latencyP95 = toSeries(input.responses.latency, "p95_latency");
38055
+ const latencyP99 = toSeries(input.responses.latency, "p99_latency");
38056
+ const errorRateSeries = parseLevelSeries(input.responses.errors);
38057
+ const errorRows = rowsOf(input.responses.errors);
38058
+ const totalObservationCount = errorRows.reduce(
38059
+ (total, row) => total + numberOf(row, "count_count"),
38060
+ 0
38061
+ );
38062
+ const totalErrors = errorRows.filter((row) => row.level === "ERROR").reduce((total, row) => total + numberOf(row, "count_count"), 0);
38063
+ const traces = sumSeries(tracesSeries);
38064
+ return {
38065
+ warnings,
38066
+ payload: {
38067
+ samplingRate: input.samplingRate,
38068
+ kpis: {
38069
+ traces,
38070
+ costUsd: sumSeries(costSeries),
38071
+ inputTokens: sumSeries(inputTokenSeries),
38072
+ outputTokens: sumSeries(outputTokenSeries),
38073
+ errorRate: totalObservationCount > 0 ? totalErrors / totalObservationCount : 0
38074
+ },
38075
+ series: {
38076
+ traces: tracesSeries,
38077
+ cost: costSeries,
38078
+ tokens: {
38079
+ input: inputTokenSeries,
38080
+ output: outputTokenSeries
38081
+ },
38082
+ latency: {
38083
+ p50: latencyP50,
38084
+ p95: latencyP95,
38085
+ p99: latencyP99
38086
+ },
38087
+ errorRate: errorRateSeries,
38088
+ toolCalls: toSeries(input.responses.toolCalls, "count_count"),
38089
+ costByModel: parseCostByModel(input.responses.costTokens)
38090
+ },
38091
+ agents: parseAgents(
38092
+ input.responses.agents,
38093
+ input.responses.agentTraces
38094
+ ),
38095
+ models: parseModels(input.responses.models),
38096
+ tools: parseTools(input.responses.tools)
38097
+ }
38098
+ };
38099
+ }
38100
+
38101
+ // src/services/observability/LangfuseObservabilityProvider.ts
38102
+ function parseSampleRate(raw) {
38103
+ const trimmed = raw?.trim();
38104
+ if (!trimmed) return 1;
38105
+ const value = Number(trimmed);
38106
+ if (!Number.isFinite(value)) return 1;
38107
+ return Math.min(1, Math.max(0, value));
38108
+ }
38109
+ function readLangfuseConfig(env = process.env) {
38110
+ if (env.LANGFUSE_ENABLED?.trim() !== "true") return void 0;
38111
+ const publicKey = env.LANGFUSE_PUBLIC_KEY?.trim();
38112
+ const secretKey = env.LANGFUSE_SECRET_KEY?.trim();
38113
+ const baseUrl = env.LANGFUSE_BASE_URL?.trim();
38114
+ if (!publicKey || !secretKey || !baseUrl) return void 0;
38115
+ return {
38116
+ publicKey,
38117
+ secretKey,
38118
+ baseUrl,
38119
+ uiUrl: env.LANGFUSE_UI_URL?.trim() || baseUrl,
38120
+ ...env.LANGFUSE_RELEASE?.trim() ? { release: env.LANGFUSE_RELEASE.trim() } : {},
38121
+ ...env.LANGFUSE_TRACING_ENVIRONMENT?.trim() ? { environment: env.LANGFUSE_TRACING_ENVIRONMENT.trim() } : {},
38122
+ sampleRate: parseSampleRate(env.LANGFUSE_SAMPLE_RATE)
38123
+ };
38124
+ }
38125
+ var USAGE_KEYS = /* @__PURE__ */ new Set([
38126
+ "max_tokens",
38127
+ "input_tokens",
38128
+ "output_tokens",
38129
+ "token_usage"
38130
+ ]);
38131
+ var STRONG_SECRET_SUBSTRINGS = [
38132
+ "apikey",
38133
+ "authorization",
38134
+ "secret",
38135
+ "password",
38136
+ "passwd",
38137
+ "privatekey",
38138
+ "credential",
38139
+ "clientkey"
38140
+ ];
38141
+ var SECRET_SUBSTRINGS = [...STRONG_SECRET_SUBSTRINGS, "token"];
38142
+ function keyWords(key) {
38143
+ return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map((word) => word.toLowerCase());
38144
+ }
38145
+ function isSecretKey(key) {
38146
+ const words = keyWords(key);
38147
+ const snake = words.join("_");
38148
+ const compact2 = words.join("");
38149
+ const components = new Set(words);
38150
+ const strongSecret = STRONG_SECRET_SUBSTRINGS.some(
38151
+ (substring) => compact2.includes(substring)
38152
+ ) || components.has("private") && components.has("key") || components.has("api") && components.has("key");
38153
+ if (strongSecret) return true;
38154
+ const tokenUsage = components.has("token") && (components.has("count") || components.has("usage") || components.has("details"));
38155
+ if (USAGE_KEYS.has(snake) || snake.endsWith("_tokens") || snake.endsWith("token_usage") || tokenUsage) {
38156
+ return false;
38157
+ }
38158
+ return SECRET_SUBSTRINGS.some((substring) => compact2.includes(substring));
38159
+ }
38160
+ function redactText(value) {
38161
+ return value.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]").replace(/sk-[A-Za-z0-9_-]{8,}/g, "[redacted]");
38162
+ }
38163
+ function redactString(value) {
38164
+ const first = value.trimStart()[0];
38165
+ if (first === "{" || first === "[" || first === '"') {
38166
+ try {
38167
+ const redacted = JSON.stringify(redactLangfuseValue(JSON.parse(value)));
38168
+ return redacted ?? "null";
38169
+ } catch {
38170
+ }
38171
+ }
38172
+ return redactText(value);
38173
+ }
38174
+ function redactLangfuseValue(value) {
38175
+ if (typeof value === "string") return redactString(value);
38176
+ if (Array.isArray(value)) return value.map(redactLangfuseValue);
38177
+ if (typeof value === "object" && value !== null) {
38178
+ return Object.fromEntries(
38179
+ Object.entries(value).map(([key, nested]) => [
38180
+ key,
38181
+ isSecretKey(key) ? "[redacted]" : redactLangfuseValue(nested)
38182
+ ])
38183
+ );
38184
+ }
38185
+ return value;
38186
+ }
38187
+ function tagsFor(context) {
38188
+ return [
38189
+ `tenant:${context.tenantId}`,
38190
+ ...context.projectId ? [`project:${context.projectId}`] : [],
38191
+ `assistant:${context.assistantId}`,
38192
+ ...context.source ? [`source:${context.source}`] : []
38193
+ ];
38194
+ }
38195
+ function traceMetadataFor(context) {
38196
+ return {
38197
+ tenant_id: context.tenantId,
38198
+ ...context.projectId ? { project_id: context.projectId } : {},
38199
+ ...context.workspaceId ? { workspace_id: context.workspaceId } : {},
38200
+ assistant_id: context.assistantId,
38201
+ thread_id: context.threadId,
38202
+ run_id: context.runId,
38203
+ ...context.taskId ? { task_id: context.taskId } : {},
38204
+ ...context.source ? { source: context.source } : {}
38205
+ };
38206
+ }
38207
+ function createLangfuseProviderFromEnv(env = process.env, logger9) {
38208
+ const config = readLangfuseConfig(env);
38209
+ return config ? new LangfuseObservabilityProvider(config, logger9) : void 0;
38210
+ }
38211
+ var LangfuseMetricsHttpError = class extends Error {
38212
+ /**
38213
+ * @param status - HTTP status returned by Langfuse.
38214
+ */
38215
+ constructor(status) {
38216
+ super(`Langfuse metrics request failed with ${status}`);
38217
+ this.status = status;
38218
+ this.name = "LangfuseMetricsHttpError";
38219
+ }
38220
+ };
38221
+ var LangfuseObservabilityProvider = class {
38222
+ /**
38223
+ * @param config - Parsed Langfuse configuration.
38224
+ * @param logger - Optional best-effort lifecycle logger.
38225
+ */
38226
+ constructor(config, logger9) {
38227
+ this.config = config;
38228
+ this.logger = logger9;
38229
+ this.type = "langfuse";
38230
+ this.capabilities = {
38231
+ autoCallbacks: true,
38232
+ customObservations: false,
38233
+ scores: false,
38234
+ sessions: true,
38235
+ promptManagement: false
38236
+ };
38237
+ }
38238
+ warn(message, error) {
38239
+ try {
38240
+ this.logger?.warn(message, error);
38241
+ } catch {
38242
+ }
38243
+ }
38244
+ /**
38245
+ * Start the OpenTelemetry tracer provider exporting to Langfuse.
38246
+ *
38247
+ * @remarks
38248
+ * Never throws. If construction or registration fails, the provider is
38249
+ * marked not started and any constructed tracer provider is shut down
38250
+ * best-effort.
38251
+ */
38252
+ async start() {
38253
+ if (this.tracerProvider) return;
38254
+ let provider;
38255
+ let processor;
38256
+ try {
38257
+ processor = new LangfuseSpanProcessor({
38258
+ publicKey: this.config.publicKey,
38259
+ secretKey: this.config.secretKey,
38260
+ baseUrl: this.config.baseUrl,
38261
+ ...this.config.release ? { release: this.config.release } : {},
38262
+ ...this.config.environment ? { environment: this.config.environment } : {},
38263
+ mask: ({ data }) => redactLangfuseValue(data)
38264
+ });
38265
+ provider = new NodeTracerProvider({ spanProcessors: [processor] });
38266
+ provider.register();
38267
+ this.tracerProvider = provider;
38268
+ } catch (error) {
38269
+ if (provider) {
38270
+ try {
38271
+ await provider.shutdown();
38272
+ } catch (shutdownError) {
38273
+ this.warn(
38274
+ "langfuse observability start cleanup failed",
38275
+ shutdownError
38276
+ );
38277
+ }
38278
+ } else if (processor) {
38279
+ try {
38280
+ await processor.shutdown();
38281
+ } catch (shutdownError) {
38282
+ this.warn(
38283
+ "langfuse observability start cleanup failed",
38284
+ shutdownError
38285
+ );
38286
+ }
38287
+ }
38288
+ this.warn("langfuse observability start failed", error);
38289
+ }
38290
+ }
38291
+ /**
38292
+ * Flush pending spans and shut down the tracer provider.
38293
+ *
38294
+ * @remarks
38295
+ * Never throws and is idempotent. OpenTelemetry has no process-wide
38296
+ * unregister API, so the host must install exactly one provider instance per
38297
+ * process; stopping flushes spans but does not remove the global registration.
38298
+ */
38299
+ async stop() {
38300
+ const provider = this.tracerProvider;
38301
+ this.tracerProvider = void 0;
38302
+ if (!provider) return;
38303
+ try {
38304
+ await provider.shutdown();
38305
+ } catch (error) {
38306
+ this.warn("langfuse observability stop failed", error);
38307
+ }
38308
+ }
38309
+ /**
38310
+ * Fetches the Langfuse metrics dashboard for one tenant.
38311
+ *
38312
+ * @param input - Time range and server-resolved tenant scope.
38313
+ * @returns The normalized Langfuse payload.
38314
+ * @throws ObservabilityDashboardUnsupportedError for Langfuse < v4.
38315
+ * @throws ObservabilityDashboardUnavailableError when every query fails.
38316
+ */
38317
+ async getDashboard(input) {
38318
+ const queries = buildDashboardQueries({
38319
+ range: input.range,
38320
+ tenantId: input.tenantId,
38321
+ ...this.config.environment ? { environment: this.config.environment } : {}
38322
+ });
38323
+ const settled = await Promise.all(
38324
+ DASHBOARD_SECTIONS.map(async (section) => {
38325
+ try {
38326
+ const data = await this.fetchMetrics(queries[section], section);
38327
+ return { section, data, ok: true };
38328
+ } catch (error) {
38329
+ return { section, error, ok: false };
38330
+ }
38331
+ })
38332
+ );
38333
+ const failures = settled.filter((item) => !item.ok);
38334
+ if (failures.length === settled.length) {
38335
+ if (failures.some(
38336
+ (item) => !item.ok && item.error instanceof LangfuseMetricsHttpError && item.error.status === 404
38337
+ )) {
38338
+ throw new ObservabilityDashboardUnsupportedError2(
38339
+ "Langfuse metrics dashboard requires Langfuse v4+"
38340
+ );
38341
+ }
38342
+ throw new ObservabilityDashboardUnavailableError2(
38343
+ "Langfuse metrics dashboard is unavailable"
38344
+ );
38345
+ }
38346
+ const responses = {};
38347
+ for (const item of settled) {
38348
+ if (item.ok) responses[item.section] = item.data;
38349
+ }
38350
+ const assembled = assembleLangfuseDashboard({
38351
+ responses,
38352
+ samplingRate: this.config.sampleRate
38353
+ });
38354
+ return {
38355
+ version: LANGFUSE_OBSERVABILITY_DASHBOARD_VERSION,
38356
+ ...assembled.warnings.length > 0 ? { warnings: assembled.warnings } : {},
38357
+ payload: {
38358
+ ...assembled.payload,
38359
+ ...this.config.environment ? { environment: this.config.environment } : {}
38360
+ }
38361
+ };
38362
+ }
38363
+ async fetchMetrics(query, section) {
38364
+ const base = this.config.baseUrl.replace(/\/+$/, "");
38365
+ const path5 = V1_METRICS_SECTIONS.has(section) ? "/api/public/metrics" : "/api/public/v2/metrics";
38366
+ let url = new URL(`${base}${path5}`);
38367
+ url.searchParams.set("query", JSON.stringify(query));
38368
+ const auth = Buffer2.from(
38369
+ `${this.config.publicKey}:${this.config.secretKey}`
38370
+ ).toString("base64");
38371
+ const headers = {
38372
+ Authorization: `Basic ${auth}`,
38373
+ Accept: "application/json"
38374
+ };
38375
+ const signal = AbortSignal.timeout(1e4);
38376
+ for (let attempt = 0; attempt < 3; attempt++) {
38377
+ const response = await fetch(url, {
38378
+ headers,
38379
+ redirect: "manual",
38380
+ signal
38381
+ });
38382
+ if (response.status >= 300 && response.status < 400) {
38383
+ const location = response.headers.get("location");
38384
+ if (!location) throw new LangfuseMetricsHttpError(response.status);
38385
+ const next = new URL(location, url);
38386
+ if (next.protocol !== "http:" && next.protocol !== "https:") {
38387
+ throw new LangfuseMetricsHttpError(response.status);
38388
+ }
38389
+ url = next;
38390
+ continue;
38391
+ }
38392
+ if (!response.ok) throw new LangfuseMetricsHttpError(response.status);
38393
+ return response.json();
38394
+ }
38395
+ throw new LangfuseMetricsHttpError(508);
38396
+ }
38397
+ /**
38398
+ * Create a Langfuse callback handler for one invocation.
38399
+ *
38400
+ * @param context - Correlation fields for the running Agent invocation.
38401
+ * @returns One callback handler, or an empty list before start or when
38402
+ * sampled out.
38403
+ */
38404
+ createCallbacks(context) {
38405
+ if (!this.tracerProvider) return [];
38406
+ if (this.config.sampleRate <= 0) return [];
38407
+ if (this.config.sampleRate < 1 && Math.random() >= this.config.sampleRate) {
38408
+ return [];
38409
+ }
38410
+ return [
38411
+ new CallbackHandler({
38412
+ sessionId: context.threadId,
38413
+ userId: context.userId,
38414
+ tags: tagsFor(context),
38415
+ traceMetadata: traceMetadataFor(context),
38416
+ ...this.config.release ? { version: this.config.release } : {}
38417
+ })
38418
+ ];
38419
+ }
38420
+ /**
38421
+ * @returns The configured Langfuse UI trace-list URL.
38422
+ */
38423
+ getTraceListUrl() {
38424
+ return this.config.uiUrl;
38425
+ }
38426
+ };
38427
+
38428
+ // src/index.ts
37003
38429
  process.on("unhandledRejection", (reason, promise) => {
37004
38430
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
37005
38431
  });
@@ -37011,7 +38437,7 @@ var DEFAULT_LOGGER_CONFIG = {
37011
38437
  loggerName: "lattice/gateway"
37012
38438
  };
37013
38439
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
37014
- var logger7 = loggerLattice.client;
38440
+ var logger8 = loggerLattice.client;
37015
38441
  function initializeLogger(config) {
37016
38442
  if (loggerLatticeManager.hasLattice("default")) {
37017
38443
  loggerLatticeManager.removeLattice("default");
@@ -37021,7 +38447,7 @@ function initializeLogger(config) {
37021
38447
  }
37022
38448
  var app = fastify({
37023
38449
  logger: false,
37024
- bodyLimit: Number(process.env.BODY_LIMIT) || 100 * 1024 * 1024,
38450
+ bodyLimit: Number(process.env.BODY_LIMIT) || 500 * 1024 * 1024,
37025
38451
  // Composite subagent thread IDs ({mainThread}____{assistant}-general-purpose_{toolCallId})
37026
38452
  // can exceed the 100-char default, which makes the router 404 before any
37027
38453
  // handler runs. Allow the longest IDs the conventions can produce.
@@ -37081,6 +38507,7 @@ app.register(cors, {
37081
38507
  exposedHeaders: [
37082
38508
  "Content-Type",
37083
38509
  "Content-Disposition",
38510
+ "A2A-Version",
37084
38511
  REPLICA_HEADER3,
37085
38512
  AFFINITY_ECHO_HEADER3
37086
38513
  ],
@@ -37089,7 +38516,7 @@ app.register(cors, {
37089
38516
  app.register(sensible);
37090
38517
  app.register(multipart, {
37091
38518
  limits: {
37092
- fileSize: Number(process.env.BODY_LIMIT) || 100 * 1024 * 1024
38519
+ fileSize: Number(process.env.BODY_LIMIT) || 500 * 1024 * 1024
37093
38520
  }
37094
38521
  });
37095
38522
  app.register(staticPlugin, {
@@ -37102,7 +38529,7 @@ app.setErrorHandler((error, request, reply) => {
37102
38529
  "x-request-id": getHeaderValue(request.headers["x-request-id"]),
37103
38530
  "x-user-id": getHeaderValue(request.headers["x-user-id"])
37104
38531
  };
37105
- logger7.error(
38532
+ logger8.error(
37106
38533
  `\u8BF7\u6C42\u9519\u8BEF: ${request.method} ${request.url} error:${error.message}`,
37107
38534
  {
37108
38535
  ...context,
@@ -37135,17 +38562,17 @@ function getConfiguredSandboxProvider() {
37135
38562
  }
37136
38563
  async function restoreMcpConnections() {
37137
38564
  try {
37138
- const storeLattice = getStoreLattice41("default", "mcp");
38565
+ const storeLattice = getStoreLattice42("default", "mcp");
37139
38566
  const store2 = storeLattice.store;
37140
38567
  if (!store2) {
37141
- logger7.info("MCP store not configured, skipping connection restoration");
38568
+ logger8.info("MCP store not configured, skipping connection restoration");
37142
38569
  return;
37143
38570
  }
37144
38571
  const configs = (await store2.getAllConfigsWithoutTenant()).filter(
37145
38572
  (c2) => c2.status === "connected"
37146
38573
  );
37147
38574
  if (configs.length === 0) return;
37148
- logger7.info(`Restoring ${configs.length} MCP server connections...`);
38575
+ logger8.info(`Restoring ${configs.length} MCP server connections...`);
37149
38576
  const deps = {
37150
38577
  store: store2,
37151
38578
  manager: mcpManager3,
@@ -37162,15 +38589,15 @@ async function restoreMcpConnections() {
37162
38589
  results.forEach((result, index) => {
37163
38590
  if (result.status === "rejected") {
37164
38591
  const config = configs[index];
37165
- logger7.warn(
38592
+ logger8.warn(
37166
38593
  `Failed to restore MCP server "${config.tenantId}/${config.key}"`,
37167
38594
  { error: result.reason }
37168
38595
  );
37169
38596
  }
37170
38597
  });
37171
- logger7.info(`MCP connection restoration complete: ${configs.length} servers`);
38598
+ logger8.info(`MCP connection restoration complete: ${configs.length} servers`);
37172
38599
  } catch (err) {
37173
- logger7.error("MCP connection restoration failed", { error: err });
38600
+ logger8.error("MCP connection restoration failed", { error: err });
37174
38601
  }
37175
38602
  }
37176
38603
  var start = async (config) => {
@@ -37183,16 +38610,37 @@ var start = async (config) => {
37183
38610
  file: config.loggerConfig.file || DEFAULT_LOGGER_CONFIG.file
37184
38611
  };
37185
38612
  loggerLattice = initializeLogger(loggerConfig);
37186
- logger7 = loggerLattice.client;
38613
+ logger8 = loggerLattice.client;
37187
38614
  }
37188
38615
  app.decorate("loggerLattice", loggerLattice);
38616
+ const observabilityProvider = observabilityLatticeManager.getDefault();
38617
+ if (observabilityProvider) {
38618
+ try {
38619
+ await observabilityProvider.start?.();
38620
+ } catch (error) {
38621
+ logger8.warn("Observability provider failed to start", {
38622
+ error: error instanceof Error ? error.message : String(error)
38623
+ });
38624
+ }
38625
+ app.addHook("onClose", async () => {
38626
+ try {
38627
+ await observabilityProvider.stop?.();
38628
+ } catch (error) {
38629
+ logger8.warn("Observability provider failed to stop", {
38630
+ error: error instanceof Error ? error.message : String(error)
38631
+ });
38632
+ }
38633
+ });
38634
+ }
37189
38635
  let executionSupervisor;
37190
38636
  let runTaskRuntime;
37191
38637
  let runTaskWake;
37192
38638
  let agentRunRetentionJob;
38639
+ let scheduleReconcileTimer;
38640
+ const channelLeaders = [];
37193
38641
  let runLedger;
37194
38642
  try {
37195
- runLedger = getStoreLattice41("default", "agentRun").store;
38643
+ runLedger = getStoreLattice42("default", "agentRun").store;
37196
38644
  } catch {
37197
38645
  }
37198
38646
  if (runLedger) agentInstanceManager13.setRunLedger(runLedger);
@@ -37220,7 +38668,7 @@ var start = async (config) => {
37220
38668
  chunkSourceHandle = await createRedisAppendLog(redisUrl, {
37221
38669
  keyPrefix: chunkKeyPrefix,
37222
38670
  logger: {
37223
- warn: (message, context) => logger7.warn(message, context)
38671
+ warn: (message, context) => logger8.warn(message, context)
37224
38672
  }
37225
38673
  });
37226
38674
  const sharedBuffer = new SharedChunkBuffer2({
@@ -37234,7 +38682,7 @@ var start = async (config) => {
37234
38682
  chunkBufferManager.remove("default");
37235
38683
  }
37236
38684
  registerChunkBuffer("default", sharedBuffer);
37237
- logger7.info("Shared Redis chunk source enabled", {
38685
+ logger8.info("Shared Redis chunk source enabled", {
37238
38686
  phase: "chunk_source",
37239
38687
  ttlMs,
37240
38688
  maxLen
@@ -37246,7 +38694,7 @@ var start = async (config) => {
37246
38694
  if (runLedger) {
37247
38695
  runCancelNotifier = createRedisRunCancelNotifier({
37248
38696
  url: runCancelRedisUrl,
37249
- owner: getReplicaId11(),
38697
+ owner: getReplicaId12(),
37250
38698
  ledger: runLedger,
37251
38699
  resolveAgent: (params) => (
37252
38700
  // Never use getAgent() here: creating an idle instance lets its
@@ -37254,14 +38702,14 @@ var start = async (config) => {
37254
38702
  resolveManagedRunCancelAgent(agentInstanceManager13, params)
37255
38703
  ),
37256
38704
  logger: {
37257
- warn: (message, context) => logger7.warn(message, context),
37258
- info: (message, context) => logger7.info(message, context)
38705
+ warn: (message, context) => logger8.warn(message, context),
38706
+ info: (message, context) => logger8.info(message, context)
37259
38707
  }
37260
38708
  });
37261
38709
  try {
37262
38710
  await runCancelNotifier.start();
37263
38711
  } catch (error) {
37264
- logger7.warn("Run cancel subscriber failed to start", {
38712
+ logger8.warn("Run cancel subscriber failed to start", {
37265
38713
  error: error instanceof Error ? error.message : String(error)
37266
38714
  });
37267
38715
  }
@@ -37269,13 +38717,13 @@ var start = async (config) => {
37269
38717
  ledger: runLedger,
37270
38718
  notifier: runCancelNotifier,
37271
38719
  logger: {
37272
- warn: (message, context) => logger7.warn(message, context)
38720
+ warn: (message, context) => logger8.warn(message, context)
37273
38721
  }
37274
38722
  }));
37275
38723
  } else {
37276
- logger7.warn(
38724
+ logger8.warn(
37277
38725
  "Run cancel subscription disabled: the agent run ledger is not configured",
37278
- { owner: getReplicaId11() }
38726
+ { owner: getReplicaId12() }
37279
38727
  );
37280
38728
  }
37281
38729
  }
@@ -37287,7 +38735,7 @@ var start = async (config) => {
37287
38735
  ),
37288
38736
  keyPrefix: chunkKeyPrefix,
37289
38737
  logger: {
37290
- warn: (message, context) => logger7.warn(message, context)
38738
+ warn: (message, context) => logger8.warn(message, context)
37291
38739
  }
37292
38740
  });
37293
38741
  setInterruptAnswerRegistry(interruptAnswerRegistry);
@@ -37295,10 +38743,10 @@ var start = async (config) => {
37295
38743
  let projectRoomRealtimeRuntime;
37296
38744
  if (projectRoomRealtimeMode === "single-gateway") {
37297
38745
  projectRoomRealtimeRuntime = createProjectRoomRealtimeRuntime({
37298
- tasks: getStoreLattice41("default", "task").store,
37299
- rooms: getStoreLattice41("default", "projectRoom").store,
37300
- memberships: getStoreLattice41("default", "projectBotMembership").store,
37301
- log: { warn: (message, context) => logger7.warn(message, context) }
38746
+ tasks: getStoreLattice42("default", "task").store,
38747
+ rooms: getStoreLattice42("default", "projectRoom").store,
38748
+ memberships: getStoreLattice42("default", "projectBotMembership").store,
38749
+ log: { warn: (message, context) => logger8.warn(message, context) }
37302
38750
  });
37303
38751
  }
37304
38752
  let channelDeps;
@@ -37306,14 +38754,14 @@ var start = async (config) => {
37306
38754
  adapterRegistry.register(larkChannelAdapter);
37307
38755
  adapterRegistry.register(wecomChannelAdapter);
37308
38756
  registerWecomCardTool();
37309
- const { wechatChannelAdapter } = await import("./WechatChannelAdapter-7WHE5UGY.mjs");
38757
+ const { wechatChannelAdapter } = await import("./WechatChannelAdapter-QLPJJD6S.mjs");
37310
38758
  adapterRegistry.register(wechatChannelAdapter);
37311
38759
  const durableChannelRuns = process.env.CHANNEL_DURABLE_RUNS !== "false";
37312
38760
  let channelReplyDispatcher;
37313
38761
  try {
37314
38762
  const infrastructure = createChannelInfrastructure({
37315
38763
  adapterRegistry,
37316
- getStore: ((kind) => getStoreLattice41("default", kind).store),
38764
+ getStore: ((kind) => getStoreLattice42("default", kind).store),
37317
38765
  trustedDispatcher: createTrustedAgentMessageDispatcher2((scope) => agentInstanceManager13.getAgent(scope)),
37318
38766
  middlewares: [
37319
38767
  createDeduplicationMiddleware(),
@@ -37321,7 +38769,7 @@ var start = async (config) => {
37321
38769
  createAuditLoggerMiddleware()
37322
38770
  ],
37323
38771
  durableChannelRuns,
37324
- warn: (message, context) => logger7.warn(message, context),
38772
+ warn: (message, context) => logger8.warn(message, context),
37325
38773
  realtime: projectRoomRealtimeRuntime?.publisher
37326
38774
  });
37327
38775
  const bindingStore = infrastructure.bindingRegistry;
@@ -37335,25 +38783,25 @@ var start = async (config) => {
37335
38783
  installationStore,
37336
38784
  bindingRegistry: bindingStore,
37337
38785
  realtime: projectRoomRealtimeRuntime?.publisher,
37338
- warn: (message, context) => logger7.warn(message, context)
38786
+ warn: (message, context) => logger8.warn(message, context)
37339
38787
  };
37340
38788
  } catch (err) {
37341
- logger7.warn("Channel infrastructure unavailable", {
38789
+ logger8.warn("Channel infrastructure unavailable", {
37342
38790
  error: err instanceof Error ? err.message : String(err)
37343
38791
  });
37344
38792
  }
37345
38793
  if (channelDeps && durableChannelRuns) {
37346
38794
  try {
37347
38795
  channelReplyDispatcher = new ChannelReplyDispatcher({
37348
- jobStore: getStoreLattice41("default", "channelReplyJob").store,
37349
- runLedger: getStoreLattice41("default", "agentRun").store,
38796
+ jobStore: getStoreLattice42("default", "channelReplyJob").store,
38797
+ runLedger: getStoreLattice42("default", "agentRun").store,
37350
38798
  loadFinalReplyText: createCheckpointFinalReplyLoader(),
37351
38799
  adapterRegistry: channelDeps.adapterRegistry,
37352
38800
  installationStore: channelDeps.installationStore,
37353
- owner: getReplicaId11(),
38801
+ owner: getReplicaId12(),
37354
38802
  logger: {
37355
- info: (message, context) => logger7.info(message, context),
37356
- warn: (message, context) => logger7.warn(message, context)
38803
+ info: (message, context) => logger8.info(message, context),
38804
+ warn: (message, context) => logger8.warn(message, context)
37357
38805
  }
37358
38806
  });
37359
38807
  const dispatcher = channelReplyDispatcher;
@@ -37363,38 +38811,54 @@ var start = async (config) => {
37363
38811
  await dispatcher.stop();
37364
38812
  });
37365
38813
  dispatcher.start();
37366
- logger7.info("Durable channel replies enabled", { owner: getReplicaId11() });
38814
+ logger8.info("Durable channel replies enabled", { owner: getReplicaId12() });
37367
38815
  } catch (err) {
37368
- logger7.warn("Durable channel replies unavailable", {
38816
+ logger8.warn("Durable channel replies unavailable", {
37369
38817
  error: err instanceof Error ? err.message : String(err)
37370
38818
  });
37371
38819
  }
37372
38820
  }
37373
38821
  setEvalRunService(evalRunner);
37374
38822
  try {
37375
- const menuStore = getStoreLattice41("default", "menu").store;
38823
+ const menuStore = getStoreLattice42("default", "menu").store;
37376
38824
  setMenuRegistry(menuStore);
37377
- logger7.info("Menu registry initialized");
38825
+ logger8.info("Menu registry initialized");
37378
38826
  } catch {
37379
38827
  }
37380
38828
  registerAllBuiltinEntities();
37381
38829
  if (channelDeps?.router) {
37382
- setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
38830
+ setChannelControllerDeps({
38831
+ adapterRegistry,
38832
+ router: channelDeps.router,
38833
+ onInstallationChanged: async ({ tenantId, installationId }) => {
38834
+ try {
38835
+ const outbox = getStoreLattice42("default", "outbox").store;
38836
+ await publishChannelInstallationChanged(outbox, getReplicaId12(), {
38837
+ tenantId,
38838
+ installationId
38839
+ });
38840
+ } catch (err) {
38841
+ logger8.warn("Channel installation change wake failed", {
38842
+ error: err instanceof Error ? err.message : String(err)
38843
+ });
38844
+ }
38845
+ }
38846
+ });
37383
38847
  }
37384
38848
  await app.register(websocket);
37385
38849
  const routes = registerLatticeRoutes(app, channelDeps, {
37386
38850
  projectTaskEnabled: process.env.AXIOM_PROJECT_TASK_EXECUTION_ENABLED !== "false",
37387
- projectTaskLog: { warn: (message, context) => logger7.warn(message, context) },
38851
+ projectTaskLog: { warn: (message, context) => logger8.warn(message, context) },
37388
38852
  projectRoomRealtimeRuntime
37389
38853
  });
37390
38854
  try {
37391
38855
  const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await import("./A2AAuthService-7UGQ7GMW.mjs");
37392
- const { registerA2AStandardRoutes } = await import("./a2a-standard-DP53K7RC.mjs");
38856
+ const { registerA2AStandardRoutes } = await import("./a2a-standard-VL5I7B4W.mjs");
37393
38857
  const a2a = await import("./a2a-BHBLACJV.mjs");
37394
- const a2aKeyStore = getStoreLattice41("default", "a2aApiKey").store;
37395
- const assistantStore = getStoreLattice41("default", "assistant").store;
37396
- const projectStore = getStoreLattice41("default", "project").store;
37397
- const taskStore = getStoreLattice41("default", "task").store;
38858
+ const a2aKeyStore = getStoreLattice42("default", "a2aApiKey").store;
38859
+ const assistantStore = getStoreLattice42("default", "assistant").store;
38860
+ const projectStore = getStoreLattice42("default", "project").store;
38861
+ const taskStore = getStoreLattice42("default", "task").store;
37398
38862
  const authService = new A2AAuthService2({
37399
38863
  keyStore: a2aKeyStore,
37400
38864
  assistantStore,
@@ -37412,19 +38876,19 @@ var start = async (config) => {
37412
38876
  getProjectStore: () => projectStore
37413
38877
  });
37414
38878
  a2a.registerA2ARoutes(app);
37415
- logger7.info("A2A standard routes registered");
38879
+ logger8.info("A2A standard routes registered");
37416
38880
  } catch (err) {
37417
- logger7.warn("A2A standard routes unavailable", {
38881
+ logger8.warn("A2A standard routes unavailable", {
37418
38882
  error: err instanceof Error ? err.message : String(err)
37419
38883
  });
37420
38884
  }
37421
38885
  try {
37422
- const { registerOpenRoutes } = await import("./open-NTH7AGD3.mjs");
38886
+ const { registerOpenRoutes } = await import("./open-7ED27I5B.mjs");
37423
38887
  const { registerOpenSessionRoutes } = await import("./open-session-2Y7XJJW2.mjs");
37424
38888
  const { registerOpenCatalogRoutes } = await import("./open-catalog-SZQ435AY.mjs");
37425
38889
  const { registerOpenFilesRoutes } = await import("./open-files-V3R2BFTV.mjs");
37426
38890
  const { registerOpenAuditRoutes } = await import("./open-audit-UJ3NXCGW.mjs");
37427
- const { registerOpenExposureRoutes } = await import("./open-exposure-AGNG4MC3.mjs");
38891
+ const { registerOpenExposureRoutes } = await import("./open-exposure-I3DN2X4E.mjs");
37428
38892
  const { openLattice } = await import("./openLattice-Q6M5S6CY.mjs");
37429
38893
  const { OpenRateLimiter } = await import("./openRateLimiter-XEOL3A5G.mjs");
37430
38894
  const { createKbProvider } = await import("./kbProvider-2D6DZ34N.mjs");
@@ -37434,8 +38898,8 @@ var start = async (config) => {
37434
38898
  const { CollectionService: CollectionService2 } = await import("./collection_service-RTRJEQFL.mjs");
37435
38899
  openLattice.register(createKbProvider(new CollectionService2()));
37436
38900
  openLattice.register(createOpenSandboxProvider());
37437
- const threadStore = getStoreLattice41("default", "thread").store;
37438
- const assistantStore = getStoreLattice41("default", "assistant").store;
38901
+ const threadStore = getStoreLattice42("default", "thread").store;
38902
+ const assistantStore = getStoreLattice42("default", "assistant").store;
37439
38903
  openLattice.register(
37440
38904
  createAgentProvider({
37441
38905
  threadStore,
@@ -37463,32 +38927,32 @@ var start = async (config) => {
37463
38927
  registerOpenFilesRoutes(app);
37464
38928
  registerOpenAuditRoutes(app);
37465
38929
  registerOpenExposureRoutes(app);
37466
- logger7.info("Open API routes registered (kb + sandbox providers, session exchange)");
38930
+ logger8.info("Open API routes registered (kb + sandbox providers, session exchange)");
37467
38931
  } catch (err) {
37468
- logger7.warn("Open API routes unavailable", {
38932
+ logger8.warn("Open API routes unavailable", {
37469
38933
  error: err instanceof Error ? err.message : String(err)
37470
38934
  });
37471
38935
  }
37472
38936
  try {
37473
38937
  const { registerGatewaySandboxOpenCredentialProvider } = await import("./sandboxOpenCredentialProvider-AT227D4N.mjs");
37474
38938
  registerGatewaySandboxOpenCredentialProvider((error) => {
37475
- logger7.warn("sandbox open-credential provider failed", { error });
38939
+ logger8.warn("sandbox open-credential provider failed", { error });
37476
38940
  });
37477
- logger7.info("Sandbox open-credential provider registered");
38941
+ logger8.info("Sandbox open-credential provider registered");
37478
38942
  } catch (err) {
37479
- logger7.warn("Sandbox open-credential provider unavailable", {
38943
+ logger8.warn("Sandbox open-credential provider unavailable", {
37480
38944
  error: err instanceof Error ? err.message : String(err)
37481
38945
  });
37482
38946
  }
37483
38947
  if (!sandboxLatticeManager2.hasLattice("default")) {
37484
38948
  sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
37485
- logger7.info("Registered sandbox manager from env configuration");
38949
+ logger8.info("Registered sandbox manager from env configuration");
37486
38950
  }
37487
38951
  if (isPluginDeploymentRuntimeEnabled()) {
37488
38952
  try {
37489
- const pluginDeploymentStore = getStoreLattice41("default", "pluginDeployment").store;
37490
- const tenantStore = getStoreLattice41("default", "tenant").store;
37491
- const leaseStore2 = getStoreLattice41("default", "lease").store;
38953
+ const pluginDeploymentStore = getStoreLattice42("default", "pluginDeployment").store;
38954
+ const tenantStore = getStoreLattice42("default", "tenant").store;
38955
+ const leaseStore2 = getStoreLattice42("default", "lease").store;
37492
38956
  const listAllDeployments = async () => {
37493
38957
  const tenants = await tenantStore.getAllTenants();
37494
38958
  const lists = await Promise.all(
@@ -37509,90 +38973,112 @@ var start = async (config) => {
37509
38973
  listAllDeployments,
37510
38974
  // The service logs fields-first; the gateway logger is message-first.
37511
38975
  logger: {
37512
- info: (fields, message) => logger7.info(message ?? "plugin deployment", fields),
37513
- warn: (fields, message) => logger7.warn(message ?? "plugin deployment", fields),
37514
- error: (fields, message) => logger7.error(message ?? "plugin deployment", fields)
38976
+ info: (fields, message) => logger8.info(message ?? "plugin deployment", fields),
38977
+ warn: (fields, message) => logger8.warn(message ?? "plugin deployment", fields),
38978
+ error: (fields, message) => logger8.error(message ?? "plugin deployment", fields)
37515
38979
  }
37516
38980
  },
37517
38981
  lease: leaseStore2,
37518
- holder: getReplicaId11(),
37519
- logger: logger7
38982
+ holder: getReplicaId12(),
38983
+ logger: logger8
37520
38984
  });
37521
38985
  app.addHook("onClose", async () => {
37522
38986
  await pluginDeploymentRuntime.dispose();
37523
38987
  });
37524
- logger7.info("Plugin deployment runtime started");
38988
+ logger8.info("Plugin deployment runtime started");
37525
38989
  } catch (err) {
37526
- logger7.warn("Plugin deployment runtime unavailable", {
38990
+ logger8.warn("Plugin deployment runtime unavailable", {
37527
38991
  error: err instanceof Error ? err.message : String(err)
37528
38992
  });
37529
38993
  }
37530
38994
  }
37531
38995
  try {
37532
38996
  const { ResourceController } = await import("./resources-DAU5ZIPB.mjs");
37533
- const sharedResourceStore = getStoreLattice41("default", "sharedResource").store;
38997
+ const sharedResourceStore = getStoreLattice42("default", "sharedResource").store;
37534
38998
  const cache = new TokenCache();
37535
38999
  const resourceController = new ResourceController({
37536
39000
  store: sharedResourceStore,
37537
39001
  sandboxManager: sandboxLatticeManager2,
37538
39002
  cache,
37539
- logger: logger7
39003
+ logger: logger8
37540
39004
  });
37541
39005
  registerResourceRoutes(app, resourceController);
37542
- logger7.info("Resource share routes registered");
39006
+ logger8.info("Resource share routes registered");
37543
39007
  } catch (err) {
37544
- logger7.warn("Resource share infrastructure unavailable", {
39008
+ logger8.warn("Resource share infrastructure unavailable", {
37545
39009
  error: err instanceof Error ? err.message : String(err)
37546
39010
  });
37547
39011
  }
37548
39012
  if (channelDeps && process.env.CHANNELS_ENABLED !== "false") {
37549
39013
  try {
37550
- const channelLeader = new ChannelConnectionLeader({
37551
- leaseStore: getStoreLattice41("default", "lease").store,
37552
- owner: getReplicaId11(),
37553
- connect: () => connectAllChannels((channel) => adapterRegistry.get(channel), {
37554
- deps: { router: channelDeps.router }
37555
- }),
37556
- disconnect: () => disconnectAllChannels((channel) => adapterRegistry.get(channel)),
37557
- log: { warn: (message, context) => logger7.warn(message, context) }
37558
- });
39014
+ const shardCount = parsePositiveInteger2(process.env.CHANNEL_SHARDS, 1);
39015
+ const leaseStore2 = getStoreLattice42("default", "lease").store;
39016
+ const installationStore = getStoreLattice42("default", "channelInstallation").store;
39017
+ for (let shard = 0; shard < shardCount; shard += 1) {
39018
+ const leader = new ChannelConnectionLeader({
39019
+ leaseStore: leaseStore2,
39020
+ owner: getReplicaId12(),
39021
+ shard,
39022
+ shardCount,
39023
+ loadDesired: () => installationStore.getAllInstallations(),
39024
+ connectInstallation: async (installation) => {
39025
+ const adapter = adapterRegistry.get(installation.channel);
39026
+ await adapter?.connect?.(installation, { router: channelDeps.router });
39027
+ },
39028
+ disconnectInstallation: async (installation) => {
39029
+ const adapter = adapterRegistry.get(installation.channel);
39030
+ await adapter?.disconnect?.(installation.id);
39031
+ },
39032
+ fingerprint: (installation) => JSON.stringify({
39033
+ channel: installation.channel,
39034
+ config: installation.config,
39035
+ fallbackAgentId: installation.fallbackAgentId,
39036
+ rejectWhenNoBinding: installation.rejectWhenNoBinding
39037
+ }),
39038
+ log: { warn: (message, context) => logger8.warn(message, context) }
39039
+ });
39040
+ channelLeaders.push(leader);
39041
+ leader.start();
39042
+ }
37559
39043
  app.addHook("onClose", async () => {
37560
- await channelLeader.stop();
39044
+ await Promise.all(channelLeaders.map((leader) => leader.stop()));
37561
39045
  });
37562
- channelLeader.start();
37563
39046
  } catch (err) {
37564
- logger7.error("Failed to start channel connections", {
39047
+ logger8.error("Failed to start channel connections", {
37565
39048
  error: err instanceof Error ? err.message : String(err)
37566
39049
  });
37567
39050
  }
37568
39051
  }
37569
39052
  const target_port = config?.port || Number(process.env.PORT) || 4001;
37570
- configureCoordinationLeaseStore(getStoreLattice41("default", "lease").store);
37571
- const outboxStore = getStoreLattice41("default", "outbox").store;
39053
+ configureCoordinationLeaseStore(getStoreLattice42("default", "lease").store);
39054
+ const outboxStore = getStoreLattice42("default", "outbox").store;
37572
39055
  taskLifecycleNotifier3.setOutbox(outboxStore);
37573
39056
  projectTaskAdmissionNotifier2.setOutbox(outboxStore);
37574
- configurePluginCatalogSync({ outbox: outboxStore, owner: getReplicaId11() });
37575
- configureCollectionSync({ outbox: outboxStore, owner: getReplicaId11() });
39057
+ configurePluginCatalogSync({ outbox: outboxStore, owner: getReplicaId12() });
39058
+ configureCollectionSync({ outbox: outboxStore, owner: getReplicaId12() });
37576
39059
  bridgeAssistantEvents(
37577
39060
  (event, cb) => eventBus3.subscribe(event, cb),
37578
39061
  outboxStore,
37579
- getReplicaId11()
39062
+ getReplicaId12()
37580
39063
  );
37581
- installAgentLifecycleSync({ outbox: outboxStore, owner: getReplicaId11() });
39064
+ installAgentLifecycleSync({ outbox: outboxStore, owner: getReplicaId12() });
37582
39065
  if (projectRoomRealtimeRuntime) {
37583
39066
  projectRoomRealtimeRuntime.broker.setBroadcastHook((scope, serialized, sourceKey) => {
37584
- void publishRoomEvent(outboxStore, getReplicaId11(), scope, serialized, sourceKey);
39067
+ void publishRoomEvent(outboxStore, getReplicaId12(), scope, serialized, sourceKey);
37585
39068
  });
37586
39069
  }
37587
39070
  const outboxPoller = new OutboxPoller({
37588
39071
  store: outboxStore,
37589
- owner: getReplicaId11(),
39072
+ owner: getReplicaId12(),
37590
39073
  handlers: {
37591
39074
  "task-lifecycle": (payload) => taskLifecycleNotifier3.deliverFromOutbox(payload),
37592
39075
  "project-task-admission": (payload) => projectTaskAdmissionNotifier2.deliverFromOutbox(payload),
37593
39076
  [ROOM_EVENT_TOPIC]: async (payload) => {
37594
39077
  if (projectRoomRealtimeRuntime) ingestRoomEvent(payload, projectRoomRealtimeRuntime.broker);
37595
39078
  },
39079
+ [CHANNEL_INSTALLATION_CHANGED_TOPIC]: async () => {
39080
+ for (const leader of channelLeaders) leader.wake();
39081
+ },
37596
39082
  "assistant-changed": (payload) => handleAssistantChanged(payload, async (tenantId) => {
37597
39083
  await agentLatticeManager6.initializeStoredAssistantsForTenant(tenantId);
37598
39084
  invalidateAgentReflectionCache(tenantId);
@@ -37603,7 +39089,7 @@ var start = async (config) => {
37603
39089
  },
37604
39090
  [A2A_KEY_CHANGED_TOPIC]: (payload) => handleA2AKeyChanged(payload, () => refreshStoreKeyMap()),
37605
39091
  [MCP_CONFIG_CHANGED_TOPIC]: (payload) => reconcileMcpConfigChange(payload, {
37606
- store: getStoreLattice41("default", "mcp").store,
39092
+ store: getStoreLattice42("default", "mcp").store,
37607
39093
  manager: mcpManager3,
37608
39094
  convertToConnection: convertToConnection2
37609
39095
  }),
@@ -37628,31 +39114,32 @@ var start = async (config) => {
37628
39114
  await handleModelProviderChanged(payload, (tenantId, providerId2) => controller.reconcileProvider(tenantId, providerId2));
37629
39115
  }
37630
39116
  },
37631
- log: logger7
39117
+ log: logger8
37632
39118
  });
37633
39119
  try {
37634
39120
  await outboxPoller.start();
37635
39121
  } catch (err) {
37636
- logger7.warn("Outbox poller failed to start", { error: err instanceof Error ? err.message : String(err) });
39122
+ logger8.warn("Outbox poller failed to start", { error: err instanceof Error ? err.message : String(err) });
37637
39123
  }
37638
- const leaseStore = getStoreLattice41("default", "lease").store;
39124
+ const leaseStore = getStoreLattice42("default", "lease").store;
37639
39125
  if (leaseStore.constructor?.name === "InMemoryLeaseStore") {
37640
- logger7.warn(
39126
+ logger8.warn(
37641
39127
  "Startup recovery leader gate uses a non-distributed lease store (per-process only); configure a shared 'lease' store for multi-replica deployments",
37642
39128
  { phase: "startup_recovery" }
37643
39129
  );
37644
39130
  }
37645
- warnMultiReplicaReadiness(logger7);
39131
+ warnMultiReplicaReadiness(logger8);
39132
+ assertMultiReplicaReadiness();
37646
39133
  if (scheduleLatticeManager3.hasLattice("default")) {
37647
39134
  registerAgentCronHandler(scheduleLatticeManager3.getScheduleLattice("default"), {
37648
39135
  getAgent: (params) => agentInstanceManager13.getAgent(params),
37649
- createThread: (tenantId, assistantId, threadId, data) => getStoreLattice41("default", "thread").store.createThread(tenantId, assistantId, threadId, data),
39136
+ createThread: (tenantId, assistantId, threadId, data) => getStoreLattice42("default", "thread").store.createThread(tenantId, assistantId, threadId, data),
37650
39137
  acquireThreadRun: (tenantId, assistantId, threadId) => tryAcquireThreadRun5({
37651
- store: getStoreLattice41("default", "lease").store,
39138
+ store: getStoreLattice42("default", "lease").store,
37652
39139
  tenantId,
37653
39140
  assistantId,
37654
39141
  threadId,
37655
- owner: getReplicaId11(),
39142
+ owner: getReplicaId12(),
37656
39143
  ttlMs: 6e4
37657
39144
  }),
37658
39145
  getMaintenanceStore: getCheckpointMaintenanceStore,
@@ -37660,63 +39147,73 @@ var start = async (config) => {
37660
39147
  removeAgent: (params) => {
37661
39148
  agentInstanceManager13.removeAgent(params);
37662
39149
  },
37663
- log: { warn: (message, context) => logger7.warn(message, context) }
39150
+ log: { warn: (message, context) => logger8.warn(message, context) }
37664
39151
  });
37665
39152
  }
37666
39153
  const startup = await runAsLeader(leaseStore, "startup-recovery", async () => {
37667
39154
  if (routes.projectTaskRuntime) {
37668
- await prepareProjectTaskRuntime(routes.projectTaskRuntime, logger7);
39155
+ await prepareProjectTaskRuntime(routes.projectTaskRuntime, logger8);
37669
39156
  }
37670
39157
  if (process.env.AXIOM_TASK_ORPHAN_RECOVERY === "true") {
37671
39158
  try {
37672
39159
  const { createOrphanRecoveryRuntime: createOrphanRecoveryRuntime2 } = await import("./OrphanRecoveryRuntime-T4T5NOZU.mjs");
37673
39160
  const { createRecoveryCallerDeps: createRecoveryCallerDeps2 } = await import("./RecoveryCallerDeps-6SJUYJ5S.mjs");
37674
39161
  const runtime = createOrphanRecoveryRuntime2({
37675
- taskStore: getStoreLattice41("default", "task").store,
37676
- workItemStore: getStoreLattice41("default", "taskWorkItem").store,
37677
- tenantStore: getStoreLattice41("default", "tenant").store,
39162
+ taskStore: getStoreLattice42("default", "task").store,
39163
+ workItemStore: getStoreLattice42("default", "taskWorkItem").store,
39164
+ tenantStore: getStoreLattice42("default", "tenant").store,
37678
39165
  ...createRecoveryCallerDeps2(),
37679
- log: logger7
39166
+ log: logger8
37680
39167
  });
37681
39168
  await runtime.recoverAll();
37682
39169
  } catch (err) {
37683
- logger7.warn("Task orphan recovery failed", { error: err instanceof Error ? err.message : String(err) });
37684
- }
37685
- }
37686
- if (scheduleLatticeManager3.hasLattice("default")) {
37687
- try {
37688
- const restored = await scheduleLatticeManager3.getScheduleLattice("default").restore();
37689
- logger7.info("Scheduled tasks restored", { phase: "startup_recovery", restored });
37690
- } catch (err) {
37691
- logger7.warn("Scheduled task restore failed", {
37692
- phase: "startup_recovery",
37693
- error: err instanceof Error ? err.message : String(err)
37694
- });
39170
+ logger8.warn("Task orphan recovery failed", { error: err instanceof Error ? err.message : String(err) });
37695
39171
  }
37696
39172
  }
37697
39173
  }, { hold: true, ttlMs: 5 * 6e4 });
37698
39174
  if (!startup.ran) {
37699
- logger7.info("Startup recovery skipped (another replica leads)", { phase: "startup_recovery" });
39175
+ logger8.info("Startup recovery skipped (another replica leads)", { phase: "startup_recovery" });
39176
+ }
39177
+ if (scheduleLatticeManager3.hasLattice("default")) {
39178
+ const schedule2 = scheduleLatticeManager3.getScheduleLattice("default");
39179
+ try {
39180
+ const restored = await schedule2.restore();
39181
+ logger8.info("Scheduled tasks restored", { phase: "startup_recovery", restored });
39182
+ } catch (err) {
39183
+ logger8.warn("Scheduled task restore failed", {
39184
+ phase: "startup_recovery",
39185
+ error: err instanceof Error ? err.message : String(err)
39186
+ });
39187
+ }
39188
+ const intervalMs = parsePositiveInteger2(process.env.SCHEDULE_RECONCILE_MS, 3e4);
39189
+ scheduleReconcileTimer = setInterval(() => {
39190
+ void Promise.resolve(schedule2.reconcile?.()).catch((err) => {
39191
+ logger8.warn("Scheduled task reconcile failed", {
39192
+ error: err instanceof Error ? err.message : String(err)
39193
+ });
39194
+ });
39195
+ }, intervalMs);
39196
+ scheduleReconcileTimer.unref?.();
37700
39197
  }
37701
39198
  if (process.env.EXECUTION_WORKER_ENABLED === "true") {
37702
39199
  try {
37703
- const queueStore = getStoreLattice41("default", "threadMessageQueue").store;
39200
+ const queueStore = getStoreLattice42("default", "threadMessageQueue").store;
37704
39201
  executionSupervisor = new ThreadExecutionSupervisor({
37705
39202
  queueStore,
37706
39203
  runLedger,
37707
39204
  scanIntervalMs: Number(process.env.EXECUTION_SCAN_MS ?? "1000"),
37708
39205
  maxConcurrentThreads: Number(process.env.EXECUTION_MAX_THREADS ?? "20"),
37709
- log: { warn: (message, context) => logger7.warn(message, context) }
39206
+ log: { warn: (message, context) => logger8.warn(message, context) }
37710
39207
  });
37711
39208
  executionSupervisor.start();
37712
39209
  setExecutionSupervisor(executionSupervisor);
37713
- logger7.info("Execution worker supervisor started", {
39210
+ logger8.info("Execution worker supervisor started", {
37714
39211
  scanIntervalMs: process.env.EXECUTION_SCAN_MS ?? "1000",
37715
39212
  maxConcurrentThreads: process.env.EXECUTION_MAX_THREADS ?? "20",
37716
39213
  runLedger: runLedger ? "enabled" : "disabled"
37717
39214
  });
37718
39215
  } catch (err) {
37719
- logger7.warn("Execution worker supervisor unavailable", {
39216
+ logger8.warn("Execution worker supervisor unavailable", {
37720
39217
  error: err instanceof Error ? err.message : String(err)
37721
39218
  });
37722
39219
  }
@@ -37730,23 +39227,23 @@ var start = async (config) => {
37730
39227
  );
37731
39228
  runTaskWake = createConfiguredRedisRunTaskWake({
37732
39229
  url: process.env.REDIS_URL,
37733
- owner: getReplicaId11(),
39230
+ owner: getReplicaId12(),
37734
39231
  wake: () => runTaskRuntime?.wake() ?? Promise.resolve(),
37735
39232
  logger: {
37736
- warn: (message, context) => logger7.warn(message, context),
37737
- info: (message, context) => logger7.info(message, context)
39233
+ warn: (message, context) => logger8.warn(message, context),
39234
+ info: (message, context) => logger8.info(message, context)
37738
39235
  }
37739
39236
  });
37740
39237
  setRunTaskWakeStatsSource(runTaskWake);
37741
39238
  if (runTaskWake) setRunTaskWakePublisher(runTaskWake);
37742
39239
  try {
37743
39240
  const projectTaskRunHooks = createProjectTaskRunHooks({
37744
- tasks: getStoreLattice41("default", "task").store,
37745
- workItems: getStoreLattice41("default", "taskWorkItem").store,
37746
- projects: getStoreLattice41("default", "project").store,
37747
- rooms: getStoreLattice41("default", "projectRoom").store,
37748
- bots: getStoreLattice41("default", "projectBotMembership").store,
37749
- threads: getStoreLattice41("default", "thread").store,
39241
+ tasks: getStoreLattice42("default", "task").store,
39242
+ workItems: getStoreLattice42("default", "taskWorkItem").store,
39243
+ projects: getStoreLattice42("default", "project").store,
39244
+ rooms: getStoreLattice42("default", "projectRoom").store,
39245
+ bots: getStoreLattice42("default", "projectBotMembership").store,
39246
+ threads: getStoreLattice42("default", "thread").store,
37750
39247
  getThreadState: async ({ task, thread }) => agentInstanceManager13.getAgent({
37751
39248
  tenant_id: task.tenantId,
37752
39249
  assistant_id: task.ownerId,
@@ -37755,15 +39252,17 @@ var start = async (config) => {
37755
39252
  project_id: task.projectId
37756
39253
  }).getCurrentState(),
37757
39254
  onTaskInterrupted: (input) => projectTaskInterrupt(input),
37758
- logger: { warn: (message, context) => logger7.warn(message, context) }
39255
+ logger: { warn: (message, context) => logger8.warn(message, context) }
37759
39256
  });
37760
39257
  runTaskRuntime = new RunTaskRuntime({
37761
39258
  ledger: runLedger,
37762
39259
  leaseStore,
37763
- getAgent: (params) => agentInstanceManager13.getAgent(params),
39260
+ // The worker holds the thread-run lease, so it can make the run's
39261
+ // scope authoritative on the shared per-thread instance.
39262
+ getAgent: (params) => agentInstanceManager13.getAgentForExecution(params),
37764
39263
  ...projectTaskRunRuntimeOptions(projectTaskRunHooks),
37765
39264
  resolveRollbackStore: getCheckpointRollbackStore,
37766
- owner: getReplicaId11(),
39265
+ owner: getReplicaId12(),
37767
39266
  executeEnabled: runTaskWorkerEnabled,
37768
39267
  maxConcurrentJobs: parsePositiveInteger2(process.env.N_JOBS_PER_WORKER, 10),
37769
39268
  maxRunningPerTenant: resolveRunTaskMaxRunningPerTenant(process.env),
@@ -37779,30 +39278,30 @@ var start = async (config) => {
37779
39278
  rollbackBackoffBaseMs: parsePositiveInteger2(process.env.RUN_TASK_ROLLBACK_BACKOFF_MS, 1e3),
37780
39279
  shutdownTimeoutMs: runTaskShutdownTimeoutMs,
37781
39280
  logger: {
37782
- info: (message, context) => logger7.info(message, context),
37783
- warn: (message, context) => logger7.warn(message, context)
39281
+ info: (message, context) => logger8.info(message, context),
39282
+ warn: (message, context) => logger8.warn(message, context)
37784
39283
  }
37785
39284
  });
37786
39285
  runTaskRuntime.start();
37787
39286
  setRunTaskRuntime(runTaskRuntime);
37788
- logger7.info("Run task runtime started", {
39287
+ logger8.info("Run task runtime started", {
37789
39288
  executeEnabled: runTaskWorkerEnabled,
37790
39289
  maxConcurrentJobs: parsePositiveInteger2(process.env.N_JOBS_PER_WORKER, 10),
37791
39290
  worker: runTaskRuntime.worker ? "enabled" : "disabled",
37792
39291
  recovery: "enabled"
37793
39292
  });
37794
39293
  } catch (err) {
37795
- logger7.warn("Run task runtime unavailable", {
39294
+ logger8.warn("Run task runtime unavailable", {
37796
39295
  error: err instanceof Error ? err.message : String(err)
37797
39296
  });
37798
39297
  }
37799
39298
  if (runTaskWake && runTaskRuntime?.worker) {
37800
39299
  void runTaskWake.start().catch((err) => {
37801
- logger7.warn("Run task wake subscriber failed to start", {
39300
+ logger8.warn("Run task wake subscriber failed to start", {
37802
39301
  error: err instanceof Error ? err.message : String(err)
37803
39302
  });
37804
39303
  });
37805
- logger7.info("Run task Redis wake started in background", {
39304
+ logger8.info("Run task Redis wake started in background", {
37806
39305
  subscriber: "enabled"
37807
39306
  });
37808
39307
  }
@@ -37810,17 +39309,17 @@ var start = async (config) => {
37810
39309
  ledger: runLedger,
37811
39310
  days: process.env.AGENT_RUN_RETENTION_DAYS,
37812
39311
  log: {
37813
- info: (message, context) => logger7.info(message, context),
37814
- warn: (message, context) => logger7.warn(message, context)
39312
+ info: (message, context) => logger8.info(message, context),
39313
+ warn: (message, context) => logger8.warn(message, context)
37815
39314
  }
37816
39315
  });
37817
39316
  if (agentRunRetentionJob) {
37818
39317
  agentRunRetentionJob.start();
37819
- logger7.info("Agent run retention job started", {
39318
+ logger8.info("Agent run retention job started", {
37820
39319
  days: parseAgentRunRetentionDays(process.env.AGENT_RUN_RETENTION_DAYS)
37821
39320
  });
37822
39321
  } else {
37823
- logger7.warn("Agent run retention disabled", {
39322
+ logger8.warn("Agent run retention disabled", {
37824
39323
  env: "AGENT_RUN_RETENTION_DAYS"
37825
39324
  });
37826
39325
  }
@@ -37829,8 +39328,8 @@ var start = async (config) => {
37829
39328
  intervalMs: parsePositiveInteger2(process.env.THREAD_TTL_SWEEP_MS, 6e4),
37830
39329
  batchSize: parsePositiveInteger2(process.env.THREAD_TTL_SWEEP_BATCH, 100),
37831
39330
  log: {
37832
- info: (message, context) => logger7.info(message, context),
37833
- warn: (message, context) => logger7.warn(message, context)
39331
+ info: (message, context) => logger8.info(message, context),
39332
+ warn: (message, context) => logger8.warn(message, context)
37834
39333
  }
37835
39334
  });
37836
39335
  if (threadTtlSweeper) {
@@ -37838,7 +39337,7 @@ var start = async (config) => {
37838
39337
  await threadTtlSweeper.stop();
37839
39338
  });
37840
39339
  threadTtlSweeper.start();
37841
- logger7.info("Thread TTL sweeper started", {
39340
+ logger8.info("Thread TTL sweeper started", {
37842
39341
  intervalMs: parsePositiveInteger2(process.env.THREAD_TTL_SWEEP_MS, 6e4),
37843
39342
  batchSize: parsePositiveInteger2(process.env.THREAD_TTL_SWEEP_BATCH, 100)
37844
39343
  });
@@ -37848,8 +39347,8 @@ var start = async (config) => {
37848
39347
  intervalMs: storeTtlConfig.intervalMs,
37849
39348
  batchSize: storeTtlConfig.batchSize,
37850
39349
  log: {
37851
- info: (message, context) => logger7.info(message, context),
37852
- warn: (message, context) => logger7.warn(message, context)
39350
+ info: (message, context) => logger8.info(message, context),
39351
+ warn: (message, context) => logger8.warn(message, context)
37853
39352
  }
37854
39353
  }) : void 0;
37855
39354
  if (storeTtlSweeper) {
@@ -37857,31 +39356,31 @@ var start = async (config) => {
37857
39356
  await storeTtlSweeper.stop();
37858
39357
  });
37859
39358
  storeTtlSweeper.start();
37860
- logger7.info("Store TTL sweeper started", {
39359
+ logger8.info("Store TTL sweeper started", {
37861
39360
  intervalMs: storeTtlConfig.intervalMs,
37862
39361
  batchSize: storeTtlConfig.batchSize
37863
39362
  });
37864
39363
  }
37865
39364
  installLifespanHooks(app, config?.lifespans, {
37866
- warn: (message, context) => logger7.warn(message, context)
39365
+ warn: (message, context) => logger8.warn(message, context)
37867
39366
  });
37868
39367
  await app.listen({ port: target_port, host: "0.0.0.0" });
37869
- logger7.info(`Lattice Gateway is running on port: ${target_port}`);
39368
+ logger8.info(`Lattice Gateway is running on port: ${target_port}`);
37870
39369
  const outboxRetention = createOutboxRetentionJob({
37871
39370
  outbox: outboxStore,
37872
- log: logger7
39371
+ log: logger8
37873
39372
  });
37874
39373
  outboxRetention.start();
37875
39374
  restoreMcpConnections().catch((error) => {
37876
- logger7.error("MCP connection restoration failed", { error });
39375
+ logger8.error("MCP connection restoration failed", { error });
37877
39376
  });
37878
39377
  if (config?.gracefulShutdown !== false && !shutdownHandlersInstalled) {
37879
39378
  shutdownHandlersInstalled = true;
37880
39379
  const gracefulShutdown = createGracefulShutdown({
37881
39380
  timeoutMs: config?.gracefulShutdownTimeoutMs ?? 1e4,
37882
39381
  log: {
37883
- info: (message, context) => logger7.info(message, context),
37884
- warn: (message, context) => logger7.warn(message, context)
39382
+ info: (message, context) => logger8.info(message, context),
39383
+ warn: (message, context) => logger8.warn(message, context)
37885
39384
  },
37886
39385
  steps: [
37887
39386
  async () => {
@@ -37908,6 +39407,10 @@ var start = async (config) => {
37908
39407
  },
37909
39408
  () => outboxPoller.stop(),
37910
39409
  () => outboxRetention.stop(),
39410
+ () => {
39411
+ if (scheduleReconcileTimer) clearInterval(scheduleReconcileTimer);
39412
+ scheduleReconcileTimer = void 0;
39413
+ },
37911
39414
  () => threadTtlSweeper?.stop(),
37912
39415
  () => storeTtlSweeper?.stop(),
37913
39416
  () => chunkSourceHandle?.close(),
@@ -37921,7 +39424,7 @@ var start = async (config) => {
37921
39424
  process.once("SIGINT", () => void gracefulShutdown("SIGINT"));
37922
39425
  }
37923
39426
  } catch (err) {
37924
- logger7.error("Server start failed", { error: err });
39427
+ logger8.error("Server start failed", { error: err });
37925
39428
  process.exit(1);
37926
39429
  }
37927
39430
  };
@@ -37945,6 +39448,7 @@ export {
37945
39448
  DEFAULT_RUN_TASK_MAX_ATTEMPTS,
37946
39449
  DEFAULT_RUN_TASK_SCAN_MS,
37947
39450
  DEFAULT_THREAD_RUN_LEASE_TTL_MS,
39451
+ LangfuseObservabilityProvider,
37948
39452
  LatticeGateway,
37949
39453
  MIRRORED_AGENT_EVENTS,
37950
39454
  MODEL_PROVIDER_CHANGED_TOPIC,
@@ -37958,6 +39462,7 @@ export {
37958
39462
  RunTaskRuntime,
37959
39463
  RunTaskWorker,
37960
39464
  createConfiguredRedisRunTaskWake,
39465
+ createLangfuseProviderFromEnv,
37961
39466
  createOrphanRecoveryRuntime,
37962
39467
  createRecoveryCallerDeps,
37963
39468
  createRedisAppendLog,
@@ -37976,8 +39481,10 @@ export {
37976
39481
  publishAgentLifecycle,
37977
39482
  publishModelProviderChanged,
37978
39483
  publishRoomEvent,
39484
+ readLangfuseConfig,
37979
39485
  recoverTask,
37980
39486
  recoverTopCaller,
39487
+ redactLangfuseValue,
37981
39488
  resolveTopCaller,
37982
39489
  runOrphanRecovery,
37983
39490
  setRunTaskRuntime,