@openclaw/clickclack 2026.7.1-beta.2 → 2026.7.1-beta.5
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,2 +1,2 @@
|
|
|
1
|
-
import { a as getClickClackRuntime, c as DEFAULT_ACCOUNT_ID, d as resolveClickClackAccount, f as resolveDefaultClickClackAccountId, i as createClickClackClient, l as listClickClackAccountIds, n as buildClickClackTarget, o as setClickClackRuntime, r as parseClickClackTarget, s as clickClackConfigSchema, t as clickClackPlugin, u as listEnabledClickClackAccounts } from "./channel-
|
|
1
|
+
import { a as getClickClackRuntime, c as DEFAULT_ACCOUNT_ID, d as resolveClickClackAccount, f as resolveDefaultClickClackAccountId, i as createClickClackClient, l as listClickClackAccountIds, n as buildClickClackTarget, o as setClickClackRuntime, r as parseClickClackTarget, s as clickClackConfigSchema, t as clickClackPlugin, u as listEnabledClickClackAccounts } from "./channel-1ZnJZTPl.js";
|
|
2
2
|
export { DEFAULT_ACCOUNT_ID, buildClickClackTarget, clickClackConfigSchema, clickClackPlugin, createClickClackClient, getClickClackRuntime, listClickClackAccountIds, listEnabledClickClackAccounts, parseClickClackTarget, resolveClickClackAccount, resolveDefaultClickClackAccountId, setClickClackRuntime };
|
|
@@ -6,15 +6,17 @@ import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-
|
|
|
6
6
|
import { buildSecretInputSchema, normalizeResolvedSecretInputString, normalizeSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
7
7
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
8
|
import { buildChannelOutboundSessionRoute, buildThreadAwareOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
9
|
-
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
|
+
import { buildChannelProgressDraftLine, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
10
10
|
import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common";
|
|
11
11
|
import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
12
12
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
13
13
|
import { z } from "zod";
|
|
14
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
14
15
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
15
16
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
16
17
|
import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
17
18
|
import { WebSocket } from "ws";
|
|
19
|
+
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
18
20
|
//#region extensions/clickclack/src/accounts.ts
|
|
19
21
|
/**
|
|
20
22
|
* Resolves ClickClack account configuration from root channel config, named
|
|
@@ -99,6 +101,7 @@ function resolveClickClackAccount(params) {
|
|
|
99
101
|
min: MIN_RECONNECT_MS,
|
|
100
102
|
max: MAX_RECONNECT_MS
|
|
101
103
|
}),
|
|
104
|
+
agentActivity: merged.agentActivity === true,
|
|
102
105
|
config: {
|
|
103
106
|
...merged,
|
|
104
107
|
allowFrom: merged.allowFrom ?? ["*"]
|
|
@@ -135,7 +138,8 @@ const ClickClackAccountConfigSchema = z.object({
|
|
|
135
138
|
toolsAllow: z.array(z.string()).optional(),
|
|
136
139
|
defaultTo: z.string().optional(),
|
|
137
140
|
allowFrom: z.array(z.string()).optional(),
|
|
138
|
-
reconnectMs: z.number().int().min(100).max(6e4).optional()
|
|
141
|
+
reconnectMs: z.number().int().min(100).max(6e4).optional(),
|
|
142
|
+
agentActivity: z.boolean().optional()
|
|
139
143
|
}).strict();
|
|
140
144
|
/**
|
|
141
145
|
* Config schema exported to core so `openclaw doctor` and config validation
|
|
@@ -213,6 +217,18 @@ async function resolveClickClackInboundAccess(params) {
|
|
|
213
217
|
* Thin ClickClack REST/websocket client used by gateway, resolver, and outbound
|
|
214
218
|
* delivery code.
|
|
215
219
|
*/
|
|
220
|
+
/**
|
|
221
|
+
* Serializes optional provenance into the wire fields. Unknown JSON fields
|
|
222
|
+
* are ignored by servers without the provenance columns, so these are safe
|
|
223
|
+
* to send unconditionally when present.
|
|
224
|
+
*/
|
|
225
|
+
function provenanceFields(provenance) {
|
|
226
|
+
const fields = {};
|
|
227
|
+
if (provenance?.model?.trim()) fields.author_model = provenance.model.trim();
|
|
228
|
+
if (provenance?.thinking?.trim()) fields.author_thinking = provenance.thinking.trim();
|
|
229
|
+
if (provenance?.runtime?.trim()) fields.author_runtime = provenance.runtime.trim();
|
|
230
|
+
return fields;
|
|
231
|
+
}
|
|
216
232
|
const CLICKCLACK_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
|
217
233
|
/**
|
|
218
234
|
* Creates a typed client for the ClickClack API using bearer-token auth.
|
|
@@ -255,16 +271,23 @@ function createClickClackClient(options) {
|
|
|
255
271
|
return (await request(`/api/dms/${encodeURIComponent(conversationId)}/messages?after_seq=${afterSeq}&limit=${limit}`)).messages;
|
|
256
272
|
},
|
|
257
273
|
thread: async (messageId) => await request(`/api/messages/${encodeURIComponent(messageId)}/thread`),
|
|
258
|
-
createChannelMessage: async (channelId, body) => {
|
|
274
|
+
createChannelMessage: async (channelId, body, opts) => {
|
|
259
275
|
return (await request(`/api/channels/${encodeURIComponent(channelId)}/messages`, {
|
|
260
276
|
method: "POST",
|
|
261
|
-
body: JSON.stringify({
|
|
277
|
+
body: JSON.stringify({
|
|
278
|
+
body,
|
|
279
|
+
...opts?.quotedMessageId ? { quoted_message_id: opts.quotedMessageId } : {},
|
|
280
|
+
...provenanceFields(opts?.provenance)
|
|
281
|
+
})
|
|
262
282
|
})).message;
|
|
263
283
|
},
|
|
264
|
-
createThreadReply: async (messageId, body) => {
|
|
284
|
+
createThreadReply: async (messageId, body, opts) => {
|
|
265
285
|
return (await request(`/api/messages/${encodeURIComponent(messageId)}/thread/replies`, {
|
|
266
286
|
method: "POST",
|
|
267
|
-
body: JSON.stringify({
|
|
287
|
+
body: JSON.stringify({
|
|
288
|
+
body,
|
|
289
|
+
...provenanceFields(opts?.provenance)
|
|
290
|
+
})
|
|
268
291
|
})).message;
|
|
269
292
|
},
|
|
270
293
|
createDirectConversation: async (workspaceId, memberIds) => {
|
|
@@ -276,12 +299,39 @@ function createClickClackClient(options) {
|
|
|
276
299
|
})
|
|
277
300
|
})).conversation;
|
|
278
301
|
},
|
|
279
|
-
|
|
280
|
-
|
|
302
|
+
/**
|
|
303
|
+
* POSTs a durable agent activity row (agent_commentary / agent_tool)
|
|
304
|
+
* through the normal message create path. Requires a bot token carrying
|
|
305
|
+
* the agent_activity:write scope on the ClickClack side.
|
|
306
|
+
*/
|
|
307
|
+
createActivityMessage: async (params) => {
|
|
308
|
+
if (!params.channelId && !params.conversationId) throw new Error("createActivityMessage requires a channelId or conversationId");
|
|
309
|
+
return (await request(params.channelId ? `/api/channels/${encodeURIComponent(params.channelId)}/messages` : `/api/dms/${encodeURIComponent(params.conversationId ?? "")}/messages`, {
|
|
281
310
|
method: "POST",
|
|
311
|
+
body: JSON.stringify({
|
|
312
|
+
body: params.body,
|
|
313
|
+
kind: params.kind,
|
|
314
|
+
turn_id: params.turnId,
|
|
315
|
+
...provenanceFields(params.provenance)
|
|
316
|
+
})
|
|
317
|
+
})).message;
|
|
318
|
+
},
|
|
319
|
+
/** PATCHes the body of an existing message (activity row coalescing). */
|
|
320
|
+
updateMessageBody: async (messageId, body) => {
|
|
321
|
+
return (await request(`/api/messages/${encodeURIComponent(messageId)}`, {
|
|
322
|
+
method: "PATCH",
|
|
282
323
|
body: JSON.stringify({ body })
|
|
283
324
|
})).message;
|
|
284
325
|
},
|
|
326
|
+
createDirectMessage: async (conversationId, body, opts) => {
|
|
327
|
+
return (await request(`/api/dms/${encodeURIComponent(conversationId)}/messages`, {
|
|
328
|
+
method: "POST",
|
|
329
|
+
body: JSON.stringify({
|
|
330
|
+
body,
|
|
331
|
+
...opts?.quotedMessageId ? { quoted_message_id: opts.quotedMessageId } : {}
|
|
332
|
+
})
|
|
333
|
+
})).message;
|
|
334
|
+
},
|
|
285
335
|
events: async (workspaceId, afterCursor) => {
|
|
286
336
|
const query = new URLSearchParams({ workspace_id: workspaceId });
|
|
287
337
|
if (afterCursor) query.set("after_cursor", afterCursor);
|
|
@@ -297,6 +347,201 @@ function createClickClackClient(options) {
|
|
|
297
347
|
};
|
|
298
348
|
}
|
|
299
349
|
//#endregion
|
|
350
|
+
//#region extensions/clickclack/src/activity.ts
|
|
351
|
+
/**
|
|
352
|
+
* Publishes agent activity (streamed commentary + tool progress) into
|
|
353
|
+
* ClickClack as durable `agent_commentary` / `agent_tool` message rows,
|
|
354
|
+
* coalesced so one logical step becomes one row instead of a row per frame.
|
|
355
|
+
*
|
|
356
|
+
* Ported from the clickglass agent-bridge sidecar, adapted from gateway
|
|
357
|
+
* websocket frames to the in-process `replyOptions.onItemEvent` seam:
|
|
358
|
+
*
|
|
359
|
+
* - Commentary/reasoning arrives as cumulative text snapshots per item id.
|
|
360
|
+
* Each segment becomes one durable row: POSTed when the segment starts
|
|
361
|
+
* streaming and PATCHed (debounced) as the snapshot grows, so prose
|
|
362
|
+
* interleaves chronologically with tool rows.
|
|
363
|
+
* - Tool/step items can emit several frames (start/update/complete) for the
|
|
364
|
+
* same call, sometimes with lane-prefixed ids (`tool:X`, `command:X`).
|
|
365
|
+
* Normalize the prefix away to one key per call, POST one row on the first
|
|
366
|
+
* frame, and PATCH it when a later frame carries a strictly longer body.
|
|
367
|
+
*/
|
|
368
|
+
/** Debounce window for PATCHing streaming commentary snapshots. */
|
|
369
|
+
const CLICKCLACK_COMMENTARY_FLUSH_MS = 700;
|
|
370
|
+
/** Item kinds rendered as agent_tool rows; everything else is commentary. */
|
|
371
|
+
const TOOL_ITEM_KINDS = /* @__PURE__ */ new Set([
|
|
372
|
+
"tool",
|
|
373
|
+
"command",
|
|
374
|
+
"command_output",
|
|
375
|
+
"patch",
|
|
376
|
+
"search",
|
|
377
|
+
"api"
|
|
378
|
+
]);
|
|
379
|
+
/** Item kinds that never become durable rows (ephemeral or plumbing lanes). */
|
|
380
|
+
const SKIPPED_ITEM_KINDS = /* @__PURE__ */ new Set(["lifecycle"]);
|
|
381
|
+
/** Item kinds emitted as cumulative commentary snapshots. */
|
|
382
|
+
const STREAMING_COMMENTARY_ITEM_KINDS = /* @__PURE__ */ new Set([
|
|
383
|
+
"preamble",
|
|
384
|
+
"commentary",
|
|
385
|
+
"analysis",
|
|
386
|
+
"thinking",
|
|
387
|
+
"reasoning"
|
|
388
|
+
]);
|
|
389
|
+
/** Provider-specific reasoning lanes normalized into one ClickClack label. */
|
|
390
|
+
const THINKING_ITEM_KINDS = /* @__PURE__ */ new Set([
|
|
391
|
+
"analysis",
|
|
392
|
+
"thinking",
|
|
393
|
+
"reasoning"
|
|
394
|
+
]);
|
|
395
|
+
function normalizedItemKind(payload) {
|
|
396
|
+
return payload.kind?.trim().toLowerCase() ?? "";
|
|
397
|
+
}
|
|
398
|
+
function commentaryBody(payload) {
|
|
399
|
+
const text = payload.progressText?.trim() || payload.summary?.trim() || payload.meta?.trim() || "";
|
|
400
|
+
if (!text) return "";
|
|
401
|
+
const kind = normalizedItemKind(payload);
|
|
402
|
+
if (THINKING_ITEM_KINDS.has(kind)) return `**Thinking**\n\n${text}`;
|
|
403
|
+
return text;
|
|
404
|
+
}
|
|
405
|
+
function activityBody(payload) {
|
|
406
|
+
const line = buildChannelProgressDraftLine({
|
|
407
|
+
event: "item",
|
|
408
|
+
itemId: payload.itemId,
|
|
409
|
+
toolCallId: payload.toolCallId,
|
|
410
|
+
itemKind: payload.kind,
|
|
411
|
+
title: payload.title,
|
|
412
|
+
name: payload.name,
|
|
413
|
+
phase: payload.phase,
|
|
414
|
+
status: payload.status,
|
|
415
|
+
summary: payload.summary,
|
|
416
|
+
progressText: payload.progressText,
|
|
417
|
+
meta: payload.meta
|
|
418
|
+
})?.text?.trim();
|
|
419
|
+
if (line) return line;
|
|
420
|
+
const head = payload.name?.trim() || payload.title?.trim();
|
|
421
|
+
const text = payload.progressText?.trim() || payload.summary?.trim();
|
|
422
|
+
if (head && text) return `**${head}**\n\n${text}`;
|
|
423
|
+
if (text) return text;
|
|
424
|
+
if (head) return head;
|
|
425
|
+
return payload.status?.trim() || payload.kind?.trim() || "";
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Creates a per-turn activity publisher. Publishing is best-effort: transport
|
|
429
|
+
* failures are reported through `onError` and never interrupt the reply turn.
|
|
430
|
+
*/
|
|
431
|
+
function createClickClackActivityPublisher(params) {
|
|
432
|
+
const flushMs = params.flushMs ?? CLICKCLACK_COMMENTARY_FLUSH_MS;
|
|
433
|
+
const commentaryByItem = /* @__PURE__ */ new Map();
|
|
434
|
+
const toolRows = /* @__PURE__ */ new Map();
|
|
435
|
+
let provenance;
|
|
436
|
+
let chain = Promise.resolve();
|
|
437
|
+
const enqueue = (work) => {
|
|
438
|
+
chain = chain.then(work).catch((error) => {
|
|
439
|
+
params.onError?.(error);
|
|
440
|
+
});
|
|
441
|
+
return chain;
|
|
442
|
+
};
|
|
443
|
+
const postRow = (kind, body) => params.client.createActivityMessage({
|
|
444
|
+
channelId: params.target.channelId,
|
|
445
|
+
conversationId: params.target.conversationId,
|
|
446
|
+
body,
|
|
447
|
+
kind,
|
|
448
|
+
turnId: params.turnId,
|
|
449
|
+
provenance
|
|
450
|
+
});
|
|
451
|
+
const flushCommentary = (segmentKey) => {
|
|
452
|
+
const segment = commentaryByItem.get(segmentKey);
|
|
453
|
+
if (!segment) return Promise.resolve();
|
|
454
|
+
if (segment.timer) {
|
|
455
|
+
clearTimeout(segment.timer);
|
|
456
|
+
segment.timer = void 0;
|
|
457
|
+
}
|
|
458
|
+
if (!segment.dirty || !segment.body.trim()) return Promise.resolve();
|
|
459
|
+
segment.dirty = false;
|
|
460
|
+
const body = segment.body;
|
|
461
|
+
return enqueue(async () => {
|
|
462
|
+
if (segment.messageId) {
|
|
463
|
+
await params.client.updateMessageBody(segment.messageId, body);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const posted = await postRow("agent_commentary", body);
|
|
467
|
+
segment.messageId = posted.id;
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
const flushAllCommentary = () => {
|
|
471
|
+
const flushes = [...commentaryByItem.keys()].map((key) => flushCommentary(key));
|
|
472
|
+
return Promise.all(flushes).then(() => void 0);
|
|
473
|
+
};
|
|
474
|
+
const handleCommentary = (payload) => {
|
|
475
|
+
const body = commentaryBody(payload);
|
|
476
|
+
if (!body.trim()) return;
|
|
477
|
+
const key = payload.itemId?.trim() || "turn";
|
|
478
|
+
let segment = commentaryByItem.get(key);
|
|
479
|
+
if (!segment) {
|
|
480
|
+
segment = {
|
|
481
|
+
body: "",
|
|
482
|
+
dirty: false
|
|
483
|
+
};
|
|
484
|
+
commentaryByItem.set(key, segment);
|
|
485
|
+
}
|
|
486
|
+
if (body.length < segment.body.length || body === segment.body) return;
|
|
487
|
+
segment.body = body;
|
|
488
|
+
segment.dirty = true;
|
|
489
|
+
if (!segment.timer) segment.timer = setTimeout(() => {
|
|
490
|
+
segment.timer = void 0;
|
|
491
|
+
flushCommentary(key);
|
|
492
|
+
}, flushMs);
|
|
493
|
+
};
|
|
494
|
+
const toolRowKey = (payload) => {
|
|
495
|
+
const toolCallId = payload.toolCallId?.trim();
|
|
496
|
+
if (toolCallId) return toolCallId;
|
|
497
|
+
return (payload.itemId?.trim() ?? "").replace(/^(tool|command):/, "");
|
|
498
|
+
};
|
|
499
|
+
const handleDiscreteItem = (payload) => {
|
|
500
|
+
const body = activityBody(payload);
|
|
501
|
+
if (!body) return;
|
|
502
|
+
const kind = TOOL_ITEM_KINDS.has(payload.kind?.trim().toLowerCase() ?? "") ? "agent_tool" : "agent_commentary";
|
|
503
|
+
flushAllCommentary();
|
|
504
|
+
const key = `${kind}:${toolRowKey(payload)}`;
|
|
505
|
+
const existing = toolRows.get(key);
|
|
506
|
+
if (!existing || !toolRowKey(payload)) {
|
|
507
|
+
const row = { body };
|
|
508
|
+
if (toolRowKey(payload)) toolRows.set(key, row);
|
|
509
|
+
enqueue(async () => {
|
|
510
|
+
const posted = await postRow(kind, row.body);
|
|
511
|
+
row.messageId = posted.id;
|
|
512
|
+
row.sentBody = row.body;
|
|
513
|
+
});
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (body.length <= existing.body.length) return;
|
|
517
|
+
existing.body = body;
|
|
518
|
+
enqueue(async () => {
|
|
519
|
+
if (existing.messageId && existing.body !== existing.sentBody) {
|
|
520
|
+
await params.client.updateMessageBody(existing.messageId, existing.body);
|
|
521
|
+
existing.sentBody = existing.body;
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
};
|
|
525
|
+
return {
|
|
526
|
+
onItemEvent: (payload) => {
|
|
527
|
+
const kind = normalizedItemKind(payload);
|
|
528
|
+
if (STREAMING_COMMENTARY_ITEM_KINDS.has(kind)) {
|
|
529
|
+
handleCommentary(payload);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (SKIPPED_ITEM_KINDS.has(kind)) return;
|
|
533
|
+
handleDiscreteItem(payload);
|
|
534
|
+
},
|
|
535
|
+
setProvenance: (next) => {
|
|
536
|
+
provenance = next;
|
|
537
|
+
},
|
|
538
|
+
finalize: async () => {
|
|
539
|
+
await flushAllCommentary();
|
|
540
|
+
await chain;
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
//#endregion
|
|
300
545
|
//#region extensions/clickclack/src/resolve.ts
|
|
301
546
|
/**
|
|
302
547
|
* Resolves a workspace slug/name/id from config to a ClickClack workspace id.
|
|
@@ -384,9 +629,9 @@ async function sendClickClackText(params) {
|
|
|
384
629
|
const parsed = parseClickClackTarget(params.to);
|
|
385
630
|
const explicitThreadId = params.threadId == null ? "" : String(params.threadId);
|
|
386
631
|
const replyToId = params.replyToId == null ? "" : String(params.replyToId);
|
|
387
|
-
if (explicitThreadId ||
|
|
388
|
-
const rootId = explicitThreadId ||
|
|
389
|
-
const message = await client.createThreadReply(rootId, params.text);
|
|
632
|
+
if (explicitThreadId || parsed.kind === "thread") {
|
|
633
|
+
const rootId = explicitThreadId || parsed.id;
|
|
634
|
+
const message = await client.createThreadReply(rootId, params.text, { provenance: params.provenance });
|
|
390
635
|
return {
|
|
391
636
|
to: params.to,
|
|
392
637
|
messageId: message.id
|
|
@@ -394,14 +639,17 @@ async function sendClickClackText(params) {
|
|
|
394
639
|
}
|
|
395
640
|
if (parsed.kind === "dm") {
|
|
396
641
|
const dm = await client.createDirectConversation(workspaceId, [parsed.id]);
|
|
397
|
-
const message = await client.createDirectMessage(dm.id, params.text);
|
|
642
|
+
const message = await client.createDirectMessage(dm.id, params.text, { quotedMessageId: replyToId || void 0 });
|
|
398
643
|
return {
|
|
399
644
|
to: params.to,
|
|
400
645
|
messageId: message.id
|
|
401
646
|
};
|
|
402
647
|
}
|
|
403
648
|
const channelId = await resolveChannelId(client, workspaceId, parsed.id);
|
|
404
|
-
const message = await client.createChannelMessage(channelId, params.text
|
|
649
|
+
const message = await client.createChannelMessage(channelId, params.text, {
|
|
650
|
+
provenance: params.provenance,
|
|
651
|
+
quotedMessageId: replyToId || void 0
|
|
652
|
+
});
|
|
405
653
|
return {
|
|
406
654
|
to: params.to,
|
|
407
655
|
messageId: message.id
|
|
@@ -421,20 +669,36 @@ function resolveAccountAgentRoute(params) {
|
|
|
421
669
|
id: params.target
|
|
422
670
|
}
|
|
423
671
|
});
|
|
424
|
-
const agentId = params.account.agentId ?? route.agentId;
|
|
672
|
+
const agentId = normalizeAgentId(params.account.agentId ?? route.agentId);
|
|
425
673
|
if (agentId === route.agentId) return route;
|
|
674
|
+
const peer = {
|
|
675
|
+
kind: params.isDirect ? "direct" : "channel",
|
|
676
|
+
id: params.target
|
|
677
|
+
};
|
|
678
|
+
const dmScope = params.cfg.session?.dmScope ?? "main";
|
|
679
|
+
const sessionKey = runtime.channel.routing.buildAgentSessionKey({
|
|
680
|
+
agentId,
|
|
681
|
+
mainKey: params.cfg.session?.mainKey,
|
|
682
|
+
channel: CHANNEL_ID$1,
|
|
683
|
+
accountId: params.account.accountId,
|
|
684
|
+
peer,
|
|
685
|
+
dmScope,
|
|
686
|
+
identityLinks: params.cfg.session?.identityLinks
|
|
687
|
+
});
|
|
688
|
+
const mainSessionKey = runtime.channel.routing.buildAgentSessionKey({
|
|
689
|
+
agentId,
|
|
690
|
+
mainKey: params.cfg.session?.mainKey,
|
|
691
|
+
channel: CHANNEL_ID$1,
|
|
692
|
+
accountId: params.account.accountId,
|
|
693
|
+
dmScope: "main"
|
|
694
|
+
});
|
|
426
695
|
return {
|
|
427
696
|
...route,
|
|
428
697
|
agentId,
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
peer: {
|
|
434
|
-
kind: params.isDirect ? "direct" : "channel",
|
|
435
|
-
id: params.target
|
|
436
|
-
}
|
|
437
|
-
})
|
|
698
|
+
dmScope,
|
|
699
|
+
sessionKey,
|
|
700
|
+
mainSessionKey,
|
|
701
|
+
lastRoutePolicy: sessionKey === mainSessionKey ? "main" : "session"
|
|
438
702
|
};
|
|
439
703
|
}
|
|
440
704
|
async function dispatchModelReply(params) {
|
|
@@ -498,6 +762,22 @@ async function handleClickClackInbound(params) {
|
|
|
498
762
|
});
|
|
499
763
|
return;
|
|
500
764
|
}
|
|
765
|
+
let turnProvenance;
|
|
766
|
+
let activity;
|
|
767
|
+
if (params.account.agentActivity && (message.channel_id || message.direct_conversation_id)) activity = createClickClackActivityPublisher({
|
|
768
|
+
client: createClickClackClient({
|
|
769
|
+
baseUrl: params.account.baseUrl,
|
|
770
|
+
token: params.account.token
|
|
771
|
+
}),
|
|
772
|
+
target: message.channel_id ? { channelId: message.channel_id } : { conversationId: message.direct_conversation_id },
|
|
773
|
+
turnId: message.id,
|
|
774
|
+
onError: (error) => {
|
|
775
|
+
runtime.logging.getChildLogger({
|
|
776
|
+
plugin: "clickclack",
|
|
777
|
+
feature: "agent-activity"
|
|
778
|
+
}).warn(`clickclack activity publish failed: ${String(error)}`);
|
|
779
|
+
}
|
|
780
|
+
});
|
|
501
781
|
const senderName = message.author?.display_name || message.author_id;
|
|
502
782
|
const previousTimestamp = runtime.channel.session.readSessionUpdatedAt({
|
|
503
783
|
storePath: runtime.channel.session.resolveStorePath(params.config.session?.store, { agentId: route.agentId }),
|
|
@@ -540,7 +820,7 @@ async function handleClickClackInbound(params) {
|
|
|
540
820
|
OriginatingTo: target,
|
|
541
821
|
CommandAuthorized: access.commandAuthorized
|
|
542
822
|
});
|
|
543
|
-
|
|
823
|
+
const dispatchPromise = runtime.channel.inbound.dispatchReply({
|
|
544
824
|
cfg: params.config,
|
|
545
825
|
channel: CHANNEL_ID$1,
|
|
546
826
|
accountId: params.account.accountId,
|
|
@@ -551,6 +831,19 @@ async function handleClickClackInbound(params) {
|
|
|
551
831
|
recordInboundSession: runtime.channel.session.recordInboundSession,
|
|
552
832
|
dispatchReplyWithBufferedBlockDispatcher: runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
553
833
|
toolsAllow: params.account.toolsAllow,
|
|
834
|
+
replyOptions: activity ? {
|
|
835
|
+
onModelSelected: (ctx) => {
|
|
836
|
+
turnProvenance = {
|
|
837
|
+
model: ctx.provider && ctx.model ? `${ctx.provider}/${ctx.model}` : ctx.model,
|
|
838
|
+
thinking: ctx.thinkLevel
|
|
839
|
+
};
|
|
840
|
+
activity?.setProvenance(turnProvenance);
|
|
841
|
+
},
|
|
842
|
+
onItemEvent: activity.onItemEvent,
|
|
843
|
+
commentaryProgressEnabled: true,
|
|
844
|
+
suppressDefaultToolProgressMessages: true,
|
|
845
|
+
allowProgressCallbacksWhenSourceDeliverySuppressed: true
|
|
846
|
+
} : void 0,
|
|
554
847
|
delivery: {
|
|
555
848
|
deliver: async (payload) => {
|
|
556
849
|
const text = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
|
|
@@ -561,7 +854,8 @@ async function handleClickClackInbound(params) {
|
|
|
561
854
|
to: target,
|
|
562
855
|
text,
|
|
563
856
|
threadId: message.parent_message_id ? message.thread_root_id : void 0,
|
|
564
|
-
replyToId: message.id
|
|
857
|
+
replyToId: message.id,
|
|
858
|
+
provenance: turnProvenance
|
|
565
859
|
});
|
|
566
860
|
},
|
|
567
861
|
onError: (error) => {
|
|
@@ -573,6 +867,11 @@ async function handleClickClackInbound(params) {
|
|
|
573
867
|
throw error instanceof Error ? error : /* @__PURE__ */ new Error(`clickclack session record failed: ${String(error)}`);
|
|
574
868
|
} }
|
|
575
869
|
});
|
|
870
|
+
try {
|
|
871
|
+
await dispatchPromise;
|
|
872
|
+
} finally {
|
|
873
|
+
await activity?.finalize();
|
|
874
|
+
}
|
|
576
875
|
}
|
|
577
876
|
//#endregion
|
|
578
877
|
//#region extensions/clickclack/src/gateway.ts
|
|
@@ -702,7 +1001,7 @@ async function startClickClackGatewayAccount(ctx) {
|
|
|
702
1001
|
event,
|
|
703
1002
|
botUserId: account.botUserId ?? ""
|
|
704
1003
|
});
|
|
705
|
-
})().catch(reject);
|
|
1004
|
+
})().catch((e) => reject(e instanceof Error ? e : new Error(`ClickClack ws message failed: ${formatErrorMessage(e)}`, { cause: e })));
|
|
706
1005
|
});
|
|
707
1006
|
socket.on("close", finishSocketCycle);
|
|
708
1007
|
socket.on("error", (error) => {
|
|
@@ -814,6 +1113,7 @@ const clickClackPlugin = createChatChannelPlugin({
|
|
|
814
1113
|
agentId,
|
|
815
1114
|
channel: CHANNEL_ID,
|
|
816
1115
|
accountId,
|
|
1116
|
+
recipientSessionExact: parsed.kind === "dm",
|
|
817
1117
|
peer: {
|
|
818
1118
|
kind: parsed.chatType === "direct" ? "direct" : "channel",
|
|
819
1119
|
id: buildClickClackTarget(parsed)
|
|
@@ -825,6 +1125,7 @@ const clickClackPlugin = createChatChannelPlugin({
|
|
|
825
1125
|
replyToId,
|
|
826
1126
|
threadId: threadId ?? (parsed.kind === "thread" ? parsed.id : void 0),
|
|
827
1127
|
currentSessionKey,
|
|
1128
|
+
useSuffix: false,
|
|
828
1129
|
canRecoverCurrentThread: () => true
|
|
829
1130
|
});
|
|
830
1131
|
},
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as clickClackPlugin } from "./channel-
|
|
1
|
+
import { t as clickClackPlugin } from "./channel-1ZnJZTPl.js";
|
|
2
2
|
export { clickClackPlugin };
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { d as resolveClickClackAccount, i as createClickClackClient, o as setClickClackRuntime, r as parseClickClackTarget } from "./channel-
|
|
1
|
+
import { d as resolveClickClackAccount, i as createClickClackClient, o as setClickClackRuntime, r as parseClickClackTarget } from "./channel-1ZnJZTPl.js";
|
|
2
2
|
import "./api.js";
|
|
3
3
|
export { createClickClackClient, parseClickClackTarget, resolveClickClackAccount, setClickClackRuntime };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/clickclack",
|
|
3
|
-
"version": "2026.7.1-beta.
|
|
3
|
+
"version": "2026.7.1-beta.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/clickclack",
|
|
9
|
-
"version": "2026.7.1-beta.
|
|
9
|
+
"version": "2026.7.1-beta.5",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ws": "8.21.0",
|
|
12
12
|
"zod": "4.4.3"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"openclaw": ">=2026.7.1-beta.
|
|
15
|
+
"openclaw": ">=2026.7.1-beta.5"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"openclaw": {
|
package/openclaw.plugin.json
CHANGED
|
@@ -157,6 +157,9 @@
|
|
|
157
157
|
"minimum": 100,
|
|
158
158
|
"maximum": 60000
|
|
159
159
|
},
|
|
160
|
+
"agentActivity": {
|
|
161
|
+
"type": "boolean"
|
|
162
|
+
},
|
|
160
163
|
"accounts": {
|
|
161
164
|
"type": "object",
|
|
162
165
|
"propertyNames": {
|
|
@@ -299,6 +302,9 @@
|
|
|
299
302
|
"type": "integer",
|
|
300
303
|
"minimum": 100,
|
|
301
304
|
"maximum": 60000
|
|
305
|
+
},
|
|
306
|
+
"agentActivity": {
|
|
307
|
+
"type": "boolean"
|
|
302
308
|
}
|
|
303
309
|
},
|
|
304
310
|
"additionalProperties": false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/clickclack",
|
|
3
|
-
"version": "2026.7.1-beta.
|
|
3
|
+
"version": "2026.7.1-beta.5",
|
|
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.1-beta.
|
|
16
|
+
"openclaw": ">=2026.7.1-beta.5"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"allowInvalidConfigRecovery": true
|
|
50
50
|
},
|
|
51
51
|
"compat": {
|
|
52
|
-
"pluginApi": ">=2026.7.1-beta.
|
|
52
|
+
"pluginApi": ">=2026.7.1-beta.5"
|
|
53
53
|
},
|
|
54
54
|
"build": {
|
|
55
|
-
"openclawVersion": "2026.7.1-beta.
|
|
55
|
+
"openclawVersion": "2026.7.1-beta.5",
|
|
56
56
|
"bundledDist": false
|
|
57
57
|
},
|
|
58
58
|
"release": {
|