fix(lifecycle): resolve utility inference agents by name, not database UUID (#2594)

Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
João Salavisa
2026-08-12 23:46:51 +01:00
committed by GitHub
parent 0c51ca1a76
commit 0c2ffeb54e
2 changed files with 72 additions and 4 deletions
@@ -83,13 +83,20 @@ func (m *Manager) ExecuteInferenceProfilePrompt(ctx context.Context, sessionID,
if sessionID == "" {
return nil, fmt.Errorf("session_id is required")
}
ia, ok := m.registry.GetInferenceAgent(profile.AgentID)
// The registry is keyed by the agent name (claude-acp, codex-acp, ...),
// while AgentID is the agents row's generated UUID. AgentName is empty only
// for resolvers that never populated it, whose AgentID is already a name.
agentName := profile.AgentName
if agentName == "" {
agentName = profile.AgentID
}
ia, ok := m.registry.GetInferenceAgent(agentName)
if !ok {
return nil, fmt.Errorf("agent %q does not support inference", profile.AgentID)
return nil, fmt.Errorf("agent %q does not support inference", agentName)
}
cfg := ia.InferenceConfig()
if cfg == nil || !cfg.Supported {
return nil, fmt.Errorf("agent %q inference not supported", profile.AgentID)
return nil, fmt.Errorf("agent %q inference not supported", agentName)
}
execution, err := m.GetOrEnsureExecution(ctx, sessionID)
if err != nil {
@@ -121,7 +128,7 @@ func (m *Manager) ExecuteInferenceProfilePrompt(ctx context.Context, sessionID,
}
autoApprove := profile.AutoApprove
return client.InferencePrompt(ctx, &utility.PromptRequest{
Prompt: prompt, AgentID: profile.AgentID, Model: profile.Model, Mode: profile.Mode,
Prompt: prompt, AgentID: agentName, Model: profile.Model, Mode: profile.Mode,
AutoApprovePermissions: &autoApprove,
InferenceConfig: &utility.InferenceConfigDTO{
Command: cfg.Command.Args(), ModelFlag: cfg.ModelFlag.Args(), WorkDir: execution.WorkspacePath,
@@ -361,6 +361,67 @@ func TestManagerExecuteInferenceProfilePrompt(t *testing.T) {
})
}
// The agents table's primary key is a generated UUID while the registry is
// keyed by the agent slug, so AgentID and AgentName never carry the same value
// in production. Every fixture above uses one string for both, which hid the
// registry lookup reading AgentID.
func TestManagerExecuteInferenceProfilePromptUsesTheRegistryAgentName(t *testing.T) {
const dbAgentUUID = "6a2b0f5c-6b9b-4f0e-8f3b-1f2c3d4e5f60"
harness := newInferenceHarness(t, &stubProfileResolver{profile: &AgentProfileInfo{
ProfileID: "profile-1",
AgentID: dbAgentUUID,
AgentName: "inference-ok",
Model: "claude-haiku-4-5",
}}, newInferenceAgent("inference-ok", true, true))
resp, err := harness.manager.ExecuteInferenceProfilePrompt(t.Context(), "session-1", "profile-1", "hello")
if err != nil {
t.Fatalf("ExecuteInferenceProfilePrompt: %v", err)
}
if resp.Response != "answer for hello" {
t.Fatalf("response = %+v", resp)
}
recorded := harness.recorded()
if len(recorded) != 1 {
t.Fatalf("agentctl calls = %d", len(recorded))
}
// agentctl keys its ACP compatibility and session handling off this field,
// so the database UUID must never reach the wire.
if recorded[0].AgentID != "inference-ok" {
t.Fatalf("outgoing AgentID = %q, want the registry agent name", recorded[0].AgentID)
}
}
func TestManagerExecuteInferenceProfilePromptFallsBackToAgentIDWithoutAName(t *testing.T) {
harness := newInferenceHarness(t, &stubProfileResolver{profile: &AgentProfileInfo{
ProfileID: "profile-1",
AgentID: "inference-ok",
}}, newInferenceAgent("inference-ok", true, true))
if _, err := harness.manager.ExecuteInferenceProfilePrompt(t.Context(), "session-1", "profile-1", "hello"); err != nil {
t.Fatalf("ExecuteInferenceProfilePrompt: %v", err)
}
recorded := harness.recorded()
if len(recorded) != 1 || recorded[0].AgentID != "inference-ok" {
t.Fatalf("requests = %+v, want the AgentID fallback to reach agentctl", recorded)
}
}
func TestManagerExecuteInferenceProfilePromptReportsTheAgentNameOnLookupFailure(t *testing.T) {
harness := newInferenceHarness(t, &stubProfileResolver{profile: &AgentProfileInfo{
ProfileID: "profile-1",
AgentID: "6a2b0f5c-6b9b-4f0e-8f3b-1f2c3d4e5f60",
AgentName: "not-registered",
}})
_, err := harness.manager.ExecuteInferenceProfilePrompt(t.Context(), "session-1", "profile-1", "hello")
// The UUID is meaningless to anyone reading the error; name the agent.
if err == nil || !strings.Contains(err.Error(), `agent "not-registered" does not support inference`) {
t.Fatalf("error = %v", err)
}
}
func TestManagerListInferenceAgentsOnlyReturnsInstalledOnes(t *testing.T) {
installed := newInferenceAgent("inference-installed", true, true)
missing := newInferenceAgent("inference-missing", true, false)