Repository navigation
Description
Summary
In a routed workflow, gh-aw sends engine.model-routing.allowed-models to AWF as the request
policy, apiProxy.allowedModels, which is enforced on every request. The routing selection
itself is advisory: a request for a different model is admitted and logged as deviated, as long
as the policy permits it. Because gh-aw makes the policy exactly the router's candidate list,
nothing outside that list is permitted. An inline sub-agent that declares its own model: outside
allowed-models is rejected by the model policy with 403 model_policy_violation. Routing isn't
what rejects it.
AWF now separates the two (gh-aw-firewall#9380, fixed by #9382): apiProxy.routing.candidateModels
limits the router and classifier, and allowedModels remains the request policy.
gh-aw doesn't emit candidateModels yet, so the only workaround is to add every sub-agent model to
allowed-models. That also makes those models routing candidates for the main task.
Evidence
-
Local reproduction (2 Oct, AWF
b777912b). AWF ran with theallowedModelsand alias map
that gh-aw compiles for a routed workflow, withallowed-modelsset togpt-5.6-luna,
gpt-6-sol,claude-sonnet-4.6, andclaude-haiku-4.5. The requests a sub-agent with its own
model would make got these results:Sub-agent model Result claude-haiku-4.5(in the list)200 alias small, resolved toclaude-haiku-4.5200 alias opus(no allowed match)403 model_policy_violationgpt-5.4-mini(not in the list)403 model_policy_violation -
In gh-aw's own workflows, 70 inline sub-agent blocks set
model:. Most use the aliases
small(36) andlarge(7).
Cause
buildAWFConfiginpkg/workflow/awf_config_build.gohandles routed workflows like this:- it replaces the workflow's model policy with the routing candidates
(resolveModelRoutingAllowedModels, intersected withmodels.allowedandmodels.blockedby
intersectModelRoutingPolicy); - it writes the result as
apiProxy.allowedModels; AWFModelRoutingConfiginpkg/workflow/awf_config.gohas nocandidateModelsfield.
- it replaces the workflow's model policy with the routing candidates
extractEffectiveMarkdowninpkg/workflow/compiler_orchestrator_tools.goextracts the inline
sub-agents (parser.ExtractInlineSubAgents) but only logs their count. Theirmodel:values
aren't available when the AWF config is built.
Proposed plan
- Emit
routing.candidateModels.- Add a
candidateModelsfield toAWFModelRoutingConfig. - Fill it with the provider-qualified
allowed-models, intersected withmodels.allowedand
models.blockedas today. - The router and classifier then stay limited to
allowed-models.
- Add a
- Widen the request policy to the declared sub-agent models.
- Set
apiProxy.allowedModelsto the routing candidates plus the models that inline
sub-agents declare inmodel:, still intersected with the workflow'smodels.allowedand
models.blocked. - Keep the sub-agent models from
extractEffectiveMarkdown(and from imported agent files, if
they declare models) on the workflow data so the AWF config builder can use them. - For aliases such as
small, add the alias's resolved patterns from the model alias map, so
AWF's alias resolution can find an admitted model. - This keeps the cost bound explicit: only models the workflow names somewhere are permitted.
- Set
- Warn at compile time. If a sub-agent's model can't be admitted, for example because
models.blockedexcludes it or an alias resolves to nothing, emit a compiler warning naming the
sub-agent and the model. Don't let it fail at run time with 403. - Gate on AWF version.
candidateModelsrequires an AWF release that includes gh-aw-firewall#9382. The first
published one is v0.28.33; v0.28.32 has a tag but no release.- Add a minimum-version constant next to the other AWF feature gates.
- For older AWF versions, keep today's behaviour and warn that sub-agent models are limited to
allowed-models. - Routed workflows also need current AWF images for this to take effect (see Model routing: routed workflows run stale AWF and router images regardless of the AWF version #66226).
- Tests.
- Compile a routed workflow with inline sub-agents that use a concrete model, an alias, and a
blocked model. - Assert that
routing.candidateModelsequals the candidates, thatallowedModelsadds the
sub-agent models, that the blocked one produces a warning, and that an old AWF version falls
back.
- Compile a routed workflow with inline sub-agents that use a concrete model, an alias, and a
Notes
- Copilot CLI's built-in sub-agents (for example Explore) inherit the routed model, so they're
unaffected. This issue is about sub-agents that declare their own model.
Activity