|
| 1 | +--- |
| 2 | +title: "NNNN. Config-driven agents" |
| 3 | +status: Accepted |
| 4 | +relates_to: |
| 5 | + - agent-architecture |
| 6 | + - agent-infrastructure |
| 7 | +topics: |
| 8 | + - agents |
| 9 | + - per-repo |
| 10 | + - configuration |
| 11 | + - extensibility |
| 12 | +--- |
| 13 | + |
| 14 | +# NNNN. Config-driven agents |
| 15 | + |
| 16 | +Date: 2026-06-29 |
| 17 | + |
| 18 | +## Status |
| 19 | + |
| 20 | +Accepted |
| 21 | + |
| 22 | +## Context |
| 23 | + |
| 24 | +Which agents fullsend knows about is currently compiled into the binary. |
| 25 | +Scaffold-embedded harnesses (`internal/scaffold/fullsend-repo/harness/`) |
| 26 | +define the canonical agent set, and a hardcoded `externalAgents` map in |
| 27 | +`baseurl.go` extends it for agents extracted to standalone repos. Both |
| 28 | +`HarnessNames()` and `HarnessWrappersLayer` read from these compiled-in |
| 29 | +sources to enumerate and generate harness wrappers. |
| 30 | + |
| 31 | +This creates three problems: |
| 32 | + |
| 33 | +1. **Adding an agent requires a code change.** A user who wants to run a |
| 34 | + custom agent must modify the fullsend binary or work around it. |
| 35 | +2. **First-party agents extracted to external repos need special |
| 36 | + handling.** The `externalAgents` map is a one-off mechanism that pins |
| 37 | + commit SHAs and content hashes in Go source. |
| 38 | +3. **First-party and third-party agents follow different paths.** |
| 39 | + Fullsend's own agents are discovered from the scaffold embed; |
| 40 | + external agents are discovered from a hardcoded map. There is no |
| 41 | + single, uniform mechanism. |
| 42 | + |
| 43 | +The triage agent extraction to `fullsend-ai/agents` |
| 44 | +([ADR 0045](0045-forge-portable-harness-schema.md) Phase 4) is the |
| 45 | +first agent to move out of the scaffold. More will follow. The |
| 46 | +registration mechanism must support both first-party extractions and |
| 47 | +user-defined agents without code changes to fullsend. |
| 48 | + |
| 49 | +## Decision |
| 50 | + |
| 51 | +Make agent registration a **config-level concept** in both org config |
| 52 | +and per-repo config. |
| 53 | + |
| 54 | +**Config schema.** Add an `agents` list to both `OrgConfig` and |
| 55 | +`PerRepoConfig`. Each entry is either a string shorthand or an object |
| 56 | +with an explicit name. The source can be a URL (for remote harnesses) |
| 57 | +or a local path (for harnesses on disk): |
| 58 | + |
| 59 | +```yaml |
| 60 | +# Per-repo config (.fullsend/config.yaml in target repo) |
| 61 | +version: "1" |
| 62 | +agents: |
| 63 | + # String shorthand — name derived from filename |
| 64 | + - https://raw.githubusercontent.com/fullsend-ai/fullsend/<sha>/internal/scaffold/fullsend-repo/harness/code.yaml#sha256=<hash> |
| 65 | + - harness/my-custom-agent.yaml |
| 66 | + |
| 67 | + # Object form — explicit name overrides filename |
| 68 | + - name: triage |
| 69 | + source: https://raw.githubusercontent.com/fullsend-ai/agents/<sha>/harness/triage.yaml#sha256=<hash> |
| 70 | + - name: lint |
| 71 | + source: harness/my-linter.yaml |
| 72 | +allowed_remote_resources: |
| 73 | + - https://raw.githubusercontent.com/fullsend-ai/fullsend/ |
| 74 | + - https://raw.githubusercontent.com/fullsend-ai/agents/ |
| 75 | +``` |
| 76 | +
|
| 77 | +An entry's source is treated as a URL if it starts with `https://`, |
| 78 | +and as a local path otherwise. Local paths resolve relative to the |
| 79 | +`.fullsend/` directory. When `name` is omitted (string shorthand), the |
| 80 | +agent name is derived from the filename (`triage.yaml` -> `triage`). |
| 81 | +When `name` is provided, it overrides the filename-derived name. Role |
| 82 | +and slug are read from the harness content at |
| 83 | +install/wrapper-generation time, not stored in config. URL entries |
| 84 | +include an integrity hash fragment for content verification |
| 85 | +([ADR 0038](0038-universal-harness-access.md)); local paths do not |
| 86 | +require one since the content is already trusted on disk. |
| 87 | + |
| 88 | +**`allowed_remote_resources` in per-repo config.** This field currently |
| 89 | +exists only in org config. Per-repo config gains it to support base |
| 90 | +composition without an org config repo. Both are checked at load time; |
| 91 | +per-repo entries are additive. |
| 92 | + |
| 93 | +**Install seeding.** During install, the default first-party agent |
| 94 | +URLs are populated into `agents` — in org config for org-mode |
| 95 | +installs (`fullsend admin install <org>`), in per-repo config for |
| 96 | +per-repo installs (`fullsend github` / `fullsend admin install |
| 97 | +<owner/repo>`). They use the same format as any user-added agent. The |
| 98 | +`--agents` flag continues to control which roles are installed; the |
| 99 | +mapping from role to default harness URL is a build-time constant (not |
| 100 | +a runtime concept). |
| 101 | + |
| 102 | +**CLI surface.** A `fullsend agent` subcommand group manages the |
| 103 | +agents list in config: |
| 104 | + |
| 105 | +| Command | Effect | |
| 106 | +|---------|--------| |
| 107 | +| `fullsend agent add <url-or-path>` | Fetch/read harness, validate, append to config | |
| 108 | +| `fullsend agent list` | List registered agents with name, role, source | |
| 109 | +| `fullsend agent update <name> [sha]` | Re-pin agent to latest HEAD or a specific commit | |
| 110 | +| `fullsend agent remove <name>` | Remove agent entry from config | |
| 111 | + |
| 112 | +`agent add` accepts a URL or a local path and validates the harness |
| 113 | +before appending it to config. `agent update` re-pins a URL-based |
| 114 | +agent to a new commit (latest HEAD or a specific SHA). Both commands |
| 115 | +automatically resolve unpinned URLs to fully pinned form — commit SHA |
| 116 | +and integrity hash are resolved and computed by the CLI so users never |
| 117 | +need to construct pinned URLs manually. The stored config entry is |
| 118 | +always fully pinned. |
| 119 | + |
| 120 | +**Additive merge model.** Config entries are merged with |
| 121 | +scaffold-discovered agents, not a replacement. Scaffold agents form |
| 122 | +the base set; config entries add new agents or override scaffold |
| 123 | +entries with the same name. The merge rule: config wins over scaffold |
| 124 | +when names collide. This enables gradual migration — agents can be |
| 125 | +extracted one at a time without requiring every config to list all |
| 126 | +agents. |
| 127 | + |
| 128 | +`fullsend agent list` shows the merged view (scaffold + config) so |
| 129 | +the full agent set is visible in one place. |
| 130 | + |
| 131 | +**Wrapper generation.** `HarnessWrappersLayer` (org mode) builds the |
| 132 | +merged agent set and generates wrappers from it. Per-repo install |
| 133 | +does the same. For URL entries, both generate the same thin wrapper |
| 134 | +format (`base:` + `role:` + `slug:`). For local path entries, the |
| 135 | +harness file is used directly — no wrapper is needed since the |
| 136 | +content is already on disk. |
| 137 | + |
| 138 | +**Removal of compiled-in agent map.** The `externalAgents` map, |
| 139 | +`IsExternalAgent()`, and the external-agent branches in |
| 140 | +`HarnessBaseURL()`/`HarnessContentHash()` are deleted. |
| 141 | +`HarnessNames()` returns only scaffold-embedded names (used as the |
| 142 | +base set for the additive merge). |
| 143 | + |
| 144 | +**Transition to authoritative config.** The additive model is a |
| 145 | +migration aid. Once all first-party agents have been extracted from |
| 146 | +the scaffold and all installations have populated `agents` in config, |
| 147 | +a follow-up change makes config authoritative: scaffold agents are no |
| 148 | +longer discovered, and `agents` is the complete list. An issue will |
| 149 | +be filed to track this transition once the last agent is extracted. |
| 150 | + |
| 151 | +## Consequences |
| 152 | + |
| 153 | +- **Extensibility.** Anyone can add an agent to the fullsend installation in their repo with |
| 154 | + `fullsend agent add <url-or-path>` — no code change, no PR to |
| 155 | + fullsend. Local paths support development and private harnesses. |
| 156 | +- **Uniform path.** First-party and third-party agents are registered |
| 157 | + the same way. Extracting an agent to a standalone repo is a config |
| 158 | + update, not a code change. |
| 159 | +- **Gradual migration.** The additive merge model means agents can be |
| 160 | + extracted from the scaffold one at a time. Users only need config |
| 161 | + entries for agents that have moved or been added — unchanged |
| 162 | + scaffold agents continue to work. Once all agents are extracted, |
| 163 | + config becomes authoritative and the scaffold fallback is removed. |
| 164 | +- **Org config dependency removed for per-repo installs.** |
| 165 | + `allowed_remote_resources` in per-repo config eliminates the need |
| 166 | + to read org config for base composition validation. |
| 167 | +- **Migration.** No forced migration. Existing installations continue |
| 168 | + to work without changes: |
| 169 | + - When `agents` is empty in config, the code falls back to the |
| 170 | + current scaffold-based agent discovery. This fallback is |
| 171 | + permanent until the config is populated — there is no deadline. |
| 172 | + - Per-repo configs that lack `allowed_remote_resources` inherit |
| 173 | + the org-level allowlist (if present) or use the scaffold |
| 174 | + fallback path, which does not require an allowlist. |
| 175 | + - To opt in, users can run `fullsend agent add` to populate the |
| 176 | + list manually, or the config is backfilled automatically the |
| 177 | + next time they run `fullsend admin install --upgrade`, |
| 178 | + `fullsend github` (re-install), or `fullsend repos sync` |
| 179 | + ([ADR 0057](0057-repos-management.md)). |
| 180 | + - A deprecation notice is logged when the fallback is used, to |
| 181 | + nudge users toward populating the config. |
| 182 | + |
| 183 | +## References |
| 184 | + |
| 185 | +- [ADR 0033](0033-per-repo-installation-mode.md) -- per-repo installation mode |
| 186 | +- [ADR 0038](0038-universal-harness-access.md) -- URL-based resource references and integrity hashes |
| 187 | +- [ADR 0045](0045-forge-portable-harness-schema.md) -- harness composition via `base:` URLs |
| 188 | +- [ADR 0057](0057-repos-management.md) -- repos management for per-repo installations |
| 189 | +- [Implementation plan](../plans/config-driven-agents.md) |
0 commit comments