@myassis/gateway 1.0.93 → 1.0.94
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.
|
@@ -206,6 +206,9 @@ class WebSocketService {
|
|
|
206
206
|
if (options.excludeClientId && client.clientId === options.excludeClientId) {
|
|
207
207
|
return;
|
|
208
208
|
}
|
|
209
|
+
if (options.onlyClientId && client.clientId !== options.onlyClientId) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
209
212
|
sent = this.sendToConnection(client, message) || sent;
|
|
210
213
|
});
|
|
211
214
|
return sent;
|
|
@@ -47,6 +47,16 @@ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
|
47
47
|
* 广播出去会让多个终端同时弹窗并重复回传批准结果。
|
|
48
48
|
*/
|
|
49
49
|
const NON_MIRRORED_SSE_EVENTS = new Set(['heartbeat', 'approval_pending']);
|
|
50
|
+
/**
|
|
51
|
+
* 只属于发起终端的事件类型。
|
|
52
|
+
*
|
|
53
|
+
* 这些事件不能广播(否则多终端重复弹窗),但也不能丢:
|
|
54
|
+
* 中继模式下 SSE 一旦被隐式关闭,approval_pending 写不出去,
|
|
55
|
+
* 发起端永远不弹确认框,而 registerApprovalWaiter 会一直堵到
|
|
56
|
+
* 5 分钟超时,整轮对话看起来就是“卡住”。
|
|
57
|
+
* 因此 SSE 不可用时改成定向走 WebSocket 补送给同一个发起终端。
|
|
58
|
+
*/
|
|
59
|
+
const ORIGINATOR_ONLY_SSE_EVENTS = new Set(['approval_pending']);
|
|
50
60
|
// 批准等待缓存:token -> { resolve, reject, expiresAt }
|
|
51
61
|
const approvalWaiters = new Map();
|
|
52
62
|
/**
|
|
@@ -790,12 +800,48 @@ class Session {
|
|
|
790
800
|
// 把镜像函数传进去,使该事件也能同步到其他终端。
|
|
791
801
|
const memoryManager = new MemoryManager_js_1.MemoryManager(this, this.abortController.signal, childAgent, res, mirrorToOtherClients);
|
|
792
802
|
const historyMessages = await memoryManager.getHistoryMessagesAsync();
|
|
803
|
+
/**
|
|
804
|
+
* 将只属于发起终端的事件改走 WebSocket 补送。
|
|
805
|
+
*
|
|
806
|
+
* 不占用 mirrorSeq:该序号是会话级镜像流的连续编号,
|
|
807
|
+
* 定向补送若占一个号,其他终端就会把它当成丢包而误报缺口。
|
|
808
|
+
*/
|
|
809
|
+
const sendToOriginator = (data) => {
|
|
810
|
+
if (!wsService)
|
|
811
|
+
return;
|
|
812
|
+
try {
|
|
813
|
+
// 没有 clientId(老版终端)时退回广播:审批 token 只能兑付一次,
|
|
814
|
+
// 重复回传会被忽略,比没人收到而卡 5 分钟更可接受。
|
|
815
|
+
const delivered = wsService.sendToUser(String(this.userId), {
|
|
816
|
+
type: 'session_stream',
|
|
817
|
+
payload: {
|
|
818
|
+
sessionId: this.id,
|
|
819
|
+
agentId: this.agentId,
|
|
820
|
+
userMessageId,
|
|
821
|
+
assistantMessageId,
|
|
822
|
+
event: data,
|
|
823
|
+
},
|
|
824
|
+
}, clientId ? { onlyClientId: clientId } : {});
|
|
825
|
+
if (!delivered) {
|
|
826
|
+
logger.warn(`会话 ${this.id} 的 ${data?.type} 事件无法下发:SSE 已断且无可用 WebSocket 连接`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
catch (error) {
|
|
830
|
+
logger.error('SSE originator fallback error:', error);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
793
833
|
// SSE 辅助方法:res 为 null 时跳过写入(本地执行模式),但仍同步给其他终端
|
|
794
834
|
const sendSSE = (res, data) => {
|
|
795
835
|
mirrorToOtherClients(data);
|
|
796
836
|
// 连接已断开时不再写入:写只会抛错刷日志,内容已改由镜像下发
|
|
797
|
-
if (!res || !sseAlive)
|
|
837
|
+
if (!res || !sseAlive) {
|
|
838
|
+
// 镜像不负责的那几类事件,在 SSE 不可用时需要定向补送;
|
|
839
|
+
// heartbeat 是 SSE 保活专用,没了 SSE 也就不需要补送。
|
|
840
|
+
if (ORIGINATOR_ONLY_SSE_EVENTS.has(data?.type)) {
|
|
841
|
+
sendToOriginator(data);
|
|
842
|
+
}
|
|
798
843
|
return;
|
|
844
|
+
}
|
|
799
845
|
try {
|
|
800
846
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
801
847
|
res.flush?.();
|