@openclaw/irc 2026.7.1-beta.1 → 2026.7.1-beta.4

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,3 +1,3 @@
1
- import { g as resolveIrcAccount, h as resolveDefaultIrcAccountId, m as listIrcAccountIds, n as ircSetupWizard, p as listEnabledIrcAccounts, r as ircSetupAdapter, t as ircPlugin } from "./channel-X7F4NSHn.js";
1
+ import { g as resolveIrcAccount, h as resolveDefaultIrcAccountId, m as listIrcAccountIds, n as ircSetupWizard, p as listEnabledIrcAccounts, r as ircSetupAdapter, t as ircPlugin } from "./channel-C-P2GolT.js";
2
2
  import { n as setIrcRuntime } from "./runtime-DZfYm3vY.js";
3
3
  export { ircPlugin, ircSetupAdapter, ircSetupWizard, listEnabledIrcAccounts, listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount, setIrcRuntime };
@@ -4,9 +4,10 @@ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries
4
4
  import { createAccountListHelpers, describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
5
5
  import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
6
6
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
7
- import { createChatChannelPlugin, parseOptionalDelimitedEntries, tryReadSecretFileSync } from "openclaw/plugin-sdk/channel-core";
7
+ import { buildChannelOutboundSessionRoute, createChatChannelPlugin, parseOptionalDelimitedEntries, tryReadSecretFileSync } from "openclaw/plugin-sdk/channel-core";
8
8
  import { composeAccountWarningCollectors, createAllowlistProviderOpenWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector } from "openclaw/plugin-sdk/channel-policy";
9
9
  import { createChannelDirectoryAdapter, createResolvedDirectoryEntriesLister } from "openclaw/plugin-sdk/directory-runtime";
10
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
10
11
  import { buildBaseChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
11
12
  import { DEFAULT_ACCOUNT_ID, DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
12
13
  import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
@@ -26,7 +27,7 @@ import { randomUUID } from "node:crypto";
26
27
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
27
28
  import { applyAccountNameToChannelSection, createAllowFromSection, createPromptParsedAllowFromForAccount, createSetupInputPresenceValidator, createSetupTranslator, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicySetter, formatDocsLink, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
28
29
  //#region extensions/irc/src/accounts.ts
29
- const TRUTHY_ENV = new Set([
30
+ const TRUTHY_ENV = /* @__PURE__ */ new Set([
30
31
  "true",
31
32
  "1",
32
33
  "yes",
@@ -196,11 +197,7 @@ const collectIrcMutableAllowlistWarnings = createDangerousNameMatchingMutableAll
196
197
  });
197
198
  //#endregion
198
199
  //#region extensions/irc/src/gateway.ts
199
- let ircChannelRuntimePromise$1;
200
- async function loadIrcChannelRuntime$1() {
201
- ircChannelRuntimePromise$1 ??= import("./channel-runtime-BUK62iQX.js");
202
- return await ircChannelRuntimePromise$1;
203
- }
200
+ const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-CLTE89ol.js"));
204
201
  async function startIrcGatewayAccount(ctx) {
205
202
  const account = ctx.account;
206
203
  const statusSink = createAccountStatusSink({
@@ -324,12 +321,30 @@ function makeIrcMessageId() {
324
321
  }
325
322
  //#endregion
326
323
  //#region extensions/irc/src/client.ts
327
- const IRC_ERROR_CODES = new Set([
324
+ const IRC_ERROR_CODES = /* @__PURE__ */ new Set([
328
325
  "432",
329
326
  "464",
330
327
  "465"
331
328
  ]);
332
- const IRC_NICK_COLLISION_CODES = new Set(["433", "436"]);
329
+ const IRC_NICK_COLLISION_CODES = /* @__PURE__ */ new Set(["433", "436"]);
330
+ const IRC_MAX_LINE_BYTES = 512;
331
+ function takeIrcPrivmsgChunk(text, maxChars, maxBytes) {
332
+ let end = 0;
333
+ let bytes = 0;
334
+ for (const codePoint of text) {
335
+ const codePointBytes = Buffer.byteLength(codePoint, "utf8");
336
+ if (end > 0 && end + codePoint.length > maxChars || bytes + codePointBytes > maxBytes) break;
337
+ end += codePoint.length;
338
+ bytes += codePointBytes;
339
+ }
340
+ if (end === 0) throw new Error("IRC target leaves no room for message text within the 512-byte line limit");
341
+ if (end === text.length) return text;
342
+ const fitted = text.slice(0, end);
343
+ if (text[end] === " ") return fitted;
344
+ const splitAt = fitted.lastIndexOf(" ");
345
+ if (splitAt >= Math.floor(fitted.length / 2)) return fitted.slice(0, splitAt);
346
+ return fitted;
347
+ }
333
348
  function toError(err) {
334
349
  if (err instanceof Error) return err;
335
350
  return new Error(typeof err === "string" ? err : JSON.stringify(err));
@@ -361,7 +376,7 @@ function buildIrcNickServCommands(options) {
361
376
  }
362
377
  async function connectIrcClient(options) {
363
378
  const timeoutMs = options.connectTimeoutMs != null ? options.connectTimeoutMs : 15e3;
364
- const messageChunkMaxChars = options.messageChunkMaxChars != null ? options.messageChunkMaxChars : 350;
379
+ const messageChunkMaxChars = Math.max(1, Math.floor(options.messageChunkMaxChars ?? 350));
365
380
  if (!options.host.trim()) throw new Error("IRC host is required");
366
381
  if (!options.nick.trim()) throw new Error("IRC nick is required");
367
382
  const desiredNick = options.nick.trim();
@@ -410,7 +425,8 @@ async function connectIrcClient(options) {
410
425
  if (nickServEnabled && !nickServRecoverAttempted && nickservPassword) {
411
426
  nickServRecoverAttempted = true;
412
427
  try {
413
- sendRaw(`PRIVMSG ${sanitizeIrcTarget(options.nickserv?.service?.trim() || "NickServ")} :GHOST ${desiredNick} ${nickservPassword}`);
428
+ const service = sanitizeIrcTarget(options.nickserv?.service?.trim() || "NickServ");
429
+ sendRaw(`PRIVMSG ${service} :GHOST ${desiredNick} ${nickservPassword}`);
414
430
  sendRaw(`NICK ${desiredNick}`);
415
431
  return true;
416
432
  } catch (err) {
@@ -439,15 +455,11 @@ async function connectIrcClient(options) {
439
455
  const normalizedTarget = sanitizeIrcTarget(target);
440
456
  const cleaned = sanitizeIrcOutboundText(text);
441
457
  if (!cleaned) return;
458
+ const lineOverheadBytes = Buffer.byteLength(`PRIVMSG ${normalizedTarget} :\r\n`, "utf8");
459
+ const maxChunkBytes = IRC_MAX_LINE_BYTES - lineOverheadBytes;
442
460
  let remaining = cleaned;
443
461
  while (remaining.length > 0) {
444
- let chunk = remaining;
445
- if (chunk.length > messageChunkMaxChars) {
446
- let splitAt = chunk.lastIndexOf(" ", messageChunkMaxChars);
447
- if (splitAt < Math.floor(messageChunkMaxChars / 2)) splitAt = messageChunkMaxChars;
448
- chunk = chunk.slice(0, splitAt).trim();
449
- }
450
- if (!chunk) break;
462
+ const chunk = takeIrcPrivmsgChunk(remaining, messageChunkMaxChars, maxChunkBytes).trim();
451
463
  sendRaw(`PRIVMSG ${normalizedTarget} :${chunk}`);
452
464
  remaining = remaining.slice(chunk.length).trimStart();
453
465
  }
@@ -484,7 +496,8 @@ async function connectIrcClient(options) {
484
496
  const line = parseIrcLine(rawLine);
485
497
  if (!line) continue;
486
498
  if (line.command === "PING") {
487
- sendRaw(`PONG :${line.trailing != null ? line.trailing : line.params[0] != null ? line.params[0] : ""}`);
499
+ const payload = line.trailing != null ? line.trailing : line.params[0] != null ? line.params[0] : "";
500
+ sendRaw(`PONG :${payload}`);
488
501
  continue;
489
502
  }
490
503
  if (line.command === "NICK") {
@@ -569,7 +582,10 @@ async function connectIrcClient(options) {
569
582
  socket.once("close", () => {
570
583
  if (!closed) {
571
584
  closed = true;
585
+ removeAbortListener?.();
586
+ removeAbortListener = null;
572
587
  if (!ready) fail(/* @__PURE__ */ new Error("IRC connection closed before ready"));
588
+ else options.onDisconnect?.();
573
589
  }
574
590
  });
575
591
  if (options.abortSignal) {
@@ -586,7 +602,12 @@ async function connectIrcClient(options) {
586
602
  removeAbortListener = () => options.abortSignal?.removeEventListener("abort", abort);
587
603
  }
588
604
  }
589
- await withTimeout(readyPromise, timeoutMs, "IRC connect");
605
+ try {
606
+ await withTimeout(readyPromise, timeoutMs, "IRC connect");
607
+ } catch (error) {
608
+ close();
609
+ throw error;
610
+ }
590
611
  return {
591
612
  get nick() {
592
613
  return currentNick;
@@ -639,6 +660,25 @@ function normalizeIrcMessagingTarget(raw) {
639
660
  if (!target || !looksLikeIrcTargetId(target)) return;
640
661
  return target;
641
662
  }
663
+ function resolveIrcOutboundSessionRoute(params) {
664
+ const target = normalizeIrcMessagingTarget(params.target);
665
+ if (!target) return null;
666
+ const chatType = isChannelTarget(target) ? "group" : "direct";
667
+ return buildChannelOutboundSessionRoute({
668
+ cfg: params.cfg,
669
+ agentId: params.agentId,
670
+ channel: "irc",
671
+ accountId: params.accountId,
672
+ recipientSessionExact: chatType === "direct" ? "direct-alias" : false,
673
+ peer: {
674
+ kind: chatType,
675
+ id: target
676
+ },
677
+ chatType,
678
+ from: `irc:${target}`,
679
+ to: target
680
+ });
681
+ }
642
682
  function looksLikeIrcTargetId(raw) {
643
683
  const trimmed = raw.trim();
644
684
  if (!trimmed) return false;
@@ -906,21 +946,22 @@ const ircSetupAdapter = {
906
946
  name: setupInput.name
907
947
  });
908
948
  const portInput = typeof setupInput.port === "number" ? String(setupInput.port) : setupInput.port ?? "";
949
+ const patch = {
950
+ enabled: true,
951
+ host: setupInput.host?.trim(),
952
+ port: portInput ? parsePort(portInput, setupInput.tls === false ? 6667 : 6697) : void 0,
953
+ tls: setupInput.tls,
954
+ nick: setupInput.nick?.trim(),
955
+ username: setupInput.username?.trim(),
956
+ realname: setupInput.realname?.trim(),
957
+ password: setupInput.password?.trim(),
958
+ channels: setupInput.channels
959
+ };
909
960
  return patchScopedAccountConfig({
910
961
  cfg: namedConfig,
911
962
  channelKey: channel$1,
912
963
  accountId,
913
- patch: {
914
- enabled: true,
915
- host: setupInput.host?.trim(),
916
- port: portInput ? parsePort(portInput, setupInput.tls === false ? 6667 : 6697) : void 0,
917
- tls: setupInput.tls,
918
- nick: setupInput.nick?.trim(),
919
- username: setupInput.username?.trim(),
920
- realname: setupInput.realname?.trim(),
921
- password: setupInput.password?.trim(),
922
- channels: setupInput.channels
923
- }
964
+ patch
924
965
  });
925
966
  }
926
967
  };
@@ -1289,11 +1330,7 @@ const meta = {
1289
1330
  systemImage: "number",
1290
1331
  markdownCapable: true
1291
1332
  };
1292
- let ircChannelRuntimePromise;
1293
- async function loadIrcChannelRuntime() {
1294
- ircChannelRuntimePromise ??= import("./channel-runtime-BUK62iQX.js");
1295
- return await ircChannelRuntimePromise;
1296
- }
1333
+ const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-CLTE89ol.js"));
1297
1334
  function normalizePairingTarget(raw) {
1298
1335
  const normalized = normalizeIrcAllowEntry(raw);
1299
1336
  if (!normalized) return "";
@@ -1430,6 +1467,7 @@ const ircPlugin = createChatChannelPlugin({
1430
1467
  messaging: {
1431
1468
  targetPrefixes: ["irc"],
1432
1469
  normalizeTarget: normalizeIrcMessagingTarget,
1470
+ resolveOutboundSessionRoute: (params) => resolveIrcOutboundSessionRoute(params),
1433
1471
  targetResolver: {
1434
1472
  looksLikeId: looksLikeIrcTargetId,
1435
1473
  hint: "<#channel|nick>"
@@ -1,2 +1,2 @@
1
- import { t as ircPlugin } from "./channel-X7F4NSHn.js";
1
+ import { t as ircPlugin } from "./channel-C-P2GolT.js";
2
2
  export { ircPlugin };
@@ -1,4 +1,4 @@
1
- import { a as resolveIrcRequireMention, c as isChannelTarget, d as connectIrcClient, f as makeIrcMessageId, g as resolveIrcAccount, i as resolveIrcGroupMatch, l as normalizeIrcAllowEntry, o as sendMessageIrc, s as buildIrcAllowlistCandidates, u as buildIrcConnectOptions } from "./channel-X7F4NSHn.js";
1
+ import { a as resolveIrcRequireMention, c as isChannelTarget, d as connectIrcClient, f as makeIrcMessageId, g as resolveIrcAccount, i as resolveIrcGroupMatch, l as normalizeIrcAllowEntry, o as sendMessageIrc, s as buildIrcAllowlistCandidates, u as buildIrcConnectOptions } from "./channel-C-P2GolT.js";
2
2
  import { t as getIrcRuntime } from "./runtime-DZfYm3vY.js";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared";
@@ -331,6 +331,7 @@ async function handleIrcInbound(params) {
331
331
  }
332
332
  //#endregion
333
333
  //#region extensions/irc/src/monitor.ts
334
+ const IRC_MONITOR_RECONNECT_DELAY_MS = 1e3;
334
335
  function resolveIrcInboundTarget(params) {
335
336
  const rawTarget = params.target;
336
337
  if (isChannelTarget(rawTarget)) return {
@@ -358,67 +359,114 @@ async function monitorIrcProvider(opts) {
358
359
  accountId: account.accountId
359
360
  });
360
361
  let client = null;
361
- client = await connectIrcClient(buildIrcConnectOptions(account, {
362
- channels: account.config.channels,
363
- abortSignal: opts.abortSignal,
364
- onLine: (line) => {
365
- if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] << ${line}`);
366
- },
367
- onNotice: (text, target) => {
368
- if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] notice ${target ?? ""}: ${text}`);
369
- },
370
- onError: (error) => {
371
- logger.error(`[${account.accountId}] IRC error: ${error.message}`);
372
- },
373
- onPrivmsg: async (event) => {
374
- if (!client) return;
375
- if (normalizeLowercaseStringOrEmpty(event.senderNick) === normalizeLowercaseStringOrEmpty(client.nick)) return;
376
- const inboundTarget = resolveIrcInboundTarget({
377
- target: event.target,
378
- senderNick: event.senderNick
379
- });
380
- const message = {
381
- messageId: makeIrcMessageId(),
382
- target: inboundTarget.target,
383
- rawTarget: inboundTarget.rawTarget,
384
- senderNick: event.senderNick,
385
- senderUser: event.senderUser,
386
- senderHost: event.senderHost,
387
- text: event.text,
388
- timestamp: Date.now(),
389
- isGroup: inboundTarget.isGroup
390
- };
391
- core.channel.activity.record({
392
- channel: "irc",
393
- accountId: account.accountId,
394
- direction: "inbound",
395
- at: message.timestamp
362
+ let reconnectTimer = null;
363
+ let stopped = false;
364
+ const monitorAbort = new AbortController();
365
+ let removeAbortListener = null;
366
+ if (opts.abortSignal) {
367
+ const forwardAbort = () => monitorAbort.abort();
368
+ if (opts.abortSignal.aborted) forwardAbort();
369
+ else {
370
+ opts.abortSignal.addEventListener("abort", forwardAbort, { once: true });
371
+ removeAbortListener = () => opts.abortSignal?.removeEventListener("abort", forwardAbort);
372
+ }
373
+ }
374
+ function scheduleReconnect() {
375
+ if (stopped || monitorAbort.signal.aborted || reconnectTimer) return;
376
+ reconnectTimer = setTimeout(() => {
377
+ reconnectTimer = null;
378
+ connect().catch((error) => {
379
+ if (stopped || monitorAbort.signal.aborted) return;
380
+ const message = error instanceof Error ? error.message : String(error);
381
+ logger.error(`[${account.accountId}] IRC reconnect failed: ${message}`);
382
+ scheduleReconnect();
396
383
  });
397
- if (opts.onMessage) {
398
- await opts.onMessage(message, client);
399
- return;
384
+ }, IRC_MONITOR_RECONNECT_DELAY_MS);
385
+ }
386
+ async function connect() {
387
+ if (stopped || monitorAbort.signal.aborted) return;
388
+ const nextClient = await connectIrcClient(buildIrcConnectOptions(account, {
389
+ channels: account.config.channels,
390
+ abortSignal: monitorAbort.signal,
391
+ onLine: (line) => {
392
+ if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] << ${line}`);
393
+ },
394
+ onNotice: (text, target) => {
395
+ if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] notice ${target ?? ""}: ${text}`);
396
+ },
397
+ onError: (error) => {
398
+ logger.error(`[${account.accountId}] IRC error: ${error.message}`);
399
+ },
400
+ onDisconnect: () => {
401
+ if (stopped || monitorAbort.signal.aborted) return;
402
+ client = null;
403
+ logger.warn?.(`[${account.accountId}] IRC connection closed; reconnecting in ${IRC_MONITOR_RECONNECT_DELAY_MS}ms`);
404
+ scheduleReconnect();
405
+ },
406
+ onPrivmsg: async (event) => {
407
+ if (!client) return;
408
+ if (normalizeLowercaseStringOrEmpty(event.senderNick) === normalizeLowercaseStringOrEmpty(client.nick)) return;
409
+ const inboundTarget = resolveIrcInboundTarget({
410
+ target: event.target,
411
+ senderNick: event.senderNick
412
+ });
413
+ const message = {
414
+ messageId: makeIrcMessageId(),
415
+ target: inboundTarget.target,
416
+ rawTarget: inboundTarget.rawTarget,
417
+ senderNick: event.senderNick,
418
+ senderUser: event.senderUser,
419
+ senderHost: event.senderHost,
420
+ text: event.text,
421
+ timestamp: Date.now(),
422
+ isGroup: inboundTarget.isGroup
423
+ };
424
+ core.channel.activity.record({
425
+ channel: "irc",
426
+ accountId: account.accountId,
427
+ direction: "inbound",
428
+ at: message.timestamp
429
+ });
430
+ if (opts.onMessage) {
431
+ await opts.onMessage(message, client);
432
+ return;
433
+ }
434
+ await handleIrcInbound({
435
+ message,
436
+ account,
437
+ config: cfg,
438
+ runtime,
439
+ connectedNick: client.nick,
440
+ sendReply: async (target, text) => {
441
+ client?.sendPrivmsg(target, text);
442
+ opts.statusSink?.({ lastOutboundAt: Date.now() });
443
+ core.channel.activity.record({
444
+ channel: "irc",
445
+ accountId: account.accountId,
446
+ direction: "outbound"
447
+ });
448
+ },
449
+ statusSink: opts.statusSink
450
+ });
400
451
  }
401
- await handleIrcInbound({
402
- message,
403
- account,
404
- config: cfg,
405
- runtime,
406
- connectedNick: client.nick,
407
- sendReply: async (target, text) => {
408
- client?.sendPrivmsg(target, text);
409
- opts.statusSink?.({ lastOutboundAt: Date.now() });
410
- core.channel.activity.record({
411
- channel: "irc",
412
- accountId: account.accountId,
413
- direction: "outbound"
414
- });
415
- },
416
- statusSink: opts.statusSink
417
- });
452
+ }));
453
+ if (stopped || monitorAbort.signal.aborted) {
454
+ nextClient.quit("shutdown");
455
+ return;
418
456
  }
419
- }));
420
- logger.info(`[${account.accountId}] connected to ${account.host}:${account.port}${account.tls ? " (tls)" : ""} as ${client.nick}`);
457
+ client = nextClient;
458
+ logger.info(`[${account.accountId}] connected to ${account.host}:${account.port}${account.tls ? " (tls)" : ""} as ${nextClient.nick}`);
459
+ }
460
+ await connect();
421
461
  return { stop: () => {
462
+ stopped = true;
463
+ removeAbortListener?.();
464
+ removeAbortListener = null;
465
+ if (!monitorAbort.signal.aborted) monitorAbort.abort();
466
+ if (reconnectTimer) {
467
+ clearTimeout(reconnectTimer);
468
+ reconnectTimer = null;
469
+ }
422
470
  client?.quit("shutdown");
423
471
  client = null;
424
472
  } };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/irc",
3
- "version": "2026.7.1-beta.1",
3
+ "version": "2026.7.1-beta.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/irc",
9
- "version": "2026.7.1-beta.1",
9
+ "version": "2026.7.1-beta.4",
10
10
  "dependencies": {
11
11
  "zod": "4.4.3"
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/irc",
3
- "version": "2026.7.1-beta.1",
3
+ "version": "2026.7.1-beta.4",
4
4
  "description": "OpenClaw IRC channel plugin",
5
5
  "type": "module",
6
6
  "openclaw": {
@@ -39,10 +39,10 @@
39
39
  }
40
40
  },
41
41
  "compat": {
42
- "pluginApi": ">=2026.7.1-beta.1"
42
+ "pluginApi": ">=2026.7.1-beta.4"
43
43
  },
44
44
  "build": {
45
- "openclawVersion": "2026.7.1-beta.1",
45
+ "openclawVersion": "2026.7.1-beta.4",
46
46
  "bundledDist": false
47
47
  },
48
48
  "release": {
@@ -68,7 +68,7 @@
68
68
  "README.md"
69
69
  ],
70
70
  "peerDependencies": {
71
- "openclaw": ">=2026.7.1-beta.1"
71
+ "openclaw": ">=2026.7.1-beta.4"
72
72
  },
73
73
  "peerDependenciesMeta": {
74
74
  "openclaw": {