5 Commits
Author SHA1 Message Date
viettranx 284c9cb2cc feat(tts): per-tenant TTS config and per-agent voice override
- Add per-tenant TTS configuration endpoints (GET/POST /v1/tts/config)
  with RoleAdmin auth instead of master scope
- Implement TenantTTSResolver for channels to use tenant-specific
  TTS providers and auto mode settings
- Add per-agent voice override in channel TTS auto-apply:
  - Extend OutboundMessage with AgentID and AgentOtherConfig
  - Inject AgentAudioSnapshot in dispatch.go from outbound message
  - MaybeApply reads tts_voice_id/tts_model_id from agent context
- Fix events.go to use RunContext.TenantID directly (H3)
- Fix dispatch.go error notification to use sendCtx (H1)
- Update UI to use new /v1/tts/config endpoints
2026-04-16 21:26:45 +07:00
viettranx 4b27a337d1 test(router): pin stale raw-UUID entry eviction on Get
Pin that a pre-hardening fragmented entry written under the raw UUID
cache key (tenantID:<uuidStr>) is still evicted by the TTL branch in
Router.Get when a UUID-form caller arrives after TTL expiry. The test
synthesizes the fragmented entry directly via a test-only map write,
then asserts the subsequent Get evicts the raw-UUID entry, re-invokes
the resolver once, and writes the canonical tenantID:agentKey entry —
leaving no fragmented entries behind.
2026-04-11 21:22:23 +07:00
viettranx 7799a9c88a fix(router): evict stale canonical entry on double-check TTL miss
Router.Get's canonical double-check branch trusted any existing entry
under the canonical key without re-checking TTL. If an earlier
agent_key caller wrote the entry and the TTL expired, a later UUID-form
caller would resolve fresh, hit the canonical branch, find the stale
entry, and return it — indefinitely — because the raw-UUID key was
never the map key and the initial-miss eviction branch did nothing.

Re-check TTL inside the double-check branch and evict+rewrite when
stale. Regression test primes a canonical entry with cachedAt set
2×TTL in the past, then asserts a UUID-form Get re-invokes the
resolver and the returned agent reflects the fresh resolver output.
2026-04-11 21:22:23 +07:00
viettranx c8ebe9a789 refactor(comments): remove plan/phase refs from agent identity hardening code
Rewrite inline comments added during the agent identity hardening so
they explain the code as it stands today, rather than tying to internal
plan terminology (phase numbers, FR/NFR/H/M/C codes, PR references,
trap zone labels). Commit history already carries the plan archaeology.
Comments now keep the non-obvious invariants (cache boundaries, bypass
gaps, silent-nil traps, dual-tenant semantics) and drop the scaffolding.
Comment-only — no runtime behavior change.
2026-04-11 21:22:23 +07:00
viettranx c599387248 fix(router): canonicalize cache key, exact-segment match, tenant-aware IsRunning
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).
2026-04-11 21:22:23 +07:00