@kyan-du/agent-wechat-openclaw 0.14.4 → 0.14.5

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 +35 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6610,9 +6610,12 @@ function safeInboundFilename(raw) {
6610
6610
  const cleaned = [...base].filter((char) => char >= " " && char !== "\x7F" && char !== "/" && char !== "\\").slice(0, MAX_FILENAME_CHARS).join("").replace(/^[.\s]+|[.\s]+$/g, "");
6611
6611
  return cleaned || void 0;
6612
6612
  }
6613
- var SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set(["pdf"]);
6613
+ var SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set(["pdf", "docx"]);
6614
6614
  function documentMimeFromMagic(buf) {
6615
6615
  if (buf.length >= 5 && buf.toString("ascii", 0, 5) === "%PDF-") return "application/pdf";
6616
+ if (buf.length >= 4 && buf[0] === 80 && buf[1] === 75 && (buf[2] === 3 || buf[2] === 5 || buf[2] === 7) && buf[3] === 4 && buf.includes(Buffer.from("word/document.xml", "ascii"))) {
6617
+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
6618
+ }
6616
6619
  return void 0;
6617
6620
  }
6618
6621
  function binaryMimeFromMagic(buf) {
@@ -6868,7 +6871,11 @@ async function validateInboundMedia(result) {
6868
6871
  if (buffer.length > MAX_FILE_BYTES) return { ok: false, code: "MEDIA_FILE_TOO_LARGE" };
6869
6872
  if (!safeInboundFilename(result.filename)) return { ok: false, code: "MEDIA_FILENAME_INVALID" };
6870
6873
  if (!SUPPORTED_FILE_FORMATS.has(format)) return { ok: false, code: "MEDIA_FILE_TYPE_MISMATCH" };
6871
- if (documentMimeFromMagic(buffer) !== "application/pdf") {
6874
+ const detected = documentMimeFromMagic(buffer);
6875
+ if (format === "pdf" && detected !== "application/pdf") {
6876
+ return { ok: false, code: "MEDIA_FILE_TYPE_MISMATCH" };
6877
+ }
6878
+ if (format === "docx" && detected !== "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
6872
6879
  return { ok: false, code: "MEDIA_FILE_TYPE_MISMATCH" };
6873
6880
  }
6874
6881
  }
@@ -6938,9 +6945,10 @@ var RETRYABLE_MEDIA_ERRORS = /* @__PURE__ */ new Set([
6938
6945
  ]);
6939
6946
  var DEFAULT_MEDIA_POLL_ATTEMPTS = 6;
6940
6947
  var DEFAULT_MEDIA_POLL_INTERVAL_MS = 500;
6948
+ var CHAT_HISTORY_MEDIA_POLL_INTERVAL_MS = 1e3;
6941
6949
  var IMAGE_MATERIALIZATION_OPEN_CHAT_TIMEOUT_MS = 400;
6942
6950
  var FILE_MATERIALIZATION_CLICK_TIMEOUT_MS = 8e3;
6943
- var CHAT_HISTORY_MATERIALIZATION_CLICK_TIMEOUT_MS = 12e3;
6951
+ var CHAT_HISTORY_MATERIALIZATION_CLICK_TIMEOUT_MS = 25e3;
6944
6952
  function shouldTriggerMediaMaterialization(result) {
6945
6953
  return result.type === "image" || result.type === "file" || result.type === "pending";
6946
6954
  }
@@ -7005,7 +7013,7 @@ function triggerChatHistoryMaterialize(client, chatId, result, attempt, options)
7005
7013
  }
7006
7014
  function createChatHistoryMaterializationTrigger(client, chatId, options) {
7007
7015
  return async (result, attempt) => {
7008
- if (!shouldTriggerChatHistoryMaterialization(result)) return;
7016
+ if (!shouldTriggerChatHistoryMaterialization(result) || !options?.title?.trim()) return;
7009
7017
  triggerChatHistoryMaterialize(client, chatId, result, attempt, options);
7010
7018
  };
7011
7019
  }
@@ -7021,6 +7029,11 @@ function chatHistoryTitleFromMessage(msg) {
7021
7029
  function createMediaMaterializationTrigger(client, chatId, options) {
7022
7030
  const timeoutMs = options?.timeoutMs ?? IMAGE_MATERIALIZATION_OPEN_CHAT_TIMEOUT_MS;
7023
7031
  return async (result, attempt) => {
7032
+ if (result.errorCode === "CHAT_HISTORY_NOT_MATERIALIZED") {
7033
+ if (!options?.title?.trim()) return;
7034
+ triggerChatHistoryMaterialize(client, chatId, result, attempt, options);
7035
+ return;
7036
+ }
7024
7037
  if (!shouldTriggerMediaMaterialization(result)) return;
7025
7038
  if (isFileMaterialization(result)) {
7026
7039
  triggerFileBubbleClick(client, chatId, result, attempt, options);
@@ -7072,6 +7085,15 @@ function isRetryable(result) {
7072
7085
  if (result.errorCode !== void 0) return RETRYABLE_MEDIA_ERRORS.has(result.errorCode);
7073
7086
  return result.type === "pending";
7074
7087
  }
7088
+ function chatHistoryMediaPollWindow(opts) {
7089
+ const intervalMs = Math.max(1, opts?.intervalMs ?? CHAT_HISTORY_MEDIA_POLL_INTERVAL_MS);
7090
+ const timeoutMs = opts?.timeoutMs ?? CHAT_HISTORY_MATERIALIZATION_CLICK_TIMEOUT_MS;
7091
+ const maxAttempts = Math.max(
7092
+ DEFAULT_MEDIA_POLL_ATTEMPTS,
7093
+ Math.ceil(timeoutMs / intervalMs) + 1
7094
+ );
7095
+ return { maxAttempts, intervalMs };
7096
+ }
7075
7097
  async function pollMedia(client, chatId, localId, log, maxAttempts = DEFAULT_MEDIA_POLL_ATTEMPTS, intervalMs = DEFAULT_MEDIA_POLL_INTERVAL_MS, onRetryTrigger) {
7076
7098
  let lastResult;
7077
7099
  let triggerUsed = false;
@@ -7353,6 +7375,9 @@ function loadFile(path) {
7353
7375
  function inboundEventId(accountId, chatId, message) {
7354
7376
  return createHash("sha256").update(`${accountId}\0${chatId}\0${cursorMessageKey(message)}`).digest("hex");
7355
7377
  }
7378
+ function nestedMediaEventId(parentEventId, item) {
7379
+ return createHash("sha256").update(JSON.stringify(["nested-media", parentEventId, item.type ?? "", item.filename ?? ""])).digest("hex");
7380
+ }
7356
7381
  var InboundEventLedger = class {
7357
7382
  path;
7358
7383
  entries = /* @__PURE__ */ new Map();
@@ -8092,13 +8117,14 @@ async function prepareMessage(client, mediaPipeline, msg, chatId, chat, liveAcco
8092
8117
  if (mayHaveMedia) {
8093
8118
  log?.info?.(`[wechat:${liveAccount.accountId}] Checking media for msg ${msg.localId} (type ${baseType})`);
8094
8119
  try {
8120
+ const chatHistoryPoll = isChatHistory ? chatHistoryMediaPollWindow() : void 0;
8095
8121
  const result = await pollMedia(
8096
8122
  client,
8097
8123
  chatId,
8098
8124
  msg.localId,
8099
8125
  log,
8100
- void 0,
8101
- void 0,
8126
+ chatHistoryPoll?.maxAttempts,
8127
+ chatHistoryPoll?.intervalMs,
8102
8128
  mediaMaterializationTriggerForMessage({
8103
8129
  client,
8104
8130
  chatId,
@@ -8112,19 +8138,16 @@ async function prepareMessage(client, mediaPipeline, msg, chatId, chat, liveAcco
8112
8138
  })
8113
8139
  );
8114
8140
  const nestedItems = nestedChatHistoryMedia(result);
8115
- if (isChatHistory && nestedItems.length > 0) {
8141
+ if (nestedItems.length > 0) {
8116
8142
  const savedPaths = [];
8117
8143
  const savedMimes = [];
8118
8144
  for (const [index, item] of nestedItems.entries()) {
8119
8145
  const saved = await mediaPipeline.process(
8120
8146
  item,
8121
8147
  {
8122
- eventId: inboundEventId(liveAccount.accountId, chatId, {
8123
- ...msg,
8124
- localId: msg.localId * 1e3 + index
8125
- }),
8148
+ eventId: nestedMediaEventId(inboundEventId(liveAccount.accountId, chatId, msg), item),
8126
8149
  chatId,
8127
- localId: msg.localId * 1e3 + index
8150
+ localId: msg.localId
8128
8151
  },
8129
8152
  async (buffer, mime, filename) => core.channel.media.saveMediaBuffer(buffer, mime, "inbound", void 0, filename),
8130
8153
  async (buffer, mime, filename) => core.channel.media.saveMediaBuffer(buffer, mime, "inbound", void 0, filename),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyan-du/agent-wechat-openclaw",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",