@openclaw/irc 2026.10.5-beta.3 → 2026.10.10-beta.1

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.
@@ -10,7 +10,7 @@ import { defineStableChannelIngressIdentity, identityEntryAuthenticationClassifi
10
10
  import { buildMutableAllowEntryDetector, collectStandardAllowlistLists, createAllowlistProviderOpenWarningCollector, createConditionalWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, resolveScopeKeyCaseInsensitive, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
11
11
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
12
12
  import { createChannelDirectoryAdapter, createResolvedDirectoryEntriesLister } from "openclaw/plugin-sdk/directory-runtime";
13
- import { buildBaseChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
13
+ import { buildProbeChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
14
14
  import { resolveAccountWithDefaultFallback } from "openclaw/plugin-sdk/account-core";
15
15
  import { parseTcpPort } from "openclaw/plugin-sdk/number-runtime";
16
16
  import { isTruthyEnvValue } from "openclaw/plugin-sdk/runtime-env";
@@ -207,7 +207,7 @@ async function startIrcGatewayAccount(ctx) {
207
207
  });
208
208
  if (!account.configured) throw new Error(`IRC is not configured for account "${account.accountId}" (need host and nick in channels.irc).`);
209
209
  ctx.log?.info?.(`[${account.accountId}] starting IRC provider (${account.host}:${account.port}${account.tls ? " tls" : ""})`);
210
- const { monitorIrcProvider } = await import("./channel-runtime-FNBqToAo.mjs");
210
+ const { monitorIrcProvider } = await import("./channel-runtime-CxEHlDNB.mjs");
211
211
  await runPassiveAccountLifecycle({
212
212
  abortSignal: ctx.abortSignal,
213
213
  start: async () => await monitorIrcProvider({
@@ -403,12 +403,6 @@ function getGraphemeSegmenter() {
403
403
  graphemeSegmenter ??= new Intl.Segmenter(void 0, { granularity: "grapheme" });
404
404
  return graphemeSegmenter;
405
405
  }
406
- /** Uses the same source text that created segments. */
407
- function containingSegment(segments, text, index) {
408
- const position = Number.isNaN(index) ? 0 : Math.trunc(index);
409
- const codePoint = text.codePointAt(position);
410
- return segments.containing(codePoint !== void 0 && codePoint > 65535 ? position + 1 : position);
411
- }
412
406
  /**
413
407
  * Chooses a whole-grapheme cut within the hard budget, honoring a usable preference.
414
408
  * If no whole grapheme fits, allowPartial permits a surrogate-safe progress cut;
@@ -423,8 +417,8 @@ function findGraphemeChunkEnd(text, start, maxEnd, preferredEnd = maxEnd, allowP
423
417
  const after = text.charCodeAt(preferred);
424
418
  if (Number.isInteger(preferred) && before <= 127 && after <= 127 && !(before === 13 && after === 10)) return preferred;
425
419
  const segments = getGraphemeSegmenter().segment(text);
426
- let end = containingSegment(segments, text, preferred)?.index ?? preferred;
427
- if (end <= start && preferred < hardEnd) end = hardEnd === text.length ? hardEnd : containingSegment(segments, text, hardEnd)?.index ?? hardEnd;
420
+ let end = segments.containing(preferred)?.index ?? preferred;
421
+ if (end <= start && preferred < hardEnd) end = hardEnd === text.length ? hardEnd : segments.containing(hardEnd)?.index ?? hardEnd;
428
422
  return end > start ? end : allowPartial ? avoidTrailingHighSurrogateBreak(text, start, hardEnd) : start;
429
423
  }
430
424
  //#endregion
@@ -742,7 +736,8 @@ async function connectIrcClient(options) {
742
736
  const target = line.params[0]?.trim() ?? "";
743
737
  const text = line.trailing ?? line.params[1] ?? "";
744
738
  const senderNick = parseIrcPrefix(line.prefix).nick?.trim() ?? "";
745
- if (!target || !senderNick || !text.trim()) continue;
739
+ const isCtcpQuery = text.startsWith("") && !text.toUpperCase().startsWith("ACTION ");
740
+ if (!target || !senderNick || !text.trim() || isCtcpQuery) continue;
746
741
  if (options.onPrivmsg) Promise.resolve(options.onPrivmsg({
747
742
  senderNick,
748
743
  connectedNick: currentNick,
@@ -1242,6 +1237,9 @@ function normalizeGroupEntry(raw) {
1242
1237
  if (isChannelTarget(normalized)) return normalized;
1243
1238
  return `#${normalized.replace(/^#+/, "")}`;
1244
1239
  }
1240
+ function parseAutoJoinChannels(value) {
1241
+ return splitSetupEntries(value).map(normalizeGroupEntry).filter((entry) => Boolean(entry && entry !== "*")).filter(isChannelTarget);
1242
+ }
1245
1243
  const promptIrcAllowFrom = createPromptParsedAllowFromForAccount({
1246
1244
  defaultAccountId: resolveDefaultIrcAccountId,
1247
1245
  noteTitle: t("wizard.irc.allowlistTitle"),
@@ -1443,9 +1441,9 @@ const ircSetupWizard = {
1443
1441
  accountId
1444
1442
  }).config.channels?.join(", "),
1445
1443
  shouldPrompt: ({ credentialValues }) => credentialValues[USE_ENV_FLAG] !== "1",
1446
- normalizeValue: ({ value }) => splitSetupEntries(value).map((entry) => normalizeGroupEntry(entry)).filter((entry) => Boolean(entry && entry !== "*")).filter((entry) => isChannelTarget(entry)).join(", "),
1444
+ normalizeValue: ({ value }) => parseAutoJoinChannels(value).join(", "),
1447
1445
  applySet: async ({ cfg, accountId, value }) => {
1448
- const channels = splitSetupEntries(value).map((entry) => normalizeGroupEntry(entry)).filter((entry) => Boolean(entry && entry !== "*")).filter((entry) => isChannelTarget(entry));
1446
+ const channels = parseAutoJoinChannels(value);
1449
1447
  return updateIrcAccountConfig(cfg, accountId, {
1450
1448
  enabled: true,
1451
1449
  channels: channels.length > 0 ? channels : void 0
@@ -1740,13 +1738,11 @@ const ircPlugin = createChatChannelPlugin({
1740
1738
  status: createComputedAccountStatusAdapter({
1741
1739
  defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
1742
1740
  buildChannelSummary: ({ account, snapshot }) => ({
1743
- ...buildBaseChannelStatusSummary(snapshot),
1741
+ ...buildProbeChannelStatusSummary(snapshot),
1744
1742
  host: account.host,
1745
1743
  port: snapshot.port,
1746
1744
  tls: account.tls,
1747
- nick: account.nick,
1748
- probe: snapshot.probe,
1749
- lastProbeAt: snapshot.lastProbeAt ?? null
1745
+ nick: account.nick
1750
1746
  }),
1751
1747
  probeAccount: async ({ cfg, account, timeoutMs }) => probeIrc(cfg, {
1752
1748
  accountId: account.accountId,
@@ -1779,7 +1775,7 @@ const ircPlugin = createChatChannelPlugin({
1779
1775
  notify: async ({ cfg, id, message, accountId }) => {
1780
1776
  const target = normalizePairingTarget(id);
1781
1777
  if (!target) throw new Error(`invalid IRC pairing id: ${id}`);
1782
- const { sendMessageIrc } = await import("./channel-runtime-FNBqToAo.mjs");
1778
+ const { sendMessageIrc } = await import("./channel-runtime-CxEHlDNB.mjs");
1783
1779
  await sendMessageIrc(target, message, {
1784
1780
  cfg,
1785
1781
  accountId
@@ -0,0 +1,2 @@
1
+ export { t as ircPlugin } from "./channel-BBSRLy1I.mjs";
2
+ import "../channel-plugin-api.js";
@@ -1,4 +1,4 @@
1
- import { a as resolveIrcGroupRequireMention, c as connectIrcClient, d as sanitizeIrcAssistantText, f as createIrcIngressSubject, h as normalizeIrcAllowEntry, i as resolveIrcGroupMatch, l as parseIrcLine, m as isChannelTarget, o as sendMessageIrc, p as ircIngressIdentity, s as buildIrcConnectOptions, u as parseIrcPrefix, y as resolveIrcAccount } from "./channel-BXrFfvr6.mjs";
1
+ import { a as resolveIrcGroupRequireMention, c as connectIrcClient, d as sanitizeIrcAssistantText, f as createIrcIngressSubject, h as normalizeIrcAllowEntry, i as resolveIrcGroupMatch, l as parseIrcLine, m as isChannelTarget, o as sendMessageIrc, p as ircIngressIdentity, s as buildIrcConnectOptions, u as parseIrcPrefix, y as resolveIrcAccount } from "./channel-BBSRLy1I.mjs";
2
2
  import { t as getIrcRuntime } from "./runtime-C_V9tmpk.mjs";
3
3
  import { channelIngressRoutes } from "openclaw/plugin-sdk/channel-ingress-runtime";
4
4
  import { computeBackoff } from "openclaw/plugin-sdk/runtime-env";
@@ -560,7 +560,10 @@ async function monitorIrcProvider(opts) {
560
560
  });
561
561
  function scheduleReconnect() {
562
562
  if (stopped || abortSignal.aborted || reconnectTimer) return;
563
- opts.statusSink?.({ lifecycle: "recovering" });
563
+ opts.statusSink?.({
564
+ connected: false,
565
+ lifecycle: "recovering"
566
+ });
564
567
  const delayMs = reconnectBackoff.nextDelayMs();
565
568
  reconnectTimer = setTimeout(() => {
566
569
  reconnectTimer = null;
@@ -568,6 +571,7 @@ async function monitorIrcProvider(opts) {
568
571
  if (stopped || abortSignal.aborted) return;
569
572
  const message = error instanceof Error ? error.message : String(error);
570
573
  logger.error(`[${account.accountId}] IRC reconnect failed: ${message}`);
574
+ opts.statusSink?.({ lastError: message });
571
575
  scheduleReconnect();
572
576
  });
573
577
  }, delayMs);
@@ -579,11 +583,8 @@ async function monitorIrcProvider(opts) {
579
583
  const nextClient = await connectIrcClient(buildIrcConnectOptions(account, {
580
584
  channels: account.config.channels,
581
585
  abortSignal,
582
- onLine: (line) => {
583
- if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] << ${line}`);
584
- },
585
586
  onNotice: (text, target) => {
586
- if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] notice ${target ?? ""}: ${text}`);
587
+ if (core.logging.shouldLogVerbose()) logger.debug?.(`[${account.accountId}] notice ${target ?? ""} len=${text.length}`);
587
588
  },
588
589
  onError: (error) => {
589
590
  logger.error(`[${account.accountId}] IRC error: ${error.message}`);
@@ -593,6 +594,7 @@ async function monitorIrcProvider(opts) {
593
594
  ingressPause = ingress.pause();
594
595
  if (activeConnectionEpoch === ingressConnection.connectionEpoch) activeConnectionEpoch = null;
595
596
  client = null;
597
+ opts.statusSink?.({ lastDisconnect: { at: Date.now() } });
596
598
  const delayMs = scheduleReconnect();
597
599
  if (delayMs !== void 0) logger.warn?.(`[${account.accountId}] IRC connection closed; reconnecting in ${delayMs}ms`);
598
600
  },
@@ -0,0 +1,2 @@
1
+ export { t as channelSecrets, n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-C5i-29Hz.mjs";
2
+ import "../secret-contract-api.js";
package/dist/api.js CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as listIrcAccountIds, g as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin, v as resolveDefaultIrcAccountId, y as resolveIrcAccount } from "./.setup/channel-BXrFfvr6.mjs";
1
+ import { _ as listIrcAccountIds, g as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin, v as resolveDefaultIrcAccountId, y as resolveIrcAccount } from "./.setup/channel-BBSRLy1I.mjs";
2
2
  import { r as setIrcRuntime } from "./.setup/runtime-C_V9tmpk.mjs";
3
3
  export { ircPlugin, ircSetupAdapter, ircSetupWizard, listEnabledIrcAccounts, listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount, setIrcRuntime };
@@ -1,2 +1 @@
1
- import { t as IrcChannelConfigSchema } from "./.setup/config-schema-Cy9tItsu.mjs";
2
- export { IrcChannelConfigSchema };
1
+ export { t as IrcChannelConfigSchema } from "./.setup/config-schema-Cy9tItsu.mjs";
@@ -1,2 +1 @@
1
- import { t as ircPlugin } from "./.setup/channel-BXrFfvr6.mjs";
2
- export { ircPlugin };
1
+ export { t as ircPlugin } from "./.setup/channel-BBSRLy1I.mjs";
@@ -1,2 +1 @@
1
- import { r as setIrcRuntime } from "./.setup/runtime-C_V9tmpk.mjs";
2
- export { setIrcRuntime };
1
+ export { r as setIrcRuntime } from "./.setup/runtime-C_V9tmpk.mjs";
@@ -1,2 +1 @@
1
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./.setup/secret-contract-C5i-29Hz.mjs";
2
- export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
1
+ export { t as channelSecrets, n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./.setup/secret-contract-C5i-29Hz.mjs";
@@ -4,11 +4,11 @@ import { fileURLToPath } from "node:url";
4
4
  var setup_entry_default = defineBundledChannelSetupEntry({
5
5
  importMetaUrl: import.meta.url,
6
6
  plugin: {
7
- specifier: fileURLToPath(new URL(".setup/channel-plugin-api-tb7ep4Ml.mjs", import.meta.url).href),
7
+ specifier: fileURLToPath(new URL(".setup/channel-plugin-api-8bCK12Fl.mjs", import.meta.url).href),
8
8
  exportName: "ircPlugin"
9
9
  },
10
10
  secrets: {
11
- specifier: fileURLToPath(new URL(".setup/secret-contract-api-ClqD0Rpm.mjs", import.meta.url).href),
11
+ specifier: fileURLToPath(new URL(".setup/secret-contract-api-4IT9_EK6.mjs", import.meta.url).href),
12
12
  exportName: "channelSecrets"
13
13
  }
14
14
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/irc",
3
- "version": "2026.10.5-beta.3",
3
+ "version": "2026.10.10-beta.1",
4
4
  "description": "OpenClaw IRC channel plugin",
5
5
  "type": "module",
6
6
  "openclaw": {
@@ -121,10 +121,10 @@
121
121
  }
122
122
  },
123
123
  "compat": {
124
- "pluginApi": ">=2026.10.5-beta.3"
124
+ "pluginApi": ">=2026.10.10-beta.1"
125
125
  },
126
126
  "build": {
127
- "openclawVersion": "2026.10.5-beta.3",
127
+ "openclawVersion": "2026.10.10-beta.1",
128
128
  "bundledDist": false
129
129
  },
130
130
  "release": {
@@ -151,7 +151,7 @@
151
151
  "assets/activity.svg"
152
152
  ],
153
153
  "peerDependencies": {
154
- "openclaw": ">=2026.10.5-beta.3"
154
+ "openclaw": ">=2026.10.10-beta.1"
155
155
  },
156
156
  "peerDependenciesMeta": {
157
157
  "openclaw": {
@@ -1,3 +0,0 @@
1
- import { t as ircPlugin } from "./channel-BXrFfvr6.mjs";
2
- import "../channel-plugin-api.js";
3
- export { ircPlugin };
@@ -1,3 +0,0 @@
1
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-C5i-29Hz.mjs";
2
- import "../secret-contract-api.js";
3
- export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };