Files
openclaw/src/auto-reply/status.ts
2026-04-25 17:59:05 +01:00

108 lines
3.1 KiB
TypeScript

import { describeToolForVerbose } from "../agents/tool-description-summary.js";
import { normalizeToolName } from "../agents/tool-policy-shared.js";
import type { EffectiveToolInventoryResult } from "../agents/tools-effective-inventory.types.js";
export {
buildCommandsMessage,
buildCommandsMessagePaginated,
buildHelpMessage,
type CommandsMessageOptions,
type CommandsMessageResult,
} from "./command-status-builders.js";
export {
buildStatusMessage,
formatContextUsageShort,
formatTokenCount,
type StatusArgs,
} from "../status/status-message.js";
type ToolsMessageItem = {
id: string;
name: string;
description: string;
rawDescription: string;
source: EffectiveToolInventoryResult["groups"][number]["source"];
pluginId?: string;
channelId?: string;
};
function sortToolsMessageItems(items: ToolsMessageItem[]): ToolsMessageItem[] {
return items.toSorted((a, b) => a.name.localeCompare(b.name));
}
function formatCompactToolEntry(tool: ToolsMessageItem): string {
if (tool.source === "plugin") {
return tool.pluginId ? `${tool.id} (${tool.pluginId})` : tool.id;
}
if (tool.source === "channel") {
return tool.channelId ? `${tool.id} (${tool.channelId})` : tool.id;
}
return tool.id;
}
function formatVerboseToolDescription(tool: ToolsMessageItem): string {
return describeToolForVerbose({
rawDescription: tool.rawDescription,
fallback: tool.description,
});
}
export function buildToolsMessage(
result: EffectiveToolInventoryResult,
options?: { verbose?: boolean },
): string {
const groups = result.groups
.map((group) => ({
label: group.label,
tools: sortToolsMessageItems(
group.tools.map((tool) => ({
id: normalizeToolName(tool.id),
name: tool.label,
description: tool.description || "Tool",
rawDescription: tool.rawDescription || tool.description || "Tool",
source: tool.source,
pluginId: tool.pluginId,
channelId: tool.channelId,
})),
),
}))
.filter((group) => group.tools.length > 0);
if (groups.length === 0) {
const lines = [
"No tools are available for this agent right now.",
"",
`Profile: ${result.profile}`,
];
return lines.join("\n");
}
const verbose = options?.verbose === true;
const lines = verbose
? ["Available tools", "", `Profile: ${result.profile}`, "What this agent can use right now:"]
: ["Available tools", "", `Profile: ${result.profile}`];
for (const group of groups) {
lines.push("", group.label);
if (verbose) {
for (const tool of group.tools) {
lines.push(` ${tool.name} - ${formatVerboseToolDescription(tool)}`);
}
continue;
}
lines.push(` ${group.tools.map((tool) => formatCompactToolEntry(tool)).join(", ")}`);
}
if (verbose) {
lines.push("", "Tool availability depends on this agent's configuration.");
} else {
lines.push("", "Use /tools verbose for descriptions.");
}
if (result.notices?.length) {
lines.push("", "Notes");
for (const notice of result.notices) {
lines.push(` ${notice.message}`);
}
}
return lines.join("\n");
}