@openclaw/clickclack 2026.9.6 → 2026.9.8
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 +0 -0
- package/dist/.setup/{channel-CT4CVd4d.mjs → channel-Bcwtohk7.mjs} +162 -134
- package/dist/.setup/resolve-BHGLohni.mjs +336 -0
- package/dist/.setup/rolldown-runtime-B4FMCO8f.mjs +28 -0
- package/dist/.setup/{runtime-api-A9r1b12d.mjs → runtime-api-BvP8DrPQ.mjs} +8 -5
- package/dist/.setup/{setup-claim-t5SbtvZy.mjs → setup-claim-Cx8knvXs.mjs} +1 -1
- package/dist/.setup/setup-plugin-api-BH_nw-AP.mjs +2 -0
- package/dist/.setup/{setup-plugin-api-BUwBPYpy.mjs → setup-plugin-api-C9QcBJuW.mjs} +1 -1
- package/dist/.setup/{setup-surface-BBl3rvVx.mjs → setup-surface-BW1Q06w5.mjs} +50 -503
- package/dist/.setup/setup-verify-Msgd70_M.mjs +111 -0
- package/dist/api.js +3 -2
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +160 -3
- package/dist/index.js +1 -1
- package/dist/runtime-api.js +4 -3
- package/dist/setup-entry.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/openclaw.plugin.json +31 -1
- package/package.json +4 -4
- package/dist/.setup/setup-plugin-api-B19mjQvl.mjs +0 -2
package/assets/icon.png
CHANGED
|
Binary file
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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:
|
|
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
|
|
812
|
-
|
|
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:
|
|
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:
|
|
862
|
+
config: effectiveGroupPolicy.botLoopProtection,
|
|
831
863
|
defaultsConfig: cfg.channels?.defaults?.botLoopProtection,
|
|
832
864
|
defaultEnabled: true
|
|
833
865
|
} : void 0;
|
|
834
|
-
const allowTextCommands =
|
|
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:
|
|
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:
|
|
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
|
|
1174
|
+
const kind = TOOL_ITEM_KINDS.has(normalizedItemKind(payload)) ? "agent_tool" : "agent_commentary";
|
|
1098
1175
|
flushAllCommentary();
|
|
1099
|
-
const
|
|
1176
|
+
const itemKey = toolRowKey(payload);
|
|
1177
|
+
const key = `${kind}:${itemKey}`;
|
|
1100
1178
|
const existing = toolRows.get(key);
|
|
1101
|
-
if (!existing || !
|
|
1179
|
+
if (!existing || !itemKey) {
|
|
1102
1180
|
const row = { body };
|
|
1103
|
-
if (
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
-
|
|
2215
|
-
|
|
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
|
-
|
|
2243
|
-
|
|
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 (
|
|
2413
|
+
sendText: async (ctx) => {
|
|
2368
2414
|
return { messageId: await sendClickClackText({
|
|
2369
|
-
|
|
2370
|
-
|
|
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 (
|
|
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
|
-
|
|
2385
|
-
|
|
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,
|