@kafca/agentdock 0.1.55 → 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.
Files changed (185) hide show
  1. package/README.md +14 -0
  2. package/dist/renderer/assets/{Badge-Bg_nNvBL.js → Badge-CDTbmw1h.js} +1 -1
  3. package/dist/renderer/assets/Card-DayUo35w.js +1 -0
  4. package/dist/renderer/assets/Config-ByGMQuzR.js +21 -0
  5. package/dist/renderer/assets/CronList-XqHdVlgu.js +1 -0
  6. package/dist/renderer/assets/Dashboard-CZj-oANw.js +41 -0
  7. package/dist/renderer/assets/{EmptyState-C60g3RMj.js → EmptyState-EFMkOjZO.js} +1 -1
  8. package/dist/renderer/assets/HighlightedMarkdown-DyvF3DHK.js +12 -0
  9. package/dist/renderer/assets/Input-DxoWVc9d.js +1 -0
  10. package/dist/renderer/assets/{KnowledgeDetail-CC5C7JYe.js → KnowledgeDetail-CbUb2tG7.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-3rlvxgWf.js → KnowledgeHome-BWV1gz4f.js} +3 -3
  12. package/dist/renderer/assets/Logs-DqnanQTc.js +6 -0
  13. package/dist/renderer/assets/{Modal-BQUW9LSf.js → Modal-yY7TzR0x.js} +3 -3
  14. package/dist/renderer/assets/MonitorList-CIjGQkFs.js +1 -0
  15. package/dist/renderer/assets/Page-vwHNUdoj.js +1 -0
  16. package/dist/renderer/assets/{Select-CfRkos3z.js → Select-CmbK36pN.js} +2 -2
  17. package/dist/renderer/assets/ThreadChat-4OceMgM4.js +86 -0
  18. package/dist/renderer/assets/Workspace-DplPJOjJ.js +19 -0
  19. package/dist/renderer/assets/{arrow-left-D1DbXrLA.js → arrow-left-C8HfBt2K.js} +1 -1
  20. package/dist/renderer/assets/{book-open-BxC1z1C1.js → book-open-BCdZWncQ.js} +1 -1
  21. package/dist/renderer/assets/channels-hgO3cAIx.js +6 -0
  22. package/dist/renderer/assets/chevron-down-BPk77tqY.js +11 -0
  23. package/dist/renderer/assets/index-BIKL3fTP.css +11 -0
  24. package/dist/renderer/assets/index-BlA4xPdd.js +41 -0
  25. package/dist/renderer/assets/index-wYpjqVqC.js +167 -0
  26. package/dist/renderer/assets/knowledge-7pl06KuH.js +1 -0
  27. package/dist/renderer/assets/{pencil-X44UaxwR.js → pencil-CB1SWZ9A.js} +1 -1
  28. package/dist/renderer/assets/{play-ujo-De38.js → play-BvsoXUrM.js} +1 -1
  29. package/dist/renderer/assets/{plus-BHgVdG_A.js → plus-CokBDnaL.js} +1 -1
  30. package/dist/renderer/assets/{save-Cz6giKrE.js → save-D-w4kATO.js} +1 -1
  31. package/dist/renderer/assets/{search-DKWvTqi5.js → search-Cnsn6i4Q.js} +1 -1
  32. package/dist/renderer/assets/{shield-check-V82O8AC1.js → shield-check-Cjja2Gh6.js} +1 -1
  33. package/dist/renderer/assets/threads-BCm6j4eI.js +1 -0
  34. package/dist/renderer/assets/{trash-2-_Fgwk8mc.js → trash-2-CIxz9qYY.js} +1 -1
  35. package/dist/renderer/index.html +2 -2
  36. package/dist-electron/packages/contracts/src/automation.js +2 -0
  37. package/dist-electron/packages/contracts/src/channels.js +2 -0
  38. package/dist-electron/packages/contracts/src/knowledge.js +2 -0
  39. package/dist-electron/packages/contracts/src/local-core.js +15 -0
  40. package/dist-electron/packages/contracts/src/runtime.js +2 -0
  41. package/dist-electron/packages/contracts/src/scheduler.js +2 -0
  42. package/dist-electron/packages/contracts/src/threads.js +2 -0
  43. package/dist-electron/packages/core-sdk/src/automation.js +32 -0
  44. package/dist-electron/packages/core-sdk/src/channels.js +121 -0
  45. package/dist-electron/packages/core-sdk/src/client.js +455 -0
  46. package/dist-electron/packages/core-sdk/src/index.js +43 -607
  47. package/dist-electron/packages/core-sdk/src/knowledge.js +85 -0
  48. package/dist-electron/packages/core-sdk/src/request.js +22 -0
  49. package/dist-electron/packages/core-sdk/src/runtime.js +214 -0
  50. package/dist-electron/packages/core-sdk/src/scheduler.js +32 -0
  51. package/dist-electron/packages/core-sdk/src/threads.js +67 -0
  52. package/dist-electron/packages/plugin-sdk/src/agents.js +2 -0
  53. package/dist-electron/packages/plugin-sdk/src/automation.js +2 -0
  54. package/dist-electron/packages/plugin-sdk/src/channels.js +2 -0
  55. package/dist-electron/packages/plugin-sdk/src/index.js +21 -0
  56. package/dist-electron/packages/plugin-sdk/src/knowledge.js +2 -0
  57. package/dist-electron/packages/plugin-sdk/src/runtime.js +2 -0
  58. package/dist-electron/packages/plugin-sdk/src/scheduler.js +2 -0
  59. package/dist-electron/packages/plugin-sdk/src/threads.js +2 -0
  60. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +6 -9
  61. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +2 -2
  62. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +3 -3
  63. package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +5 -5
  64. package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +12 -12
  65. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +2 -2
  66. package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +11 -11
  67. package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +3 -3
  68. package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +7 -7
  69. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +2 -2
  70. package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +3 -3
  71. package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +3 -3
  72. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +2 -2
  73. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +4 -4
  74. package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +2 -2
  75. package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +2 -2
  76. package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +3 -3
  77. package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +1 -1
  78. package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +4 -4
  79. package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +33 -18
  80. package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -2
  81. package/dist-electron/services/local-ai-core/src/channel/lark/gateway-utils.js +91 -0
  82. package/dist-electron/services/local-ai-core/src/channel/lark/inbound-handler.js +180 -0
  83. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +52 -456
  84. package/dist-electron/services/local-ai-core/src/channel/lark/outbound-transport.js +124 -0
  85. package/dist-electron/services/local-ai-core/src/channel/shared/inbound-attachment-store.js +93 -0
  86. package/dist-electron/services/local-ai-core/src/channel/shared/inbound-authorization.js +54 -0
  87. package/dist-electron/services/local-ai-core/src/channel/weixin/config.js +2 -2
  88. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-media.js +21 -0
  89. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +167 -0
  90. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +98 -483
  91. package/dist-electron/services/local-ai-core/src/channel/weixin/runtime-state.js +156 -0
  92. package/dist-electron/services/local-ai-core/src/channel/weixin/text-utils.js +100 -0
  93. package/dist-electron/services/local-ai-core/src/cli/lac.js +6 -6
  94. package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +2 -2
  95. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +28 -70
  96. package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +33 -5
  97. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +2 -2
  98. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +2 -2
  99. package/dist-electron/services/local-ai-core/src/router/workspace-bridge-event-stream.js +29 -0
  100. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +5 -5
  101. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +47 -60
  102. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -5
  103. package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +6 -6
  104. package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +3 -3
  105. package/dist-electron/services/local-ai-core/src/runtime/external-service.js +3 -1
  106. package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +11 -2
  107. package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +12 -6
  108. package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +10 -2
  109. package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +18 -6
  110. package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +7 -2
  111. package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +28 -3
  112. package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +10 -2
  113. package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +18 -5
  114. package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +14 -2
  115. package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +18 -15
  116. package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +13 -2
  117. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +35 -8
  118. package/dist-electron/services/local-ai-core/src/runtime/request-validation.js +79 -0
  119. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +9 -9
  120. package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +10 -1
  121. package/dist-electron/services/local-ai-core/src/runtime/server.js +2 -1
  122. package/dist-electron/services/local-ai-core/src/runtime/workspace-project-registry.js +76 -0
  123. package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +8 -8
  124. package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +75 -0
  125. package/dist-electron/shared/desktop.js +1 -64
  126. package/dist-electron/src/components/chat/chat-controller-state.js +60 -0
  127. package/dist-electron/src/components/chat/chat-event-gate.js +146 -0
  128. package/dist-electron/src/components/chat/markdown-code-block.js +8 -0
  129. package/dist-electron/src/pages/Desktop/workspace-model.js +212 -0
  130. package/dist-electron/src/pages/Threads/thread-chat-model.js +50 -51
  131. package/dist-electron/tests/contracts/agent-message-policy.test.js +21 -0
  132. package/dist-electron/tests/contracts/architecture-docs.test.js +56 -33
  133. package/dist-electron/tests/contracts/chat-event-gate.test.js +114 -0
  134. package/dist-electron/tests/contracts/chat-markdown.test.js +16 -0
  135. package/dist-electron/tests/contracts/thread-task-state.test.js +32 -0
  136. package/dist-electron/tests/contracts/workspace-identity.test.js +23 -0
  137. package/dist-electron/tests/electron/core-client.test.js +284 -0
  138. package/dist-electron/tests/electron/plugin-kernel.test.js +54 -0
  139. package/dist-electron/tests/electron/renderer-packaging.test.js +9 -0
  140. package/dist-electron/tests/electron/workspace-task-store.test.js +31 -0
  141. package/dist-electron/tests/integration/channel-inbound-attachment-store.test.js +117 -0
  142. package/dist-electron/tests/integration/channel-inbound-authorization.test.js +80 -0
  143. package/dist-electron/tests/integration/lark-gateway-utils.test.js +13 -0
  144. package/dist-electron/tests/integration/lark-gateway.test.js +58 -4
  145. package/dist-electron/tests/integration/local-core-errors.test.js +41 -0
  146. package/dist-electron/tests/integration/weixin-gateway.test.js +36 -0
  147. package/dist-electron/tests/integration/weixin-runtime-state.test.js +32 -0
  148. package/dist-electron/tests/integration/weixin-text-utils.test.js +15 -0
  149. package/package.json +9 -4
  150. package/dist/renderer/assets/Chat-BJveQQKu.js +0 -2
  151. package/dist/renderer/assets/ChatMarkdown-Bqn2HJ6f.js +0 -45
  152. package/dist/renderer/assets/Config-CMtZDRAE.js +0 -21
  153. package/dist/renderer/assets/CronList-C-ILB_Ae.js +0 -1
  154. package/dist/renderer/assets/Dashboard-CsKBSQIe.js +0 -26
  155. package/dist/renderer/assets/Logs-CktemGN0.js +0 -1
  156. package/dist/renderer/assets/MonitorList-B0hxjuOZ.js +0 -1
  157. package/dist/renderer/assets/Page-DL_Nv2i5.js +0 -1
  158. package/dist/renderer/assets/ProjectDetail-Djr9-MyT.js +0 -11
  159. package/dist/renderer/assets/ProjectList-BKl4eW7q.js +0 -1
  160. package/dist/renderer/assets/SessionChat-Dxv8D5DW.js +0 -1
  161. package/dist/renderer/assets/SessionList-BJ2GjXvR.js +0 -1
  162. package/dist/renderer/assets/ThreadChat-CAsIl9qf.js +0 -28
  163. package/dist/renderer/assets/Workspace-CaUhiGBX.js +0 -19
  164. package/dist/renderer/assets/bot-Fw3IzhJA.js +0 -6
  165. package/dist/renderer/assets/check-5zCTgp3A.js +0 -6
  166. package/dist/renderer/assets/chevron-down-N3PxTNxH.js +0 -6
  167. package/dist/renderer/assets/circle-C_chUflo.js +0 -6
  168. package/dist/renderer/assets/desktop-DkQTZNhe.js +0 -1
  169. package/dist/renderer/assets/desktop-JQkhNOF4.js +0 -5
  170. package/dist/renderer/assets/funnel-B8ZAv6I7.js +0 -6
  171. package/dist/renderer/assets/heart-M9A3E4Ed.js +0 -6
  172. package/dist/renderer/assets/index-Bwv855ID.js +0 -41
  173. package/dist/renderer/assets/index-CjCuR6DI.css +0 -11
  174. package/dist/renderer/assets/index-DJ8w4e0l.js +0 -182
  175. package/dist/renderer/assets/projects-DB1FLZ3A.js +0 -1
  176. package/dist/renderer/assets/server-BsuHcYl4.js +0 -11
  177. package/dist/renderer/assets/session-utils-DnAWL3eo.js +0 -6
  178. package/dist/renderer/assets/sessions-DptxO_BE.js +0 -1
  179. package/dist/renderer/assets/user-DQKTaUcR.js +0 -6
  180. package/dist/renderer/assets/wifi-off-vjH-MD2w.js +0 -11
  181. package/dist-electron/src/api/client.js +0 -73
  182. package/dist-electron/src/api/sessions.js +0 -21
  183. package/dist-electron/src/lib/session-utils.js +0 -51
  184. package/dist-electron/src/pages/Projects/project-provider-config.js +0 -75
  185. package/dist-electron/tests/electron/project-provider-config.test.js +0 -71
@@ -3,26 +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
- const desktop_js_1 = require("../../../../../shared/desktop.js");
10
+ const node_stream_1 = require("node:stream");
13
11
  const local_core_errors_js_1 = require("../../kernel/local-core-errors.js");
14
12
  const content_js_1 = require("../shared/content.js");
15
13
  const file_utils_js_1 = require("../shared/file-utils.js");
14
+ const inbound_attachment_store_js_1 = require("../shared/inbound-attachment-store.js");
16
15
  const thread_routing_js_1 = require("../shared/thread-routing.js");
17
16
  const base_channel_gateway_js_1 = require("../shared/base-channel-gateway.js");
17
+ const inbound_authorization_js_1 = require("../shared/inbound-authorization.js");
18
18
  const channel_keys_js_1 = require("../shared/channel-keys.js");
19
19
  const config_js_1 = require("./config.js");
20
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; } });
21
26
  // ==================== Constants ====================
22
- const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
23
- const RETRY_DELAY_MS = 2_000;
24
- const BACKOFF_DELAY_MS = 30_000;
25
- const MAX_CONSECUTIVE_FAILURES = 3;
26
27
  const PROCESSED_MESSAGE_TTL_MS = 10 * 60 * 1000;
27
28
  const WEIXIN_TEXT_MESSAGE_MAX_BYTES = 900;
28
29
  const WEIXIN_CONTEXT_REPLY_MAX_BYTES = 3500;
@@ -31,124 +32,10 @@ const WEIXIN_RESERVED_TERMINAL_SENDS = 1;
31
32
  const WEIXIN_PROGRESS_SEND_BUDGET = WEIXIN_CONTEXT_SEND_LIMIT - WEIXIN_RESERVED_TERMINAL_SENDS;
32
33
  const RESPONSE_TIMEOUT_MS = 5 * 60 * 1000;
33
34
  const WEIXIN_MAX_UPLOAD_FILE_SIZE = 30 * 1024 * 1024;
34
- const ERROR_LOG_WINDOW_MS = 5 * 60 * 1000;
35
- // ==================== Utilities ====================
36
- function stripHtml(html) {
37
- let result = html;
38
- let prev;
39
- do {
40
- prev = result;
41
- result = result.replace(/<[^>]*>/g, '');
42
- } while (result !== prev);
43
- return result.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, ' ');
44
- }
45
- function formatError(err) {
46
- if (err instanceof Error) {
47
- const cause = err.cause;
48
- return cause !== undefined ? `${err.message}: ${String(cause)}` : err.message;
49
- }
50
- return String(err);
51
- }
52
- function sleep(ms, signal) {
53
- return new Promise((resolve, reject) => {
54
- const t = setTimeout(resolve, ms);
55
- const onAbort = () => { clearTimeout(t); reject(new Error('aborted')); };
56
- signal?.addEventListener('abort', onAbort, { once: true });
57
- });
58
- }
59
- function utf8ByteLength(value) {
60
- return Buffer.byteLength(value, 'utf-8');
61
- }
62
- function splitTextByUtf8Bytes(text, maxBytes) {
63
- const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
64
- if (!normalized)
65
- return [];
66
- if (utf8ByteLength(normalized) <= maxBytes)
67
- return [normalized];
68
- const chunks = [];
69
- let current = '';
70
- const pushCurrent = () => {
71
- const trimmed = current.trim();
72
- if (trimmed)
73
- chunks.push(trimmed);
74
- current = '';
75
- };
76
- const appendPart = (part) => {
77
- if (!part.trim())
78
- return;
79
- const separator = current ? '\n\n' : '';
80
- if (current && utf8ByteLength(`${current}${separator}${part}`) <= maxBytes) {
81
- current = `${current}${separator}${part}`;
82
- return;
83
- }
84
- if (current)
85
- pushCurrent();
86
- if (utf8ByteLength(part) <= maxBytes) {
87
- current = part;
88
- return;
89
- }
90
- let segment = '';
91
- for (const char of Array.from(part)) {
92
- if (segment && utf8ByteLength(`${segment}${char}`) > maxBytes) {
93
- chunks.push(segment);
94
- segment = '';
95
- }
96
- segment += char;
97
- }
98
- current = segment;
99
- };
100
- for (const part of normalized.split(/\n{2,}/)) {
101
- appendPart(part);
102
- }
103
- pushCurrent();
104
- return chunks;
105
- }
106
- function createWeixinAttachmentContentPart(att) {
107
- if (att.kind === 'image') {
108
- if (!att.data)
109
- return null;
110
- return {
111
- type: 'image',
112
- data: att.data,
113
- mimeType: att.mimeType,
114
- fileName: att.name,
115
- };
116
- }
117
- return {
118
- type: 'file',
119
- path: att.path,
120
- fileName: att.name,
121
- };
122
- }
123
- function truncateTextByUtf8Bytes(text, maxBytes) {
124
- const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
125
- if (utf8ByteLength(normalized) <= maxBytes)
126
- return normalized;
127
- const suffix = '\n\n(内容过长,已截断以保证微信送达)';
128
- const budget = Math.max(0, maxBytes - utf8ByteLength(suffix));
129
- let result = '';
130
- for (const char of Array.from(normalized)) {
131
- if (utf8ByteLength(`${result}${char}`) > budget)
132
- break;
133
- result += char;
134
- }
135
- return `${result.trim()}${suffix}`;
136
- }
137
- function renderBridgeContentForWeixin(event) {
138
- const toolCall = event.toolCall;
139
- if (!toolCall) {
140
- return String(event.content || '').trim();
141
- }
142
- const name = String(toolCall.name || '').trim() || 'Tool update';
143
- const status = String(toolCall.status || '').trim();
144
- if (name === 'Tool update' && status === 'completed')
145
- return '';
146
- return [name, status].filter(Boolean).join(' - ');
147
- }
148
35
  // ==================== Gateway Class ====================
149
36
  class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGateway {
150
37
  processedInboundMessages = new Map();
151
- pollErrorLogWindows = new Map();
38
+ inboundAttachmentStore = new inbound_attachment_store_js_1.FileSystemInboundAttachmentStore();
152
39
  platform = 'weixin';
153
40
  constructor(options) {
154
41
  super(options);
@@ -168,7 +55,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
168
55
  return (0, config_js_1.collectWeixinWorkspaceBindings)(config);
169
56
  }
170
57
  buildStatusObject(state, resolved) {
171
- this.options.store.expirePendingPairings();
172
58
  const platformKey = state.platformKey || (0, channel_keys_js_1.channelPlatformKey)('weixin', resolved.instanceId);
173
59
  const pairings = this.options.store.listPendingPairings(state.workspaceId)
174
60
  .filter((row) => row.platform === platformKey && row.expires_at >= new Date().toISOString());
@@ -325,19 +211,19 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
325
211
  const bridgeThreadId = route.threadId || binding.thread_id;
326
212
  if (this.mutedThreadBridgeCounts.has(bridgeThreadId))
327
213
  return;
328
- const turn = this.getOrCreateTurnState(sessionKey);
214
+ const turn = (0, runtime_state_js_1.getOrCreateWeixinTurnState)(this.outboundTurns, sessionKey);
329
215
  if (event.replyCtx) {
330
216
  // WeChat doesn't support reply context; ignore
331
217
  }
332
- this.consumeBridgeEvent(turn, event);
218
+ (0, runtime_state_js_1.consumeWeixinBridgeEvent)(turn, event);
333
219
  if (event.type !== 'reply' && event.type !== 'buttons' && event.type !== 'status')
334
220
  return;
335
- const rendered = this.renderTurnText(turn);
221
+ const rendered = (0, runtime_state_js_1.renderWeixinTurnText)(turn);
336
222
  if (!rendered)
337
223
  return;
338
224
  if (rendered === turn.lastSentText)
339
225
  return;
340
- const terminalMessage = this.isTerminalBridgeMessage(event, rendered);
226
+ const terminalMessage = (0, runtime_state_js_1.isTerminalWeixinBridgeMessage)(event, rendered);
341
227
  if (binding.last_platform_message_id && !terminalMessage && turn.sentCount >= WEIXIN_PROGRESS_SEND_BUDGET) {
342
228
  turn.foldedProgressCount += 1;
343
229
  this.options.log?.(`localcore-weixin folded progress for sessionKey=${sessionKey}: sent=${turn.sentCount} folded=${turn.foldedProgressCount}`);
@@ -360,7 +246,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
360
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'}`);
361
247
  }
362
248
  catch (error) {
363
- this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${formatError(error)}`);
249
+ this.options.log?.(`localcore-weixin send failed for sessionKey=${sessionKey}: ${(0, text_utils_js_1.formatWeixinError)(error)}`);
364
250
  }
365
251
  })
366
252
  .finally(() => {
@@ -385,7 +271,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
385
271
  const previousRoute = this.threadRouting.get(input.sessionKey);
386
272
  this.threadRouting.set(input.sessionKey, route);
387
273
  if (!this.outboundTurns.has(input.sessionKey)) {
388
- this.createTurnState(input.sessionKey);
274
+ this.outboundTurns.set(input.sessionKey, (0, runtime_state_js_1.createWeixinTurnState)(input.sessionKey));
389
275
  }
390
276
  return () => {
391
277
  if (previousRoute) {
@@ -488,52 +374,32 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
488
374
  messageId: msg.messageId,
489
375
  },
490
376
  });
491
- this.options.store.expirePendingPairings();
492
377
  const binding = await this.getBinding(msg.workspaceId, instanceId);
493
- let authorized = this.options.store.getAuthorizedUser(msg.workspaceId, msg.platformUserId, platformKey);
494
- if (!authorized) {
495
- if (binding.allowFrom === '*') {
496
- const authorizedAt = new Date().toISOString();
497
- this.options.store.createAuthorizedUser({
498
- id: `wx-user-${(0, node_crypto_2.randomUUID)()}`,
499
- workspace_id: msg.workspaceId,
500
- platform: platformKey,
501
- platform_user_id: msg.platformUserId,
502
- chat_id: msg.chatId,
503
- display_name: msg.displayName,
504
- thread_id: null,
505
- authorized_at: authorizedAt,
506
- });
507
- authorized = this.options.store.getAuthorizedUser(msg.workspaceId, msg.platformUserId, platformKey);
508
- this.options.log?.(`localcore-weixin auto-approved user for ${msg.workspaceId}: ${msg.platformUserId}`);
509
- this.notifyRuntimeStateChanged();
510
- }
511
- }
512
- if (!authorized) {
513
- 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');
514
- let pairingCode = existingPending?.code || '';
515
- if (!existingPending) {
516
- const now = new Date();
517
- pairingCode = String((0, node_crypto_2.randomInt)(100000, 1000000));
518
- this.options.store.createPairingRequest({
519
- code: pairingCode,
520
- workspace_id: msg.workspaceId,
521
- platform: platformKey,
522
- platform_user_id: msg.platformUserId,
523
- chat_id: msg.chatId,
524
- display_name: msg.displayName,
525
- requested_at: now.toISOString(),
526
- expires_at: new Date(now.getTime() + PAIRING_EXPIRY_MS).toISOString(),
527
- status: 'pending',
528
- });
529
- this.notifyRuntimeStateChanged();
530
- }
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') {
531
393
  const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(msg.workspaceId, instanceId));
532
394
  if (state?.connected) {
533
- 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);
534
396
  }
535
397
  return;
536
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;
537
403
  const router = this.options.getWorkspaceRouter();
538
404
  let { threadId } = await (0, thread_routing_js_1.resolveChannelThreadRoute)({
539
405
  store: this.options.store,
@@ -584,10 +450,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
584
450
  return;
585
451
  }
586
452
  this.options.store.updatePlatformThreadMessageId(msg.workspaceId, msg.chatId, msg.platformUserId, msg.contextToken || msg.messageId, platformKey);
587
- const wrappedText = slashCommand
588
- ? msg.text
589
- : (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(msg.text);
590
- 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));
591
454
  return;
592
455
  }
593
456
  async resolveDefaultAgentType(workspaceId, threadId) {
@@ -634,7 +497,18 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
634
497
  status.connectedAt = new Date().toISOString();
635
498
  this.clearRuntimeError(status);
636
499
  // Start long-poll loop in background
637
- this.runMonitorLoop(binding, abortController.signal).catch((err) => {
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) => {
638
512
  if (!abortController.signal.aborted) {
639
513
  status.status = 'error';
640
514
  status.connected = false;
@@ -661,127 +535,13 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
661
535
  return 'expired';
662
536
  return 'wait';
663
537
  }
664
- async runMonitorLoop(binding, signal) {
665
- let buf = (0, config_js_1.loadWeixinBuf)(binding);
666
- let consecutiveFailures = 0;
667
- while (!signal.aborted) {
668
- try {
669
- const resp = await (0, transport_js_1.getWeixinUpdates)(binding, buf, signal);
670
- const isApiError = (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
671
- if (isApiError) {
672
- consecutiveFailures++;
673
- const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
674
- const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
675
- if (resp.errcode === -14 || resp.ret === -14) {
676
- if (state) {
677
- state.status = 'error';
678
- state.connected = false;
679
- state.consecutiveFailures = consecutiveFailures;
680
- state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
681
- this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(new local_core_errors_js_1.LocalCoreError('channel_session_expired', 'WeChat login expired.')));
682
- this.notifyRuntimeStateChanged();
683
- }
684
- }
685
- 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})`);
686
- if (state && resp.errcode !== -14 && resp.ret !== -14) {
687
- state.consecutiveFailures = consecutiveFailures;
688
- state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
689
- }
690
- await sleep(retryDelayMs, signal);
691
- if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
692
- consecutiveFailures = MAX_CONSECUTIVE_FAILURES;
693
- }
694
- continue;
695
- }
696
- consecutiveFailures = 0;
697
- const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
698
- if (state) {
699
- this.clearRuntimeError(state);
700
- state.status = 'running';
701
- state.connected = true;
702
- this.notifyRuntimeStateChanged();
703
- }
704
- if (resp.get_updates_buf) {
705
- buf = resp.get_updates_buf;
706
- (0, config_js_1.saveWeixinBuf)(binding, buf);
707
- }
708
- for (const msg of resp.msgs ?? []) {
709
- const items = msg.item_list ?? [];
710
- const textItem = items.find((i) => i.type === transport_js_1.TEXT_ITEM_TYPE);
711
- const voiceTextItems = items.filter((i) => i.type === transport_js_1.VOICE_ITEM_TYPE && i.voice_item?.text);
712
- const mediaItems = items.filter((i) => i.type === transport_js_1.IMAGE_ITEM_TYPE || i.type === transport_js_1.FILE_ITEM_TYPE);
713
- if (!textItem && voiceTextItems.length === 0 && mediaItems.length === 0)
714
- continue;
715
- const conversationId = msg.from_user_id ?? '';
716
- const text = [textItem?.text_item?.text?.trim(), ...voiceTextItems.map((i) => i.voice_item?.text?.trim())]
717
- .filter((part) => Boolean(part))
718
- .join('\n\n');
719
- const msgId = msg.msg_id ?? String(Date.now());
720
- // Handle attachments
721
- let attachmentText = '';
722
- const attachmentParts = [];
723
- if (mediaItems.length > 0) {
724
- const uploadsDir = node_path_1.default.join(binding.stateDir, 'weixin-uploads');
725
- for (const [idx, item] of mediaItems.entries()) {
726
- try {
727
- const att = await this.downloadMediaItem(item, msgId, idx, uploadsDir, binding);
728
- if (att) {
729
- attachmentText += attachmentText ? '\n' : '';
730
- attachmentText += att.kind === 'image' ? `[Image: ${att.path}]` : `[File "${att.name}": ${att.path}]`;
731
- const part = createWeixinAttachmentContentPart(att);
732
- if (part)
733
- attachmentParts.push(part);
734
- }
735
- }
736
- catch (dlErr) {
737
- this.options.log?.(`localcore-weixin attachment download failed (${conversationId}#${idx}): ${formatError(dlErr)}`);
738
- }
739
- }
740
- }
741
- const fullText = [text, attachmentText].filter(Boolean).join('\n\n');
742
- if (!fullText)
743
- continue;
744
- await this.handleInboundMessage({
745
- workspaceId: binding.workspaceId,
746
- instanceId: binding.instanceId,
747
- platformKey: binding.platformKey,
748
- platformUserId: conversationId,
749
- chatId: conversationId,
750
- displayName: conversationId.slice(-6),
751
- text: fullText,
752
- messageId: msgId,
753
- contextToken: msg.context_token,
754
- contentParts: [
755
- ...(text ? [{ type: 'text', text }] : []),
756
- ...attachmentParts,
757
- ],
758
- });
759
- }
760
- }
761
- catch (err) {
762
- if (signal.aborted)
763
- return;
764
- consecutiveFailures++;
765
- const retryDelayMs = this.computeRetryDelay(consecutiveFailures);
766
- const state = this.runtime.get((0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId));
767
- if (state) {
768
- state.consecutiveFailures = consecutiveFailures;
769
- state.nextRetryAt = new Date(Date.now() + retryDelayMs).toISOString();
770
- this.setRuntimeError(state, (0, local_core_errors_js_1.toLocalCoreErrorInfo)(err));
771
- this.notifyRuntimeStateChanged();
772
- }
773
- this.logPollError(binding, formatError(err), `localcore-weixin getUpdates error for ${binding.workspaceId} (${consecutiveFailures}): ${formatError(err)}`);
774
- await sleep(retryDelayMs, signal);
775
- }
776
- }
777
- }
778
538
  // ==================== Private: HTTP API ====================
779
539
  async sendTextMessage(state, toUserId, text, contextToken, options = {}) {
780
540
  const binding = await this.getBinding(state.workspaceId);
781
- const stripped = stripHtml(text);
541
+ const stripped = (0, text_utils_js_1.stripWeixinHtml)(text);
782
542
  const chunks = contextToken
783
- ? [truncateTextByUtf8Bytes(stripped, WEIXIN_CONTEXT_REPLY_MAX_BYTES)].filter(Boolean)
784
- : 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);
785
545
  for (const [index, chunk] of chunks.entries()) {
786
546
  const finalChunk = options.final && index === chunks.length - 1;
787
547
  const resp = await (0, transport_js_1.sendWeixinTextMessageChunk)(binding, toUserId, chunk, contextToken, {
@@ -789,7 +549,7 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
789
549
  final: finalChunk,
790
550
  });
791
551
  if ((0, transport_js_1.isWeixinApiError)(resp)) {
792
- 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}`);
793
553
  }
794
554
  }
795
555
  this.options.log?.(`localcore-weixin sent message to ${toUserId} for workspace ${state.workspaceId}${chunks.length > 1 ? ` chunks=${chunks.length}` : ''}`);
@@ -887,37 +647,58 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
887
647
  aesKey = decoded.length === 16 ? decoded : decoded.length === 32 ? Buffer.from(decoded.toString('ascii'), 'hex') : undefined;
888
648
  }
889
649
  const cdnUrl = `${binding.cdnBaseUrl}/download?encrypted_query_param=${encodeURIComponent(encryptQueryParam)}`;
890
- const resp = await fetch(cdnUrl, { signal: AbortSignal.timeout(30_000) });
891
- if (!resp.ok)
892
- throw new Error(`CDN HTTP ${resp.status}`);
893
- const rawBuf = Buffer.from(await resp.arrayBuffer());
894
- if (rawBuf.length === 0)
895
- throw new Error('CDN returned empty data');
896
- let resultBuf;
897
- if (aesKey) {
898
- const decipher = node_crypto_1.default.createDecipheriv('aes-128-ecb', aesKey, null);
899
- decipher.setAutoPadding(true);
900
- resultBuf = Buffer.concat([decipher.update(rawBuf), decipher.final()]);
901
- }
902
- else {
903
- resultBuf = rawBuf;
904
- }
905
- const { ext, kind } = this.sniffExtAndKind(resultBuf);
906
650
  const declaredName = String(itemData?.file_name ?? (item.type === transport_js_1.IMAGE_ITEM_TYPE ? 'image' : 'file'));
907
- const safeName = declaredName.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 64);
908
- const safeMsgId = msgId.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 48);
909
- const fileName = `${safeMsgId}-${idx}-${safeName}${ext}`;
910
- const filePath = node_path_1.default.join(uploadsDir, fileName);
911
- node_fs_1.default.mkdirSync(uploadsDir, { recursive: true });
912
- node_fs_1.default.writeFileSync(filePath, resultBuf);
651
+ const stored = await this.inboundAttachmentStore.save({
652
+ directory: uploadsDir,
653
+ storedFileName: `${msgId}-${idx}-${declaredName}`,
654
+ displayFileName: declaredName,
655
+ maxBytes: WEIXIN_MAX_UPLOAD_FILE_SIZE,
656
+ includeBase64: ({ prefix }) => this.sniffExtAndKind(prefix).kind === 'image',
657
+ finalizeStoredFileName: ({ storedFileName, prefix }) => `${storedFileName}${this.sniffExtAndKind(prefix).ext}`,
658
+ source: {
659
+ open: async () => {
660
+ const resp = await fetch(cdnUrl, { signal: AbortSignal.timeout(30_000) });
661
+ if (!resp.ok)
662
+ throw new Error(`CDN HTTP ${resp.status}`);
663
+ if (!resp.body)
664
+ throw new Error('CDN returned no response body');
665
+ const rawStream = node_stream_1.Readable.fromWeb(resp.body);
666
+ if (!aesKey) {
667
+ return { stream: rawStream };
668
+ }
669
+ const decipher = node_crypto_1.default.createDecipheriv('aes-128-ecb', aesKey, null);
670
+ decipher.setAutoPadding(true);
671
+ return { stream: rawStream.pipe(decipher) };
672
+ },
673
+ },
674
+ });
675
+ const { ext, kind } = this.sniffExtAndKind(stored.prefix);
676
+ const uri = await this.resolveInboundAttachmentUri(binding, stored.path);
913
677
  return {
914
- path: filePath,
678
+ path: stored.path,
915
679
  kind,
916
680
  name: declaredName,
917
- data: kind === 'image' ? resultBuf.toString('base64') : undefined,
681
+ data: stored.data,
918
682
  mimeType: kind === 'image' ? this.mimeTypeForImageExt(ext) : undefined,
683
+ ...(uri ? { uri } : {}),
919
684
  };
920
685
  }
686
+ async resolveInboundAttachmentUri(binding, filePath) {
687
+ const sandbox = binding.project?.agent?.options?.sandbox;
688
+ if (!sandbox?.enabled) {
689
+ return (0, inbound_attachment_store_js_1.resolveInboundAttachmentUri)({ filePath });
690
+ }
691
+ const workspace = await this.options.getWorkspaceRouter().getWorkspaceRegistryEntry(binding.workspaceId).catch(() => undefined);
692
+ if (!workspace) {
693
+ return undefined;
694
+ }
695
+ return (0, inbound_attachment_store_js_1.resolveInboundAttachmentUri)({
696
+ filePath,
697
+ workspacePath: workspace.path,
698
+ sandboxEnabled: true,
699
+ sandboxWorkspacePath: sandbox.workspace_mount_path,
700
+ });
701
+ }
921
702
  mimeTypeForImageExt(ext) {
922
703
  if (ext === '.jpg')
923
704
  return 'image/jpeg';
@@ -927,149 +708,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
927
708
  return 'image/gif';
928
709
  return 'application/octet-stream';
929
710
  }
930
- // ==================== Private: Turn State ====================
931
- createTurnState(sessionKey) {
932
- const turn = {
933
- sessionKey,
934
- sentCount: 0,
935
- foldedProgressCount: 0,
936
- awaitingPermission: false,
937
- processing: false,
938
- previewText: '',
939
- finalText: '',
940
- thinkingSteps: [],
941
- pendingThoughtText: undefined,
942
- statusLines: [],
943
- buttonRows: [],
944
- lastSentAt: 0,
945
- lastSentText: '',
946
- };
947
- this.outboundTurns.set(sessionKey, turn);
948
- return turn;
949
- }
950
- getOrCreateTurnState(sessionKey) {
951
- return this.outboundTurns.get(sessionKey) || this.createTurnState(sessionKey);
952
- }
953
- consumeBridgeEvent(turn, event) {
954
- const content = renderBridgeContentForWeixin(event);
955
- const bridgeKind = this.resolveBridgeEventKind(event);
956
- if (event.type === 'typing_start') {
957
- turn.processing = true;
958
- turn.previewText = '';
959
- turn.finalText = '';
960
- turn.thinkingSteps = [];
961
- turn.pendingThoughtText = undefined;
962
- turn.statusLines = [];
963
- turn.buttonRows = [];
964
- return;
965
- }
966
- if (event.type === 'typing_stop') {
967
- turn.processing = false;
968
- return;
969
- }
970
- if (event.type === 'preview_start' || event.type === 'update_message') {
971
- if (bridgeKind === 'thought') {
972
- turn.pendingThoughtText = content;
973
- return;
974
- }
975
- turn.previewText = content;
976
- return;
977
- }
978
- if (bridgeKind !== 'thought') {
979
- this.flushPendingThought(turn);
980
- }
981
- if (event.type === 'status') {
982
- if (content) {
983
- this.pushUnique(turn.statusLines, content);
984
- turn.finalText = content;
985
- turn.previewText = content;
986
- }
987
- return;
988
- }
989
- if (event.type === 'buttons') {
990
- turn.awaitingPermission = true;
991
- turn.buttonRows = Array.isArray(event.buttonRows)
992
- ? event.buttonRows
993
- .map((row) => Array.isArray(row)
994
- ? row.filter((b) => Boolean(b?.text && b?.data))
995
- .map((b) => ({ text: b.text, data: b.data }))
996
- : [])
997
- .filter((row) => row.length > 0)
998
- : [];
999
- return;
1000
- }
1001
- if (!content)
1002
- return;
1003
- if (bridgeKind === 'thought' || bridgeKind === 'plan') {
1004
- this.pushUnique(turn.thinkingSteps, content);
1005
- return;
1006
- }
1007
- if (bridgeKind === 'tool' || bridgeKind === 'status') {
1008
- this.pushUnique(turn.statusLines, content);
1009
- return;
1010
- }
1011
- turn.finalText = content;
1012
- turn.previewText = content;
1013
- }
1014
- renderTurnText(turn) {
1015
- const sections = [];
1016
- if (turn.thinkingSteps.length > 0) {
1017
- sections.push(`**中间过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
1018
- }
1019
- if (turn.finalText) {
1020
- sections.push(turn.finalText);
1021
- }
1022
- else if (turn.previewText) {
1023
- sections.push(turn.previewText);
1024
- }
1025
- else if (turn.statusLines.length > 0) {
1026
- sections.push(`**处理中**\n${turn.statusLines.slice(-3).map((l) => `• ${l.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
1027
- }
1028
- else if (turn.processing) {
1029
- sections.push('**处理中**\n正在思考...');
1030
- }
1031
- if (turn.awaitingPermission) {
1032
- sections.push('\n回复:`allow` / `allow all` / `deny`');
1033
- }
1034
- return sections.join('\n\n').trim();
1035
- }
1036
- flushPendingThought(turn) {
1037
- const text = String(turn.pendingThoughtText || '').trim();
1038
- if (!text)
1039
- return;
1040
- this.pushUnique(turn.thinkingSteps, text);
1041
- turn.pendingThoughtText = undefined;
1042
- }
1043
- isTerminalBridgeMessage(event, rendered) {
1044
- if (event.type === 'buttons')
1045
- return true;
1046
- if (event.type !== 'reply')
1047
- return false;
1048
- const bridgeKind = this.resolveBridgeEventKind(event);
1049
- if (bridgeKind === 'tool' || bridgeKind === 'thought' || bridgeKind === 'plan' || bridgeKind === 'status')
1050
- return false;
1051
- const normalized = rendered.trim();
1052
- if (!normalized)
1053
- return false;
1054
- return true;
1055
- }
1056
- resolveBridgeEventKind(event) {
1057
- if (event.bridgeKind) {
1058
- return event.bridgeKind;
1059
- }
1060
- return event.type === 'status' ? 'status' : 'assistant';
1061
- }
1062
- // ==================== Private: Helpers ====================
1063
- pushUnique(target, value) {
1064
- const normalized = value.trim();
1065
- if (!normalized)
1066
- return;
1067
- if (target[target.length - 1] === normalized)
1068
- return;
1069
- target.push(normalized);
1070
- if (target.length > 8)
1071
- target.splice(0, target.length - 8);
1072
- }
1073
711
  isDuplicateInboundMessage(input) {
1074
712
  const messageKey = input.contextToken || input.messageId;
1075
713
  if (!messageKey)
@@ -1104,29 +742,6 @@ class LocalCoreWeixinGateway extends base_channel_gateway_js_1.BaseChannelGatewa
1104
742
  return null;
1105
743
  return { name: name.trim().toLowerCase(), args };
1106
744
  }
1107
- computeRetryDelay(failures) {
1108
- if (failures <= 1)
1109
- return RETRY_DELAY_MS;
1110
- if (failures === 2)
1111
- return 5_000;
1112
- if (failures === 3)
1113
- return 15_000;
1114
- if (failures === 4)
1115
- return 30_000;
1116
- return 60_000;
1117
- }
1118
- logPollError(binding, errorKey, message) {
1119
- const key = (0, channel_keys_js_1.runtimeKey)(binding.workspaceId, binding.instanceId);
1120
- const current = this.pollErrorLogWindows.get(key);
1121
- const now = Date.now();
1122
- if (!current || current.errorKey !== errorKey || now - current.at >= ERROR_LOG_WINDOW_MS) {
1123
- this.pollErrorLogWindows.set(key, { at: now, count: 1, errorKey });
1124
- this.options.log?.(message);
1125
- return;
1126
- }
1127
- current.count += 1;
1128
- this.pollErrorLogWindows.set(key, current);
1129
- }
1130
745
  }
1131
746
  exports.LocalCoreWeixinGateway = LocalCoreWeixinGateway;
1132
747
  function getWeixinInstanceId(platform) {