@axiom-lattice/gateway 4.10.2 → 4.11.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 (22) hide show
  1. package/dist/embed/{embed-page-AgentDetailSideApp-2M4MLVQQ.js → embed-page-AgentDetailSideApp-FHC56ARN.js} +1 -1
  2. package/dist/embed/{embed-page-AgentGovernanceWorkbench-KEVBIQP6.js → embed-page-AgentGovernanceWorkbench-KAPZJBE6.js} +1 -1
  3. package/dist/embed/{embed-page-MessageGraph-PRBUQINR.js → embed-page-MessageGraph-DMP5EZBM.js} +1 -1
  4. package/dist/embed/{embed-page-ReactChart-V6JJXCEV.js → embed-page-ReactChart-PYGNSRHG.js} +1 -1
  5. package/dist/embed/{embed-page-TasksSideApp-OFZI54MF.js → embed-page-TasksSideApp-MPYOPRXW.js} +1 -1
  6. package/dist/embed/{embed-page-TeamWorkspace-QSSAF3AA.js → embed-page-TeamWorkspace-EXUAXKAH.js} +1 -1
  7. package/dist/embed/{embed-page-TopologyRuntimeView-PHULW2NG.js → embed-page-TopologyRuntimeView-MH4YMU7D.js} +1 -1
  8. package/dist/embed/{embed-page-WorkflowAutomationView-LQS6H7X6.js → embed-page-WorkflowAutomationView-KQYFSLLH.js} +1 -1
  9. package/dist/embed/{embed-page-chunk-SSWDAKQU.js → embed-page-chunk-I7BVN6EY.js} +1 -1
  10. package/dist/embed/{embed-page-chunk-D5EY6ASK.js → embed-page-chunk-JSAUTBU5.js} +1 -1
  11. package/dist/embed/{embed-page-chunk-YNHKD2CY.js → embed-page-chunk-NW37OVAX.js} +1 -1
  12. package/dist/embed/{embed-page-chunk-PFCR6CFL.js → embed-page-chunk-QGXGVLAX.js} +1 -1
  13. package/dist/embed/{embed-page-chunk-7PNQFZMK.js → embed-page-chunk-QHXKZNDU.js} +6 -6
  14. package/dist/embed/{embed-page-chunk-IJ2YWGKU.js → embed-page-chunk-YQZI5LNV.js} +1 -1
  15. package/dist/embed/embed-page.js +1 -1
  16. package/dist/index.d.mts +12 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.js +135 -6
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +137 -7
  21. package/dist/index.mjs.map +1 -1
  22. package/package.json +7 -7
package/dist/index.mjs CHANGED
@@ -25369,6 +25369,25 @@ var wecomConfigSchema = z12.object({
25369
25369
  var RECONNECT_BASE_MS = 5e3;
25370
25370
  var RECONNECT_MAX_MS = 6e4;
25371
25371
  var activeConnections2 = /* @__PURE__ */ new Map();
25372
+ var streamIdsByReqId = /* @__PURE__ */ new Map();
25373
+ var MAX_TRACKED_STREAMS = 1e3;
25374
+ function streamKeyOf(replyTarget) {
25375
+ const frame = replyTarget.rawTarget.frame;
25376
+ return frame?.headers?.req_id ?? replyTarget.channelInstallationId;
25377
+ }
25378
+ function streamIdFor(replyTarget) {
25379
+ const key = streamKeyOf(replyTarget);
25380
+ let streamId = streamIdsByReqId.get(key);
25381
+ if (!streamId) {
25382
+ streamId = randomUUID26();
25383
+ streamIdsByReqId.set(key, streamId);
25384
+ if (streamIdsByReqId.size > MAX_TRACKED_STREAMS) {
25385
+ const oldest = streamIdsByReqId.keys().next().value;
25386
+ if (oldest !== void 0) streamIdsByReqId.delete(oldest);
25387
+ }
25388
+ }
25389
+ return streamId;
25390
+ }
25372
25391
  var wecomChannelAdapter = {
25373
25392
  channel: "wecom",
25374
25393
  configSchema: wecomConfigSchema,
@@ -25410,14 +25429,41 @@ var wecomChannelAdapter = {
25410
25429
  });
25411
25430
  return;
25412
25431
  }
25432
+ const key = streamKeyOf(replyTarget);
25413
25433
  connection.client.reply(replyTarget.rawTarget.frame, {
25414
25434
  msgtype: "stream",
25415
25435
  stream: {
25416
- id: randomUUID26(),
25436
+ id: streamIdFor(replyTarget),
25417
25437
  finish: true,
25418
25438
  content: message.text
25419
25439
  }
25420
25440
  });
25441
+ streamIdsByReqId.delete(key);
25442
+ },
25443
+ /**
25444
+ * 流式回复:同一入站消息复用同一个 `stream.id`,增量刷新内容。
25445
+ *
25446
+ * 微信的 `stream` 是"刷新语义"(`content` 为累计全文),所以 `text` 直接用
25447
+ * 调用方给的累计值即可。失败只记日志,不打断派发链路(最终由 `reply:ready`
25448
+ * 的那次 `done: true` 兜底)。
25449
+ */
25450
+ async sendStreamChunk(replyTarget, chunk, installation) {
25451
+ const connection = activeConnections2.get(installation.id);
25452
+ if (!connection) {
25453
+ logger6.warn("WeCom bot not connected, cannot stream reply", {
25454
+ installationId: installation.id
25455
+ });
25456
+ return;
25457
+ }
25458
+ connection.client.reply(replyTarget.rawTarget.frame, {
25459
+ msgtype: "stream",
25460
+ stream: {
25461
+ id: streamIdFor(replyTarget),
25462
+ finish: chunk.done,
25463
+ content: chunk.text
25464
+ }
25465
+ });
25466
+ if (chunk.done) streamIdsByReqId.delete(streamKeyOf(replyTarget));
25421
25467
  },
25422
25468
  resolveThreadId(message, binding) {
25423
25469
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -25962,7 +26008,8 @@ import {
25962
26008
  } from "@axiom-lattice/core";
25963
26009
  import {
25964
26010
  snapshotExactArray as snapshotExactArray3,
25965
- snapshotExactRecord as snapshotExactRecord15
26011
+ snapshotExactRecord as snapshotExactRecord15,
26012
+ MessageChunkTypes as MessageChunkTypes4
25966
26013
  } from "@axiom-lattice/protocols";
25967
26014
  import { randomUUID as randomUUID28 } from "crypto";
25968
26015
  var BindingNotFoundError = class extends Error {
@@ -25977,6 +26024,15 @@ var ChannelInstallationNotFoundError = class extends Error {
25977
26024
  this.name = "ChannelInstallationNotFoundError";
25978
26025
  }
25979
26026
  };
26027
+ var DEFAULT_STREAM_PLACEHOLDER = "\u6B63\u5728\u5904\u7406\u2026";
26028
+ function streamPlaceholderFor(installation) {
26029
+ const config = installation?.config;
26030
+ if (config !== null && typeof config === "object" && !Array.isArray(config)) {
26031
+ const value = config.streamPlaceholder;
26032
+ if (typeof value === "string") return value;
26033
+ }
26034
+ return DEFAULT_STREAM_PLACEHOLDER;
26035
+ }
25980
26036
  var InvalidChannelInstallationError = class extends Error {
25981
26037
  constructor(message = "Channel installation is not valid for this message") {
25982
26038
  super(message);
@@ -26074,7 +26130,7 @@ function correlatedRoomReplyTarget(replyTarget, inputMessageId) {
26074
26130
  }
26075
26131
  };
26076
26132
  }
26077
- var MessageRouter = class {
26133
+ var _MessageRouter = class _MessageRouter {
26078
26134
  constructor(config) {
26079
26135
  this.replySubscriptions = /* @__PURE__ */ new Set();
26080
26136
  /** Recent answer-by-message resumes per presence thread (first answer wins). */
@@ -26377,6 +26433,7 @@ var MessageRouter = class {
26377
26433
  },
26378
26434
  ...resumeCommand ? { command: resumeCommand } : {}
26379
26435
  };
26436
+ const streamState = { finished: false };
26380
26437
  let cleanupReplySubscription;
26381
26438
  if (replyTarget && replyAdapter) {
26382
26439
  let cleaned = false;
@@ -26387,10 +26444,11 @@ var MessageRouter = class {
26387
26444
  if (cleaned || !event.consumedInputMessageIds.includes(inputMessageId)) return;
26388
26445
  cleanup();
26389
26446
  if (event.inputMessageId !== inputMessageId) return;
26390
- void replyAdapter.sendReply(replyTarget, {
26447
+ const delivery = replyAdapter.sendStreamChunk ? streamState.finished ? void 0 : replyAdapter.sendStreamChunk(replyTarget, { text: event.outputText, done: true }, installation) : replyAdapter.sendReply(replyTarget, {
26391
26448
  text: event.outputText,
26392
26449
  metadata: { inputMessageId, outputMessageId: event.outputMessageId }
26393
- }, installation).catch((error) => console.error({ event: "dispatch:reply:failed", error }));
26450
+ }, installation);
26451
+ void delivery?.catch((error) => console.error({ event: "dispatch:reply:failed", error }));
26394
26452
  };
26395
26453
  const timer = setTimeout(() => {
26396
26454
  console.warn({
@@ -26452,6 +26510,16 @@ var MessageRouter = class {
26452
26510
  cleanupReplySubscription?.();
26453
26511
  throw error;
26454
26512
  }
26513
+ if (addResult && replyTarget && replyAdapter?.sendStreamChunk) {
26514
+ void this.streamAgentOutputToChannel({
26515
+ agent,
26516
+ adapter: replyAdapter,
26517
+ replyTarget,
26518
+ installation,
26519
+ inputMessageId,
26520
+ state: streamState
26521
+ });
26522
+ }
26455
26523
  console.log({
26456
26524
  event: "dispatch:complete",
26457
26525
  agentId,
@@ -26487,6 +26555,64 @@ var MessageRouter = class {
26487
26555
  };
26488
26556
  return dispatch(0);
26489
26557
  }
26558
+ /**
26559
+ * 消费 agent 的逐块输出,把**中途**内容推给支持流式的渠道适配器。
26560
+ *
26561
+ * @remarks
26562
+ * 纯加法、best-effort:不抛错,也**不发最终内容** —— 最终由
26563
+ * `reply:ready` → `sendStreamChunk({ done: true })` 收尾。未实现
26564
+ * `sendStreamChunk` 的适配器不会走到这里,行为完全不变。
26565
+ */
26566
+ async streamAgentOutputToChannel(input) {
26567
+ const { agent, adapter, replyTarget, installation, inputMessageId, state } = input;
26568
+ const send = adapter.sendStreamChunk;
26569
+ if (!send) return;
26570
+ let started = false;
26571
+ let accumulated = "";
26572
+ let lastSentText = "";
26573
+ let lastSentAt = 0;
26574
+ let sent = 0;
26575
+ const warn = (event, error) => console.warn({ event, inputMessageId, error: error instanceof Error ? error.message : String(error) });
26576
+ const push = (text, done) => send.call(adapter, replyTarget, { text, done }, installation).catch((error) => warn("dispatch:stream:failed", error));
26577
+ const placeholder = streamPlaceholderFor(installation);
26578
+ try {
26579
+ if (placeholder) await push(placeholder, false);
26580
+ const stream = agent.chunkStream(inputMessageId, []);
26581
+ for await (const chunk of stream) {
26582
+ if (!started) {
26583
+ if (chunk?.type === MessageChunkTypes4.HUMAN && chunk?.data?.id === inputMessageId) {
26584
+ started = true;
26585
+ }
26586
+ continue;
26587
+ }
26588
+ if (chunk?.type === MessageChunkTypes4.MESSAGE_COMPLETED || chunk?.type === MessageChunkTypes4.MESSAGE_FAILED || chunk?.type === MessageChunkTypes4.INTERRUPT) {
26589
+ if (chunk.type !== MessageChunkTypes4.MESSAGE_COMPLETED) {
26590
+ const text2 = accumulated || placeholder;
26591
+ if (text2) {
26592
+ await push(text2, true);
26593
+ state.finished = true;
26594
+ }
26595
+ }
26596
+ break;
26597
+ }
26598
+ if (chunk?.type !== MessageChunkTypes4.AI) continue;
26599
+ const text = typeof chunk.data?.content === "string" ? chunk.data.content : void 0;
26600
+ if (!text) continue;
26601
+ accumulated += text;
26602
+ if (accumulated === lastSentText) continue;
26603
+ const now = Date.now();
26604
+ if (sent >= _MessageRouter.MAX_STREAM_CHUNKS || now - lastSentAt < _MessageRouter.STREAM_CHUNK_INTERVAL_MS) {
26605
+ continue;
26606
+ }
26607
+ lastSentAt = now;
26608
+ lastSentText = accumulated;
26609
+ sent += 1;
26610
+ await push(accumulated, false);
26611
+ }
26612
+ } catch (error) {
26613
+ warn("dispatch:stream:error", error);
26614
+ }
26615
+ }
26490
26616
  /**
26491
26617
  * Projects a pending HITL interrupt into the room as an independent bot message.
26492
26618
  *
@@ -26510,6 +26636,10 @@ var MessageRouter = class {
26510
26636
  }
26511
26637
  }
26512
26638
  };
26639
+ /** 流式中途刷新的节流间隔与上限(微信单会话 30 条/分钟,留余量)。 */
26640
+ _MessageRouter.STREAM_CHUNK_INTERVAL_MS = 800;
26641
+ _MessageRouter.MAX_STREAM_CHUNKS = 60;
26642
+ var MessageRouter = _MessageRouter;
26513
26643
 
26514
26644
  // src/channels/room/RoomChannelAdapter.ts
26515
26645
  import { z as z13 } from "zod";
@@ -27747,7 +27877,7 @@ function initializeLogger(config) {
27747
27877
  }
27748
27878
  var app = fastify({
27749
27879
  logger: false,
27750
- bodyLimit: Number(process.env.BODY_LIMIT) || 50 * 1024 * 1024,
27880
+ bodyLimit: Number(process.env.BODY_LIMIT) || 100 * 1024 * 1024,
27751
27881
  // Composite subagent thread IDs ({mainThread}____{assistant}-general-purpose_{toolCallId})
27752
27882
  // can exceed the 100-char default, which makes the router 404 before any
27753
27883
  // handler runs. Allow the longest IDs the conventions can produce.
@@ -27810,7 +27940,7 @@ app.register(cors, {
27810
27940
  app.register(sensible);
27811
27941
  app.register(multipart, {
27812
27942
  limits: {
27813
- fileSize: Number(process.env.BODY_LIMIT) || 50 * 1024 * 1024
27943
+ fileSize: Number(process.env.BODY_LIMIT) || 100 * 1024 * 1024
27814
27944
  }
27815
27945
  });
27816
27946
  app.register(websocket);