@openclaw/qqbot 2026.7.2-beta.1 → 2026.7.2-beta.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.
package/dist/api.js CHANGED
@@ -1,9 +1,9 @@
1
- import { C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, S as MESSAGE_REPLY_LIMIT, T as getMessageReplyConfig } from "./sender-0vqsivoI.js";
2
- import { t as qqbotPlugin } from "./channel-BbwpkiY3.js";
1
+ import { C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, S as MESSAGE_REPLY_LIMIT, T as getMessageReplyConfig } from "./sender-BAUHZqDW.js";
2
+ import { t as qqbotPlugin } from "./channel-nz3Mkify.js";
3
3
  import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-CpOXnoEc.js";
4
- import { t as qqbotSetupPlugin } from "./channel.setup-DfhritPL.js";
5
- import { o as getFrameworkCommands } from "./group-Dbpnjalm.js";
6
- import { C as resolveUserFacingMediaError, E as setOutboundAudioPort, T as OUTBOUND_ERROR_CODES, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-BLl8Tsu7.js";
7
- import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-DYb23LBU.js";
8
- import { t as registerQQBotFull } from "./channel-entry-Cj1lWXpt.js";
4
+ import { t as qqbotSetupPlugin } from "./channel.setup-CNoBtKXQ.js";
5
+ import { o as getFrameworkCommands } from "./group-BVHG8qUZ.js";
6
+ import { C as resolveUserFacingMediaError, E as setOutboundAudioPort, T as OUTBOUND_ERROR_CODES, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-FOG4zNLY.js";
7
+ import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-CC5CKQig.js";
8
+ import { t as registerQQBotFull } from "./channel-entry-CA2T2sf8.js";
9
9
  export { DEFAULT_ACCOUNT_ID, DEFAULT_MEDIA_SEND_ERROR, MESSAGE_REPLY_LIMIT, OUTBOUND_ERROR_CODES, applyQQBotAccountConfig, buildMediaTarget, checkMessageReplyLimit, getFrameworkCommands, getMessageReplyConfig, getMessageReplyStats, listQQBotAccountIds, parseTarget, qqbotPlugin, qqbotSetupPlugin, recordMessageReply, registerChannelTool, registerQQBotFull, registerQQBotTools, registerRemindTool, resolveDefaultQQBotAccountId, resolveOutboundMediaPath, resolveQQBotAccount, resolveUserFacingMediaError, sendCronMessage, sendDocument, sendMedia, sendPhoto, sendProactiveMessage, sendText, sendVideoMsg, sendVoice, setOutboundAudioPort };
@@ -1,7 +1,7 @@
1
1
  import { a as resolveQQBotAccount } from "./config-CpOXnoEc.js";
2
- import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-Dbpnjalm.js";
3
- import { h as sendDocument } from "./outbound-BLl8Tsu7.js";
4
- import { t as registerQQBotTools } from "./tools-DYb23LBU.js";
2
+ import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-BVHG8qUZ.js";
3
+ import { h as sendDocument } from "./outbound-FOG4zNLY.js";
4
+ import { t as registerQQBotTools } from "./tools-CC5CKQig.js";
5
5
  //#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
6
6
  /**
7
7
  * Default queue snapshot used for framework-registered commands.
@@ -1,2 +1,2 @@
1
- import { t as registerQQBotFull } from "./channel-entry-Cj1lWXpt.js";
1
+ import { t as registerQQBotFull } from "./channel-entry-CA2T2sf8.js";
2
2
  export { registerQQBotFull };
@@ -1,7 +1,7 @@
1
1
  import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-B5Mle_87.js";
2
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-D7MaH5X5.js";
3
3
  import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-CpOXnoEc.js";
4
- import { t as getQQBotRuntime } from "./runtime-Du28LbOJ.js";
4
+ import { t as getQQBotRuntime } from "./runtime-CyjBiGD2.js";
5
5
  import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
6
6
  import { n as normalizeTarget, r as parseTarget, t as looksLikeQQBotTarget } from "./target-parser-DagYRQkP.js";
7
7
  import { getExecApprovalReplyMetadata, resolveExecApprovalCommandDisplay } from "openclaw/plugin-sdk/approval-runtime";
@@ -422,7 +422,7 @@ function createQQBotApprovalCapability() {
422
422
  },
423
423
  load: async () => {
424
424
  ensurePlatformAdapter();
425
- return (await import("./handler-runtime-BvR-ayNW.js")).qqbotApprovalNativeRuntime;
425
+ return (await import("./handler-runtime-S1_XF8on.js")).qqbotApprovalNativeRuntime;
426
426
  }
427
427
  })
428
428
  });
@@ -1053,8 +1053,8 @@ function resolveQQBotGroupToolPolicy(params) {
1053
1053
  }
1054
1054
  //#endregion
1055
1055
  //#region extensions/qqbot/src/channel.ts
1056
- const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-be5-Ckdc.js"));
1057
- const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-BLl8Tsu7.js").then((n) => n.t));
1056
+ const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-D8uYPtoy.js"));
1057
+ const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-FOG4zNLY.js").then((n) => n.t));
1058
1058
  function createQQBotSendReceipt(params) {
1059
1059
  const messageId = params.messageId?.trim();
1060
1060
  return createMessageReceiptFromOutboundResults({
@@ -1,2 +1,2 @@
1
- import { t as qqbotPlugin } from "./channel-BbwpkiY3.js";
1
+ import { t as qqbotPlugin } from "./channel-nz3Mkify.js";
2
2
  export { qqbotPlugin };
@@ -1,4 +1,4 @@
1
- import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-B5Mle_87.js";
1
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-D7MaH5X5.js";
2
2
  //#region extensions/qqbot/src/channel.setup.ts
3
3
  /**
4
4
  * Setup-only QQBot plugin — lightweight subset used during `openclaw onboard`
@@ -1,13 +1,13 @@
1
1
  import { d as registerPlatformAdapterFactory, l as hasPlatformAdapter, n as normalizeLowercaseStringOrEmpty, u as registerPlatformAdapter } from "./string-normalize-R_0cKO7Q.js";
2
2
  import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-CpOXnoEc.js";
3
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
3
+ import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
4
4
  import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
5
5
  import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
6
6
  import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
7
7
  import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
8
8
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
9
9
  import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
10
- import { AllowFromListSchema, ToolPolicySchema, buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
10
+ import { AllowFromListSchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
11
11
  import { z } from "zod";
12
12
  //#region extensions/qqbot/src/bridge/logger.ts
13
13
  let loggerInstance = null;
@@ -25,7 +25,7 @@ function getBridgeLogger() {
25
25
  }
26
26
  //#endregion
27
27
  //#region extensions/qqbot/src/bridge/bootstrap.ts
28
- const loadMediaRuntimeModule = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/media-runtime"));
28
+ const loadReadRemoteMediaBuffer = createLazyRuntimeNamedExport(() => import("openclaw/plugin-sdk/media-runtime"), "readRemoteMediaBuffer");
29
29
  function createBuiltinAdapter() {
30
30
  return {
31
31
  async validateRemoteUrl(_url, _options) {},
@@ -33,8 +33,7 @@ function createBuiltinAdapter() {
33
33
  if (typeof value === "string") return value || void 0;
34
34
  },
35
35
  async downloadFile(url, destDir, filename) {
36
- const { readRemoteMediaBuffer } = await loadMediaRuntimeModule();
37
- const result = await readRemoteMediaBuffer({
36
+ const result = await (await loadReadRemoteMediaBuffer())({
38
37
  url,
39
38
  filePathHint: filename
40
39
  });
@@ -46,8 +45,7 @@ function createBuiltinAdapter() {
46
45
  return destPath;
47
46
  },
48
47
  async fetchMedia(options) {
49
- const { readRemoteMediaBuffer } = await loadMediaRuntimeModule();
50
- const result = await readRemoteMediaBuffer({
48
+ const result = await (await loadReadRemoteMediaBuffer())({
51
49
  url: options.url,
52
50
  filePathHint: options.filePathHint,
53
51
  maxBytes: options.maxBytes,
@@ -384,26 +382,23 @@ const QQBotDmPolicySchema = z.enum([
384
382
  "allowlist",
385
383
  "disabled"
386
384
  ]).optional();
387
- const QQBotGroupPolicySchema = z.enum([
388
- "open",
389
- "allowlist",
390
- "disabled"
391
- ]).optional();
392
- const QQBotGroupCommandLevelSchema = z.enum([
393
- "all",
394
- "safety",
395
- "strict"
396
- ]).optional();
397
- const QQBotGroupSchema = z.object({
398
- requireMention: z.boolean().optional(),
399
- commandLevel: QQBotGroupCommandLevelSchema,
385
+ const QQBotGroupPolicySchema = GroupPolicySchema.optional();
386
+ const QQBotGroupSchema = buildGroupEntrySchema({
387
+ commandLevel: z.enum([
388
+ "all",
389
+ "safety",
390
+ "strict"
391
+ ]).optional(),
400
392
  ignoreOtherMentions: z.boolean().optional(),
401
393
  historyLimit: z.number().optional(),
402
394
  name: z.string().optional(),
403
- prompt: z.string().optional(),
404
- tools: ToolPolicySchema,
405
- toolsBySender: z.record(z.string(), ToolPolicySchema).optional()
406
- }).strict();
395
+ prompt: z.string().optional()
396
+ }).omit({
397
+ skills: true,
398
+ enabled: true,
399
+ allowFrom: true,
400
+ systemPrompt: true
401
+ });
407
402
  const QQBotGroupsSchema = z.record(z.string(), QQBotGroupSchema).optional();
408
403
  const QQBotAccountSchema = z.object({
409
404
  enabled: z.boolean().optional(),
@@ -426,10 +421,9 @@ const QQBotAccountSchema = z.object({
426
421
  execApprovals: QQBotExecApprovalsSchema,
427
422
  groups: QQBotGroupsSchema
428
423
  }).passthrough();
429
- const qqbotChannelConfigSchema = buildChannelConfigSchema(QQBotAccountSchema.extend({
430
- stt: QQBotSttSchema,
431
- accounts: z.object({}).catchall(QQBotAccountSchema.passthrough()).optional(),
432
- defaultAccount: z.string().optional()
433
- }).passthrough());
424
+ const qqbotChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(QQBotAccountSchema.extend({ stt: QQBotSttSchema }).passthrough(), {
425
+ accountSchema: QQBotAccountSchema,
426
+ accountsMode: "catchall"
427
+ }));
434
428
  //#endregion
435
429
  export { qqbotSetupAdapterShared as a, setBridgeLogger as c, qqbotMeta as i, qqbotSetupWizard as n, ensurePlatformAdapter as o, qqbotConfigAdapter as r, getBridgeLogger as s, qqbotChannelConfigSchema as t };
@@ -1,4 +1,5 @@
1
1
  import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
2
+ import { fileExists } from "openclaw/plugin-sdk/security-runtime";
2
3
  import path from "node:path";
3
4
  import fs from "node:fs/promises";
4
5
  //#region extensions/qqbot/src/state-migrations.ts
@@ -7,13 +8,6 @@ const MAX_CREDENTIAL_BACKUPS = 1e3;
7
8
  function safeName(id) {
8
9
  return id.replace(/[^a-zA-Z0-9._-]/g, "_");
9
10
  }
10
- async function fileExists(filePath) {
11
- try {
12
- return (await fs.lstat(filePath)).isFile();
13
- } catch {
14
- return false;
15
- }
16
- }
17
11
  async function readCredentialBackup(filePath) {
18
12
  try {
19
13
  const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
@@ -42,7 +36,7 @@ async function credentialBackupCandidates(stateDir) {
42
36
  } catch {}
43
37
  accountFiles.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
44
38
  const singlePath = path.join(dataDir, "credential-backup.json");
45
- return await fileExists(singlePath) ? [...accountFiles, { sourcePath: singlePath }] : accountFiles;
39
+ return fileExists(singlePath) ? [...accountFiles, { sourcePath: singlePath }] : accountFiles;
46
40
  }
47
41
  async function readLegacyCredentialBackups(stateDir) {
48
42
  const backups = [];
@@ -59,7 +53,7 @@ async function readLegacyCredentialBackups(stateDir) {
59
53
  }
60
54
  async function archiveLegacySource(params) {
61
55
  const archivedPath = `${params.sourcePath}.migrated`;
62
- if (await fileExists(archivedPath)) {
56
+ if (fileExists(archivedPath)) {
63
57
  params.warnings.push(`Left QQBot credential backup in place because ${archivedPath} already exists`);
64
58
  return;
65
59
  }
@@ -1,13 +1,13 @@
1
- import { B as StreamContentType, F as formatFileSize, H as StreamInputState, L as getImageMimeType, N as downloadFile, R as getMaxUploadSize, V as StreamInputMode, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as openLocalFile, k as getNextMsgSeq, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, w as claimMessageReply, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-0vqsivoI.js";
1
+ import { B as StreamContentType, F as formatFileSize, H as StreamInputState, L as getImageMimeType, N as downloadFile, R as getMaxUploadSize, V as StreamInputMode, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as openLocalFile, k as getNextMsgSeq, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, w as claimMessageReply, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-BAUHZqDW.js";
2
2
  import { c as getPlatformAdapter, i as normalizeOptionalString$1, n as normalizeLowercaseStringOrEmpty$1, o as readStringField, s as sanitizeFileName, t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
3
- import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-B5Mle_87.js";
4
- import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as createQQBotMarkdownChunker, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-BbwpkiY3.js";
3
+ import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-D7MaH5X5.js";
4
+ import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as createQQBotMarkdownChunker, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-nz3Mkify.js";
5
5
  import { d as resolveAccountBase } from "./config-CpOXnoEc.js";
6
6
  import { a as formatErrorMessage, i as formatDuration, n as debugLog, r as debugWarn, t as debugError } from "./log-Da4jz75I.js";
7
- import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-Du28LbOJ.js";
7
+ import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-CyjBiGD2.js";
8
8
  import { t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
9
- import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-Dbpnjalm.js";
10
- import { A as getQQBotMediaDir, C as resolveUserFacingMediaError, D as checkSilkWasmAvailable, E as setOutboundAudioPort, F as normalizePath, M as getTempDir, N as isLocalPath, O as getHomeDir, P as isWindows, S as resolveWorkspaceScopedLocalRoots, _ as sendVideoMsg, b as resolveOutboundMediaLocalRoots, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, k as getQQBotDataDir, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, w as DEFAULT_MEDIA_SEND_ERROR, x as resolveWorkspacePathCandidates, y as resolveTrustedOutboundMediaPath } from "./outbound-BLl8Tsu7.js";
9
+ import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-BVHG8qUZ.js";
10
+ import { A as getQQBotMediaDir, C as resolveUserFacingMediaError, D as checkSilkWasmAvailable, E as setOutboundAudioPort, F as normalizePath, M as getTempDir, N as isLocalPath, O as getHomeDir, P as isWindows, S as resolveWorkspaceScopedLocalRoots, _ as sendVideoMsg, b as resolveOutboundMediaLocalRoots, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, k as getQQBotDataDir, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, w as DEFAULT_MEDIA_SEND_ERROR, x as resolveWorkspacePathCandidates, y as resolveTrustedOutboundMediaPath } from "./outbound-FOG4zNLY.js";
11
11
  import { n as runWithRequestContext } from "./request-context-Bm7PTBD1.js";
12
12
  import { createTypingKeepaliveLoop } from "openclaw/plugin-sdk/channel-outbound";
13
13
  import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -1418,10 +1418,12 @@ var ReconnectState = class {
1418
1418
  };
1419
1419
  //#endregion
1420
1420
  //#region extensions/qqbot/src/engine/gateway/ws-client.ts
1421
+ const QQBOT_WEBSOCKET_HANDSHAKE_TIMEOUT_MS = 3e4;
1421
1422
  async function createQQWSClient(options) {
1422
1423
  const wsAgent = await resolveAmbientNodeProxyAgent();
1423
1424
  return new WebSocket(options.gatewayUrl, {
1424
1425
  headers: { "User-Agent": options.userAgent },
1426
+ handshakeTimeout: QQBOT_WEBSOCKET_HANDSHAKE_TIMEOUT_MS,
1425
1427
  ...wsAgent ? { agent: wsAgent } : {}
1426
1428
  });
1427
1429
  }
@@ -1,11 +1,12 @@
1
1
  import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
2
2
  import { d as resolveAccountBase } from "./config-CpOXnoEc.js";
3
3
  import { n as debugLog } from "./log-Da4jz75I.js";
4
- import { O as getHomeDir, P as isWindows, j as getQQBotMediaPath, k as getQQBotDataDir } from "./outbound-BLl8Tsu7.js";
4
+ import { O as getHomeDir, P as isWindows, j as getQQBotMediaPath, k as getQQBotDataDir } from "./outbound-FOG4zNLY.js";
5
5
  import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  import { formatByteSize } from "openclaw/plugin-sdk/number-runtime";
9
+ import { resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
9
10
  import { clearRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
10
11
  import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
11
12
  //#region extensions/qqbot/src/engine/commands/builtin/state.ts
@@ -666,10 +667,15 @@ function tailFileLines(filePath, maxLines) {
666
667
  const readSize = Math.min(CHUNK_SIZE, position);
667
668
  position -= readSize;
668
669
  const buf = Buffer.alloc(readSize);
669
- fs.readSync(fd, buf, 0, readSize, position);
670
+ let actualRead = 0;
671
+ while (actualRead < readSize) {
672
+ const justRead = fs.readSync(fd, buf, actualRead, readSize - actualRead, position + actualRead);
673
+ if (justRead === 0) throw new Error(`Could not complete log read for ${filePath}`);
674
+ actualRead += justRead;
675
+ }
670
676
  chunks.unshift(buf);
671
- bytesRead += readSize;
672
- for (let i = 0; i < readSize; i++) if (buf[i] === 10) newlineCount++;
677
+ bytesRead += actualRead;
678
+ for (let i = 0; i < actualRead; i++) if (buf[i] === 10) newlineCount++;
673
679
  }
674
680
  const allLines = Buffer.concat(chunks).toString("utf8").split("\n");
675
681
  const tail = allLines.slice(-maxLines);
@@ -1136,11 +1142,20 @@ function readHistoryLimit(obj, key) {
1136
1142
  function resolveGroupConfig(cfg, groupOpenid, accountId) {
1137
1143
  const account = resolveAccountBase(cfg, accountId);
1138
1144
  const groups = readGroupsMap(cfg, accountId);
1139
- const wildcard = groups["*"] ?? {};
1145
+ const { "*": wildcard = {}, ...scopes } = groups;
1140
1146
  const specific = groupOpenid ? groups[groupOpenid] ?? {} : {};
1141
- const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention) ?? DEFAULT_GROUP_CONFIG.requireMention;
1147
+ const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention);
1148
+ const mentionTree = {
1149
+ defaults: { requireMention: readBoolean(wildcard, "requireMention") },
1150
+ scopes: Object.fromEntries(Object.entries(scopes).map(([key, entry]) => [key, { requireMention: readBoolean(entry, "requireMention") }]))
1151
+ };
1142
1152
  return {
1143
- requireMention: readBoolean(specific, "requireMention") ?? readBoolean(wildcard, "requireMention") ?? accountDefaultRequireMention,
1153
+ requireMention: resolveScopeRequireMention({
1154
+ tree: mentionTree,
1155
+ path: groupOpenid && Object.hasOwn(mentionTree.scopes, groupOpenid) ? [groupOpenid] : [],
1156
+ requireMentionOverride: accountDefaultRequireMention,
1157
+ overrideOrder: "after-config"
1158
+ }),
1144
1159
  ignoreOtherMentions: readBoolean(specific, "ignoreOtherMentions") ?? readBoolean(wildcard, "ignoreOtherMentions") ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
1145
1160
  commandLevel: readCommandLevel(specific, "commandLevel") ?? readCommandLevel(wildcard, "commandLevel") ?? DEFAULT_GROUP_CONFIG.commandLevel,
1146
1161
  name: readString(specific, "name") ?? readString(wildcard, "name") ?? DEFAULT_GROUP_CONFIG.name,
@@ -1,6 +1,6 @@
1
- import { c as getMessageApi, t as accountToCreds } from "./sender-0vqsivoI.js";
2
- import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-B5Mle_87.js";
3
- import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-BbwpkiY3.js";
1
+ import { c as getMessageApi, t as accountToCreds } from "./sender-BAUHZqDW.js";
2
+ import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-D7MaH5X5.js";
3
+ import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-nz3Mkify.js";
4
4
  import { a as resolveQQBotAccount } from "./config-CpOXnoEc.js";
5
5
  import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
6
6
  import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
@@ -1,4 +1,4 @@
1
- import { A as UPLOAD_PREPARE_FALLBACK_CODE, C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, F as formatFileSize, I as getFileTypeName, L as getImageMimeType, M as checkFileSize, N as downloadFile, O as UploadDailyLimitExceededError, P as fileExistsAsync, R as getMaxUploadSize, T as getMessageReplyConfig, U as __exportAll, g as sendText$1, h as sendMedia$1, t as accountToCreds, u as initApiConfig, z as readFileAsync } from "./sender-0vqsivoI.js";
1
+ import { A as UPLOAD_PREPARE_FALLBACK_CODE, C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, F as formatFileSize, I as getFileTypeName, L as getImageMimeType, M as checkFileSize, N as downloadFile, O as UploadDailyLimitExceededError, P as fileExistsAsync, R as getMaxUploadSize, T as getMessageReplyConfig, U as __exportAll, g as sendText$1, h as sendMedia$1, t as accountToCreds, u as initApiConfig, z as readFileAsync } from "./sender-BAUHZqDW.js";
2
2
  import { c as getPlatformAdapter, i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
3
3
  import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-Da4jz75I.js";
4
4
  import { r as parseTarget$1 } from "./target-parser-DagYRQkP.js";
@@ -1,4 +1,4 @@
1
- import { v as setOpenClawVersion } from "./sender-0vqsivoI.js";
1
+ import { v as setOpenClawVersion } from "./sender-BAUHZqDW.js";
2
2
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
3
3
  //#region extensions/qqbot/src/bridge/runtime.ts
4
4
  const { setRuntime: _setRuntime, getRuntime: getQQBotRuntime } = createPluginRuntimeStore({
@@ -1,2 +1,2 @@
1
- import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-Du28LbOJ.js";
1
+ import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-CyjBiGD2.js";
2
2
  export { getQQBotRuntime, setQQBotRuntime };
@@ -13,7 +13,7 @@ import { FsSafeError, openLocalFileSafely, readRegularFile, statRegularFileSync
13
13
  import * as path$1 from "node:path";
14
14
  import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
15
15
  import { asDateTimestampMs, formatByteSize, isFutureDateTimestampMs, parseStrictPositiveInteger, resolveExpiresAtMsFromDurationSeconds, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
16
- import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
16
+ import { createChannelApiRetryRunner, resolveRetryConfig } from "openclaw/plugin-sdk/retry-runtime";
17
17
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
18
18
  //#region \0rolldown/runtime.js
19
19
  var __defProp = Object.defineProperty;
@@ -494,7 +494,26 @@ async function normalizeSource(raw, opts = {}) {
494
494
  */
495
495
  async function withRetry(fn, policy, persistentPolicy, logger) {
496
496
  const persistentFailures = /* @__PURE__ */ new WeakSet();
497
- return await retryAsync(async () => {
497
+ const retryConfig = resolveRetryConfig(void 0, {
498
+ attempts: policy.maxRetries + 1,
499
+ minDelayMs: policy.baseDelayMs,
500
+ maxDelayMs: policy.backoff === "fixed" ? policy.baseDelayMs : 2147e6,
501
+ jitter: 0
502
+ });
503
+ return await createChannelApiRetryRunner({
504
+ retry: retryConfig,
505
+ strictShouldRetry: true,
506
+ retryAfterMs: () => void 0,
507
+ shouldRetry: (err, attempt) => {
508
+ const error = err instanceof Error ? err : new Error(formatErrorMessage(err));
509
+ const shouldRetry = !persistentFailures.has(error) && policy.shouldRetry?.(error, attempt - 1) !== false;
510
+ if (shouldRetry) {
511
+ const delayMs = policy.backoff === "fixed" ? retryConfig.minDelayMs : Math.min(retryConfig.minDelayMs * 2 ** (attempt - 1), retryConfig.maxDelayMs);
512
+ logger?.debug?.(`[qqbot:retry] Attempt ${attempt} failed, retrying in ${delayMs}ms: ${truncateUtf16Safe(error.message, 100)}`);
513
+ }
514
+ return shouldRetry;
515
+ }
516
+ })(async () => {
498
517
  try {
499
518
  return await fn();
500
519
  } catch (err) {
@@ -509,20 +528,6 @@ async function withRetry(fn, policy, persistentPolicy, logger) {
509
528
  throw terminal;
510
529
  }
511
530
  }
512
- }, {
513
- attempts: policy.maxRetries + 1,
514
- minDelayMs: 0,
515
- maxDelayMs: 2147e6,
516
- delayMs: ({ attempt }) => policy.backoff === "exponential" ? policy.baseDelayMs * 2 ** (attempt - 1) : policy.baseDelayMs,
517
- shouldRetry: (err, attempt) => {
518
- const error = err instanceof Error ? err : new Error(formatErrorMessage(err));
519
- return !persistentFailures.has(error) && policy.shouldRetry?.(error, attempt - 1) !== false;
520
- },
521
- onRetry: ({ attempt, delayMs, err }) => {
522
- const error = err instanceof Error ? err : new Error(formatErrorMessage(err));
523
- logger?.debug?.(`[qqbot:retry] Attempt ${attempt} failed, retrying in ${delayMs}ms: ${truncateUtf16Safe(error.message, 100)}`);
524
- },
525
- sleep
526
531
  });
527
532
  }
528
533
  /**
@@ -1,2 +1,2 @@
1
- import { t as qqbotSetupPlugin } from "./channel.setup-DfhritPL.js";
1
+ import { t as qqbotSetupPlugin } from "./channel.setup-CNoBtKXQ.js";
2
2
  export { qqbotSetupPlugin };
@@ -203,8 +203,8 @@ async function executeChannelApi(params, options) {
203
203
  "PATCH"
204
204
  ].includes(method)) fetchOptions.body = JSON.stringify(params.body);
205
205
  debugLog(`[qqbot-channel-api] >>> ${method} ${url} (timeout: ${DEFAULT_TIMEOUT_MS}ms)`);
206
- let res;
207
206
  let release;
207
+ let receivedResponse = false;
208
208
  try {
209
209
  const guarded = await fetchWithSsrFGuard({
210
210
  url,
@@ -212,26 +212,9 @@ async function executeChannelApi(params, options) {
212
212
  auditContext: "qqbot-channel-api",
213
213
  policy: resolveChannelApiSsrfPolicy(url)
214
214
  });
215
- res = guarded.response;
216
215
  release = guarded.release;
217
- } catch (err) {
218
- clearTimeout(timeoutId);
219
- if (err instanceof Error && err.name === "AbortError") {
220
- debugError(`[qqbot-channel-api] <<< Request timeout after ${DEFAULT_TIMEOUT_MS}ms`);
221
- return jsonResult({
222
- error: `Request timed out after ${DEFAULT_TIMEOUT_MS}ms`,
223
- path: params.path
224
- });
225
- }
226
- debugError("[qqbot-channel-api] <<< Network error:", err);
227
- return jsonResult({
228
- error: `Network error: ${formatErrorMessage(err)}`,
229
- path: params.path
230
- });
231
- } finally {
232
- clearTimeout(timeoutId);
233
- }
234
- try {
216
+ receivedResponse = true;
217
+ const res = guarded.response;
235
218
  debugLog(`[qqbot-channel-api] <<< Status: ${res.status} ${res.statusText}`);
236
219
  const rawBody = res.ok ? await readProviderTextResponse(res, "QQ channel API response") : await readResponseTextLimited(res, CHANNEL_API_ERROR_BODY_LIMIT_BYTES);
237
220
  if (!rawBody || rawBody.trim() === "") {
@@ -268,7 +251,27 @@ async function executeChannelApi(params, options) {
268
251
  path: params.path,
269
252
  data: parsed
270
253
  });
254
+ } catch (err) {
255
+ if (controller.signal.aborted && err instanceof Error && err.name === "AbortError") {
256
+ debugError(`[qqbot-channel-api] <<< Request timeout after ${DEFAULT_TIMEOUT_MS}ms`);
257
+ return jsonResult({
258
+ error: `Request timed out after ${DEFAULT_TIMEOUT_MS}ms`,
259
+ path: params.path
260
+ });
261
+ }
262
+ if (!receivedResponse) {
263
+ debugError("[qqbot-channel-api] <<< Network error:", err);
264
+ return jsonResult({
265
+ error: `Network error: ${formatErrorMessage(err)}`,
266
+ path: params.path
267
+ });
268
+ }
269
+ return jsonResult({
270
+ error: formatErrorMessage(err),
271
+ path: params.path
272
+ });
271
273
  } finally {
274
+ clearTimeout(timeoutId);
272
275
  await release?.();
273
276
  }
274
277
  } catch (err) {
@@ -301,7 +304,7 @@ function registerChannelTool(api) {
301
304
  description: "Authenticated HTTP proxy for QQ Open Platform channel APIs. Use write and delete endpoints only after explicit user intent; DELETE requires confirmed=true, and bulk deletes require bulkConfirmed=true after confirming the exact target. Common endpoints: list guilds GET /users/@me/guilds | list channels GET /guilds/{guild_id}/channels | get channel GET /channels/{channel_id} | create channel POST /guilds/{guild_id}/channels | list members GET /guilds/{guild_id}/members?after=0&limit=100 | get member GET /guilds/{guild_id}/members/{user_id} | list threads GET /channels/{channel_id}/threads | create thread PUT /channels/{channel_id}/threads | create announce POST /guilds/{guild_id}/announces | create schedule POST /channels/{channel_id}/schedules. See the qqbot-channel skill for full endpoint details.",
302
305
  parameters: ChannelApiSchema,
303
306
  async execute(_toolCallId, params) {
304
- const { getAccessToken } = await import("./sender-0vqsivoI.js").then((n) => n._);
307
+ const { getAccessToken } = await import("./sender-BAUHZqDW.js").then((n) => n._);
305
308
  return executeChannelApi(params, {
306
309
  accessToken: await getAccessToken(account.appId, account.clientSecret),
307
310
  cfg,
package/dist/tools-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as registerQQBotTools } from "./tools-DYb23LBU.js";
1
+ import { t as registerQQBotTools } from "./tools-CC5CKQig.js";
2
2
  export { registerQQBotTools };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.7.2-beta.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/qqbot",
9
- "version": "2026.7.2-beta.1",
9
+ "version": "2026.7.2-beta.2",
10
10
  "dependencies": {
11
11
  "@tencent-connect/qqbot-connector": "1.1.0",
12
12
  "mpg123-decoder": "1.0.3",
@@ -17,7 +17,7 @@
17
17
  "zod": "4.4.3"
18
18
  },
19
19
  "peerDependencies": {
20
- "openclaw": ">=2026.7.2-beta.1"
20
+ "openclaw": ">=2026.7.2-beta.2"
21
21
  },
22
22
  "peerDependenciesMeta": {
23
23
  "openclaw": {
@@ -438,8 +438,8 @@
438
438
  "type": "string",
439
439
  "enum": [
440
440
  "open",
441
- "allowlist",
442
- "disabled"
441
+ "disabled",
442
+ "allowlist"
443
443
  ]
444
444
  },
445
445
  "systemPrompt": {
@@ -562,26 +562,6 @@
562
562
  "requireMention": {
563
563
  "type": "boolean"
564
564
  },
565
- "commandLevel": {
566
- "type": "string",
567
- "enum": [
568
- "all",
569
- "safety",
570
- "strict"
571
- ]
572
- },
573
- "ignoreOtherMentions": {
574
- "type": "boolean"
575
- },
576
- "historyLimit": {
577
- "type": "number"
578
- },
579
- "name": {
580
- "type": "string"
581
- },
582
- "prompt": {
583
- "type": "string"
584
- },
585
565
  "tools": {
586
566
  "type": "object",
587
567
  "properties": {
@@ -635,6 +615,26 @@
635
615
  },
636
616
  "additionalProperties": false
637
617
  }
618
+ },
619
+ "commandLevel": {
620
+ "type": "string",
621
+ "enum": [
622
+ "all",
623
+ "safety",
624
+ "strict"
625
+ ]
626
+ },
627
+ "ignoreOtherMentions": {
628
+ "type": "boolean"
629
+ },
630
+ "historyLimit": {
631
+ "type": "number"
632
+ },
633
+ "name": {
634
+ "type": "string"
635
+ },
636
+ "prompt": {
637
+ "type": "string"
638
638
  }
639
639
  },
640
640
  "additionalProperties": false
@@ -795,8 +795,8 @@
795
795
  "type": "string",
796
796
  "enum": [
797
797
  "open",
798
- "allowlist",
799
- "disabled"
798
+ "disabled",
799
+ "allowlist"
800
800
  ]
801
801
  },
802
802
  "systemPrompt": {
@@ -919,26 +919,6 @@
919
919
  "requireMention": {
920
920
  "type": "boolean"
921
921
  },
922
- "commandLevel": {
923
- "type": "string",
924
- "enum": [
925
- "all",
926
- "safety",
927
- "strict"
928
- ]
929
- },
930
- "ignoreOtherMentions": {
931
- "type": "boolean"
932
- },
933
- "historyLimit": {
934
- "type": "number"
935
- },
936
- "name": {
937
- "type": "string"
938
- },
939
- "prompt": {
940
- "type": "string"
941
- },
942
922
  "tools": {
943
923
  "type": "object",
944
924
  "properties": {
@@ -992,6 +972,26 @@
992
972
  },
993
973
  "additionalProperties": false
994
974
  }
975
+ },
976
+ "commandLevel": {
977
+ "type": "string",
978
+ "enum": [
979
+ "all",
980
+ "safety",
981
+ "strict"
982
+ ]
983
+ },
984
+ "ignoreOtherMentions": {
985
+ "type": "boolean"
986
+ },
987
+ "historyLimit": {
988
+ "type": "number"
989
+ },
990
+ "name": {
991
+ "type": "string"
992
+ },
993
+ "prompt": {
994
+ "type": "string"
995
995
  }
996
996
  },
997
997
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.7.2-beta.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "private": false,
5
5
  "description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
6
6
  "repository": {
@@ -18,7 +18,7 @@
18
18
  "zod": "4.4.3"
19
19
  },
20
20
  "peerDependencies": {
21
- "openclaw": ">=2026.7.2-beta.1"
21
+ "openclaw": ">=2026.7.2-beta.2"
22
22
  },
23
23
  "peerDependenciesMeta": {
24
24
  "openclaw": {
@@ -47,10 +47,10 @@
47
47
  "minHostVersion": ">=2026.4.10"
48
48
  },
49
49
  "compat": {
50
- "pluginApi": ">=2026.7.2-beta.1"
50
+ "pluginApi": ">=2026.7.2-beta.2"
51
51
  },
52
52
  "build": {
53
- "openclawVersion": "2026.7.2-beta.1"
53
+ "openclawVersion": "2026.7.2-beta.2"
54
54
  },
55
55
  "release": {
56
56
  "publishToClawHub": true,