refactor(agents): enrich tool descriptions

This commit is contained in:
Peter Steinberger
2026-04-05 13:31:33 +01:00
parent 987f4bba80
commit 31f5463a1c
11 changed files with 109 additions and 19 deletions

View File

@@ -52,6 +52,7 @@ import {
truncateMiddle,
} from "./bash-tools.shared.js";
import { assertSandboxPath } from "./sandbox-paths.js";
import { EXEC_TOOL_DISPLAY_SUMMARY } from "./tool-description-presets.js";
import { failedTextResult, textResult } from "./tools/common.js";
export type { BashSandboxConfig } from "./bash-tools.shared.js";
@@ -1219,6 +1220,7 @@ export function createExecTool(
return {
name: "exec",
label: "exec",
displaySummary: EXEC_TOOL_DISPLAY_SUMMARY,
get description() {
return describeExecTool({ agentId, hasCronTool: defaults?.hasCronTool === true });
},

View File

@@ -18,6 +18,7 @@ import {
import { deriveSessionName, pad, sliceLogLines, truncateMiddle } from "./bash-tools.shared.js";
import { recordCommandPoll, resetCommandPollCount } from "./command-poll-backoff.js";
import { encodeKeySequence, encodePaste, hasCursorModeSensitiveKeys } from "./pty-keys.js";
import { PROCESS_TOOL_DISPLAY_SUMMARY } from "./tool-description-presets.js";
export type ProcessToolDefaults = {
cleanupMs?: number;
@@ -162,6 +163,7 @@ export function createProcessTool(
return {
name: "process",
label: "process",
displaySummary: PROCESS_TOOL_DISPLAY_SUMMARY,
description: describeProcessTool({ hasCronTool: defaults?.hasCronTool === true }),
parameters: processSchema,
execute: async (_toolCallId, args, _signal, _onUpdate): Promise<AgentToolResult<unknown>> => {

View File

@@ -440,8 +440,12 @@ describe("buildAgentSystemPrompt", () => {
docsPath: "/tmp/openclaw/docs",
});
expect(prompt).toContain("- Read: Read file contents");
expect(prompt).toContain("- Exec: Run shell commands");
expect(prompt).toContain(
"Tool names are case-sensitive. Call tools exactly as listed in the structured tool definitions.",
);
expect(prompt).toContain(
"For long waits, avoid rapid poll loops: use Exec with enough yieldMs or process(action=poll, timeout=<ms>).",
);
expect(prompt).toContain(
"- If exactly one skill clearly applies: read its SKILL.md at <location> with `Read`, then follow it.",
);

View File

@@ -1,3 +1,14 @@
import {
CRON_TOOL_DISPLAY_SUMMARY,
EXEC_TOOL_DISPLAY_SUMMARY,
PROCESS_TOOL_DISPLAY_SUMMARY,
SESSIONS_HISTORY_TOOL_DISPLAY_SUMMARY,
SESSIONS_LIST_TOOL_DISPLAY_SUMMARY,
SESSIONS_SEND_TOOL_DISPLAY_SUMMARY,
SESSIONS_SPAWN_TOOL_DISPLAY_SUMMARY,
SESSION_STATUS_TOOL_DISPLAY_SUMMARY,
} from "./tool-description-presets.js";
export type ToolProfileId = "minimal" | "coding" | "messaging" | "full";
type ToolProfilePolicy = {
@@ -70,14 +81,14 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "exec",
label: "exec",
description: "Run shell commands",
description: EXEC_TOOL_DISPLAY_SUMMARY,
sectionId: "runtime",
profiles: ["coding"],
},
{
id: "process",
label: "process",
description: "Manage background processes",
description: PROCESS_TOOL_DISPLAY_SUMMARY,
sectionId: "runtime",
profiles: ["coding"],
},
@@ -132,7 +143,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "sessions_list",
label: "sessions_list",
description: "List sessions",
description: SESSIONS_LIST_TOOL_DISPLAY_SUMMARY,
sectionId: "sessions",
profiles: ["coding", "messaging"],
includeInOpenClawGroup: true,
@@ -140,7 +151,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "sessions_history",
label: "sessions_history",
description: "Session history",
description: SESSIONS_HISTORY_TOOL_DISPLAY_SUMMARY,
sectionId: "sessions",
profiles: ["coding", "messaging"],
includeInOpenClawGroup: true,
@@ -148,7 +159,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "sessions_send",
label: "sessions_send",
description: "Send to session",
description: SESSIONS_SEND_TOOL_DISPLAY_SUMMARY,
sectionId: "sessions",
profiles: ["coding", "messaging"],
includeInOpenClawGroup: true,
@@ -156,7 +167,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "sessions_spawn",
label: "sessions_spawn",
description: "Spawn sub-agent",
description: SESSIONS_SPAWN_TOOL_DISPLAY_SUMMARY,
sectionId: "sessions",
profiles: ["coding"],
includeInOpenClawGroup: true,
@@ -180,7 +191,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "session_status",
label: "session_status",
description: "Session status",
description: SESSION_STATUS_TOOL_DISPLAY_SUMMARY,
sectionId: "sessions",
profiles: ["minimal", "coding", "messaging"],
includeInOpenClawGroup: true,
@@ -212,7 +223,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
{
id: "cron",
label: "cron",
description: "Schedule tasks",
description: CRON_TOOL_DISPLAY_SUMMARY,
sectionId: "automation",
profiles: ["coding"],
includeInOpenClawGroup: true,

View File

@@ -0,0 +1,48 @@
export const EXEC_TOOL_DISPLAY_SUMMARY = "Run shell commands that start now.";
export const PROCESS_TOOL_DISPLAY_SUMMARY = "Inspect and control running exec sessions.";
export const CRON_TOOL_DISPLAY_SUMMARY = "Schedule cron jobs, reminders, and wake events.";
export const SESSIONS_LIST_TOOL_DISPLAY_SUMMARY =
"List visible sessions and optional recent messages.";
export const SESSIONS_HISTORY_TOOL_DISPLAY_SUMMARY =
"Read sanitized message history for a visible session.";
export const SESSIONS_SEND_TOOL_DISPLAY_SUMMARY = "Send a message to another visible session.";
export const SESSIONS_SPAWN_TOOL_DISPLAY_SUMMARY = "Spawn sub-agent or ACP sessions.";
export const SESSION_STATUS_TOOL_DISPLAY_SUMMARY = "Show session status, usage, and model state.";
export function describeSessionsListTool(): string {
return [
"List visible sessions with optional filters for kind, recent activity, and last messages.",
"Use this to discover a target session before calling sessions_history or sessions_send.",
].join(" ");
}
export function describeSessionsHistoryTool(): string {
return [
"Fetch sanitized message history for a visible session.",
"Supports limits and optional tool messages; use this to inspect another session before replying, debugging, or resuming work.",
].join(" ");
}
export function describeSessionsSendTool(): string {
return [
"Send a message into another visible session by sessionKey or label.",
"Use this to delegate follow-up work to an existing session; waits for the target run and returns the updated assistant reply when available.",
].join(" ");
}
export function describeSessionsSpawnTool(): string {
return [
'Spawn an isolated session with `runtime="subagent"` or `runtime="acp"`.',
'`mode="run"` is one-shot and `mode="session"` is persistent or thread-bound.',
"Subagents inherit the parent workspace directory automatically.",
"Use this when the work should happen in a fresh child session instead of the current one.",
].join(" ");
}
export function describeSessionStatusTool(): string {
return [
"Show a /status-equivalent session status card for the current or another visible session, including usage, time, cost when available, and linked background task context.",
"Optional `model` sets a per-session model override; `model=default` resets overrides.",
"Use this for questions like what model is active or how a session is configured.",
].join(" ");
}

View File

@@ -8,6 +8,7 @@ import { extractTextFromChatContent } from "../../shared/chat-content.js";
import { isRecord, truncateUtf16Safe } from "../../utils.js";
import { resolveSessionAgentId } from "../agent-scope.js";
import { optionalStringEnum, stringEnum } from "../schema/typebox.js";
import { CRON_TOOL_DISPLAY_SUMMARY } from "../tool-description-presets.js";
import { type AnyAgentTool, jsonResult, readStringParam } from "./common.js";
import { callGatewayTool, readGatewayCallOptions, type GatewayCallOptions } from "./gateway.js";
import { resolveInternalSessionKey, resolveMainSessionAlias } from "./sessions-helpers.js";
@@ -424,7 +425,7 @@ export function createCronTool(opts?: CronToolOptions, deps?: CronToolDeps): Any
label: "Cron",
name: "cron",
ownerOnly: true,
displaySummary: "Schedule and manage cron jobs and wake events.",
displaySummary: CRON_TOOL_DISPLAY_SUMMARY,
description: `Manage Gateway cron jobs (status/list/add/update/remove/run/runs) and send wake events. Use this for reminders, "check back later" requests, delayed follow-ups, and recurring tasks. Do not emulate scheduling with exec sleep or process polling.
Main-session cron jobs enqueue system events for heartbeat handling. Isolated cron jobs create background task runs that appear in \`openclaw tasks\`.

View File

@@ -32,6 +32,10 @@ import {
resolveDefaultModelForAgent,
resolveModelRefFromString,
} from "../model-selection.js";
import {
describeSessionStatusTool,
SESSION_STATUS_TOOL_DISPLAY_SUMMARY,
} from "../tool-description-presets.js";
import type { AnyAgentTool } from "./common.js";
import { readStringParam } from "./common.js";
import {
@@ -212,8 +216,8 @@ export function createSessionStatusTool(opts?: {
return {
label: "Session Status",
name: "session_status",
description:
"Show a /status-equivalent session status card (usage + time + cost when available), including linked background task context when present. Use for model-use questions (📊 session_status). Optional: set per-session model override (model=default resets overrides).",
displaySummary: SESSION_STATUS_TOOL_DISPLAY_SUMMARY,
description: describeSessionStatusTool(),
parameters: SessionStatusToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;

View File

@@ -5,6 +5,10 @@ import { capArrayByJsonBytes } from "../../gateway/session-utils.fs.js";
import { jsonUtf8Bytes } from "../../infra/json-utf8-bytes.js";
import { redactSensitiveText } from "../../logging/redact.js";
import { truncateUtf16Safe } from "../../utils.js";
import {
describeSessionsHistoryTool,
SESSIONS_HISTORY_TOOL_DISPLAY_SUMMARY,
} from "../tool-description-presets.js";
import type { AnyAgentTool } from "./common.js";
import { jsonResult, readStringParam } from "./common.js";
import {
@@ -176,7 +180,8 @@ export function createSessionsHistoryTool(opts?: {
return {
label: "Session History",
name: "sessions_history",
description: "Fetch message history for a session.",
displaySummary: SESSIONS_HISTORY_TOOL_DISPLAY_SUMMARY,
description: describeSessionsHistoryTool(),
parameters: SessionsHistoryToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;

View File

@@ -8,6 +8,10 @@ import {
} from "../../config/sessions.js";
import { callGateway } from "../../gateway/call.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import {
describeSessionsListTool,
SESSIONS_LIST_TOOL_DISPLAY_SUMMARY,
} from "../tool-description-presets.js";
import type { AnyAgentTool } from "./common.js";
import { jsonResult, readStringArrayParam } from "./common.js";
import {
@@ -41,7 +45,8 @@ export function createSessionsListTool(opts?: {
return {
label: "Sessions",
name: "sessions_list",
description: "List sessions with optional filters and last messages.",
displaySummary: SESSIONS_LIST_TOOL_DISPLAY_SUMMARY,
description: describeSessionsListTool(),
parameters: SessionsListToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;

View File

@@ -13,6 +13,10 @@ import {
readLatestAssistantReplySnapshot,
waitForAgentRunAndReadUpdatedAssistantReply,
} from "../run-wait.js";
import {
describeSessionsSendTool,
SESSIONS_SEND_TOOL_DISPLAY_SUMMARY,
} from "../tool-description-presets.js";
import type { AnyAgentTool } from "./common.js";
import { jsonResult, readStringParam } from "./common.js";
import {
@@ -78,8 +82,8 @@ export function createSessionsSendTool(opts?: {
return {
label: "Session Send",
name: "sessions_send",
description:
"Send a message into another session. Use sessionKey or label to identify the target.",
displaySummary: SESSIONS_SEND_TOOL_DISPLAY_SUMMARY,
description: describeSessionsSendTool(),
parameters: SessionsSendToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;

View File

@@ -8,6 +8,10 @@ import { optionalStringEnum } from "../schema/typebox.js";
import type { SpawnedToolContext } from "../spawned-context.js";
import { registerSubagentRun } from "../subagent-registry.js";
import { SUBAGENT_SPAWN_MODES, spawnSubagentDirect } from "../subagent-spawn.js";
import {
describeSessionsSpawnTool,
SESSIONS_SPAWN_TOOL_DISPLAY_SUMMARY,
} from "../tool-description-presets.js";
import type { AnyAgentTool } from "./common.js";
import { jsonResult, readStringParam, ToolInputError } from "./common.js";
import {
@@ -131,8 +135,8 @@ export function createSessionsSpawnTool(
return {
label: "Sessions",
name: "sessions_spawn",
description:
'Spawn an isolated session (runtime="subagent" or runtime="acp"). mode="run" is one-shot and mode="session" is persistent/thread-bound. Subagents inherit the parent workspace directory automatically.',
displaySummary: SESSIONS_SPAWN_TOOL_DISPLAY_SUMMARY,
description: describeSessionsSpawnTool(),
parameters: SessionsSpawnToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;