Derive meaningful names for external sub-agents instead of using 'root'#2132
Derive meaningful names for external sub-agents instead of using 'root'#2132dgageot wants to merge 1 commit intodocker:mainfrom
Conversation
External sub-agents referenced via OCI images or URLs now get a proper name derived from their reference (last path segment, without tag) instead of keeping their internal 'root' name which collides with the parent config. Support explicit naming with 'name:reference' syntax (e.g. 'reviewer:agentcatalog/review-pr') and automatic base name extraction (e.g. 'agentcatalog/review-pr' becomes 'review-pr'). Detect and reject name collisions at both config validation time and agent loading time to prevent external agents from silently shadowing locally-defined agents. - Add ParseExternalAgentRef and externalRefBaseName in pkg/config/resolve.go - Rename loaded external agents in pkg/teamloader/teamloader.go - Validate name collisions in pkg/config/config.go - Update schema, example, and tests Assisted-By: docker-agent
There was a problem hiding this comment.
Review Summary
Assessment: 🟡 NEEDS ATTENTION
This PR introduces automatic name derivation for external sub-agents, which is a valuable feature. However, there are several issues with the collision detection and caching logic that could cause problems in production:
Key Issues Found
-
Collision detection incomplete (pkg/config/config.go:139): The validation only checks for collisions between external agents and local agents, but doesn't detect when multiple external references derive the same name (e.g.,
org1/helperandorg2/helperboth resolving tohelper). -
External agent caching broken (pkg/teamloader/teamloader.go:534, 560): The cache uses the original reference string as the key instead of the normalized external reference. This means
reviewer:agentcatalog/review-prandagentcatalog/review-prare treated as different cache entries, causing the same external agent to be loaded multiple times.
Recommendation
These issues should be addressed before merging:
- Extend
validateConfigto track and detect collisions between external agent names - Update the caching logic to use the normalized
externalRefas the cache key instead of the originalrefstring
The test coverage is good, but consider adding tests for:
- Multiple external references deriving the same name
- The same external reference accessed with different syntax forms
| } | ||
| if IsExternalReference(subAgentRef) { | ||
| name, _ := ParseExternalAgentRef(subAgentRef) | ||
| if allNames[name] { |
There was a problem hiding this comment.
🟡 MEDIUM: Collision detection incomplete
The validation checks for collisions between external agents and locally-defined agents, but doesn't detect when multiple external references derive the same name.
For example, if a config has both org1/helper and org2/helper as sub-agents, both would derive the name helper, but this collision wouldn't be caught during validation. The collision would only be detected at runtime in resolveAgentRefs.
Recommendation: Track external agent names in validateConfig to detect these collisions early:
externalNames := make(map[string]string) // name -> first ref that claimed it
for _, subAgentRef := range agent.SubAgents {
if IsExternalReference(subAgentRef) {
name, _ := ParseExternalAgentRef(subAgentRef)
if allNames[name] {
return fmt.Errorf("...")
}
if firstRef, exists := externalNames[name]; exists {
return fmt.Errorf("external agents '%s' and '%s' both resolve to name '%s'", firstRef, subAgentRef, name)
}
externalNames[name] = subAgentRef
}
}| } | ||
|
|
||
| a, err := loadExternalAgent(ctx, ref, runConfig, loadOpts) | ||
| agentName, externalRef := config.ParseExternalAgentRef(ref) |
There was a problem hiding this comment.
🟡 MEDIUM: External agent caching uses wrong key
The cache check at line 534 uses the original reference string (ref) instead of the normalized external reference (externalRef returned here). This means different syntax forms of the same reference won't hit the cache.
For example:
- First reference:
reviewer:agentcatalog/review-pr→ loads and caches under keyreviewer:agentcatalog/review-pr - Second reference:
agentcatalog/review-pr→ cache miss, loads the same agent again
Both references normalize to externalRef = "agentcatalog/review-pr", but the cache at line 534 checks externalAgents[ref] using the original string, and line 560 stores using the original string.
Recommendation: Use externalRef as the cache key in both the check (line 534) and the store (line 560):
// At line 534:
if a, ok := externalAgents[externalRef]; ok {
resolved = append(resolved, a)
continue
}
// At line 560:
externalAgents[externalRef] = a| agent.WithName(agentName)(a) | ||
|
|
||
| *agents = append(*agents, a) | ||
| externalAgents[ref] = a |
There was a problem hiding this comment.
🟡 MEDIUM: External agent caching stores with wrong key
The cache stores the loaded agent using the original reference string (ref) instead of the normalized external reference (externalRef). This is the storage side of the caching issue.
This means reviewer:agentcatalog/review-pr and agentcatalog/review-pr will be stored as separate cache entries, even though they refer to the same external agent.
Recommendation: Store using externalRef as the cache key:
externalAgents[externalRef] = a
External sub-agents referenced via OCI images or URLs kept their internal
"root" name, colliding with the parent config's root agent.
Changes
path segment (e.g.
agentcatalog/review-pr→review-pr), with tags anddigests stripped.
name:referencesyntax for custom names(e.g.
reviewer:agentcatalog/review-pr).conflicts with a locally-defined agent, both at config validation time and at
agent loading time.