mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-17 17:21:36 +00:00
fix(agents): follow through on promised asynchronous work (#105958)
* fix(agents): close promised asynchronous work loops * fix(agents): close promised asynchronous work loops * refactor(agents): keep promised work policy focused * refactor(agents): preserve prompt module documentation * style(agents): keep prompt renderer within LOC ratchet * fix(agents): close promised asynchronous work loops * fix(agents): close promised asynchronous work loops
This commit is contained in:
@@ -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", () => {
|
||||
|
||||
12
src/agents/promised-work-prompt.ts
Normal file
12
src/agents/promised-work-prompt.ts
Normal file
@@ -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.",
|
||||
"",
|
||||
];
|
||||
}
|
||||
19
src/agents/sessions/system-prompt.test.ts
Normal file
19
src/agents/sessions/system-prompt.test.ts
Normal file
@@ -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");
|
||||
});
|
||||
});
|
||||
@@ -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}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
Reference in New Issue
Block a user