From 085b89bf726c84cd3cfe93cf0d1ad1ca1995fb64 Mon Sep 17 00:00:00 2001 From: viettranx Date: Thu, 16 Apr 2026 18:55:43 +0700 Subject: [PATCH] feat(agent): inject TTS [[tts]] directive guidance when auto mode is "tagged" - Add TTSAutoMode field to LoopConfig, Loop, SystemPromptConfig - Wire from config.Tts.Auto through resolver to system prompt - Inject "## Voice Response" section explaining [[tts]] usage - Update AGENTS.md: voice only when user explicitly requests --- cmd/gateway_managed.go | 1 + internal/agent/loop_history.go | 1 + internal/agent/loop_types.go | 8 ++++++++ internal/agent/resolver.go | 4 ++++ internal/agent/systemprompt.go | 9 +++++++++ internal/agent/systemprompt_sections.go | 19 +++++++++++++++++++ internal/bootstrap/templates/AGENTS.md | 2 +- 7 files changed, 43 insertions(+), 1 deletion(-) diff --git a/cmd/gateway_managed.go b/cmd/gateway_managed.go index af837ef5..5d2c111b 100644 --- a/cmd/gateway_managed.go +++ b/cmd/gateway_managed.go @@ -235,6 +235,7 @@ func wireExtras( SkillTenantCfgs: stores.SkillTenantCfgs, SystemConfigs: stores.SystemConfigs, Workspace: workspace, + TTSAutoMode: appCfg.Tts.Auto, AutoInjector: autoInjector, EvolutionMetricsStore: stores.EvolutionMetrics, DomainBus: domainBus, diff --git a/internal/agent/loop_history.go b/internal/agent/loop_history.go index 855d6200..ba2f366f 100644 --- a/internal/agent/loop_history.go +++ b/internal/agent/loop_history.go @@ -231,6 +231,7 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s SandboxWorkspaceAccess: l.sandboxWorkspaceAccess, ShellDenyGroups: l.shellDenyGroups, SelfEvolve: l.selfEvolve, + TTSAutoMode: l.ttsAutoMode, ProviderType: providerTypeOf(l.provider), CredentialCLIContext: l.buildCredentialCLIContext(ctx), IsBootstrap: hadBootstrap && l.agentType != store.AgentTypePredefined, diff --git a/internal/agent/loop_types.go b/internal/agent/loop_types.go index 7a2d5743..8dd106e8 100644 --- a/internal/agent/loop_types.go +++ b/internal/agent/loop_types.go @@ -200,6 +200,9 @@ type Loop struct { // Self-evolve: predefined agents can update SOUL.md through chat selfEvolve bool + // TTS auto mode from config: "off", "always", "inbound", "tagged" + ttsAutoMode string + // Skill learning loop: when skillEvolve=true, the loop injects nudges reminding // the agent to capture reusable patterns as skills via skill_manage. skillEvolve bool @@ -388,6 +391,10 @@ type LoopConfig struct { // Self-evolve: predefined agents can update SOUL.md (style/tone) through chat SelfEvolve bool + // TTS auto mode from config: "off", "always", "inbound", "tagged" + // When "tagged", inject [[tts]] directive guidance into system prompt. + TTSAutoMode string + // Skill evolution: agent learning loop config (from other_config JSONB) SkillEvolve bool SkillNudgeInterval int // 0 = disabled, 15 = default @@ -529,6 +536,7 @@ func NewLoop(cfg LoopConfig) *Loop { promptMode: cfg.PromptMode, pinnedSkills: cfg.PinnedSkills, selfEvolve: cfg.SelfEvolve, + ttsAutoMode: cfg.TTSAutoMode, skillEvolve: cfg.SkillEvolve, skillNudgeInterval: cfg.SkillNudgeInterval, isTeamLead: cfg.IsTeamLead, diff --git a/internal/agent/resolver.go b/internal/agent/resolver.go index eec9dfd9..b50fc472 100644 --- a/internal/agent/resolver.go +++ b/internal/agent/resolver.go @@ -120,6 +120,9 @@ type ResolverDeps struct { // Global workspace root (GOCLAW_WORKSPACE) Workspace string + // TTS auto mode from config: "off", "always", "inbound", "tagged" + TTSAutoMode string + // V3 auto-inject: episodic memory injection into system prompt (nil = disabled) AutoInjector memory.AutoInjector @@ -511,6 +514,7 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc { PromptMode: PromptMode(ag.ParsePromptMode()), PinnedSkills: ag.ParsePinnedSkills(), SelfEvolve: ag.ParseSelfEvolve(), + TTSAutoMode: deps.TTSAutoMode, SkillEvolve: ag.AgentType == store.AgentTypePredefined && ag.ParseSkillEvolve(), SkillNudgeInterval: ag.ParseSkillNudgeInterval(), WorkspaceSharing: ag.ParseWorkspaceSharing(), diff --git a/internal/agent/systemprompt.go b/internal/agent/systemprompt.go index 59e5ea6f..44773973 100644 --- a/internal/agent/systemprompt.go +++ b/internal/agent/systemprompt.go @@ -135,6 +135,10 @@ type SystemPromptConfig struct { // Self-evolution: predefined agents can update SOUL.md (style/tone) SelfEvolve bool + // TTSAutoMode: "off", "always", "inbound", "tagged". When "tagged", inject + // [[tts]] directive guidance so the agent knows how to trigger voice responses. + TTSAutoMode string + // ShellDenyGroups holds effective deny group overrides for this agent. // nil = all defaults. Used to adapt system prompt instructions. ShellDenyGroups map[string]bool @@ -327,6 +331,11 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string { lines = append(lines, cfg.CredentialCLIContext, "") } + // 2.6. ## Voice Response — inject when TTS auto mode is "tagged" + if (isFull || isTask) && !cfg.IsBootstrap && cfg.TTSAutoMode == "tagged" { + lines = append(lines, buildVoiceResponseSection()...) + } + // 3. ## Safety — task/none get slim version (keeps prompt injection defense) if isTask || isNone { lines = append(lines, buildSafetySlimSection()...) diff --git a/internal/agent/systemprompt_sections.go b/internal/agent/systemprompt_sections.go index 370cbf7a..73c26b1a 100644 --- a/internal/agent/systemprompt_sections.go +++ b/internal/agent/systemprompt_sections.go @@ -729,3 +729,22 @@ func buildTeamMembersSection(members []store.TeamMemberData, teamGuidance string lines = append(lines, "") return lines } + +// buildVoiceResponseSection generates guidance for triggering auto TTS in "tagged" mode. +// When TTS auto mode is "tagged", agent responses containing [[tts]] are converted to voice. +func buildVoiceResponseSection() []string { + return []string{ + "## Voice Response", + "", + "You can respond with voice/audio by wrapping text with `[[tts]]`:", + "", + "```", + "[[tts]]This text will be spoken aloud.[[/tts]]", + "```", + "", + "**ONLY use [[tts]] when the user explicitly asks for voice/audio response.**", + "Examples: \"read this aloud\", \"respond with voice\", \"speak this\", \"tell me a story (voice)\".", + "Do NOT add [[tts]] just because you think it would be nice — text is the default.", + "", + } +} diff --git a/internal/bootstrap/templates/AGENTS.md b/internal/bootstrap/templates/AGENTS.md index 6343ca75..0c5f7ab2 100644 --- a/internal/bootstrap/templates/AGENTS.md +++ b/internal/bootstrap/templates/AGENTS.md @@ -113,4 +113,4 @@ Tips: ## Voice -If you have TTS capability, use voice for stories and "storytime" moments — more engaging than walls of text. +If you have TTS capability, only use voice when the user explicitly asks for it (e.g. "read aloud", "respond with voice", "tell me a story in voice").