You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ia): campaign match routes to configured agent_id
Motivation
Ads that land on WhatsApp with a keyword ("Quero saber mais sobre marketing para incorporadoras") already make the contact AI-eligible via campanhas_whatsapp. The schema even accepts agent_id, but the comment in lib/ai/elegibilidade/campanha.ts is explicit: RESERVADO — ainda NÃO roteado. After a match, pos-entrada only stamps ai_authorized_reason=campanha:<id>; resolve-turn-agent still picks the session’s published agent / intent router. Multi-niche tenants (clínica + imobiliária on one inbox) get the wrong brain on the first turn.
This is declared product debt (J20 journey map) with a finished match engine — only the last mile is missing. No open issue owns it (dup-check CLEAN).
Proposed from code analysis only (bulk dogfooding skipped).
Discussion Prompt para configuração #673 asks for niche config prompts — routing by campaign is the runtime half of that story
Competitive Analysis
Kommo/Octadesk: campaign/source → bot or pipeline routing is standard in WhatsApp sales CRMs
Deskcomm: eligibility without agent binding
Proposed Solution
When a campaign match includes agent_id, the first (and sticky) AI turn for that conversation uses that published agent, without weakening the allowlist gate.
Behaviour:
Match payload: extend authorization / dispatch metadata to carry campaign_agent_id when casada.agent_id is set (conversation row column orevent_log / dispatch payload — prefer setting conversations.active_ai_agent_id when null/sticky-compatible, documented in code comments).
resolve-turn-agent: new early rule — if conversation has a campaign-bound agent id that still has a published version and is allowed for the org, return that config with a distinct outcome e.g. 'campaign' (add to outcome union + telemetry). Do not skip eligibility; this runs only after the contact is already authorized.
Invalid agent_id: if UUID points at missing/unpublished agent, log.warn and fall through to existing router/session behaviour (fail open to current path, fail closed on eligibility).
Settings UI: Settings › Atendimento — list/add/edit/delete entries in organizations.settings.campanhas_whatsapp (id, label, match tipo/valor, optional agent select from published agents, optional channel_session). Validate with the same zod schema. Admin role.
API:GET/PUT /api/v1/settings/campanhas-whatsapp merging jsonb without wiping sibling settings keys.
Tests: unit for resolve-turn campaign outcome; pos-entrada sets sticky/agent when agent_id present; API validation rejects bad UUIDs; existing campanha tests stay green.
Docs: add J20.x row — campaign with agent_id → that agent answers first turn.
Out of scope: building a visual ads creative editor; changing allowlist semantics; per-message re-routing after sticky intent changes (intent router may still reclassify later — document that campaign bind is initial sticky, not a permanent lock, unless product prefers lock: default initial sticky, reclassify allowed).
Success: configure campaign match + agent_id for “implantes”; inbound keyword authorizes contact; first AI reply uses that agent’s prompt/tools, not the default session agent.
Approach
Complexity M. Mostly wiring + UI; no new table required if sticky column reused.
1. pos-entrada
After casarCampanha, if agent_id, persist onto conversation (active_ai_agent_id) under the same best-effort try/catch (despacho still proceeds on failure).
2. resolve-turn-agent
Insert campaign/sticky-from-campaign branch with tests covering: published agent hit; unpublished miss; no agent_id unchanged.
3. Settings surface
New form component; keep file under soft line budget; reuse published-agent list hooks if they exist under hooks/ai.
4. Journey map
Honest J20 addition with UNIT proof commands.
PR strategy
Title: feat(ia): campanha com agent_id escolhe o agente do turno
Proposal lane Ideas discussion first; PR after acceptance
AI disclosure per project norms
Happy to open a PR if this direction works for you.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Proposal
feat(ia): campaign match routes to configured agent_id
Motivation
Ads that land on WhatsApp with a keyword ("Quero saber mais sobre marketing para incorporadoras") already make the contact AI-eligible via
campanhas_whatsapp. The schema even acceptsagent_id, but the comment inlib/ai/elegibilidade/campanha.tsis explicit: RESERVADO — ainda NÃO roteado. After a match,pos-entradaonly stampsai_authorized_reason=campanha:<id>;resolve-turn-agentstill picks the session’s published agent / intent router. Multi-niche tenants (clínica + imobiliária on one inbox) get the wrong brain on the first turn.This is declared product debt (J20 journey map) with a finished match engine — only the last mile is missing. No open issue owns it (dup-check CLEAN).
Proposed from code analysis only (bulk dogfooding skipped).
Research Summary
General Project Dogfooding
Feature-Specific Validation
casarCampanha+lerCampanhasunit-tested (J20.8 / J20.9)avaliarCampanhainpos-entrada.tsauthorizes contact whenai_gate=allowlistagent_idoptional on zod schema; zero call sites read it for routingcampanhas_whatsappdoes not exist (“a tela ainda não existe”)Related Issues & Community Signals
Competitive Analysis
Proposed Solution
When a campaign match includes
agent_id, the first (and sticky) AI turn for that conversation uses that published agent, without weakening the allowlist gate.Behaviour:
campaign_agent_idwhencasada.agent_idis set (conversation row column orevent_log/ dispatch payload — prefer settingconversations.active_ai_agent_idwhen null/sticky-compatible, documented in code comments).resolve-turn-agent: new early rule — if conversation has a campaign-bound agent id that still has a published version and is allowed for the org, return that config with a distinct outcome e.g.'campaign'(add to outcome union + telemetry). Do not skip eligibility; this runs only after the contact is already authorized.organizations.settings.campanhas_whatsapp(id, label, match tipo/valor, optional agent select from published agents, optional channel_session). Validate with the same zod schema. Admin role.GET/PUT /api/v1/settings/campanhas-whatsappmerging jsonb without wiping sibling settings keys..changes/campanha-roteia-agente.md.Out of scope: building a visual ads creative editor; changing allowlist semantics; per-message re-routing after sticky intent changes (intent router may still reclassify later — document that campaign bind is initial sticky, not a permanent lock, unless product prefers lock: default initial sticky, reclassify allowed).
Success: configure campaign match + agent_id for “implantes”; inbound keyword authorizes contact; first AI reply uses that agent’s prompt/tools, not the default session agent.
Approach
Complexity M. Mostly wiring + UI; no new table required if sticky column reused.
1. pos-entrada
After
casarCampanha, ifagent_id, persist onto conversation (active_ai_agent_id) under the same best-effort try/catch (despacho still proceeds on failure).2. resolve-turn-agent
Insert campaign/sticky-from-campaign branch with tests covering: published agent hit; unpublished miss; no agent_id unchanged.
3. Settings surface
New form component; keep file under soft line budget; reuse published-agent list hooks if they exist under
hooks/ai.4. Journey map
Honest J20 addition with UNIT proof commands.
PR strategy
feat(ia): campanha com agent_id escolhe o agente do turnoHappy to open a PR if this direction works for you.
AI was used for assistance.
All reactions