57 lines
1.7 KiB
Go
57 lines
1.7 KiB
Go
package agent
|
|
|
|
import "fmt"
|
|
|
|
// custom is an operator-defined agent declared in config.toml's
|
|
// `[agents.<name>]` table. It wraps an arbitrary command — typically a
|
|
// shell wrapper such as `copilot-local` that points a CLI at a
|
|
// self-hosted model — and reuses a built-in backend's MCP-config and
|
|
// env conventions so MCPs are formatted exactly as the underlying CLI
|
|
// expects.
|
|
type custom struct {
|
|
name string
|
|
desc string
|
|
command string
|
|
backend backend
|
|
}
|
|
|
|
func (c custom) Name() string { return c.name }
|
|
func (c custom) Description() string { return c.desc }
|
|
|
|
func (c custom) Spawn(ctx Context, args []string) error {
|
|
return c.backend.spawn(c.name, c.command, ctx, args)
|
|
}
|
|
|
|
// RegisterCustom adds an operator-defined agent to the registry.
|
|
//
|
|
// - name is the subcommand: `sherlock <name>`.
|
|
// - command is the binary to exec; it defaults to name when empty.
|
|
// - backendName selects the MCP/-env conventions ("copilot" or
|
|
// "claude").
|
|
//
|
|
// Unlike Register, it returns an error (rather than panicking) on a bad
|
|
// backend or a name that collides with an already-registered agent,
|
|
// since the input comes from operator config rather than program code.
|
|
func RegisterCustom(name, command, backendName string) error {
|
|
if name == "" {
|
|
return fmt.Errorf("agent: custom agent needs a name")
|
|
}
|
|
b, err := resolveBackend(backendName)
|
|
if err != nil {
|
|
return fmt.Errorf("agent: custom agent %q: %w", name, err)
|
|
}
|
|
if command == "" {
|
|
command = name
|
|
}
|
|
if _, dup := registry[name]; dup {
|
|
return fmt.Errorf("agent: custom agent %q conflicts with an existing agent", name)
|
|
}
|
|
registry[name] = custom{
|
|
name: name,
|
|
desc: fmt.Sprintf("custom %s agent (%s)", backendName, command),
|
|
command: command,
|
|
backend: b,
|
|
}
|
|
return nil
|
|
}
|