@openclaw/clickclack 2026.9.6 → 2026.9.7

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/assets/icon.png CHANGED
Binary file
@@ -1,4 +1,6 @@
1
- import { _ as DEFAULT_ACCOUNT_ID, b as resolveClickClackAccount, c as resolveWorkspaceId, f as normalizeClickClackCorrelationId, g as clickClackMeta, h as clickClackConfigAdapter, l as ClickClackHttpError, m as CLICKCLACK_CHANNEL_ID, n as clickClackSetupContract, p as clickClackConfigSchema, s as resolveChannelId, t as clickClackSetupWizard, u as createClickClackClient, y as listEnabledClickClackAccounts } from "./setup-surface-BBl3rvVx.mjs";
1
+ import { n as __reExport, t as __exportAll } from "./rolldown-runtime-B4FMCO8f.mjs";
2
+ import { c as CLICKCLACK_CHANNEL_ID, d as DEFAULT_ACCOUNT_ID, f as isClickClackAccountCurrent, g as resolveClickClackAccountConfig, h as resolveClickClackAccount, l as clickClackConfigAdapter, m as listEnabledClickClackAccounts, n as clickClackSetupContract, s as clickClackConfigSchema, t as clickClackSetupWizard, u as clickClackMeta } from "./setup-surface-BW1Q06w5.mjs";
3
+ import { i as createClickClackClient, n as resolveWorkspaceId, o as normalizeClickClackCorrelationId, r as ClickClackHttpError, t as resolveChannelId } from "./resolve-BHGLohni.mjs";
2
4
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-BKkzzjBA.mjs";
3
5
  import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
4
6
  import { parseDateStringTimestampMs } from "openclaw/plugin-sdk/number-runtime";
@@ -11,16 +13,35 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
11
13
  import { channelReadyPatch, channelStoppedPatch } from "openclaw/plugin-sdk/gateway-runtime";
12
14
  import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
13
15
  import { rawDataToString } from "openclaw/plugin-sdk/webhook-ingress";
16
+ import { isDeepStrictEqual } from "node:util";
17
+ import { parseAccessGroupAllowFromEntry } from "openclaw/plugin-sdk/access-groups";
18
+ import { formatNormalizedAllowFromEntries, resolveAllowlistMatchByCandidates } from "openclaw/plugin-sdk/allow-from";
14
19
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
15
20
  import { createHash, randomUUID } from "node:crypto";
16
21
  import { truncateCodePoints } from "openclaw/plugin-sdk/text-utility-runtime";
17
- import { buildMentionRegexes, normalizeMentionText } from "openclaw/plugin-sdk/channel-mention-gating";
18
22
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
19
23
  import { listNativeCommandSpecsForConfig } from "openclaw/plugin-sdk/native-command-registry";
20
24
  import { buildChannelInboundEventContext, createChannelInboundEnvelopeBuilder, createChannelPartialDeliveryError, recordChannelBotPairLoopAndCheckSuppression } from "openclaw/plugin-sdk/channel-inbound";
21
25
  import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
22
26
  import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
23
27
  import { FormatCapabilityProfile, renderMarkdownWithMarkers, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
28
+ //#region src/channels/mention-gating.ts
29
+ function resolveBotThreadMentionPolicy(params) {
30
+ if (!params.isBotOwnedThread || params.requireMentionInBotThreads === void 0) return {
31
+ requireMention: params.requireMention,
32
+ implicitMentionKinds: params.implicitMentionKinds
33
+ };
34
+ return {
35
+ requireMention: params.requireMentionInBotThreads,
36
+ implicitMentionKinds: params.requireMentionInBotThreads ? params.implicitMentionKinds?.filter((kind) => kind === "native") : params.implicitMentionKinds
37
+ };
38
+ }
39
+ //#endregion
40
+ //#region \0openclaw:bundled-sdk:openclaw/plugin-sdk/channel-mention-gating
41
+ var channel_mention_gating_exports = /* @__PURE__ */ __exportAll({ resolveBotThreadMentionPolicy: () => resolveBotThreadMentionPolicy });
42
+ import * as import_openclaw_plugin_sdk_channel_mention_gating from "openclaw/plugin-sdk/channel-mention-gating";
43
+ __reExport(channel_mention_gating_exports, import_openclaw_plugin_sdk_channel_mention_gating);
44
+ //#endregion
24
45
  //#region extensions/clickclack/src/discussions/binding-generation.ts
25
46
  const GENERATION_STORE_OPTIONS = {
26
47
  namespace: "discussion-binding-generations",
@@ -509,45 +530,20 @@ async function resolveClickClackDiscussionRoute(params) {
509
530
  }
510
531
  //#endregion
511
532
  //#region extensions/clickclack/src/group-policy.ts
512
- /**
513
- * Resolves bot-authored message policy using the same exact, wildcard, and
514
- * account-level precedence as mention gating.
515
- */
516
- function resolveClickClackBotPolicy(params) {
533
+ function resolveClickClackGroupPolicy(params) {
517
534
  const { account, channelId } = params;
518
535
  const channelKey = channelId?.trim();
519
536
  const groups = channelKey ? account.groups : void 0;
520
537
  const wildcard = groups?.["*"];
521
538
  const exact = channelKey ? groups?.[channelKey] : void 0;
522
539
  return {
540
+ requireMention: exact?.requireMention ?? wildcard?.requireMention ?? account.requireMention === true,
541
+ requireMentionInBotThreads: exact?.requireMentionInBotThreads ?? wildcard?.requireMentionInBotThreads ?? account.requireMentionInBotThreads,
542
+ mentionPatterns: exact?.mentionPatterns ?? wildcard?.mentionPatterns ?? account.mentionPatterns ?? [],
523
543
  allowBots: exact?.allowBots ?? wildcard?.allowBots ?? account.allowBots ?? false,
524
544
  botLoopProtection: mergePairLoopGuardConfig(account.botLoopProtection, wildcard?.botLoopProtection, exact?.botLoopProtection)
525
545
  };
526
546
  }
527
- /**
528
- * Resolves the effective group policy for a ClickClack channel.
529
- *
530
- * Lookup order:
531
- * 1. Exact channel ID in `groups`
532
- * 2. Wildcard `'*'` entry in `groups`
533
- * 3. Account-level `requireMention` / `mentionPatterns`
534
- * 4. Backward-compatible default: { requireMention: false, mentionPatterns: [] }
535
- */
536
- function resolveClickClackGroupPolicy(params) {
537
- const { account, channelId } = params;
538
- const accountPolicy = {
539
- requireMention: account.requireMention === true,
540
- mentionPatterns: account.mentionPatterns ?? []
541
- };
542
- const channelKey = channelId?.trim();
543
- const groups = channelKey ? account.groups : void 0;
544
- const wildcard = groups?.["*"];
545
- const exact = channelKey ? groups?.[channelKey] : void 0;
546
- return {
547
- requireMention: exact?.requireMention ?? wildcard?.requireMention ?? accountPolicy.requireMention,
548
- mentionPatterns: exact?.mentionPatterns ?? wildcard?.mentionPatterns ?? accountPolicy.mentionPatterns
549
- };
550
- }
551
547
  //#endregion
552
548
  //#region extensions/clickclack/src/mention-facts.ts
553
549
  /**
@@ -570,7 +566,7 @@ function buildLocalMentionRegexes(params) {
570
566
  }
571
567
  }
572
568
  };
573
- return buildMentionRegexes(syntheticCfg, void 0, {
569
+ return (0, channel_mention_gating_exports.buildMentionRegexes)(syntheticCfg, void 0, {
574
570
  provider: "clickclack",
575
571
  conversationId: params.channelId
576
572
  });
@@ -601,7 +597,7 @@ function resolveClickClackMentionFacts(params) {
601
597
  wasMentioned: false,
602
598
  hasAnyMention: false
603
599
  };
604
- const sharedMentionRegexes = buildMentionRegexes(cfg, agentId, {
600
+ const sharedMentionRegexes = (0, channel_mention_gating_exports.buildMentionRegexes)(cfg, agentId, {
605
601
  provider: "clickclack",
606
602
  conversationId: channelId
607
603
  });
@@ -611,7 +607,7 @@ function resolveClickClackMentionFacts(params) {
611
607
  channelId
612
608
  });
613
609
  const mentionRegexes = [...sharedMentionRegexes, ...localMentionRegexes];
614
- const bodyForRegex = normalizeMentionText(body);
610
+ const bodyForRegex = (0, channel_mention_gating_exports.normalizeMentionText)(body);
615
611
  const hasConfiguredMention = mentionRegexes.some((regex) => regex.test(bodyForRegex));
616
612
  const mentionHandles = resolveMentionHandles(body);
617
613
  const normalizedBotHandle = botHandle?.replace(/^@/u, "").trim().toLowerCase();
@@ -691,6 +687,19 @@ const clickClackIngressIdentity = {
691
687
  isWildcardEntry: (entry) => normalizeClickClackUserId(entry) === "*",
692
688
  entryIdPrefix: "clickclack-user"
693
689
  };
690
+ async function isClickClackBotOwnedThread(params) {
691
+ const { account, message } = params;
692
+ if (!account.botUserId || !message.parent_message_id || !message.thread_root_id || message.thread_root_id === message.id || !message.channel_id || message.direct_conversation_id || message.workspace_id !== account.workspace) return false;
693
+ try {
694
+ const root = await createClickClackClient({
695
+ baseUrl: account.apiEndpoint,
696
+ token: account.token
697
+ }).message(message.thread_root_id);
698
+ return root.id === message.thread_root_id && root.thread_root_id === root.id && !root.parent_message_id && !root.direct_conversation_id && root.workspace_id === message.workspace_id && root.channel_id === message.channel_id && root.author_id === account.botUserId;
699
+ } catch {
700
+ return false;
701
+ }
702
+ }
694
703
  function resolveClickClackBotLoopConversationId(params) {
695
704
  if (params.message.parent_message_id && params.message.thread_root_id) return params.message.thread_root_id;
696
705
  return params.isDirect ? params.message.direct_conversation_id ?? params.message.author_id : params.message.channel_id ?? params.message.thread_root_id ?? params.message.author_id;
@@ -780,13 +789,37 @@ async function resolvePreparedInboundRoute(params) {
780
789
  */
781
790
  async function resolveClickClackInboundAccess(params) {
782
791
  const runtime = getClickClackRuntime();
783
- const cfg = params.config;
784
- const preparedRoute = await resolvePreparedInboundRoute(params);
785
- const shouldCheckCommand = runtime.channel.commands.shouldComputeCommandAuthorized(params.message.body, cfg);
786
- const effectiveGroupPolicy = resolveClickClackGroupPolicy({
792
+ const rootPolicyConfigured = resolveClickClackGroupPolicy({
787
793
  account: params.account,
788
794
  channelId: params.message.channel_id
795
+ }).requireMentionInBotThreads !== void 0;
796
+ const isCurrent = () => isClickClackAccountCurrent({
797
+ cfg: runtime.config.current(),
798
+ account: params.account
789
799
  });
800
+ const isBotOwnedThread = rootPolicyConfigured && await isClickClackBotOwnedThread(params);
801
+ const routeConfig = rootPolicyConfigured ? runtime.config.current() : params.config;
802
+ const routeAccountConfig = rootPolicyConfigured ? resolveClickClackAccountConfig(routeConfig, params.account.accountId) : params.account;
803
+ const preparedRoute = await resolvePreparedInboundRoute({
804
+ ...params,
805
+ config: routeConfig,
806
+ account: {
807
+ ...params.account,
808
+ agentId: routeAccountConfig.agentId
809
+ }
810
+ });
811
+ const cfg = rootPolicyConfigured ? runtime.config.current() : params.config;
812
+ const accountPolicy = rootPolicyConfigured ? resolveClickClackAccountConfig(cfg, params.account.accountId) : params.account;
813
+ const effectiveGroupPolicy = resolveClickClackGroupPolicy({
814
+ account: accountPolicy,
815
+ channelId: params.message.channel_id
816
+ });
817
+ const threadMentionPolicy = resolveBotThreadMentionPolicy({
818
+ isBotOwnedThread,
819
+ requireMentionInBotThreads: effectiveGroupPolicy.requireMentionInBotThreads,
820
+ requireMention: effectiveGroupPolicy.requireMention
821
+ });
822
+ const shouldCheckCommand = runtime.channel.commands.shouldComputeCommandAuthorized(params.message.body, cfg);
790
823
  const mentionFacts = resolveClickClackMentionFacts({
791
824
  isDirect: preparedRoute.isDirect,
792
825
  body: params.message.body,
@@ -796,23 +829,22 @@ async function resolveClickClackInboundAccess(params) {
796
829
  agentId: preparedRoute.route.agentId,
797
830
  channelId: params.message.channel_id
798
831
  });
799
- if (params.message.kind !== void 0 && params.message.kind !== "message") return {
832
+ if (!isCurrent() || params.message.kind !== void 0 && params.message.kind !== "message") return {
800
833
  shouldDispatch: false,
834
+ isCurrent,
801
835
  commandAuthorized: false,
802
- requireMention: effectiveGroupPolicy.requireMention,
836
+ requireMention: threadMentionPolicy.requireMention,
803
837
  mentionFacts,
804
838
  preparedRoute
805
839
  };
806
- const effectiveBotPolicy = resolveClickClackBotPolicy({
807
- account: params.account,
808
- channelId: params.message.channel_id
809
- });
810
840
  const isBotAuthor = params.message.author?.kind === "bot";
811
- const ingressAllowFrom = isBotAuthor ? params.account.allowFrom.filter((entry) => normalizeClickClackUserId(entry) !== "*") : params.account.allowFrom;
812
- if (!(!isBotAuthor || effectiveBotPolicy.allowBots === true || effectiveBotPolicy.allowBots === "mentions" && (preparedRoute.isDirect || mentionFacts.wasMentioned))) return {
841
+ const allowFrom = accountPolicy.allowFrom ?? ["*"];
842
+ const ingressAllowFrom = isBotAuthor ? allowFrom.filter((entry) => normalizeClickClackUserId(entry) !== "*") : allowFrom;
843
+ if (!(!isBotAuthor || effectiveGroupPolicy.allowBots === true || effectiveGroupPolicy.allowBots === "mentions" && (preparedRoute.isDirect || mentionFacts.wasMentioned))) return {
813
844
  shouldDispatch: false,
845
+ isCurrent,
814
846
  commandAuthorized: false,
815
- requireMention: effectiveGroupPolicy.requireMention,
847
+ requireMention: threadMentionPolicy.requireMention,
816
848
  mentionFacts,
817
849
  preparedRoute
818
850
  };
@@ -827,11 +859,11 @@ async function resolveClickClackInboundAccess(params) {
827
859
  receiverId: params.account.botUserId,
828
860
  eventId: params.message.id,
829
861
  ...botLoopNowMs !== void 0 ? { nowMs: botLoopNowMs } : {},
830
- config: effectiveBotPolicy.botLoopProtection,
862
+ config: effectiveGroupPolicy.botLoopProtection,
831
863
  defaultsConfig: cfg.channels?.defaults?.botLoopProtection,
832
864
  defaultEnabled: true
833
865
  } : void 0;
834
- const allowTextCommands = params.account.replyMode === "agent" && runtime.channel.commands.shouldHandleTextCommands({
866
+ const allowTextCommands = accountPolicy.replyMode !== "model" && runtime.channel.commands.shouldHandleTextCommands({
835
867
  cfg,
836
868
  surface: CHANNEL_ID$2,
837
869
  commandSource: "text"
@@ -858,7 +890,7 @@ async function resolveClickClackInboundAccess(params) {
858
890
  groupPolicy: "allowlist",
859
891
  mentionFacts,
860
892
  policy: { activation: {
861
- requireMention: effectiveGroupPolicy.requireMention,
893
+ requireMention: threadMentionPolicy.requireMention,
862
894
  allowTextCommands
863
895
  } },
864
896
  command: shouldCheckCommand ? {
@@ -866,10 +898,68 @@ async function resolveClickClackInboundAccess(params) {
866
898
  modeWhenAccessGroupsOff: "configured"
867
899
  } : false
868
900
  });
901
+ const isAdmissionCurrent = () => {
902
+ if (!isCurrent()) return false;
903
+ const currentCfg = runtime.config.current();
904
+ const currentAccountPolicy = resolveClickClackAccountConfig(currentCfg, params.account.accountId);
905
+ const currentGroupPolicy = resolveClickClackGroupPolicy({
906
+ account: currentAccountPolicy,
907
+ channelId: params.message.channel_id
908
+ });
909
+ const currentAllowFrom = currentAccountPolicy.allowFrom ?? ["*"];
910
+ const currentIngressAllowFrom = isBotAuthor ? currentAllowFrom.filter((entry) => normalizeClickClackUserId(entry) !== "*") : currentAllowFrom;
911
+ const directSenderAllowed = resolveAllowlistMatchByCandidates({
912
+ allowList: formatNormalizedAllowFromEntries({
913
+ allowFrom: currentIngressAllowFrom.filter((entry) => parseAccessGroupAllowFromEntry(entry) === null),
914
+ normalizeEntry: normalizeClickClackUserId
915
+ }),
916
+ candidates: [{
917
+ value: normalizeClickClackUserId(params.message.author_id) ?? void 0,
918
+ source: "id"
919
+ }]
920
+ }).allowed;
921
+ const staticGroupAllowed = currentIngressAllowFrom.some((entry) => {
922
+ const name = parseAccessGroupAllowFromEntry(entry);
923
+ if (!name) return false;
924
+ const group = currentCfg.accessGroups?.[name];
925
+ return group?.type === "message.senders" && resolved.state.allowlists[preparedRoute.isDirect ? "dm" : "group"].accessGroups.matched.includes(name) && isDeepStrictEqual(group, cfg.accessGroups?.[name]);
926
+ });
927
+ if (!directSenderAllowed && !staticGroupAllowed) return false;
928
+ const currentMentionFacts = resolveClickClackMentionFacts({
929
+ isDirect: preparedRoute.isDirect,
930
+ body: params.message.body,
931
+ mentionPatterns: currentGroupPolicy.mentionPatterns,
932
+ botHandle: params.account.botHandle,
933
+ cfg: currentCfg,
934
+ agentId: preparedRoute.route.agentId,
935
+ channelId: params.message.channel_id
936
+ });
937
+ if (isBotAuthor && currentGroupPolicy.allowBots !== true && !(currentGroupPolicy.allowBots === "mentions" && (preparedRoute.isDirect || currentMentionFacts.wasMentioned))) return false;
938
+ const currentThreadPolicy = resolveBotThreadMentionPolicy({
939
+ isBotOwnedThread,
940
+ requireMentionInBotThreads: currentGroupPolicy.requireMentionInBotThreads,
941
+ requireMention: currentGroupPolicy.requireMention
942
+ });
943
+ return !(0, channel_mention_gating_exports.resolveInboundMentionDecision)({
944
+ facts: currentMentionFacts,
945
+ policy: {
946
+ isGroup: !preparedRoute.isDirect,
947
+ requireMention: currentThreadPolicy.requireMention,
948
+ allowTextCommands: allowTextCommands && currentAccountPolicy.replyMode !== "model" && runtime.channel.commands.shouldHandleTextCommands({
949
+ cfg: currentCfg,
950
+ surface: CHANNEL_ID$2,
951
+ commandSource: "text"
952
+ }),
953
+ hasControlCommand: shouldCheckCommand && runtime.channel.commands.shouldComputeCommandAuthorized(params.message.body, currentCfg),
954
+ commandAuthorized: resolved.commandAccess.authorized
955
+ }
956
+ }).shouldSkip;
957
+ };
869
958
  return {
870
- shouldDispatch: !preparedRoute.revoked && resolved.ingress.admission === "dispatch",
959
+ shouldDispatch: isAdmissionCurrent() && !preparedRoute.revoked && resolved.ingress.admission === "dispatch",
960
+ isCurrent: isAdmissionCurrent,
871
961
  commandAuthorized: resolved.commandAccess.requested ? resolved.commandAccess.authorized : resolved.senderAccess.allowed,
872
- requireMention: effectiveGroupPolicy.requireMention,
962
+ requireMention: threadMentionPolicy.requireMention,
873
963
  mentionFacts,
874
964
  botLoopProtection,
875
965
  preparedRoute,
@@ -938,20 +1028,7 @@ async function syncClickClackCommandMenu(params) {
938
1028
  * Publishes agent activity (streamed commentary + tool progress) into
939
1029
  * ClickClack as durable `agent_commentary` / `agent_tool` message rows,
940
1030
  * coalesced so one logical step becomes one row instead of a row per frame.
941
- *
942
- * Ported from the clickglass agent-bridge sidecar, adapted from gateway
943
- * websocket frames to the in-process `replyOptions.onItemEvent` seam:
944
- *
945
- * - Commentary/reasoning arrives as cumulative text snapshots per item id.
946
- * Each segment becomes one durable row: POSTed when the segment starts
947
- * streaming and PATCHed (debounced) as the snapshot grows, so prose
948
- * interleaves chronologically with tool rows.
949
- * - Tool/step items can emit several frames (start/update/complete) for the
950
- * same call, sometimes with lane-prefixed ids (`tool:X`, `command:X`).
951
- * Normalize the prefix away to one key per call, POST one row on the first
952
- * frame, and PATCH it when a later frame carries a strictly longer body.
953
1031
  */
954
- /** Debounce window for PATCHing streaming commentary snapshots. */
955
1032
  const CLICKCLACK_COMMENTARY_FLUSH_MS = 700;
956
1033
  /** Item kinds rendered as agent_tool rows; everything else is commentary. */
957
1034
  const TOOL_ITEM_KINDS = /* @__PURE__ */ new Set([
@@ -1094,13 +1171,14 @@ function createClickClackActivityPublisher(params) {
1094
1171
  const handleDiscreteItem = (payload) => {
1095
1172
  const body = activityBody(payload);
1096
1173
  if (!body) return;
1097
- const kind = TOOL_ITEM_KINDS.has(payload.kind?.trim().toLowerCase() ?? "") ? "agent_tool" : "agent_commentary";
1174
+ const kind = TOOL_ITEM_KINDS.has(normalizedItemKind(payload)) ? "agent_tool" : "agent_commentary";
1098
1175
  flushAllCommentary();
1099
- const key = `${kind}:${toolRowKey(payload)}`;
1176
+ const itemKey = toolRowKey(payload);
1177
+ const key = `${kind}:${itemKey}`;
1100
1178
  const existing = toolRows.get(key);
1101
- if (!existing || !toolRowKey(payload)) {
1179
+ if (!existing || !itemKey) {
1102
1180
  const row = { body };
1103
- if (toolRowKey(payload)) toolRows.set(key, row);
1181
+ if (itemKey) toolRows.set(key, row);
1104
1182
  enqueue(async () => {
1105
1183
  const posted = await postRow(kind, row.body);
1106
1184
  row.messageId = posted.id;
@@ -1689,7 +1767,7 @@ function createClickClackAgentProgressPublisher(params) {
1689
1767
  const CHANNEL_ID$1 = "clickclack";
1690
1768
  const CLICKCLACK_MESSAGE_ID_PATTERN = /^msg_[0-9a-hjkmnp-tv-z]{26}$/u;
1691
1769
  function hasClickClackReplyMedia(payload) {
1692
- return Boolean(payload.mediaUrl?.trim() || payload.mediaUrls?.some((mediaUrl) => typeof mediaUrl === "string" && mediaUrl.trim()));
1770
+ return Boolean(payload.mediaUrl?.trim() || payload.mediaUrls?.some((mediaUrl) => mediaUrl.trim()));
1693
1771
  }
1694
1772
  function resolveClickClackAgentRunId(messageId) {
1695
1773
  return CLICKCLACK_MESSAGE_ID_PATTERN.test(messageId) ? `${CHANNEL_ID$1}:${messageId}` : void 0;
@@ -1737,10 +1815,6 @@ async function dispatchModelReply(params) {
1737
1815
  correlationId: params.correlationId
1738
1816
  });
1739
1817
  }
1740
- /**
1741
- * Dispatches one already-fetched ClickClack message through the configured
1742
- * reply mode for its account.
1743
- */
1744
1818
  async function handleClickClackInbound(params) {
1745
1819
  const runtime = getClickClackRuntime();
1746
1820
  const message = params.message;
@@ -1749,7 +1823,7 @@ async function handleClickClackInbound(params) {
1749
1823
  config: params.config,
1750
1824
  message
1751
1825
  });
1752
- if (!access.shouldDispatch || !access.channelIngress) return;
1826
+ if (!access.shouldDispatch || !access.channelIngress || !access.isCurrent()) return;
1753
1827
  const conversationId = message.channel_id || message.direct_conversation_id;
1754
1828
  if (!conversationId) return;
1755
1829
  const { discussionRoute, isDirect, route, target } = access.preparedRoute;
@@ -1803,7 +1877,7 @@ async function handleClickClackInbound(params) {
1803
1877
  }
1804
1878
  let turnProvenance;
1805
1879
  let activity;
1806
- if (params.account.agentActivity && (message.channel_id || message.direct_conversation_id)) activity = createClickClackActivityPublisher({
1880
+ if (params.account.agentActivity) activity = createClickClackActivityPublisher({
1807
1881
  client: createClickClackClient({
1808
1882
  baseUrl: params.account.apiEndpoint,
1809
1883
  token: params.account.token,
@@ -1915,7 +1989,7 @@ async function handleClickClackInbound(params) {
1915
1989
  delivery: {
1916
1990
  deliver: async (payload) => {
1917
1991
  if (hasClickClackReplyMedia(payload)) throw new Error("ClickClack media reply requires durable delivery");
1918
- const text = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
1992
+ const text = payload.text ?? "";
1919
1993
  if (!text.trim()) return;
1920
1994
  await sendClickClackText({
1921
1995
  cfg: params.config,
@@ -2186,10 +2260,6 @@ async function startClickClackGatewayAccount(ctx) {
2186
2260
  }
2187
2261
  //#endregion
2188
2262
  //#region extensions/clickclack/src/channel.ts
2189
- /**
2190
- * ClickClack channel plugin definition: target parsing, account config, status,
2191
- * gateway startup, and outbound delivery wiring.
2192
- */
2193
2263
  const CHANNEL_ID = CLICKCLACK_CHANNEL_ID;
2194
2264
  const clickClackMessageAdapter = defineChannelMessageAdapter({
2195
2265
  id: CHANNEL_ID,
@@ -2211,16 +2281,8 @@ const clickClackMessageAdapter = defineChannelMessageAdapter({
2211
2281
  send: {
2212
2282
  text: async (ctx) => {
2213
2283
  const messageId = await sendClickClackText({
2214
- cfg: ctx.cfg,
2215
- accountId: ctx.accountId,
2216
- to: ctx.to,
2217
- text: ctx.text,
2218
- threadId: ctx.threadId,
2219
- replyToId: ctx.replyToId,
2220
- deliveryQueueId: ctx.deliveryQueueId,
2221
- deliveryPartIndex: ctx.deliveryPartIndex,
2222
- onPlatformSendDispatch: ctx.onPlatformSendDispatch,
2223
- assertDirectAdapterHandoff: ctx.assertDirectAdapterHandoff
2284
+ ...ctx,
2285
+ cfg: ctx.cfg
2224
2286
  });
2225
2287
  const threadId = ctx.threadId == null ? void 0 : String(ctx.threadId);
2226
2288
  const replyToId = ctx.replyToId ?? void 0;
@@ -2239,21 +2301,8 @@ const clickClackMessageAdapter = defineChannelMessageAdapter({
2239
2301
  },
2240
2302
  media: async (ctx) => {
2241
2303
  const messageId = await sendClickClackMedia({
2242
- cfg: ctx.cfg,
2243
- accountId: ctx.accountId,
2244
- to: ctx.to,
2245
- text: ctx.text,
2246
- mediaUrl: ctx.mediaUrl,
2247
- mediaAccess: ctx.mediaAccess,
2248
- mediaLocalRoots: ctx.mediaLocalRoots,
2249
- mediaReadFile: ctx.mediaReadFile,
2250
- threadId: ctx.threadId,
2251
- replyToId: ctx.replyToId,
2252
- deliveryQueueId: ctx.deliveryQueueId,
2253
- deliveryPartIndex: ctx.deliveryPartIndex,
2254
- onPlatformSendDispatch: ctx.onPlatformSendDispatch,
2255
- assertDirectAdapterHandoff: ctx.assertDirectAdapterHandoff,
2256
- onDeliveryResult: ctx.onDeliveryResult
2304
+ ...ctx,
2305
+ cfg: ctx.cfg
2257
2306
  });
2258
2307
  const threadId = ctx.threadId == null ? void 0 : String(ctx.threadId);
2259
2308
  const replyToId = ctx.replyToId ?? void 0;
@@ -2272,9 +2321,6 @@ const clickClackMessageAdapter = defineChannelMessageAdapter({
2272
2321
  }
2273
2322
  }
2274
2323
  });
2275
- /**
2276
- * Channel plugin instance registered by the bundled ClickClack entry.
2277
- */
2278
2324
  const clickClackPlugin = createChatChannelPlugin({
2279
2325
  base: {
2280
2326
  id: CHANNEL_ID,
@@ -2364,37 +2410,19 @@ const clickClackPlugin = createChatChannelPlugin({
2364
2410
  base: { deliveryMode: "direct" },
2365
2411
  attachedResults: {
2366
2412
  channel: CHANNEL_ID,
2367
- sendText: async ({ cfg, to, text, accountId, threadId, replyToId, deliveryQueueId, deliveryPartIndex, onPlatformSendDispatch, assertDirectAdapterHandoff }) => {
2413
+ sendText: async (ctx) => {
2368
2414
  return { messageId: await sendClickClackText({
2369
- cfg,
2370
- accountId,
2371
- to,
2372
- text,
2373
- threadId,
2374
- replyToId,
2375
- deliveryQueueId,
2376
- deliveryPartIndex,
2377
- onPlatformSendDispatch,
2378
- assertDirectAdapterHandoff
2415
+ ...ctx,
2416
+ cfg: ctx.cfg
2379
2417
  }) ?? "" };
2380
2418
  },
2381
- sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId, threadId, replyToId, deliveryQueueId, deliveryPartIndex, onPlatformSendDispatch, assertDirectAdapterHandoff, onDeliveryResult }) => {
2419
+ sendMedia: async (ctx) => {
2420
+ const { mediaUrl, onDeliveryResult } = ctx;
2382
2421
  if (!mediaUrl) throw new Error("ClickClack media send requires mediaUrl");
2383
2422
  return { messageId: await sendClickClackMedia({
2384
- cfg,
2385
- accountId,
2386
- to,
2387
- text,
2423
+ ...ctx,
2424
+ cfg: ctx.cfg,
2388
2425
  mediaUrl,
2389
- mediaAccess,
2390
- mediaLocalRoots,
2391
- mediaReadFile,
2392
- threadId,
2393
- replyToId,
2394
- deliveryQueueId,
2395
- deliveryPartIndex,
2396
- onPlatformSendDispatch,
2397
- assertDirectAdapterHandoff,
2398
2426
  onDeliveryResult: onDeliveryResult ? ({ messageId: acceptedMessageId, receipt }) => onDeliveryResult({
2399
2427
  channel: CHANNEL_ID,
2400
2428
  messageId: acceptedMessageId,