


























@@ -1,12 +1,18 @@
1+import { resolveAgentConfig, resolveDefaultAgentId } from "../../../agents/agent-scope-config.js";
12import { pickSandboxToolPolicy } from "../../../agents/sandbox-tool-policy.js";
23import { isToolAllowedByPolicies } from "../../../agents/tool-policy-match.js";
34import { mergeAlsoAllowPolicy, resolveToolProfilePolicy } from "../../../agents/tool-policy.js";
5+import { listRouteBindings } from "../../../config/bindings.js";
6+import type { AgentRouteBinding } from "../../../config/types.agents.js";
47import type { OpenClawConfig } from "../../../config/types.openclaw.js";
58import type { AgentToolsConfig, ToolsConfig } from "../../../config/types.tools.js";
9+import { normalizeAgentId } from "../../../routing/session-key.js";
610import { createLazyImportLoader } from "../../../shared/lazy-promise.js";
711812type ChannelDoctorModule = typeof import("./channel-doctor.js");
91314+const CHANNELS_CONFIG_META_KEYS = new Set(["defaults", "modelByChannel"]);
15+1016const channelDoctorModuleLoader = createLazyImportLoader<ChannelDoctorModule>(
1117() => import("./channel-doctor.js"),
1218);
@@ -27,6 +33,21 @@ function hasChannels(cfg: OpenClawConfig): boolean {
2733return hasRecord(cfg.channels);
2834}
293536+function listConfiguredChannelIds(cfg: OpenClawConfig): string[] {
37+if (!hasRecord(cfg.channels)) {
38+return [];
39+}
40+return Object.entries(cfg.channels)
41+.filter(([id, value]) => {
42+if (CHANNELS_CONFIG_META_KEYS.has(id)) {
43+return false;
44+}
45+return !(hasRecord(value) && value.enabled === false);
46+})
47+.map(([id]) => id)
48+.toSorted();
49+}
50+3051function hasPlugins(cfg: OpenClawConfig): boolean {
3152return hasRecord(cfg.plugins);
3253}
@@ -191,6 +212,70 @@ export function collectVisibleReplyToolPolicyWarnings(cfg: OpenClawConfig): stri
191212return warnings;
192213}
193214215+function formatChannelList(channels: string[]): string {
216+if (channels.length <= 2) {
217+return channels.map((channel) => `"${channel}"`).join(" and ");
218+}
219+return `${channels
220+ .slice(0, 2)
221+ .map((channel) => `"${channel}"`)
222+ .join(", ")}, and ${channels.length - 2} more`;
223+}
224+225+function collectBoundChannelTargets(cfg: OpenClawConfig): Array<{
226+agentId: string;
227+channels: string[];
228+}> {
229+const byAgent = new Map<string, Set<string>>();
230+const add = (agentId: string, channel: string) => {
231+const normalizedAgentId = normalizeAgentId(agentId);
232+const trimmedChannel = channel.trim();
233+if (!normalizedAgentId || !trimmedChannel) {
234+return;
235+}
236+let channels = byAgent.get(normalizedAgentId);
237+if (!channels) {
238+channels = new Set<string>();
239+byAgent.set(normalizedAgentId, channels);
240+}
241+channels.add(trimmedChannel);
242+};
243+244+const routeBindings: AgentRouteBinding[] = listRouteBindings(cfg);
245+for (const binding of routeBindings) {
246+add(binding.agentId, binding.match.channel);
247+}
248+249+if (routeBindings.length === 0) {
250+const defaultAgentId = resolveDefaultAgentId(cfg);
251+for (const channel of listConfiguredChannelIds(cfg)) {
252+add(defaultAgentId, channel);
253+}
254+}
255+256+return Array.from(byAgent.entries())
257+.map(([agentId, channels]) => ({
258+ agentId,
259+channels: Array.from(channels).toSorted(),
260+}))
261+.filter((target) => target.channels.length > 0)
262+.toSorted((a, b) => a.agentId.localeCompare(b.agentId));
263+}
264+265+export function collectChannelBoundMessageToolPolicyWarnings(cfg: OpenClawConfig): string[] {
266+return collectBoundChannelTargets(cfg).flatMap((target) => {
267+const agentTools = resolveAgentConfig(cfg, target.agentId)?.tools;
268+if (resolveMessageToolAvailability({ globalTools: cfg.tools, agentTools })) {
269+return [];
270+}
271+return [
272+`- Agent "${target.agentId}" is routed from channel ${formatChannelList(
273+ target.channels,
274+ )}, but the message tool is unavailable for that agent; explicit channel actions such as sendAttachment, upload-file, thread-reply, or reply can fail. Add "message" to the agent tool allowlist, add "group:messaging", or switch the agent to a profile that includes messaging tools.`,
275+];
276+});
277+}
278+194279export async function collectDoctorPreviewWarnings(params: {
195280cfg: OpenClawConfig;
196281doctorFixCommand: string;
@@ -202,6 +287,7 @@ export async function collectDoctorPreviewWarnings(params: {
202287const hasPluginConfig = hasPlugins(params.cfg);
203288204289warnings.push(...collectVisibleReplyToolPolicyWarnings(params.cfg));
290+warnings.push(...collectChannelBoundMessageToolPolicyWarnings(params.cfg));
205291206292const channelPluginRuntime =
207293hasChannelConfig && hasExplicitChannelPluginBlockerConfig(params.cfg)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。