package definition import ( "fmt" "os" "path/filepath" "sort" "strings" ) // CuratedIDs reads the agent specs that ship with the deployment and returns // the set of definition ids they declare. // // # WHY THIS EXISTS // // An agent is "built-in" when the product ships its spec. There is no column // saying so and deliberately none is added here: `importagents` publishes these // same files into `agent_definitions` as ordinary `organization` rows, so a // curated agent and a tenant-authored shared agent are indistinguishable in the // table. The distinguishing fact lives on disk, in the directory the importer // publishes FROM — which is the same fact the frontend uses, where `isShipped` // tests membership of the ids bundled from `src/agents/**/*.md`. // // Reading the directory rather than listing ids in configuration keeps the // protected set and the published set the same set by construction. Adding a // ninth agent protects it; removing one stops protecting it; neither needs a // code change, and neither can drift. // // The ids are parsed out of the frontmatter with the same parser the importer // uses, NOT taken from the filename. A file named `analytics-agent.md` whose // frontmatter says `id: analytics` publishes as `analytics`, and protecting the // filename would protect nothing. // // A missing directory returns an empty set and no error: development checkouts // and test binaries run from working directories that have no `agents/`, and // this is a protection list rather than something to serve from. A directory // that exists but holds a spec that will not parse IS an error — that same file // would fail the importer, and staying quiet about it would leave an agent // unprotected for a reason nobody could see. func CuratedIDs(dir string) (map[string]bool, error) { entries, err := os.ReadDir(dir) if os.IsNotExist(err) { return map[string]bool{}, nil } if err != nil { return nil, fmt.Errorf("read curated agents in %s: %w", dir, err) } ids := make(map[string]bool, len(entries)) for _, e := range entries { name := e.Name() // README.md is documentation, not a spec — skipped by name, the same // way cmd/importagents skips it, so that a parse failure always means // something is actually wrong. if e.IsDir() || !strings.HasSuffix(name, ".md") || name == "README.md" { continue } raw, err := os.ReadFile(filepath.Join(dir, name)) if err != nil { return nil, fmt.Errorf("read %s: %w", name, err) } parsed, err := ParseAgent(string(raw), Options{}) if err != nil { return nil, fmt.Errorf("%s: %w", name, err) } if parsed.ID != "" { ids[parsed.ID] = true } } return ids, nil } // SortedIDs renders a set as a stable list, for logging. func SortedIDs(set map[string]bool) []string { out := make([]string, 0, len(set)) for id := range set { out = append(out, id) } sort.Strings(out) return out }