mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-10 06:42:56 +00:00
* refactor: centralize inbound supplemental context * refactor: trim supplemental finalizer typing * docs: clarify supplemental context projection * refactor: move inbound finalization into core * refactor: simplify channel inbound facts * refactor: fold supplemental media into inbound finalizer * refactor: migrate channel inbound callers to builder * docs: mark inbound finalizer compat types deprecated * refactor: wire runtime turn context builder * refactor: replace channel turn runtime API * fix: respect discord quote visibility * fix: avoid deprecated line dispatch helper * refactor: deprecate channel message SDK seams * docs: trim channel outbound SDK page * test: migrate irc inbound assertion * refactor: deprecate outbound SDK facades * refactor: deprecate channel helper SDK facades * refactor: deprecate channel streaming SDK facade * refactor: move direct dm helpers into inbound SDK * chore: mark legacy test-utils SDK alias deprecated * refactor: remove unused allow-from read helper * refactor: route remaining channel dispatch through core * refactor: enforce modern extension SDK imports * test: give slow image root tests more time * ci: support node fallback on windows * fix: add transcripts tool display metadata * refactor: trim legacy channel test seams * fix: preserve channel compat after rebase * fix: keep deprecated channel inbound aliases * fix: preserve discord thread context visibility * fix: clean final rebase conflicts * fix: preserve channel message dispatch aliases * fix: sync channel refactor after rebase * fix: sync channel refactor after latest main * fix: dedupe memory-core subagent mock * test: align clickclack inbound dispatch assertions * fix: sync plugin sdk api hash after rebase * fix: sync channel refactor after latest main * fix: sync plugin sdk api hash after rebase * fix: sync plugin sdk api hash after latest main * test: remove stale inbound context awaits
183 lines
5.7 KiB
TypeScript
183 lines
5.7 KiB
TypeScript
import {
|
|
buildChannelOutboundSessionRoute,
|
|
buildThreadAwareOutboundSessionRoute,
|
|
createChatChannelPlugin,
|
|
} from "openclaw/plugin-sdk/channel-core";
|
|
import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
import {
|
|
createMessageReceiptFromOutboundResults,
|
|
defineChannelMessageAdapter,
|
|
} from "openclaw/plugin-sdk/channel-outbound";
|
|
import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common";
|
|
import {
|
|
createComputedAccountStatusAdapter,
|
|
createDefaultChannelRuntimeState,
|
|
} from "openclaw/plugin-sdk/status-helpers";
|
|
import {
|
|
DEFAULT_ACCOUNT_ID,
|
|
listClickClackAccountIds,
|
|
resolveClickClackAccount,
|
|
resolveDefaultClickClackAccountId,
|
|
} from "./accounts.js";
|
|
import { clickClackConfigSchema } from "./config-schema.js";
|
|
import { startClickClackGatewayAccount } from "./gateway.js";
|
|
import { sendClickClackText } from "./outbound.js";
|
|
import {
|
|
buildClickClackTarget,
|
|
looksLikeClickClackTarget,
|
|
parseClickClackTarget,
|
|
normalizeClickClackTarget,
|
|
} from "./target.js";
|
|
import type { CoreConfig, ResolvedClickClackAccount } from "./types.js";
|
|
|
|
const CHANNEL_ID = "clickclack" as const;
|
|
const meta = { ...getChatChannelMeta(CHANNEL_ID) };
|
|
|
|
const clickClackMessageAdapter = defineChannelMessageAdapter({
|
|
id: CHANNEL_ID,
|
|
durableFinal: {
|
|
capabilities: {
|
|
text: true,
|
|
replyTo: true,
|
|
thread: true,
|
|
messageSendingHooks: true,
|
|
},
|
|
},
|
|
send: {
|
|
text: async (ctx) => {
|
|
const result = await sendClickClackText({
|
|
cfg: ctx.cfg as CoreConfig,
|
|
accountId: ctx.accountId,
|
|
to: ctx.to,
|
|
text: ctx.text,
|
|
threadId: ctx.threadId,
|
|
replyToId: ctx.replyToId,
|
|
});
|
|
const threadId = ctx.threadId == null ? undefined : String(ctx.threadId);
|
|
const replyToId = ctx.replyToId ?? undefined;
|
|
return {
|
|
messageId: result.messageId,
|
|
receipt: createMessageReceiptFromOutboundResults({
|
|
results: [{ channel: CHANNEL_ID, messageId: result.messageId }],
|
|
threadId,
|
|
replyToId,
|
|
kind: "text",
|
|
}),
|
|
};
|
|
},
|
|
},
|
|
});
|
|
|
|
export const clickClackPlugin: ChannelPlugin<ResolvedClickClackAccount> = createChatChannelPlugin({
|
|
base: {
|
|
id: CHANNEL_ID,
|
|
meta,
|
|
capabilities: {
|
|
chatTypes: ["direct", "group"],
|
|
threads: true,
|
|
blockStreaming: true,
|
|
},
|
|
reload: { configPrefixes: ["channels.clickclack"] },
|
|
configSchema: clickClackConfigSchema,
|
|
config: {
|
|
listAccountIds: (cfg) => listClickClackAccountIds(cfg as CoreConfig),
|
|
resolveAccount: (cfg, accountId) =>
|
|
resolveClickClackAccount({ cfg: cfg as CoreConfig, accountId }),
|
|
defaultAccountId: (cfg) => resolveDefaultClickClackAccountId(cfg as CoreConfig),
|
|
isConfigured: (account) => account.configured,
|
|
resolveAllowFrom: ({ cfg, accountId }) =>
|
|
resolveClickClackAccount({ cfg: cfg as CoreConfig, accountId }).allowFrom,
|
|
resolveDefaultTo: ({ cfg, accountId }) =>
|
|
resolveClickClackAccount({ cfg: cfg as CoreConfig, accountId }).defaultTo,
|
|
},
|
|
messaging: {
|
|
targetPrefixes: ["clickclack", "cc"],
|
|
normalizeTarget: normalizeClickClackTarget,
|
|
inferTargetChatType: ({ to }) => parseClickClackTarget(to).chatType,
|
|
targetResolver: {
|
|
looksLikeId: looksLikeClickClackTarget,
|
|
hint: "<channel:name|dm:usr_id|thread:msg_id>",
|
|
},
|
|
resolveOutboundSessionRoute: ({
|
|
cfg,
|
|
agentId,
|
|
accountId,
|
|
target,
|
|
replyToId,
|
|
threadId,
|
|
currentSessionKey,
|
|
}) => {
|
|
const parsed = parseClickClackTarget(target);
|
|
const baseRoute = buildChannelOutboundSessionRoute({
|
|
cfg,
|
|
agentId,
|
|
channel: CHANNEL_ID,
|
|
accountId,
|
|
peer: {
|
|
kind: parsed.chatType === "direct" ? "direct" : "channel",
|
|
id: buildClickClackTarget(parsed),
|
|
},
|
|
chatType: parsed.chatType,
|
|
from: `clickclack:${accountId ?? DEFAULT_ACCOUNT_ID}`,
|
|
to: buildClickClackTarget(parsed),
|
|
});
|
|
return buildThreadAwareOutboundSessionRoute({
|
|
route: baseRoute,
|
|
replyToId,
|
|
threadId: threadId ?? (parsed.kind === "thread" ? parsed.id : undefined),
|
|
currentSessionKey,
|
|
canRecoverCurrentThread: () => true,
|
|
});
|
|
},
|
|
resolveSessionConversation: ({ rawId }) => {
|
|
const parsed = parseClickClackTarget(rawId);
|
|
if (parsed.kind === "dm") {
|
|
return null;
|
|
}
|
|
return {
|
|
id: parsed.id,
|
|
threadId: parsed.kind === "thread" ? parsed.id : undefined,
|
|
baseConversationId: parsed.id,
|
|
parentConversationCandidates: [parsed.id],
|
|
};
|
|
},
|
|
},
|
|
status: createComputedAccountStatusAdapter<ResolvedClickClackAccount>({
|
|
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
|
buildChannelSummary: ({ snapshot }) => ({
|
|
ok: snapshot.configured,
|
|
label: snapshot.configured ? "configured" : "missing config",
|
|
detail: snapshot.baseUrl ?? "",
|
|
}),
|
|
resolveAccountSnapshot: ({ account }) => ({
|
|
accountId: account.accountId,
|
|
name: account.name,
|
|
enabled: account.enabled,
|
|
configured: account.configured,
|
|
baseUrl: account.baseUrl,
|
|
}),
|
|
}),
|
|
gateway: {
|
|
startAccount: startClickClackGatewayAccount,
|
|
},
|
|
message: clickClackMessageAdapter,
|
|
},
|
|
outbound: {
|
|
base: {
|
|
deliveryMode: "direct",
|
|
},
|
|
attachedResults: {
|
|
channel: CHANNEL_ID,
|
|
sendText: async ({ cfg, to, text, accountId, threadId, replyToId }) =>
|
|
await sendClickClackText({
|
|
cfg: cfg as CoreConfig,
|
|
accountId,
|
|
to,
|
|
text,
|
|
threadId,
|
|
replyToId,
|
|
}),
|
|
},
|
|
},
|
|
});
|