mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-03 03:03:24 -04:00
refactor(agents): use silent token constant in prompts
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||
|
||||
const agentSpy = vi.fn(async () => ({ runId: "run-main", status: "ok" }));
|
||||
const sessionsDeleteSpy = vi.fn();
|
||||
@@ -222,7 +223,7 @@ describe("subagent announce formatting", () => {
|
||||
expect(msg).toContain("[sessionId: child-session-usage]");
|
||||
expect(msg).toContain("A completed subagent task is ready for user delivery.");
|
||||
expect(msg).toContain(
|
||||
"Reply ONLY: NO_REPLY if this exact result was already delivered to the user in this same turn.",
|
||||
`Reply ONLY: ${SILENT_REPLY_TOKEN} if this exact result was already delivered to the user in this same turn.`,
|
||||
);
|
||||
expect(msg).toContain("step-0");
|
||||
expect(msg).toContain("step-139");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { resolveQueueSettings } from "../auto-reply/reply/queue.js";
|
||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import {
|
||||
loadSessionStore,
|
||||
@@ -364,9 +365,9 @@ function buildAnnounceReplyInstruction(params: {
|
||||
return `There are still ${params.remainingActiveSubagentRuns} active subagent ${activeRunsLabel} for this session. If they are part of the same workflow, wait for the remaining results before sending a user update. If they are unrelated, respond normally using only the result above.`;
|
||||
}
|
||||
if (params.requesterIsSubagent) {
|
||||
return "Convert this completion into a concise internal orchestration update for your parent agent in your own words. Keep this internal context private (don't mention system/log/stats/session details or announce type). If this result is duplicate or no update is needed, reply ONLY: NO_REPLY.";
|
||||
return `Convert this completion into a concise internal orchestration update for your parent agent in your own words. Keep this internal context private (don't mention system/log/stats/session details or announce type). If this result is duplicate or no update is needed, reply ONLY: ${SILENT_REPLY_TOKEN}.`;
|
||||
}
|
||||
return `A completed ${params.announceType} is ready for user delivery. Convert the result above into your normal assistant voice and send that user-facing update now. Keep this internal context private (don't mention system/log/stats/session details or announce type), and do not copy the system message verbatim. Reply ONLY: NO_REPLY if this exact result was already delivered to the user in this same turn.`;
|
||||
return `A completed ${params.announceType} is ready for user delivery. Convert the result above into your normal assistant voice and send that user-facing update now. Keep this internal context private (don't mention system/log/stats/session details or announce type), and do not copy the system message verbatim. Reply ONLY: ${SILENT_REPLY_TOKEN} if this exact result was already delivered to the user in this same turn.`;
|
||||
}
|
||||
|
||||
export async function runSubagentAnnounceFlow(params: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||
import { buildSubagentSystemPrompt } from "./subagent-announce.js";
|
||||
import { buildAgentSystemPrompt, buildRuntimeLine } from "./system-prompt.js";
|
||||
|
||||
@@ -367,7 +368,7 @@ describe("buildAgentSystemPrompt", () => {
|
||||
|
||||
expect(prompt).toContain("message: Send messages and channel actions");
|
||||
expect(prompt).toContain("### message tool");
|
||||
expect(prompt).toContain("respond with ONLY: NO_REPLY");
|
||||
expect(prompt).toContain(`respond with ONLY: ${SILENT_REPLY_TOKEN}`);
|
||||
});
|
||||
|
||||
it("includes runtime provider capabilities when present", () => {
|
||||
|
||||
@@ -112,7 +112,7 @@ function buildMessagingSection(params: {
|
||||
"- Cross-session messaging → use sessions_send(sessionKey, message)",
|
||||
"- Sub-agent orchestration → use subagents(action=list|steer|kill)",
|
||||
"- `[System Message] ...` blocks are internal context and are not user-visible by default.",
|
||||
"- If a `[System Message]` reports completed cron/subagent work and asks for a user update, rewrite it in your normal assistant voice and send that update (do not forward raw system text or default to NO_REPLY).",
|
||||
`- If a \`[System Message]\` reports completed cron/subagent work and asks for a user update, rewrite it in your normal assistant voice and send that update (do not forward raw system text or default to ${SILENT_REPLY_TOKEN}).`,
|
||||
"- Never use exec/curl for provider messaging; OpenClaw handles all routing internally.",
|
||||
params.availableTools.has("message")
|
||||
? [
|
||||
|
||||
16
src/agents/tools/tts-tool.test.ts
Normal file
16
src/agents/tools/tts-tool.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../../auto-reply/tokens.js", () => ({
|
||||
SILENT_REPLY_TOKEN: "QUIET_TOKEN",
|
||||
}));
|
||||
|
||||
const { createTtsTool } = await import("./tts-tool.js");
|
||||
|
||||
describe("createTtsTool", () => {
|
||||
it("uses SILENT_REPLY_TOKEN in guidance text", () => {
|
||||
const tool = createTtsTool();
|
||||
|
||||
expect(tool.description).toContain("QUIET_TOKEN");
|
||||
expect(tool.description).not.toContain("NO_REPLY");
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { GatewayMessageChannel } from "../../utils/message-channel.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { textToSpeech } from "../../tts/tts.js";
|
||||
import { readStringParam } from "./common.js";
|
||||
@@ -20,8 +21,7 @@ export function createTtsTool(opts?: {
|
||||
return {
|
||||
label: "TTS",
|
||||
name: "tts",
|
||||
description:
|
||||
"Convert text to speech. Audio is delivered automatically from the tool result — reply with NO_REPLY after a successful call to avoid duplicate messages.",
|
||||
description: `Convert text to speech. Audio is delivered automatically from the tool result — reply with ${SILENT_REPLY_TOKEN} after a successful call to avoid duplicate messages.`,
|
||||
parameters: TtsToolSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const params = args as Record<string, unknown>;
|
||||
|
||||
@@ -138,6 +138,45 @@ describe("dispatchReplyFromConfig", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("routes media-only tool results when summaries are suppressed", async () => {
|
||||
mocks.tryFastAbortFromMessage.mockResolvedValue({
|
||||
handled: false,
|
||||
aborted: false,
|
||||
});
|
||||
mocks.routeReply.mockClear();
|
||||
const cfg = {} as OpenClawConfig;
|
||||
const dispatcher = createDispatcher();
|
||||
const ctx = buildTestCtx({
|
||||
Provider: "slack",
|
||||
ChatType: "group",
|
||||
AccountId: "acc-1",
|
||||
OriginatingChannel: "telegram",
|
||||
OriginatingTo: "telegram:999",
|
||||
});
|
||||
|
||||
const replyResolver = async (
|
||||
_ctx: MsgContext,
|
||||
opts: GetReplyOptions | undefined,
|
||||
_cfg: OpenClawConfig,
|
||||
) => {
|
||||
expect(opts?.onToolResult).toBeDefined();
|
||||
await opts?.onToolResult?.({
|
||||
text: "NO_REPLY",
|
||||
mediaUrls: ["https://example.com/tts-routed.opus"],
|
||||
});
|
||||
return undefined;
|
||||
};
|
||||
|
||||
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
|
||||
|
||||
expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
|
||||
expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
|
||||
expect(mocks.routeReply).toHaveBeenCalledTimes(1);
|
||||
const routed = mocks.routeReply.mock.calls[0]?.[0] as { payload?: ReplyPayload } | undefined;
|
||||
expect(routed?.payload?.mediaUrls).toEqual(["https://example.com/tts-routed.opus"]);
|
||||
expect(routed?.payload?.text).toBeUndefined();
|
||||
});
|
||||
|
||||
it("provides onToolResult in DM sessions", async () => {
|
||||
mocks.tryFastAbortFromMessage.mockResolvedValue({
|
||||
handled: false,
|
||||
|
||||
Reference in New Issue
Block a user