From 2f3f30c86be977178366a134a665f5cde1bda8ba Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 9 Apr 2026 22:05:13 +0000 Subject: [PATCH] feat: launcher descubre agentes en _specials/ con validacion de tipo El launcher ahora escanea agents/_specials/*/config.yaml ademas de agents/*/config.yaml para descubrir agentes del sistema con identidad Matrix (ej: father-bot). Los SpecialConfig ya cargados (orchestrator) se detectan y saltan via isSpecialConfig() para evitar errores de validacion. Tambien actualiza dev-scripts/_common.sh para que config_path_for() y list_agents_raw() incluyan _specials/ en la busqueda. Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/launcher/main.go | 33 +++++++++++++++++++++++++++++++++ dev-scripts/_common.sh | 6 ++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go index d13a9d1..d07b1f3 100644 --- a/cmd/launcher/main.go +++ b/cmd/launcher/main.go @@ -34,6 +34,7 @@ import ( _ "github.com/enmanuel/agents/agents/asistente-2" _ "github.com/enmanuel/agents/agents/meteorologo" _ "github.com/enmanuel/agents/agents/test-personality" + _ "github.com/enmanuel/agents/agents/_specials/father-bot" testbot "github.com/enmanuel/agents/agents/test-bot" ) @@ -51,6 +52,10 @@ func main() { if len(configPaths) == 0 { matches, _ := filepath.Glob("agents/*/config.yaml") configPaths = matches + // Also discover agent-type specials (e.g. father-bot). + // SpecialConfig middleware (orchestrator) is handled separately. + specials, _ := filepath.Glob("agents/_specials/*/config.yaml") + configPaths = append(configPaths, specials...) } return nil }, @@ -143,9 +148,22 @@ func main() { }() // ── Start normal agents ── + // Build a set of special IDs already loaded (e.g. orchestrator) + // so the discovery loop skips them instead of failing on validation. + loadedSpecials := make(map[string]bool) + if orch != nil { + loadedSpecials[orch.cfg.Special.ID] = true + } + var scannerOnce scanOnce for _, path := range configPaths { path := path + + // Skip configs that belong to already-loaded specials. + if isSpecialConfig(path, loadedSpecials) { + continue + } + cfg, err := config.Load(path) if err != nil { logger.Error("failed to load config", "path", path, "err", err) @@ -337,3 +355,18 @@ func parseLogLevel(level string) slog.Level { func newLogger(level string) *slog.Logger { return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: parseLogLevel(level)})) } + +// isSpecialConfig checks whether a config path belongs to a special agent +// that was already loaded (e.g. orchestrator). It reads the YAML to detect +// a "special:" top-level key. This avoids config.Load() failing with +// validation errors for SpecialConfig files. +func isSpecialConfig(path string, loadedSpecials map[string]bool) bool { + if len(loadedSpecials) == 0 { + return false + } + cfg, err := config.LoadSpecial(path) + if err != nil { + return false // not a valid special config → let Load() handle it + } + return loadedSpecials[cfg.Special.ID] +} diff --git a/dev-scripts/_common.sh b/dev-scripts/_common.sh index 00ce6da..1174b22 100755 --- a/dev-scripts/_common.sh +++ b/dev-scripts/_common.sh @@ -51,9 +51,10 @@ read_pid() { } # Map agent ID to its config path by scanning agent directories. +# Also scans agents/_specials/ for privileged system agents (e.g. father-bot). config_path_for() { local target_id="$1" - for cfg in agents/*/config.yaml; do + for cfg in agents/*/config.yaml agents/_specials/*/config.yaml; do [[ -f "$cfg" ]] || continue local id id=$(grep -m1 '^ id:' "$cfg" | awk '{print $2}') @@ -150,8 +151,9 @@ is_launcher_running() { # ── Agent discovery ──────────────────────────────────────────────────────── # Prints: id|version|enabled|description (one line per agent) +# Also scans agents/_specials/ for privileged system agents. list_agents_raw() { - for cfg in agents/*/config.yaml; do + for cfg in agents/*/config.yaml agents/_specials/*/config.yaml; do [[ -f "$cfg" ]] || continue local id version enabled desc id=$(grep -m1 '^ id:' "$cfg" | awk '{print $2}')