@openclaw/clickclack 2026.7.2-beta.7 → 2026.8.1-beta.3

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-DAy8vDJK.js";
1
+ import { a as isClickClackSetupLoopbackHost, i as buildClickClackSetupClaimUrl, o as requireClickClackSetupApiBaseUrl } from "./setup-surface-BQ1Zyohu.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-DAy8vDJK.js";
1
+ import { g as clickClackMeta, h as clickClackConfigAdapter, n as clickClackSetupContract, p as clickClackConfigSchema, t as clickClackSetupWizard } from "./setup-surface-BQ1Zyohu.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,12 +2,13 @@ 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
- import { buildSecretInputSchema, normalizeResolvedSecretInputString, normalizeSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
8
+ import { buildSecretInputSchema, 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";
@@ -41,7 +42,7 @@ 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());
@@ -52,10 +53,13 @@ function mergeClickClackGroups(...sources) {
52
53
  for (const source of sources) for (const [rawKey, value] of Object.entries(source ?? {})) {
53
54
  const key = rawKey.trim();
54
55
  if (!key) continue;
56
+ const mergedBotLoopProtection = mergePairLoopGuardConfig(merged.get(key)?.botLoopProtection, value.botLoopProtection);
55
57
  merged.set(key, {
56
58
  ...merged.get(key),
57
59
  ...value.requireMention !== void 0 ? { requireMention: value.requireMention } : {},
58
- ...value.mentionPatterns !== void 0 ? { mentionPatterns: value.mentionPatterns } : {}
60
+ ...value.mentionPatterns !== void 0 ? { mentionPatterns: value.mentionPatterns } : {},
61
+ ...value.allowBots !== void 0 ? { allowBots: value.allowBots } : {},
62
+ ...mergedBotLoopProtection ? { botLoopProtection: mergedBotLoopProtection } : {}
59
63
  });
60
64
  }
61
65
  return Object.fromEntries(merged);
@@ -83,7 +87,20 @@ function resolveClickClackAccountConfig(cfg, accountId) {
83
87
  }
84
88
  function resolveClickClackToken(params) {
85
89
  const tokenFile = params.tokenFile?.trim();
86
- if (tokenFile) return tryReadSecretFileSync(tokenFile, params.accountId === DEFAULT_ACCOUNT_ID$2 ? "channels.clickclack.tokenFile" : `channels.clickclack.accounts.${params.accountId}.tokenFile`, { rejectSymlink: true }) ?? "";
90
+ if (tokenFile) {
91
+ const accountTokenFile = resolveNormalizedAccountEntry(params.cfg.channels?.clickclack?.accounts, params.accountId, normalizeAccountId)?.tokenFile?.trim();
92
+ const result = tryReadSecretFileSync(tokenFile, "ClickClack bot token", { rejectSymlink: true }, { configPath: accountTokenFile ? `channels.clickclack.accounts.${params.accountId}.tokenFile` : "channels.clickclack.tokenFile" });
93
+ return result.status === "available" ? {
94
+ token: result.value,
95
+ tokenSource: "tokenFile",
96
+ tokenStatus: "available"
97
+ } : {
98
+ token: "",
99
+ tokenSource: "tokenFile",
100
+ tokenStatus: "configured_unavailable",
101
+ credentialDiagnostics: [result.diagnostic]
102
+ };
103
+ }
87
104
  const resolved = resolveSecretInputString({
88
105
  value: params.value,
89
106
  path: params.accountId === DEFAULT_ACCOUNT_ID$2 ? "channels.clickclack.token" : `channels.clickclack.accounts.${params.accountId}.token`,
@@ -91,21 +108,42 @@ function resolveClickClackToken(params) {
91
108
  mode: "inspect"
92
109
  });
93
110
  if (resolved.status !== "available") {
94
- if (resolved.status === "missing" && params.accountId === DEFAULT_ACCOUNT_ID$2) return normalizeSecretInputString((params.env ?? process.env).CLICKCLACK_BOT_TOKEN) ?? "";
111
+ if (resolved.status === "missing" && params.accountId === DEFAULT_ACCOUNT_ID$2) {
112
+ const token = normalizeSecretInputString((params.env ?? process.env).CLICKCLACK_BOT_TOKEN);
113
+ return token ? {
114
+ token,
115
+ tokenSource: "env",
116
+ tokenStatus: "available"
117
+ } : {
118
+ token: "",
119
+ tokenSource: "none",
120
+ tokenStatus: "missing"
121
+ };
122
+ }
95
123
  if (resolved.status === "configured_unavailable" && resolved.ref.source === "env") {
96
124
  const providerConfig = params.cfg.secrets?.providers?.[resolved.ref.provider];
97
125
  if (providerConfig) {
98
126
  if (providerConfig.source !== "env") throw new Error(`Secret provider "${resolved.ref.provider}" has source "${providerConfig.source}" but ref requests "env".`);
99
127
  if (providerConfig.allowlist && !providerConfig.allowlist.includes(resolved.ref.id)) throw new Error(`Environment variable "${resolved.ref.id}" is not allowlisted in secrets.providers.${resolved.ref.provider}.allowlist.`);
100
128
  } else if (resolved.ref.provider !== resolveDefaultSecretProviderAlias({ secrets: params.cfg.secrets }, "env")) throw new Error(`Secret provider "${resolved.ref.provider}" is not configured (ref: env:${resolved.ref.provider}:${resolved.ref.id}).`);
101
- return normalizeSecretInputString((params.env ?? process.env)[resolved.ref.id]) ?? "";
129
+ const token = normalizeSecretInputString((params.env ?? process.env)[resolved.ref.id]);
130
+ return {
131
+ token: token ?? "",
132
+ tokenSource: "config",
133
+ tokenStatus: token ? "available" : "configured_unavailable"
134
+ };
102
135
  }
103
- return "";
136
+ return {
137
+ token: "",
138
+ tokenSource: resolved.status === "missing" ? "none" : "config",
139
+ tokenStatus: resolved.status
140
+ };
104
141
  }
105
- return normalizeResolvedSecretInputString({
106
- value: resolved.value,
107
- path: "channels.clickclack.token"
108
- }) ?? "";
142
+ return {
143
+ token: resolved.value,
144
+ tokenSource: "config",
145
+ tokenStatus: "available"
146
+ };
109
147
  }
110
148
  /**
111
149
  * Builds the normalized account snapshot used by gateway, outbound delivery,
@@ -130,10 +168,10 @@ function resolveClickClackAccount(params) {
130
168
  return {
131
169
  accountId,
132
170
  enabled,
133
- configured: Boolean(baseUrl && token && workspace),
171
+ configured: Boolean(baseUrl && token.tokenStatus !== "missing" && workspace),
134
172
  name: normalizeOptionalString(merged.name),
135
173
  baseUrl,
136
- token,
174
+ ...token,
137
175
  workspace,
138
176
  botUserId: normalizeOptionalString(merged.botUserId),
139
177
  agentId: normalizeOptionalString(merged.agentId),
@@ -148,6 +186,7 @@ function resolveClickClackAccount(params) {
148
186
  max: MAX_RECONNECT_MS
149
187
  }),
150
188
  agentActivity: merged.agentActivity === true,
189
+ nativeProgress: merged.nativeProgress === true,
151
190
  commandMenu: merged.commandMenu !== false,
152
191
  discussions: {
153
192
  enabled: merged.discussions?.enabled === true,
@@ -157,6 +196,8 @@ function resolveClickClackAccount(params) {
157
196
  },
158
197
  requireMention: merged.requireMention === true,
159
198
  mentionPatterns: merged.mentionPatterns ?? [],
199
+ allowBots: merged.allowBots ?? false,
200
+ botLoopProtection: merged.botLoopProtection,
160
201
  groups: mergeClickClackGroups(merged.groups),
161
202
  config: {
162
203
  ...merged,
@@ -204,6 +245,7 @@ const clickClackConfigAdapter = {
204
245
  const ClickClackAccountConfigSchema = z.object({
205
246
  name: z.string().optional(),
206
247
  enabled: z.boolean().optional(),
248
+ configWrites: z.boolean().optional(),
207
249
  baseUrl: z.string().url().optional(),
208
250
  apiBaseUrl: z.string().url().optional(),
209
251
  token: buildSecretInputSchema().optional(),
@@ -217,14 +259,19 @@ const ClickClackAccountConfigSchema = z.object({
217
259
  toolsAllow: z.array(z.string()).optional(),
218
260
  defaultTo: z.string().optional(),
219
261
  allowFrom: z.array(z.string()).optional(),
262
+ allowBots: buildChannelAllowBotsSchema({ allowMentions: true }),
263
+ botLoopProtection: ChannelBotLoopProtectionSchema.optional(),
220
264
  reconnectMs: z.number().int().min(100).max(6e4).optional(),
221
265
  agentActivity: z.boolean().optional(),
266
+ nativeProgress: z.boolean().optional(),
222
267
  commandMenu: z.boolean().optional(),
223
268
  requireMention: z.boolean().optional(),
224
269
  mentionPatterns: z.array(z.string()).optional(),
225
270
  groups: z.record(z.string(), z.object({
226
271
  requireMention: z.boolean().optional(),
227
- mentionPatterns: z.array(z.string()).optional()
272
+ mentionPatterns: z.array(z.string()).optional(),
273
+ allowBots: buildChannelAllowBotsSchema({ allowMentions: true }),
274
+ botLoopProtection: ChannelBotLoopProtectionSchema.optional()
228
275
  }).strict()).optional(),
229
276
  discussions: z.object({
230
277
  enabled: z.boolean().optional(),
@@ -262,6 +309,7 @@ const CLICKCLACK_CORRELATION_ID_PATTERN = /^[A-Za-z0-9._:-]+$/u;
262
309
  const CLICKCLACK_CORRELATION_ID_HEADER = "X-Correlation-ID";
263
310
  const CLICKCLACK_INBOUND_JSON_LIMIT_BYTES = 16 * 1024 * 1024;
264
311
  const CLICKCLACK_WEBSOCKET_HANDSHAKE_TIMEOUT_MS = 3e4;
312
+ const CLICKCLACK_EPHEMERAL_REQUEST_TIMEOUT_MS = 15e3;
265
313
  const CLICKCLACK_MESSAGE_PAGE_LIMIT = 200;
266
314
  const CLICKCLACK_DISCUSSION_ROOT_PAGE_LIMIT = 8;
267
315
  const CLICKCLACK_DISCUSSION_THREAD_REQUEST_LIMIT = 24;
@@ -302,20 +350,33 @@ function createClickClackClient(options) {
302
350
  Authorization: `Bearer ${options.token}`,
303
351
  Accept: "application/json"
304
352
  };
305
- async function request(path, init = {}) {
353
+ async function request(path, init = {}, requestOptions = {}) {
306
354
  const requestHeaders = new Headers(init.headers);
307
355
  for (const [key, value] of Object.entries(headers)) requestHeaders.set(key, value);
308
356
  if (correlationId) requestHeaders.set(CLICKCLACK_CORRELATION_ID_HEADER, correlationId);
309
357
  if (init.body && !(init.body instanceof FormData)) requestHeaders.set("Content-Type", "application/json");
310
- const response = await fetcher(`${baseUrl}${path}`, {
311
- ...init,
312
- headers: requestHeaders
313
- });
314
- if (!response.ok) {
315
- const detail = await readResponseTextLimited(response, CLICKCLACK_ERROR_BODY_LIMIT_BYTES);
316
- throw new ClickClackHttpError(response.status, redactToolPayloadText(detail), new Headers(response.headers));
358
+ const controller = requestOptions.timeoutMs !== void 0 && !init.signal ? new AbortController() : void 0;
359
+ const timeout = controller ? setTimeout(() => controller.abort(), requestOptions.timeoutMs) : void 0;
360
+ try {
361
+ const response = await fetcher(`${baseUrl}${path}`, {
362
+ ...init,
363
+ ...controller ? { signal: controller.signal } : {},
364
+ headers: requestHeaders
365
+ });
366
+ if (!response.ok) {
367
+ const detail = await readResponseTextLimited(response, CLICKCLACK_ERROR_BODY_LIMIT_BYTES);
368
+ throw new ClickClackHttpError(response.status, redactToolPayloadText(detail), new Headers(response.headers));
369
+ }
370
+ if (requestOptions.responseMode === "none") {
371
+ try {
372
+ await response.body?.cancel();
373
+ } catch {}
374
+ return;
375
+ }
376
+ return await readProviderJsonResponse(response, "ClickClack response", { maxBytes: CLICKCLACK_INBOUND_JSON_LIMIT_BYTES });
377
+ } finally {
378
+ if (timeout) clearTimeout(timeout);
317
379
  }
318
- return await readProviderJsonResponse(response, "ClickClack response", { maxBytes: CLICKCLACK_INBOUND_JSON_LIMIT_BYTES });
319
380
  }
320
381
  async function fetchEventPage(workspaceId, pageOptions = {}) {
321
382
  const query = new URLSearchParams({ workspace_id: workspaceId });
@@ -455,7 +516,7 @@ function createClickClackClient(options) {
455
516
  },
456
517
  createUpload: async (params) => {
457
518
  const form = new FormData();
458
- const bytes = new Uint8Array(params.buffer);
519
+ const bytes = params.buffer.buffer instanceof ArrayBuffer ? new Uint8Array(params.buffer.buffer, params.buffer.byteOffset, params.buffer.byteLength) : Uint8Array.from(params.buffer);
459
520
  form.append("file", new Blob([bytes], { type: params.contentType }), params.filename);
460
521
  const query = new URLSearchParams({ workspace_id: params.workspaceId });
461
522
  if (params.nonce) query.set("nonce", params.nonce);
@@ -509,6 +570,25 @@ function createClickClackClient(options) {
509
570
  body: JSON.stringify({ body })
510
571
  })).message;
511
572
  },
573
+ /**
574
+ * Publishes an ephemeral realtime signal such as native agent progress.
575
+ * These frames are intentionally not persisted as messages.
576
+ */
577
+ publishEphemeral: async (params) => {
578
+ await request("/api/realtime/ephemeral", {
579
+ method: "POST",
580
+ body: JSON.stringify({
581
+ workspace_id: params.workspaceId,
582
+ ...params.channelId ? { channel_id: params.channelId } : {},
583
+ ...params.conversationId ? { direct_conversation_id: params.conversationId } : {},
584
+ type: params.type,
585
+ payload: params.payload ?? {}
586
+ })
587
+ }, {
588
+ timeoutMs: CLICKCLACK_EPHEMERAL_REQUEST_TIMEOUT_MS,
589
+ responseMode: "none"
590
+ });
591
+ },
512
592
  createDirectMessage: async (conversationId, body, opts) => {
513
593
  return (await request(`/api/dms/${encodeURIComponent(conversationId)}/messages`, {
514
594
  method: "POST",
@@ -733,7 +813,7 @@ const clickClackSetupAdapter = {
733
813
  const claimUrl = setup.exactClaimUrl && !privateApiBaseUrl ? setup.exactClaimUrl : buildClickClackSetupClaimUrl(privateApiBaseUrl ?? setup.baseUrl);
734
814
  let claim;
735
815
  try {
736
- const { claimClickClackSetupCode } = await import("./setup-claim-CP-QZ5bt.js");
816
+ const { claimClickClackSetupCode } = await import("./setup-claim--cdFCz2P.js");
737
817
  claim = await claimClickClackSetupCode({
738
818
  claimUrl,
739
819
  code: setup.code,
@@ -893,7 +973,8 @@ const clickClackSetupContract = defineChannelSetupContract({
893
973
  cli: {
894
974
  flags: "--use-env",
895
975
  description: "Use CLICKCLACK_BOT_TOKEN"
896
- }
976
+ },
977
+ envVars: ["CLICKCLACK_BOT_TOKEN"]
897
978
  }
898
979
  },
899
980
  legacyAdapter: clickClackSetupAdapter
@@ -1128,4 +1209,4 @@ const clickClackSetupWizard = {
1128
1209
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
1129
1210
  };
1130
1211
  //#endregion
1131
- 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 };
1212
+ 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,5 +1,8 @@
1
1
  {
2
2
  "id": "clickclack",
3
+ "doctorContract": {
4
+ "configRepair": true
5
+ },
3
6
  "activation": {
4
7
  "onStartup": false
5
8
  },
@@ -18,6 +21,9 @@
18
21
  "enabled": {
19
22
  "type": "boolean"
20
23
  },
24
+ "configWrites": {
25
+ "type": "boolean"
26
+ },
21
27
  "baseUrl": {
22
28
  "type": "string",
23
29
  "format": "uri"
@@ -56,6 +62,29 @@
56
62
  ],
57
63
  "additionalProperties": false
58
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
+ },
59
88
  {
60
89
  "type": "object",
61
90
  "properties": {
@@ -144,6 +173,41 @@
144
173
  "type": "string"
145
174
  }
146
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
+ },
147
211
  "reconnectMs": {
148
212
  "type": "integer",
149
213
  "minimum": 100,
@@ -152,6 +216,9 @@
152
216
  "agentActivity": {
153
217
  "type": "boolean"
154
218
  },
219
+ "nativeProgress": {
220
+ "type": "boolean"
221
+ },
155
222
  "commandMenu": {
156
223
  "type": "boolean"
157
224
  },
@@ -180,6 +247,41 @@
180
247
  "items": {
181
248
  "type": "string"
182
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
183
285
  }
184
286
  },
185
287
  "additionalProperties": false
@@ -218,6 +320,9 @@
218
320
  "enabled": {
219
321
  "type": "boolean"
220
322
  },
323
+ "configWrites": {
324
+ "type": "boolean"
325
+ },
221
326
  "baseUrl": {
222
327
  "type": "string",
223
328
  "format": "uri"
@@ -256,6 +361,29 @@
256
361
  ],
257
362
  "additionalProperties": false
258
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
+ },
259
387
  {
260
388
  "type": "object",
261
389
  "properties": {
@@ -344,6 +472,41 @@
344
472
  "type": "string"
345
473
  }
346
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
+ },
347
510
  "reconnectMs": {
348
511
  "type": "integer",
349
512
  "minimum": 100,
@@ -352,6 +515,9 @@
352
515
  "agentActivity": {
353
516
  "type": "boolean"
354
517
  },
518
+ "nativeProgress": {
519
+ "type": "boolean"
520
+ },
355
521
  "commandMenu": {
356
522
  "type": "boolean"
357
523
  },
@@ -380,6 +546,41 @@
380
546
  "items": {
381
547
  "type": "string"
382
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
383
584
  }
384
585
  },
385
586
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/clickclack",
3
- "version": "2026.7.2-beta.7",
3
+ "version": "2026.8.1-beta.3",
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.7"
16
+ "openclaw": ">=2026.8.1-beta.3"
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.7"
142
+ "pluginApi": ">=2026.8.1-beta.3"
140
143
  },
141
144
  "build": {
142
- "openclawVersion": "2026.7.2-beta.7",
145
+ "openclawVersion": "2026.8.1-beta.3",
143
146
  "bundledDist": false
144
147
  },
145
148
  "release": {