feat: Add rig-level custom agent support (#12)
* feat: Add rig-level custom agent support Implement rig-level custom agent configuration support to enable per-rig agent definitions in <rig>/settings/config.json, following the same pattern as town-level agents in settings/config.json. Changes: - Added RigSettings.Agents field to internal/config/types.go - Added DefaultRigAgentRegistryPath() and LoadRigAgentRegistry() functions to internal/config/agents.go - Updated ResolveAgentConfigWithOverride() to accept and pass rigSettings parameter - Updated GetRuntimeCommandWithAgentOverride() to use rigSettings when available - Updated GetRuntimeCommandWithPromptAndAgentOverride() to use rigSettings - Updated all Build*WithOverride functions to pass rigSettings This fixes the issue where rig-level agent settings were loaded but ignored by lookupAgentConfig, enabling per-rig custom agents for polecats and crew members. * test: Add rig-level custom agent tests Added comprehensive unit tests for rig agent registry functions: - TestDefaultRigAgentRegistryPath: verifies path construction - TestLoadRigAgentRegistry: verifies file loading and JSON parsing - TestLookupAgentConfigWithRigSettings: verifies agent lookup priority (rig > town > builtin) Added placeholder integration test for future CI/CD setup. * initial commit * fix: resolve compilation errors in rig-level custom agent support - Add missing RigAgentRegistryPath function (alias for DefaultRigAgentRegistryPath) - Restore ResolveAgentConfigWithOverride function that was incorrectly removed - Fix ResolveAgentConfig to return single value (not triple) - Add initRegistryLocked() call to LoadRigAgentRegistry to prevent nil panic - Fix DefaultRigAgentRegistryPath to use rigPath directly (not parent dir) - Fix test file syntax errors (remove EOF artifacts) - Fix test parameter order for lookupAgentConfig calls - Fix test expectations to match correct custom agent override behavior * test: implement rig-level custom agent integration test - Add stub agent script that simulates AI agent with Q&A capability - Test ResolveAgentConfig correctly picks up rig-level agents - Test BuildPolecatStartupCommand includes custom agent command - Test ResolveAgentConfigWithOverride respects rig agents - Test rig agents override town agents with same name - Add tmux integration test that spawns session and verifies output - Stub agent echoes 'STUB_AGENT_STARTED' and handles ping/pong Q&A - All tests pass including real tmux session verification * docs: add OpenCode custom agent example to reference - Show settings/agents.json format for advanced configs - Include OpenCode example with session resume flags - Document OPENCODE_PERMISSION env var for autonomous mode * fix: improve rig-level agent support with docs and test fixes - Add rig-level agent documentation to reference.md - Document agent resolution order (rig → town → built-in) - Deduplicate LoadAgentRegistry/LoadRigAgentRegistry into shared helper - Fix test isolation in TestLoadRigAgentRegistry - Fix nil pointer dereference in test assertions (use t.Fatal not t.Error)
This commit is contained in:
committed by
Steve Yegge
parent
2de2d6b7e4
commit
00a59dec44
@@ -215,16 +215,11 @@ func ensureRegistry() {
|
||||
initRegistryLocked()
|
||||
}
|
||||
|
||||
// LoadAgentRegistry loads agent definitions from a JSON file and merges with built-ins.
|
||||
// User-defined agents override built-in presets with the same name.
|
||||
// This function caches loaded paths to avoid redundant file reads.
|
||||
func LoadAgentRegistry(path string) error {
|
||||
registryMu.Lock()
|
||||
defer registryMu.Unlock()
|
||||
|
||||
// loadAgentRegistryFromPath loads agent definitions from a JSON file and merges with built-ins.
|
||||
// Caller must hold registryMu write lock.
|
||||
func loadAgentRegistryFromPathLocked(path string) error {
|
||||
initRegistryLocked()
|
||||
|
||||
// Check if already loaded from this path
|
||||
if loadedPaths[path] {
|
||||
return nil
|
||||
}
|
||||
@@ -232,8 +227,8 @@ func LoadAgentRegistry(path string) error {
|
||||
data, err := os.ReadFile(path) //nolint:gosec // G304: path is from config
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
loadedPaths[path] = true // Mark as "loaded" (no file)
|
||||
return nil // No custom config, use built-ins only
|
||||
loadedPaths[path] = true
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -243,7 +238,6 @@ func LoadAgentRegistry(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Merge user-defined agents (override built-ins)
|
||||
for name, preset := range userRegistry.Agents {
|
||||
preset.Name = AgentPreset(name)
|
||||
globalRegistry.Agents[name] = preset
|
||||
@@ -253,12 +247,41 @@ func LoadAgentRegistry(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAgentRegistry loads agent definitions from a JSON file and merges with built-ins.
|
||||
// User-defined agents override built-in presets with the same name.
|
||||
// This function caches loaded paths to avoid redundant file reads.
|
||||
func LoadAgentRegistry(path string) error {
|
||||
registryMu.Lock()
|
||||
defer registryMu.Unlock()
|
||||
return loadAgentRegistryFromPathLocked(path)
|
||||
}
|
||||
|
||||
// DefaultAgentRegistryPath returns the default path for agent registry.
|
||||
// Located alongside other town settings.
|
||||
func DefaultAgentRegistryPath(townRoot string) string {
|
||||
return filepath.Join(townRoot, "settings", "agents.json")
|
||||
}
|
||||
|
||||
// DefaultRigAgentRegistryPath returns the default path for rig-level agent registry.
|
||||
// Located in <rig>/settings/agents.json.
|
||||
func DefaultRigAgentRegistryPath(rigPath string) string {
|
||||
return filepath.Join(rigPath, "settings", "agents.json")
|
||||
}
|
||||
|
||||
// RigAgentRegistryPath returns the path for rig-level agent registry.
|
||||
// Alias for DefaultRigAgentRegistryPath for consistency with other path functions.
|
||||
func RigAgentRegistryPath(rigPath string) string {
|
||||
return DefaultRigAgentRegistryPath(rigPath)
|
||||
}
|
||||
|
||||
// LoadRigAgentRegistry loads agent definitions from a rig-level JSON file and merges with built-ins.
|
||||
// This function works similarly to LoadAgentRegistry but for rig-level configurations.
|
||||
func LoadRigAgentRegistry(path string) error {
|
||||
registryMu.Lock()
|
||||
defer registryMu.Unlock()
|
||||
return loadAgentRegistryFromPathLocked(path)
|
||||
}
|
||||
|
||||
// GetAgentPreset returns the preset info for a given agent name.
|
||||
// Returns nil if the preset is not found.
|
||||
func GetAgentPreset(name AgentPreset) *AgentPresetInfo {
|
||||
|
||||
Reference in New Issue
Block a user