feat: add TTS hint to system prompt

This commit is contained in:
Peter Steinberger
2026-01-24 10:25:37 +00:00
parent 585e20b72e
commit a6ddd82a14
8 changed files with 48 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ import type { EmbeddedContextFile } from "../pi-embedded-helpers.js";
import { buildSystemPromptParams } from "../system-prompt-params.js";
import { resolveDefaultModelForAgent } from "../model-selection.js";
import { buildAgentSystemPrompt } from "../system-prompt.js";
import { buildTtsSystemPromptHint } from "../../tts/tts.js";
const CLI_RUN_QUEUE = new Map<string, Promise<unknown>>();
@@ -194,6 +195,7 @@ export function buildSystemPrompt(params: {
defaultModel: defaultModelLabel,
},
});
const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined;
return buildAgentSystemPrompt({
workspaceDir: params.workspaceDir,
defaultThinkLevel: params.defaultThinkLevel,
@@ -209,6 +211,7 @@ export function buildSystemPrompt(params: {
userTime,
userTimeFormat,
contextFiles: params.contextFiles,
ttsHint,
});
}

View File

@@ -66,6 +66,7 @@ import { splitSdkTools } from "./tool-split.js";
import type { EmbeddedPiCompactResult } from "./types.js";
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
import { describeUnknownError, mapThinkingLevel, resolveExecToolDefaults } from "./utils.js";
import { buildTtsSystemPromptHint } from "../../tts/tts.js";
export async function compactEmbeddedPiSession(params: {
sessionId: string;
@@ -298,6 +299,7 @@ export async function compactEmbeddedPiSession(params: {
cwd: process.cwd(),
moduleUrl: import.meta.url,
});
const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined;
const appendPrompt = buildEmbeddedSystemPrompt({
workspaceDir: effectiveWorkspace,
defaultThinkLevel: params.thinkLevel,
@@ -310,6 +312,7 @@ export async function compactEmbeddedPiSession(params: {
: undefined,
skillsPrompt,
docsPath: docsPath ?? undefined,
ttsHint,
promptMode,
runtimeInfo,
messageToolHints,

View File

@@ -78,6 +78,7 @@ import { toClientToolDefinitions } from "../../pi-tool-definition-adapter.js";
import { buildSystemPromptParams } from "../../system-prompt-params.js";
import { describeUnknownError, mapThinkingLevel } from "../utils.js";
import { resolveSandboxRuntimeStatus } from "../../sandbox/runtime-status.js";
import { buildTtsSystemPromptHint } from "../../../tts/tts.js";
import { isTimeoutError } from "../../failover-error.js";
import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
import { MAX_IMAGE_BYTES } from "../../../media/constants.js";
@@ -315,6 +316,7 @@ export async function runEmbeddedAttempt(
cwd: process.cwd(),
moduleUrl: import.meta.url,
});
const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined;
const appendPrompt = buildEmbeddedSystemPrompt({
workspaceDir: effectiveWorkspace,
@@ -328,6 +330,7 @@ export async function runEmbeddedAttempt(
: undefined,
skillsPrompt,
docsPath: docsPath ?? undefined,
ttsHint,
workspaceNotes,
reactionGuidance,
promptMode,

View File

@@ -16,6 +16,7 @@ export function buildEmbeddedSystemPrompt(params: {
heartbeatPrompt?: string;
skillsPrompt?: string;
docsPath?: string;
ttsHint?: string;
reactionGuidance?: {
level: "minimal" | "extensive";
channel: string;
@@ -55,6 +56,7 @@ export function buildEmbeddedSystemPrompt(params: {
heartbeatPrompt: params.heartbeatPrompt,
skillsPrompt: params.skillsPrompt,
docsPath: params.docsPath,
ttsHint: params.ttsHint,
workspaceNotes: params.workspaceNotes,
reactionGuidance: params.reactionGuidance,
promptMode: params.promptMode,

View File

@@ -34,6 +34,7 @@ describe("buildAgentSystemPrompt", () => {
toolNames: ["message", "memory_search"],
docsPath: "/tmp/clawd/docs",
extraSystemPrompt: "Subagent details",
ttsHint: "Voice (TTS) is enabled.",
});
expect(prompt).not.toContain("## User Identity");
@@ -42,6 +43,7 @@ describe("buildAgentSystemPrompt", () => {
expect(prompt).not.toContain("## Documentation");
expect(prompt).not.toContain("## Reply Tags");
expect(prompt).not.toContain("## Messaging");
expect(prompt).not.toContain("## Voice (TTS)");
expect(prompt).not.toContain("## Silent Replies");
expect(prompt).not.toContain("## Heartbeats");
expect(prompt).toContain("## Subagent Context");
@@ -49,6 +51,16 @@ describe("buildAgentSystemPrompt", () => {
expect(prompt).toContain("Subagent details");
});
it("includes voice hint when provided", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/clawd",
ttsHint: "Voice (TTS) is enabled.",
});
expect(prompt).toContain("## Voice (TTS)");
expect(prompt).toContain("Voice (TTS) is enabled.");
});
it("adds reasoning tag hint when enabled", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/clawd",

View File

@@ -103,6 +103,13 @@ function buildMessagingSection(params: {
];
}
function buildVoiceSection(params: { isMinimal: boolean; ttsHint?: string }) {
if (params.isMinimal) return [];
const hint = params.ttsHint?.trim();
if (!hint) return [];
return ["## Voice (TTS)", hint, ""];
}
function buildDocsSection(params: { docsPath?: string; isMinimal: boolean; readToolName: string }) {
const docsPath = params.docsPath?.trim();
if (!docsPath || params.isMinimal) return [];
@@ -137,6 +144,7 @@ export function buildAgentSystemPrompt(params: {
heartbeatPrompt?: string;
docsPath?: string;
workspaceNotes?: string[];
ttsHint?: string;
/** Controls which hardcoded sections to include. Defaults to "full". */
promptMode?: PromptMode;
runtimeInfo?: {
@@ -464,6 +472,7 @@ export function buildAgentSystemPrompt(params: {
runtimeChannel,
messageToolHints: params.messageToolHints,
}),
...buildVoiceSection({ isMinimal, ttsHint: params.ttsHint }),
];
if (extraSystemPrompt) {

View File

@@ -12,6 +12,7 @@ import { buildToolSummaryMap } from "../../agents/tool-summaries.js";
import { resolveBootstrapContextForRun } from "../../agents/bootstrap-files.js";
import type { SessionSystemPromptReport } from "../../config/sessions/types.js";
import { getRemoteSkillEligibility } from "../../infra/skills-remote.js";
import { buildTtsSystemPromptHint } from "../../tts/tts.js";
import type { ReplyPayload } from "../types.js";
import type { HandleCommandsParams } from "./commands-types.js";
@@ -128,6 +129,7 @@ async function resolveContextReport(
},
}
: { enabled: false };
const ttsHint = params.cfg ? buildTtsSystemPromptHint(params.cfg) : undefined;
const systemPrompt = buildAgentSystemPrompt({
workspaceDir,
@@ -145,6 +147,7 @@ async function resolveContextReport(
contextFiles: injectedFiles,
skillsPrompt,
heartbeatPrompt: undefined,
ttsHint,
runtimeInfo,
sandboxInfo,
});

View File

@@ -244,6 +244,19 @@ export function resolveTtsPrefsPath(config: ResolvedTtsConfig): string {
return path.join(CONFIG_DIR, "settings", "tts.json");
}
export function buildTtsSystemPromptHint(cfg: ClawdbotConfig): string | undefined {
const config = resolveTtsConfig(cfg);
const prefsPath = resolveTtsPrefsPath(config);
if (!isTtsEnabled(config, prefsPath)) return undefined;
const maxLength = getTtsMaxLength(prefsPath);
const summarize = isSummarizationEnabled(prefsPath) ? "on" : "off";
return [
"Voice (TTS) is enabled.",
`Keep spoken text ≤${maxLength} chars to avoid auto-summary (summary ${summarize}).`,
"Use [[tts:...]] and optional [[tts:text]]...[[/tts:text]] to control voice/expressiveness.",
].join("\n");
}
function readPrefs(prefsPath: string): TtsUserPrefs {
try {
if (!existsSync(prefsPath)) return {};