Three related fixes to agent router identity handling:
1. Canonicalize on resolve — Router.Get now stores entries under the
canonical tenantID:agent_key after a successful resolver call,
regardless of whether the caller passed a UUID or agent_key. Prevents
fragmentation where the same logical agent would occupy two cache
slots. Callers that pass the UUID form are now un-cached and resolve
on every call; all production callers pass agent_key today.
2. Exact-segment match — matchAgentCacheKey helper replaces HasSuffix in
Router.Remove and Router.InvalidateAgent. Prevents substring collisions
like "tenantX:sub-foo" being wiped when invalidating "foo". Rejects
empty agentKey to guard against wildcard wipes.
3. Tenant-scoped IsRunning (C6) — Router.IsRunning now accepts ctx and
uses agentCacheKey for lookup. Pre-fix, the bare r.agents[agentID]
lookup always returned false in tenant-scoped deployments, causing
agents.list to report every live agent as idle.
Updates the sole caller at gateway/methods/agents.go:134.
Phase 2 of agent identity hardening (TD-3).
Session keys must use agentKey (human-readable, e.g. "default") not UUID.
Cache invalidation via InvalidateAgent(agentKey) only matches agentKey suffix,
so UUID-keyed entries were never cleared — causing stale provider/model after
agent updates from the web UI.
- heartbeat: use agentKey (already resolved) instead of UUID for session key
and HasActiveSessionsForAgent check
- cron: resolve agentKey from UUID via agentStore before building session key
- agents_update: also invalidate by UUID as safety net for legacy entries
- startup migration: auto-fix existing UUID-based session keys in DB
- tests: 7 new tests covering session key format and cache invalidation