// Package cagent is the cagent harness adapter. // // cagent is per-run (no persistent agent state). v1 renders a starter // ~/.config/cagent/default.yaml with a single default agent pointing at the // gateway. Import always returns an empty result. package cagent import ( "os" "path/filepath" "github.com/lukaszraczylo/harness-sync/internal/adapter" "github.com/lukaszraczylo/harness-sync/internal/adapter/common" "github.com/lukaszraczylo/harness-sync/internal/canonical" "gopkg.in/yaml.v3" ) const name = "cagent" // Adapter implements adapter.Adapter for the cagent harness. type Adapter struct { *adapter.Base } // WithHome overrides the home directory used to resolve target paths. func WithHome(h string) adapter.BaseOption { return func(b *adapter.Base) { b.Home = h } } // New returns a new Adapter with the given options applied. func New(opts ...adapter.BaseOption) *Adapter { return &Adapter{Base: adapter.NewBase(opts...)} } // Name returns the harness identifier. func (a *Adapter) Name() string { return name } // Capabilities declares what cagent harness-sync manages. func (a *Adapter) Capabilities() adapter.HarnessCapabilities { return adapter.HarnessCapabilities{ ManagesProviders: true, ManagesModels: true, ManagesMCP: true, ManagesSkills: false, ManagesInstructions: true, HasBuiltInSub: false, } } // Detect returns true when ~/.config/cagent/ exists. func (a *Adapter) Detect() bool { _, err := os.Stat(filepath.Join(a.Home, ".config", "cagent")) return err == nil } // Render produces a starter default.yaml for cagent. func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) { fs := adapter.NewFileSet() cfgPath := filepath.Join(a.Home, ".config", "cagent", "default.yaml") instructions := b.Instructions.Global if override, ok := b.Instructions.PerHarness[name]; ok && override != "" { instructions = override } mcps := map[string]any{} for _, s := range b.MCP.Servers { e := map[string]any{} if s.Command != "" { e["command"] = s.Command } args := s.Args if args == nil { args = []string{} } e["args"] = args mcps[s.Name] = e } provKey := common.GatewayProviderKey(b.Profile.Gateway.URL) // inline model shorthand: "providerID/modelID" modelRef := common.KiloModelString(&b.Profile) cfg := map[string]any{ "version": 8, "metadata": map[string]any{ "name": "harness-sync", }, "agents": map[string]any{ "default": map[string]any{ "model": modelRef, "instruction": instructions, }, }, "providers": common.ProvidersAsCagentMap(&b.Profile), "models": map[string]any{ provKey: map[string]any{ "provider": provKey, "model": b.Profile.Gateway.DefaultModel, }, }, } if len(mcps) > 0 { cfg["mcps"] = mcps } body, err := yaml.Marshal(cfg) if err != nil { return nil, err } fs.Add(adapter.File{ Dest: cfgPath, Kind: adapter.RenderedFile, Content: body, }) return fs, nil } // Import returns an empty result — cagent has no persistent state to import. func (a *Adapter) Import(home string) (*adapter.ImportResult, error) { return &adapter.ImportResult{}, nil }