fix(spawn): honor an agent's declared agents: access-control policy - #255
Merged
Conversation
An agent can declare agents: -- a Smart Single Value that controls which sub-agents its spawned session may delegate to. merge_configs() honors it. The runtime-registry propagation block added later then silently undid it. This commit: - Applies agent_config declarations to the live registry propagation (not just merge_configs) - Ensures same-name local-wins collision avoidance is preserved - Adds comprehensive spawn-level test coverage for all declaration forms - Fixes PR #178's original complaint about allowlists under-delivering Blast radius: zero. Surveyed all 749 config files under ~/.amplifier/cache/ -- 51 agents: occurrences exist, all are dict-shaped agent rosters. Zero access-control declarations in the installed ecosystem, so nothing changes for deployed systems. Preserves from #253: fresh-dict-and-rebind (no cross-session mutation), deepcopy per agent, local-wins. Follow-up to #178 and #253. Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
An agent can declare
agents:— a Smart Single Value ("all"" |"none"" | list) that controls which sub-agents its spawned session may delegate to.merge_configs()honors it. The runtime-registry propagation block added later then silently undid it.Measured on
main(parent static ={explorer, builder}; live registry additionally holds mode-contributedsibling_b):merge_configs"none"[][builder, explorer, sibling_b]["sibling_b"][][builder, explorer, sibling_b]["explorer"][explorer][builder, explorer, sibling_b]"all"[builder, explorer][builder, explorer, sibling_b][builder, explorer][builder, explorer, sibling_b]An agent that declared
agents: nonereceived every agent in the live registry.Why this is a bug and not a design decision
Traced through primary sources before changing anything:
d609bb2(2025-11-30) introduced the field. Its commit message states the intent: ""none"→ empty agents dict (disable delegation); list → filter parent's agents to specified names… This enables agents to control which sub-agents their spawned sessions can delegate to via the task tool."microsoft/amplifier-profilesdocs/AGENT_AUTHORING.mddocumentsagents: none # Cannot call task tool to delegate, followed by a three-step "How it works" describing exactly the filtering this block defeats.amplifier-app-cli/docs/AGENT_DELEGATION_IMPLEMENTATION.md:409-416documentsagents: none # Disable agents.774d8b9(PR fix(session-spawner): propagate runtime overlay agents and skill capability to child sessions (#233) #178, formicrosoft-amplifier/amplifier-support#233) added the propagation so mode-contributed agents reach children. Its rule was "Local agent_config declarations win over inherited live registry (never overwrite)" — implemented only as same-name collision avoidance, never as policy enforcement. Nothing in that PR, the issue, or the maintainer's closing comment addresses the filter interaction.test_session_spawner_issue_233.pynever pass an overlay carrying anagents:key, and the twomerge_configsfilter tests intest_agent_config.pystop before the spawner runs. The two suites passed independently while contradicting each other at runtime.So this restores stated intent rather than changing designed behavior.
It also fixes PR #178's own complaint
PR #178's body noted: "even an explicit
agents: [sibling_b]declaration in agent A's config wouldn't reach sibling_b — the source dict is the wrong one." That under-delivery was never fixed —merge_configsfilters the static snapshot, which does not contain mode-contributed agents, so an allowlist naming one resolved to{}.Applying the declaration to the live registry as well reconciles both intents in one place: the allowlist is satisfied from the union of static + live agents. Row 2 of the table goes from "everything" to exactly
[sibling_b]— the agent gets precisely what it asked for, no more and no less.After
"none"[]["sibling_b"][sibling_b]["explorer"][explorer]"all"[builder, explorer, sibling_b](unchanged)[builder, explorer, sibling_b](unchanged)The gate keys off
agent_config— the overlay's own declaration — not offmerged_config["agents"]being empty. An unrestricted agent whose parent simply has no static agents also produces an empty merged dict; gating on emptiness would wrongly suppress propagation for it.TestS6ParentIsolation::test_propagation_does_not_mutate_parent_configis exactly that case and still passes unchanged.Preserved from #253: fresh-dict-and-rebind (no cross-session mutation of the parent's live config),
deepcopyper propagated agent, same-name local-wins.Blast radius: zero on the installed ecosystem
Surveyed every parsed bundle/agent/behavior/mode/profile config under
~/.amplifier/cache/— 749 config files, 51agents:key occurrences. All 51 are the dict-shaped defining section (agents: include: [...]rosters and inline agent definitions). Zero are the access-control Smart Single Value form. No installed agent declares a restriction, so nothing currently in the ecosystem changes behavior.Checked the consumers that depend on propagation: 4 modes contribute agents at runtime, and
context-intelligenceis the one shipped configuration with a real sibling hop (facilitator → tool-designer). None of them declaresagents:, so the gate leaves them untouched. Everysession.spawncaller —tool-delegate,tool-recipes,tool-skills(which spawnsagent_name="self", i.e. an empty overlay by construction), attractor's loop modules — passes noagents:declaration.Zero current users is also the argument for doing this now rather than later: nobody depends on the broken behavior yet, so this is the cheapest this fix will ever be.
Tests
TestS7AccessControlDeclaration— 5 spawn-level tests, one per row, asserting the exact resulting agent-name set. This is the first spawn-level coverage of the access-control declaration; no prior assertion needed reversing.Non-vacuousness — with
session_spawner.pyreverted tomainand the new tests kept:The two
"all"/absent tests correctly still pass — that behavior is unchanged by design.Follow-up, not in this PR
amplifier-foundation'sdocs/AGENT_AUTHORING.mdanddocs/BUNDLE_GUIDE.mddocumentagents:only in the bundle-roster sense and are silent on the access-control form; a repo-wide grep of the installed foundation docs foragents: nonereturns zero hits. The feature is documented only inamplifier-app-cliandamplifier-profiles. Worth a cross-repo docs pass now that the behavior matches the promise.Follow-up to #178 and #253.