@agent-wechat/agent-wechat 0.13.0 → 0.15.0

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 (2) hide show
  1. package/dist/index.js +182 -94
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1127,6 +1127,9 @@ function getWeChatRuntime() {
1127
1127
  return runtime;
1128
1128
  }
1129
1129
 
1130
+ // src/monitor.ts
1131
+ import { createHash as createHash3 } from "node:crypto";
1132
+
1130
1133
  // ../shared/dist/client.js
1131
1134
  function normalizeUrl(base) {
1132
1135
  const url = base.startsWith("http") ? base : `http://${base}`;
@@ -1230,6 +1233,23 @@ var WeChatClient = class {
1230
1233
  async sendMessage(params) {
1231
1234
  return this.post("/api/messages/send", params);
1232
1235
  }
1236
+ async createVoiceJob(chatId, audio, idempotencyKey) {
1237
+ const form = new FormData();
1238
+ form.set("chatId", chatId);
1239
+ form.set("audio", new Blob([new Uint8Array(audio)]), "voice-audio");
1240
+ const headers = { ...this.headers, "Idempotency-Key": idempotencyKey };
1241
+ delete headers["Content-Type"];
1242
+ const res = await fetch(`${this.base}/api/messages/voice`, { method: "POST", headers, body: form });
1243
+ if (!res.ok)
1244
+ throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
1245
+ return res.json();
1246
+ }
1247
+ async getVoiceJob(jobId) {
1248
+ return this.get(`/api/messages/voice/${encodeURIComponent(jobId)}`);
1249
+ }
1250
+ async cancelVoiceJob(jobId) {
1251
+ return this.post(`/api/messages/voice/${encodeURIComponent(jobId)}/cancel`);
1252
+ }
1233
1253
  // ---- Debug ----
1234
1254
  async screenshot() {
1235
1255
  return this.get("/api/debug/screenshot");
@@ -5561,11 +5581,25 @@ ${reference}` : reference;
5561
5581
  function safeDisplayName(name) {
5562
5582
  return name.replace(/[\r\n\0]/g, " ").trim() || "attachment";
5563
5583
  }
5584
+ function attachmentFallbackFilename(kind, localId, originalBody = "") {
5585
+ if (kind === "file" && originalBody && !originalBody.trimStart().startsWith("<")) {
5586
+ return safeDisplayName(originalBody);
5587
+ }
5588
+ const extension = kind === "image" ? "jpg" : kind === "audio" ? "mp3" : kind === "video" ? "mp4" : "bin";
5589
+ return `message-${localId}.${extension}`;
5590
+ }
5591
+ function attachmentSourceBody(originalBody, kind) {
5592
+ if (!kind || kind === "file") return originalBody;
5593
+ return originalBody.trimStart().startsWith("<") ? "" : originalBody;
5594
+ }
5564
5595
  function renderAttachmentBody(originalBody, attachment, exposeNonVisualPath = true) {
5565
5596
  if (!attachment) return originalBody;
5566
5597
  const name = safeDisplayName(attachment.filename);
5567
5598
  if (attachment.status !== "ready" || !attachment.path) {
5568
5599
  const label = attachment.kind === "file" ? "File" : attachment.kind === "image" ? "Image" : attachment.kind === "audio" ? "Voice message" : "Video";
5600
+ if (attachment.status === "pending") {
5601
+ return appendReference(originalBody, `[${label} pending: ${name}]`);
5602
+ }
5569
5603
  return appendReference(originalBody, `[${label} unavailable: ${name} (${attachment.status})]`);
5570
5604
  }
5571
5605
  if (attachment.kind === "file") {
@@ -5765,6 +5799,96 @@ async function listMessageWindow(client, chatId, previousLastSeen, firstPoll, un
5765
5799
  return messages;
5766
5800
  }
5767
5801
 
5802
+ // src/outbound-media.ts
5803
+ import { randomUUID as randomUUID2 } from "node:crypto";
5804
+ import { readFile } from "node:fs/promises";
5805
+ import { basename, extname } from "node:path";
5806
+ var EXT_MIME = {
5807
+ png: "image/png",
5808
+ jpg: "image/jpeg",
5809
+ jpeg: "image/jpeg",
5810
+ gif: "image/gif",
5811
+ webp: "image/webp",
5812
+ mp3: "audio/mpeg",
5813
+ wav: "audio/wav",
5814
+ ogg: "audio/ogg",
5815
+ opus: "audio/ogg",
5816
+ m4a: "audio/mp4",
5817
+ flac: "audio/flac",
5818
+ aac: "audio/aac"
5819
+ };
5820
+ function isTerminal(job) {
5821
+ return ["completed", "failed", "cancelled", "needs_review"].includes(job.status);
5822
+ }
5823
+ async function waitForVoiceJob(client, jobId) {
5824
+ const deadline = Date.now() + 25 * 6e4;
5825
+ let lastStatusAt = Date.now();
5826
+ while (Date.now() < deadline) {
5827
+ let job;
5828
+ try {
5829
+ job = await client.getVoiceJob(jobId);
5830
+ lastStatusAt = Date.now();
5831
+ } catch (error) {
5832
+ if (Date.now() - lastStatusAt > 3e4) {
5833
+ throw new Error(`Voice job ${jobId} status is unavailable: ${String(error)}. Inspect the job before retrying.`);
5834
+ }
5835
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
5836
+ continue;
5837
+ }
5838
+ if (isTerminal(job)) {
5839
+ if (job.status === "completed") return job;
5840
+ const sent = job.chunks.filter((chunk) => chunk.status === "verified").length;
5841
+ throw new Error(`Voice job ${jobId} ${job.status}: ${sent}/${job.chunks.length} notes verified. ${job.error ?? ""} Do not resend the whole recording.`);
5842
+ }
5843
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
5844
+ }
5845
+ throw new Error(`Voice job ${jobId} exceeded the 25-minute wait. Inspect the job before retrying.`);
5846
+ }
5847
+ async function sendWeChatMedia(cfg, to, text, mediaUrl, audioAsVoice = false, idempotencyKey) {
5848
+ const account = resolveWeChatAccount(cfg);
5849
+ if (!account?.serverUrl) throw new Error("No serverUrl configured");
5850
+ const client = new WeChatClient({ baseUrl: account.serverUrl, token: account.token });
5851
+ if (!mediaUrl) {
5852
+ if (audioAsVoice) throw new Error("Voice sending requires an audio attachment");
5853
+ const result2 = await client.sendMessage({ chatId: to, text: text || void 0 });
5854
+ if (!result2.success) throw new Error(result2.error ?? "Send failed");
5855
+ return `agent-wechat:${to}:${Date.now()}`;
5856
+ }
5857
+ let bytes;
5858
+ let mimeType;
5859
+ let filename;
5860
+ if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
5861
+ const res = await fetch(mediaUrl);
5862
+ if (!res.ok) throw new Error(`Media fetch failed: ${res.status}`);
5863
+ bytes = new Uint8Array(await res.arrayBuffer());
5864
+ mimeType = res.headers.get("content-type")?.split(";")[0] ?? "application/octet-stream";
5865
+ filename = basename(new URL(mediaUrl).pathname) || "file";
5866
+ if (mimeType === "application/octet-stream") {
5867
+ mimeType = EXT_MIME[extname(filename).toLowerCase().slice(1)] ?? mimeType;
5868
+ }
5869
+ } else {
5870
+ const localPath = mediaUrl.startsWith("file://") ? new URL(mediaUrl) : mediaUrl;
5871
+ const pathText = localPath instanceof URL ? localPath.pathname : localPath;
5872
+ bytes = new Uint8Array(await readFile(localPath));
5873
+ filename = basename(pathText);
5874
+ mimeType = EXT_MIME[extname(filename).toLowerCase().slice(1)] ?? "application/octet-stream";
5875
+ }
5876
+ if (audioAsVoice) {
5877
+ if (!mimeType.startsWith("audio/")) throw new Error(`Voice media must be audio, got ${mimeType}`);
5878
+ const job = await client.createVoiceJob(to, bytes, idempotencyKey ?? randomUUID2());
5879
+ await waitForVoiceJob(client, job.jobId);
5880
+ if (text) {
5881
+ const caption = await client.sendMessage({ chatId: to, text });
5882
+ if (!caption.success) throw new Error(`Voice job ${job.jobId} completed, but caption failed: ${caption.error ?? "unknown error"}`);
5883
+ }
5884
+ return `agent-wechat:voice:${job.jobId}`;
5885
+ }
5886
+ const base64 = Buffer.from(bytes).toString("base64");
5887
+ const result = mimeType.startsWith("image/") ? await client.sendMessage({ chatId: to, text: text || void 0, image: { data: base64, mimeType } }) : await client.sendMessage({ chatId: to, text: text || void 0, file: { data: base64, filename } });
5888
+ if (!result.success) throw new Error(result.error ?? "Send media failed");
5889
+ return `agent-wechat:${to}:${Date.now()}`;
5890
+ }
5891
+
5768
5892
  // src/access-control.ts
5769
5893
  import {
5770
5894
  buildChannelKeyCandidates,
@@ -5975,6 +6099,7 @@ function resolveWeChatMentionGate(params) {
5975
6099
  // src/monitor.ts
5976
6100
  var HISTORY_CONTEXT_MARKER = "[Chat messages since your last reply - for context]";
5977
6101
  var CURRENT_MESSAGE_MARKER = "[Current message - respond to this]";
6102
+ var voiceTranscriptCache = /* @__PURE__ */ new Map();
5978
6103
  function isProcessedMessage(value) {
5979
6104
  if (!value || typeof value !== "object") return false;
5980
6105
  const message = value;
@@ -6024,17 +6149,18 @@ async function retrieveAttachment(client, chatId, msg, liveAccount, log, maxAtte
6024
6149
  const baseType = msg.type & 2147483647;
6025
6150
  const expectedKind = attachmentKindForMessageType(baseType);
6026
6151
  if (!expectedKind) return void 0;
6152
+ const fallbackFilename = attachmentFallbackFilename(expectedKind, msg.localId, msg.content);
6027
6153
  try {
6028
6154
  const result = await pollMedia(client, chatId, msg.localId, log, maxAttempts);
6029
6155
  if (!result) {
6030
- return { kind: expectedKind, status: "unavailable", filename: msg.content || `message-${msg.localId}` };
6156
+ return { kind: expectedKind, status: "unavailable", filename: fallbackFilename };
6031
6157
  }
6032
6158
  if (result.type === "unsupported") {
6033
6159
  if (baseType === 49) return void 0;
6034
- return { kind: expectedKind, status: "unsupported", filename: msg.content || `message-${msg.localId}` };
6160
+ return { kind: expectedKind, status: "unsupported", filename: fallbackFilename };
6035
6161
  }
6036
6162
  const kind = attachmentKindForResult(result, expectedKind);
6037
- const filename = result.filename || msg.content || `message-${msg.localId}`;
6163
+ const filename = result.filename || fallbackFilename;
6038
6164
  if (!result.data) {
6039
6165
  return { kind, status: result.type === "pending" ? "pending" : "unavailable", filename };
6040
6166
  }
@@ -6058,7 +6184,7 @@ async function retrieveAttachment(client, chatId, msg, liveAccount, log, maxAtte
6058
6184
  return {
6059
6185
  kind: expectedKind,
6060
6186
  status,
6061
- filename: msg.content || `message-${msg.localId}`,
6187
+ filename: fallbackFilename,
6062
6188
  error: error instanceof Error ? error.message : String(error)
6063
6189
  };
6064
6190
  }
@@ -6228,13 +6354,14 @@ async function prepareMessage(client, msg, chatId, chat, liveAccount, policy, lo
6228
6354
  return null;
6229
6355
  }
6230
6356
  const baseType = msg.type & 2147483647;
6357
+ const messageAttachmentKind = attachmentKindForMessageType(baseType);
6231
6358
  let attachment;
6232
- if (attachmentKindForMessageType(baseType)) {
6359
+ if (messageAttachmentKind) {
6233
6360
  log?.info?.(`[wechat:${liveAccount.accountId}] Checking media for msg ${msg.localId} (type ${baseType})`);
6234
6361
  attachment = await retrieveAttachment(client, chatId, msg, liveAccount, log);
6235
6362
  }
6236
6363
  const timestamp = new Date(msg.timestamp).getTime();
6237
- let sourceBody = msg.content || "";
6364
+ let sourceBody = attachmentSourceBody(msg.content || "", messageAttachmentKind);
6238
6365
  if (msg.reply) {
6239
6366
  const replySender = msg.reply.sender ?? "unknown sender";
6240
6367
  const quotedBody = msg.reply.content.length > 50 ? msg.reply.content.slice(0, 50) + "..." : msg.reply.content;
@@ -6344,6 +6471,36 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, polic
6344
6471
  return "ignored";
6345
6472
  }
6346
6473
  try {
6474
+ for (const item of segment) {
6475
+ const attachment = item.attachment;
6476
+ if (item.mediaPath || attachment?.kind !== "audio" || attachment.status !== "ready" || !attachment.path) continue;
6477
+ const cacheKey = `${liveAccount.accountId}:${chatId}:${item.msg.localId}:${attachment.path}`;
6478
+ let transcript = voiceTranscriptCache.get(cacheKey);
6479
+ if (!transcript) {
6480
+ try {
6481
+ const result = await core.mediaUnderstanding.transcribeAudioFile({
6482
+ filePath: attachment.path,
6483
+ cfg,
6484
+ mime: attachment.mime
6485
+ });
6486
+ transcript = result.text?.trim();
6487
+ if (transcript) {
6488
+ if (voiceTranscriptCache.size >= 500) voiceTranscriptCache.clear();
6489
+ voiceTranscriptCache.set(cacheKey, transcript);
6490
+ }
6491
+ } catch (error) {
6492
+ log?.error?.(`[wechat:${liveAccount.accountId}] Voice transcription failed for ${item.msg.localId}: ${String(error)}`);
6493
+ }
6494
+ }
6495
+ if (transcript) {
6496
+ item.rawBody += `
6497
+ [Voice transcript: ${transcript.slice(0, 1e4)}]`;
6498
+ item.commandBody = normalizeWeChatCommandBody(item.rawBody, {
6499
+ isGroup: item.isGroup,
6500
+ wasMentioned: item.isMentioned
6501
+ });
6502
+ }
6503
+ }
6347
6504
  const route = core.channel.routing.resolveAgentRoute({
6348
6505
  cfg,
6349
6506
  channel: "agent-wechat",
@@ -6372,7 +6529,7 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, polic
6372
6529
  timestamp,
6373
6530
  previousTimestamp,
6374
6531
  envelope: envelopeOptions,
6375
- body: isGroup ? `${senderName}: ${rawBody}` : rawBody
6532
+ body: isGroup ? `${senderName}: ${lastMsg.rawBody}` : lastMsg.rawBody
6376
6533
  });
6377
6534
  } else {
6378
6535
  const historyMessages = segment.slice(0, -1);
@@ -6392,7 +6549,7 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, polic
6392
6549
  timestamp,
6393
6550
  previousTimestamp,
6394
6551
  envelope: envelopeOptions,
6395
- body: isGroup ? `${senderName}: ${rawBody}` : rawBody
6552
+ body: isGroup ? `${senderName}: ${lastMsg.rawBody}` : lastMsg.rawBody
6396
6553
  });
6397
6554
  body = [
6398
6555
  HISTORY_CONTEXT_MARKER,
@@ -6414,8 +6571,8 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, polic
6414
6571
  }
6415
6572
  const ctxPayload = core.channel.reply.finalizeInboundContext({
6416
6573
  Body: body,
6417
- BodyForAgent: rawBody,
6418
- RawBody: rawBody,
6574
+ BodyForAgent: lastMsg.rawBody,
6575
+ RawBody: lastMsg.rawBody,
6419
6576
  CommandBody: commandBody,
6420
6577
  InboundHistory: inboundHistory,
6421
6578
  From: isGroup ? `wechat:group:${chatId}` : `wechat:${senderId}`,
@@ -6457,12 +6614,14 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, polic
6457
6614
  accountId: liveAccount.accountId
6458
6615
  });
6459
6616
  let replyFailed = false;
6617
+ let deliverySequence = 0;
6460
6618
  await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
6461
6619
  ctx: ctxPayload,
6462
6620
  cfg,
6463
6621
  dispatcherOptions: {
6464
6622
  ...prefixOptions,
6465
6623
  deliver: async (payload) => {
6624
+ const deliveryIndex = deliverySequence++;
6466
6625
  const mediaList = payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
6467
6626
  const tableMode = core.channel.text.resolveMarkdownTableMode({
6468
6627
  cfg,
@@ -6474,49 +6633,17 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, polic
6474
6633
  tableMode
6475
6634
  );
6476
6635
  if (mediaList.length > 0) {
6477
- for (const mediaUrl of mediaList) {
6478
- try {
6479
- const fsmod = await import("fs/promises");
6480
- const pathmod = await import("path");
6481
- let base64;
6482
- let mimeType;
6483
- let filename;
6484
- if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
6485
- const res = await fetch(mediaUrl);
6486
- const buffer = await res.arrayBuffer();
6487
- base64 = Buffer.from(buffer).toString("base64");
6488
- mimeType = res.headers.get("content-type") ?? "application/octet-stream";
6489
- const urlPath = new URL(mediaUrl).pathname;
6490
- filename = pathmod.basename(urlPath) || "file";
6491
- } else {
6492
- const buf = await fsmod.readFile(mediaUrl);
6493
- base64 = buf.toString("base64");
6494
- filename = pathmod.basename(mediaUrl);
6495
- const ext = pathmod.extname(mediaUrl).toLowerCase().replace(".", "");
6496
- const extMime = {
6497
- png: "image/png",
6498
- jpg: "image/jpeg",
6499
- jpeg: "image/jpeg",
6500
- gif: "image/gif",
6501
- webp: "image/webp"
6502
- };
6503
- mimeType = extMime[ext] ?? "application/octet-stream";
6504
- }
6505
- const isImage = mimeType.startsWith("image/");
6506
- if (isImage) {
6507
- await client.sendMessage({ chatId, image: { data: base64, mimeType } });
6508
- } else {
6509
- await client.sendMessage({ chatId, file: { data: base64, filename } });
6510
- }
6511
- } catch (err) {
6512
- log?.error?.(`[wechat:${liveAccount.accountId}] Failed to send media: ${err}`);
6513
- }
6636
+ for (let index = 0; index < mediaList.length; index++) {
6637
+ const retryKey = createHash3("sha256").update(`${liveAccount.accountId}\0${chatId}\0${msg.localId}\0${deliveryIndex}\0${index}`).digest("hex");
6638
+ await sendWeChatMedia(cfg, chatId, "", mediaList[index], payload.audioAsVoice === true, retryKey);
6514
6639
  }
6515
6640
  if (text) {
6516
- await client.sendMessage({ chatId, text });
6641
+ const result = await client.sendMessage({ chatId, text });
6642
+ if (!result.success) throw new Error(result.error ?? "Text send failed");
6517
6643
  }
6518
6644
  } else if (text) {
6519
- await client.sendMessage({ chatId, text });
6645
+ const result = await client.sendMessage({ chatId, text });
6646
+ if (!result.success) throw new Error(result.error ?? "Text send failed");
6520
6647
  }
6521
6648
  },
6522
6649
  onError: (err, info) => {
@@ -7093,46 +7220,6 @@ async function sendWeChatText(cfg, to, text) {
7093
7220
  if (!result.success) throw new Error(result.error ?? "Send failed");
7094
7221
  return `agent-wechat:${to}:${Date.now()}`;
7095
7222
  }
7096
- async function sendWeChatMedia(cfg, to, text, mediaUrl) {
7097
- const account = resolveWeChatAccount(cfg);
7098
- if (!account?.serverUrl) throw new Error("No serverUrl configured");
7099
- const client = new WeChatClient({ baseUrl: account.serverUrl, token: account.token });
7100
- if (!mediaUrl) {
7101
- const result2 = await client.sendMessage({ chatId: to, text: text || void 0 });
7102
- if (!result2.success) throw new Error(result2.error ?? "Send failed");
7103
- return `agent-wechat:${to}:${Date.now()}`;
7104
- }
7105
- const fsmod = await import("fs/promises");
7106
- const pathmod = await import("path");
7107
- let base64;
7108
- let mimeType;
7109
- let filename;
7110
- if (mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://")) {
7111
- const res = await fetch(mediaUrl);
7112
- const buffer = await res.arrayBuffer();
7113
- base64 = Buffer.from(buffer).toString("base64");
7114
- mimeType = res.headers.get("content-type") ?? "application/octet-stream";
7115
- const urlPath = new URL(mediaUrl).pathname;
7116
- filename = pathmod.basename(urlPath) || "file";
7117
- } else {
7118
- const buf = await fsmod.readFile(mediaUrl);
7119
- base64 = buf.toString("base64");
7120
- filename = pathmod.basename(mediaUrl);
7121
- const ext = pathmod.extname(mediaUrl).toLowerCase().replace(".", "");
7122
- const extMime = {
7123
- png: "image/png",
7124
- jpg: "image/jpeg",
7125
- jpeg: "image/jpeg",
7126
- gif: "image/gif",
7127
- webp: "image/webp"
7128
- };
7129
- mimeType = extMime[ext] ?? "application/octet-stream";
7130
- }
7131
- const isImage = mimeType.startsWith("image/");
7132
- const result = isImage ? await client.sendMessage({ chatId: to, text: text || void 0, image: { data: base64, mimeType } }) : await client.sendMessage({ chatId: to, text: text || void 0, file: { data: base64, filename } });
7133
- if (!result.success) throw new Error(result.error ?? "Send media failed");
7134
- return `agent-wechat:${to}:${Date.now()}`;
7135
- }
7136
7223
  var wechatMessageAdapter = createChannelMessageAdapterFromOutbound({
7137
7224
  id: "agent-wechat",
7138
7225
  capabilities: {
@@ -7145,9 +7232,9 @@ var wechatMessageAdapter = createChannelMessageAdapterFromOutbound({
7145
7232
  channel: "agent-wechat",
7146
7233
  messageId: await sendWeChatText(cfg, to, text)
7147
7234
  }),
7148
- sendMedia: async ({ cfg, to, text, mediaUrl }) => ({
7235
+ sendMedia: async ({ cfg, to, text, mediaUrl, audioAsVoice }) => ({
7149
7236
  channel: "agent-wechat",
7150
- messageId: await sendWeChatMedia(cfg, to, text, mediaUrl)
7237
+ messageId: await sendWeChatMedia(cfg, to, text, mediaUrl, audioAsVoice)
7151
7238
  })
7152
7239
  }
7153
7240
  });
@@ -7168,7 +7255,8 @@ var wechatPlugin = {
7168
7255
  reactions: false,
7169
7256
  threads: false,
7170
7257
  media: true,
7171
- reply: true
7258
+ reply: true,
7259
+ tts: { voice: { synthesisTarget: "voice-note", transcodesAudio: true } }
7172
7260
  },
7173
7261
  reload: { configPrefixes: ["channels.agent-wechat"] },
7174
7262
  configSchema: {
@@ -7295,9 +7383,9 @@ var wechatPlugin = {
7295
7383
  channel: "agent-wechat",
7296
7384
  messageId: await sendWeChatText(cfg, to, text)
7297
7385
  }),
7298
- sendMedia: async ({ cfg, to, text, mediaUrl }) => ({
7386
+ sendMedia: async ({ cfg, to, text, mediaUrl, audioAsVoice }) => ({
7299
7387
  channel: "agent-wechat",
7300
- messageId: await sendWeChatMedia(cfg, to, text, mediaUrl)
7388
+ messageId: await sendWeChatMedia(cfg, to, text, mediaUrl, audioAsVoice)
7301
7389
  })
7302
7390
  },
7303
7391
  message: wechatMessageAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-wechat/agent-wechat",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -11,7 +11,7 @@
11
11
  "esbuild": "^0.25.0",
12
12
  "openclaw": "^2026.8.1",
13
13
  "typescript": "^5.4.5",
14
- "@agent-wechat/shared": "0.2.0"
14
+ "@agent-wechat/shared": "0.3.0"
15
15
  },
16
16
  "openclaw": {
17
17
  "extensions": [