diff --git a/src/agents/embedded-agent-runner/system-prompt.test.ts b/src/agents/embedded-agent-runner/system-prompt.test.ts index 08c18e5d9d85..3c237b5ca4d0 100644 --- a/src/agents/embedded-agent-runner/system-prompt.test.ts +++ b/src/agents/embedded-agent-runner/system-prompt.test.ts @@ -223,6 +223,7 @@ describe("buildEmbeddedSystemPrompt", () => { nativeCommandGuidanceLines: ["Subagent-only command guidance."], modelAliasLines: [], userTimezone: "UTC", + promptMode: "minimal", }); expect(prompt).toContain("- sessions_spawn"); @@ -231,6 +232,9 @@ describe("buildEmbeddedSystemPrompt", () => { expect(prompt).not.toContain("Larger work: use `sessions_spawn`"); expect(prompt).not.toContain("Do not poll `subagents list` / `sessions_list` in a loop"); expect(prompt).toContain("Subagent-only command guidance."); + expect(prompt).toContain("## Promised Work"); + expect(prompt).toContain("Progress such as `running` is not completion."); + expect(prompt.match(/## Promised Work/g)).toHaveLength(1); }); it("can omit base memory guidance for non-legacy context engines", () => { diff --git a/src/agents/promised-work-prompt.ts b/src/agents/promised-work-prompt.ts new file mode 100644 index 000000000000..8534646f9a69 --- /dev/null +++ b/src/agents/promised-work-prompt.ts @@ -0,0 +1,12 @@ +/** Shared prompt policy for commitments that outlive the current turn. */ +export function buildPromisedWorkPromptSection(): string[] { + return [ + "## Promised Work", + "- Promising future, background, delegated, or continued work creates follow-through ownership.", + "- Before ending a turn, arrange an available push-based completion or watch path; keep the originating request and any existing goal or task open.", + "- Proactively return with the result, link, proof, or a concrete blocker; do not wait for the requester to ask.", + "- If no completion path exists, do not promise later; stay in the turn or state the blocker.", + "- Progress such as `running` is not completion.", + "", + ]; +} diff --git a/src/agents/sessions/system-prompt.test.ts b/src/agents/sessions/system-prompt.test.ts new file mode 100644 index 000000000000..8885355aac66 --- /dev/null +++ b/src/agents/sessions/system-prompt.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; +import { buildSystemPrompt } from "./system-prompt.js"; + +describe("buildSystemPrompt", () => { + it("includes promised-work policy in the default prompt only", () => { + const prompt = buildSystemPrompt({ cwd: "/tmp/workspace" }); + + expect(prompt).toContain("## Promised Work"); + expect(prompt).toContain("Progress such as `running` is not completion."); + expect(prompt.match(/## Promised Work/g)).toHaveLength(1); + + expect( + buildSystemPrompt({ + cwd: "/tmp/workspace", + customPrompt: "Custom replacement prompt", + }), + ).not.toContain("## Promised Work"); + }); +}); diff --git a/src/agents/sessions/system-prompt.ts b/src/agents/sessions/system-prompt.ts index 1fdf5c851ff7..12d63369fd04 100644 --- a/src/agents/sessions/system-prompt.ts +++ b/src/agents/sessions/system-prompt.ts @@ -4,6 +4,7 @@ import { formatSkillsForPrompt, type Skill } from "../../skills/loading/session.js"; import { getDocsPath, getExamplesPath, getReadmePath } from "../config.js"; +import { buildPromisedWorkPromptSection } from "../promised-work-prompt.js"; export interface BuildSystemPromptOptions { /** Custom system prompt (replaces default). */ @@ -143,6 +144,8 @@ In addition to the tools above, you may have access to other custom tools depend Guidelines: ${guidelines} +${buildPromisedWorkPromptSection().join("\n")} + Embedded agent documentation (read only when the user asks about the embedded agent SDK, extensions, themes, skills, or TUI): - Main documentation: ${readmePath} - Additional docs: ${docsPath} diff --git a/src/agents/system-prompt.test.ts b/src/agents/system-prompt.test.ts index 1cf115b20d3f..96857ad749b5 100644 --- a/src/agents/system-prompt.test.ts +++ b/src/agents/system-prompt.test.ts @@ -164,6 +164,26 @@ describe("buildAgentSystemPrompt", () => { expect(prompt).toContain("Subagent details"); }); + it("keeps promised asynchronous work open in full and minimal prompts", () => { + for (const promptMode of ["full", "minimal"] as const) { + const prompt = buildAgentSystemPrompt({ + workspaceDir: "/tmp/openclaw", + promptMode, + }); + + expect(prompt).toContain("## Promised Work"); + expect(prompt).toContain("Progress such as `running` is not completion."); + expect(prompt.match(/## Promised Work/g)).toHaveLength(1); + } + + expect( + buildAgentSystemPrompt({ + workspaceDir: "/tmp/openclaw", + promptMode: "none", + }), + ).not.toContain("## Promised Work"); + }); + it("can omit generic silent-reply guidance for channel-aware prompts", () => { const prompt = buildAgentSystemPrompt({ workspaceDir: "/tmp/openclaw", diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index c2ded76dbc8a..ed5bb0782a5e 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -44,6 +44,7 @@ import type { EmbeddedFullAccessBlockedReason, EmbeddedSandboxInfo, } from "./embedded-agent-runner/types.js"; +import { buildPromisedWorkPromptSection } from "./promised-work-prompt.js"; import { buildOpenClawToolFallbackText, shouldRenderOpenClawToolWorkflowHints, @@ -1142,6 +1143,7 @@ export function buildAgentSystemPrompt(params: { isMinimal, }), }), + ...buildPromisedWorkPromptSection(), ...buildOverridablePromptSection({ override: providerStablePrefix, fallback: [],