@openclaw/clickclack 2026.7.1-beta.6 → 2026.7.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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-Ds6d_3nx.js";
|
|
2
2
|
export { DEFAULT_ACCOUNT_ID, buildClickClackTarget, clickClackConfigSchema, clickClackPlugin, createClickClackClient, getClickClackRuntime, listClickClackAccountIds, listEnabledClickClackAccounts, parseClickClackTarget, resolveClickClackAccount, resolveDefaultClickClackAccountId, setClickClackRuntime };
|
|
@@ -6,7 +6,7 @@ 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 { buildChannelProgressDraftLine, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
|
+
import { buildChannelProgressDraftLine, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, deriveDurableFinalDeliveryRequirements } 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";
|
|
@@ -17,6 +17,9 @@ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
|
17
17
|
import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
18
18
|
import { WebSocket } from "ws";
|
|
19
19
|
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
|
22
|
+
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
20
23
|
//#region extensions/clickclack/src/accounts.ts
|
|
21
24
|
/**
|
|
22
25
|
* Resolves ClickClack account configuration from root channel config, named
|
|
@@ -230,12 +233,31 @@ function provenanceFields(provenance) {
|
|
|
230
233
|
return fields;
|
|
231
234
|
}
|
|
232
235
|
const CLICKCLACK_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
|
236
|
+
const CLICKCLACK_CORRELATION_ID_MAX_LENGTH = 128;
|
|
237
|
+
const CLICKCLACK_CORRELATION_ID_PATTERN = /^[A-Za-z0-9._:-]+$/u;
|
|
238
|
+
const CLICKCLACK_CORRELATION_ID_HEADER = "X-Correlation-ID";
|
|
239
|
+
const CLICKCLACK_INBOUND_JSON_LIMIT_BYTES = 16 * 1024 * 1024;
|
|
240
|
+
var ClickClackHttpError = class extends Error {
|
|
241
|
+
constructor(status, detail, headers) {
|
|
242
|
+
super(`ClickClack ${status}: ${detail}`);
|
|
243
|
+
this.status = status;
|
|
244
|
+
this.headers = headers;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
/** Accepts the same bounded request-correlation shape as the ClickClack API. */
|
|
248
|
+
function normalizeClickClackCorrelationId(value) {
|
|
249
|
+
if (typeof value !== "string") return;
|
|
250
|
+
const normalized = value.trim();
|
|
251
|
+
if (!normalized || normalized.length > CLICKCLACK_CORRELATION_ID_MAX_LENGTH || !CLICKCLACK_CORRELATION_ID_PATTERN.test(normalized)) return;
|
|
252
|
+
return normalized;
|
|
253
|
+
}
|
|
233
254
|
/**
|
|
234
255
|
* Creates a typed client for the ClickClack API using bearer-token auth.
|
|
235
256
|
*/
|
|
236
257
|
function createClickClackClient(options) {
|
|
237
258
|
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
238
259
|
const fetcher = options.fetch ?? fetch;
|
|
260
|
+
const correlationId = normalizeClickClackCorrelationId(options.correlationId);
|
|
239
261
|
const headers = {
|
|
240
262
|
Authorization: `Bearer ${options.token}`,
|
|
241
263
|
Accept: "application/json"
|
|
@@ -243,6 +265,7 @@ function createClickClackClient(options) {
|
|
|
243
265
|
async function request(path, init = {}) {
|
|
244
266
|
const requestHeaders = new Headers(init.headers);
|
|
245
267
|
for (const [key, value] of Object.entries(headers)) requestHeaders.set(key, value);
|
|
268
|
+
if (correlationId) requestHeaders.set(CLICKCLACK_CORRELATION_ID_HEADER, correlationId);
|
|
246
269
|
if (init.body && !(init.body instanceof FormData)) requestHeaders.set("Content-Type", "application/json");
|
|
247
270
|
const response = await fetcher(`${baseUrl}${path}`, {
|
|
248
271
|
...init,
|
|
@@ -250,9 +273,20 @@ function createClickClackClient(options) {
|
|
|
250
273
|
});
|
|
251
274
|
if (!response.ok) {
|
|
252
275
|
const detail = await readResponseTextLimited(response, CLICKCLACK_ERROR_BODY_LIMIT_BYTES);
|
|
253
|
-
throw new
|
|
276
|
+
throw new ClickClackHttpError(response.status, detail, new Headers(response.headers));
|
|
254
277
|
}
|
|
255
|
-
return await readProviderJsonResponse(response, "ClickClack response");
|
|
278
|
+
return await readProviderJsonResponse(response, "ClickClack response", { maxBytes: CLICKCLACK_INBOUND_JSON_LIMIT_BYTES });
|
|
279
|
+
}
|
|
280
|
+
async function fetchEventPage(workspaceId, pageOptions = {}) {
|
|
281
|
+
const query = new URLSearchParams({ workspace_id: workspaceId });
|
|
282
|
+
if (pageOptions.afterCursor) query.set("after_cursor", pageOptions.afterCursor);
|
|
283
|
+
if (pageOptions.limit !== void 0) query.set("limit", String(pageOptions.limit));
|
|
284
|
+
if (pageOptions.includeTail) query.set("include_tail", "true");
|
|
285
|
+
const data = await request(`/api/realtime/events?${query.toString()}`);
|
|
286
|
+
return {
|
|
287
|
+
events: data.events,
|
|
288
|
+
...typeof data.tail_cursor === "string" ? { tailCursor: data.tail_cursor } : {}
|
|
289
|
+
};
|
|
256
290
|
}
|
|
257
291
|
return {
|
|
258
292
|
me: async () => {
|
|
@@ -271,12 +305,31 @@ function createClickClackClient(options) {
|
|
|
271
305
|
return (await request(`/api/dms/${encodeURIComponent(conversationId)}/messages?after_seq=${afterSeq}&limit=${limit}`)).messages;
|
|
272
306
|
},
|
|
273
307
|
thread: async (messageId) => await request(`/api/messages/${encodeURIComponent(messageId)}/thread`),
|
|
308
|
+
message: async (messageId) => {
|
|
309
|
+
return (await request(`/api/messages/${encodeURIComponent(messageId)}`)).message;
|
|
310
|
+
},
|
|
311
|
+
findMessageByNonce: async (params) => {
|
|
312
|
+
const query = new URLSearchParams({
|
|
313
|
+
workspace_id: params.workspaceId,
|
|
314
|
+
nonce: params.nonce
|
|
315
|
+
});
|
|
316
|
+
try {
|
|
317
|
+
return (await request(`/api/messages/by-nonce?${query.toString()}`)).message;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
if (error instanceof ClickClackHttpError && error.status === 404) {
|
|
320
|
+
if (error.headers.get("X-ClickClack-Message-Nonce") === "supported") return;
|
|
321
|
+
throw new Error("ClickClack server does not support durable message nonce lookup", { cause: error });
|
|
322
|
+
}
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
},
|
|
274
326
|
createChannelMessage: async (channelId, body, opts) => {
|
|
275
327
|
return (await request(`/api/channels/${encodeURIComponent(channelId)}/messages`, {
|
|
276
328
|
method: "POST",
|
|
277
329
|
body: JSON.stringify({
|
|
278
330
|
body,
|
|
279
331
|
...opts?.quotedMessageId ? { quoted_message_id: opts.quotedMessageId } : {},
|
|
332
|
+
...opts?.nonce ? { nonce: opts.nonce } : {},
|
|
280
333
|
...provenanceFields(opts?.provenance)
|
|
281
334
|
})
|
|
282
335
|
})).message;
|
|
@@ -286,6 +339,7 @@ function createClickClackClient(options) {
|
|
|
286
339
|
method: "POST",
|
|
287
340
|
body: JSON.stringify({
|
|
288
341
|
body,
|
|
342
|
+
...opts?.nonce ? { nonce: opts.nonce } : {},
|
|
289
343
|
...provenanceFields(opts?.provenance)
|
|
290
344
|
})
|
|
291
345
|
})).message;
|
|
@@ -299,6 +353,38 @@ function createClickClackClient(options) {
|
|
|
299
353
|
})
|
|
300
354
|
})).conversation;
|
|
301
355
|
},
|
|
356
|
+
createUpload: async (params) => {
|
|
357
|
+
const form = new FormData();
|
|
358
|
+
const bytes = new Uint8Array(params.buffer);
|
|
359
|
+
form.append("file", new Blob([bytes], { type: params.contentType }), params.filename);
|
|
360
|
+
const query = new URLSearchParams({ workspace_id: params.workspaceId });
|
|
361
|
+
if (params.nonce) query.set("nonce", params.nonce);
|
|
362
|
+
return (await request(`/api/uploads?${query.toString()}`, {
|
|
363
|
+
method: "POST",
|
|
364
|
+
body: form
|
|
365
|
+
})).upload;
|
|
366
|
+
},
|
|
367
|
+
findUploadByNonce: async (params) => {
|
|
368
|
+
const query = new URLSearchParams({
|
|
369
|
+
workspace_id: params.workspaceId,
|
|
370
|
+
nonce: params.nonce
|
|
371
|
+
});
|
|
372
|
+
try {
|
|
373
|
+
return (await request(`/api/uploads/by-nonce?${query.toString()}`)).upload;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof ClickClackHttpError && error.status === 404) {
|
|
376
|
+
if (error.headers.get("X-ClickClack-Upload-Nonce") === "supported") return;
|
|
377
|
+
throw new Error("ClickClack server does not support durable upload nonce lookup", { cause: error });
|
|
378
|
+
}
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
attachUpload: async (messageId, uploadId) => {
|
|
383
|
+
await request(`/api/messages/${encodeURIComponent(messageId)}/attachments`, {
|
|
384
|
+
method: "POST",
|
|
385
|
+
body: JSON.stringify({ upload_id: uploadId })
|
|
386
|
+
});
|
|
387
|
+
},
|
|
302
388
|
/**
|
|
303
389
|
* POSTs a durable agent activity row (agent_commentary / agent_tool)
|
|
304
390
|
* through the normal message create path. Requires a bot token carrying
|
|
@@ -328,21 +414,22 @@ function createClickClackClient(options) {
|
|
|
328
414
|
method: "POST",
|
|
329
415
|
body: JSON.stringify({
|
|
330
416
|
body,
|
|
331
|
-
...opts?.quotedMessageId ? { quoted_message_id: opts.quotedMessageId } : {}
|
|
417
|
+
...opts?.quotedMessageId ? { quoted_message_id: opts.quotedMessageId } : {},
|
|
418
|
+
...opts?.nonce ? { nonce: opts.nonce } : {}
|
|
332
419
|
})
|
|
333
420
|
})).message;
|
|
334
421
|
},
|
|
335
|
-
events: async (workspaceId, afterCursor) => {
|
|
336
|
-
|
|
337
|
-
if (afterCursor) query.set("after_cursor", afterCursor);
|
|
338
|
-
return (await request(`/api/realtime/events?${query.toString()}`)).events;
|
|
339
|
-
},
|
|
422
|
+
events: async (workspaceId, afterCursor) => (await fetchEventPage(workspaceId, { afterCursor })).events,
|
|
423
|
+
eventPage: fetchEventPage,
|
|
340
424
|
websocket: (workspaceId, afterCursor) => {
|
|
341
425
|
const url = new URL(`${baseUrl}/api/realtime/ws`);
|
|
342
426
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
343
427
|
url.searchParams.set("workspace_id", workspaceId);
|
|
344
428
|
if (afterCursor) url.searchParams.set("after_cursor", afterCursor);
|
|
345
|
-
return new WebSocket(url, {
|
|
429
|
+
return new WebSocket(url, {
|
|
430
|
+
headers: { Authorization: `Bearer ${options.token}` },
|
|
431
|
+
maxPayload: CLICKCLACK_INBOUND_JSON_LIMIT_BYTES
|
|
432
|
+
});
|
|
346
433
|
}
|
|
347
434
|
};
|
|
348
435
|
}
|
|
@@ -612,52 +699,285 @@ function looksLikeClickClackTarget(raw) {
|
|
|
612
699
|
* Outbound ClickClack delivery helpers for channel messages, thread replies,
|
|
613
700
|
* and direct messages.
|
|
614
701
|
*/
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
702
|
+
const CLICKCLACK_MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
703
|
+
async function createTargetMessage(params) {
|
|
704
|
+
const parsed = parseClickClackTarget(params.to);
|
|
705
|
+
const explicitThreadId = params.threadId == null ? "" : String(params.threadId);
|
|
706
|
+
const replyToId = params.replyToId == null ? "" : String(params.replyToId);
|
|
707
|
+
if (explicitThreadId || parsed.kind === "thread") {
|
|
708
|
+
const rootId = explicitThreadId || parsed.id;
|
|
709
|
+
await params.onPlatformSendDispatch?.();
|
|
710
|
+
return await params.client.createThreadReply(rootId, params.text, {
|
|
711
|
+
provenance: params.provenance,
|
|
712
|
+
nonce: params.nonce
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
if (parsed.kind === "dm") {
|
|
716
|
+
await params.onPlatformSendDispatch?.();
|
|
717
|
+
const dm = await params.client.createDirectConversation(params.workspaceId, [parsed.id]);
|
|
718
|
+
return await params.client.createDirectMessage(dm.id, params.text, {
|
|
719
|
+
quotedMessageId: replyToId || void 0,
|
|
720
|
+
nonce: params.nonce
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
const channelId = await resolveChannelId(params.client, params.workspaceId, parsed.id);
|
|
724
|
+
await params.onPlatformSendDispatch?.();
|
|
725
|
+
return await params.client.createChannelMessage(channelId, params.text, {
|
|
726
|
+
provenance: params.provenance,
|
|
727
|
+
quotedMessageId: replyToId || void 0,
|
|
728
|
+
nonce: params.nonce
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
function durableDeliveryDigest(params) {
|
|
732
|
+
if (!params.deliveryQueueId) return;
|
|
733
|
+
if (!Number.isSafeInteger(params.deliveryPartIndex) || (params.deliveryPartIndex ?? -1) < 0) throw new Error("ClickClack durable delivery requires a stable delivery part index");
|
|
734
|
+
return createHash("sha256").update(`${params.deliveryQueueId}\n${params.deliveryPartIndex}`).digest("hex");
|
|
735
|
+
}
|
|
736
|
+
function mediaDeliveryNonces(params) {
|
|
737
|
+
const digest = durableDeliveryDigest(params);
|
|
738
|
+
if (!digest) return {};
|
|
739
|
+
return {
|
|
740
|
+
message: `openclaw-media:${digest}`,
|
|
741
|
+
upload: `openclaw-upload:${digest}`
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function textDeliveryNonce(params) {
|
|
745
|
+
const digest = durableDeliveryDigest(params);
|
|
746
|
+
return digest ? `openclaw-text:${digest}` : void 0;
|
|
747
|
+
}
|
|
748
|
+
function createDispatchOnce(onPlatformSendDispatch) {
|
|
749
|
+
let dispatched = false;
|
|
750
|
+
return async () => {
|
|
751
|
+
if (dispatched) return;
|
|
752
|
+
await onPlatformSendDispatch?.();
|
|
753
|
+
dispatched = true;
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
async function attachUploadRetrySafe(params) {
|
|
757
|
+
try {
|
|
758
|
+
await params.client.attachUpload(params.messageId, params.uploadId);
|
|
759
|
+
} catch (firstError) {
|
|
760
|
+
try {
|
|
761
|
+
if ((await params.client.message(params.messageId)).attachments?.some((attachment) => attachment.id === params.uploadId)) return;
|
|
762
|
+
} catch {}
|
|
763
|
+
try {
|
|
764
|
+
await params.client.attachUpload(params.messageId, params.uploadId);
|
|
765
|
+
} catch {
|
|
766
|
+
throw firstError;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function createOutboundContext(params) {
|
|
620
771
|
const account = resolveClickClackAccount({
|
|
621
772
|
cfg: params.cfg,
|
|
622
773
|
accountId: params.accountId
|
|
623
774
|
});
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
775
|
+
return {
|
|
776
|
+
account,
|
|
777
|
+
client: createClickClackClient({
|
|
778
|
+
baseUrl: account.baseUrl,
|
|
779
|
+
token: account.token,
|
|
780
|
+
correlationId: params.correlationId
|
|
781
|
+
})
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Sends visible text to a normalized ClickClack target and returns the created
|
|
786
|
+
* message id, or undefined when sanitization removes all content.
|
|
787
|
+
*/
|
|
788
|
+
async function sendClickClackText(params) {
|
|
789
|
+
const text = sanitizeAssistantVisibleText(params.text);
|
|
790
|
+
if (!text) return;
|
|
791
|
+
const { account, client } = createOutboundContext(params);
|
|
792
|
+
const workspaceId = await resolveWorkspaceId(client, account.workspace);
|
|
793
|
+
const dispatch = createDispatchOnce(params.onPlatformSendDispatch);
|
|
794
|
+
return (await createTargetMessage({
|
|
795
|
+
client,
|
|
796
|
+
workspaceId,
|
|
797
|
+
to: params.to,
|
|
798
|
+
text,
|
|
799
|
+
threadId: params.threadId,
|
|
800
|
+
replyToId: params.replyToId,
|
|
801
|
+
provenance: params.provenance,
|
|
802
|
+
nonce: textDeliveryNonce({
|
|
803
|
+
deliveryQueueId: params.deliveryQueueId,
|
|
804
|
+
deliveryPartIndex: params.deliveryPartIndex
|
|
805
|
+
}),
|
|
806
|
+
onPlatformSendDispatch: dispatch
|
|
807
|
+
})).id;
|
|
808
|
+
}
|
|
809
|
+
/** Resolves, uploads, sends, then attaches one file to a ClickClack message. */
|
|
810
|
+
async function sendClickClackMedia(params) {
|
|
811
|
+
const nonces = mediaDeliveryNonces({
|
|
812
|
+
deliveryQueueId: params.deliveryQueueId,
|
|
813
|
+
deliveryPartIndex: params.deliveryPartIndex
|
|
814
|
+
});
|
|
815
|
+
const preloadedMedia = nonces.upload ? void 0 : await loadOutboundMediaFromUrl(params.mediaUrl, {
|
|
816
|
+
maxBytes: CLICKCLACK_MAX_UPLOAD_BYTES,
|
|
817
|
+
mediaAccess: params.mediaAccess,
|
|
818
|
+
mediaLocalRoots: params.mediaLocalRoots,
|
|
819
|
+
mediaReadFile: params.mediaReadFile
|
|
627
820
|
});
|
|
821
|
+
const { account, client } = createOutboundContext(params);
|
|
628
822
|
const workspaceId = await resolveWorkspaceId(client, account.workspace);
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
823
|
+
const persistedUpload = nonces.upload ? await client.findUploadByNonce({
|
|
824
|
+
workspaceId,
|
|
825
|
+
nonce: nonces.upload
|
|
826
|
+
}) : void 0;
|
|
827
|
+
const dispatch = createDispatchOnce(params.onPlatformSendDispatch);
|
|
828
|
+
let upload = persistedUpload;
|
|
829
|
+
let mediaFilename = preloadedMedia?.fileName?.trim();
|
|
830
|
+
if (!upload) {
|
|
831
|
+
const media = preloadedMedia ?? await loadOutboundMediaFromUrl(params.mediaUrl, {
|
|
832
|
+
maxBytes: CLICKCLACK_MAX_UPLOAD_BYTES,
|
|
833
|
+
mediaAccess: params.mediaAccess,
|
|
834
|
+
mediaLocalRoots: params.mediaLocalRoots,
|
|
835
|
+
mediaReadFile: params.mediaReadFile
|
|
836
|
+
});
|
|
837
|
+
const filename = media.fileName?.trim() || "attachment";
|
|
838
|
+
mediaFilename = filename;
|
|
839
|
+
const contentType = media.contentType?.trim() || "application/octet-stream";
|
|
840
|
+
await dispatch();
|
|
841
|
+
upload = await client.createUpload({
|
|
842
|
+
workspaceId,
|
|
843
|
+
buffer: media.buffer,
|
|
844
|
+
filename,
|
|
845
|
+
contentType,
|
|
846
|
+
...nonces.upload ? { nonce: nonces.upload } : {}
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
const text = sanitizeAssistantVisibleText(params.text) || mediaFilename || upload.filename || "attachment";
|
|
850
|
+
const message = await createTargetMessage({
|
|
851
|
+
client,
|
|
852
|
+
workspaceId,
|
|
853
|
+
to: params.to,
|
|
854
|
+
text,
|
|
855
|
+
threadId: params.threadId,
|
|
856
|
+
replyToId: params.replyToId,
|
|
857
|
+
nonce: nonces.message,
|
|
858
|
+
onPlatformSendDispatch: dispatch
|
|
859
|
+
});
|
|
860
|
+
await attachUploadRetrySafe({
|
|
861
|
+
client,
|
|
862
|
+
messageId: message.id,
|
|
863
|
+
uploadId: upload.id
|
|
864
|
+
});
|
|
865
|
+
return message.id;
|
|
866
|
+
}
|
|
867
|
+
function collectReconciliationMediaUrls(ctx) {
|
|
868
|
+
const planned = ctx.renderedBatchPlan?.items[0]?.mediaUrls;
|
|
869
|
+
if (planned?.length) return planned.map((url) => url.trim()).filter(Boolean);
|
|
870
|
+
const payload = ctx.payloads[0];
|
|
871
|
+
return [payload?.mediaUrl, ...payload?.mediaUrls ?? []].map((url) => url?.trim()).filter((url) => Boolean(url));
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Completes an unknown durable send through ClickClack's message/upload nonces.
|
|
875
|
+
* Media recovery never rereads the original source after restart.
|
|
876
|
+
*/
|
|
877
|
+
async function reconcileClickClackUnknownSend(ctx) {
|
|
878
|
+
if (ctx.payloads.length !== 1 || (ctx.renderedBatchPlan?.items.length ?? 1) !== 1) return {
|
|
879
|
+
status: "unresolved",
|
|
880
|
+
error: "ClickClack reconciliation requires exactly one payload"
|
|
881
|
+
};
|
|
882
|
+
const mediaUrls = collectReconciliationMediaUrls(ctx);
|
|
883
|
+
const { account, client } = createOutboundContext({
|
|
884
|
+
cfg: ctx.cfg,
|
|
885
|
+
accountId: ctx.accountId
|
|
886
|
+
});
|
|
887
|
+
const workspaceId = await resolveWorkspaceId(client, account.workspace);
|
|
888
|
+
const effectiveReplyToId = ctx.effectiveReplyToId !== void 0 ? ctx.effectiveReplyToId : ctx.replyToMode === "off" ? void 0 : ctx.replyToId;
|
|
889
|
+
const payload = ctx.payloads[0];
|
|
890
|
+
const caption = ctx.renderedBatchPlan?.items[0]?.text ?? payload?.text ?? "";
|
|
891
|
+
if (mediaUrls.length === 0) {
|
|
892
|
+
const nonce = textDeliveryNonce({
|
|
893
|
+
deliveryQueueId: ctx.queueId,
|
|
894
|
+
deliveryPartIndex: 0
|
|
895
|
+
});
|
|
896
|
+
if (!nonce || !sanitizeAssistantVisibleText(caption)) return { status: "not_sent" };
|
|
897
|
+
const message = await client.findMessageByNonce({
|
|
898
|
+
workspaceId,
|
|
899
|
+
nonce
|
|
900
|
+
});
|
|
901
|
+
if (!message) return { status: "not_sent" };
|
|
902
|
+
const receipt = createMessageReceiptFromOutboundResults({
|
|
903
|
+
results: [{
|
|
904
|
+
channel: "clickclack",
|
|
905
|
+
messageId: message.id
|
|
906
|
+
}],
|
|
907
|
+
threadId: ctx.threadId == null ? void 0 : String(ctx.threadId),
|
|
908
|
+
replyToId: effectiveReplyToId ?? void 0,
|
|
909
|
+
kind: "text"
|
|
910
|
+
});
|
|
635
911
|
return {
|
|
636
|
-
|
|
637
|
-
messageId: message.id
|
|
912
|
+
status: "sent",
|
|
913
|
+
messageId: message.id,
|
|
914
|
+
receipt
|
|
638
915
|
};
|
|
639
916
|
}
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
|
|
917
|
+
const parts = await Promise.all(mediaUrls.map(async (_mediaUrl, index) => {
|
|
918
|
+
const nonces = mediaDeliveryNonces({
|
|
919
|
+
deliveryQueueId: ctx.queueId,
|
|
920
|
+
deliveryPartIndex: index
|
|
921
|
+
});
|
|
922
|
+
if (!nonces.upload || !nonces.message) throw new Error("ClickClack durable media nonces were not derived");
|
|
923
|
+
const [upload, message] = await Promise.all([client.findUploadByNonce({
|
|
924
|
+
workspaceId,
|
|
925
|
+
nonce: nonces.upload
|
|
926
|
+
}), client.findMessageByNonce({
|
|
927
|
+
workspaceId,
|
|
928
|
+
nonce: nonces.message
|
|
929
|
+
})]);
|
|
643
930
|
return {
|
|
644
|
-
|
|
645
|
-
|
|
931
|
+
upload,
|
|
932
|
+
message
|
|
933
|
+
};
|
|
934
|
+
}));
|
|
935
|
+
for (const part of parts) {
|
|
936
|
+
if (part.message && !part.upload) return {
|
|
937
|
+
status: "unresolved",
|
|
938
|
+
error: `ClickClack message ${part.message.id} exists without its nonce-keyed upload`,
|
|
939
|
+
retryable: false
|
|
646
940
|
};
|
|
941
|
+
if (!part.message) return { status: "not_sent" };
|
|
647
942
|
}
|
|
648
|
-
const
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
943
|
+
const messageIds = [];
|
|
944
|
+
for (const part of parts) {
|
|
945
|
+
const message = part.message;
|
|
946
|
+
const upload = part.upload;
|
|
947
|
+
if (!message || !upload) throw new Error("ClickClack reconciliation state changed unexpectedly");
|
|
948
|
+
if (!message.attachments?.some((attachment) => attachment.id === upload.id)) await attachUploadRetrySafe({
|
|
949
|
+
client,
|
|
950
|
+
messageId: message.id,
|
|
951
|
+
uploadId: upload.id
|
|
952
|
+
});
|
|
953
|
+
messageIds.push(message.id);
|
|
954
|
+
}
|
|
955
|
+
const receipt = createMessageReceiptFromOutboundResults({
|
|
956
|
+
results: messageIds.map((messageId) => ({
|
|
957
|
+
channel: "clickclack",
|
|
958
|
+
messageId
|
|
959
|
+
})),
|
|
960
|
+
threadId: ctx.threadId == null ? void 0 : String(ctx.threadId),
|
|
961
|
+
replyToId: effectiveReplyToId ?? void 0,
|
|
962
|
+
kind: "media"
|
|
652
963
|
});
|
|
964
|
+
const messageId = messageIds.at(-1);
|
|
653
965
|
return {
|
|
654
|
-
|
|
655
|
-
messageId:
|
|
966
|
+
status: "sent",
|
|
967
|
+
...messageId ? { messageId } : {},
|
|
968
|
+
receipt
|
|
656
969
|
};
|
|
657
970
|
}
|
|
658
971
|
//#endregion
|
|
659
972
|
//#region extensions/clickclack/src/inbound.ts
|
|
660
973
|
const CHANNEL_ID$1 = "clickclack";
|
|
974
|
+
const CLICKCLACK_MESSAGE_ID_PATTERN = /^msg_[0-9a-hjkmnp-tv-z]{26}$/u;
|
|
975
|
+
function hasClickClackReplyMedia(payload) {
|
|
976
|
+
return Boolean(payload.mediaUrl?.trim() || payload.mediaUrls?.some((mediaUrl) => typeof mediaUrl === "string" && mediaUrl.trim()));
|
|
977
|
+
}
|
|
978
|
+
function resolveClickClackAgentRunId(messageId) {
|
|
979
|
+
return CLICKCLACK_MESSAGE_ID_PATTERN.test(messageId) ? `${CHANNEL_ID$1}:${messageId}` : void 0;
|
|
980
|
+
}
|
|
661
981
|
function resolveAccountAgentRoute(params) {
|
|
662
982
|
const runtime = getClickClackRuntime();
|
|
663
983
|
const route = runtime.channel.routing.resolveAgentRoute({
|
|
@@ -702,10 +1022,10 @@ function resolveAccountAgentRoute(params) {
|
|
|
702
1022
|
};
|
|
703
1023
|
}
|
|
704
1024
|
async function dispatchModelReply(params) {
|
|
705
|
-
const
|
|
1025
|
+
const runtime = getClickClackRuntime();
|
|
1026
|
+
const text = (await runtime.llm.complete({
|
|
706
1027
|
agentId: params.route.agentId,
|
|
707
1028
|
model: params.account.model,
|
|
708
|
-
maxTokens: 96,
|
|
709
1029
|
purpose: "clickclack bot reply",
|
|
710
1030
|
systemPrompt: params.account.systemPrompt,
|
|
711
1031
|
messages: [{
|
|
@@ -713,14 +1033,21 @@ async function dispatchModelReply(params) {
|
|
|
713
1033
|
content: params.message.body
|
|
714
1034
|
}]
|
|
715
1035
|
})).text.trim();
|
|
716
|
-
if (!text)
|
|
1036
|
+
if (!text) {
|
|
1037
|
+
runtime.logging.getChildLogger({
|
|
1038
|
+
plugin: "clickclack",
|
|
1039
|
+
feature: "model-reply"
|
|
1040
|
+
}).warn(`[${params.account.accountId}] ClickClack model reply produced no sendable text`);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
717
1043
|
await sendClickClackText({
|
|
718
1044
|
cfg: params.cfg,
|
|
719
1045
|
accountId: params.account.accountId,
|
|
720
1046
|
to: params.target,
|
|
721
1047
|
text,
|
|
722
1048
|
threadId: params.message.parent_message_id ? params.message.thread_root_id : void 0,
|
|
723
|
-
replyToId: params.message.id
|
|
1049
|
+
replyToId: params.message.id,
|
|
1050
|
+
correlationId: params.correlationId
|
|
724
1051
|
});
|
|
725
1052
|
}
|
|
726
1053
|
/**
|
|
@@ -758,7 +1085,8 @@ async function handleClickClackInbound(params) {
|
|
|
758
1085
|
cfg: params.config,
|
|
759
1086
|
message,
|
|
760
1087
|
route,
|
|
761
|
-
target
|
|
1088
|
+
target,
|
|
1089
|
+
correlationId: params.correlationId
|
|
762
1090
|
});
|
|
763
1091
|
return;
|
|
764
1092
|
}
|
|
@@ -767,7 +1095,8 @@ async function handleClickClackInbound(params) {
|
|
|
767
1095
|
if (params.account.agentActivity && (message.channel_id || message.direct_conversation_id)) activity = createClickClackActivityPublisher({
|
|
768
1096
|
client: createClickClackClient({
|
|
769
1097
|
baseUrl: params.account.baseUrl,
|
|
770
|
-
token: params.account.token
|
|
1098
|
+
token: params.account.token,
|
|
1099
|
+
correlationId: params.correlationId
|
|
771
1100
|
}),
|
|
772
1101
|
target: message.channel_id ? { channelId: message.channel_id } : { conversationId: message.direct_conversation_id },
|
|
773
1102
|
turnId: message.id,
|
|
@@ -820,6 +1149,20 @@ async function handleClickClackInbound(params) {
|
|
|
820
1149
|
OriginatingTo: target,
|
|
821
1150
|
CommandAuthorized: access.commandAuthorized
|
|
822
1151
|
});
|
|
1152
|
+
const runId = resolveClickClackAgentRunId(message.id);
|
|
1153
|
+
const activityReplyOptions = activity ? {
|
|
1154
|
+
onModelSelected: (ctx) => {
|
|
1155
|
+
turnProvenance = {
|
|
1156
|
+
model: ctx.provider && ctx.model ? `${ctx.provider}/${ctx.model}` : ctx.model,
|
|
1157
|
+
thinking: ctx.thinkLevel
|
|
1158
|
+
};
|
|
1159
|
+
activity?.setProvenance(turnProvenance);
|
|
1160
|
+
},
|
|
1161
|
+
onItemEvent: activity.onItemEvent,
|
|
1162
|
+
commentaryProgressEnabled: true,
|
|
1163
|
+
suppressDefaultToolProgressMessages: true,
|
|
1164
|
+
allowProgressCallbacksWhenSourceDeliverySuppressed: true
|
|
1165
|
+
} : void 0;
|
|
823
1166
|
const dispatchPromise = runtime.channel.inbound.dispatchReply({
|
|
824
1167
|
cfg: params.config,
|
|
825
1168
|
channel: CHANNEL_ID$1,
|
|
@@ -831,21 +1174,13 @@ async function handleClickClackInbound(params) {
|
|
|
831
1174
|
recordInboundSession: runtime.channel.session.recordInboundSession,
|
|
832
1175
|
dispatchReplyWithBufferedBlockDispatcher: runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
833
1176
|
toolsAllow: params.account.toolsAllow,
|
|
834
|
-
replyOptions:
|
|
835
|
-
|
|
836
|
-
|
|
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
|
|
1177
|
+
replyOptions: runId || activityReplyOptions ? {
|
|
1178
|
+
...runId ? { runId } : {},
|
|
1179
|
+
...activityReplyOptions
|
|
846
1180
|
} : void 0,
|
|
847
1181
|
delivery: {
|
|
848
1182
|
deliver: async (payload) => {
|
|
1183
|
+
if (hasClickClackReplyMedia(payload)) throw new Error("ClickClack media reply requires durable delivery");
|
|
849
1184
|
const text = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
|
|
850
1185
|
if (!text.trim()) return;
|
|
851
1186
|
await sendClickClackText({
|
|
@@ -855,9 +1190,25 @@ async function handleClickClackInbound(params) {
|
|
|
855
1190
|
text,
|
|
856
1191
|
threadId: message.parent_message_id ? message.thread_root_id : void 0,
|
|
857
1192
|
replyToId: message.id,
|
|
858
|
-
provenance: turnProvenance
|
|
1193
|
+
provenance: turnProvenance,
|
|
1194
|
+
correlationId: params.correlationId
|
|
859
1195
|
});
|
|
860
1196
|
},
|
|
1197
|
+
durable: (payload) => {
|
|
1198
|
+
if (!hasClickClackReplyMedia(payload)) return false;
|
|
1199
|
+
const threadId = message.parent_message_id ? message.thread_root_id : void 0;
|
|
1200
|
+
return {
|
|
1201
|
+
to: target,
|
|
1202
|
+
threadId,
|
|
1203
|
+
replyToId: message.id,
|
|
1204
|
+
requiredCapabilities: deriveDurableFinalDeliveryRequirements({
|
|
1205
|
+
payload,
|
|
1206
|
+
threadId,
|
|
1207
|
+
replyToId: message.id,
|
|
1208
|
+
reconcileUnknownSend: true
|
|
1209
|
+
})
|
|
1210
|
+
};
|
|
1211
|
+
},
|
|
861
1212
|
onError: (error) => {
|
|
862
1213
|
throw error instanceof Error ? error : /* @__PURE__ */ new Error(`clickclack dispatch failed: ${String(error)}`);
|
|
863
1214
|
}
|
|
@@ -875,10 +1226,14 @@ async function handleClickClackInbound(params) {
|
|
|
875
1226
|
}
|
|
876
1227
|
//#endregion
|
|
877
1228
|
//#region extensions/clickclack/src/gateway.ts
|
|
1229
|
+
const CLICKCLACK_EVENT_PAGE_LIMIT = 500;
|
|
878
1230
|
function payloadString(event, key) {
|
|
879
1231
|
const value = event.payload?.[key];
|
|
880
1232
|
return typeof value === "string" ? value : "";
|
|
881
1233
|
}
|
|
1234
|
+
function eventCorrelationId(event) {
|
|
1235
|
+
return normalizeClickClackCorrelationId(event.payload?.correlation_id);
|
|
1236
|
+
}
|
|
882
1237
|
async function resolveEventMessage(params) {
|
|
883
1238
|
const messageId = payloadString(params.event, "message_id");
|
|
884
1239
|
if (!messageId) return null;
|
|
@@ -908,8 +1263,13 @@ function parseSocketEvent(data) {
|
|
|
908
1263
|
async function processEvent(params) {
|
|
909
1264
|
if (params.event.type !== "message.created" && params.event.type !== "thread.reply_created") return;
|
|
910
1265
|
if (payloadString(params.event, "author_id") === params.botUserId) return;
|
|
1266
|
+
const correlationId = eventCorrelationId(params.event);
|
|
911
1267
|
const message = await resolveEventMessage({
|
|
912
|
-
client:
|
|
1268
|
+
client: correlationId ? createClickClackClient({
|
|
1269
|
+
baseUrl: params.account.baseUrl,
|
|
1270
|
+
token: params.account.token,
|
|
1271
|
+
correlationId
|
|
1272
|
+
}) : params.client,
|
|
913
1273
|
event: params.event
|
|
914
1274
|
});
|
|
915
1275
|
if (!message || message.author_id === params.botUserId) return;
|
|
@@ -924,9 +1284,27 @@ async function processEvent(params) {
|
|
|
924
1284
|
account: params.account,
|
|
925
1285
|
config: params.config,
|
|
926
1286
|
message,
|
|
927
|
-
access
|
|
1287
|
+
access,
|
|
1288
|
+
...correlationId ? { correlationId } : {}
|
|
928
1289
|
});
|
|
929
1290
|
}
|
|
1291
|
+
async function drainEventBacklog(params) {
|
|
1292
|
+
let afterCursor = params.afterCursor;
|
|
1293
|
+
while (!params.abortSignal.aborted) {
|
|
1294
|
+
const events = (await params.client.eventPage(params.workspaceId, {
|
|
1295
|
+
afterCursor,
|
|
1296
|
+
limit: CLICKCLACK_EVENT_PAGE_LIMIT
|
|
1297
|
+
})).events;
|
|
1298
|
+
for (const event of events) {
|
|
1299
|
+
if (params.abortSignal.aborted) return afterCursor;
|
|
1300
|
+
if (!event.cursor || event.cursor === afterCursor) throw new Error("ClickClack event backlog returned a non-advancing cursor");
|
|
1301
|
+
await params.onEvent(event);
|
|
1302
|
+
afterCursor = event.cursor;
|
|
1303
|
+
}
|
|
1304
|
+
if (events.length === 0) return afterCursor;
|
|
1305
|
+
}
|
|
1306
|
+
return afterCursor;
|
|
1307
|
+
}
|
|
930
1308
|
async function startClickClackGatewayAccount(ctx) {
|
|
931
1309
|
const configuredAccount = resolveClickClackAccount({
|
|
932
1310
|
cfg: ctx.cfg,
|
|
@@ -955,20 +1333,27 @@ async function startClickClackGatewayAccount(ctx) {
|
|
|
955
1333
|
let initialized = false;
|
|
956
1334
|
try {
|
|
957
1335
|
while (!ctx.abortSignal.aborted) {
|
|
958
|
-
const backlog = await client.events(workspaceId, afterCursor);
|
|
959
1336
|
if (!initialized) {
|
|
960
|
-
|
|
1337
|
+
const page = await client.eventPage(workspaceId, { includeTail: true });
|
|
1338
|
+
if (page.tailCursor !== void 0) afterCursor = page.tailCursor;
|
|
1339
|
+
else for (const event of page.events) afterCursor = event.cursor || afterCursor;
|
|
961
1340
|
initialized = true;
|
|
962
|
-
} else
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1341
|
+
} else afterCursor = await drainEventBacklog({
|
|
1342
|
+
client,
|
|
1343
|
+
workspaceId,
|
|
1344
|
+
afterCursor,
|
|
1345
|
+
abortSignal: ctx.abortSignal,
|
|
1346
|
+
onEvent: async (event) => {
|
|
1347
|
+
await processEvent({
|
|
1348
|
+
account,
|
|
1349
|
+
config: ctx.cfg,
|
|
1350
|
+
client,
|
|
1351
|
+
event,
|
|
1352
|
+
botUserId: account.botUserId
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
if (ctx.abortSignal.aborted) break;
|
|
972
1357
|
const socket = client.websocket(workspaceId, afterCursor);
|
|
973
1358
|
await new Promise((resolve, reject) => {
|
|
974
1359
|
let settled = false;
|
|
@@ -1035,36 +1420,81 @@ const CHANNEL_ID = "clickclack";
|
|
|
1035
1420
|
const meta = { ...getChatChannelMeta(CHANNEL_ID) };
|
|
1036
1421
|
const clickClackMessageAdapter = defineChannelMessageAdapter({
|
|
1037
1422
|
id: CHANNEL_ID,
|
|
1038
|
-
durableFinal: {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
})
|
|
1066
|
-
|
|
1067
|
-
|
|
1423
|
+
durableFinal: {
|
|
1424
|
+
capabilities: {
|
|
1425
|
+
text: true,
|
|
1426
|
+
media: true,
|
|
1427
|
+
replyTo: true,
|
|
1428
|
+
thread: true,
|
|
1429
|
+
messageSendingHooks: true,
|
|
1430
|
+
reconcileUnknownSend: true
|
|
1431
|
+
},
|
|
1432
|
+
reconcileUnknownSendKinds: {
|
|
1433
|
+
text: true,
|
|
1434
|
+
media: true
|
|
1435
|
+
},
|
|
1436
|
+
reconcileUnknownSend: reconcileClickClackUnknownSend
|
|
1437
|
+
},
|
|
1438
|
+
send: {
|
|
1439
|
+
text: async (ctx) => {
|
|
1440
|
+
const messageId = await sendClickClackText({
|
|
1441
|
+
cfg: ctx.cfg,
|
|
1442
|
+
accountId: ctx.accountId,
|
|
1443
|
+
to: ctx.to,
|
|
1444
|
+
text: ctx.text,
|
|
1445
|
+
threadId: ctx.threadId,
|
|
1446
|
+
replyToId: ctx.replyToId,
|
|
1447
|
+
deliveryQueueId: ctx.deliveryQueueId,
|
|
1448
|
+
deliveryPartIndex: ctx.deliveryPartIndex,
|
|
1449
|
+
onPlatformSendDispatch: ctx.onPlatformSendDispatch
|
|
1450
|
+
});
|
|
1451
|
+
const threadId = ctx.threadId == null ? void 0 : String(ctx.threadId);
|
|
1452
|
+
const replyToId = ctx.replyToId ?? void 0;
|
|
1453
|
+
return {
|
|
1454
|
+
...messageId ? { messageId } : {},
|
|
1455
|
+
receipt: createMessageReceiptFromOutboundResults({
|
|
1456
|
+
results: messageId ? [{
|
|
1457
|
+
channel: CHANNEL_ID,
|
|
1458
|
+
messageId
|
|
1459
|
+
}] : [],
|
|
1460
|
+
threadId,
|
|
1461
|
+
replyToId,
|
|
1462
|
+
kind: "text"
|
|
1463
|
+
})
|
|
1464
|
+
};
|
|
1465
|
+
},
|
|
1466
|
+
media: async (ctx) => {
|
|
1467
|
+
const messageId = await sendClickClackMedia({
|
|
1468
|
+
cfg: ctx.cfg,
|
|
1469
|
+
accountId: ctx.accountId,
|
|
1470
|
+
to: ctx.to,
|
|
1471
|
+
text: ctx.text,
|
|
1472
|
+
mediaUrl: ctx.mediaUrl,
|
|
1473
|
+
mediaAccess: ctx.mediaAccess,
|
|
1474
|
+
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
1475
|
+
mediaReadFile: ctx.mediaReadFile,
|
|
1476
|
+
threadId: ctx.threadId,
|
|
1477
|
+
replyToId: ctx.replyToId,
|
|
1478
|
+
deliveryQueueId: ctx.deliveryQueueId,
|
|
1479
|
+
deliveryPartIndex: ctx.deliveryPartIndex,
|
|
1480
|
+
onPlatformSendDispatch: ctx.onPlatformSendDispatch
|
|
1481
|
+
});
|
|
1482
|
+
const threadId = ctx.threadId == null ? void 0 : String(ctx.threadId);
|
|
1483
|
+
const replyToId = ctx.replyToId ?? void 0;
|
|
1484
|
+
return {
|
|
1485
|
+
messageId,
|
|
1486
|
+
receipt: createMessageReceiptFromOutboundResults({
|
|
1487
|
+
results: [{
|
|
1488
|
+
channel: CHANNEL_ID,
|
|
1489
|
+
messageId
|
|
1490
|
+
}],
|
|
1491
|
+
threadId,
|
|
1492
|
+
replyToId,
|
|
1493
|
+
kind: "media"
|
|
1494
|
+
})
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1068
1498
|
});
|
|
1069
1499
|
/**
|
|
1070
1500
|
* Channel plugin instance registered by the bundled ClickClack entry.
|
|
@@ -1162,14 +1592,37 @@ const clickClackPlugin = createChatChannelPlugin({
|
|
|
1162
1592
|
base: { deliveryMode: "direct" },
|
|
1163
1593
|
attachedResults: {
|
|
1164
1594
|
channel: CHANNEL_ID,
|
|
1165
|
-
sendText: async ({ cfg, to, text, accountId, threadId, replyToId }) =>
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1595
|
+
sendText: async ({ cfg, to, text, accountId, threadId, replyToId, deliveryQueueId, deliveryPartIndex, onPlatformSendDispatch }) => {
|
|
1596
|
+
return { messageId: await sendClickClackText({
|
|
1597
|
+
cfg,
|
|
1598
|
+
accountId,
|
|
1599
|
+
to,
|
|
1600
|
+
text,
|
|
1601
|
+
threadId,
|
|
1602
|
+
replyToId,
|
|
1603
|
+
deliveryQueueId,
|
|
1604
|
+
deliveryPartIndex,
|
|
1605
|
+
onPlatformSendDispatch
|
|
1606
|
+
}) ?? "" };
|
|
1607
|
+
},
|
|
1608
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId, threadId, replyToId, deliveryQueueId, deliveryPartIndex, onPlatformSendDispatch }) => {
|
|
1609
|
+
if (!mediaUrl) throw new Error("ClickClack media send requires mediaUrl");
|
|
1610
|
+
return { messageId: await sendClickClackMedia({
|
|
1611
|
+
cfg,
|
|
1612
|
+
accountId,
|
|
1613
|
+
to,
|
|
1614
|
+
text,
|
|
1615
|
+
mediaUrl,
|
|
1616
|
+
mediaAccess,
|
|
1617
|
+
mediaLocalRoots,
|
|
1618
|
+
mediaReadFile,
|
|
1619
|
+
threadId,
|
|
1620
|
+
replyToId,
|
|
1621
|
+
deliveryQueueId,
|
|
1622
|
+
deliveryPartIndex,
|
|
1623
|
+
onPlatformSendDispatch
|
|
1624
|
+
}) };
|
|
1625
|
+
}
|
|
1173
1626
|
}
|
|
1174
1627
|
}
|
|
1175
1628
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as clickClackPlugin } from "./channel-
|
|
1
|
+
import { t as clickClackPlugin } from "./channel-Ds6d_3nx.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-Ds6d_3nx.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.
|
|
3
|
+
"version": "2026.7.2-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/clickclack",
|
|
9
|
-
"version": "2026.7.
|
|
9
|
+
"version": "2026.7.2-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ws": "8.21.0",
|
|
12
12
|
"zod": "4.4.3"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"openclaw": ">=2026.7.
|
|
15
|
+
"openclaw": ">=2026.7.2-beta.1"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/clickclack",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.1",
|
|
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.
|
|
16
|
+
"openclaw": ">=2026.7.2-beta.1"
|
|
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.
|
|
52
|
+
"pluginApi": ">=2026.7.2-beta.1"
|
|
53
53
|
},
|
|
54
54
|
"build": {
|
|
55
|
-
"openclawVersion": "2026.7.
|
|
55
|
+
"openclawVersion": "2026.7.2-beta.1",
|
|
56
56
|
"bundledDist": false
|
|
57
57
|
},
|
|
58
58
|
"release": {
|