feat(kb): per-agent hf-token via HostAPI.GetAgentSecret

Phase 4c. Each KB tool call now picks the right KBClient based on the
calling agent: per-agent hf-token (read via HostAPI.GetAgentSecret)
takes priority, plugin-level Config.APIKey is the fallback for cases
where the agent has no per-agent token set.

Behaviour:
  - Agent has hf-token in secrets.enc → use that, KB calls run with
    the agent's own HF role
  - Agent has no token but plugin-level apiKey is set → fall back
  - Neither configured → clear "HF KB backend unavailable" error
    surfaces to the model (no silent 401 chain)

  internal/tools/kb.go:
    - KBDeps.AgentClient(ctx, agentID) callback resolves per-agent
      client; nil result means "no per-agent token, try fallback"
    - clientForCall picks the right client per tool invocation
    - List* tools resolve agentID from sdkplugin.AgentIDFromContext
    - Cache tool already had agentID from the dispatch shim

  cmd/.../main.go: wires AgentClient closure — host.GetAgentSecret
    for "hf-token", kbclient.New(BackendURL, token) if non-empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-06-06 00:32:41 +01:00
parent 9e24c52ae5
commit 1c737bb21c
2 changed files with 59 additions and 12 deletions

View File

@@ -207,6 +207,19 @@ func (p *harborForgePlugin) Init(ctx context.Context, host sdkplugin.HostAPI) er
return ""
},
Turn: func(agentID string) int { return 0 }, // best-effort; turn ctx not available to plugins yet
AgentClient: func(ctx context.Context, agentID string) (*kbclient.Client, error) {
if agentID == "" {
return nil, nil
}
token, err := host.GetAgentSecret(ctx, agentID, "hf-token")
if err != nil {
return nil, err
}
if token == "" {
return nil, nil
}
return kbclient.New(p.cfg.BackendURL, token), nil
},
},
}