@openclaw/irc 2026.7.2-beta.7 → 2026.8.1-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,3 +1,3 @@
1
- import { _ as resolveDefaultIrcAccountId, g as listIrcAccountIds, h as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin, v as resolveIrcAccount } from "./channel-DoavT7vs.js";
1
+ import { _ as resolveDefaultIrcAccountId, g as listIrcAccountIds, h as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin, v as resolveIrcAccount } from "./channel-Bfg9kJRw.js";
2
2
  import { n as setIrcRuntime } from "./runtime-xy-FcjJC.js";
3
3
  export { ircPlugin, ircSetupAdapter, ircSetupWizard, listEnabledIrcAccounts, listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount, setIrcRuntime };
@@ -1,4 +1,4 @@
1
- import { t as IrcChannelConfigSchema } from "./config-schema-BPoye3--.js";
1
+ import { t as IrcChannelConfigSchema } from "./config-schema-DdB_h5mL.js";
2
2
  import { t as getIrcRuntime } from "./runtime-xy-FcjJC.js";
3
3
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-D_DC3YWj.js";
4
4
  import { createAccountListHelpers, describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
@@ -18,7 +18,7 @@ import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-i
18
18
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, normalizeStringifiedOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
19
19
  import { createAccountStatusSink, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, runPassiveAccountLifecycle, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
20
20
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
21
- import { chunkTextForOutbound, convertMarkdownTables, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
21
+ import { chunkTextForOutbound, convertMarkdownTables, sanitizeAssistantVisibleText, stripMarkdown } from "openclaw/plugin-sdk/text-chunking";
22
22
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
23
23
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
24
24
  import net from "node:net";
@@ -183,7 +183,7 @@ const collectIrcMutableAllowlistWarnings = createDangerousNameMatchingMutableAll
183
183
  });
184
184
  //#endregion
185
185
  //#region extensions/irc/src/gateway.ts
186
- const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-DsN_0hPp.js"));
186
+ const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-kWMtRD4g.js"));
187
187
  async function startIrcGatewayAccount(ctx) {
188
188
  const account = ctx.account;
189
189
  const statusSink = createAccountStatusSink({
@@ -334,7 +334,7 @@ function takeIrcPrivmsgChunk(text, maxChars, maxBytes) {
334
334
  if (splitAt >= Math.floor(fitted.length / 2)) return fitted.slice(0, splitAt);
335
335
  return fitted;
336
336
  }
337
- function toError(err) {
337
+ function toIrcError(err) {
338
338
  if (err instanceof Error) return err;
339
339
  return new Error(typeof err === "string" ? err : JSON.stringify(err));
340
340
  }
@@ -391,7 +391,7 @@ async function connectIrcClient(options) {
391
391
  rejectReady = reject;
392
392
  });
393
393
  const fail = (err) => {
394
- const error = toError(err);
394
+ const error = toIrcError(err);
395
395
  if (options.onError) options.onError(error);
396
396
  if (!ready && rejectReady) {
397
397
  rejectReady(error);
@@ -726,9 +726,9 @@ async function sendMessageIrc(to, text, opts) {
726
726
  channel: "irc",
727
727
  accountId: account.accountId
728
728
  });
729
- const prepared = convertMarkdownTables(text.trim(), tableMode);
729
+ const prepared = stripMarkdown(convertMarkdownTables(text.trim(), tableMode));
730
+ if (!prepared.trim()) throw new Error("Message must be non-empty for IRC sends");
730
731
  const payload = opts.replyTo ? `${prepared}\n\n[reply:${opts.replyTo}]` : prepared;
731
- if (!payload.trim()) throw new Error("Message must be non-empty for IRC sends");
732
732
  const client = opts.client;
733
733
  if (client?.isReady()) client.sendPrivmsg(target, payload);
734
734
  else {
@@ -1028,7 +1028,8 @@ const ircSetupContract = defineChannelSetupContract({
1028
1028
  cli: {
1029
1029
  flags: "--use-env",
1030
1030
  description: "Use IRC environment configuration"
1031
- }
1031
+ },
1032
+ envVars: ["IRC_HOST", "IRC_NICK"]
1032
1033
  }
1033
1034
  },
1034
1035
  legacyAdapter: ircSetupAdapter
@@ -1398,7 +1399,7 @@ const meta = {
1398
1399
  systemImage: "number",
1399
1400
  markdownCapable: true
1400
1401
  };
1401
- const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-DsN_0hPp.js"));
1402
+ const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-kWMtRD4g.js"));
1402
1403
  function normalizePairingTarget(raw) {
1403
1404
  const normalized = normalizeIrcAllowEntry(raw);
1404
1405
  if (!normalized) return "";
@@ -1470,7 +1471,6 @@ const ircPlugin = createChatChannelPlugin({
1470
1471
  ...meta,
1471
1472
  quickstartAllowFrom: true
1472
1473
  },
1473
- setup: ircSetupAdapter,
1474
1474
  setupContract: ircSetupContract,
1475
1475
  setupWizard: ircSetupWizard,
1476
1476
  capabilities: {
@@ -1532,6 +1532,10 @@ const ircPlugin = createChatChannelPlugin({
1532
1532
  messaging: {
1533
1533
  targetPrefixes: ["irc"],
1534
1534
  normalizeTarget: normalizeIrcMessagingTarget,
1535
+ inferTargetChatType: ({ to }) => {
1536
+ const target = normalizeIrcMessagingTarget(to);
1537
+ return target ? isChannelTarget(target) ? "group" : "direct" : void 0;
1538
+ },
1535
1539
  resolveOutboundSessionRoute: (params) => resolveIrcOutboundSessionRoute(params),
1536
1540
  targetResolver: {
1537
1541
  looksLikeId: looksLikeIrcTargetId,
@@ -1,2 +1,2 @@
1
- import { t as IrcChannelConfigSchema } from "./config-schema-BPoye3--.js";
1
+ import { t as IrcChannelConfigSchema } from "./config-schema-DdB_h5mL.js";
2
2
  export { IrcChannelConfigSchema };
@@ -1,2 +1,2 @@
1
- import { t as ircPlugin } from "./channel-DoavT7vs.js";
1
+ import { t as ircPlugin } from "./channel-Bfg9kJRw.js";
2
2
  export { ircPlugin };
@@ -1,10 +1,11 @@
1
- import { a as resolveIrcGroupRequireMention, c as buildIrcAllowlistCandidates, d as buildIrcConnectOptions, f as connectIrcClient, i as resolveIrcGroupMatch, l as isChannelTarget, m as parseIrcPrefix, o as sanitizeIrcAssistantText, p as parseIrcLine, s as sendMessageIrc, u as normalizeIrcAllowEntry, v as resolveIrcAccount } from "./channel-DoavT7vs.js";
1
+ import { a as resolveIrcGroupRequireMention, c as buildIrcAllowlistCandidates, d as buildIrcConnectOptions, f as connectIrcClient, i as resolveIrcGroupMatch, l as isChannelTarget, m as parseIrcPrefix, o as sanitizeIrcAssistantText, p as parseIrcLine, s as sendMessageIrc, u as normalizeIrcAllowEntry, v as resolveIrcAccount } from "./channel-Bfg9kJRw.js";
2
2
  import { t as getIrcRuntime } from "./runtime-xy-FcjJC.js";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared";
7
- import { buildChannelInboundEventContext, logInboundDrop, resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
7
+ import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
8
+ import { logInboundDrop, resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
8
9
  import { channelIngressRoutes, createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
9
10
  import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
10
11
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
@@ -46,6 +47,23 @@ const ircIngressIdentity = defineStableChannelIngressIdentity({
46
47
  resolveEntryId: ({ entryIndex, fieldKey }) => `irc-entry-${entryIndex + 1}:${fieldKey === "irc-nick" ? "nick" : "id"}`
47
48
  });
48
49
  const escapeIrcRegexLiteral = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
+ const IRC_NICK_CHARACTER = String.raw`[A-Za-z0-9_\-\[\]\\\x60^{}|~]`;
51
+ const IRC_RFC1459_CASE_EQUIVALENTS = /* @__PURE__ */ new Map([
52
+ ["[", "{"],
53
+ ["{", "["],
54
+ ["]", "}"],
55
+ ["}", "]"],
56
+ ["\\", "|"],
57
+ ["|", "\\"],
58
+ ["^", "~"],
59
+ ["~", "^"]
60
+ ]);
61
+ function buildIrcNickMentionPattern(value) {
62
+ return Array.from(value, (character) => {
63
+ const equivalent = IRC_RFC1459_CASE_EQUIVALENTS.get(character);
64
+ return equivalent ? `[${escapeIrcRegexLiteral(character)}${escapeIrcRegexLiteral(equivalent)}]` : escapeIrcRegexLiteral(character);
65
+ }).join("");
66
+ }
49
67
  function isBareNick(value) {
50
68
  return !value.includes("!") && !value.includes("@");
51
69
  }
@@ -165,7 +183,7 @@ async function handleIrcInbound(params) {
165
183
  const hasControlCommand = core.channel.text.hasControlCommand(rawBody, config);
166
184
  const mentionRegexes = core.channel.mentions.buildMentionRegexes(config);
167
185
  const mentionNick = connectedNick?.trim() || account.nick;
168
- const explicitMentionRegex = mentionNick ? new RegExp(`\\b${escapeIrcRegexLiteral(mentionNick)}\\b[:,]?`, "i") : null;
186
+ const explicitMentionRegex = mentionNick ? new RegExp(`(?<!${IRC_NICK_CHARACTER})${buildIrcNickMentionPattern(mentionNick)}(?!${IRC_NICK_CHARACTER})[:,]?`, "i") : null;
169
187
  const wasMentioned = core.channel.mentions.matchesMentionPatterns(rawBody, mentionRegexes) || (explicitMentionRegex ? explicitMentionRegex.test(rawBody) : false);
170
188
  const requireMention = message.isGroup ? resolveIrcGroupRequireMention({
171
189
  groups: account.config.groups,
@@ -173,6 +191,17 @@ async function handleIrcInbound(params) {
173
191
  }) : false;
174
192
  const routeGroupAllowFrom = normalizeStringEntries(groupMatch.groupConfig?.allowFrom?.length ? groupMatch.groupConfig.allowFrom : groupMatch.wildcardConfig?.allowFrom);
175
193
  const accessGroupPolicy = groupPolicy === "open" && (hasEntries(account.config.groupAllowFrom) || hasEntries(routeGroupAllowFrom)) ? "allowlist" : groupPolicy;
194
+ const channelTarget = message.target.startsWith("#") || message.target.startsWith("&") ? message.target : `#${message.target}`;
195
+ const peerId = message.isGroup ? channelTarget : message.senderNick;
196
+ const { route, buildEnvelope } = resolveChannelInboundRouteEnvelope({
197
+ cfg: config,
198
+ channel: CHANNEL_ID,
199
+ accountId: account.accountId,
200
+ peer: {
201
+ kind: message.isGroup ? "group" : "direct",
202
+ id: peerId
203
+ }
204
+ });
176
205
  const access = await createChannelIngressResolver({
177
206
  channelId: CHANNEL_ID,
178
207
  accountId: account.accountId,
@@ -185,6 +214,12 @@ async function handleIrcInbound(params) {
185
214
  kind: message.isGroup ? "group" : "direct",
186
215
  id: message.target
187
216
  },
217
+ contextBinding: {
218
+ agentId: route.agentId,
219
+ sessionKey: route.sessionKey,
220
+ ...message.messageId ? { messageId: message.messageId } : {},
221
+ inboundEventKind: "user_request"
222
+ },
188
223
  route: routeDescriptorsForIrcGroup({
189
224
  isGroup: message.isGroup,
190
225
  groupPolicy,
@@ -262,17 +297,6 @@ async function handleIrcInbound(params) {
262
297
  kind: "failed-retryable",
263
298
  error: turnAdoptionLifecycle.abortSignal.reason
264
299
  };
265
- const channelTarget = message.target.startsWith("#") || message.target.startsWith("&") ? message.target : `#${message.target}`;
266
- const peerId = message.isGroup ? channelTarget : message.senderNick;
267
- const { route, buildEnvelope } = resolveChannelInboundRouteEnvelope({
268
- cfg: config,
269
- channel: CHANNEL_ID,
270
- accountId: account.accountId,
271
- peer: {
272
- kind: message.isGroup ? "group" : "direct",
273
- id: peerId
274
- }
275
- });
276
300
  const fromLabel = message.isGroup ? message.target : senderDisplay;
277
301
  const body = buildEnvelope({
278
302
  channel: "IRC",
@@ -282,7 +306,8 @@ async function handleIrcInbound(params) {
282
306
  });
283
307
  const groupSystemPrompt = normalizeOptionalString(groupMatch.groupConfig?.systemPrompt);
284
308
  const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(account.config);
285
- const ctxPayload = buildChannelInboundEventContext({
309
+ const ctxPayload = core.channel.inbound.buildContext({
310
+ channelIngress: access,
286
311
  channel: CHANNEL_ID,
287
312
  accountId: route.accountId,
288
313
  messageId: message.messageId,
@@ -592,6 +617,7 @@ async function monitorIrcProvider(opts) {
592
617
  });
593
618
  function scheduleReconnect() {
594
619
  if (stopped || monitorAbort.signal.aborted || reconnectTimer) return;
620
+ opts.statusSink?.({ lifecycle: "recovering" });
595
621
  reconnectTimer = setTimeout(() => {
596
622
  reconnectTimer = null;
597
623
  connect().catch((error) => {
@@ -648,6 +674,7 @@ async function monitorIrcProvider(opts) {
648
674
  return;
649
675
  }
650
676
  ingress.start();
677
+ opts.statusSink?.(channelReadyPatch());
651
678
  logger.info(`[${account.accountId}] connected to ${account.host}:${account.port}${account.tls ? " (tls)" : ""} as ${nextClient.nick}`);
652
679
  }
653
680
  try {
@@ -59,6 +59,7 @@ const IrcNickServSchema = z.object({
59
59
  const IrcChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(z.object({
60
60
  name: z.string().optional(),
61
61
  enabled: z.boolean().optional(),
62
+ configWrites: z.boolean().optional(),
62
63
  dangerouslyAllowNameMatching: z.boolean().optional(),
63
64
  host: z.string().optional(),
64
65
  port: z.number().int().min(1).max(65535).optional(),
@@ -1,4 +1,4 @@
1
- import { defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor";
1
+ import { defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor-migrations";
2
2
  //#region extensions/irc/doctor-contract-api.ts
3
3
  const streamingAliasMigration = defineChannelAliasMigration({
4
4
  channelId: "irc",
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "id": "irc",
3
+ "doctorContract": {
4
+ "configRepair": true
5
+ },
3
6
  "activation": {
4
7
  "onStartup": false
5
8
  },
@@ -23,6 +26,9 @@
23
26
  "enabled": {
24
27
  "type": "boolean"
25
28
  },
29
+ "configWrites": {
30
+ "type": "boolean"
31
+ },
26
32
  "dangerouslyAllowNameMatching": {
27
33
  "type": "boolean"
28
34
  },
@@ -346,6 +352,9 @@
346
352
  "enabled": {
347
353
  "type": "boolean"
348
354
  },
355
+ "configWrites": {
356
+ "type": "boolean"
357
+ },
349
358
  "dangerouslyAllowNameMatching": {
350
359
  "type": "boolean"
351
360
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/irc",
3
- "version": "2026.7.2-beta.7",
3
+ "version": "2026.8.1-beta.2",
4
4
  "description": "OpenClaw IRC channel plugin",
5
5
  "type": "module",
6
6
  "openclaw": {
@@ -111,16 +111,20 @@
111
111
  "cli": {
112
112
  "flags": "--use-env",
113
113
  "description": "Use IRC environment configuration"
114
- }
114
+ },
115
+ "envVars": [
116
+ "IRC_HOST",
117
+ "IRC_NICK"
118
+ ]
115
119
  }
116
120
  ]
117
121
  }
118
122
  },
119
123
  "compat": {
120
- "pluginApi": ">=2026.7.2-beta.7"
124
+ "pluginApi": ">=2026.8.1-beta.2"
121
125
  },
122
126
  "build": {
123
- "openclawVersion": "2026.7.2-beta.7",
127
+ "openclawVersion": "2026.8.1-beta.2",
124
128
  "bundledDist": false
125
129
  },
126
130
  "release": {
@@ -145,7 +149,7 @@
145
149
  "README.md"
146
150
  ],
147
151
  "peerDependencies": {
148
- "openclaw": ">=2026.7.2-beta.7"
152
+ "openclaw": ">=2026.8.1-beta.2"
149
153
  },
150
154
  "peerDependenciesMeta": {
151
155
  "openclaw": {