mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
fix(agents): keep automation tool guidance consistent (#129953)
This commit is contained in:
committed by
GitHub
parent
4708c0b607
commit
3316bfd5ab
@@ -12,11 +12,11 @@ import {
|
||||
} from "./tool-description-presets.js";
|
||||
import { createConversationsSendTool } from "./tools/conversation-tools.js";
|
||||
|
||||
function findToolDescription(toolName: string, includeCron: boolean) {
|
||||
function findToolDescription(toolName: string, schedulerToolName?: "automations" | "cron") {
|
||||
const tools = applyToolAvailabilityDescriptions([
|
||||
{ name: "exec", description: "exec base" },
|
||||
{ name: "process", description: "process base" },
|
||||
...(includeCron ? [{ name: "cron", description: "cron base" }] : []),
|
||||
...(schedulerToolName ? [{ name: schedulerToolName, description: "scheduler base" }] : []),
|
||||
] as AnyAgentTool[]);
|
||||
const tool = tools.find((entry) => entry.name === toolName);
|
||||
return {
|
||||
@@ -26,22 +26,25 @@ function findToolDescription(toolName: string, includeCron: boolean) {
|
||||
}
|
||||
|
||||
describe("createOpenClawCodingTools availability guidance", () => {
|
||||
it("keeps cron-specific guidance when cron survives filtering", () => {
|
||||
const exec = findToolDescription("exec", true);
|
||||
const process = findToolDescription("process", true);
|
||||
it.each(["automations", "cron"] as const)(
|
||||
"uses canonical automation guidance when %s survives filtering",
|
||||
(schedulerToolName) => {
|
||||
const exec = findToolDescription("exec", schedulerToolName);
|
||||
const process = findToolDescription("process", schedulerToolName);
|
||||
|
||||
expect(exec.toolNames).toEqual(["exec", "process", "cron"]);
|
||||
expect(exec.description).toBe(
|
||||
"Run shell now; background continuation supported. Use yieldMs/background, then process for logs/status/input/intervention. Long run: automatic completion wake when enabled and output/failure occurs; otherwise process confirms completion. No sleep/delay loops for reminders/follow-ups; use cron. TTY CLI/UI/coding agent: pty=true.",
|
||||
);
|
||||
expect(process.description).toBe(
|
||||
"Control existing exec: list, poll, log, write, send-keys, submit, paste, kill. poll/log: status, output, quiet success, completion without auto-wake, input hints. Others: input/intervention. No polling as timer/reminder; scheduled follow-up uses cron.",
|
||||
);
|
||||
});
|
||||
expect(exec.toolNames).toEqual(["exec", "process", schedulerToolName]);
|
||||
expect(exec.description).toBe(
|
||||
"Run shell now; background continuation supported. Use yieldMs/background, then process for logs/status/input/intervention. Long run: automatic completion wake when enabled and output/failure occurs; otherwise process confirms completion. No sleep loops for reminders/follow-ups; use automations. TTY CLI/UI/coding agent: pty=true.",
|
||||
);
|
||||
expect(process.description).toBe(
|
||||
"Control existing exec: list, poll, log, write, send-keys, submit, paste, kill. poll/log: status, output, quiet success, completion without auto-wake, input hints. Others: input/intervention. No polling as timer/reminder; scheduled follow-up uses automations.",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it("drops cron-specific guidance when cron is unavailable", () => {
|
||||
const exec = findToolDescription("exec", false);
|
||||
const process = findToolDescription("process", false);
|
||||
it("drops automation guidance when the scheduler is unavailable", () => {
|
||||
const exec = findToolDescription("exec");
|
||||
const process = findToolDescription("process");
|
||||
|
||||
expect(exec.toolNames).toEqual(["exec", "process"]);
|
||||
expect(exec.description).toBe(
|
||||
|
||||
@@ -34,7 +34,7 @@ export function describeExecTool(params?: {
|
||||
];
|
||||
const base = [
|
||||
...continuation,
|
||||
params?.hasCronTool ? "No sleep/delay loops for reminders/follow-ups; use cron." : undefined,
|
||||
params?.hasCronTool ? "No sleep loops for reminders/follow-ups; use automations." : undefined,
|
||||
"TTY CLI/UI/coding agent: pty=true.",
|
||||
]
|
||||
.filter(Boolean)
|
||||
@@ -83,7 +83,7 @@ export function describeProcessTool(params?: { hasCronTool?: boolean }): string
|
||||
"Control existing exec: list, poll, log, write, send-keys, submit, paste, kill.",
|
||||
"poll/log: status, output, quiet success, completion without auto-wake, input hints. Others: input/intervention.",
|
||||
params?.hasCronTool
|
||||
? "No polling as timer/reminder; scheduled follow-up uses cron."
|
||||
? "No polling as timer/reminder; scheduled follow-up uses automations."
|
||||
: undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
|
||||
@@ -688,7 +688,7 @@ const runNotifyNoopCase = async ({ label, defaults, expectNotification }: Notify
|
||||
};
|
||||
|
||||
describe("tool descriptions", () => {
|
||||
it("adds cron-specific deferred follow-up guidance only when cron is available", () => {
|
||||
it("adds automation follow-up guidance only when the scheduler is available", () => {
|
||||
const execWithCron = createTestExecTool({ hasCronTool: true });
|
||||
const processWithCron = createProcessTool({ hasCronTool: true });
|
||||
|
||||
@@ -698,10 +698,10 @@ describe("tool descriptions", () => {
|
||||
expect(processWithCron.description).toContain("completion without auto-wake");
|
||||
expect(processWithCron.description).toContain("write, send-keys, submit, paste, kill");
|
||||
expect(execWithCron.description).toContain(
|
||||
"No sleep/delay loops for reminders/follow-ups; use cron.",
|
||||
"No sleep loops for reminders/follow-ups; use automations.",
|
||||
);
|
||||
expect(processWithCron.description).toContain(
|
||||
"No polling as timer/reminder; scheduled follow-up uses cron.",
|
||||
"No polling as timer/reminder; scheduled follow-up uses automations.",
|
||||
);
|
||||
expect(execTool.description).not.toContain("use cron instead");
|
||||
expect(processTool.description).not.toContain("scheduled follow-ups");
|
||||
|
||||
@@ -32,7 +32,13 @@ describe("createHeartbeatResponseTool", () => {
|
||||
|
||||
const outcome = readSchemaProperty(tool.parameters, "outcome");
|
||||
const priority = readSchemaProperty(tool.parameters, "priority");
|
||||
const scratch = readSchemaProperty(tool.parameters, "scratch");
|
||||
|
||||
expect(tool.catalogMode).toBe("direct-only");
|
||||
expect(tool.description).toContain("Scratch is monitor prose only.");
|
||||
expect(tool.description).not.toMatch(/\b(?:cron|automations)\b/);
|
||||
expect(JSON.stringify(tool.parameters)).not.toMatch(/\b(?:cron|automations)\b/);
|
||||
expect(scratch.description).toContain("not a recurring schedule");
|
||||
expect(outcome.type).toBe("string");
|
||||
expect(outcome.enum).toEqual(["no_change", "progress", "done", "blocked", "needs_attention"]);
|
||||
expect(priority.type).toBe("string");
|
||||
|
||||
@@ -29,8 +29,7 @@ const HeartbeatResponseToolSchema = Type.Object(
|
||||
nextCheck: Type.Optional(Type.String()),
|
||||
scratch: Type.Optional(
|
||||
Type.String({
|
||||
description:
|
||||
"Complete replacement for heartbeat monitor prose. Recurring schedules belong in automations, not scratch.",
|
||||
description: "Complete replacement for heartbeat monitor prose; not a recurring schedule.",
|
||||
}),
|
||||
),
|
||||
},
|
||||
@@ -56,7 +55,7 @@ export function createHeartbeatResponseTool(): AnyAgentTool {
|
||||
catalogMode: "direct-only",
|
||||
displaySummary: "Accept heartbeat outcome/notify choice.",
|
||||
description:
|
||||
"Accept heartbeat result for post-turn handling. `notify=false` no visible send. `notify=true` needs concise notificationText. Scratch is monitor prose only; recurring work belongs in automations.",
|
||||
"Accept heartbeat result for post-turn handling. `notify=false` no visible send. `notify=true` needs concise notificationText. Scratch is monitor prose only.",
|
||||
parameters: HeartbeatResponseToolSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
if (!isRecord(args)) {
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
"name": "openclaw_direct",
|
||||
"tools": [
|
||||
{
|
||||
"description": "Accept heartbeat result for post-turn handling. `notify=false` no visible send. `notify=true` needs concise notificationText. Scratch is monitor prose only; recurring work belongs in automations.",
|
||||
"description": "Accept heartbeat result for post-turn handling. `notify=false` no visible send. `notify=true` needs concise notificationText. Scratch is monitor prose only.",
|
||||
"inputSchema": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -31,7 +31,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"scratch": {
|
||||
"description": "Complete replacement for heartbeat monitor prose. Recurring schedules belong in automations, not scratch.",
|
||||
"description": "Complete replacement for heartbeat monitor prose; not a recurring schedule.",
|
||||
"type": "string"
|
||||
},
|
||||
"summary": {
|
||||
|
||||
test/fixtures/agents/prompt-snapshots/codex-runtime-happy-path/telegram-heartbeat-codex-tool.md.diff
Vendored
+7
-7
@@ -1,5 +1,5 @@
|
||||
--- telegram-direct-codex-message-tool.md sha256=0a75703590d0c2198fdadbf2e427bd7f3242ebfb7d10ae339585e17f566be58c
|
||||
+++ telegram-heartbeat-codex-tool.md sha256=c2205edcd94a0c5735c4ef1d6250a10ae111b3df58d53583a13d6bb197104fa0
|
||||
+++ telegram-heartbeat-codex-tool.md sha256=3d557f5b83324ad5ff9ab39c3fed596c3aaf935f084674a0a620d452a1102489
|
||||
@@ -1,1 +1,1 @@
|
||||
-# Telegram Direct Codex Message Tool Turn
|
||||
+# Telegram Direct Codex Heartbeat Tool Turn
|
||||
@@ -34,8 +34,8 @@
|
||||
@@ -235,2 +230,2 @@
|
||||
- "chars": 54893,
|
||||
- "roughTokens": 13724
|
||||
+ "chars": 56455,
|
||||
+ "roughTokens": 14114
|
||||
+ "chars": 56386,
|
||||
+ "roughTokens": 14097
|
||||
@@ -243,2 +238,2 @@
|
||||
- "chars": 27336,
|
||||
- "roughTokens": 6834
|
||||
@@ -44,8 +44,8 @@
|
||||
@@ -247,2 +242,2 @@
|
||||
- "chars": 82231,
|
||||
- "roughTokens": 20558
|
||||
+ "chars": 84135,
|
||||
+ "roughTokens": 21034
|
||||
+ "chars": 84066,
|
||||
+ "roughTokens": 21017
|
||||
@@ -251,2 +246,2 @@
|
||||
- "chars": 863,
|
||||
- "roughTokens": 216
|
||||
@@ -65,7 +65,7 @@
|
||||
@@ -699,0 +696,39 @@
|
||||
+ },
|
||||
+ {
|
||||
+ "description": "Accept heartbeat result for post-turn handling. `notify=false` no visible send. `notify=true` needs concise notificationText. Scratch is monitor prose only; recurring work belongs in automations.",
|
||||
+ "description": "Accept heartbeat result for post-turn handling. `notify=false` no visible send. `notify=true` needs concise notificationText. Scratch is monitor prose only.",
|
||||
+ "inputSchema": {
|
||||
+ "additionalProperties": false,
|
||||
+ "properties": {
|
||||
@@ -90,7 +90,7 @@
|
||||
+ "type": "string"
|
||||
+ },
|
||||
+ "scratch": {
|
||||
+ "description": "Complete replacement for heartbeat monitor prose. Recurring schedules belong in automations, not scratch.",
|
||||
+ "description": "Complete replacement for heartbeat monitor prose; not a recurring schedule.",
|
||||
+ "type": "string"
|
||||
+ },
|
||||
+ "summary": {
|
||||
|
||||
Reference in New Issue
Block a user