@tencent-weixin/openclaw-weixin 2.4.1 → 2.4.2

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 (41) hide show
  1. package/dist/index.js +0 -4
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/api/api.js +1 -2
  4. package/dist/src/api/api.js.map +1 -1
  5. package/dist/src/auth/accounts.js.map +1 -1
  6. package/dist/src/channel.js +11 -0
  7. package/dist/src/channel.js.map +1 -1
  8. package/dist/src/media/voice-outbound.js +177 -0
  9. package/dist/src/media/voice-outbound.js.map +1 -0
  10. package/dist/src/messaging/abort-fence.js +70 -0
  11. package/dist/src/messaging/abort-fence.js.map +1 -0
  12. package/dist/src/messaging/buttons.js +117 -0
  13. package/dist/src/messaging/buttons.js.map +1 -0
  14. package/dist/src/messaging/lane-key.js +66 -0
  15. package/dist/src/messaging/lane-key.js.map +1 -0
  16. package/dist/src/messaging/merged-record.js +149 -0
  17. package/dist/src/messaging/merged-record.js.map +1 -0
  18. package/dist/src/messaging/model-buttons.js +182 -0
  19. package/dist/src/messaging/model-buttons.js.map +1 -0
  20. package/dist/src/messaging/model-callback-handler.js +133 -0
  21. package/dist/src/messaging/model-callback-handler.js.map +1 -0
  22. package/dist/src/monitor/lane-scheduler.js +46 -0
  23. package/dist/src/monitor/lane-scheduler.js.map +1 -0
  24. package/dist/src/monitor/monitor.js +5 -12
  25. package/dist/src/monitor/monitor.js.map +1 -1
  26. package/dist/src/streaming/stream-pipeline.js +431 -0
  27. package/dist/src/streaming/stream-pipeline.js.map +1 -0
  28. package/dist/src/streaming/stream-session.js +260 -0
  29. package/dist/src/streaming/stream-session.js.map +1 -0
  30. package/dist/src/streaming/stream.js +239 -0
  31. package/dist/src/streaming/stream.js.map +1 -0
  32. package/dist/src/util/markdown-fences.js +54 -0
  33. package/dist/src/util/markdown-fences.js.map +1 -0
  34. package/index.ts +0 -5
  35. package/openclaw.plugin.json +1 -1
  36. package/package.json +1 -1
  37. package/src/api/api.ts +2 -3
  38. package/src/auth/accounts.ts +0 -1
  39. package/src/channel.ts +13 -1
  40. package/src/monitor/monitor.ts +11 -10
  41. package/src/runtime.ts +0 -70
@@ -0,0 +1,66 @@
1
+ import { isAbortRequestText } from "openclaw/plugin-sdk/reply-runtime";
2
+ import { MessageItemType } from "../api/types.js";
3
+ /**
4
+ * Lane key for Weixin message scheduling.
5
+ *
6
+ * Mirrors the Telegram channel's `getTelegramSequentialKey` (see
7
+ * extensions/telegram/src/sequential-key.ts in the openclaw main repo).
8
+ *
9
+ * Design:
10
+ * - Different users get different lane keys -> processed concurrently.
11
+ * - Same user, regular messages -> share `wx:<account>:<user>` lane,
12
+ * processed strictly in arrival order so AI replies, typing state and
13
+ * inbound session writes do not interleave.
14
+ * - Abort requests (e.g. "stop", "中止") are routed to a separate
15
+ * `wx:<account>:<user>:control` lane so they can run immediately even when
16
+ * the main lane is occupied by a long-running tool turn. The actual
17
+ * "stopping" is implemented via the abort-fence mechanism (see
18
+ * `abort-fence.ts`).
19
+ */
20
+ function extractFirstTextBody(itemList) {
21
+ if (!itemList?.length)
22
+ return "";
23
+ for (const item of itemList) {
24
+ if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
25
+ return String(item.text_item.text);
26
+ }
27
+ }
28
+ return "";
29
+ }
30
+ /** Extract text body suitable for abort detection from an inbound message. */
31
+ export function extractTextBodyForLane(msg) {
32
+ return extractFirstTextBody(msg.item_list);
33
+ }
34
+ /** Returns true when the message text is an abort intent (stop / 中止 / ...). */
35
+ export function isWeixinAbortMessage(msg) {
36
+ const text = extractTextBodyForLane(msg);
37
+ if (!text)
38
+ return false;
39
+ return isAbortRequestText(text);
40
+ }
41
+ /**
42
+ * Compute the lane key used by the per-key scheduler. Abort requests get the
43
+ * `:control` suffix to bypass the main lane.
44
+ *
45
+ * Note: the fallback `unknown` user happens for malformed payloads. Keep them
46
+ * on a shared lane so they cannot fan out and exhaust resources.
47
+ */
48
+ export function getWeixinLaneKey(ctx) {
49
+ const userId = ctx.msg.from_user_id?.trim() || "unknown";
50
+ const baseKey = `wx:${ctx.accountId}:${userId}`;
51
+ if (isWeixinAbortMessage(ctx.msg)) {
52
+ return `${baseKey}:control`;
53
+ }
54
+ return baseKey;
55
+ }
56
+ /**
57
+ * Per-user fence key shared by all dispatches that should be aborted as a
58
+ * group when the user sends an abort. This is intentionally the *base* key
59
+ * (without `:control`) so that an abort dispatched on the control lane bumps
60
+ * the same generation that the long-running main-lane dispatch is checking.
61
+ */
62
+ export function getWeixinFenceKey(ctx) {
63
+ const userId = ctx.msg.from_user_id?.trim() || "unknown";
64
+ return `wx:${ctx.accountId}:${userId}`;
65
+ }
66
+ //# sourceMappingURL=lane-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lane-key.js","sourceRoot":"","sources":["../../../src/messaging/lane-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD;;;;;;;;;;;;;;;;GAgBG;AAEH,SAAS,oBAAoB,CAAC,QAAwB;IACpD,IAAI,CAAC,QAAQ,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;YACvE,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,sBAAsB,CAAC,GAAkB;IACvD,OAAO,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,GAAkB;IACrD,MAAM,IAAI,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAyB;IACxD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IACzD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;IAChD,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,GAAG,OAAO,UAAU,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAyB;IACzD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IACzD,OAAO,MAAM,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;AACzC,CAAC"}
@@ -0,0 +1,149 @@
1
+ import { MessageItemType } from "../api/types.js";
2
+ import { downloadMediaFromItem } from "../media/media-download.js";
3
+ import { logger } from "../util/logger.js";
4
+ const MAX_DEPTH = 5;
5
+ function formatTimestamp(ms) {
6
+ if (!ms)
7
+ return "";
8
+ const d = new Date(ms);
9
+ const pad = (n) => String(n).padStart(2, "0");
10
+ const date = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
11
+ const time = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
12
+ const tz = new Intl.DateTimeFormat("en", { timeZoneName: "short" })
13
+ .formatToParts(d)
14
+ .find((p) => p.type === "timeZoneName")?.value ?? "";
15
+ return `${date} ${time}${tz ? ` ${tz}` : ""}`;
16
+ }
17
+ function formatSubItemContent(item) {
18
+ switch (item.type) {
19
+ case MessageItemType.TEXT:
20
+ return item.text_item?.text ?? "";
21
+ case MessageItemType.VOICE:
22
+ if (item.voice_item?.text)
23
+ return item.voice_item.text;
24
+ return undefined;
25
+ case MessageItemType.UNSUPPORTED:
26
+ return item.unsupported_item?.text || "[不支持的消息类型]";
27
+ default:
28
+ return undefined;
29
+ }
30
+ }
31
+ async function formatMediaContent(item, deps) {
32
+ const downloaded = await downloadMediaFromItem(item, {
33
+ cdnBaseUrl: deps.cdnBaseUrl,
34
+ saveMedia: deps.saveMedia,
35
+ log: deps.log,
36
+ errLog: deps.errLog,
37
+ label: "merged-record",
38
+ });
39
+ const filePath = downloaded.decryptedPicPath ??
40
+ downloaded.decryptedVideoPath ??
41
+ downloaded.decryptedFilePath ??
42
+ downloaded.decryptedVoicePath;
43
+ let text;
44
+ let mediaType;
45
+ switch (item.type) {
46
+ case MessageItemType.IMAGE:
47
+ text = filePath ? `![图片](${filePath})` : "[图片]";
48
+ mediaType = "image/*";
49
+ break;
50
+ case MessageItemType.VIDEO:
51
+ text = filePath ? `[视频](${filePath})` : "[视频]";
52
+ mediaType = "video/mp4";
53
+ break;
54
+ case MessageItemType.FILE: {
55
+ const name = item.file_item?.file_name;
56
+ if (name && filePath)
57
+ text = `[${name}](${filePath})`;
58
+ else if (name)
59
+ text = `[文件: ${name}]`;
60
+ else if (filePath)
61
+ text = `[文件](${filePath})`;
62
+ else
63
+ text = "[文件]";
64
+ mediaType = downloaded.fileMediaType ?? "application/octet-stream";
65
+ break;
66
+ }
67
+ case MessageItemType.VOICE:
68
+ text = filePath ? `[语音](${filePath})` : "[语音]";
69
+ mediaType = downloaded.voiceMediaType ?? "audio/wav";
70
+ break;
71
+ default:
72
+ text = "[不支持的消息]";
73
+ break;
74
+ }
75
+ return { text, mediaPath: filePath, mediaType: filePath ? mediaType : undefined };
76
+ }
77
+ /**
78
+ * Format a MergedRecordItem into a readable text body with nested structure.
79
+ *
80
+ * Each nesting level is represented by `│ ` prefixes. Media sub-items are
81
+ * downloaded via CDN; the body contains text placeholders (e.g. `[图片]`)
82
+ * while actual file paths are returned in `mediaPaths` / `mediaTypes`
83
+ * for populating `ctx.MediaPaths` / `ctx.MediaTypes`.
84
+ */
85
+ export async function formatMergedRecordBody(record, deps, depth = 0) {
86
+ const title = record.title ?? "聊天记录";
87
+ const lines = [`[合并转发: ${title}]`];
88
+ const mediaPaths = [];
89
+ const mediaTypes = [];
90
+ if (depth >= MAX_DEPTH) {
91
+ lines.push("│ [嵌套层级过深,省略]");
92
+ return { body: lines.join("\n"), mediaPaths, mediaTypes };
93
+ }
94
+ for (const sub of record.sub_item_list ?? []) {
95
+ const nick = sub.from_nick_name ?? "未知";
96
+ const item = sub.item;
97
+ if (!item)
98
+ continue;
99
+ const ts = formatTimestamp(item.create_time_ms);
100
+ const header = ts ? `${nick} (${ts}):` : `${nick}:`;
101
+ if (item.type === MessageItemType.MERGED_RECORD && item.merged_record) {
102
+ lines.push(`│ ${header}`);
103
+ const nested = await formatMergedRecordBody(item.merged_record, deps, depth + 1);
104
+ for (const nestedLine of nested.body.split("\n")) {
105
+ lines.push(`│ ${nestedLine}`);
106
+ }
107
+ mediaPaths.push(...nested.mediaPaths);
108
+ mediaTypes.push(...nested.mediaTypes);
109
+ }
110
+ else {
111
+ const syncContent = formatSubItemContent(item);
112
+ if (syncContent !== undefined) {
113
+ lines.push(`│ ${header}`);
114
+ for (const contentLine of syncContent.split("\n")) {
115
+ lines.push(`│ ${contentLine}`);
116
+ }
117
+ }
118
+ else if (item.type === MessageItemType.IMAGE ||
119
+ item.type === MessageItemType.VIDEO ||
120
+ item.type === MessageItemType.FILE ||
121
+ item.type === MessageItemType.VOICE) {
122
+ try {
123
+ const media = await formatMediaContent(item, deps);
124
+ lines.push(`│ ${header}`);
125
+ lines.push(`│ ${media.text}`);
126
+ if (media.mediaPath && media.mediaType) {
127
+ mediaPaths.push(media.mediaPath);
128
+ mediaTypes.push(media.mediaType);
129
+ }
130
+ }
131
+ catch (err) {
132
+ logger.error(`merged-record: media format failed: ${String(err)}`);
133
+ lines.push(`│ ${header}`);
134
+ lines.push("│ [媒体加载失败]");
135
+ }
136
+ }
137
+ else {
138
+ lines.push(`│ ${header}`);
139
+ lines.push("│ [不支持的消息]");
140
+ }
141
+ }
142
+ lines.push("│");
143
+ }
144
+ if (lines.length > 1 && lines[lines.length - 1] === "│") {
145
+ lines.pop();
146
+ }
147
+ return { body: lines.join("\n"), mediaPaths, mediaTypes };
148
+ }
149
+ //# sourceMappingURL=merged-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged-record.js","sourceRoot":"","sources":["../../../src/messaging/merged-record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Model selection buttons for Weixin.
3
+ *
4
+ * Reuses the same mdl_* callback_data format as Telegram so that core
5
+ * helpers (buildModelsProviderData, formatModelsAvailableHeader) can be
6
+ * shared directly.
7
+ *
8
+ * Callback data patterns:
9
+ * - mdl_prov - show providers list
10
+ * - mdl_list_{prov}_{pg} - show models for provider (page N, 1-indexed)
11
+ * - mdl_sel_{provider/id} - select model
12
+ * - mdl_back - back to providers list
13
+ */
14
+ const MODELS_PAGE_SIZE = 8;
15
+ // ---------------------------------------------------------------------------
16
+ // Callback data parsing
17
+ // ---------------------------------------------------------------------------
18
+ export function parseModelCallbackData(data) {
19
+ const trimmed = data.trim();
20
+ if (!trimmed.startsWith("mdl_"))
21
+ return null;
22
+ if (trimmed === "mdl_prov" || trimmed === "mdl_back") {
23
+ return { type: trimmed === "mdl_prov" ? "providers" : "back" };
24
+ }
25
+ const listMatch = trimmed.match(/^mdl_list_([a-z0-9_-]+)_(\d+)$/i);
26
+ if (listMatch) {
27
+ const [, provider, pageStr] = listMatch;
28
+ const page = Number.parseInt(pageStr ?? "1", 10);
29
+ if (provider && Number.isFinite(page) && page >= 1) {
30
+ return { type: "list", provider, page };
31
+ }
32
+ }
33
+ const selMatch = trimmed.match(/^mdl_sel_(.+)$/);
34
+ if (selMatch) {
35
+ const modelRef = selMatch[1];
36
+ if (modelRef) {
37
+ const slashIndex = modelRef.indexOf("/");
38
+ if (slashIndex > 0 && slashIndex < modelRef.length - 1) {
39
+ return {
40
+ type: "select",
41
+ provider: modelRef.slice(0, slashIndex),
42
+ model: modelRef.slice(slashIndex + 1),
43
+ };
44
+ }
45
+ return { type: "select", model: modelRef };
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Keyboard builders
52
+ // ---------------------------------------------------------------------------
53
+ export function buildProviderKeyboard(providers) {
54
+ if (providers.length === 0)
55
+ return [];
56
+ const rows = [];
57
+ let currentRow = [];
58
+ for (const provider of providers) {
59
+ currentRow.push({
60
+ text: `${provider.id} (${provider.count})`,
61
+ callback_data: `mdl_list_${provider.id}_1`,
62
+ });
63
+ if (currentRow.length === 2) {
64
+ rows.push(currentRow);
65
+ currentRow = [];
66
+ }
67
+ }
68
+ if (currentRow.length > 0)
69
+ rows.push(currentRow);
70
+ return rows;
71
+ }
72
+ function truncateModelId(modelId, maxLen) {
73
+ if (modelId.length <= maxLen)
74
+ return modelId;
75
+ return `…${modelId.slice(-(maxLen - 1))}`;
76
+ }
77
+ export function buildModelsKeyboard(params) {
78
+ const { provider, models, currentModel, currentPage, totalPages, modelNames } = params;
79
+ const pageSize = params.pageSize ?? MODELS_PAGE_SIZE;
80
+ if (models.length === 0) {
81
+ return [[{ text: "<< Back", callback_data: "mdl_back" }]];
82
+ }
83
+ const rows = [];
84
+ const startIndex = (currentPage - 1) * pageSize;
85
+ const endIndex = Math.min(startIndex + pageSize, models.length);
86
+ const pageModels = models.slice(startIndex, endIndex);
87
+ for (const model of pageModels) {
88
+ const callbackData = `mdl_sel_${provider}/${model}`;
89
+ const isCurrentModel = currentModel
90
+ ? currentModel.includes("/")
91
+ ? currentModel === `${provider}/${model}`
92
+ : currentModel === model
93
+ : false;
94
+ const displayLabel = modelNames?.get(`${provider}/${model}`) ?? model;
95
+ const displayText = truncateModelId(displayLabel, 38);
96
+ const text = isCurrentModel ? `${displayText} ✓` : displayText;
97
+ rows.push([{ text, callback_data: callbackData }]);
98
+ }
99
+ if (totalPages > 1) {
100
+ const paginationRow = [];
101
+ if (currentPage > 1) {
102
+ paginationRow.push({
103
+ text: "◀ Prev",
104
+ callback_data: `mdl_list_${provider}_${currentPage - 1}`,
105
+ });
106
+ }
107
+ paginationRow.push({
108
+ text: `${currentPage}/${totalPages}`,
109
+ callback_data: `mdl_list_${provider}_${currentPage}`,
110
+ });
111
+ if (currentPage < totalPages) {
112
+ paginationRow.push({
113
+ text: "Next ▶",
114
+ callback_data: `mdl_list_${provider}_${currentPage + 1}`,
115
+ });
116
+ }
117
+ rows.push(paginationRow);
118
+ }
119
+ rows.push([{ text: "<< Back", callback_data: "mdl_back" }]);
120
+ return rows;
121
+ }
122
+ export function buildBrowseProvidersButton() {
123
+ return [[{ text: "Browse providers", callback_data: "mdl_prov" }]];
124
+ }
125
+ export function getModelsPageSize() {
126
+ return MODELS_PAGE_SIZE;
127
+ }
128
+ export function calculateTotalPages(totalModels, pageSize) {
129
+ const size = pageSize ?? MODELS_PAGE_SIZE;
130
+ return size > 0 ? Math.ceil(totalModels / size) : 1;
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // channelData builders (used by plugin.commands adapter)
134
+ // ---------------------------------------------------------------------------
135
+ export function buildWeixinModelsProviderChannelData(params) {
136
+ if (params.providers.length === 0)
137
+ return null;
138
+ return { "openclaw-weixin": { buttons: buildProviderKeyboard(params.providers) } };
139
+ }
140
+ export function buildWeixinModelsListChannelData(params) {
141
+ return { "openclaw-weixin": { buttons: buildModelsKeyboard(params) } };
142
+ }
143
+ export function buildWeixinModelBrowseChannelData() {
144
+ return { "openclaw-weixin": { buttons: buildBrowseProvidersButton() } };
145
+ }
146
+ export function buildWeixinCommandsListChannelData(params) {
147
+ if (params.totalPages <= 1)
148
+ return null;
149
+ const buttons = [];
150
+ const suffix = params.agentId ? `:${params.agentId}` : "";
151
+ if (params.currentPage > 1) {
152
+ buttons.push({
153
+ text: "◀ Prev",
154
+ callback_data: `commands_page_${params.currentPage - 1}${suffix}`,
155
+ });
156
+ }
157
+ buttons.push({
158
+ text: `${params.currentPage}/${params.totalPages}`,
159
+ callback_data: `commands_page_noop${suffix}`,
160
+ });
161
+ if (params.currentPage < params.totalPages) {
162
+ buttons.push({
163
+ text: "Next ▶",
164
+ callback_data: `commands_page_${params.currentPage + 1}${suffix}`,
165
+ });
166
+ }
167
+ return { "openclaw-weixin": { buttons: [buttons] } };
168
+ }
169
+ // ---------------------------------------------------------------------------
170
+ // Model selection resolution (when provider is ambiguous)
171
+ // ---------------------------------------------------------------------------
172
+ export function resolveModelSelection(params) {
173
+ if (params.callback.provider) {
174
+ return { kind: "resolved", provider: params.callback.provider, model: params.callback.model };
175
+ }
176
+ const matchingProviders = params.providers.filter((id) => params.byProvider.get(id)?.has(params.callback.model));
177
+ if (matchingProviders.length === 1) {
178
+ return { kind: "resolved", provider: matchingProviders[0], model: params.callback.model };
179
+ }
180
+ return { kind: "ambiguous", model: params.callback.model, matchingProviders };
181
+ }
182
+ //# sourceMappingURL=model-buttons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-buttons.js","sourceRoot":"","sources":["../../../src/messaging/model-buttons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Handle mdl_* button callbacks for /model interactive flow.
3
+ *
4
+ * When a user taps a model-selection button, the BUTTON_CALLBACK arrives
5
+ * as an inbound message with call_back_data = "mdl_...". We intercept
6
+ * it here, build the next response (provider list / model list / confirmation),
7
+ * and send a NEW message (no editing).
8
+ */
9
+ import { buildModelsProviderData, formatModelsAvailableHeader } from "openclaw/plugin-sdk/models-provider-runtime";
10
+ import { resolveAgentDir, resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
11
+ import { applyModelOverrideToSessionEntry, updateSessionStore, resolveStorePath, } from "openclaw/plugin-sdk/config-runtime";
12
+ import { logger } from "../util/logger.js";
13
+ import { sendMessageWeixin } from "./send.js";
14
+ import { getContextToken } from "./inbound.js";
15
+ import { parseModelCallbackData, buildProviderKeyboard, buildModelsKeyboard, calculateTotalPages, getModelsPageSize, resolveModelSelection, } from "./model-buttons.js";
16
+ /**
17
+ * Try to handle an inbound message as a model callback.
18
+ * Returns true if the callback was handled, false otherwise.
19
+ */
20
+ export async function handleModelCallback(callbackData, deps) {
21
+ const parsed = parseModelCallbackData(callbackData);
22
+ if (!parsed)
23
+ return false;
24
+ const contextToken = getContextToken(deps.accountId, deps.to);
25
+ const sendOpts = { baseUrl: deps.baseUrl, token: deps.token, contextToken };
26
+ const modelData = await buildModelsProviderData(deps.cfg, deps.agentId);
27
+ const { byProvider, providers } = modelData;
28
+ const sendWithButtons = async (text, buttons) => {
29
+ await sendMessageWeixin({
30
+ to: deps.to,
31
+ text,
32
+ opts: sendOpts,
33
+ buttons: buttons.length > 0 ? buttons : undefined,
34
+ });
35
+ };
36
+ if (parsed.type === "providers" || parsed.type === "back") {
37
+ if (providers.length === 0) {
38
+ await sendWithButtons("No providers available.", []);
39
+ return true;
40
+ }
41
+ const providerInfos = providers.map((p) => ({
42
+ id: p,
43
+ count: byProvider.get(p)?.size ?? 0,
44
+ }));
45
+ await sendWithButtons("Select a provider:", buildProviderKeyboard(providerInfos));
46
+ return true;
47
+ }
48
+ if (parsed.type === "list") {
49
+ const { provider, page } = parsed;
50
+ const modelSet = byProvider.get(provider);
51
+ if (!modelSet || modelSet.size === 0) {
52
+ const providerInfos = providers.map((p) => ({
53
+ id: p,
54
+ count: byProvider.get(p)?.size ?? 0,
55
+ }));
56
+ await sendWithButtons(`Unknown provider: ${provider}\n\nSelect a provider:`, buildProviderKeyboard(providerInfos));
57
+ return true;
58
+ }
59
+ const models = [...modelSet].toSorted();
60
+ const pageSize = getModelsPageSize();
61
+ const totalPages = calculateTotalPages(models.length, pageSize);
62
+ const safePage = Math.max(1, Math.min(page, totalPages));
63
+ const currentModel = undefined; // TODO: resolve from session if needed
64
+ const buttons = buildModelsKeyboard({
65
+ provider,
66
+ models,
67
+ currentModel,
68
+ currentPage: safePage,
69
+ totalPages,
70
+ pageSize,
71
+ modelNames: modelData.modelNames,
72
+ });
73
+ const text = formatModelsAvailableHeader({
74
+ provider,
75
+ total: models.length,
76
+ cfg: deps.cfg,
77
+ agentDir: deps.agentId ? resolveAgentDir(deps.cfg, deps.agentId) : undefined,
78
+ });
79
+ await sendWithButtons(text, buttons);
80
+ return true;
81
+ }
82
+ if (parsed.type === "select") {
83
+ const selection = resolveModelSelection({
84
+ callback: parsed,
85
+ providers,
86
+ byProvider,
87
+ });
88
+ if (selection.kind !== "resolved") {
89
+ const providerInfos = providers.map((p) => ({
90
+ id: p,
91
+ count: byProvider.get(p)?.size ?? 0,
92
+ }));
93
+ await sendWithButtons(`Could not resolve model "${selection.model}".\n\nSelect a provider:`, buildProviderKeyboard(providerInfos));
94
+ return true;
95
+ }
96
+ const modelSet = byProvider.get(selection.provider);
97
+ if (!modelSet?.has(selection.model)) {
98
+ await sendWithButtons(`❌ Model "${selection.provider}/${selection.model}" is not allowed.`, []);
99
+ return true;
100
+ }
101
+ try {
102
+ const storePath = resolveStorePath(deps.cfg.session?.store, { agentId: deps.agentId }) ?? "";
103
+ const resolvedDefault = resolveDefaultModelForAgent({ cfg: deps.cfg, agentId: deps.agentId });
104
+ const isDefaultSelection = selection.provider === resolvedDefault.provider &&
105
+ selection.model === resolvedDefault.model;
106
+ await updateSessionStore(storePath, (store) => {
107
+ if (!deps.sessionKey)
108
+ return;
109
+ const entry = store[deps.sessionKey] ?? {};
110
+ store[deps.sessionKey] = entry;
111
+ applyModelOverrideToSessionEntry({
112
+ entry,
113
+ selection: {
114
+ provider: selection.provider,
115
+ model: selection.model,
116
+ isDefault: isDefaultSelection,
117
+ },
118
+ });
119
+ });
120
+ const actionText = isDefaultSelection
121
+ ? "reset to default"
122
+ : `changed to ${selection.provider}/${selection.model}`;
123
+ await sendWithButtons(`✅ Model ${actionText}\n\nThis model will be used for your next message.`, []);
124
+ }
125
+ catch (err) {
126
+ logger.error(`handleModelCallback: failed to set model: ${String(err)}`);
127
+ await sendWithButtons(`❌ Failed to set model: ${String(err)}`, []);
128
+ }
129
+ return true;
130
+ }
131
+ return false;
132
+ }
133
+ //# sourceMappingURL=model-callback-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-callback-handler.js","sourceRoot":"","sources":["../../../src/messaging/model-callback-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export function createLaneScheduler() {
2
+ /**
3
+ * laneKey -> tail promise of the lane's in-flight chain. The chain is built
4
+ * with internal `try/catch` so failures of earlier tasks do not poison later
5
+ * tasks on the same lane. Entries are pruned when the lane goes idle.
6
+ */
7
+ const lanes = new Map();
8
+ function enqueue(key, task) {
9
+ const previousTail = lanes.get(key) ?? Promise.resolve();
10
+ let resolveResult;
11
+ let rejectResult;
12
+ const result = new Promise((resolve, reject) => {
13
+ resolveResult = resolve;
14
+ rejectResult = reject;
15
+ });
16
+ const nextTail = previousTail.then(async () => {
17
+ try {
18
+ const value = await task();
19
+ resolveResult(value);
20
+ }
21
+ catch (err) {
22
+ rejectResult(err);
23
+ }
24
+ });
25
+ lanes.set(key, nextTail);
26
+ void nextTail.finally(() => {
27
+ // Only prune if no one queued behind us in the meantime; otherwise the
28
+ // newer enqueue() owns the slot.
29
+ if (lanes.get(key) === nextTail) {
30
+ lanes.delete(key);
31
+ }
32
+ });
33
+ return result;
34
+ }
35
+ function size() {
36
+ return lanes.size;
37
+ }
38
+ async function drain() {
39
+ while (lanes.size > 0) {
40
+ const tails = [...lanes.values()];
41
+ await Promise.allSettled(tails);
42
+ }
43
+ }
44
+ return { enqueue, size, drain };
45
+ }
46
+ //# sourceMappingURL=lane-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lane-scheduler.js","sourceRoot":"","sources":["../../../src/monitor/lane-scheduler.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,mBAAmB;IACjC;;;;OAIG;IACH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE/C,SAAS,OAAO,CAAI,GAAW,EAAE,IAAsB;QACrD,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACzD,IAAI,aAAmD,CAAC;QACxD,IAAI,YAAyC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAChD,aAAa,GAAG,OAAO,CAAC;YACxB,YAAY,GAAG,MAAM,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YAC5C,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,IAAI,EAAE,CAAC;gBAC3B,aAAa,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,YAAY,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzB,KAAK,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;YACzB,uEAAuE;YACvE,iCAAiC;YACjC,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAChC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,SAAS,IAAI;QACX,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,UAAU,KAAK;QAClB,OAAO,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAClC,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC"}
@@ -2,7 +2,6 @@ import { getUpdates } from "../api/api.js";
2
2
  import { WeixinConfigManager } from "../api/config-cache.js";
3
3
  import { SESSION_EXPIRED_ERRCODE, pauseSession, getRemainingPauseMs } from "../api/session-guard.js";
4
4
  import { processOneMessage } from "../messaging/process-message.js";
5
- import { waitForWeixinRuntime } from "../runtime.js";
6
5
  import { getSyncBufFilePath, loadGetUpdatesBuf, saveGetUpdatesBuf } from "../storage/sync-buf.js";
7
6
  import { logger } from "../util/logger.js";
8
7
  import { redactBody } from "../util/redact.js";
@@ -15,20 +14,14 @@ const RETRY_DELAY_MS = 2_000;
15
14
  * Runs until abort.
16
15
  */
17
16
  export async function monitorWeixinProvider(opts) {
18
- const { baseUrl, cdnBaseUrl, token, accountId, config, abortSignal, longPollTimeoutMs, setStatus, } = opts;
17
+ const { baseUrl, cdnBaseUrl, token, accountId, config, channelRuntime, abortSignal, longPollTimeoutMs, setStatus, } = opts;
19
18
  const log = opts.runtime?.log ?? (() => { });
20
19
  const errLog = opts.runtime?.error ?? ((m) => log(m));
21
20
  const aLog = logger.withAccount(accountId);
22
- aLog.info(`waiting for Weixin runtime...`);
23
- let channelRuntime;
24
- try {
25
- const pluginRuntime = await waitForWeixinRuntime();
26
- channelRuntime = pluginRuntime.channel;
27
- aLog.info(`Weixin runtime acquired, channelRuntime type: ${typeof channelRuntime}`);
28
- }
29
- catch (err) {
30
- aLog.error(`waitForWeixinRuntime() failed: ${String(err)}`);
31
- throw err;
21
+ if (!channelRuntime) {
22
+ const msg = "channelRuntime missing on monitor opts; gateway must inject ChannelGatewayContext.channelRuntime";
23
+ aLog.error(msg);
24
+ throw new Error(msg);
32
25
  }
33
26
  log(`weixin monitor started (${baseUrl}, account=${accountId})`);
34
27
  aLog.info(`Monitor started: baseUrl=${baseUrl} timeoutMs=${longPollTimeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS}`);