How ACP/ACPX Agent Targeting Works with acp_registry_overrides in no-mistakes
When you specify an agent as acp:<target> in no-mistakes, the acp_registry_overrides configuration map substitutes the target name with a custom raw command, injecting it via the --agent flag when spawning the acpx binary.
The no-mistakes repository supports external ACP agents through the acpx binary, allowing users to extend functionality with custom agent implementations. The acp_registry_overrides configuration option provides a flexible mechanism to remap agent targets to arbitrary commands without modifying the underlying agent code. Understanding how ACP/acpx agent targeting works with acp_registry_overrides enables precise control over agent execution workflows.
Understanding the ACP/ACPX Agent Pattern
ACP agents in no-mistakes operate by invoking an external acpx binary with specific arguments. When you configure an agent name prefixed with acp:, the system recognizes it as an external agent target rather than a built-in implementation. This design delegates agent logic to external processes while maintaining a consistent internal interface.
The targeting mechanism relies on three core components: target detection in internal/agent/agent.go, registry override resolution, and command-line construction in internal/agent/acpx.go.
Target Detection and Validation
How acpTarget Extracts the Target Name
The acpTarget helper function in internal/agent/agent.go validates and extracts the target portion from agent names. It strips the acp: prefix and enforces strict validation rules to prevent malformed targets.
// internal/agent/agent.go
func acpTarget(name types.AgentName) (string, bool) {
if !strings.HasPrefix(string(name), "acp:") { return "", false }
target := strings.TrimPrefix(string(name), "acp:")
if target == "" || strings.ContainsAny(target, " \t\r\n") { return "", false }
return target, true
}
This function ensures that target names are non-empty and contain no whitespace characters before proceeding to registry lookup.
Registry Override Resolution
The ACPRegistryOverrides Map
When constructing an agent via NewWithOptions, the system checks for registry overrides in the Options struct. If ACPRegistryOverrides contains an entry matching the extracted target, that value becomes the raw command for the agent.
// internal/agent/agent.go (NewWithOptions)
if target, ok := acpTarget(name); ok {
rawCommand := ""
if opts.ACPRegistryOverrides != nil {
rawCommand = opts.ACPRegistryOverrides[target]
}
return &acpxAgent{bin: bin, target: target, rawCommand: rawCommand}, nil
}
The acpxAgent struct stores this raw command for later use during argument building, maintaining a clean separation between target identification and command resolution.
Command Construction with buildArgs
The acpxAgent.buildArgs method determines how the final command line is assembled based on whether a registry override exists. When rawCommand is populated, it injects the --agent flag; otherwise, it falls back to passing the target name directly.
// internal/agent/acpx.go (buildArgs)
if a.rawCommand != "" {
args = append(args, "--agent", a.rawCommand)
}
…
if a.rawCommand == "" {
args = append(args, a.target) // fall‑back to target name
}
args = append(args, "exec", prompt)
This conditional logic ensures that registry overrides take precedence while maintaining backward compatibility for standard target names.
Configuration Wiring in YAML
The acp_registry_overrides field in your configuration file maps directly to the Config.ACPRegistryOverrides map in internal/config/config.go.
// internal/config/config.go (excerpt)
type Config struct {
ACPXPath string `yaml:"acpx_path"`
ACPRegistryOverrides map[string]string `yaml:"acp_registry_overrides"`
}
When the CLI loads ~/.no-mistakes/config.yaml, it unmarshals this structure and forwards the overrides map to agent.NewWithOptions. A typical configuration looks like:
# ~/.no-mistakes/config.yaml
agent: acp:local-gemini
acpx_path: /usr/local/bin/acpx
acp_registry_overrides:
local-gemini: node /opt/mock-acp-agent.mjs
This configuration causes the acp:local-gemini agent to execute as acpx --agent "node /opt/mock-acp-agent.mjs" exec "<prompt>".
Practical Implementation Examples
Programmatic Agent Creation
You can instantiate ACP agents with registry overrides directly in Go code:
opts := agent.Options{
ACPRegistryOverrides: map[string]string{
"local-gemini": "node /tmp/mock-acp.mjs",
},
}
a, _ := agent.NewWithOptions("acp:local-gemini", "/usr/local/bin/acpx", nil, opts)
fmt.Println(a.Name()) // → "acp:local-gemini"
cmdArgs := a.(*acpxAgent).buildArgs(agent.RunOpts{Prompt: "do work", CWD: "/repo"})
fmt.Println(cmdArgs)
// Output:
// ["--agent" "node /tmp/mock-acp.mjs" "--format" "json" ... "exec" "do work"]
YAML Configuration Setup
For CLI usage, define your overrides in the configuration file:
acp_registry_overrides:
local-gemini: node /opt/mock-acp-agent.mjs
custom-bridge: /usr/local/bin/custom-agent --mode=production
The daemon automatically loads these mappings when initializing the agent pool, applying them to any matching acp:<target> agent requests.
Summary
- Target detection occurs via
acpTargetininternal/agent/agent.go, which validatesacp:prefixes and extracts clean target names. - Registry overrides are stored in
ACPRegistryOverridesand resolved during agent construction inNewWithOptions. - Command injection happens in
acpxAgent.buildArgsthrough the--agentflag when overrides exist, falling back to the target name otherwise. - Configuration binding maps the YAML
acp_registry_overridesfield to the internal config struct ininternal/config/config.go. - External execution ultimately invokes the
acpxbinary with the resolved arguments, enabling custom agent logic without code changes.
Frequently Asked Questions
What happens if no registry override exists for an ACP target?
If ACPRegistryOverrides is nil or lacks an entry for the specific target, the acpxAgent passes the plain target name as a positional argument to the binary instead of using the --agent flag. This fallback behavior allows standard acpx registry lookups to function normally when no custom override is defined.
Can I override multiple ACP agents in the same configuration?
Yes, the acp_registry_overrides map supports multiple key-value pairs simultaneously. You can define distinct raw commands for different targets such as gemini, claude, or custom identifiers, and the system will resolve each independently based on the agent name requested.
What validation is performed on the target name?
The acpTarget function enforces that extracted targets are non-empty and contain no whitespace characters (spaces, tabs, carriage returns, or newlines). This validation prevents command injection and ensures that target names are valid identifiers before attempting registry lookups.
How does the acpx binary receive the override command?
When an override exists, the buildArgs method appends the literal string "--agent" followed by the raw command value to the argument slice. This occurs before the exec subcommand and prompt payload, positioning the override as a top-level flag that the acpx binary processes to determine which agent implementation to execute.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →