@kafca/agentdock 0.1.56 → 0.1.57
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/README.md +7 -0
- package/dist/renderer/assets/{Badge-DjcoXx2J.js → Badge-CDTbmw1h.js} +1 -1
- package/dist/renderer/assets/Card-DayUo35w.js +1 -0
- package/dist/renderer/assets/Config-ByGMQuzR.js +21 -0
- package/dist/renderer/assets/CronList-XqHdVlgu.js +1 -0
- package/dist/renderer/assets/Dashboard-CZj-oANw.js +41 -0
- package/dist/renderer/assets/{EmptyState-DIZLiccW.js → EmptyState-EFMkOjZO.js} +1 -1
- package/dist/renderer/assets/HighlightedMarkdown-DyvF3DHK.js +12 -0
- package/dist/renderer/assets/Input-DxoWVc9d.js +1 -0
- package/dist/renderer/assets/{KnowledgeDetail-CJ9IYOsa.js → KnowledgeDetail-CbUb2tG7.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-BLGPwnBO.js → KnowledgeHome-BWV1gz4f.js} +3 -3
- package/dist/renderer/assets/Logs-DqnanQTc.js +6 -0
- package/dist/renderer/assets/{Modal-CQiUpfl4.js → Modal-yY7TzR0x.js} +3 -3
- package/dist/renderer/assets/MonitorList-CIjGQkFs.js +1 -0
- package/dist/renderer/assets/Page-vwHNUdoj.js +1 -0
- package/dist/renderer/assets/{Select-D6z65IT3.js → Select-CmbK36pN.js} +2 -2
- package/dist/renderer/assets/ThreadChat-4OceMgM4.js +86 -0
- package/dist/renderer/assets/Workspace-DplPJOjJ.js +19 -0
- package/dist/renderer/assets/{arrow-left-CV5cL3f4.js → arrow-left-C8HfBt2K.js} +1 -1
- package/dist/renderer/assets/{book-open-DThvB7pj.js → book-open-BCdZWncQ.js} +1 -1
- package/dist/renderer/assets/channels-hgO3cAIx.js +6 -0
- package/dist/renderer/assets/chevron-down-BPk77tqY.js +11 -0
- package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
- package/dist/renderer/assets/index-BlA4xPdd.js +41 -0
- package/dist/renderer/assets/index-wYpjqVqC.js +167 -0
- package/dist/renderer/assets/knowledge-7pl06KuH.js +1 -0
- package/dist/renderer/assets/{pencil-Dv5WxhdC.js → pencil-CB1SWZ9A.js} +1 -1
- package/dist/renderer/assets/{play-DnCnft83.js → play-BvsoXUrM.js} +1 -1
- package/dist/renderer/assets/{plus-DsxIagzS.js → plus-CokBDnaL.js} +1 -1
- package/dist/renderer/assets/{save-CST01lIc.js → save-D-w4kATO.js} +1 -1
- package/dist/renderer/assets/{search-B_bqy6Wd.js → search-Cnsn6i4Q.js} +1 -1
- package/dist/renderer/assets/{shield-check-D6lG5Aoc.js → shield-check-Cjja2Gh6.js} +1 -1
- package/dist/renderer/assets/threads-BCm6j4eI.js +1 -0
- package/dist/renderer/assets/{trash-2-Cefngb6Q.js → trash-2-CIxz9qYY.js} +1 -1
- package/dist/renderer/index.html +2 -2
- package/dist-electron/packages/contracts/src/automation.js +2 -0
- package/dist-electron/packages/contracts/src/channels.js +2 -0
- package/dist-electron/packages/contracts/src/knowledge.js +2 -0
- package/dist-electron/packages/contracts/src/local-core.js +15 -0
- package/dist-electron/packages/contracts/src/runtime.js +2 -0
- package/dist-electron/packages/contracts/src/scheduler.js +2 -0
- package/dist-electron/packages/contracts/src/threads.js +2 -0
- package/dist-electron/packages/core-sdk/src/automation.js +32 -0
- package/dist-electron/packages/core-sdk/src/channels.js +121 -0
- package/dist-electron/packages/core-sdk/src/client.js +455 -0
- package/dist-electron/packages/core-sdk/src/index.js +43 -607
- package/dist-electron/packages/core-sdk/src/knowledge.js +85 -0
- package/dist-electron/packages/core-sdk/src/request.js +22 -0
- package/dist-electron/packages/core-sdk/src/runtime.js +214 -0
- package/dist-electron/packages/core-sdk/src/scheduler.js +32 -0
- package/dist-electron/packages/core-sdk/src/threads.js +67 -0
- package/dist-electron/packages/plugin-sdk/src/agents.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/automation.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/channels.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/index.js +21 -0
- package/dist-electron/packages/plugin-sdk/src/knowledge.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/runtime.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/scheduler.js +2 -0
- package/dist-electron/packages/plugin-sdk/src/threads.js +2 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +5 -8
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +2 -2
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +3 -3
- package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +5 -5
- package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +12 -12
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +2 -2
- package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +11 -11
- package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +3 -3
- package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +7 -7
- package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +4 -4
- package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +2 -2
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +3 -3
- package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +1 -1
- package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +4 -4
- package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +33 -18
- package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -2
- package/dist-electron/services/local-ai-core/src/channel/lark/gateway-utils.js +91 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound-handler.js +180 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +52 -486
- package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +124 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/inbound-authorization.js +54 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +2 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-media.js +21 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +167 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +50 -469
- package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +156 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +100 -0
- package/dist-electron/services/local-ai-core/src/cli/lac.js +6 -6
- package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +2 -2
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +28 -70
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +2 -2
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +2 -2
- package/dist-electron/services/local-ai-core/src/router/workspace-bridge-event-stream.js +29 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +5 -5
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +47 -60
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -5
- package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +6 -6
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +3 -3
- package/dist-electron/services/local-ai-core/src/runtime/external-service.js +3 -1
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +11 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +12 -6
- package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +10 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +18 -6
- package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +7 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +28 -3
- package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +10 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +18 -5
- package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +14 -2
- package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +18 -15
- package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +13 -2
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +35 -8
- package/dist-electron/services/local-ai-core/src/runtime/request-validation.js +79 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +9 -9
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +10 -1
- package/dist-electron/services/local-ai-core/src/runtime/server.js +2 -1
- package/dist-electron/services/local-ai-core/src/runtime/workspace-project-registry.js +76 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
- package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +75 -0
- package/dist-electron/shared/desktop.js +1 -64
- package/dist-electron/src/components/chat/chat-controller-state.js +60 -0
- package/dist-electron/src/components/chat/chat-event-gate.js +146 -0
- package/dist-electron/src/components/chat/markdown-code-block.js +8 -0
- package/dist-electron/src/pages/Desktop/workspace-model.js +212 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +50 -51
- package/dist-electron/tests/contracts/agent-message-policy.test.js +21 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +56 -33
- package/dist-electron/tests/contracts/chat-event-gate.test.js +114 -0
- package/dist-electron/tests/contracts/chat-markdown.test.js +16 -0
- package/dist-electron/tests/contracts/thread-task-state.test.js +32 -0
- package/dist-electron/tests/contracts/workspace-identity.test.js +23 -0
- package/dist-electron/tests/electron/core-client.test.js +284 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +54 -0
- package/dist-electron/tests/electron/renderer-packaging.test.js +9 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +31 -0
- package/dist-electron/tests/integration/channel-inbound-authorization.test.js +80 -0
- package/dist-electron/tests/integration/lark-gateway-utils.test.js +13 -0
- package/dist-electron/tests/integration/lark-gateway.test.js +4 -4
- package/dist-electron/tests/integration/weixin-runtime-state.test.js +32 -0
- package/dist-electron/tests/integration/weixin-text-utils.test.js +15 -0
- package/package.json +9 -4
- package/dist/renderer/assets/Chat-C5Bkm9-M.js +0 -2
- package/dist/renderer/assets/ChatMarkdown-uc36EzyD.js +0 -45
- package/dist/renderer/assets/Config-DHtQuuLy.js +0 -21
- package/dist/renderer/assets/CronList-BYUnhzSC.js +0 -1
- package/dist/renderer/assets/Dashboard-BPnGMDg3.js +0 -26
- package/dist/renderer/assets/Logs-Dyd2Uwlt.js +0 -1
- package/dist/renderer/assets/MonitorList-Byg3E3Gt.js +0 -1
- package/dist/renderer/assets/Page-7DxXvTMm.js +0 -1
- package/dist/renderer/assets/ProjectDetail-Dfr5s0Ja.js +0 -11
- package/dist/renderer/assets/ProjectList-CDDQjAQ_.js +0 -1
- package/dist/renderer/assets/SessionChat-s7CAaCPL.js +0 -1
- package/dist/renderer/assets/SessionList-D-OycKKS.js +0 -1
- package/dist/renderer/assets/ThreadChat-CLdFynB5.js +0 -28
- package/dist/renderer/assets/Workspace-xfT5z4j_.js +0 -19
- package/dist/renderer/assets/bot-CWQ5-ovl.js +0 -6
- package/dist/renderer/assets/check-DKM7zfWG.js +0 -6
- package/dist/renderer/assets/chevron-down-Dfw2KU87.js +0 -6
- package/dist/renderer/assets/circle-CoP4RA4s.js +0 -6
- package/dist/renderer/assets/desktop-CsDYrOOU.js +0 -1
- package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
- package/dist/renderer/assets/funnel-DpVLlG3g.js +0 -6
- package/dist/renderer/assets/heart-BMD36ejP.js +0 -6
- package/dist/renderer/assets/index-COxSYBjE.js +0 -182
- package/dist/renderer/assets/index-CTGTI-cm.js +0 -41
- package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
- package/dist/renderer/assets/projects-DgjHcNgt.js +0 -1
- package/dist/renderer/assets/server-ecH4GNwa.js +0 -11
- package/dist/renderer/assets/session-utils-Dd_WKMYW.js +0 -6
- package/dist/renderer/assets/sessions-BQ33Mv4k.js +0 -1
- package/dist/renderer/assets/user-D_8RSH-T.js +0 -6
- package/dist/renderer/assets/wifi-off-DB9D-pOu.js +0 -11
- package/dist-electron/src/api/client.js +0 -73
- package/dist-electron/src/api/sessions.js +0 -21
- package/dist-electron/src/lib/session-utils.js +0 -51
- package/dist-electron/src/pages/Projects/project-provider-config.js +0 -75
- package/dist-electron/tests/electron/project-provider-config.test.js +0 -71
package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js
CHANGED
|
@@ -3,28 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.LocalCoreWeixinGateway = void 0;
|
|
7
|
-
exports.createWeixinAttachmentContentPart = createWeixinAttachmentContentPart;
|
|
6
|
+
exports.LocalCoreWeixinGateway = exports.createWeixinAttachmentContentPart = void 0;
|
|
8
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
9
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
11
9
|
const node_crypto_2 = require("node:crypto");
|
|
12
10
|
const node_stream_1 = require("node:stream");
|
|
13
|
-
const desktop_js_1 = require("../../../../../shared/desktop.js");
|
|
14
11
|
const local_core_errors_js_1 = require("../../kernel/local-core-errors.js");
|
|
15
12
|
const content_js_1 = require("../shared/content.js");
|
|
16
13
|
const file_utils_js_1 = require("../shared/file-utils.js");
|
|
17
14
|
const inbound_attachment_store_js_1 = require("../shared/inbound-attachment-store.js");
|
|
18
15
|
const thread_routing_js_1 = require("../shared/thread-routing.js");
|
|
19
16
|
const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
|
|
17
|
+
const inbound_authorization_js_1 = require("../shared/inbound-authorization.js");
|
|
20
18
|
const channel_keys_js_1 = require("../shared/channel-keys.js");
|
|
21
19
|
const config_js_1 = require("./config.js");
|
|
22
20
|
const transport_js_1 = require("./transport.js");
|
|
21
|
+
const runtime_state_js_1 = require("./runtime-state.js");
|
|
22
|
+
const text_utils_js_1 = require("./text-utils.js");
|
|
23
|
+
const inbound_poller_js_1 = require("./inbound-poller.js");
|
|
24
|
+
var inbound_media_js_1 = require("./inbound-media.js");
|
|
25
|
+
Object.defineProperty(exports, "createWeixinAttachmentContentPart", { enumerable: true, get: function () { return inbound_media_js_1.createWeixinAttachmentContentPart; } });
|
|
23
26
|
// ==================== Constants ====================
|
|
24
|
-
const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
|
|
25
|
-
const RETRY_DELAY_MS = 2_000;
|
|
26
|
-
const BACKOFF_DELAY_MS = 30_000;
|
|
27
|
-
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
28
27
|
const PROCESSED_MESSAGE_TTL_MS = 10 * 60 * 1000;
|
|
29
28
|
const WEIXIN_TEXT_MESSAGE_MAX_BYTES = 900;
|
|
30
29
|
const WEIXIN_CONTEXT_REPLY_MAX_BYTES = 3500;
|
|
@@ -33,125 +32,9 @@ const WEIXIN_RESERVED_TERMINAL_SENDS = 1;
|
|
|
33
32
|
const WEIXIN_PROGRESS_SEND_BUDGET = WEIXIN_CONTEXT_SEND_LIMIT - WEIXIN_RESERVED_TERMINAL_SENDS;
|
|
34
33
|
const RESPONSE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
35
34
|
const WEIXIN_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
|
|
36
|
-
const ERROR_LOG_WINDOW_MS = 5 * 60 * 1000;
|
|
37
|
-
// ==================== Utilities ====================
|
|
38
|
-
function stripHtml(html) {
|
|
39
|
-
let result = html;
|
|
40
|
-
let prev;
|
|
41
|
-
do {
|
|
42
|
-
prev = result;
|
|
43
|
-
result = result.replace(/<[^>]*>/g, '');
|
|
44
|
-
} while (result !== prev);
|
|
45
|
-
return result.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, ' ');
|
|
46
|
-
}
|
|
47
|
-
function formatError(err) {
|
|
48
|
-
if (err instanceof Error) {
|
|
49
|
-
const cause = err.cause;
|
|
50
|
-
return cause !== undefined ? `${err.message}: ${String(cause)}` : err.message;
|
|
51
|
-
}
|
|
52
|
-
return String(err);
|
|
53
|
-
}
|
|
54
|
-
function sleep(ms, signal) {
|
|
55
|
-
return new Promise((resolve, reject) => {
|
|
56
|
-
const t = setTimeout(resolve, ms);
|
|
57
|
-
const onAbort = () => { clearTimeout(t); reject(new Error('aborted')); };
|
|
58
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
function utf8ByteLength(value) {
|
|
62
|
-
return Buffer.byteLength(value, 'utf-8');
|
|
63
|
-
}
|
|
64
|
-
function splitTextByUtf8Bytes(text, maxBytes) {
|
|
65
|
-
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
66
|
-
if (!normalized)
|
|
67
|
-
return [];
|
|
68
|
-
if (utf8ByteLength(normalized) <= maxBytes)
|
|
69
|
-
return [normalized];
|
|
70
|
-
const chunks = [];
|
|
71
|
-
let current = '';
|
|
72
|
-
const pushCurrent = () => {
|
|
73
|
-
const trimmed = current.trim();
|
|
74
|
-
if (trimmed)
|
|
75
|
-
chunks.push(trimmed);
|
|
76
|
-
current = '';
|
|
77
|
-
};
|
|
78
|
-
const appendPart = (part) => {
|
|
79
|
-
if (!part.trim())
|
|
80
|
-
return;
|
|
81
|
-
const separator = current ? '\n\n' : '';
|
|
82
|
-
if (current && utf8ByteLength(`${current}${separator}${part}`) <= maxBytes) {
|
|
83
|
-
current = `${current}${separator}${part}`;
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (current)
|
|
87
|
-
pushCurrent();
|
|
88
|
-
if (utf8ByteLength(part) <= maxBytes) {
|
|
89
|
-
current = part;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
let segment = '';
|
|
93
|
-
for (const char of Array.from(part)) {
|
|
94
|
-
if (segment && utf8ByteLength(`${segment}${char}`) > maxBytes) {
|
|
95
|
-
chunks.push(segment);
|
|
96
|
-
segment = '';
|
|
97
|
-
}
|
|
98
|
-
segment += char;
|
|
99
|
-
}
|
|
100
|
-
current = segment;
|
|
101
|
-
};
|
|
102
|
-
for (const part of normalized.split(/\n{2,}/)) {
|
|
103
|
-
appendPart(part);
|
|
104
|
-
}
|
|
105
|
-
pushCurrent();
|
|
106
|
-
return chunks;
|
|
107
|
-
}
|
|
108
|
-
function createWeixinAttachmentContentPart(att) {
|
|
109
|
-
if (att.kind === 'image') {
|
|
110
|
-
if (!att.data)
|
|
111
|
-
return null;
|
|
112
|
-
return {
|
|
113
|
-
type: 'image',
|
|
114
|
-
data: att.data,
|
|
115
|
-
...(att.uri ? { uri: att.uri } : {}),
|
|
116
|
-
mimeType: att.mimeType,
|
|
117
|
-
fileName: att.name,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
type: 'file',
|
|
122
|
-
path: att.path,
|
|
123
|
-
fileName: att.name,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
function truncateTextByUtf8Bytes(text, maxBytes) {
|
|
127
|
-
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
128
|
-
if (utf8ByteLength(normalized) <= maxBytes)
|
|
129
|
-
return normalized;
|
|
130
|
-
const suffix = '\n\n(内容过长,已截断以保证微信送达)';
|
|
131
|
-
const budget = Math.max(0, maxBytes - utf8ByteLength(suffix));
|
|
132
|
-
let result = '';
|
|
133
|
-
for (const char of Array.from(normalized)) {
|
|
134
|
-
if (utf8ByteLength(`${result}${char}`) > budget)
|
|
135
|
-
break;
|
|
136
|
-
result += char;
|
|
137
|
-
}
|
|
138
|
-
return `${result.trim()}${suffix}`;
|
|
139
|
-
}
|
|
140
|
-
function renderBridgeContentForWeixin(event) {
|
|
141
|
-
const toolCall = event.toolCall;
|
|
142
|
-
if (!toolCall) {
|
|
143
|
-
return String(event.content || '').trim();
|
|
144
|
-
}
|
|
145
|
-
const name = String(toolCall.name || '').trim() || 'Tool update';
|
|
146
|
-
const status = String(toolCall.status || '').trim();
|
|
147
|
-
if (name === 'Tool update' && status === 'completed')
|
|
148
|
-
return '';
|
|
149
|
-
return [name, status].filter(Boolean).join(' - ');
|
|
150
|
-
}
|
|
151
35
|
// ==================== Gateway Class ====================
|
|
152
36
|
class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGateway {
|
|
153
37
|
processedInboundMessages = new Map();
|
|
154
|
-
pollErrorLogWindows = new Map();
|
|
155
38
|
inboundAttachmentStore = new inbound_attachment_store_js_1.FileSystemInboundAttachmentStore();
|
|
156
39
|
platform = 'weixin';
|
|
157
40
|
constructor(options) {
|
|
@@ -172,7 +55,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
172
55
|
return (0, config_js_1.collectWeixinWorkspaceBindings)(config);
|
|
173
56
|
}
|
|
174
57
|
buildStatusObject(state, resolved) {
|
|
175
|
-
this.options.store.expirePendingPairings();
|
|
176
58
|
const platformKey = state.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', resolved.instanceId);
|
|
177
59
|
const pairings = this.options.store.listPendingPairings(state.workspaceId)
|
|
178
60
|
.filter((row) => row.platform === platformKey && row.expires_at >= new Date().toISOString());
|
|
@@ -329,19 +211,19 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
329
211
|
const bridgeThreadId = route.threadId || binding.thread_id;
|
|
330
212
|
if (this.mutedThreadBridgeCounts.has(bridgeThreadId))
|
|
331
213
|
return;
|
|
332
|
-
const turn = this.
|
|
214
|
+
const turn = (0, runtime_state_js_1.getOrCreateWeixinTurnState)(this.outboundTurns, sessionKey);
|
|
333
215
|
if (event.replyCtx) {
|
|
334
216
|
// WeChat doesn't support reply context; ignore
|
|
335
217
|
}
|
|
336
|
-
|
|
218
|
+
(0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, event);
|
|
337
219
|
if (event.type !== 'reply' && event.type !== 'buttons' && event.type !== 'status')
|
|
338
220
|
return;
|
|
339
|
-
const rendered =
|
|
221
|
+
const rendered = (0, runtime_state_js_1.renderWeixinTurnText)(turn);
|
|
340
222
|
if (!rendered)
|
|
341
223
|
return;
|
|
342
224
|
if (rendered === turn.lastSentText)
|
|
343
225
|
return;
|
|
344
|
-
const terminalMessage =
|
|
226
|
+
const terminalMessage = (0, runtime_state_js_1.isTerminalWeixinBridgeMessage)(event, rendered);
|
|
345
227
|
if (binding.last_platform_message_id && !terminalMessage && turn.sentCount >= WEIXIN_PROGRESS_SEND_BUDGET) {
|
|
346
228
|
turn.foldedProgressCount += 1;
|
|
347
229
|
this.options.log?.(`localcore-weixin folded progress for sessionKey=${sessionKey}: sent=${turn.sentCount} folded=${turn.foldedProgressCount}`);
|
|
@@ -364,7 +246,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
364
246
|
this.options.log?.(`localcore-weixin sent message for sessionKey=${sessionKey} type=${event.type} sent=${turn.sentCount}/${binding.last_platform_message_id ? WEIXIN_CONTEXT_SEND_LIMIT : 'unlimited'}`);
|
|
365
247
|
}
|
|
366
248
|
catch (error) {
|
|
367
|
-
this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${
|
|
249
|
+
this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${(0, text_utils_js_1.formatWeixinError)(error)}`);
|
|
368
250
|
}
|
|
369
251
|
})
|
|
370
252
|
.finally(() => {
|
|
@@ -389,7 +271,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
389
271
|
const previousRoute = this.threadRouting.get(input.sessionKey);
|
|
390
272
|
this.threadRouting.set(input.sessionKey, route);
|
|
391
273
|
if (!this.outboundTurns.has(input.sessionKey)) {
|
|
392
|
-
this.
|
|
274
|
+
this.outboundTurns.set(input.sessionKey, (0, runtime_state_js_1.createWeixinTurnState)(input.sessionKey));
|
|
393
275
|
}
|
|
394
276
|
return () => {
|
|
395
277
|
if (previousRoute) {
|
|
@@ -492,52 +374,32 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
492
374
|
messageId: msg.messageId,
|
|
493
375
|
},
|
|
494
376
|
});
|
|
495
|
-
this.options.store.expirePendingPairings();
|
|
496
377
|
const binding = await this.getBinding(msg.workspaceId, instanceId);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
|
|
513
|
-
this.notifyRuntimeStateChanged();
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
if (!authorized) {
|
|
517
|
-
const existingPending = this.options.store.listPendingPairings(msg.workspaceId).find((item) => item.platform === platformKey && item.platform_user_id === msg.platformUserId && item.chat_id === msg.chatId && item.status === 'pending');
|
|
518
|
-
let pairingCode = existingPending?.code || '';
|
|
519
|
-
if (!existingPending) {
|
|
520
|
-
const now = new Date();
|
|
521
|
-
pairingCode = String((0, node_crypto_2.randomInt)(100000, 1000000));
|
|
522
|
-
this.options.store.createPairingRequest({
|
|
523
|
-
code: pairingCode,
|
|
524
|
-
workspace_id: msg.workspaceId,
|
|
525
|
-
platform: platformKey,
|
|
526
|
-
platform_user_id: msg.platformUserId,
|
|
527
|
-
chat_id: msg.chatId,
|
|
528
|
-
display_name: msg.displayName,
|
|
529
|
-
requested_at: now.toISOString(),
|
|
530
|
-
expires_at: new Date(now.getTime() + PAIRING_EXPIRY_MS).toISOString(),
|
|
531
|
-
status: 'pending',
|
|
532
|
-
});
|
|
533
|
-
this.notifyRuntimeStateChanged();
|
|
534
|
-
}
|
|
378
|
+
const authorization = (0, inbound_authorization_js_1.resolveInboundChannelAuthorization)({
|
|
379
|
+
store: this.options.store,
|
|
380
|
+
identity: {
|
|
381
|
+
workspaceId: msg.workspaceId,
|
|
382
|
+
platformKey,
|
|
383
|
+
platformUserId: msg.platformUserId,
|
|
384
|
+
chatId: msg.chatId,
|
|
385
|
+
displayName: msg.displayName,
|
|
386
|
+
},
|
|
387
|
+
autoApprove: binding.allowFrom === '*',
|
|
388
|
+
authorizedUserIdPrefix: 'wx-user',
|
|
389
|
+
generatePairingCode: () => String((0, node_crypto_2.randomInt)(100000, 1000000)),
|
|
390
|
+
onStateChanged: () => this.notifyRuntimeStateChanged(),
|
|
391
|
+
});
|
|
392
|
+
if (authorization.status === 'pending') {
|
|
535
393
|
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(msg.workspaceId, instanceId));
|
|
536
394
|
if (state?.connected) {
|
|
537
|
-
await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${pairingCode}\``, msg.contextToken);
|
|
395
|
+
await this.sendTextMessage(state, msg.chatId, `**已收到消息**\n\n当前账号还未授权接入这个工作区。\n请在桌面端完成审批后再次发送消息。\n\n配对码:\`${authorization.pairingCode}\``, msg.contextToken);
|
|
538
396
|
}
|
|
539
397
|
return;
|
|
540
398
|
}
|
|
399
|
+
if (authorization.autoApproved) {
|
|
400
|
+
this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
|
|
401
|
+
}
|
|
402
|
+
const authorized = authorization.authorized;
|
|
541
403
|
const router = this.options.getWorkspaceRouter();
|
|
542
404
|
let { threadId } = await (0, thread_routing_js_1.resolveChannelThreadRoute)({
|
|
543
405
|
store: this.options.store,
|
|
@@ -588,10 +450,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
588
450
|
return;
|
|
589
451
|
}
|
|
590
452
|
this.options.store.updatePlatformThreadMessageId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.contextToken || msg.messageId, platformKey);
|
|
591
|
-
|
|
592
|
-
? msg.text
|
|
593
|
-
: (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(msg.text);
|
|
594
|
-
await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(wrappedText, msg.contentParts));
|
|
453
|
+
await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(msg.text, msg.contentParts));
|
|
595
454
|
return;
|
|
596
455
|
}
|
|
597
456
|
async resolveDefaultAgentType(workspaceId, threadId) {
|
|
@@ -638,7 +497,18 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
638
497
|
status.connectedAt = new Date().toISOString();
|
|
639
498
|
this.clearRuntimeError(status);
|
|
640
499
|
// Start long-poll loop in background
|
|
641
|
-
|
|
500
|
+
(0, inbound_poller_js_1.runWeixinInboundPoller)({
|
|
501
|
+
binding,
|
|
502
|
+
signal: abortController.signal,
|
|
503
|
+
getRuntimeState: () => this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId)),
|
|
504
|
+
getAuthorizedUser: (workspaceId, platformUserId, platformKey) => this.options.store.getAuthorizedUser(workspaceId, platformUserId, platformKey),
|
|
505
|
+
clearRuntimeError: (runtimeState) => this.clearRuntimeError(runtimeState),
|
|
506
|
+
setRuntimeError: (runtimeState, errorInfo) => this.setRuntimeError(runtimeState, errorInfo),
|
|
507
|
+
notifyRuntimeStateChanged: () => this.notifyRuntimeStateChanged(),
|
|
508
|
+
downloadMediaItem: (item, messageId, index, uploadsDir, workspaceBinding) => this.downloadMediaItem(item, messageId, index, uploadsDir, workspaceBinding),
|
|
509
|
+
handleInboundMessage: (message) => this.handleInboundMessage(message),
|
|
510
|
+
log: this.options.log,
|
|
511
|
+
}).catch((err) => {
|
|
642
512
|
if (!abortController.signal.aborted) {
|
|
643
513
|
status.status = 'error';
|
|
644
514
|
status.connected = false;
|
|
@@ -665,136 +535,13 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
665
535
|
return 'expired';
|
|
666
536
|
return 'wait';
|
|
667
537
|
}
|
|
668
|
-
async runMonitorLoop(binding, signal) {
|
|
669
|
-
let buf = (0, config_js_1.loadWeixinBuf)(binding);
|
|
670
|
-
let consecutiveFailures = 0;
|
|
671
|
-
while (!signal.aborted) {
|
|
672
|
-
try {
|
|
673
|
-
const resp = await (0, transport_js_1.getWeixinUpdates)(binding, buf, signal);
|
|
674
|
-
const isApiError = (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
|
|
675
|
-
if (isApiError) {
|
|
676
|
-
consecutiveFailures++;
|
|
677
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
678
|
-
const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
|
|
679
|
-
if (resp.errcode === -14 || resp.ret === -14) {
|
|
680
|
-
if (state) {
|
|
681
|
-
state.status = 'error';
|
|
682
|
-
state.connected = false;
|
|
683
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
684
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
685
|
-
this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(new local_core_errors_js_1.LocalCoreError('channel_session_expired', 'WeChat login expired.')));
|
|
686
|
-
this.notifyRuntimeStateChanged();
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
this.logPollError(binding, `ret=${resp.ret} errcode=${resp.errcode} errmsg=${resp.errmsg || ''}`, `localcore-weixin getUpdates failed for ${binding.workspaceId}: ret=${resp.ret} errcode=${resp.errcode}${resp.errmsg ? ` errmsg=${resp.errmsg}` : ''} (${consecutiveFailures})`);
|
|
690
|
-
if (state && resp.errcode !== -14 && resp.ret !== -14) {
|
|
691
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
692
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
693
|
-
}
|
|
694
|
-
await sleep(retryDelayMs, signal);
|
|
695
|
-
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
696
|
-
consecutiveFailures = MAX_CONSECUTIVE_FAILURES;
|
|
697
|
-
}
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
consecutiveFailures = 0;
|
|
701
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
702
|
-
if (state) {
|
|
703
|
-
this.clearRuntimeError(state);
|
|
704
|
-
state.status = 'running';
|
|
705
|
-
state.connected = true;
|
|
706
|
-
this.notifyRuntimeStateChanged();
|
|
707
|
-
}
|
|
708
|
-
if (resp.get_updates_buf) {
|
|
709
|
-
buf = resp.get_updates_buf;
|
|
710
|
-
(0, config_js_1.saveWeixinBuf)(binding, buf);
|
|
711
|
-
}
|
|
712
|
-
for (const msg of resp.msgs ?? []) {
|
|
713
|
-
const items = msg.item_list ?? [];
|
|
714
|
-
const textItem = items.find((i) => i.type === transport_js_1.TEXT_ITEM_TYPE);
|
|
715
|
-
const voiceTextItems = items.filter((i) => i.type === transport_js_1.VOICE_ITEM_TYPE && i.voice_item?.text);
|
|
716
|
-
const mediaItems = items.filter((i) => i.type === transport_js_1.IMAGE_ITEM_TYPE || i.type === transport_js_1.FILE_ITEM_TYPE);
|
|
717
|
-
if (!textItem && voiceTextItems.length === 0 && mediaItems.length === 0)
|
|
718
|
-
continue;
|
|
719
|
-
const conversationId = msg.from_user_id ?? '';
|
|
720
|
-
const text = [textItem?.text_item?.text?.trim(), ...voiceTextItems.map((i) => i.voice_item?.text?.trim())]
|
|
721
|
-
.filter((part) => Boolean(part))
|
|
722
|
-
.join('\n\n');
|
|
723
|
-
const msgId = msg.msg_id ?? String(Date.now());
|
|
724
|
-
// Handle attachments
|
|
725
|
-
let attachmentText = '';
|
|
726
|
-
const attachmentParts = [];
|
|
727
|
-
if (mediaItems.length > 0) {
|
|
728
|
-
const uploadsDir = node_path_1.default.join(binding.stateDir, 'weixin-uploads');
|
|
729
|
-
const mayMaterializeAttachments = binding.allowFrom === '*'
|
|
730
|
-
|| Boolean(this.options.store.getAuthorizedUser(binding.workspaceId, conversationId, binding.platformKey));
|
|
731
|
-
for (const [idx, item] of mediaItems.entries()) {
|
|
732
|
-
const itemData = item.image_item ?? item.file_item ?? null;
|
|
733
|
-
const declaredName = String(itemData?.file_name ?? (item.type === transport_js_1.IMAGE_ITEM_TYPE ? 'image' : 'file'));
|
|
734
|
-
if (!mayMaterializeAttachments) {
|
|
735
|
-
attachmentText += attachmentText ? '\n' : '';
|
|
736
|
-
attachmentText += item.type === transport_js_1.IMAGE_ITEM_TYPE ? '[Image]' : `[File: ${declaredName}]`;
|
|
737
|
-
continue;
|
|
738
|
-
}
|
|
739
|
-
try {
|
|
740
|
-
const att = await this.downloadMediaItem(item, msgId, idx, uploadsDir, binding);
|
|
741
|
-
if (att) {
|
|
742
|
-
attachmentText += attachmentText ? '\n' : '';
|
|
743
|
-
attachmentText += att.kind === 'image' ? `[Image: ${att.path}]` : `[File "${att.name}": ${att.path}]`;
|
|
744
|
-
const part = createWeixinAttachmentContentPart(att);
|
|
745
|
-
if (part)
|
|
746
|
-
attachmentParts.push(part);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
catch (dlErr) {
|
|
750
|
-
this.options.log?.(`localcore-weixin attachment download failed (${conversationId}#${idx}): ${formatError(dlErr)}`);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
const fullText = [text, attachmentText].filter(Boolean).join('\n\n');
|
|
755
|
-
if (!fullText)
|
|
756
|
-
continue;
|
|
757
|
-
await this.handleInboundMessage({
|
|
758
|
-
workspaceId: binding.workspaceId,
|
|
759
|
-
instanceId: binding.instanceId,
|
|
760
|
-
platformKey: binding.platformKey,
|
|
761
|
-
platformUserId: conversationId,
|
|
762
|
-
chatId: conversationId,
|
|
763
|
-
displayName: conversationId.slice(-6),
|
|
764
|
-
text: fullText,
|
|
765
|
-
messageId: msgId,
|
|
766
|
-
contextToken: msg.context_token,
|
|
767
|
-
contentParts: [
|
|
768
|
-
...(text ? [{ type: 'text', text }] : []),
|
|
769
|
-
...attachmentParts,
|
|
770
|
-
],
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
catch (err) {
|
|
775
|
-
if (signal.aborted)
|
|
776
|
-
return;
|
|
777
|
-
consecutiveFailures++;
|
|
778
|
-
const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
|
|
779
|
-
const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
|
|
780
|
-
if (state) {
|
|
781
|
-
state.consecutiveFailures = consecutiveFailures;
|
|
782
|
-
state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
783
|
-
this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(err));
|
|
784
|
-
this.notifyRuntimeStateChanged();
|
|
785
|
-
}
|
|
786
|
-
this.logPollError(binding, formatError(err), `localcore-weixin getUpdates error for ${binding.workspaceId} (${consecutiveFailures}): ${formatError(err)}`);
|
|
787
|
-
await sleep(retryDelayMs, signal);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
538
|
// ==================== Private: HTTP API ====================
|
|
792
539
|
async sendTextMessage(state, toUserId, text, contextToken, options = {}) {
|
|
793
540
|
const binding = await this.getBinding(state.workspaceId);
|
|
794
|
-
const stripped =
|
|
541
|
+
const stripped = (0, text_utils_js_1.stripWeixinHtml)(text);
|
|
795
542
|
const chunks = contextToken
|
|
796
|
-
? [truncateTextByUtf8Bytes(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
|
|
797
|
-
: splitTextByUtf8Bytes(stripped, WEIXIN_TEXT_MESSAGE_MAX_BYTES);
|
|
543
|
+
? [(0, text_utils_js_1.truncateTextByUtf8Bytes)(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
|
|
544
|
+
: (0, text_utils_js_1.splitTextByUtf8Bytes)(stripped, WEIXIN_TEXT_MESSAGE_MAX_BYTES);
|
|
798
545
|
for (const [index, chunk] of chunks.entries()) {
|
|
799
546
|
const finalChunk = options.final && index === chunks.length - 1;
|
|
800
547
|
const resp = await (0, transport_js_1.sendWeixinTextMessageChunk)(binding, toUserId, chunk, contextToken, {
|
|
@@ -802,7 +549,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
802
549
|
final: finalChunk,
|
|
803
550
|
});
|
|
804
551
|
if ((0, transport_js_1.isWeixinApiError)(resp)) {
|
|
805
|
-
throw new Error(`WeChat sendmessage failed: ret=${resp.ret} errcode=${resp.errcode}${resp.errmsg ? ` errmsg=${resp.errmsg}` : ''} chunk=${index + 1}/${chunks.length} bytes=${utf8ByteLength(chunk)} context=${contextToken ? 'yes' : 'no'} message_state=${finalChunk ? 2 : 1}`);
|
|
552
|
+
throw new Error(`WeChat sendmessage failed: ret=${resp.ret} errcode=${resp.errcode}${resp.errmsg ? ` errmsg=${resp.errmsg}` : ''} chunk=${index + 1}/${chunks.length} bytes=${(0, text_utils_js_1.utf8ByteLength)(chunk)} context=${contextToken ? 'yes' : 'no'} message_state=${finalChunk ? 2 : 1}`);
|
|
806
553
|
}
|
|
807
554
|
}
|
|
808
555
|
this.options.log?.(`localcore-weixin sent message to ${toUserId} for workspace ${state.workspaceId}${chunks.length > 1 ? ` chunks=${chunks.length}` : ''}`);
|
|
@@ -961,149 +708,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
961
708
|
return 'image/gif';
|
|
962
709
|
return 'application/octet-stream';
|
|
963
710
|
}
|
|
964
|
-
// ==================== Private: Turn State ====================
|
|
965
|
-
createTurnState(sessionKey) {
|
|
966
|
-
const turn = {
|
|
967
|
-
sessionKey,
|
|
968
|
-
sentCount: 0,
|
|
969
|
-
foldedProgressCount: 0,
|
|
970
|
-
awaitingPermission: false,
|
|
971
|
-
processing: false,
|
|
972
|
-
previewText: '',
|
|
973
|
-
finalText: '',
|
|
974
|
-
thinkingSteps: [],
|
|
975
|
-
pendingThoughtText: undefined,
|
|
976
|
-
statusLines: [],
|
|
977
|
-
buttonRows: [],
|
|
978
|
-
lastSentAt: 0,
|
|
979
|
-
lastSentText: '',
|
|
980
|
-
};
|
|
981
|
-
this.outboundTurns.set(sessionKey, turn);
|
|
982
|
-
return turn;
|
|
983
|
-
}
|
|
984
|
-
getOrCreateTurnState(sessionKey) {
|
|
985
|
-
return this.outboundTurns.get(sessionKey) || this.createTurnState(sessionKey);
|
|
986
|
-
}
|
|
987
|
-
consumeBridgeEvent(turn, event) {
|
|
988
|
-
const content = renderBridgeContentForWeixin(event);
|
|
989
|
-
const bridgeKind = this.resolveBridgeEventKind(event);
|
|
990
|
-
if (event.type === 'typing_start') {
|
|
991
|
-
turn.processing = true;
|
|
992
|
-
turn.previewText = '';
|
|
993
|
-
turn.finalText = '';
|
|
994
|
-
turn.thinkingSteps = [];
|
|
995
|
-
turn.pendingThoughtText = undefined;
|
|
996
|
-
turn.statusLines = [];
|
|
997
|
-
turn.buttonRows = [];
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
if (event.type === 'typing_stop') {
|
|
1001
|
-
turn.processing = false;
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
if (event.type === 'preview_start' || event.type === 'update_message') {
|
|
1005
|
-
if (bridgeKind === 'thought') {
|
|
1006
|
-
turn.pendingThoughtText = content;
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1009
|
-
turn.previewText = content;
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1012
|
-
if (bridgeKind !== 'thought') {
|
|
1013
|
-
this.flushPendingThought(turn);
|
|
1014
|
-
}
|
|
1015
|
-
if (event.type === 'status') {
|
|
1016
|
-
if (content) {
|
|
1017
|
-
this.pushUnique(turn.statusLines, content);
|
|
1018
|
-
turn.finalText = content;
|
|
1019
|
-
turn.previewText = content;
|
|
1020
|
-
}
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
if (event.type === 'buttons') {
|
|
1024
|
-
turn.awaitingPermission = true;
|
|
1025
|
-
turn.buttonRows = Array.isArray(event.buttonRows)
|
|
1026
|
-
? event.buttonRows
|
|
1027
|
-
.map((row) => Array.isArray(row)
|
|
1028
|
-
? row.filter((b) => Boolean(b?.text && b?.data))
|
|
1029
|
-
.map((b) => ({ text: b.text, data: b.data }))
|
|
1030
|
-
: [])
|
|
1031
|
-
.filter((row) => row.length > 0)
|
|
1032
|
-
: [];
|
|
1033
|
-
return;
|
|
1034
|
-
}
|
|
1035
|
-
if (!content)
|
|
1036
|
-
return;
|
|
1037
|
-
if (bridgeKind === 'thought' || bridgeKind === 'plan') {
|
|
1038
|
-
this.pushUnique(turn.thinkingSteps, content);
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (bridgeKind === 'tool' || bridgeKind === 'status') {
|
|
1042
|
-
this.pushUnique(turn.statusLines, content);
|
|
1043
|
-
return;
|
|
1044
|
-
}
|
|
1045
|
-
turn.finalText = content;
|
|
1046
|
-
turn.previewText = content;
|
|
1047
|
-
}
|
|
1048
|
-
renderTurnText(turn) {
|
|
1049
|
-
const sections = [];
|
|
1050
|
-
if (turn.thinkingSteps.length > 0) {
|
|
1051
|
-
sections.push(`**中间过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
|
|
1052
|
-
}
|
|
1053
|
-
if (turn.finalText) {
|
|
1054
|
-
sections.push(turn.finalText);
|
|
1055
|
-
}
|
|
1056
|
-
else if (turn.previewText) {
|
|
1057
|
-
sections.push(turn.previewText);
|
|
1058
|
-
}
|
|
1059
|
-
else if (turn.statusLines.length > 0) {
|
|
1060
|
-
sections.push(`**处理中**\n${turn.statusLines.slice(-3).map((l) => `• ${l.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
|
|
1061
|
-
}
|
|
1062
|
-
else if (turn.processing) {
|
|
1063
|
-
sections.push('**处理中**\n正在思考...');
|
|
1064
|
-
}
|
|
1065
|
-
if (turn.awaitingPermission) {
|
|
1066
|
-
sections.push('\n回复:`allow` / `allow all` / `deny`');
|
|
1067
|
-
}
|
|
1068
|
-
return sections.join('\n\n').trim();
|
|
1069
|
-
}
|
|
1070
|
-
flushPendingThought(turn) {
|
|
1071
|
-
const text = String(turn.pendingThoughtText || '').trim();
|
|
1072
|
-
if (!text)
|
|
1073
|
-
return;
|
|
1074
|
-
this.pushUnique(turn.thinkingSteps, text);
|
|
1075
|
-
turn.pendingThoughtText = undefined;
|
|
1076
|
-
}
|
|
1077
|
-
isTerminalBridgeMessage(event, rendered) {
|
|
1078
|
-
if (event.type === 'buttons')
|
|
1079
|
-
return true;
|
|
1080
|
-
if (event.type !== 'reply')
|
|
1081
|
-
return false;
|
|
1082
|
-
const bridgeKind = this.resolveBridgeEventKind(event);
|
|
1083
|
-
if (bridgeKind === 'tool' || bridgeKind === 'thought' || bridgeKind === 'plan' || bridgeKind === 'status')
|
|
1084
|
-
return false;
|
|
1085
|
-
const normalized = rendered.trim();
|
|
1086
|
-
if (!normalized)
|
|
1087
|
-
return false;
|
|
1088
|
-
return true;
|
|
1089
|
-
}
|
|
1090
|
-
resolveBridgeEventKind(event) {
|
|
1091
|
-
if (event.bridgeKind) {
|
|
1092
|
-
return event.bridgeKind;
|
|
1093
|
-
}
|
|
1094
|
-
return event.type === 'status' ? 'status' : 'assistant';
|
|
1095
|
-
}
|
|
1096
|
-
// ==================== Private: Helpers ====================
|
|
1097
|
-
pushUnique(target, value) {
|
|
1098
|
-
const normalized = value.trim();
|
|
1099
|
-
if (!normalized)
|
|
1100
|
-
return;
|
|
1101
|
-
if (target[target.length - 1] === normalized)
|
|
1102
|
-
return;
|
|
1103
|
-
target.push(normalized);
|
|
1104
|
-
if (target.length > 8)
|
|
1105
|
-
target.splice(0, target.length - 8);
|
|
1106
|
-
}
|
|
1107
711
|
isDuplicateInboundMessage(input) {
|
|
1108
712
|
const messageKey = input.contextToken || input.messageId;
|
|
1109
713
|
if (!messageKey)
|
|
@@ -1138,29 +742,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
|
|
|
1138
742
|
return null;
|
|
1139
743
|
return { name: name.trim().toLowerCase(), args };
|
|
1140
744
|
}
|
|
1141
|
-
computeRetryDelay(failures) {
|
|
1142
|
-
if (failures <= 1)
|
|
1143
|
-
return RETRY_DELAY_MS;
|
|
1144
|
-
if (failures === 2)
|
|
1145
|
-
return 5_000;
|
|
1146
|
-
if (failures === 3)
|
|
1147
|
-
return 15_000;
|
|
1148
|
-
if (failures === 4)
|
|
1149
|
-
return 30_000;
|
|
1150
|
-
return 60_000;
|
|
1151
|
-
}
|
|
1152
|
-
logPollError(binding, errorKey, message) {
|
|
1153
|
-
const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
|
|
1154
|
-
const current = this.pollErrorLogWindows.get(key);
|
|
1155
|
-
const now = Date.now();
|
|
1156
|
-
if (!current || current.errorKey !== errorKey || now - current.at >= ERROR_LOG_WINDOW_MS) {
|
|
1157
|
-
this.pollErrorLogWindows.set(key, { at: now, count: 1, errorKey });
|
|
1158
|
-
this.options.log?.(message);
|
|
1159
|
-
return;
|
|
1160
|
-
}
|
|
1161
|
-
current.count += 1;
|
|
1162
|
-
this.pollErrorLogWindows.set(key, current);
|
|
1163
|
-
}
|
|
1164
745
|
}
|
|
1165
746
|
exports.LocalCoreWeixinGateway = LocalCoreWeixinGateway;
|
|
1166
747
|
function getWeixinInstanceId(platform) {
|