2026-02-10 02:57:41 +00:00
|
|
|
// Package agentci provides configuration, security, and orchestration for AgentCI dispatch targets.
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
package agentci
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"github.com/host-uk/core/pkg/config"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AgentConfig represents a single agent machine in the config file.
|
|
|
|
|
type AgentConfig struct {
|
2026-02-10 02:57:41 +00:00
|
|
|
Host string `yaml:"host" mapstructure:"host"`
|
|
|
|
|
QueueDir string `yaml:"queue_dir" mapstructure:"queue_dir"`
|
|
|
|
|
ForgejoUser string `yaml:"forgejo_user" mapstructure:"forgejo_user"`
|
|
|
|
|
Model string `yaml:"model" mapstructure:"model"` // primary AI model
|
|
|
|
|
Runner string `yaml:"runner" mapstructure:"runner"` // runner binary: claude, codex, gemini
|
|
|
|
|
VerifyModel string `yaml:"verify_model" mapstructure:"verify_model"` // secondary model for dual-run
|
|
|
|
|
SecurityLevel string `yaml:"security_level" mapstructure:"security_level"` // low, high
|
|
|
|
|
Roles []string `yaml:"roles" mapstructure:"roles"`
|
|
|
|
|
DualRun bool `yaml:"dual_run" mapstructure:"dual_run"`
|
|
|
|
|
Active bool `yaml:"active" mapstructure:"active"`
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-10 02:57:41 +00:00
|
|
|
// ClothoConfig controls the orchestration strategy.
|
|
|
|
|
type ClothoConfig struct {
|
|
|
|
|
Strategy string `yaml:"strategy" mapstructure:"strategy"` // direct, clotho-verified
|
|
|
|
|
ValidationThreshold float64 `yaml:"validation_threshold" mapstructure:"validation_threshold"` // divergence limit (0.0-1.0)
|
|
|
|
|
SigningKeyPath string `yaml:"signing_key_path" mapstructure:"signing_key_path"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoadAgents reads agent targets from config and returns a map of AgentConfig.
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
// Returns an empty map (not an error) if no agents are configured.
|
2026-02-10 02:57:41 +00:00
|
|
|
func LoadAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
var agents map[string]AgentConfig
|
|
|
|
|
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
2026-02-10 02:57:41 +00:00
|
|
|
return map[string]AgentConfig{}, nil
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-10 02:57:41 +00:00
|
|
|
// Validate and apply defaults.
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
for name, ac := range agents {
|
|
|
|
|
if !ac.Active {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if ac.Host == "" {
|
2026-02-10 02:57:41 +00:00
|
|
|
return nil, fmt.Errorf("agent %q: host is required", name)
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
2026-02-10 02:57:41 +00:00
|
|
|
if ac.QueueDir == "" {
|
|
|
|
|
ac.QueueDir = "/home/claude/ai-work/queue"
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
2026-02-10 02:57:41 +00:00
|
|
|
if ac.Model == "" {
|
|
|
|
|
ac.Model = "sonnet"
|
2026-02-09 10:58:46 +00:00
|
|
|
}
|
2026-02-10 02:57:41 +00:00
|
|
|
if ac.Runner == "" {
|
|
|
|
|
ac.Runner = "claude"
|
2026-02-09 10:58:46 +00:00
|
|
|
}
|
2026-02-10 02:57:41 +00:00
|
|
|
agents[name] = ac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return agents, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoadActiveAgents returns only active agents.
|
|
|
|
|
func LoadActiveAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
|
|
|
|
all, err := LoadAgents(cfg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
active := make(map[string]AgentConfig)
|
|
|
|
|
for name, ac := range all {
|
|
|
|
|
if ac.Active {
|
|
|
|
|
active[name] = ac
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-10 02:57:41 +00:00
|
|
|
return active, nil
|
|
|
|
|
}
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
|
2026-02-10 02:57:41 +00:00
|
|
|
// LoadClothoConfig loads the Clotho orchestrator settings.
|
|
|
|
|
// Returns sensible defaults if no config is present.
|
|
|
|
|
func LoadClothoConfig(cfg *config.Config) (ClothoConfig, error) {
|
|
|
|
|
var cc ClothoConfig
|
|
|
|
|
if err := cfg.Get("agentci.clotho", &cc); err != nil {
|
|
|
|
|
return ClothoConfig{
|
|
|
|
|
Strategy: "direct",
|
|
|
|
|
ValidationThreshold: 0.85,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
if cc.Strategy == "" {
|
|
|
|
|
cc.Strategy = "direct"
|
|
|
|
|
}
|
|
|
|
|
if cc.ValidationThreshold == 0 {
|
|
|
|
|
cc.ValidationThreshold = 0.85
|
|
|
|
|
}
|
|
|
|
|
return cc, nil
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveAgent writes an agent config entry to the config file.
|
|
|
|
|
func SaveAgent(cfg *config.Config, name string, ac AgentConfig) error {
|
|
|
|
|
key := fmt.Sprintf("agentci.agents.%s", name)
|
2026-02-09 10:58:46 +00:00
|
|
|
data := map[string]any{
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
"host": ac.Host,
|
|
|
|
|
"queue_dir": ac.QueueDir,
|
|
|
|
|
"forgejo_user": ac.ForgejoUser,
|
|
|
|
|
"active": ac.Active,
|
2026-02-10 02:57:41 +00:00
|
|
|
"dual_run": ac.DualRun,
|
2026-02-09 10:58:46 +00:00
|
|
|
}
|
|
|
|
|
if ac.Model != "" {
|
|
|
|
|
data["model"] = ac.Model
|
|
|
|
|
}
|
|
|
|
|
if ac.Runner != "" {
|
|
|
|
|
data["runner"] = ac.Runner
|
|
|
|
|
}
|
2026-02-10 02:57:41 +00:00
|
|
|
if ac.VerifyModel != "" {
|
|
|
|
|
data["verify_model"] = ac.VerifyModel
|
|
|
|
|
}
|
|
|
|
|
if ac.SecurityLevel != "" {
|
|
|
|
|
data["security_level"] = ac.SecurityLevel
|
|
|
|
|
}
|
|
|
|
|
if len(ac.Roles) > 0 {
|
|
|
|
|
data["roles"] = ac.Roles
|
|
|
|
|
}
|
2026-02-09 10:58:46 +00:00
|
|
|
return cfg.Set(key, data)
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveAgent removes an agent from the config file.
|
|
|
|
|
func RemoveAgent(cfg *config.Config, name string) error {
|
|
|
|
|
var agents map[string]AgentConfig
|
|
|
|
|
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
2026-02-10 02:57:41 +00:00
|
|
|
return fmt.Errorf("no agents configured")
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
if _, ok := agents[name]; !ok {
|
2026-02-10 02:57:41 +00:00
|
|
|
return fmt.Errorf("agent %q not found", name)
|
feat(agentci): package dispatch system for multi-agent deployment
Config-driven agent targets replace hardcoded map so new agents
can be added via CLI instead of recompiling. Includes setup script
for bootstrapping agent machines and CLI commands for management.
- Add pkg/agentci with config types and CRUD (LoadAgents, SaveAgent, etc.)
- Add CLI: core ai agent {add,list,status,logs,setup,remove}
- Add scripts/agent-setup.sh (SSH bootstrap: dirs, cron, prereq check)
- Headless loads agents from ~/.core/config.yaml
- Dispatch ticket includes forgejo_user for dynamic clone URLs
- agent-runner.sh reads username from ticket JSON, not hardcoded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:36:23 +00:00
|
|
|
}
|
|
|
|
|
delete(agents, name)
|
|
|
|
|
return cfg.Set("agentci.agents", agents)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListAgents returns all configured agents (active and inactive).
|
|
|
|
|
func ListAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
|
|
|
|
var agents map[string]AgentConfig
|
|
|
|
|
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
|
|
|
|
return map[string]AgentConfig{}, nil
|
|
|
|
|
}
|
|
|
|
|
return agents, nil
|
|
|
|
|
}
|