@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.
- package/dist/embed/{embed-page-AgentDetailSideApp-2M4MLVQQ.js → embed-page-AgentDetailSideApp-FHC56ARN.js} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-KEVBIQP6.js → embed-page-AgentGovernanceWorkbench-KAPZJBE6.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-PRBUQINR.js → embed-page-MessageGraph-DMP5EZBM.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-V6JJXCEV.js → embed-page-ReactChart-PYGNSRHG.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-OFZI54MF.js → embed-page-TasksSideApp-MPYOPRXW.js} +1 -1
- package/dist/embed/{embed-page-TeamWorkspace-QSSAF3AA.js → embed-page-TeamWorkspace-EXUAXKAH.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-PHULW2NG.js → embed-page-TopologyRuntimeView-MH4YMU7D.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-LQS6H7X6.js → embed-page-WorkflowAutomationView-KQYFSLLH.js} +1 -1
- package/dist/embed/{embed-page-chunk-SSWDAKQU.js → embed-page-chunk-I7BVN6EY.js} +1 -1
- package/dist/embed/{embed-page-chunk-D5EY6ASK.js → embed-page-chunk-JSAUTBU5.js} +1 -1
- package/dist/embed/{embed-page-chunk-YNHKD2CY.js → embed-page-chunk-NW37OVAX.js} +1 -1
- package/dist/embed/{embed-page-chunk-PFCR6CFL.js → embed-page-chunk-QGXGVLAX.js} +1 -1
- package/dist/embed/{embed-page-chunk-7PNQFZMK.js → embed-page-chunk-QHXKZNDU.js} +6 -6
- package/dist/embed/{embed-page-chunk-IJ2YWGKU.js → embed-page-chunk-YQZI5LNV.js} +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +135 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +137 -7
- package/dist/index.mjs.map +1 -1
- 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:
|
|
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
|
|
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)
|
|
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) ||
|
|
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) ||
|
|
27943
|
+
fileSize: Number(process.env.BODY_LIMIT) || 100 * 1024 * 1024
|
|
27814
27944
|
}
|
|
27815
27945
|
});
|
|
27816
27946
|
app.register(websocket);
|