mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix: avoid no reply prompt in message tool mode (#75779)
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Agents/Codex: stop prompting message-tool-only source turns to finish with `NO_REPLY`, so quiet turns are represented by not calling the visible message tool instead of conflicting final-text instructions. Thanks @pashpashpash.
|
||||
- Gateway/config: report failed backup restores as failed in logs and config observe audit records instead of marking them valid. (#70515) Thanks @davidangularme.
|
||||
- Compaction: use the active session model fallback chain for implicit summarization failures without persisting fallback model selection, so Azure content-filter 400s can recover. Fixes #64960. (#74470) Thanks @jalehman and @OpenCodeEngineer.
|
||||
|
||||
|
||||
@@ -803,6 +803,8 @@ describe("buildAgentSystemPrompt", () => {
|
||||
expect(prompt).toContain("use `message(action=send)` for visible channel output");
|
||||
expect(prompt).toContain("The target defaults to the current source channel");
|
||||
expect(prompt).toContain("final answers are private in this mode");
|
||||
expect(prompt).not.toContain("## Silent Replies");
|
||||
expect(prompt).not.toContain(SILENT_REPLY_TOKEN);
|
||||
expect(prompt).not.toContain(
|
||||
`respond with ONLY: ${SILENT_REPLY_TOKEN} (avoid duplicate replies)`,
|
||||
);
|
||||
|
||||
@@ -351,6 +351,9 @@ function buildMessagingSection(params: {
|
||||
const showGenericInlineButtonHint = params.runtimeChannel !== "slack";
|
||||
const hasSessionsSpawn = params.availableTools.has("sessions_spawn");
|
||||
const hasSubagents = params.availableTools.has("subagents");
|
||||
const completionEventGuidance = messageToolOnly
|
||||
? "- Runtime-generated completion events may ask for a user update. Rewrite those in your normal assistant voice and send the update (do not forward raw internal metadata or default to a silent placeholder)."
|
||||
: `- Runtime-generated completion events may ask for a user update. Rewrite those in your normal assistant voice and send the update (do not forward raw internal metadata or default to ${SILENT_REPLY_TOKEN}).`;
|
||||
const subagentOrchestrationGuidance = hasSessionsSpawn
|
||||
? hasSubagents
|
||||
? '- Sub-agent orchestration → use `sessions_spawn(...)` to start delegated work; omit `context` for isolated children, set `context:"fork"` only when the child needs the current transcript; use `subagents(action=list|steer|kill)` to manage already-spawned children.'
|
||||
@@ -365,7 +368,7 @@ function buildMessagingSection(params: {
|
||||
: "- Reply in current session → automatically routes to the source channel (Signal, Telegram, etc.)",
|
||||
"- Cross-session messaging → use sessions_send(sessionKey, message)",
|
||||
subagentOrchestrationGuidance,
|
||||
`- Runtime-generated completion events may ask for a user update. Rewrite those in your normal assistant voice and send the update (do not forward raw internal metadata or default to ${SILENT_REPLY_TOKEN}).`,
|
||||
completionEventGuidance,
|
||||
"- Never use exec/curl for provider messaging; OpenClaw handles all routing internally.",
|
||||
params.availableTools.has("message")
|
||||
? [
|
||||
@@ -665,7 +668,10 @@ export function buildAgentSystemPrompt(params: {
|
||||
const messageChannelOptions = listDeliverableMessageChannels().join("|");
|
||||
const promptMode = params.promptMode ?? "full";
|
||||
const isMinimal = promptMode === "minimal" || promptMode === "none";
|
||||
const silentReplyPromptMode = params.silentReplyPromptMode ?? "generic";
|
||||
const sourceMessageToolOnly = params.sourceReplyDeliveryMode === "message_tool_only";
|
||||
const silentReplyPromptMode = sourceMessageToolOnly
|
||||
? "none"
|
||||
: (params.silentReplyPromptMode ?? "generic");
|
||||
const sandboxContainerWorkspace = params.sandboxInfo?.containerWorkspaceDir?.trim();
|
||||
const sanitizedWorkspaceDir = sanitizeForPromptLiteral(params.workspaceDir);
|
||||
const sanitizedSandboxContainerWorkspace = sandboxContainerWorkspace
|
||||
|
||||
@@ -450,6 +450,7 @@ export async function runPreparedReply(
|
||||
const directChatContext = isDirectChat
|
||||
? buildDirectChatContext({
|
||||
sessionCtx: promptSessionCtx,
|
||||
sourceReplyDeliveryMode: opts?.sourceReplyDeliveryMode,
|
||||
silentReplyPolicy: silentReplySettings.policy,
|
||||
silentReplyRewrite: silentReplySettings.rewrite,
|
||||
silentToken: SILENT_REPLY_TOKEN,
|
||||
|
||||
@@ -87,6 +87,19 @@ describe("group runtime loading", () => {
|
||||
silentToken: "NO_REPLY",
|
||||
}),
|
||||
).toContain('reply with exactly "NO_REPLY"');
|
||||
|
||||
const toolOnlyContext = groups.buildDirectChatContext({
|
||||
sessionCtx: { ChatType: "direct", Provider: "telegram" },
|
||||
sourceReplyDeliveryMode: "message_tool_only",
|
||||
silentReplyPolicy: "allow",
|
||||
silentReplyRewrite: true,
|
||||
silentToken: "NO_REPLY",
|
||||
});
|
||||
expect(toolOnlyContext).toContain("Normal final replies are private");
|
||||
expect(toolOnlyContext).toContain("message tool with action=send");
|
||||
expect(toolOnlyContext).toContain("do not call message(action=send)");
|
||||
expect(toolOnlyContext).not.toContain("NO_REPLY");
|
||||
expect(toolOnlyContext).not.toContain("Your replies are automatically sent");
|
||||
});
|
||||
|
||||
it("gates group silent-token instructions on the resolved silent reply policy", async () => {
|
||||
|
||||
@@ -283,13 +283,24 @@ export function buildGroupChatContext(params: {
|
||||
|
||||
export function buildDirectChatContext(params: {
|
||||
sessionCtx: TemplateContext;
|
||||
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
|
||||
silentReplyPolicy?: SilentReplyPolicy;
|
||||
silentReplyRewrite?: boolean;
|
||||
silentToken: string;
|
||||
}): string {
|
||||
const providerLabel = resolveProviderLabel(params.sessionCtx.Provider);
|
||||
const messageToolOnly = params.sourceReplyDeliveryMode === "message_tool_only";
|
||||
const lines: string[] = [];
|
||||
lines.push(`You are in a ${providerLabel} direct conversation.`);
|
||||
if (messageToolOnly) {
|
||||
lines.push(
|
||||
"Normal final replies are private and are not automatically sent to this conversation. To post visible output here, use the message tool with action=send; the target defaults to this conversation.",
|
||||
);
|
||||
lines.push(
|
||||
"If no visible direct response is needed, do not call message(action=send). Your normal final answer stays private and will not be posted to the conversation.",
|
||||
);
|
||||
return lines.join(" ");
|
||||
}
|
||||
lines.push("Your replies are automatically sent to this conversation.");
|
||||
if (params.silentReplyPolicy === "allow") {
|
||||
lines.push(
|
||||
|
||||
Reference in New Issue
Block a user