@yanhaidao/wecom 2.4.160 → 2.5.110
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/index.js +68 -0
- package/dist/src/accounts.js +20 -0
- package/dist/src/agent/handler.js +895 -0
- package/dist/src/agent/index.js +5 -0
- package/dist/src/app/account-runtime.js +216 -0
- package/dist/src/app/bootstrap.js +19 -0
- package/dist/src/app/index.js +118 -0
- package/dist/src/capability/agent/delivery-service.js +63 -0
- package/dist/src/capability/agent/fallback-policy.js +6 -0
- package/dist/src/capability/agent/ingress-service.js +33 -0
- package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
- package/dist/src/capability/bot/dispatch-config.js +45 -0
- package/dist/src/capability/bot/fallback-delivery.js +147 -0
- package/dist/src/capability/bot/local-path-delivery.js +178 -0
- package/dist/src/capability/bot/sandbox-media.js +138 -0
- package/dist/src/capability/bot/service.js +49 -0
- package/dist/src/capability/bot/stream-delivery.js +321 -0
- package/dist/src/capability/bot/stream-finalizer.js +81 -0
- package/dist/src/capability/bot/stream-orchestrator.js +318 -0
- package/dist/src/capability/bot/types.js +1 -0
- package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
- package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
- package/dist/src/capability/calendar/tool.js +365 -0
- package/dist/src/capability/calendar/types.js +12 -0
- package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
- package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
- package/dist/src/capability/doc/tool.js +1556 -0
- package/dist/src/capability/doc/types.js +113 -0
- package/dist/src/capability/mcp/index.js +3 -0
- package/dist/src/capability/mcp/schema.js +102 -0
- package/dist/src/capability/mcp/tool.js +146 -0
- package/dist/src/capability/mcp/transport.js +293 -0
- package/dist/src/channel.js +224 -0
- package/dist/src/config/accounts.js +236 -0
- package/dist/src/config/derived-paths.js +31 -0
- package/dist/src/config/index.js +7 -0
- package/dist/src/config/media.js +110 -0
- package/dist/src/config/network.js +32 -0
- package/dist/src/config/routing.js +20 -0
- package/dist/src/config/runtime-config.js +25 -0
- package/dist/src/config/schema.js +4 -0
- package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
- package/dist/src/context-store.js +219 -0
- package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
- package/dist/src/crypto/index.js +9 -0
- package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
- package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
- package/dist/src/crypto.js +145 -0
- package/dist/src/domain/models.js +1 -0
- package/dist/src/domain/policies.js +32 -0
- package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
- package/dist/src/gateway-monitor.js +139 -0
- package/dist/src/http.js +114 -0
- package/{src/media.ts → dist/src/media.js} +21 -40
- package/dist/src/monitor/limits.js +7 -0
- package/dist/src/monitor/state.js +28 -0
- package/dist/src/monitor.js +84 -0
- package/dist/src/observability/audit-log.js +30 -0
- package/dist/src/observability/legacy-operational-event-store.js +22 -0
- package/dist/src/observability/raw-envelope-log.js +24 -0
- package/dist/src/observability/status-registry.js +9 -0
- package/dist/src/observability/transport-session-view.js +14 -0
- package/dist/src/onboarding.js +546 -0
- package/dist/src/outbound.js +557 -0
- package/dist/src/runtime/dispatcher.js +57 -0
- package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
- package/dist/src/runtime/outbound-intent.js +1 -0
- package/dist/src/runtime/reply-orchestrator.js +38 -0
- package/dist/src/runtime/routing-bridge.js +26 -0
- package/dist/src/runtime/session-manager.js +112 -0
- package/dist/src/runtime/source-registry.js +174 -0
- package/dist/src/runtime.js +1 -0
- package/dist/src/shared/command-auth.js +57 -0
- package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
- package/dist/src/shared/media-asset.js +65 -0
- package/dist/src/shared/media-service.js +59 -0
- package/dist/src/shared/media-types.js +1 -0
- package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
- package/dist/src/store/active-reply-store.js +41 -0
- package/dist/src/store/interfaces.js +1 -0
- package/dist/src/store/memory-store.js +33 -0
- package/dist/src/store/stream-batch-store.js +319 -0
- package/{src/target.ts → dist/src/target.js} +15 -48
- package/dist/src/transport/agent-api/client.js +168 -0
- package/dist/src/transport/agent-api/core.js +337 -0
- package/dist/src/transport/agent-api/delivery.js +28 -0
- package/dist/src/transport/agent-api/media-upload.js +4 -0
- package/dist/src/transport/agent-api/reply.js +24 -0
- package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
- package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
- package/dist/src/transport/agent-api/upstream-reply.js +26 -0
- package/dist/src/transport/agent-callback/http-handler.js +30 -0
- package/dist/src/transport/agent-callback/inbound.js +4 -0
- package/dist/src/transport/agent-callback/reply.js +8 -0
- package/dist/src/transport/agent-callback/request-handler.js +189 -0
- package/dist/src/transport/agent-callback/session.js +15 -0
- package/dist/src/transport/bot-webhook/active-reply.js +27 -0
- package/dist/src/transport/bot-webhook/http-handler.js +31 -0
- package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
- package/dist/src/transport/bot-webhook/inbound.js +4 -0
- package/dist/src/transport/bot-webhook/message-shape.js +98 -0
- package/dist/src/transport/bot-webhook/protocol.js +124 -0
- package/dist/src/transport/bot-webhook/reply.js +9 -0
- package/dist/src/transport/bot-webhook/request-handler.js +285 -0
- package/dist/src/transport/bot-webhook/session.js +15 -0
- package/dist/src/transport/bot-ws/inbound.js +147 -0
- package/dist/src/transport/bot-ws/media.js +236 -0
- package/dist/src/transport/bot-ws/reply.js +310 -0
- package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
- package/dist/src/transport/bot-ws/session.js +15 -0
- package/dist/src/transport/http/common.js +78 -0
- package/dist/src/transport/http/registry.js +71 -0
- package/dist/src/transport/http/request-handler.js +51 -0
- package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
- package/dist/src/types/account.js +1 -0
- package/dist/src/types/config.js +1 -0
- package/dist/src/types/constants.js +28 -0
- package/dist/src/types/events.js +1 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/types/legacy-stream.js +1 -0
- package/dist/src/types/message.js +5 -0
- package/dist/src/types/runtime-context.js +1 -0
- package/dist/src/types/runtime.js +1 -0
- package/dist/src/types.js +1 -0
- package/dist/src/upstream/index.js +111 -0
- package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
- package/openclaw.plugin.json +15 -0
- package/package.json +18 -1
- package/.github/workflows/release.yml +0 -143
- package/GOVERNANCE.md +0 -26
- package/SKILLS_CAL.md +0 -895
- package/SKILLS_DOC.md +0 -2288
- package/UPSTREAM_CONFIG.md +0 -170
- package/UPSTREAM_PLAN.md +0 -175
- package/assets/01.bot-add.png +0 -0
- package/assets/01.bot-setp2.png +0 -0
- package/assets/01.image.jpg +0 -0
- package/assets/02.agent.add.png +0 -0
- package/assets/02.agent.api-set.png +0 -0
- package/assets/02.image.jpg +0 -0
- package/assets/03.agent.page.png +0 -0
- package/assets/03.bot.page.png +0 -0
- package/assets/link-me.jpg +0 -0
- package/assets/register.png +0 -0
- package/changelog/v2.2.28.md +0 -70
- package/changelog/v2.3.10.md +0 -17
- package/changelog/v2.3.11.md +0 -19
- package/changelog/v2.3.12.md +0 -25
- package/changelog/v2.3.13.md +0 -19
- package/changelog/v2.3.14.md +0 -48
- package/changelog/v2.3.15.md +0 -15
- package/changelog/v2.3.16.md +0 -11
- package/changelog/v2.3.18.md +0 -22
- package/changelog/v2.3.19.md +0 -73
- package/changelog/v2.3.2.md +0 -28
- package/changelog/v2.3.26.md +0 -21
- package/changelog/v2.3.27.md +0 -33
- package/changelog/v2.3.4.md +0 -20
- package/changelog/v2.3.9.md +0 -22
- package/changelog/v2.4.12.md +0 -37
- package/changelog/v2.4.16.md +0 -19
- package/compat-single-account.md +0 -148
- package/index.test.ts +0 -38
- package/scripts/test-proxy.ts +0 -70
- package/src/accounts.ts +0 -34
- package/src/agent/api-client.upload.test.ts +0 -109
- package/src/agent/handler.event-filter.test.ts +0 -100
- package/src/agent/handler.ts +0 -1105
- package/src/agent/index.ts +0 -12
- package/src/app/account-runtime.ts +0 -276
- package/src/app/bootstrap.ts +0 -29
- package/src/app/index.ts +0 -192
- package/src/capability/agent/delivery-service.ts +0 -87
- package/src/capability/agent/fallback-policy.ts +0 -13
- package/src/capability/agent/ingress-service.ts +0 -38
- package/src/capability/agent/upstream-delivery-service.ts +0 -96
- package/src/capability/bot/dispatch-config.ts +0 -47
- package/src/capability/bot/fallback-delivery.ts +0 -178
- package/src/capability/bot/local-path-delivery.ts +0 -215
- package/src/capability/bot/sandbox-media.test.ts +0 -221
- package/src/capability/bot/sandbox-media.ts +0 -176
- package/src/capability/bot/service.ts +0 -56
- package/src/capability/bot/stream-delivery.ts +0 -379
- package/src/capability/bot/stream-finalizer.ts +0 -120
- package/src/capability/bot/stream-orchestrator.ts +0 -371
- package/src/capability/bot/types.ts +0 -8
- package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
- package/src/capability/calendar/tool.ts +0 -417
- package/src/capability/calendar/types.ts +0 -309
- package/src/capability/doc/tool.ts +0 -1629
- package/src/capability/doc/types.ts +0 -792
- package/src/capability/mcp/index.ts +0 -10
- package/src/capability/mcp/schema.ts +0 -107
- package/src/capability/mcp/tool.ts +0 -174
- package/src/capability/mcp/transport.ts +0 -394
- package/src/channel.config.test.ts +0 -147
- package/src/channel.lifecycle.test.ts +0 -255
- package/src/channel.meta.test.ts +0 -26
- package/src/channel.ts +0 -256
- package/src/config/accounts.resolve.test.ts +0 -75
- package/src/config/accounts.ts +0 -296
- package/src/config/derived-paths.test.ts +0 -111
- package/src/config/derived-paths.ts +0 -41
- package/src/config/index.ts +0 -26
- package/src/config/media.test.ts +0 -113
- package/src/config/media.ts +0 -139
- package/src/config/network.ts +0 -53
- package/src/config/routing.test.ts +0 -88
- package/src/config/routing.ts +0 -26
- package/src/config/runtime-config.ts +0 -46
- package/src/config/schema.ts +0 -90
- package/src/context-store.ts +0 -297
- package/src/crypto/index.ts +0 -24
- package/src/crypto.test.ts +0 -32
- package/src/crypto.ts +0 -176
- package/src/domain/models.ts +0 -7
- package/src/domain/policies.ts +0 -36
- package/src/dynamic-agent.account-scope.test.ts +0 -17
- package/src/gateway-monitor.ts +0 -181
- package/src/http.ts +0 -145
- package/src/media.test.ts +0 -82
- package/src/monitor/limits.ts +0 -7
- package/src/monitor/state.queue.test.ts +0 -185
- package/src/monitor/state.ts +0 -34
- package/src/monitor.active.test.ts +0 -245
- package/src/monitor.inbound-filter.test.ts +0 -63
- package/src/monitor.integration.test.ts +0 -208
- package/src/monitor.ts +0 -121
- package/src/monitor.webhook.test.ts +0 -774
- package/src/observability/audit-log.ts +0 -48
- package/src/observability/legacy-operational-event-store.ts +0 -36
- package/src/observability/raw-envelope-log.ts +0 -28
- package/src/observability/status-registry.ts +0 -13
- package/src/observability/transport-session-view.ts +0 -14
- package/src/onboarding.test.ts +0 -336
- package/src/onboarding.ts +0 -704
- package/src/outbound.test.ts +0 -1271
- package/src/outbound.ts +0 -746
- package/src/runtime/dispatcher.ts +0 -71
- package/src/runtime/outbound-intent.ts +0 -4
- package/src/runtime/reply-orchestrator.test.ts +0 -71
- package/src/runtime/reply-orchestrator.ts +0 -67
- package/src/runtime/routing-bridge.test.ts +0 -115
- package/src/runtime/routing-bridge.ts +0 -44
- package/src/runtime/session-manager.test.ts +0 -174
- package/src/runtime/session-manager.ts +0 -139
- package/src/runtime/source-registry.ts +0 -249
- package/src/runtime.ts +0 -14
- package/src/shared/command-auth.ts +0 -87
- package/src/shared/media-asset.ts +0 -78
- package/src/shared/media-service.test.ts +0 -111
- package/src/shared/media-service.ts +0 -84
- package/src/shared/media-types.ts +0 -5
- package/src/shared/xml-parser.test.ts +0 -50
- package/src/store/active-reply-store.ts +0 -42
- package/src/store/interfaces.ts +0 -11
- package/src/store/memory-store.ts +0 -43
- package/src/store/stream-batch-store.ts +0 -350
- package/src/transport/agent-api/client.ts +0 -277
- package/src/transport/agent-api/core.ts +0 -463
- package/src/transport/agent-api/delivery.ts +0 -41
- package/src/transport/agent-api/media-upload.ts +0 -11
- package/src/transport/agent-api/reply.ts +0 -39
- package/src/transport/agent-api/upstream-delivery.ts +0 -45
- package/src/transport/agent-api/upstream-media-upload.ts +0 -70
- package/src/transport/agent-api/upstream-reply.ts +0 -43
- package/src/transport/agent-callback/http-handler.ts +0 -47
- package/src/transport/agent-callback/inbound.ts +0 -5
- package/src/transport/agent-callback/reply.ts +0 -13
- package/src/transport/agent-callback/request-handler.ts +0 -244
- package/src/transport/agent-callback/session.ts +0 -23
- package/src/transport/bot-webhook/active-reply.ts +0 -39
- package/src/transport/bot-webhook/http-handler.ts +0 -48
- package/src/transport/bot-webhook/inbound-normalizer.test.ts +0 -433
- package/src/transport/bot-webhook/inbound-normalizer.ts +0 -558
- package/src/transport/bot-webhook/inbound.ts +0 -5
- package/src/transport/bot-webhook/message-shape.ts +0 -92
- package/src/transport/bot-webhook/protocol.ts +0 -148
- package/src/transport/bot-webhook/reply.ts +0 -15
- package/src/transport/bot-webhook/request-handler.ts +0 -394
- package/src/transport/bot-webhook/session.ts +0 -23
- package/src/transport/bot-ws/inbound.test.ts +0 -290
- package/src/transport/bot-ws/inbound.ts +0 -163
- package/src/transport/bot-ws/media.test.ts +0 -44
- package/src/transport/bot-ws/media.ts +0 -321
- package/src/transport/bot-ws/reply.test.ts +0 -450
- package/src/transport/bot-ws/reply.ts +0 -365
- package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
- package/src/transport/bot-ws/sdk-adapter.ts +0 -314
- package/src/transport/bot-ws/session.ts +0 -28
- package/src/transport/http/common.ts +0 -109
- package/src/transport/http/registry.ts +0 -92
- package/src/transport/http/request-handler.ts +0 -84
- package/src/types/account.ts +0 -70
- package/src/types/config.ts +0 -114
- package/src/types/constants.ts +0 -31
- package/src/types/events.ts +0 -21
- package/src/types/global.d.ts +0 -9
- package/src/types/index.ts +0 -17
- package/src/types/legacy-stream.ts +0 -50
- package/src/types/message.ts +0 -189
- package/src/types/runtime-context.ts +0 -28
- package/src/types/runtime.ts +0 -165
- package/src/types.ts +0 -41
- package/src/upstream/index.ts +0 -150
- package/src/upstream.test.ts +0 -84
- package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -26
- /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
- /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
- /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
- /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
* WeCom XML 解析器
|
|
3
3
|
* 用于 Agent 模式解析 XML 格式消息
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import { XMLParser } from "fast-xml-parser";
|
|
7
|
-
import type { WecomAgentInboundMessage } from "../types/index.js";
|
|
8
|
-
|
|
9
6
|
const xmlParser = new XMLParser({
|
|
10
7
|
ignoreAttributes: false,
|
|
11
8
|
trimValues: true,
|
|
@@ -13,109 +10,113 @@ const xmlParser = new XMLParser({
|
|
|
13
10
|
parseTagValue: false,
|
|
14
11
|
parseAttributeValue: false,
|
|
15
12
|
});
|
|
16
|
-
|
|
17
13
|
/**
|
|
18
14
|
* 解析 XML 字符串为消息对象
|
|
19
15
|
*/
|
|
20
|
-
export function parseXml(xml
|
|
16
|
+
export function parseXml(xml) {
|
|
21
17
|
const obj = xmlParser.parse(xml);
|
|
22
18
|
const root = obj?.xml ?? obj;
|
|
23
19
|
return root ?? {};
|
|
24
20
|
}
|
|
25
|
-
|
|
26
21
|
/**
|
|
27
22
|
* 从 XML 中提取消息类型
|
|
28
23
|
*/
|
|
29
|
-
export function extractMsgType(msg
|
|
24
|
+
export function extractMsgType(msg) {
|
|
30
25
|
return String(msg.MsgType ?? "").toLowerCase();
|
|
31
26
|
}
|
|
32
|
-
|
|
33
27
|
/**
|
|
34
28
|
* 从 XML 中提取发送者 ID
|
|
35
29
|
*/
|
|
36
|
-
export function extractFromUser(msg
|
|
30
|
+
export function extractFromUser(msg) {
|
|
37
31
|
return String(msg.FromUserName ?? "");
|
|
38
32
|
}
|
|
39
|
-
|
|
40
33
|
/**
|
|
41
34
|
* 从 XML 中提取文件名(主要用于 file 消息)
|
|
42
35
|
*/
|
|
43
|
-
export function extractFileName(msg
|
|
44
|
-
const raw =
|
|
45
|
-
if (raw == null)
|
|
46
|
-
|
|
47
|
-
if (typeof raw === "
|
|
36
|
+
export function extractFileName(msg) {
|
|
37
|
+
const raw = msg.FileName ?? msg.Filename ?? msg.fileName ?? msg.filename;
|
|
38
|
+
if (raw == null)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (typeof raw === "string")
|
|
41
|
+
return raw.trim() || undefined;
|
|
42
|
+
if (typeof raw === "number" || typeof raw === "boolean" || typeof raw === "bigint")
|
|
43
|
+
return String(raw);
|
|
48
44
|
if (Array.isArray(raw)) {
|
|
49
45
|
const merged = raw.map((v) => (v == null ? "" : String(v))).join("\n").trim();
|
|
50
46
|
return merged || undefined;
|
|
51
47
|
}
|
|
52
48
|
if (typeof raw === "object") {
|
|
53
|
-
const obj = raw
|
|
49
|
+
const obj = raw;
|
|
54
50
|
const text = (typeof obj["#text"] === "string" ? obj["#text"] :
|
|
55
51
|
typeof obj["_text"] === "string" ? obj["_text"] :
|
|
56
52
|
typeof obj["text"] === "string" ? obj["text"] : undefined);
|
|
57
|
-
if (text && text.trim())
|
|
53
|
+
if (text && text.trim())
|
|
54
|
+
return text.trim();
|
|
58
55
|
}
|
|
59
56
|
const s = String(raw);
|
|
60
57
|
return s.trim() || undefined;
|
|
61
58
|
}
|
|
62
|
-
|
|
63
59
|
/**
|
|
64
60
|
* 从 XML 中提取接收者 ID (CorpID)
|
|
65
61
|
*/
|
|
66
|
-
export function extractToUser(msg
|
|
62
|
+
export function extractToUser(msg) {
|
|
67
63
|
return String(msg.ToUserName ?? "");
|
|
68
64
|
}
|
|
69
|
-
|
|
70
65
|
/**
|
|
71
66
|
* 从 XML 中提取群聊 ID
|
|
72
67
|
*/
|
|
73
|
-
export function extractChatId(msg
|
|
68
|
+
export function extractChatId(msg) {
|
|
74
69
|
return msg.ChatId ? String(msg.ChatId) : undefined;
|
|
75
70
|
}
|
|
76
|
-
|
|
77
71
|
/**
|
|
78
72
|
* 从 XML 中提取 AgentID(兼容 AgentID/agentid 等大小写)
|
|
79
73
|
*/
|
|
80
|
-
export function extractAgentId(msg
|
|
81
|
-
const raw =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (typeof raw === "string")
|
|
88
|
-
|
|
74
|
+
export function extractAgentId(msg) {
|
|
75
|
+
const raw = msg.AgentID ??
|
|
76
|
+
msg.AgentId ??
|
|
77
|
+
msg.agentid ??
|
|
78
|
+
msg.agentId;
|
|
79
|
+
if (raw == null)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (typeof raw === "string")
|
|
82
|
+
return raw.trim() || undefined;
|
|
83
|
+
if (typeof raw === "number")
|
|
84
|
+
return raw;
|
|
89
85
|
const asString = String(raw).trim();
|
|
90
86
|
return asString || undefined;
|
|
91
87
|
}
|
|
92
|
-
|
|
93
88
|
/**
|
|
94
89
|
* 从 XML 中提取消息内容
|
|
95
90
|
*/
|
|
96
|
-
export function extractContent(msg
|
|
91
|
+
export function extractContent(msg) {
|
|
97
92
|
const msgType = extractMsgType(msg);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (typeof value === "string")
|
|
102
|
-
|
|
103
|
-
if (
|
|
93
|
+
const asText = (value) => {
|
|
94
|
+
if (value == null)
|
|
95
|
+
return "";
|
|
96
|
+
if (typeof value === "string")
|
|
97
|
+
return value;
|
|
98
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
|
|
99
|
+
return String(value);
|
|
100
|
+
if (Array.isArray(value))
|
|
101
|
+
return value.map(asText).filter(Boolean).join("\n");
|
|
104
102
|
if (typeof value === "object") {
|
|
105
|
-
const obj = value
|
|
103
|
+
const obj = value;
|
|
106
104
|
// fast-xml-parser 在某些情况下(例如带属性)会把文本放在 "#text"
|
|
107
|
-
if (typeof obj["#text"] === "string")
|
|
108
|
-
|
|
109
|
-
if (typeof obj["
|
|
105
|
+
if (typeof obj["#text"] === "string")
|
|
106
|
+
return obj["#text"];
|
|
107
|
+
if (typeof obj["_text"] === "string")
|
|
108
|
+
return obj["_text"];
|
|
109
|
+
if (typeof obj["text"] === "string")
|
|
110
|
+
return obj["text"];
|
|
110
111
|
try {
|
|
111
112
|
return JSON.stringify(obj);
|
|
112
|
-
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
113
115
|
return String(value);
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
return String(value);
|
|
117
119
|
};
|
|
118
|
-
|
|
119
120
|
switch (msgType) {
|
|
120
121
|
case "text":
|
|
121
122
|
return asText(msg.Content);
|
|
@@ -138,30 +139,34 @@ export function extractContent(msg: WecomAgentInboundMessage): string {
|
|
|
138
139
|
return `[${msgType || "未知消息类型"}]`;
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
|
|
142
142
|
/**
|
|
143
143
|
* 从 XML 中提取媒体 ID (Image, Voice, Video)
|
|
144
144
|
* 根据官方文档,MediaId 在 Agent 回调中直接位于根节点
|
|
145
145
|
*/
|
|
146
|
-
export function extractMediaId(msg
|
|
147
|
-
const raw =
|
|
148
|
-
if (raw == null)
|
|
149
|
-
|
|
150
|
-
if (typeof raw === "
|
|
146
|
+
export function extractMediaId(msg) {
|
|
147
|
+
const raw = msg.MediaId ?? msg.MediaID ?? msg.mediaid ?? msg.mediaId;
|
|
148
|
+
if (raw == null)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (typeof raw === "string")
|
|
151
|
+
return raw.trim() || undefined;
|
|
152
|
+
if (typeof raw === "number" || typeof raw === "boolean" || typeof raw === "bigint")
|
|
153
|
+
return String(raw);
|
|
151
154
|
if (Array.isArray(raw)) {
|
|
152
155
|
const merged = raw.map((v) => (v == null ? "" : String(v))).join("\n").trim();
|
|
153
156
|
return merged || undefined;
|
|
154
157
|
}
|
|
155
158
|
if (typeof raw === "object") {
|
|
156
|
-
const obj = raw
|
|
159
|
+
const obj = raw;
|
|
157
160
|
const text = (typeof obj["#text"] === "string" ? obj["#text"] :
|
|
158
161
|
typeof obj["_text"] === "string" ? obj["_text"] :
|
|
159
162
|
typeof obj["text"] === "string" ? obj["text"] : undefined);
|
|
160
|
-
if (text && text.trim())
|
|
163
|
+
if (text && text.trim())
|
|
164
|
+
return text.trim();
|
|
161
165
|
try {
|
|
162
166
|
const s = JSON.stringify(obj);
|
|
163
167
|
return s.trim() || undefined;
|
|
164
|
-
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
165
170
|
const s = String(raw);
|
|
166
171
|
return s.trim() || undefined;
|
|
167
172
|
}
|
|
@@ -169,29 +174,33 @@ export function extractMediaId(msg: WecomAgentInboundMessage): string | undefine
|
|
|
169
174
|
const s = String(raw);
|
|
170
175
|
return s.trim() || undefined;
|
|
171
176
|
}
|
|
172
|
-
|
|
173
177
|
/**
|
|
174
178
|
* 从 XML 中提取 MsgId(用于去重)
|
|
175
179
|
*/
|
|
176
|
-
export function extractMsgId(msg
|
|
177
|
-
const raw =
|
|
178
|
-
if (raw == null)
|
|
179
|
-
|
|
180
|
-
if (typeof raw === "
|
|
180
|
+
export function extractMsgId(msg) {
|
|
181
|
+
const raw = msg.MsgId ?? msg.MsgID ?? msg.msgid ?? msg.msgId;
|
|
182
|
+
if (raw == null)
|
|
183
|
+
return undefined;
|
|
184
|
+
if (typeof raw === "string")
|
|
185
|
+
return raw.trim() || undefined;
|
|
186
|
+
if (typeof raw === "number" || typeof raw === "boolean" || typeof raw === "bigint")
|
|
187
|
+
return String(raw);
|
|
181
188
|
if (Array.isArray(raw)) {
|
|
182
189
|
const merged = raw.map((v) => (v == null ? "" : String(v))).join("\n").trim();
|
|
183
190
|
return merged || undefined;
|
|
184
191
|
}
|
|
185
192
|
if (typeof raw === "object") {
|
|
186
|
-
const obj = raw
|
|
193
|
+
const obj = raw;
|
|
187
194
|
const text = (typeof obj["#text"] === "string" ? obj["#text"] :
|
|
188
195
|
typeof obj["_text"] === "string" ? obj["_text"] :
|
|
189
196
|
typeof obj["text"] === "string" ? obj["text"] : undefined);
|
|
190
|
-
if (text && text.trim())
|
|
197
|
+
if (text && text.trim())
|
|
198
|
+
return text.trim();
|
|
191
199
|
try {
|
|
192
200
|
const s = JSON.stringify(obj);
|
|
193
201
|
return s.trim() || undefined;
|
|
194
|
-
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
195
204
|
const s = String(raw);
|
|
196
205
|
return s.trim() || undefined;
|
|
197
206
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LIMITS } from "../monitor/limits.js";
|
|
2
|
+
export class ActiveReplyStore {
|
|
3
|
+
policy;
|
|
4
|
+
activeReplies = new Map();
|
|
5
|
+
constructor(policy = "once") {
|
|
6
|
+
this.policy = policy;
|
|
7
|
+
}
|
|
8
|
+
store(streamId, responseUrl, proxyUrl) {
|
|
9
|
+
const url = responseUrl?.trim();
|
|
10
|
+
if (!url)
|
|
11
|
+
return;
|
|
12
|
+
this.activeReplies.set(streamId, { response_url: url, proxyUrl, createdAt: Date.now() });
|
|
13
|
+
}
|
|
14
|
+
getUrl(streamId) {
|
|
15
|
+
return this.activeReplies.get(streamId)?.response_url;
|
|
16
|
+
}
|
|
17
|
+
async use(streamId, fn) {
|
|
18
|
+
const state = this.activeReplies.get(streamId);
|
|
19
|
+
if (!state?.response_url)
|
|
20
|
+
return;
|
|
21
|
+
if (this.policy === "once" && state.usedAt) {
|
|
22
|
+
throw new Error(`response_url already used for stream ${streamId} (Policy: once)`);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
await fn({ responseUrl: state.response_url, proxyUrl: state.proxyUrl });
|
|
26
|
+
state.usedAt = Date.now();
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
state.lastError = err instanceof Error ? err.message : String(err);
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
prune(now = Date.now()) {
|
|
34
|
+
const cutoff = now - LIMITS.ACTIVE_REPLY_TTL_MS;
|
|
35
|
+
for (const [id, state] of this.activeReplies.entries()) {
|
|
36
|
+
if (state.createdAt < cutoff) {
|
|
37
|
+
this.activeReplies.delete(id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { buildDedupKey } from "../domain/policies.js";
|
|
2
|
+
export class InMemoryRuntimeStore {
|
|
3
|
+
seen = new Set();
|
|
4
|
+
replyContexts = new Map();
|
|
5
|
+
transportSessions = new Map();
|
|
6
|
+
deliveryTasks = new Map();
|
|
7
|
+
markInboundSeen(event) {
|
|
8
|
+
const key = buildDedupKey(event);
|
|
9
|
+
if (this.seen.has(key)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
this.seen.add(key);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
readReplyContext(messageId) {
|
|
16
|
+
return this.replyContexts.get(messageId);
|
|
17
|
+
}
|
|
18
|
+
writeReplyContext(messageId, context) {
|
|
19
|
+
this.replyContexts.set(messageId, context);
|
|
20
|
+
}
|
|
21
|
+
readTransportSession(accountId, transport) {
|
|
22
|
+
return this.transportSessions.get(`${accountId}:${transport}`);
|
|
23
|
+
}
|
|
24
|
+
writeTransportSession(snapshot) {
|
|
25
|
+
this.transportSessions.set(`${snapshot.accountId}:${snapshot.transport}`, snapshot);
|
|
26
|
+
}
|
|
27
|
+
writeDeliveryTask(task) {
|
|
28
|
+
this.deliveryTasks.set(task.messageId, task);
|
|
29
|
+
}
|
|
30
|
+
readDeliveryTask(messageId) {
|
|
31
|
+
return this.deliveryTasks.get(messageId);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { LIMITS } from "../monitor/limits.js";
|
|
3
|
+
export class StreamStore {
|
|
4
|
+
streams = new Map();
|
|
5
|
+
msgidToStreamId = new Map();
|
|
6
|
+
pendingInbounds = new Map();
|
|
7
|
+
conversationState = new Map();
|
|
8
|
+
streamIdToBatchKey = new Map();
|
|
9
|
+
batchKeyToStreamIds = new Map();
|
|
10
|
+
batchStreamIdToAckStreamIds = new Map();
|
|
11
|
+
onFlush;
|
|
12
|
+
setFlushHandler(handler) {
|
|
13
|
+
this.onFlush = handler;
|
|
14
|
+
}
|
|
15
|
+
linkStreamToBatch(streamId, batchKey) {
|
|
16
|
+
const key = String(batchKey ?? "").trim();
|
|
17
|
+
if (!key)
|
|
18
|
+
return;
|
|
19
|
+
this.streamIdToBatchKey.set(streamId, key);
|
|
20
|
+
const linked = this.batchKeyToStreamIds.get(key) ?? new Set();
|
|
21
|
+
linked.add(streamId);
|
|
22
|
+
this.batchKeyToStreamIds.set(key, linked);
|
|
23
|
+
}
|
|
24
|
+
unlinkStreamFromBatch(streamId, batchKey) {
|
|
25
|
+
const key = String(batchKey ?? this.streamIdToBatchKey.get(streamId) ?? "").trim();
|
|
26
|
+
this.streamIdToBatchKey.delete(streamId);
|
|
27
|
+
if (!key)
|
|
28
|
+
return;
|
|
29
|
+
const linked = this.batchKeyToStreamIds.get(key);
|
|
30
|
+
if (!linked)
|
|
31
|
+
return;
|
|
32
|
+
linked.delete(streamId);
|
|
33
|
+
if (linked.size === 0) {
|
|
34
|
+
this.batchKeyToStreamIds.delete(key);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.batchKeyToStreamIds.set(key, linked);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
hasLiveBatchKey(batchKey) {
|
|
41
|
+
const key = batchKey.trim();
|
|
42
|
+
if (!key)
|
|
43
|
+
return false;
|
|
44
|
+
return this.pendingInbounds.has(key) || (this.batchKeyToStreamIds.get(key)?.size ?? 0) > 0;
|
|
45
|
+
}
|
|
46
|
+
clearPendingTimer(pending) {
|
|
47
|
+
if (!pending?.timeout)
|
|
48
|
+
return;
|
|
49
|
+
clearTimeout(pending.timeout);
|
|
50
|
+
pending.timeout = null;
|
|
51
|
+
}
|
|
52
|
+
removePendingBatch(batchKey) {
|
|
53
|
+
const pending = this.pendingInbounds.get(batchKey);
|
|
54
|
+
if (!pending)
|
|
55
|
+
return undefined;
|
|
56
|
+
this.pendingInbounds.delete(batchKey);
|
|
57
|
+
this.clearPendingTimer(pending);
|
|
58
|
+
pending.readyToFlush = false;
|
|
59
|
+
return pending;
|
|
60
|
+
}
|
|
61
|
+
removeStreamRecord(streamId, state) {
|
|
62
|
+
const current = state ?? this.streams.get(streamId);
|
|
63
|
+
if (!current)
|
|
64
|
+
return;
|
|
65
|
+
this.streams.delete(streamId);
|
|
66
|
+
this.unlinkStreamFromBatch(streamId, current.batchKey);
|
|
67
|
+
if (current.msgid && this.msgidToStreamId.get(current.msgid) === streamId) {
|
|
68
|
+
this.msgidToStreamId.delete(current.msgid);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
pruneAckStreamMappings() {
|
|
72
|
+
for (const [batchStreamId, ackIds] of this.batchStreamIdToAckStreamIds.entries()) {
|
|
73
|
+
if (!this.streams.has(batchStreamId)) {
|
|
74
|
+
this.batchStreamIdToAckStreamIds.delete(batchStreamId);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const nextAckIds = ackIds.filter((ackId) => this.streams.has(ackId));
|
|
78
|
+
if (nextAckIds.length === 0) {
|
|
79
|
+
this.batchStreamIdToAckStreamIds.delete(batchStreamId);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
this.batchStreamIdToAckStreamIds.set(batchStreamId, nextAckIds);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
pruneConversationState() {
|
|
86
|
+
for (const [convKey, conv] of this.conversationState.entries()) {
|
|
87
|
+
conv.queue = conv.queue.filter((batchKey) => this.pendingInbounds.has(batchKey));
|
|
88
|
+
if (!this.hasLiveBatchKey(conv.activeBatchKey)) {
|
|
89
|
+
const next = conv.queue.shift();
|
|
90
|
+
if (!next) {
|
|
91
|
+
this.conversationState.delete(convKey);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
conv.activeBatchKey = next;
|
|
95
|
+
}
|
|
96
|
+
this.conversationState.set(convKey, conv);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
createStream(params) {
|
|
100
|
+
const streamId = crypto.randomBytes(16).toString("hex");
|
|
101
|
+
if (params.msgid) {
|
|
102
|
+
this.msgidToStreamId.set(String(params.msgid), streamId);
|
|
103
|
+
}
|
|
104
|
+
this.streams.set(streamId, {
|
|
105
|
+
streamId,
|
|
106
|
+
msgid: params.msgid,
|
|
107
|
+
conversationKey: params.conversationKey,
|
|
108
|
+
batchKey: params.batchKey,
|
|
109
|
+
createdAt: Date.now(),
|
|
110
|
+
updatedAt: Date.now(),
|
|
111
|
+
started: false,
|
|
112
|
+
finished: false,
|
|
113
|
+
content: "",
|
|
114
|
+
});
|
|
115
|
+
this.linkStreamToBatch(streamId, params.batchKey);
|
|
116
|
+
return streamId;
|
|
117
|
+
}
|
|
118
|
+
getStream(streamId) {
|
|
119
|
+
return this.streams.get(streamId);
|
|
120
|
+
}
|
|
121
|
+
getStreamByMsgId(msgid) {
|
|
122
|
+
return this.msgidToStreamId.get(String(msgid));
|
|
123
|
+
}
|
|
124
|
+
setStreamIdForMsgId(msgid, streamId) {
|
|
125
|
+
const key = String(msgid).trim();
|
|
126
|
+
const value = String(streamId).trim();
|
|
127
|
+
if (!key || !value)
|
|
128
|
+
return;
|
|
129
|
+
this.msgidToStreamId.set(key, value);
|
|
130
|
+
}
|
|
131
|
+
addAckStreamForBatch(params) {
|
|
132
|
+
const batchStreamId = params.batchStreamId.trim();
|
|
133
|
+
const ackStreamId = params.ackStreamId.trim();
|
|
134
|
+
if (!batchStreamId || !ackStreamId)
|
|
135
|
+
return;
|
|
136
|
+
const list = this.batchStreamIdToAckStreamIds.get(batchStreamId) ?? [];
|
|
137
|
+
list.push(ackStreamId);
|
|
138
|
+
this.batchStreamIdToAckStreamIds.set(batchStreamId, list);
|
|
139
|
+
}
|
|
140
|
+
drainAckStreamsForBatch(batchStreamId) {
|
|
141
|
+
const key = batchStreamId.trim();
|
|
142
|
+
if (!key)
|
|
143
|
+
return [];
|
|
144
|
+
const list = this.batchStreamIdToAckStreamIds.get(key) ?? [];
|
|
145
|
+
this.batchStreamIdToAckStreamIds.delete(key);
|
|
146
|
+
return list;
|
|
147
|
+
}
|
|
148
|
+
updateStream(streamId, mutator) {
|
|
149
|
+
const state = this.streams.get(streamId);
|
|
150
|
+
if (state) {
|
|
151
|
+
mutator(state);
|
|
152
|
+
state.updatedAt = Date.now();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
markStarted(streamId) {
|
|
156
|
+
this.updateStream(streamId, (s) => {
|
|
157
|
+
s.started = true;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
markFinished(streamId) {
|
|
161
|
+
this.updateStream(streamId, (s) => {
|
|
162
|
+
s.finished = true;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
addPendingMessage(params) {
|
|
166
|
+
const { conversationKey, target, msg, msgContent, nonce, timestamp, debounceMs } = params;
|
|
167
|
+
const effectiveDebounceMs = debounceMs ?? LIMITS.DEFAULT_DEBOUNCE_MS;
|
|
168
|
+
const state = this.conversationState.get(conversationKey);
|
|
169
|
+
if (!state) {
|
|
170
|
+
const batchKey = conversationKey;
|
|
171
|
+
const streamId = this.createStream({ msgid: msg.msgid, conversationKey, batchKey });
|
|
172
|
+
const pending = {
|
|
173
|
+
streamId,
|
|
174
|
+
conversationKey,
|
|
175
|
+
batchKey,
|
|
176
|
+
target,
|
|
177
|
+
msg,
|
|
178
|
+
contents: [msgContent],
|
|
179
|
+
msgids: msg.msgid ? [msg.msgid] : [],
|
|
180
|
+
nonce,
|
|
181
|
+
timestamp,
|
|
182
|
+
createdAt: Date.now(),
|
|
183
|
+
timeout: setTimeout(() => {
|
|
184
|
+
this.requestFlush(batchKey);
|
|
185
|
+
}, effectiveDebounceMs),
|
|
186
|
+
};
|
|
187
|
+
this.pendingInbounds.set(batchKey, pending);
|
|
188
|
+
this.conversationState.set(conversationKey, { activeBatchKey: batchKey, queue: [], nextSeq: 1 });
|
|
189
|
+
return { streamId, status: "active_new" };
|
|
190
|
+
}
|
|
191
|
+
const activeBatchKey = state.activeBatchKey;
|
|
192
|
+
const activeIsInitial = activeBatchKey === conversationKey;
|
|
193
|
+
const activePending = this.pendingInbounds.get(activeBatchKey);
|
|
194
|
+
if (activePending && !activeIsInitial) {
|
|
195
|
+
const activeStream = this.streams.get(activePending.streamId);
|
|
196
|
+
const activeStarted = Boolean(activeStream?.started);
|
|
197
|
+
if (!activeStarted) {
|
|
198
|
+
activePending.contents.push(msgContent);
|
|
199
|
+
if (msg.msgid) {
|
|
200
|
+
activePending.msgids.push(msg.msgid);
|
|
201
|
+
}
|
|
202
|
+
if (activePending.timeout)
|
|
203
|
+
clearTimeout(activePending.timeout);
|
|
204
|
+
activePending.timeout = setTimeout(() => {
|
|
205
|
+
this.requestFlush(activeBatchKey);
|
|
206
|
+
}, effectiveDebounceMs);
|
|
207
|
+
return { streamId: activePending.streamId, status: "active_merged" };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const queuedBatchKey = state.queue[0];
|
|
211
|
+
if (queuedBatchKey) {
|
|
212
|
+
const existingQueued = this.pendingInbounds.get(queuedBatchKey);
|
|
213
|
+
if (existingQueued) {
|
|
214
|
+
existingQueued.contents.push(msgContent);
|
|
215
|
+
if (msg.msgid) {
|
|
216
|
+
existingQueued.msgids.push(msg.msgid);
|
|
217
|
+
}
|
|
218
|
+
if (existingQueued.timeout)
|
|
219
|
+
clearTimeout(existingQueued.timeout);
|
|
220
|
+
existingQueued.timeout = setTimeout(() => {
|
|
221
|
+
this.requestFlush(queuedBatchKey);
|
|
222
|
+
}, effectiveDebounceMs);
|
|
223
|
+
return { streamId: existingQueued.streamId, status: "queued_merged" };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const seq = state.nextSeq++;
|
|
227
|
+
const batchKey = `${conversationKey}#q${seq}`;
|
|
228
|
+
state.queue = [batchKey];
|
|
229
|
+
const streamId = this.createStream({ msgid: msg.msgid, conversationKey, batchKey });
|
|
230
|
+
const pending = {
|
|
231
|
+
streamId,
|
|
232
|
+
conversationKey,
|
|
233
|
+
batchKey,
|
|
234
|
+
target,
|
|
235
|
+
msg,
|
|
236
|
+
contents: [msgContent],
|
|
237
|
+
msgids: msg.msgid ? [msg.msgid] : [],
|
|
238
|
+
nonce,
|
|
239
|
+
timestamp,
|
|
240
|
+
createdAt: Date.now(),
|
|
241
|
+
timeout: setTimeout(() => {
|
|
242
|
+
this.requestFlush(batchKey);
|
|
243
|
+
}, effectiveDebounceMs),
|
|
244
|
+
};
|
|
245
|
+
this.pendingInbounds.set(batchKey, pending);
|
|
246
|
+
this.conversationState.set(conversationKey, state);
|
|
247
|
+
return { streamId, status: "queued_new" };
|
|
248
|
+
}
|
|
249
|
+
requestFlush(batchKey) {
|
|
250
|
+
const pending = this.pendingInbounds.get(batchKey);
|
|
251
|
+
if (!pending)
|
|
252
|
+
return;
|
|
253
|
+
const state = this.conversationState.get(pending.conversationKey);
|
|
254
|
+
const isActive = state?.activeBatchKey === batchKey;
|
|
255
|
+
if (!isActive) {
|
|
256
|
+
if (pending.timeout) {
|
|
257
|
+
clearTimeout(pending.timeout);
|
|
258
|
+
pending.timeout = null;
|
|
259
|
+
}
|
|
260
|
+
pending.readyToFlush = true;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.flushPending(batchKey);
|
|
264
|
+
}
|
|
265
|
+
flushPending(pendingKey) {
|
|
266
|
+
const pending = this.removePendingBatch(pendingKey);
|
|
267
|
+
if (!pending)
|
|
268
|
+
return;
|
|
269
|
+
if (this.onFlush) {
|
|
270
|
+
this.onFlush(pending);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
onStreamFinished(streamId) {
|
|
274
|
+
const batchKey = this.streamIdToBatchKey.get(streamId);
|
|
275
|
+
const state = batchKey ? this.streams.get(streamId) : undefined;
|
|
276
|
+
const conversationKey = state?.conversationKey;
|
|
277
|
+
if (!batchKey || !conversationKey)
|
|
278
|
+
return;
|
|
279
|
+
this.unlinkStreamFromBatch(streamId, batchKey);
|
|
280
|
+
const conv = this.conversationState.get(conversationKey);
|
|
281
|
+
if (!conv)
|
|
282
|
+
return;
|
|
283
|
+
if (conv.activeBatchKey !== batchKey)
|
|
284
|
+
return;
|
|
285
|
+
const next = conv.queue.shift();
|
|
286
|
+
if (!next) {
|
|
287
|
+
this.conversationState.delete(conversationKey);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
conv.activeBatchKey = next;
|
|
291
|
+
this.conversationState.set(conversationKey, conv);
|
|
292
|
+
const pending = this.pendingInbounds.get(next);
|
|
293
|
+
if (!pending)
|
|
294
|
+
return;
|
|
295
|
+
if (pending.readyToFlush) {
|
|
296
|
+
this.flushPending(next);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
prune(now = Date.now()) {
|
|
300
|
+
const streamCutoff = now - LIMITS.STREAM_TTL_MS;
|
|
301
|
+
for (const [id, state] of this.streams.entries()) {
|
|
302
|
+
if (state.updatedAt < streamCutoff) {
|
|
303
|
+
this.removeStreamRecord(id, state);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
for (const [msgid, id] of this.msgidToStreamId.entries()) {
|
|
307
|
+
if (!this.streams.has(id)) {
|
|
308
|
+
this.msgidToStreamId.delete(msgid);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
for (const [key, pending] of this.pendingInbounds.entries()) {
|
|
312
|
+
if (now - pending.createdAt > LIMITS.STREAM_TTL_MS) {
|
|
313
|
+
this.removePendingBatch(key);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
this.pruneAckStreamMappings();
|
|
317
|
+
this.pruneConversationState();
|
|
318
|
+
}
|
|
319
|
+
}
|