b92a350023
Pieza 2 — schema (internal/config/schema.go): - DeviceMeshConfig.ExposeViaMCP *bool: pointer para distinguir "no establecido" vs "false explicito". Helper ShouldExposeViaMCP() devuelve true cuando enabled && (nil || *true). - ClaudeCodeCfg.MCPConfigPath y MCPServerName: poblados en runtime por la launcher, NUNCA por YAML. Pieza 3 — launcher wiring (devagents/mcp_bridge.go + cmd/launcher/main.go): - ApplyMCPBridge(cfg, logger): si DeviceMesh.ShouldExposeViaMCP() y provider=claude-code, resuelve binario devicemesh-mcp (junto al launcher), URL device_agent (env override > YAML), lista tools allowed (RegisterBuiltins + FilterByAllowed igual que registry_build.go), y escribe /tmp/<agent_id>-mcp-config.json (0600). - Aplica overrides a cfg.LLM.Primary.ClaudeCode: MCPConfigPath, AllowedTools (formato mcp__<server>__<tool>), DisableTools=false defensivo. - Launcher main.go llama ApplyMCPBridge inmediatamente despues de config.Load, ANTES de devagents.New (que es donde se construye el CompleteFunc del provider). Pieza 4 — claude args (shell/llm/claudecode.go): - buildClaudeArgs ahora emite "--mcp-config <path>" cuando cfg.MCPConfigPath no esta vacio. - Guard defensivo: DisableTools=true + AllowedTools no vacio ahora produce solo --allowedTools (efectivamente ignora DisableTools). El launcher ya lo previene en ApplyMCPBridge, pero esto protege a callers directos. Build limpio con goolm. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
594 lines
26 KiB
Go
594 lines
26 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"`
|
|
|
|
// DeviceMesh holds the optional device-mesh block. When nil the agent has
|
|
// no device_mesh tools; when set and Enabled the runtime constructs a
|
|
// devicemesh.Client + ToolRegistry and registers the builtin tools (filtered
|
|
// by ToolsAllowed). See issue 0144 §6.1 + .claude/rules/cpp_apps.md.
|
|
DeviceMesh *DeviceMeshConfig `yaml:"device_mesh,omitempty"`
|
|
|
|
// 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:"-"`
|
|
}
|
|
|
|
// DeviceMeshConfig is the optional device-mesh block on the agent config.
|
|
// When DeviceMesh is non-nil and Enabled is true, the launcher builds a
|
|
// devicemesh.Client + ToolRegistry, registers builtin tools filtered by
|
|
// Mode (user|sudo), optionally narrows them via ToolsAllowed, and exposes
|
|
// each tool to the LLM tool-use loop via the standard tool registry.
|
|
type DeviceMeshConfig struct {
|
|
// Enabled gates the whole block. False keeps it inert even when present.
|
|
Enabled bool `yaml:"enabled"`
|
|
|
|
// Host identifies the target device for log/audit context. Matches
|
|
// device_id from the manifest (ex "home-wsl", "aurgi-pc").
|
|
Host string `yaml:"host"`
|
|
|
|
// DeviceID is an alias for Host. Templates use device_id; keep both for
|
|
// compatibility. When both are set Host wins.
|
|
DeviceID string `yaml:"device_id,omitempty"`
|
|
|
|
// Mode controls which subset of the builtin catalog gets registered.
|
|
// "user" → non-approval tools. "sudo" → approval-gated tools (shell.eval
|
|
// promoted to requires_approval). Empty defaults to "user".
|
|
Mode string `yaml:"mode"`
|
|
|
|
// DeviceAgentURL is the http://host:port URL of the remote device_agent.
|
|
// May be empty when URLEnv is set.
|
|
DeviceAgentURL string `yaml:"device_agent_url"`
|
|
|
|
// URLEnv allows the agent_url to be supplied at runtime via env var
|
|
// (ex "AGENT_HOME_WSL_DEVICE_MESH_URL"). When non-empty the runtime reads
|
|
// the env var; if both are set, the env var wins when non-empty. This
|
|
// keeps device URLs out of the YAML/git history.
|
|
URLEnv string `yaml:"device_agent_url_env,omitempty"`
|
|
|
|
// ManifestID is metadata for log/audit context. The device_agent enforces
|
|
// the actual manifest binding. Empty allowed.
|
|
ManifestID string `yaml:"manifest_id,omitempty"`
|
|
|
|
// ToolsAllowed is a whitelist applied AFTER RegisterBuiltins. Empty means
|
|
// "keep all tools the mode-filter accepted". Names that do not match any
|
|
// registered tool are logged and ignored.
|
|
ToolsAllowed []string `yaml:"tools_allowed,omitempty"`
|
|
|
|
// TimeoutSeconds overrides the per-call HTTP timeout. 0 → DefaultTimeout
|
|
// of the devicemesh client (30s).
|
|
TimeoutSeconds int `yaml:"timeout_seconds,omitempty"`
|
|
|
|
// ClientTimeoutS is an alias for TimeoutSeconds. Templates use
|
|
// client_timeout_s; we accept both. When both set, ClientTimeoutS wins
|
|
// when non-zero.
|
|
ClientTimeoutS int `yaml:"client_timeout_s,omitempty"`
|
|
|
|
// ExposeViaMCP gates the MCP bridge (issue 0145). When the field is
|
|
// absent from YAML, the launcher defaults to "expose" (true) so an
|
|
// agent with device_mesh.enabled=true gets the bridge for free. The
|
|
// pointer shape lets us distinguish "unset" from "explicitly false";
|
|
// use ShouldExposeViaMCP() to read it.
|
|
ExposeViaMCP *bool `yaml:"expose_via_mcp,omitempty"`
|
|
}
|
|
|
|
// ShouldExposeViaMCP reports whether the launcher must build the MCP bridge
|
|
// for this device-mesh block. Returns false when the block is nil or not
|
|
// enabled; otherwise returns true unless ExposeViaMCP is explicitly false.
|
|
// Pure function — used by both the launcher and tests.
|
|
func (d *DeviceMeshConfig) ShouldExposeViaMCP() bool {
|
|
if d == nil || !d.Enabled {
|
|
return false
|
|
}
|
|
if d.ExposeViaMCP != nil {
|
|
return *d.ExposeViaMCP
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ResolvedHost returns Host if non-empty, otherwise DeviceID. Used by the
|
|
// runtime to log audit context without caring which key the YAML used.
|
|
func (d *DeviceMeshConfig) ResolvedHost() string {
|
|
if d == nil {
|
|
return ""
|
|
}
|
|
if d.Host != "" {
|
|
return d.Host
|
|
}
|
|
return d.DeviceID
|
|
}
|
|
|
|
// ResolvedTimeoutSeconds returns the first non-zero of TimeoutSeconds and
|
|
// ClientTimeoutS. 0 means "use devicemesh defaults".
|
|
func (d *DeviceMeshConfig) ResolvedTimeoutSeconds() int {
|
|
if d == nil {
|
|
return 0
|
|
}
|
|
if d.TimeoutSeconds > 0 {
|
|
return d.TimeoutSeconds
|
|
}
|
|
return d.ClientTimeoutS
|
|
}
|
|
|
|
// ── 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
|
|
|
|
// MCPConfigPath points to a JSON file consumed by `claude -p --mcp-config`.
|
|
// Set at runtime by the launcher (issue 0145) when the agent has
|
|
// device_mesh.enabled=true and ExposeViaMCP. Empty means claude runs
|
|
// without external MCP servers. NEVER set in YAML — overrides the
|
|
// runtime-generated bridge.
|
|
MCPConfigPath string `yaml:"mcp_config_path,omitempty"`
|
|
|
|
// MCPServerName is the key inside the mcp-config JSON's "mcpServers"
|
|
// map. claude prefixes tool names exposed to the model as
|
|
// `mcp__<MCPServerName>__<tool>`. Defaults to "devicemesh" when empty.
|
|
MCPServerName string `yaml:"mcp_server_name,omitempty"`
|
|
}
|
|
|
|
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"`
|
|
ExchangeRate ExchangeRateToolCfg `yaml:"exchange_rate"`
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
type ExchangeRateToolCfg struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
APIKey string `yaml:"api_key"` // ExchangeRate-API key (get from https://www.exchangerate-api.com)
|
|
APIKeyEnv string `yaml:"api_key_env"` // env var name for API key (e.g., "EXCHANGE_RATE_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
|
|
}
|