Files
agents_and_robots/internal/config/schema.go
T
egutierrez 1b499c9b67 fix: resolver system_prompt_file relativo al directorio del config
Antes, el runtime construia la ruta del system prompt como
agents/<agent-id>/<file>, lo cual fallaba para agentes en
agents/_specials/ (como Father Bot). Ahora:

1. config.Load() guarda el directorio del config en ConfigDir
2. llm.go usa ConfigDir para resolver rutas relativas

Esto corrige que Father Bot operara sin su system prompt completo
(369 lineas de instrucciones, pipeline, seguridad) usando solo la
description de una linea como fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:21:23 +00:00

472 lines
21 KiB
Go

// Package config provides the configuration schema and loader for agents.
package config
import "time"
// AgentConfig is the root configuration for a single agent.
type AgentConfig struct {
Agent AgentMeta `yaml:"agent"`
Personality PersonalityCfg `yaml:"personality"`
LLM LLMCfg `yaml:"llm"`
Tools ToolsCfg `yaml:"tools"`
Matrix MatrixCfg `yaml:"matrix"`
SSH SSHCfg `yaml:"ssh"`
Security SecurityCfg `yaml:"security"`
Schedules []ScheduleCfg `yaml:"schedules"`
Storage StorageCfg `yaml:"storage"`
Memory MemoryCfg `yaml:"memory"`
Skills SkillsCfg `yaml:"skills"`
// ConfigDir is the directory containing the config file. Set by the loader
// at load time, not from YAML. Used to resolve relative paths like
// system_prompt_file correctly regardless of where the agent lives.
ConfigDir string `yaml:"-"`
}
// ── Identity ──────────────────────────────────────────────────────────────
type AgentMeta struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Version string `yaml:"version"`
Type string `yaml:"type"` // "agent" (default) or "robot" (command-only, no LLM)
Enabled bool `yaml:"enabled"`
Template bool `yaml:"template"` // if true, launcher will skip this agent
Description string `yaml:"description"`
Tags []string `yaml:"tags"`
}
// ── Personality ───────────────────────────────────────────────────────────
type PersonalityCfg struct {
// --- campos existentes (sin cambios) ---
Tone string `yaml:"tone"`
Verbosity string `yaml:"verbosity"`
Language string `yaml:"language"`
LanguagesSupported []string `yaml:"languages_supported"`
EmojiStyle string `yaml:"emoji_style"`
Prefix string `yaml:"prefix"`
ErrorStyle string `yaml:"error_style"`
Templates TemplatesCfg `yaml:"templates"`
Behavior BehaviorCfg `yaml:"behavior"`
// --- NUEVOS campos ---
// Identidad narrativa
Role string `yaml:"role"` // rol principal: "asistente general", "devops engineer", "analista de datos"
Backstory string `yaml:"backstory"` // breve historia/contexto del personaje (1-3 frases)
Expertise []string `yaml:"expertise"` // areas de experiencia: ["linux", "docker", "monitoring"]
Limitations []string `yaml:"limitations"` // que NO sabe o no debe intentar
// Estilo de comunicacion
Communication CommunicationCfg `yaml:"communication"`
// Directivas de comportamiento en texto libre
CustomDirectives []string `yaml:"custom_directives"` // instrucciones adicionales para el system prompt
}
type TemplatesCfg struct {
Greeting string `yaml:"greeting"`
UnknownCommand string `yaml:"unknown_command"`
PermissionDenied string `yaml:"permission_denied"`
Error string `yaml:"error"`
Success string `yaml:"success"`
Busy string `yaml:"busy"`
}
type BehaviorCfg struct {
Proactive bool `yaml:"proactive"`
AskConfirmation bool `yaml:"ask_confirmation"`
ShowReasoning bool `yaml:"show_reasoning"`
ThreadReplies bool `yaml:"thread_replies"`
TypingIndicator bool `yaml:"typing_indicator"`
AcknowledgeReceipt bool `yaml:"acknowledge_receipt"`
}
// CommunicationCfg define como se expresa el agente mas alla del tone basico.
type CommunicationCfg struct {
Formality string `yaml:"formality"` // formal | semiformal | casual | coloquial
Humor string `yaml:"humor"` // none | subtle | moderate | frequent
Personality string `yaml:"personality"` // analytical | creative | pragmatic | empathetic | assertive
ResponseStyle string `yaml:"response_style"` // structured | conversational | bullet_points | narrative
Quirks []string `yaml:"quirks"` // rasgos unicos: ["usa analogias de cocina", "cita a Linus Torvalds"]
AvoidTopics []string `yaml:"avoid_topics"` // temas que evita o redirige
Catchphrases []string `yaml:"catchphrases"` // frases tipicas que usa ocasionalmente
}
// ── LLM ───────────────────────────────────────────────────────────────────
type LLMCfg struct {
Primary LLMProviderCfg `yaml:"primary"`
Fallback LLMProviderCfg `yaml:"fallback"`
Reasoning LLMReasoningCfg `yaml:"reasoning"`
ToolUse LLMToolUseCfg `yaml:"tool_use"`
RateLimit LLMRateLimitCfg `yaml:"rate_limit"`
}
type LLMProviderCfg struct {
Provider string `yaml:"provider"`
Model string `yaml:"model"`
APIKeyEnv string `yaml:"api_key_env"`
BaseURL string `yaml:"base_url"`
MaxTokens int `yaml:"max_tokens"`
Temperature float64 `yaml:"temperature"`
// ClaudeCode holds configuration for the claude-code provider (claude -p).
ClaudeCode ClaudeCodeCfg `yaml:"claude_code"`
}
// ClaudeCodeCfg configures the claude -p subprocess provider.
type ClaudeCodeCfg struct {
Binary string `yaml:"binary"` // path to claude binary (default: "claude")
Timeout time.Duration `yaml:"timeout"` // subprocess timeout (default: 5m)
DisableTools bool `yaml:"disable_tools"` // pass --tools "" to disable all internal tools
AllowedTools []string `yaml:"allowed_tools"` // tools claude -p can use internally (e.g. Bash, Read, Edit)
DisallowedTools []string `yaml:"disallowed_tools"` // tools to block
WorkingDir string `yaml:"working_dir"` // working directory for claude -p
PermissionMode string `yaml:"permission_mode"` // default, acceptEdits, bypassPermissions, plan
Model string `yaml:"model"` // inner model: sonnet, opus, haiku, or full name
FallbackModel string `yaml:"fallback_model"` // fallback model if primary is overloaded
SessionID string `yaml:"session_id"` // fixed session ID for continuity
AddDirs []string `yaml:"add_dirs"` // additional directories accessible
Streaming bool `yaml:"streaming"` // use --output-format stream-json for realtime progress
ShowToolProgress bool `yaml:"show_tool_progress"` // edit Matrix message to show tool usage progress
}
type LLMReasoningCfg struct {
SystemPromptFile string `yaml:"system_prompt_file"`
ContextWindow int `yaml:"context_window"`
MemoryMessages int `yaml:"memory_messages"`
}
type LLMToolUseCfg struct {
Enabled bool `yaml:"enabled"`
MaxIterations int `yaml:"max_iterations"`
ParallelCalls bool `yaml:"parallel_calls"`
}
type LLMRateLimitCfg struct {
RequestsPerMinute int `yaml:"requests_per_minute"`
TokensPerMinute int `yaml:"tokens_per_minute"`
ConcurrentRequests int `yaml:"concurrent_requests"`
}
// ── Tools ─────────────────────────────────────────────────────────────────
type ToolsCfg struct {
SSH SSHToolCfg `yaml:"ssh"`
HTTP HTTPToolCfg `yaml:"http"`
Scripts ScriptsCfg `yaml:"scripts"`
FileOps FileOpsCfg `yaml:"file_ops"`
Matrix MatrixToolCfg `yaml:"matrix_send"`
MCP MCPToolCfg `yaml:"mcp"`
Memory MemoryToolCfg `yaml:"memory"`
Knowledge KnowledgeToolCfg `yaml:"knowledge"`
SharedKnowledge SharedKnowledgeToolCfg `yaml:"shared_knowledge"`
Skills SkillsToolCfg `yaml:"skills"`
IMDb IMDbToolCfg `yaml:"imdb"`
}
type MatrixToolCfg struct {
AllowedRooms []string `yaml:"allowed_rooms"` // if non-empty, only these room IDs can be targeted
}
type KnowledgeToolCfg struct {
Enabled bool `yaml:"enabled"`
Dir string `yaml:"dir"` // default: "./knowledge" (relative to agent dir)
}
type SharedKnowledgeToolCfg struct {
Enabled bool `yaml:"enabled"` // default false
Dir string `yaml:"dir"` // default "knowledges" (relative to project root)
DBPath string `yaml:"db_path"` // default "knowledges/data/knowledge.db"
}
type SSHToolCfg struct {
Enabled bool `yaml:"enabled"`
AllowedTargets []string `yaml:"allowed_targets"`
AllowedCommands []string `yaml:"allowed_commands"` // allowlist: if non-empty, only these command prefixes are permitted
ForbiddenCommands []string `yaml:"forbidden_commands"`
Timeout time.Duration `yaml:"timeout"`
MaxConcurrent int `yaml:"max_concurrent"`
RequireConfirmation []string `yaml:"require_confirmation"`
}
type HTTPToolCfg struct {
Enabled bool `yaml:"enabled"`
AllowedDomains []string `yaml:"allowed_domains"`
Timeout time.Duration `yaml:"timeout"`
MaxRetries int `yaml:"max_retries"`
}
type ScriptsCfg struct {
Enabled bool `yaml:"enabled"`
ScriptsDir string `yaml:"scripts_dir"`
Allowed []string `yaml:"allowed"`
Timeout time.Duration `yaml:"timeout"`
Sandbox bool `yaml:"sandbox"`
}
type FileOpsCfg struct {
Enabled bool `yaml:"enabled"`
AllowedPaths []string `yaml:"allowed_paths"`
ReadOnly bool `yaml:"read_only"`
}
type MCPToolCfg struct {
Enabled bool `yaml:"enabled"`
Servers []MCPServerCfg `yaml:"servers"`
Expose MCPExposeCfg `yaml:"expose"`
}
type MCPServerCfg struct {
Name string `yaml:"name"` // nombre logico del servidor
Transport string `yaml:"transport"` // "stdio" | "sse" (default: auto-detect)
Command string `yaml:"command"` // stdio: comando a ejecutar
Args []string `yaml:"args"` // stdio: argumentos del comando
Env map[string]string `yaml:"env"` // stdio: variables de entorno extra
URL string `yaml:"url"` // sse: URL del servidor
Headers map[string]string `yaml:"headers"` // sse: headers HTTP extra (auth, etc.)
Tools []string `yaml:"tools"` // filtro: solo exponer estas tools (vacio = todas)
Prefix string `yaml:"prefix"` // prefijo para nombres de tools (evitar colisiones)
Timeout time.Duration `yaml:"timeout"` // timeout por llamada (default: 30s)
}
type MCPExposeCfg struct {
Port int `yaml:"port"`
Tools []string `yaml:"tools"`
}
// ── Matrix ────────────────────────────────────────────────────────────────
type MatrixCfg struct {
Homeserver string `yaml:"homeserver"`
UserID string `yaml:"user_id"`
AccessTokenEnv string `yaml:"access_token_env"`
DeviceID string `yaml:"device_id"`
Encryption EncryptionCfg `yaml:"encryption"`
Rooms RoomsCfg `yaml:"rooms"`
Filters FiltersCfg `yaml:"filters"`
Threads ThreadsCfg `yaml:"threads"`
}
// ThreadsCfg controls Matrix thread support (m.thread).
type ThreadsCfg struct {
Enabled bool `yaml:"enabled"` // respond in threads when message is in a thread (default true)
AutoThread bool `yaml:"auto_thread"` // auto-create a thread for each new conversation (default false)
}
type EncryptionCfg struct {
Enabled bool `yaml:"enabled"`
StorePath string `yaml:"store_path"`
PickleKeyEnv string `yaml:"pickle_key_env"` // env var with hex-encoded 32-byte key
TrustMode string `yaml:"trust_mode"` // tofu | cross-signing | manual
RecoveryKeyEnv string `yaml:"recovery_key_env"` // env var with base58 SSSS recovery key for cross-signing
}
type RoomsCfg struct {
Listen []string `yaml:"listen"`
Respond []string `yaml:"respond"`
Admin []string `yaml:"admin"`
}
type FiltersCfg struct {
// CommandPrefix is the prefix required for commands (e.g. "!" means "!help").
// Set to "" (empty) to allow commands without prefix — useful for robots where
// every message is a potential command. When empty, "!help" still works for
// backward compatibility (the leading "!" is stripped automatically).
CommandPrefix string `yaml:"command_prefix"`
MentionRespond bool `yaml:"mention_respond"`
DMRespond bool `yaml:"dm_respond"`
IgnoreBots bool `yaml:"ignore_bots"`
IgnoreUsers []string `yaml:"ignore_users"`
// Deprecated: use security/ centralized groups instead. Kept for backward compatibility.
AllowedUsers []string `yaml:"allowed_users"`
UnauthorizedResponse string `yaml:"unauthorized_response"` // silent (default) | explicit
MinPowerLevel int `yaml:"min_power_level"`
}
// ── SSH Inventory ─────────────────────────────────────────────────────────
type SSHCfg struct {
Defaults SSHDefaultsCfg `yaml:"defaults"`
Targets map[string]SSHTargetCfg `yaml:"targets"`
}
type SSHDefaultsCfg struct {
User string `yaml:"user"`
Port int `yaml:"port"`
KeyFileEnv string `yaml:"key_file_env"`
KnownHosts string `yaml:"known_hosts"`
KeepaliveInterval time.Duration `yaml:"keepalive_interval"`
Timeout time.Duration `yaml:"timeout"`
}
type SSHTargetCfg struct {
Hosts []string `yaml:"hosts"`
User string `yaml:"user"`
Port int `yaml:"port"`
JumpHost string `yaml:"jump_host"`
KeyFileEnv string `yaml:"key_file_env"`
}
// ── Security ──────────────────────────────────────────────────────────────
type SecurityCfg struct {
// Deprecated: use security/ centralized groups instead (see security/user-groups.yaml, permissions.yaml).
// Kept for backward compatibility; will be removed in a future issue.
Roles map[string]RoleCfg `yaml:"roles"`
Audit AuditCfg `yaml:"audit"`
Secrets SecretsCfg `yaml:"secrets"`
Sanitize SanitizeCfg `yaml:"sanitize"`
ToolRateLimit ToolRateLimitCfg `yaml:"tool_rate_limit"`
}
// ToolRateLimitCfg controls per-room rate limiting of tool executions.
type ToolRateLimitCfg struct {
Enabled bool `yaml:"enabled"` // enable tool rate limiting (default false)
MaxCallsPerMin int `yaml:"max_calls_per_min"` // max tool calls per room per minute (default 10)
CleanupIntervalS int `yaml:"cleanup_interval_s"` // seconds between stale entry cleanup (default 60)
}
// SanitizeCfg controls prompt injection detection on incoming messages.
type SanitizeCfg struct {
Enabled bool `yaml:"enabled"` // enable sanitization (default false)
Mode string `yaml:"mode"` // warn | strip | reject (default warn)
MinSeverity string `yaml:"min_severity"` // low | medium | high (default medium)
DisabledPatterns []string `yaml:"disabled_patterns"` // pattern names to skip
}
type RoleCfg struct {
Users []string `yaml:"users"`
Actions []string `yaml:"actions"`
}
type AuditCfg struct {
Enabled bool `yaml:"enabled"`
LogFile string `yaml:"log_file"`
LogToRoom string `yaml:"log_to_room"`
Include []string `yaml:"include"`
}
type SecretsCfg struct {
Provider string `yaml:"provider"` // env | vault | sops
}
// ── Scheduling ────────────────────────────────────────────────────────────
type ScheduleCfg struct {
Name string `yaml:"name"`
Cron string `yaml:"cron"`
Action ScheduledAction `yaml:"action"`
OnFailure FailureAction `yaml:"on_failure"`
OutputRoom string `yaml:"output_room"`
}
type ScheduledAction struct {
Kind string `yaml:"kind"`
Target string `yaml:"target"`
Command string `yaml:"command"`
Script string `yaml:"script"`
// Phase 1: send_message and llm_prompt fields
Message string `yaml:"message"` // inline text for send_message
Template string `yaml:"template"` // path to .md file for send_message
Prompt string `yaml:"prompt"` // inline prompt text for llm_prompt
}
type FailureAction struct {
NotifyRoom string `yaml:"notify_room"`
EscalateTo string `yaml:"escalate_to"`
}
// ── Storage ───────────────────────────────────────────────────────────────
type StorageCfg struct {
BasePath string `yaml:"base_path"` // root for all data; default $AGENTS_DATA_DIR/<id> or agents/<id>/data
State StateStorageCfg `yaml:"state"`
Cache CacheStorageCfg `yaml:"cache"`
History HistoryStorageCfg `yaml:"history"`
}
type StateStorageCfg struct {
Backend string `yaml:"backend"` // sqlite | redis | file
Path string `yaml:"path"`
}
type CacheStorageCfg struct {
Enabled bool `yaml:"enabled"`
Backend string `yaml:"backend"` // memory | redis
TTL time.Duration `yaml:"ttl"`
MaxEntries int `yaml:"max_entries"`
}
type HistoryStorageCfg struct {
Backend string `yaml:"backend"`
Path string `yaml:"path"`
Retention time.Duration `yaml:"retention"`
}
// ── Memory ────────────────────────────────────────────────────────────────
type MemoryCfg struct {
Enabled bool `yaml:"enabled"`
WindowSize int `yaml:"window_size"` // sliding window size per room (default 20)
DBPath string `yaml:"db_path"` // SQLite path (default agents/<id>/data/memory.db)
}
type MemoryToolCfg struct {
Enabled bool `yaml:"enabled"`
}
// ── Skills ────────────────────────────────────────────────────────────────
type SkillsCfg struct {
Enabled bool `yaml:"enabled"` // enable skills system (default false)
SkillsPath string `yaml:"path"` // path to skills directory (default: "skills/")
Categories []string `yaml:"categories"` // filter: only load skills from these categories (empty = all)
Timeout time.Duration `yaml:"timeout"` // timeout for script execution (default: 60s)
}
type SkillsToolCfg struct {
AllowedInterpreters []string `yaml:"allowed_interpreters"` // allowlist for skill script execution (default: ["bash", "sh"])
}
type IMDbToolCfg struct {
Enabled bool `yaml:"enabled"`
APIKey string `yaml:"api_key"` // OMDb API key (get from http://www.omdbapi.com/)
APIKeyEnv string `yaml:"api_key_env"` // env var name for API key (e.g., "OMDB_API_KEY")
Timeout time.Duration `yaml:"timeout"` // timeout for API requests (default: 10s)
}
// ── Special Agents ────────────────────────────────────────────────────────
// SpecialConfig is the root configuration for a special agent (no Matrix identity).
type SpecialConfig struct {
Special SpecialMeta `yaml:"special"`
LLM LLMCfg `yaml:"llm"`
Orchestration OrchestrationCfg `yaml:"orchestration"`
}
// SpecialMeta identifies a special agent.
type SpecialMeta struct {
ID string `yaml:"id"`
Type string `yaml:"type"` // "orchestrator", "scheduler", etc.
Enabled bool `yaml:"enabled"`
Description string `yaml:"description"`
}
// OrchestrationCfg configures the multi-bot orchestrator.
type OrchestrationCfg struct {
MaxIterations int `yaml:"max_iterations"`
QualityThreshold float64 `yaml:"quality_threshold"`
DelegationTimeout time.Duration `yaml:"delegation_timeout"`
RepetitionThreshold float64 `yaml:"repetition_threshold"` // 0-1: similarity ratio to detect circular conversations
Rooms []OrchestratedRoomCfg `yaml:"rooms"`
}
// OrchestratedRoomCfg defines a room managed by the orchestrator.
type OrchestratedRoomCfg struct {
RoomID string `yaml:"room_id"`
Participants []string `yaml:"participants"` // bot IDs that participate in this room
}