feat: conversion-driven extensions — resolvers, DefineTool, hooks, ops controls
CI / Tidy (push) Successful in 9m31s
CI / Build & Test (push) Successful in 10m13s

Phase 9a (ADR-0014): Registry.RegisterResolver for dynamic tiers;
DefineTool[Args] typed tools; Usage cache/reasoning detail fields wired
through anthropic/openai/google; WithPromptCaching (Anthropic
cache_control); agent supervision hooks (WithMaxStepsFunc, WithSteer,
WithCompactor, WithToolErrorLimits + ErrToolLoop); health
Bench/Unbench/Snapshot; ChainConfig.Observer failover events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 13:30:06 +02:00
parent 04b21fdad2
commit 0147a79d18
21 changed files with 767 additions and 29 deletions
+12 -1
View File
@@ -97,12 +97,23 @@ func (r *Registry) expand(spec string, visiting []string) ([]element, error) {
continue
}
// Bare token: must be a registered alias.
// Bare token: a registered alias, or a dynamic resolver hit.
r.mu.RLock()
target, isAlias := r.aliases[raw]
_, isProvider := r.providers[raw]
resolvers := slices.Clone(r.resolvers)
r.mu.RUnlock()
if !isAlias {
// Resolvers run without the lock — they may call back into the
// registry (and DB-backed ones block on I/O).
for _, res := range resolvers {
if spec, ok := res.Resolve(raw); ok && spec != "" {
target, isAlias = spec, true
break
}
}
}
if !isAlias {
if isProvider {
return nil, fmt.Errorf("%q is a provider, not an alias — use %q", raw, raw+"/<model-id>")