@openclaw/clickclack 2026.7.2-beta.5 → 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.
@@ -1,4 +1,4 @@
1
- import { a as buildClickClackSetupClaimUrl, o as isClickClackSetupLoopbackHost, s as requireClickClackSetupApiBaseUrl } from "./setup-surface-CD6MUC1g.js";
1
+ import { a as isClickClackSetupLoopbackHost, i as buildClickClackSetupClaimUrl, o as requireClickClackSetupApiBaseUrl } from "./setup-surface-DjT-gmMe.js";
2
2
  import { createProviderOperationDeadline, readProviderJsonResponse, readResponseTextLimited, resolveProviderOperationTimeoutMs } from "openclaw/plugin-sdk/provider-http";
3
3
  import { fetchWithSsrFGuard, resolvePinnedHostnameWithPolicy, ssrfPolicyFromHttpBaseUrlAllowedHostname } from "openclaw/plugin-sdk/ssrf-runtime";
4
4
  import { withTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -1,4 +1,4 @@
1
- import { _ as clickClackMeta, g as clickClackConfigAdapter, m as clickClackConfigSchema, n as clickClackSetupAdapter, r as clickClackSetupContract, t as clickClackSetupWizard } from "./setup-surface-CD6MUC1g.js";
1
+ import { g as clickClackMeta, h as clickClackConfigAdapter, n as clickClackSetupContract, p as clickClackConfigSchema, t as clickClackSetupWizard } from "./setup-surface-DjT-gmMe.js";
2
2
  //#region extensions/clickclack/src/channel.setup.ts
3
3
  const clickClackSetupPlugin = {
4
4
  id: "clickclack",
@@ -11,7 +11,6 @@ const clickClackSetupPlugin = {
11
11
  reload: { configPrefixes: ["channels.clickclack"] },
12
12
  configSchema: clickClackConfigSchema,
13
13
  config: clickClackConfigAdapter,
14
- setup: clickClackSetupAdapter,
15
14
  setupContract: clickClackSetupContract,
16
15
  setupWizard: clickClackSetupWizard
17
16
  };
@@ -2,19 +2,20 @@ import { createAccountListHelpers, hasConfiguredAccountValue } from "openclaw/pl
2
2
  import { DEFAULT_ACCOUNT_ID, DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
3
3
  import { resolveNormalizedAccountEntry } from "openclaw/plugin-sdk/account-resolution-runtime";
4
4
  import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
5
+ import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
5
6
  import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth";
6
7
  import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
7
8
  import { buildSecretInputSchema, normalizeResolvedSecretInputString, normalizeSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
8
9
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
9
10
  import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common";
10
- import { buildChannelConfigSchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
11
+ import { ChannelBotLoopProtectionSchema, buildChannelAllowBotsSchema, buildChannelConfigSchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
11
12
  import { z } from "zod";
12
13
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
13
14
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
14
15
  import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
15
16
  import { WebSocket } from "ws";
16
17
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
17
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, applyAccountNameToChannelSection, baseUrlTextInput, createSetupTranslator, createStandardChannelSetupStatus, defineTokenCredential, formatDocsLink, hasConfiguredSecretInput, migrateBaseNameToDefaultAccount, moveSingleAccountChannelSectionToDefaultAccount, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
18
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, applyAccountNameToChannelSection, baseUrlTextInput, createSetupTranslator, createStandardChannelSetupStatus, defineTokenCredential, formatDocsLink, hasConfiguredSecretInput, moveSingleAccountChannelSectionToDefaultAccount, patchScopedAccountConfig, prepareScopedSetupConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
18
19
  import { createSetupInputPresenceValidator } from "openclaw/plugin-sdk/setup-runtime";
19
20
  import net from "node:net";
20
21
  //#region \0rolldown/runtime.js
@@ -41,28 +42,48 @@ const DEFAULT_DISCUSSIONS_SECTION = "Sessions";
41
42
  const { listAccountIds: listClickClackAccountIds, resolveDefaultAccountId: resolveDefaultClickClackAccountId, resolveAccountConfig: resolveMergedClickClackAccountConfig } = createAccountListHelpers("clickclack", {
42
43
  normalizeAccountId,
43
44
  omitKeys: ["defaultAccount"],
44
- nestedObjectKeys: ["discussions"],
45
+ nestedObjectKeys: ["botLoopProtection", "discussions"],
45
46
  hasImplicitDefaultAccount: (cfg) => {
46
47
  const channel = cfg.channels?.clickclack;
47
48
  return Boolean(channel?.baseUrl?.trim() && (hasConfiguredAccountValue(channel.token) || Boolean(channel.tokenFile?.trim()) || Boolean(process.env.CLICKCLACK_BOT_TOKEN?.trim())) && channel.workspace?.trim());
48
49
  }
49
50
  });
51
+ function mergeClickClackGroups(...sources) {
52
+ const merged = /* @__PURE__ */ new Map();
53
+ for (const source of sources) for (const [rawKey, value] of Object.entries(source ?? {})) {
54
+ const key = rawKey.trim();
55
+ if (!key) continue;
56
+ const mergedBotLoopProtection = mergePairLoopGuardConfig(merged.get(key)?.botLoopProtection, value.botLoopProtection);
57
+ merged.set(key, {
58
+ ...merged.get(key),
59
+ ...value.requireMention !== void 0 ? { requireMention: value.requireMention } : {},
60
+ ...value.mentionPatterns !== void 0 ? { mentionPatterns: value.mentionPatterns } : {},
61
+ ...value.allowBots !== void 0 ? { allowBots: value.allowBots } : {},
62
+ ...mergedBotLoopProtection ? { botLoopProtection: mergedBotLoopProtection } : {}
63
+ });
64
+ }
65
+ return Object.fromEntries(merged);
66
+ }
50
67
  function resolveClickClackAccountConfig(cfg, accountId) {
51
68
  const channel = cfg.channels?.clickclack;
52
69
  const merged = resolveMergedClickClackAccountConfig(cfg, accountId);
53
70
  const account = resolveNormalizedAccountEntry(channel?.accounts, accountId, normalizeAccountId);
71
+ const mergedWithGroups = channel?.groups || account?.groups ? {
72
+ ...merged,
73
+ groups: mergeClickClackGroups(channel?.groups, account?.groups)
74
+ } : merged;
54
75
  const accountTokenFile = account?.tokenFile?.trim();
55
76
  if (accountTokenFile) return {
56
- ...merged,
77
+ ...mergedWithGroups,
57
78
  token: account?.token,
58
79
  tokenFile: accountTokenFile
59
80
  };
60
81
  if (hasConfiguredAccountValue(account?.token)) return {
61
- ...merged,
82
+ ...mergedWithGroups,
62
83
  token: account?.token,
63
84
  tokenFile: void 0
64
85
  };
65
- return merged;
86
+ return mergedWithGroups;
66
87
  }
67
88
  function resolveClickClackToken(params) {
68
89
  const tokenFile = params.tokenFile?.trim();
@@ -131,6 +152,7 @@ function resolveClickClackAccount(params) {
131
152
  max: MAX_RECONNECT_MS
132
153
  }),
133
154
  agentActivity: merged.agentActivity === true,
155
+ nativeProgress: merged.nativeProgress === true,
134
156
  commandMenu: merged.commandMenu !== false,
135
157
  discussions: {
136
158
  enabled: merged.discussions?.enabled === true,
@@ -138,6 +160,11 @@ function resolveClickClackAccount(params) {
138
160
  ...controlUrlBase ? { controlUrlBase } : {},
139
161
  section: merged.discussions?.section?.trim() || DEFAULT_DISCUSSIONS_SECTION
140
162
  },
163
+ requireMention: merged.requireMention === true,
164
+ mentionPatterns: merged.mentionPatterns ?? [],
165
+ allowBots: merged.allowBots ?? false,
166
+ botLoopProtection: merged.botLoopProtection,
167
+ groups: mergeClickClackGroups(merged.groups),
141
168
  config: {
142
169
  ...merged,
143
170
  allowFrom: merged.allowFrom ?? ["*"]
@@ -184,6 +211,7 @@ const clickClackConfigAdapter = {
184
211
  const ClickClackAccountConfigSchema = z.object({
185
212
  name: z.string().optional(),
186
213
  enabled: z.boolean().optional(),
214
+ configWrites: z.boolean().optional(),
187
215
  baseUrl: z.string().url().optional(),
188
216
  apiBaseUrl: z.string().url().optional(),
189
217
  token: buildSecretInputSchema().optional(),
@@ -197,9 +225,20 @@ const ClickClackAccountConfigSchema = z.object({
197
225
  toolsAllow: z.array(z.string()).optional(),
198
226
  defaultTo: z.string().optional(),
199
227
  allowFrom: z.array(z.string()).optional(),
228
+ allowBots: buildChannelAllowBotsSchema({ allowMentions: true }),
229
+ botLoopProtection: ChannelBotLoopProtectionSchema.optional(),
200
230
  reconnectMs: z.number().int().min(100).max(6e4).optional(),
201
231
  agentActivity: z.boolean().optional(),
232
+ nativeProgress: z.boolean().optional(),
202
233
  commandMenu: z.boolean().optional(),
234
+ requireMention: z.boolean().optional(),
235
+ mentionPatterns: z.array(z.string()).optional(),
236
+ groups: z.record(z.string(), z.object({
237
+ requireMention: z.boolean().optional(),
238
+ mentionPatterns: z.array(z.string()).optional(),
239
+ allowBots: buildChannelAllowBotsSchema({ allowMentions: true }),
240
+ botLoopProtection: ChannelBotLoopProtectionSchema.optional()
241
+ }).strict()).optional(),
203
242
  discussions: z.object({
204
243
  enabled: z.boolean().optional(),
205
244
  workspace: z.string().optional(),
@@ -236,6 +275,7 @@ const CLICKCLACK_CORRELATION_ID_PATTERN = /^[A-Za-z0-9._:-]+$/u;
236
275
  const CLICKCLACK_CORRELATION_ID_HEADER = "X-Correlation-ID";
237
276
  const CLICKCLACK_INBOUND_JSON_LIMIT_BYTES = 16 * 1024 * 1024;
238
277
  const CLICKCLACK_WEBSOCKET_HANDSHAKE_TIMEOUT_MS = 3e4;
278
+ const CLICKCLACK_EPHEMERAL_REQUEST_TIMEOUT_MS = 15e3;
239
279
  const CLICKCLACK_MESSAGE_PAGE_LIMIT = 200;
240
280
  const CLICKCLACK_DISCUSSION_ROOT_PAGE_LIMIT = 8;
241
281
  const CLICKCLACK_DISCUSSION_THREAD_REQUEST_LIMIT = 24;
@@ -276,20 +316,33 @@ function createClickClackClient(options) {
276
316
  Authorization: `Bearer ${options.token}`,
277
317
  Accept: "application/json"
278
318
  };
279
- async function request(path, init = {}) {
319
+ async function request(path, init = {}, requestOptions = {}) {
280
320
  const requestHeaders = new Headers(init.headers);
281
321
  for (const [key, value] of Object.entries(headers)) requestHeaders.set(key, value);
282
322
  if (correlationId) requestHeaders.set(CLICKCLACK_CORRELATION_ID_HEADER, correlationId);
283
323
  if (init.body && !(init.body instanceof FormData)) requestHeaders.set("Content-Type", "application/json");
284
- const response = await fetcher(`${baseUrl}${path}`, {
285
- ...init,
286
- headers: requestHeaders
287
- });
288
- if (!response.ok) {
289
- const detail = await readResponseTextLimited(response, CLICKCLACK_ERROR_BODY_LIMIT_BYTES);
290
- throw new ClickClackHttpError(response.status, redactToolPayloadText(detail), new Headers(response.headers));
324
+ const controller = requestOptions.timeoutMs !== void 0 && !init.signal ? new AbortController() : void 0;
325
+ const timeout = controller ? setTimeout(() => controller.abort(), requestOptions.timeoutMs) : void 0;
326
+ try {
327
+ const response = await fetcher(`${baseUrl}${path}`, {
328
+ ...init,
329
+ ...controller ? { signal: controller.signal } : {},
330
+ headers: requestHeaders
331
+ });
332
+ if (!response.ok) {
333
+ const detail = await readResponseTextLimited(response, CLICKCLACK_ERROR_BODY_LIMIT_BYTES);
334
+ throw new ClickClackHttpError(response.status, redactToolPayloadText(detail), new Headers(response.headers));
335
+ }
336
+ if (requestOptions.responseMode === "none") {
337
+ try {
338
+ await response.body?.cancel();
339
+ } catch {}
340
+ return;
341
+ }
342
+ return await readProviderJsonResponse(response, "ClickClack response", { maxBytes: CLICKCLACK_INBOUND_JSON_LIMIT_BYTES });
343
+ } finally {
344
+ if (timeout) clearTimeout(timeout);
291
345
  }
292
- return await readProviderJsonResponse(response, "ClickClack response", { maxBytes: CLICKCLACK_INBOUND_JSON_LIMIT_BYTES });
293
346
  }
294
347
  async function fetchEventPage(workspaceId, pageOptions = {}) {
295
348
  const query = new URLSearchParams({ workspace_id: workspaceId });
@@ -483,6 +536,25 @@ function createClickClackClient(options) {
483
536
  body: JSON.stringify({ body })
484
537
  })).message;
485
538
  },
539
+ /**
540
+ * Publishes an ephemeral realtime signal such as native agent progress.
541
+ * These frames are intentionally not persisted as messages.
542
+ */
543
+ publishEphemeral: async (params) => {
544
+ await request("/api/realtime/ephemeral", {
545
+ method: "POST",
546
+ body: JSON.stringify({
547
+ workspace_id: params.workspaceId,
548
+ ...params.channelId ? { channel_id: params.channelId } : {},
549
+ ...params.conversationId ? { direct_conversation_id: params.conversationId } : {},
550
+ type: params.type,
551
+ payload: params.payload ?? {}
552
+ })
553
+ }, {
554
+ timeoutMs: CLICKCLACK_EPHEMERAL_REQUEST_TIMEOUT_MS,
555
+ responseMode: "none"
556
+ });
557
+ },
486
558
  createDirectMessage: async (conversationId, body, opts) => {
487
559
  return (await request(`/api/dms/${encodeURIComponent(conversationId)}/messages`, {
488
560
  method: "POST",
@@ -666,23 +738,20 @@ function formatClickClackSetupCodeClaimError(error) {
666
738
  }
667
739
  function applyClickClackSetupConfigPatch(params) {
668
740
  const accountId = normalizeAccountId(params.accountId);
669
- const namedConfig = applyAccountNameToChannelSection({
670
- cfg: accountId === DEFAULT_ACCOUNT_ID ? params.cfg : moveSingleAccountChannelSectionToDefaultAccount({
671
- cfg: params.cfg,
741
+ return patchScopedAccountConfig({
742
+ cfg: prepareScopedSetupConfig({
743
+ cfg: accountId === DEFAULT_ACCOUNT_ID ? params.cfg : moveSingleAccountChannelSectionToDefaultAccount({
744
+ cfg: params.cfg,
745
+ channelKey: channel$1,
746
+ setupSurface: clickClackSetupAdapter
747
+ }),
672
748
  channelKey: channel$1,
673
- setupSurface: clickClackSetupAdapter
749
+ accountId,
750
+ name: params.name,
751
+ migrateBaseName: accountId !== DEFAULT_ACCOUNT_ID
674
752
  }),
675
753
  channelKey: channel$1,
676
754
  accountId,
677
- name: params.name
678
- });
679
- return patchScopedAccountConfig({
680
- cfg: accountId !== DEFAULT_ACCOUNT_ID ? migrateBaseNameToDefaultAccount({
681
- cfg: namedConfig,
682
- channelKey: channel$1
683
- }) : namedConfig,
684
- channelKey: channel$1,
685
- accountId,
686
755
  patch: params.patch,
687
756
  ...params.clearFields ? { clearFields: params.clearFields } : {}
688
757
  });
@@ -710,7 +779,7 @@ const clickClackSetupAdapter = {
710
779
  const claimUrl = setup.exactClaimUrl && !privateApiBaseUrl ? setup.exactClaimUrl : buildClickClackSetupClaimUrl(privateApiBaseUrl ?? setup.baseUrl);
711
780
  let claim;
712
781
  try {
713
- const { claimClickClackSetupCode } = await import("./setup-claim-BFU16zoo.js");
782
+ const { claimClickClackSetupCode } = await import("./setup-claim-CQjgPGxU.js");
714
783
  claim = await claimClickClackSetupCode({
715
784
  claimUrl,
716
785
  code: setup.code,
@@ -870,7 +939,8 @@ const clickClackSetupContract = defineChannelSetupContract({
870
939
  cli: {
871
940
  flags: "--use-env",
872
941
  description: "Use CLICKCLACK_BOT_TOKEN"
873
- }
942
+ },
943
+ envVars: ["CLICKCLACK_BOT_TOKEN"]
874
944
  }
875
945
  },
876
946
  legacyAdapter: clickClackSetupAdapter
@@ -1105,4 +1175,4 @@ const clickClackSetupWizard = {
1105
1175
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
1106
1176
  };
1107
1177
  //#endregion
1108
- export { resolveDefaultClickClackAccountId as S, clickClackMeta as _, buildClickClackSetupClaimUrl as a, listEnabledClickClackAccounts as b, resolveChannelId as c, createClickClackClient as d, isClickClackChannelNameConflict as f, clickClackConfigAdapter as g, CLICKCLACK_CHANNEL_ID as h, CLICKCLACK_SETUP_CODE_CLAIM_PATH as i, resolveWorkspaceId as l, clickClackConfigSchema as m, clickClackSetupAdapter as n, isClickClackSetupLoopbackHost as o, normalizeClickClackCorrelationId as p, clickClackSetupContract as r, requireClickClackSetupApiBaseUrl as s, clickClackSetupWizard as t, ClickClackHttpError as u, DEFAULT_ACCOUNT_ID$2 as v, resolveClickClackAccount as x, listClickClackAccountIds as y };
1178
+ export { DEFAULT_ACCOUNT_ID$2 as _, isClickClackSetupLoopbackHost as a, resolveClickClackAccount as b, resolveWorkspaceId as c, isClickClackChannelNameConflict as d, normalizeClickClackCorrelationId as f, clickClackMeta as g, clickClackConfigAdapter as h, buildClickClackSetupClaimUrl as i, ClickClackHttpError as l, CLICKCLACK_CHANNEL_ID as m, clickClackSetupContract as n, requireClickClackSetupApiBaseUrl as o, clickClackConfigSchema as p, CLICKCLACK_SETUP_CODE_CLAIM_PATH as r, resolveChannelId as s, clickClackSetupWizard as t, createClickClackClient as u, listClickClackAccountIds as v, resolveDefaultClickClackAccountId as x, listEnabledClickClackAccounts as y };
@@ -1,21 +1,14 @@
1
1
  {
2
2
  "id": "clickclack",
3
+ "doctorContract": {
4
+ "configRepair": true
5
+ },
3
6
  "activation": {
4
7
  "onStartup": false
5
8
  },
6
9
  "channels": [
7
10
  "clickclack"
8
11
  ],
9
- "contracts": {
10
- "tools": [
11
- "discussion"
12
- ]
13
- },
14
- "configSchema": {
15
- "type": "object",
16
- "additionalProperties": false,
17
- "properties": {}
18
- },
19
12
  "channelConfigs": {
20
13
  "clickclack": {
21
14
  "schema": {
@@ -28,6 +21,9 @@
28
21
  "enabled": {
29
22
  "type": "boolean"
30
23
  },
24
+ "configWrites": {
25
+ "type": "boolean"
26
+ },
31
27
  "baseUrl": {
32
28
  "type": "string",
33
29
  "format": "uri"
@@ -66,6 +62,29 @@
66
62
  ],
67
63
  "additionalProperties": false
68
64
  },
65
+ {
66
+ "type": "object",
67
+ "properties": {
68
+ "source": {
69
+ "type": "string",
70
+ "const": "store"
71
+ },
72
+ "provider": {
73
+ "type": "string",
74
+ "pattern": "^[a-z][a-z0-9_-]{0,63}$"
75
+ },
76
+ "id": {
77
+ "type": "string",
78
+ "pattern": "^[A-Z][A-Z0-9_]{0,127}$"
79
+ }
80
+ },
81
+ "required": [
82
+ "source",
83
+ "provider",
84
+ "id"
85
+ ],
86
+ "additionalProperties": false
87
+ },
69
88
  {
70
89
  "type": "object",
71
90
  "properties": {
@@ -154,6 +173,41 @@
154
173
  "type": "string"
155
174
  }
156
175
  },
176
+ "allowBots": {
177
+ "anyOf": [
178
+ {
179
+ "type": "boolean"
180
+ },
181
+ {
182
+ "type": "string",
183
+ "const": "mentions"
184
+ }
185
+ ]
186
+ },
187
+ "botLoopProtection": {
188
+ "type": "object",
189
+ "properties": {
190
+ "enabled": {
191
+ "type": "boolean"
192
+ },
193
+ "maxEventsPerWindow": {
194
+ "type": "integer",
195
+ "exclusiveMinimum": 0,
196
+ "maximum": 9007199254740991
197
+ },
198
+ "windowSeconds": {
199
+ "type": "integer",
200
+ "exclusiveMinimum": 0,
201
+ "maximum": 9007199254740991
202
+ },
203
+ "cooldownSeconds": {
204
+ "type": "integer",
205
+ "exclusiveMinimum": 0,
206
+ "maximum": 9007199254740991
207
+ }
208
+ },
209
+ "additionalProperties": false
210
+ },
157
211
  "reconnectMs": {
158
212
  "type": "integer",
159
213
  "minimum": 100,
@@ -162,9 +216,77 @@
162
216
  "agentActivity": {
163
217
  "type": "boolean"
164
218
  },
219
+ "nativeProgress": {
220
+ "type": "boolean"
221
+ },
165
222
  "commandMenu": {
166
223
  "type": "boolean"
167
224
  },
225
+ "requireMention": {
226
+ "type": "boolean"
227
+ },
228
+ "mentionPatterns": {
229
+ "type": "array",
230
+ "items": {
231
+ "type": "string"
232
+ }
233
+ },
234
+ "groups": {
235
+ "type": "object",
236
+ "propertyNames": {
237
+ "type": "string"
238
+ },
239
+ "additionalProperties": {
240
+ "type": "object",
241
+ "properties": {
242
+ "requireMention": {
243
+ "type": "boolean"
244
+ },
245
+ "mentionPatterns": {
246
+ "type": "array",
247
+ "items": {
248
+ "type": "string"
249
+ }
250
+ },
251
+ "allowBots": {
252
+ "anyOf": [
253
+ {
254
+ "type": "boolean"
255
+ },
256
+ {
257
+ "type": "string",
258
+ "const": "mentions"
259
+ }
260
+ ]
261
+ },
262
+ "botLoopProtection": {
263
+ "type": "object",
264
+ "properties": {
265
+ "enabled": {
266
+ "type": "boolean"
267
+ },
268
+ "maxEventsPerWindow": {
269
+ "type": "integer",
270
+ "exclusiveMinimum": 0,
271
+ "maximum": 9007199254740991
272
+ },
273
+ "windowSeconds": {
274
+ "type": "integer",
275
+ "exclusiveMinimum": 0,
276
+ "maximum": 9007199254740991
277
+ },
278
+ "cooldownSeconds": {
279
+ "type": "integer",
280
+ "exclusiveMinimum": 0,
281
+ "maximum": 9007199254740991
282
+ }
283
+ },
284
+ "additionalProperties": false
285
+ }
286
+ },
287
+ "additionalProperties": false
288
+ }
289
+ },
168
290
  "discussions": {
169
291
  "type": "object",
170
292
  "properties": {
@@ -198,6 +320,9 @@
198
320
  "enabled": {
199
321
  "type": "boolean"
200
322
  },
323
+ "configWrites": {
324
+ "type": "boolean"
325
+ },
201
326
  "baseUrl": {
202
327
  "type": "string",
203
328
  "format": "uri"
@@ -236,6 +361,29 @@
236
361
  ],
237
362
  "additionalProperties": false
238
363
  },
364
+ {
365
+ "type": "object",
366
+ "properties": {
367
+ "source": {
368
+ "type": "string",
369
+ "const": "store"
370
+ },
371
+ "provider": {
372
+ "type": "string",
373
+ "pattern": "^[a-z][a-z0-9_-]{0,63}$"
374
+ },
375
+ "id": {
376
+ "type": "string",
377
+ "pattern": "^[A-Z][A-Z0-9_]{0,127}$"
378
+ }
379
+ },
380
+ "required": [
381
+ "source",
382
+ "provider",
383
+ "id"
384
+ ],
385
+ "additionalProperties": false
386
+ },
239
387
  {
240
388
  "type": "object",
241
389
  "properties": {
@@ -324,6 +472,41 @@
324
472
  "type": "string"
325
473
  }
326
474
  },
475
+ "allowBots": {
476
+ "anyOf": [
477
+ {
478
+ "type": "boolean"
479
+ },
480
+ {
481
+ "type": "string",
482
+ "const": "mentions"
483
+ }
484
+ ]
485
+ },
486
+ "botLoopProtection": {
487
+ "type": "object",
488
+ "properties": {
489
+ "enabled": {
490
+ "type": "boolean"
491
+ },
492
+ "maxEventsPerWindow": {
493
+ "type": "integer",
494
+ "exclusiveMinimum": 0,
495
+ "maximum": 9007199254740991
496
+ },
497
+ "windowSeconds": {
498
+ "type": "integer",
499
+ "exclusiveMinimum": 0,
500
+ "maximum": 9007199254740991
501
+ },
502
+ "cooldownSeconds": {
503
+ "type": "integer",
504
+ "exclusiveMinimum": 0,
505
+ "maximum": 9007199254740991
506
+ }
507
+ },
508
+ "additionalProperties": false
509
+ },
327
510
  "reconnectMs": {
328
511
  "type": "integer",
329
512
  "minimum": 100,
@@ -332,9 +515,77 @@
332
515
  "agentActivity": {
333
516
  "type": "boolean"
334
517
  },
518
+ "nativeProgress": {
519
+ "type": "boolean"
520
+ },
335
521
  "commandMenu": {
336
522
  "type": "boolean"
337
523
  },
524
+ "requireMention": {
525
+ "type": "boolean"
526
+ },
527
+ "mentionPatterns": {
528
+ "type": "array",
529
+ "items": {
530
+ "type": "string"
531
+ }
532
+ },
533
+ "groups": {
534
+ "type": "object",
535
+ "propertyNames": {
536
+ "type": "string"
537
+ },
538
+ "additionalProperties": {
539
+ "type": "object",
540
+ "properties": {
541
+ "requireMention": {
542
+ "type": "boolean"
543
+ },
544
+ "mentionPatterns": {
545
+ "type": "array",
546
+ "items": {
547
+ "type": "string"
548
+ }
549
+ },
550
+ "allowBots": {
551
+ "anyOf": [
552
+ {
553
+ "type": "boolean"
554
+ },
555
+ {
556
+ "type": "string",
557
+ "const": "mentions"
558
+ }
559
+ ]
560
+ },
561
+ "botLoopProtection": {
562
+ "type": "object",
563
+ "properties": {
564
+ "enabled": {
565
+ "type": "boolean"
566
+ },
567
+ "maxEventsPerWindow": {
568
+ "type": "integer",
569
+ "exclusiveMinimum": 0,
570
+ "maximum": 9007199254740991
571
+ },
572
+ "windowSeconds": {
573
+ "type": "integer",
574
+ "exclusiveMinimum": 0,
575
+ "maximum": 9007199254740991
576
+ },
577
+ "cooldownSeconds": {
578
+ "type": "integer",
579
+ "exclusiveMinimum": 0,
580
+ "maximum": 9007199254740991
581
+ }
582
+ },
583
+ "additionalProperties": false
584
+ }
585
+ },
586
+ "additionalProperties": false
587
+ }
588
+ },
338
589
  "discussions": {
339
590
  "type": "object",
340
591
  "properties": {
@@ -365,7 +616,17 @@
365
616
  "additionalProperties": false
366
617
  },
367
618
  "label": "ClickClack",
368
- "description": "self-hosted chat via first-class ClickClack bot tokens."
619
+ "description": "ClickClack channel accounts and group activation policy."
369
620
  }
621
+ },
622
+ "contracts": {
623
+ "tools": [
624
+ "discussion"
625
+ ]
626
+ },
627
+ "configSchema": {
628
+ "type": "object",
629
+ "additionalProperties": false,
630
+ "properties": {}
370
631
  }
371
632
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/clickclack",
3
- "version": "2026.7.2-beta.5",
3
+ "version": "2026.8.1-beta.2",
4
4
  "description": "OpenClaw ClickClack channel plugin",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,7 +13,7 @@
13
13
  "zod": "4.4.3"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.7.2-beta.5"
16
+ "openclaw": ">=2026.8.1-beta.2"
17
17
  },
18
18
  "peerDependenciesMeta": {
19
19
  "openclaw": {
@@ -123,7 +123,10 @@
123
123
  "cli": {
124
124
  "flags": "--use-env",
125
125
  "description": "Use CLICKCLACK_BOT_TOKEN"
126
- }
126
+ },
127
+ "envVars": [
128
+ "CLICKCLACK_BOT_TOKEN"
129
+ ]
127
130
  }
128
131
  ]
129
132
  }
@@ -136,10 +139,10 @@
136
139
  "allowInvalidConfigRecovery": true
137
140
  },
138
141
  "compat": {
139
- "pluginApi": ">=2026.7.2-beta.5"
142
+ "pluginApi": ">=2026.8.1-beta.2"
140
143
  },
141
144
  "build": {
142
- "openclawVersion": "2026.7.2-beta.5",
145
+ "openclawVersion": "2026.8.1-beta.2",
143
146
  "bundledDist": false
144
147
  },
145
148
  "release": {