+
+
\ No newline at end of file
diff --git a/agent_framework_oci/Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md b/agent_framework_oci/Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md
new file mode 100644
index 0000000..79d8405
--- /dev/null
+++ b/agent_framework_oci/Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md
@@ -0,0 +1,64 @@
+# Implementação — workflows transacionais determinísticos
+
+## Entrega
+
+Foi adicionada ao `agent_framework_oci` uma capacidade opcional para executar transações multi-etapas como workflows determinísticos compilados em LangGraph.
+
+### Módulo novo
+
+`libs/agent_framework/src/agent_framework/workflows/`
+
+- `models.py`: contratos Pydantic e validação estrutural;
+- `repository.py`: resolução de versão ativa e leitura de YAML imutável;
+- `registry.py`: registro desacoplado de actions sync/async;
+- `runtime.py`: compilação, cache e execução do StateGraph;
+- `tool_executor.py`: integração com a política da tool;
+- `__init__.py`: API pública.
+
+### Política expandida
+
+`ToolPolicy` agora aceita:
+
+```yaml
+execution:
+ mode: direct_tool | workflow | agent
+ workflow: nome_do_workflow
+ version: active | 1
+```
+
+O default permanece `direct_tool`, preservando compatibilidade.
+
+### Configuração
+
+Foram adicionados:
+
+- `ENABLE_TRANSACTIONAL_WORKFLOWS=false`;
+- `WORKFLOWS_PATH=./workflows`.
+
+### Template
+
+Inclui um exemplo completo de devolução de pedido com:
+
+- confirmação e campos obrigatórios pela política;
+- workflow YAML versionado;
+- actions de domínio no backend;
+- bifurcação determinística baseada no resultado da validação.
+
+## Validação realizada
+
+- `tests/unit/test_tool_policies.py`: 4 testes aprovados;
+- compilação Python de framework, template e novos testes: aprovada;
+- o teste funcional novo do LangGraph foi criado, mas não pôde ser executado neste container porque `langgraph` não está instalado no ambiente. A dependência já está declarada no `pyproject.toml` do framework.
+
+## Escopo e segurança
+
+Esta entrega cria o motor e a integração de política. Para operações críticas em produção ainda é necessário conectar:
+
+- execution store persistente;
+- idempotência de negócio nas actions/APIs;
+- autorização por escopo;
+- telemetria IC/NOC específica de workflow;
+- compensação/Saga quando aplicável;
+- estratégia corporativa de timeout e retry.
+
+Esses itens foram explicitamente documentados para evitar a falsa impressão de que retry por si só garante segurança transacional.
diff --git a/agent_framework_oci/Documentacao/Implementando_Basic_Auth.md b/agent_framework_oci/Documentacao/Implementando_Basic_Auth.md
new file mode 100644
index 0000000..d6ff282
--- /dev/null
+++ b/agent_framework_oci/Documentacao/Implementando_Basic_Auth.md
@@ -0,0 +1,987 @@
+# Implementando Basic Auth
+
+Para validar **todo o circuito com Basic Auth**, você precisa configurar três relações distintas:
+
+```text
+Cliente de teste
+ └─ Basic Auth A ─► Agent Gateway :8010
+ └─ Basic Auth B ─► Agent Backend :8000
+ └─ Basic Auth C ─► MCP Gateway :8300
+```
+
+Há um detalhe importante: no pacote atual, a autenticação Basic já funciona para chamadas **de entrada**, mas os clientes internos ainda não enviam Basic Auth:
+
+* `Agent Gateway → Agent Backend` não envia credencial;
+* `Agent Backend → MCP Gateway` envia apenas Bearer Token.
+
+Portanto, para testar o circuito inteiro com Basic Auth, faça os dois pequenos ajustes de código descritos abaixo.
+
+---
+
+# 1. Preparar o ambiente
+
+Considere que o ZIP foi extraído em:
+
+```bash
+cd agent_framework_oci_authentication_v2_1
+```
+
+Crie um único ambiente virtual para facilitar o teste:
+
+```bash
+python -m venv .venv
+source .venv/bin/activate
+```
+
+No Windows PowerShell:
+
+```powershell
+python -m venv .venv
+.\.venv\Scripts\Activate.ps1
+```
+
+Instale o framework e as dependências dos três componentes:
+
+```bash
+pip install -U pip
+
+pip install -e ./libs/agent_framework
+
+pip install \
+ -r ./Tuning-Performance/Authentication/agent_template_backend_authentication/requirements.txt \
+ -r ./apps/agent_gateway/requirements.txt \
+ -r ./apps/mcp_gateway/requirements.txt
+```
+
+Confirme a importação:
+
+```bash
+python -c "from agent_framework.security import install_authentication; print('framework ok')"
+```
+
+---
+
+# 2. Criar três pares de Client ID e Secret
+
+Use credenciais diferentes para cada trecho. Para teste local:
+
+| Fluxo | Client ID | Secret de teste |
+| ----------------------- | -------------------- | --------------------------- |
+| Cliente → Agent Gateway | `tia-test` | `TiaGateway-Test-2026!` |
+| Agent Gateway → Backend | `agent-gateway-test` | `GatewayBackend-Test-2026!` |
+| Backend → MCP Gateway | `agent-backend-test` | `BackendMcp-Test-2026!` |
+
+Esses valores são apenas para ambiente local. Não os reutilize em produção.
+
+## Gerar os hashes
+
+O script está em:
+
+```text
+Tuning-Performance/Authentication/
+ agent_template_backend_authentication/
+ scripts/generate_secret_hash.py
+```
+
+Execute:
+
+```bash
+python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \
+ --secret 'TiaGateway-Test-2026!'
+```
+
+Depois:
+
+```bash
+python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \
+ --secret 'GatewayBackend-Test-2026!'
+```
+
+E:
+
+```bash
+python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \
+ --secret 'BackendMcp-Test-2026!'
+```
+
+Você receberá três valores semelhantes a:
+
+```text
+pbkdf2_sha256:310000::
+```
+
+Guarde-os temporariamente:
+
+```bash
+HASH_CLIENT_GATEWAY='pbkdf2_sha256:310000:...'
+HASH_GATEWAY_BACKEND='pbkdf2_sha256:310000:...'
+HASH_BACKEND_MCP='pbkdf2_sha256:310000:...'
+```
+
+O hash muda a cada execução porque o salt é aleatório. Isso é esperado.
+
+---
+
+# 3. Configurar o Agent Gateway
+
+Entre no diretório:
+
+```bash
+cd apps/agent_gateway
+```
+
+Copie o exemplo:
+
+```bash
+cp .env.example .env
+```
+
+Adicione ao final do `.env`:
+
+```env
+# Entrada: cliente/TIA -> Agent Gateway
+AGENT_GATEWAY_AUTH_ENABLED=true
+AGENT_GATEWAY_AUTH_MODE=basic
+AGENT_GATEWAY_AUTH_BASIC_CLIENT_ID=tia-test
+AGENT_GATEWAY_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_CLIENT_GATEWAY
+AGENT_GATEWAY_AUTH_BASIC_REALM=agent-gateway
+
+AGENT_GATEWAY_AUTH_PUBLIC_PATHS=/health,/docs,/openapi.json,/redoc
+AGENT_GATEWAY_AUTH_PUBLIC_PREFIXES=
+
+# Saída: Agent Gateway -> Agent Backend
+BACKEND_AUTH_MODE=basic
+BACKEND_AUTH_CLIENT_ID=agent-gateway-test
+BACKEND_AUTH_SECRET=GatewayBackend-Test-2026!
+```
+
+Não coloque aspas no `.env`:
+
+```env
+BACKEND_AUTH_SECRET=GatewayBackend-Test-2026!
+```
+
+O arquivo de backends já aponta o backend Contas para:
+
+```yaml
+contas:
+ url: http://localhost:8000
+```
+
+Arquivo:
+
+```text
+apps/agent_gateway/config/backends.yaml
+```
+
+Para este teste, mantenha apenas o backend `contas` ou force o backend no payload. Caso contrário, pedidos sobre ofertas e suporte podem ser roteados para portas em que nenhum backend está rodando.
+
+---
+
+# 4. Fazer o Agent Gateway enviar Basic Auth ao backend
+
+Abra:
+
+```text
+libs/agent_framework/src/agent_framework/global_supervisor/client.py
+```
+
+Substitua a classe `BackendClient` por uma versão que aceite autenticação Basic.
+
+No início do arquivo, adicione:
+
+```python
+import os
+```
+
+Altere o construtor:
+
+```python
+class BackendClient:
+ def __init__(
+ self,
+ timeout_seconds: float = 120.0,
+ basic_client_id: str | None = None,
+ basic_secret: str | None = None,
+ ):
+ self.timeout_seconds = timeout_seconds
+ self.basic_client_id = basic_client_id
+ self.basic_secret = basic_secret
+
+ def _auth(self) -> httpx.BasicAuth | None:
+ if self.basic_client_id and self.basic_secret:
+ return httpx.BasicAuth(
+ username=self.basic_client_id,
+ password=self.basic_secret,
+ )
+ return None
+```
+
+No método `call_message`, troque:
+
+```python
+resp = await client.post(url, json=payload)
+```
+
+por:
+
+```python
+resp = await client.post(
+ url,
+ json=payload,
+ auth=self._auth(),
+)
+```
+
+No método `health`, você pode manter `/health` público. Caso queira enviar autenticação também, use:
+
+```python
+resp = await client.get(url, auth=self._auth())
+```
+
+Agora abra:
+
+```text
+apps/agent_gateway/app/main.py
+```
+
+Adicione:
+
+```python
+import os
+```
+
+Troque:
+
+```python
+backend_client = BackendClient(
+ timeout_seconds=settings.BACKEND_TIMEOUT_SECONDS
+)
+```
+
+por:
+
+```python
+backend_client = BackendClient(
+ timeout_seconds=settings.BACKEND_TIMEOUT_SECONDS,
+ basic_client_id=os.getenv("BACKEND_AUTH_CLIENT_ID"),
+ basic_secret=os.getenv("BACKEND_AUTH_SECRET"),
+)
+```
+
+Isso implementa:
+
+```text
+Agent Gateway → Agent Backend
+Authorization: Basic base64(agent-gateway-test:GatewayBackend-Test-2026!)
+```
+
+---
+
+# 5. Configurar o Agent Backend autenticado
+
+Entre no diretório:
+
+```bash
+cd Tuning-Performance/Authentication/agent_template_backend_authentication
+```
+
+Copie o exemplo:
+
+```bash
+cp .env.example .env
+```
+
+Ajuste a seção de autenticação:
+
+```env
+# Entrada: Agent Gateway -> Agent Backend
+AGENT_AUTH_ENABLED=true
+AGENT_AUTH_MODE=basic
+AGENT_AUTH_BASIC_CLIENT_ID=agent-gateway-test
+AGENT_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_GATEWAY_BACKEND
+AGENT_AUTH_BASIC_REALM=agent-contas
+
+AGENT_AUTH_PUBLIC_PATHS=/health,/docs,/openapi.json,/redoc
+AGENT_AUTH_PUBLIC_PREFIXES=
+```
+
+Para usar o MCP Gateway:
+
+```env
+MCP_GATEWAY_ENABLED=true
+MCP_GATEWAY_URL=http://localhost:8300
+MCP_GATEWAY_TIMEOUT_SECONDS=60
+
+# Saída: Agent Backend -> MCP Gateway
+MCP_GATEWAY_AUTH_MODE=basic
+MCP_GATEWAY_BASIC_CLIENT_ID=agent-backend-test
+MCP_GATEWAY_BASIC_SECRET=BackendMcp-Test-2026!
+```
+
+Para evitar dependências externas durante o primeiro teste, configure também:
+
+```env
+LLM_PROVIDER=mock
+ENABLE_LANGFUSE=false
+ENABLE_ANALYTICS=false
+
+SESSION_REPOSITORY_PROVIDER=memory
+MEMORY_REPOSITORY_PROVIDER=memory
+CHECKPOINT_REPOSITORY_PROVIDER=memory
+CACHE_PROVIDER=memory
+USAGE_REPOSITORY_PROVIDER=memory
+```
+
+Os nomes exatos de alguns providers podem depender do arquivo de configuração atual do framework. Caso o `.env.example` já contenha valores locais ou mock, preserve-os.
+
+---
+
+# 6. Fazer o Backend enviar Basic Auth ao MCP Gateway
+
+Abra:
+
+```text
+libs/agent_framework/src/agent_framework/gateways/mcp_gateway_client.py
+```
+
+Substitua a implementação por:
+
+```python
+from __future__ import annotations
+
+import base64
+from typing import Any
+
+import httpx
+
+
+class MCPGatewayClient:
+ def __init__(
+ self,
+ base_url: str,
+ token: str | None = None,
+ timeout_seconds: int = 60,
+ auth_mode: str | None = None,
+ basic_client_id: str | None = None,
+ basic_secret: str | None = None,
+ ):
+ self.base_url = base_url.rstrip("/")
+ self.token = token
+ self.timeout_seconds = timeout_seconds
+ self.auth_mode = (auth_mode or "").strip().lower()
+ self.basic_client_id = basic_client_id
+ self.basic_secret = basic_secret
+
+ def _headers(self) -> dict[str, str]:
+ if (
+ self.auth_mode == "basic"
+ and self.basic_client_id
+ and self.basic_secret
+ ):
+ raw = f"{self.basic_client_id}:{self.basic_secret}".encode("utf-8")
+ encoded = base64.b64encode(raw).decode("ascii")
+ return {"Authorization": f"Basic {encoded}"}
+
+ if self.token:
+ return {"Authorization": f"Bearer {self.token}"}
+
+ return {}
+
+ async def list_tools(self) -> dict[str, Any]:
+ async with httpx.AsyncClient(
+ timeout=self.timeout_seconds
+ ) as client:
+ response = await client.get(
+ f"{self.base_url}/v1/tools",
+ headers=self._headers(),
+ )
+ response.raise_for_status()
+ return response.json()
+
+ async def invoke_tool(
+ self,
+ *,
+ tenant_id: str,
+ agent_id: str,
+ channel: str | None,
+ tool_name: str,
+ arguments: dict[str, Any] | None = None,
+ business_context: dict[str, Any] | None = None,
+ metadata: dict[str, Any] | None = None,
+ ) -> dict[str, Any]:
+ payload = {
+ "tenant_id": tenant_id,
+ "agent_id": agent_id,
+ "channel": channel,
+ "tool_name": tool_name,
+ "arguments": arguments or {},
+ "business_context": business_context or {},
+ "metadata": metadata or {},
+ }
+
+ async with httpx.AsyncClient(
+ timeout=self.timeout_seconds
+ ) as client:
+ response = await client.post(
+ f"{self.base_url}/v1/tools/{tool_name}/invoke",
+ json=payload,
+ headers=self._headers(),
+ )
+ response.raise_for_status()
+ return response.json()
+```
+
+Agora abra:
+
+```text
+libs/agent_framework/src/agent_framework/mcp/tool_router.py
+```
+
+Localize:
+
+```python
+MCPGatewayClient(
+ base_url=getattr(
+ settings,
+ "MCP_GATEWAY_URL",
+ "http://localhost:8300",
+ ),
+ token=getattr(settings, "MCP_GATEWAY_TOKEN", None),
+ timeout_seconds=getattr(
+ settings,
+ "MCP_GATEWAY_TIMEOUT_SECONDS",
+ settings.MCP_TOOL_TIMEOUT_SECONDS,
+ ),
+)
+```
+
+Altere para:
+
+```python
+MCPGatewayClient(
+ base_url=getattr(
+ settings,
+ "MCP_GATEWAY_URL",
+ "http://localhost:8300",
+ ),
+ token=getattr(settings, "MCP_GATEWAY_TOKEN", None),
+ timeout_seconds=getattr(
+ settings,
+ "MCP_GATEWAY_TIMEOUT_SECONDS",
+ settings.MCP_TOOL_TIMEOUT_SECONDS,
+ ),
+ auth_mode=getattr(
+ settings,
+ "MCP_GATEWAY_AUTH_MODE",
+ None,
+ ),
+ basic_client_id=getattr(
+ settings,
+ "MCP_GATEWAY_BASIC_CLIENT_ID",
+ None,
+ ),
+ basic_secret=getattr(
+ settings,
+ "MCP_GATEWAY_BASIC_SECRET",
+ None,
+ ),
+)
+```
+
+Adicione estes campos em:
+
+```text
+libs/agent_framework/src/agent_framework/config/settings.py
+```
+
+Próximo das configurações existentes de MCP Gateway:
+
+```python
+MCP_GATEWAY_AUTH_MODE: str | None = None
+MCP_GATEWAY_BASIC_CLIENT_ID: str | None = None
+MCP_GATEWAY_BASIC_SECRET: str | None = None
+```
+
+Há também uma factory local em:
+
+```text
+Tuning-Performance/Authentication/
+ agent_template_backend_authentication/
+ app/mcp_gateway_client_factory.py
+```
+
+Ajuste para:
+
+```python
+from __future__ import annotations
+
+import os
+
+from agent_framework.gateways import MCPGatewayClient
+
+
+def build_mcp_gateway_client() -> MCPGatewayClient | None:
+ if os.getenv("MCP_GATEWAY_ENABLED", "true").lower() != "true":
+ return None
+
+ return MCPGatewayClient(
+ base_url=os.getenv(
+ "MCP_GATEWAY_URL",
+ "http://localhost:8300",
+ ),
+ token=os.getenv("MCP_GATEWAY_TOKEN") or None,
+ timeout_seconds=int(
+ os.getenv("MCP_GATEWAY_TIMEOUT_SECONDS", "60")
+ ),
+ auth_mode=os.getenv("MCP_GATEWAY_AUTH_MODE"),
+ basic_client_id=os.getenv(
+ "MCP_GATEWAY_BASIC_CLIENT_ID"
+ ),
+ basic_secret=os.getenv(
+ "MCP_GATEWAY_BASIC_SECRET"
+ ),
+ )
+```
+
+---
+
+# 7. Configurar o MCP Gateway
+
+Entre no diretório:
+
+```bash
+cd apps/mcp_gateway
+```
+
+Crie `.env`:
+
+```bash
+cp .env.example .env
+```
+
+Adicione:
+
+```env
+# Entrada: Agent Backend -> MCP Gateway
+MCP_GATEWAY_AUTH_ENABLED=true
+MCP_GATEWAY_AUTH_MODE=basic
+MCP_GATEWAY_AUTH_BASIC_CLIENT_ID=agent-backend-test
+MCP_GATEWAY_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_BACKEND_MCP
+MCP_GATEWAY_AUTH_BASIC_REALM=mcp-gateway
+
+MCP_GATEWAY_AUTH_PUBLIC_PATHS=/health,/ready,/docs,/openapi.json,/redoc
+MCP_GATEWAY_AUTH_PUBLIC_PREFIXES=
+
+MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml
+```
+
+## Desabilitar o mecanismo Bearer legado
+
+O MCP Gateway ainda possui um segundo mecanismo antigo, configurado dentro de:
+
+```text
+apps/mcp_gateway/config/mcp_gateway.yaml
+```
+
+Localize a seção:
+
+```yaml
+auth:
+ enabled: true
+```
+
+Altere para:
+
+```yaml
+auth:
+ enabled: false
+```
+
+Isso é necessário porque o novo middleware já faz a autenticação Basic. Caso o `auth_check()` legado continue habilitado, a requisição passará pelo Basic e depois será rejeitada por não possuir Bearer Token.
+
+---
+
+# 8. Subir os componentes
+
+Use quatro terminais.
+
+## Terminal 1 — MCP Servers
+
+O MCP Gateway precisa ter pelo menos um servidor MCP disponível para demonstrar uma chamada real.
+
+Na raiz do projeto:
+
+```bash
+source .venv/bin/activate
+```
+
+Suba o servidor telecom:
+
+```bash
+uvicorn mcp.servers.telecom_mcp_server.main:app \
+ --host 0.0.0.0 \
+ --port 8100 \
+ --reload
+```
+
+Em outro terminal, caso queira também o retail:
+
+```bash
+uvicorn mcp.servers.retail_mcp_server.main:app \
+ --host 0.0.0.0 \
+ --port 8200 \
+ --reload
+```
+
+Confira as URLs configuradas em:
+
+```text
+apps/mcp_gateway/config/mcp_gateway.yaml
+```
+
+Para execução local, devem apontar para:
+
+```yaml
+url: http://localhost:8100
+```
+
+e:
+
+```yaml
+url: http://localhost:8200
+```
+
+---
+
+## Terminal 2 — MCP Gateway
+
+```bash
+cd apps/mcp_gateway
+source ../../.venv/bin/activate
+```
+
+Suba usando `--env-file`. Isso é importante porque o middleware lê variáveis com `os.getenv()`:
+
+```bash
+uvicorn app.main:app \
+ --host 0.0.0.0 \
+ --port 8300 \
+ --reload \
+ --env-file .env
+```
+
+Teste a saúde pública:
+
+```bash
+curl http://localhost:8300/health
+```
+
+Teste um endpoint protegido sem credencial:
+
+```bash
+curl -i http://localhost:8300/v1/tools
+```
+
+Esperado:
+
+```text
+HTTP/1.1 401 Unauthorized
+```
+
+Teste com Basic Auth:
+
+```bash
+curl -i \
+ -u 'agent-backend-test:BackendMcp-Test-2026!' \
+ http://localhost:8300/v1/tools
+```
+
+Esperado:
+
+```text
+HTTP/1.1 200 OK
+```
+
+---
+
+## Terminal 3 — Agent Backend
+
+```bash
+cd Tuning-Performance/Authentication/agent_template_backend_authentication
+source ../../../.venv/bin/activate
+```
+
+Suba:
+
+```bash
+uvicorn app.main:app \
+ --host 0.0.0.0 \
+ --port 8000 \
+ --reload \
+ --env-file .env
+```
+
+Teste saúde:
+
+```bash
+curl http://localhost:8000/health
+```
+
+Teste endpoint protegido sem credencial:
+
+```bash
+curl -i http://localhost:8000/agents
+```
+
+Esperado:
+
+```text
+HTTP/1.1 401 Unauthorized
+```
+
+Teste com a credencial usada pelo Agent Gateway:
+
+```bash
+curl -i \
+ -u 'agent-gateway-test:GatewayBackend-Test-2026!' \
+ http://localhost:8000/agents
+```
+
+Esperado:
+
+```text
+HTTP/1.1 200 OK
+```
+
+Teste mensagem diretamente:
+
+```bash
+curl -X POST http://localhost:8000/gateway/message \
+ -u 'agent-gateway-test:GatewayBackend-Test-2026!' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "channel": "web",
+ "agent_id": "telecom_contas",
+ "tenant_id": "default",
+ "payload": {
+ "text": "Quero consultar minha fatura",
+ "session_id": "teste-backend-001",
+ "user_id": "user-001",
+ "customer_id": "12345",
+ "message_id": "msg-001"
+ }
+ }'
+```
+
+---
+
+## Terminal 4 — Agent Gateway
+
+```bash
+cd apps/agent_gateway
+source ../../.venv/bin/activate
+```
+
+Suba:
+
+```bash
+uvicorn app.main:app \
+ --host 0.0.0.0 \
+ --port 8010 \
+ --reload \
+ --env-file .env
+```
+
+Teste saúde:
+
+```bash
+curl http://localhost:8010/health
+```
+
+Teste endpoint protegido sem credencial:
+
+```bash
+curl -i http://localhost:8010/backends
+```
+
+Esperado:
+
+```text
+HTTP/1.1 401 Unauthorized
+```
+
+Teste com a credencial externa:
+
+```bash
+curl -i \
+ -u 'tia-test:TiaGateway-Test-2026!' \
+ http://localhost:8010/backends
+```
+
+Esperado:
+
+```text
+HTTP/1.1 200 OK
+```
+
+---
+
+# 9. Validar o circuito completo
+
+Force o backend `contas` para evitar que o roteador selecione um backend não iniciado:
+
+```bash
+curl -X POST http://localhost:8010/gateway/message \
+ -u 'tia-test:TiaGateway-Test-2026!' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "channel": "web",
+ "backend_id": "contas",
+ "tenant_id": "default",
+ "agent_id": "telecom_contas",
+ "session_id": "circuito-basic-001",
+ "payload": {
+ "text": "Quero consultar minha fatura",
+ "session_id": "circuito-basic-001",
+ "user_id": "user-001",
+ "customer_id": "12345",
+ "message_id": "msg-circuito-001"
+ }
+ }'
+```
+
+O circuito esperado é:
+
+```text
+curl
+ │ Basic tia-test
+ ▼
+Agent Gateway :8010
+ │ Basic agent-gateway-test
+ ▼
+Agent Backend :8000
+ │ Basic agent-backend-test
+ ▼
+MCP Gateway :8300
+ ▼
+MCP Server :8100 ou :8200
+```
+
+---
+
+# 10. Como comprovar cada autenticação
+
+Faça testes negativos em cada trecho.
+
+## Secret externo incorreto
+
+```bash
+curl -i \
+ -u 'tia-test:senha-errada' \
+ http://localhost:8010/backends
+```
+
+Resultado esperado:
+
+```text
+401 Unauthorized
+```
+
+## Secret do gateway para backend incorreto
+
+Altere temporariamente no `apps/agent_gateway/.env`:
+
+```env
+BACKEND_AUTH_SECRET=senha-errada
+```
+
+Reinicie o Agent Gateway e envie uma mensagem.
+
+O gateway deverá retornar erro de backend, normalmente:
+
+```text
+502 Bad Gateway
+```
+
+O erro interno será originado por um:
+
+```text
+401 Unauthorized
+```
+
+do Agent Backend.
+
+## Secret do backend para MCP incorreto
+
+Altere temporariamente:
+
+```env
+MCP_GATEWAY_BASIC_SECRET=senha-errada
+```
+
+Reinicie o backend e execute uma frase que acione uma ferramenta MCP.
+
+O backend deverá registrar falha na chamada ao MCP Gateway com:
+
+```text
+401 Unauthorized
+```
+
+---
+
+# 11. Verificação rápida de portas
+
+No Linux ou WSL:
+
+```bash
+ss -lntp | grep -E ':8000|:8010|:8100|:8200|:8300'
+```
+
+No Windows PowerShell:
+
+```powershell
+Get-NetTCPConnection -State Listen |
+ Where-Object LocalPort -in 8000,8010,8100,8200,8300 |
+ Sort-Object LocalPort
+```
+
+Você deverá ver:
+
+```text
+8000 Agent Backend
+8010 Agent Gateway
+8100 Telecom MCP Server
+8200 Retail MCP Server
+8300 MCP Gateway
+```
+
+## Observação importante
+
+O segredo original precisa existir no componente cliente:
+
+```text
+TIA ou curl:
+ TiaGateway-Test-2026!
+
+Agent Gateway:
+ GatewayBackend-Test-2026!
+
+Agent Backend:
+ BackendMcp-Test-2026!
+```
+
+Os componentes servidores armazenam apenas os hashes:
+
+```text
+Agent Gateway:
+ hash de TiaGateway-Test-2026!
+
+Agent Backend:
+ hash de GatewayBackend-Test-2026!
+
+MCP Gateway:
+ hash de BackendMcp-Test-2026!
+```
+
+Em produção, os segredos originais e hashes devem vir de Vault ou Kubernetes Secret, não de arquivos `.env`.
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py
index e6429c4..7a1a9be 100644
--- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py
@@ -3,5 +3,8 @@ from __future__ import annotations
# Compatibilidade local do template/backend.
# A implementação oficial agora fica no framework para evitar duplicação entre agentes.
from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
+from app.presentation import register_tool_renderers
+
+register_tool_renderers()
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..0c79403
Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..f77c47a
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/tool_renderers.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ return f"[{agent_label}] Fatura consultada: {result}."
+
+
+def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ plano = result.get("plano")
+ if plano is None:
+ return None
+ parts = [f"[{agent_label}] Seu plano é {plano}"]
+ internet_gb = result.get("internet_gb")
+ status = result.get("status")
+ if internet_gb is not None:
+ parts.append(f"com {internet_gb} GB")
+ if status is not None:
+ parts.append(f"status {status}")
+ return ", ".join(parts) + "."
+
+
+def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ status = result.get("status")
+ if order_id is None or status is None:
+ return None
+ lines = [f"[{agent_label}] Pedido {order_id}: status {status}."]
+ total = result.get("valor_total")
+ if total is not None:
+ lines.append(f"Valor total: R$ {_money_brl(total)}.")
+ items = result.get("itens") or []
+ rendered_items: list[str] = []
+ if isinstance(items, list):
+ for item in items:
+ if isinstance(item, dict):
+ value = item.get("descricao") or item.get("nome") or item.get("sku")
+ else:
+ value = item
+ if value not in (None, ""):
+ rendered_items.append(str(value))
+ if rendered_items:
+ lines.append("Itens: " + "; ".join(rendered_items) + ".")
+ return " ".join(lines)
+
+
+def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ transportadora = result.get("transportadora")
+ codigo = result.get("codigo_rastreio")
+ previsao = result.get("previsao_entrega")
+ if any(v is None for v in (order_id, transportadora, codigo, previsao)):
+ return None
+ return (
+ f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, "
+ f"rastreio {codigo}, previsão {previsao}."
+ )
+
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("telecom.invoice", render_telecom_invoice)
+ register_tool_response_renderer("telecom.plan", render_telecom_plan)
+ register_tool_response_renderer("retail.order", render_retail_order)
+ register_tool_response_renderer("retail.delivery", render_retail_delivery)
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml
index 3c4fd05..9b1ab9e 100644
--- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml
index d85fae1..f8a53ca 100644
--- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml
@@ -10,6 +10,9 @@ tools:
- fatura
- conta
- boleto
+ response:
+ mode: renderer
+ renderer: telecom.invoice
consultar_pagamentos:
description: Consulta histórico de pagamentos do cliente.
mcp_server: telecom
@@ -28,6 +31,9 @@ tools:
asset_id: string
selection_keywords:
- plano
+ response:
+ mode: renderer
+ renderer: telecom.plan
listar_servicos:
description: Lista serviços ativos e adicionais VAS.
mcp_server: telecom
@@ -49,6 +55,9 @@ tools:
- consultar pedido
- status do pedido
- pedido
+ response:
+ mode: renderer
+ renderer: retail.order
consultar_entrega:
description: Consulta entrega e rastreamento do pedido.
mcp_server: retail
@@ -61,6 +70,26 @@ tools:
- rastreamento
- transportadora
- previsão
+ response:
+ mode: renderer
+ renderer: retail.delivery
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..009b7c1
Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..f77c47a
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ return f"[{agent_label}] Fatura consultada: {result}."
+
+
+def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ plano = result.get("plano")
+ if plano is None:
+ return None
+ parts = [f"[{agent_label}] Seu plano é {plano}"]
+ internet_gb = result.get("internet_gb")
+ status = result.get("status")
+ if internet_gb is not None:
+ parts.append(f"com {internet_gb} GB")
+ if status is not None:
+ parts.append(f"status {status}")
+ return ", ".join(parts) + "."
+
+
+def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ status = result.get("status")
+ if order_id is None or status is None:
+ return None
+ lines = [f"[{agent_label}] Pedido {order_id}: status {status}."]
+ total = result.get("valor_total")
+ if total is not None:
+ lines.append(f"Valor total: R$ {_money_brl(total)}.")
+ items = result.get("itens") or []
+ rendered_items: list[str] = []
+ if isinstance(items, list):
+ for item in items:
+ if isinstance(item, dict):
+ value = item.get("descricao") or item.get("nome") or item.get("sku")
+ else:
+ value = item
+ if value not in (None, ""):
+ rendered_items.append(str(value))
+ if rendered_items:
+ lines.append("Itens: " + "; ".join(rendered_items) + ".")
+ return " ".join(lines)
+
+
+def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ transportadora = result.get("transportadora")
+ codigo = result.get("codigo_rastreio")
+ previsao = result.get("previsao_entrega")
+ if any(v is None for v in (order_id, transportadora, codigo, previsao)):
+ return None
+ return (
+ f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, "
+ f"rastreio {codigo}, previsão {previsao}."
+ )
+
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("telecom.invoice", render_telecom_invoice)
+ register_tool_response_renderer("telecom.plan", render_telecom_plan)
+ register_tool_response_renderer("retail.order", render_retail_order)
+ register_tool_response_renderer("retail.delivery", render_retail_delivery)
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml
index 3c4fd05..9b1ab9e 100644
--- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml
index d85fae1..f8a53ca 100644
--- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml
@@ -10,6 +10,9 @@ tools:
- fatura
- conta
- boleto
+ response:
+ mode: renderer
+ renderer: telecom.invoice
consultar_pagamentos:
description: Consulta histórico de pagamentos do cliente.
mcp_server: telecom
@@ -28,6 +31,9 @@ tools:
asset_id: string
selection_keywords:
- plano
+ response:
+ mode: renderer
+ renderer: telecom.plan
listar_servicos:
description: Lista serviços ativos e adicionais VAS.
mcp_server: telecom
@@ -49,6 +55,9 @@ tools:
- consultar pedido
- status do pedido
- pedido
+ response:
+ mode: renderer
+ renderer: retail.order
consultar_entrega:
description: Consulta entrega e rastreamento do pedido.
mcp_server: retail
@@ -61,6 +70,26 @@ tools:
- rastreamento
- transportadora
- previsão
+ response:
+ mode: renderer
+ renderer: retail.delivery
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py
index e6429c4..7a1a9be 100644
--- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py
@@ -3,5 +3,8 @@ from __future__ import annotations
# Compatibilidade local do template/backend.
# A implementação oficial agora fica no framework para evitar duplicação entre agentes.
from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
+from app.presentation import register_tool_renderers
+
+register_tool_renderers()
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..32cbdf8
Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..f77c47a
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ return f"[{agent_label}] Fatura consultada: {result}."
+
+
+def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ plano = result.get("plano")
+ if plano is None:
+ return None
+ parts = [f"[{agent_label}] Seu plano é {plano}"]
+ internet_gb = result.get("internet_gb")
+ status = result.get("status")
+ if internet_gb is not None:
+ parts.append(f"com {internet_gb} GB")
+ if status is not None:
+ parts.append(f"status {status}")
+ return ", ".join(parts) + "."
+
+
+def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ status = result.get("status")
+ if order_id is None or status is None:
+ return None
+ lines = [f"[{agent_label}] Pedido {order_id}: status {status}."]
+ total = result.get("valor_total")
+ if total is not None:
+ lines.append(f"Valor total: R$ {_money_brl(total)}.")
+ items = result.get("itens") or []
+ rendered_items: list[str] = []
+ if isinstance(items, list):
+ for item in items:
+ if isinstance(item, dict):
+ value = item.get("descricao") or item.get("nome") or item.get("sku")
+ else:
+ value = item
+ if value not in (None, ""):
+ rendered_items.append(str(value))
+ if rendered_items:
+ lines.append("Itens: " + "; ".join(rendered_items) + ".")
+ return " ".join(lines)
+
+
+def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ transportadora = result.get("transportadora")
+ codigo = result.get("codigo_rastreio")
+ previsao = result.get("previsao_entrega")
+ if any(v is None for v in (order_id, transportadora, codigo, previsao)):
+ return None
+ return (
+ f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, "
+ f"rastreio {codigo}, previsão {previsao}."
+ )
+
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("telecom.invoice", render_telecom_invoice)
+ register_tool_response_renderer("telecom.plan", render_telecom_plan)
+ register_tool_response_renderer("retail.order", render_retail_order)
+ register_tool_response_renderer("retail.delivery", render_retail_delivery)
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml
index 66c9854..48a83d5 100644
--- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml
index d85fae1..f8a53ca 100644
--- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml
@@ -10,6 +10,9 @@ tools:
- fatura
- conta
- boleto
+ response:
+ mode: renderer
+ renderer: telecom.invoice
consultar_pagamentos:
description: Consulta histórico de pagamentos do cliente.
mcp_server: telecom
@@ -28,6 +31,9 @@ tools:
asset_id: string
selection_keywords:
- plano
+ response:
+ mode: renderer
+ renderer: telecom.plan
listar_servicos:
description: Lista serviços ativos e adicionais VAS.
mcp_server: telecom
@@ -49,6 +55,9 @@ tools:
- consultar pedido
- status do pedido
- pedido
+ response:
+ mode: renderer
+ renderer: retail.order
consultar_entrega:
description: Consulta entrega e rastreamento do pedido.
mcp_server: retail
@@ -61,6 +70,26 @@ tools:
- rastreamento
- transportadora
- previsão
+ response:
+ mode: renderer
+ renderer: retail.delivery
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py
index e6429c4..7a1a9be 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py
@@ -3,5 +3,8 @@ from __future__ import annotations
# Compatibilidade local do template/backend.
# A implementação oficial agora fica no framework para evitar duplicação entre agentes.
from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
+from app.presentation import register_tool_renderers
+
+register_tool_renderers()
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..23b52e1
Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..f77c47a
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ return f"[{agent_label}] Fatura consultada: {result}."
+
+
+def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ plano = result.get("plano")
+ if plano is None:
+ return None
+ parts = [f"[{agent_label}] Seu plano é {plano}"]
+ internet_gb = result.get("internet_gb")
+ status = result.get("status")
+ if internet_gb is not None:
+ parts.append(f"com {internet_gb} GB")
+ if status is not None:
+ parts.append(f"status {status}")
+ return ", ".join(parts) + "."
+
+
+def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ status = result.get("status")
+ if order_id is None or status is None:
+ return None
+ lines = [f"[{agent_label}] Pedido {order_id}: status {status}."]
+ total = result.get("valor_total")
+ if total is not None:
+ lines.append(f"Valor total: R$ {_money_brl(total)}.")
+ items = result.get("itens") or []
+ rendered_items: list[str] = []
+ if isinstance(items, list):
+ for item in items:
+ if isinstance(item, dict):
+ value = item.get("descricao") or item.get("nome") or item.get("sku")
+ else:
+ value = item
+ if value not in (None, ""):
+ rendered_items.append(str(value))
+ if rendered_items:
+ lines.append("Itens: " + "; ".join(rendered_items) + ".")
+ return " ".join(lines)
+
+
+def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ transportadora = result.get("transportadora")
+ codigo = result.get("codigo_rastreio")
+ previsao = result.get("previsao_entrega")
+ if any(v is None for v in (order_id, transportadora, codigo, previsao)):
+ return None
+ return (
+ f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, "
+ f"rastreio {codigo}, previsão {previsao}."
+ )
+
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("telecom.invoice", render_telecom_invoice)
+ register_tool_response_renderer("telecom.plan", render_telecom_plan)
+ register_tool_response_renderer("retail.order", render_retail_order)
+ register_tool_response_renderer("retail.delivery", render_retail_delivery)
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml
index 66c9854..48a83d5 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml
index d85fae1..f8a53ca 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml
@@ -10,6 +10,9 @@ tools:
- fatura
- conta
- boleto
+ response:
+ mode: renderer
+ renderer: telecom.invoice
consultar_pagamentos:
description: Consulta histórico de pagamentos do cliente.
mcp_server: telecom
@@ -28,6 +31,9 @@ tools:
asset_id: string
selection_keywords:
- plano
+ response:
+ mode: renderer
+ renderer: telecom.plan
listar_servicos:
description: Lista serviços ativos e adicionais VAS.
mcp_server: telecom
@@ -49,6 +55,9 @@ tools:
- consultar pedido
- status do pedido
- pedido
+ response:
+ mode: renderer
+ renderer: retail.order
consultar_entrega:
description: Consulta entrega e rastreamento do pedido.
mcp_server: retail
@@ -61,6 +70,26 @@ tools:
- rastreamento
- transportadora
- previsão
+ response:
+ mode: renderer
+ renderer: retail.delivery
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml
index 66c9854..48a83d5 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml
index d85fae1..2329dc7 100644
--- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml
@@ -61,6 +61,23 @@ tools:
- rastreamento
- transportadora
- previsão
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md b/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md
index bbd3250..f3bc196 100644
--- a/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md
+++ b/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md
@@ -2,7 +2,7 @@
O backend de produção de `WorkflowRuntime` continua sendo **LangGraph**. A ausência do pacote `langgraph` em produção é erro de configuração.
-Para builders restritos/offline, o runtime aceita `allow_deterministic_fallback=True`. Esse modo é deliberadamente opt-in e existe somente para exercitar a DSL do framework (actions, edges, condições, pause/resume e trace) de forma reproduzível em testes offline/regressão. Quando `allow_deterministic_fallback=True`, o backend determinístico é selecionado explicitamente mesmo que LangGraph esteja instalado. Ele nunca é selecionado automaticamente em produção.
+Para builders restritos/offline, o runtime aceita `allow_deterministic_fallback=True`. Esse modo é deliberadamente opt-in e existe somente para exercitar a DSL do framework (actions, edges, condições, pause/resume e trace) quando a dependência externa não pode ser instalada. Ele não é selecionado automaticamente.
Exemplo de teste:
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py
index e6429c4..7a1a9be 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py
@@ -3,5 +3,8 @@ from __future__ import annotations
# Compatibilidade local do template/backend.
# A implementação oficial agora fica no framework para evitar duplicação entre agentes.
from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
+from app.presentation import register_tool_renderers
+
+register_tool_renderers()
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..567eb4d
Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..f77c47a
--- /dev/null
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ return f"[{agent_label}] Fatura consultada: {result}."
+
+
+def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ plano = result.get("plano")
+ if plano is None:
+ return None
+ parts = [f"[{agent_label}] Seu plano é {plano}"]
+ internet_gb = result.get("internet_gb")
+ status = result.get("status")
+ if internet_gb is not None:
+ parts.append(f"com {internet_gb} GB")
+ if status is not None:
+ parts.append(f"status {status}")
+ return ", ".join(parts) + "."
+
+
+def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ status = result.get("status")
+ if order_id is None or status is None:
+ return None
+ lines = [f"[{agent_label}] Pedido {order_id}: status {status}."]
+ total = result.get("valor_total")
+ if total is not None:
+ lines.append(f"Valor total: R$ {_money_brl(total)}.")
+ items = result.get("itens") or []
+ rendered_items: list[str] = []
+ if isinstance(items, list):
+ for item in items:
+ if isinstance(item, dict):
+ value = item.get("descricao") or item.get("nome") or item.get("sku")
+ else:
+ value = item
+ if value not in (None, ""):
+ rendered_items.append(str(value))
+ if rendered_items:
+ lines.append("Itens: " + "; ".join(rendered_items) + ".")
+ return " ".join(lines)
+
+
+def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ transportadora = result.get("transportadora")
+ codigo = result.get("codigo_rastreio")
+ previsao = result.get("previsao_entrega")
+ if any(v is None for v in (order_id, transportadora, codigo, previsao)):
+ return None
+ return (
+ f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, "
+ f"rastreio {codigo}, previsão {previsao}."
+ )
+
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("telecom.invoice", render_telecom_invoice)
+ register_tool_response_renderer("telecom.plan", render_telecom_plan)
+ register_tool_response_renderer("retail.order", render_retail_order)
+ register_tool_response_renderer("retail.delivery", render_retail_delivery)
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml
index 66c9854..48a83d5 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml
index d85fae1..f8a53ca 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml
@@ -10,6 +10,9 @@ tools:
- fatura
- conta
- boleto
+ response:
+ mode: renderer
+ renderer: telecom.invoice
consultar_pagamentos:
description: Consulta histórico de pagamentos do cliente.
mcp_server: telecom
@@ -28,6 +31,9 @@ tools:
asset_id: string
selection_keywords:
- plano
+ response:
+ mode: renderer
+ renderer: telecom.plan
listar_servicos:
description: Lista serviços ativos e adicionais VAS.
mcp_server: telecom
@@ -49,6 +55,9 @@ tools:
- consultar pedido
- status do pedido
- pedido
+ response:
+ mode: renderer
+ renderer: retail.order
consultar_entrega:
description: Consulta entrega e rastreamento do pedido.
mcp_server: retail
@@ -61,6 +70,26 @@ tools:
- rastreamento
- transportadora
- previsão
+ response:
+ mode: renderer
+ renderer: retail.delivery
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml
index 66c9854..48a83d5 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml
index d85fae1..2329dc7 100644
--- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml
+++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml
@@ -61,6 +61,23 @@ tools:
- rastreamento
- transportadora
- previsão
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/apps/agent_gateway/app/__pycache__/governance_middleware.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/__pycache__/governance_middleware.cpython-313.pyc
new file mode 100644
index 0000000..9ef1c4f
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/__pycache__/governance_middleware.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/__pycache__/main.cpython-313.pyc
new file mode 100644
index 0000000..fffdb5d
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/__pycache__/main.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/__pycache__/settings.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/__pycache__/settings.cpython-313.pyc
new file mode 100644
index 0000000..0331ed7
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/__pycache__/settings.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/config/__pycache__/governance_loader.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/config/__pycache__/governance_loader.cpython-313.pyc
new file mode 100644
index 0000000..49664df
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/config/__pycache__/governance_loader.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..9facf87
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/audit.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/audit.cpython-313.pyc
new file mode 100644
index 0000000..1da29c6
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/audit.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/evaluation_hooks.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/evaluation_hooks.cpython-313.pyc
new file mode 100644
index 0000000..473f2bc
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/evaluation_hooks.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/model_policies.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/model_policies.cpython-313.pyc
new file mode 100644
index 0000000..1141210
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/model_policies.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/rate_limit.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/rate_limit.cpython-313.pyc
new file mode 100644
index 0000000..507ee7d
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/rate_limit.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/usage.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/usage.cpython-313.pyc
new file mode 100644
index 0000000..b102d11
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/usage.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/agent_gateway/app/routes/__pycache__/governed_proxy_example.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/routes/__pycache__/governed_proxy_example.cpython-313.pyc
new file mode 100644
index 0000000..e4d8952
Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/routes/__pycache__/governed_proxy_example.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/mcp_gateway/app/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..0e96f67
Binary files /dev/null and b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/apps/mcp_gateway/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/main.cpython-313.pyc
new file mode 100644
index 0000000..83107d4
Binary files /dev/null and b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/main.cpython-313.pyc differ
diff --git a/agent_framework_oci/docs/features/01_authentication.md b/agent_framework_oci/docs/features/01_authentication.md
new file mode 100644
index 0000000..1460cbb
--- /dev/null
+++ b/agent_framework_oci/docs/features/01_authentication.md
@@ -0,0 +1,165 @@
+# Autenticação / Authentication
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `security/authentication.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Verifica quem pode acessar APIs, gateways e serviços protegidos antes que a requisição chegue ao agente.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Cliente/Sistema
+ ↓
+Authentication Provider
+ ↓
+credencial válida?
+ ├─ não → 401/nega acesso
+ └─ sim → principal autenticado → agente
+```
+
+### 4. Como funciona internamente
+
+O framework contém uma abstração `AuthenticationProvider` e implementações para cenários diferentes. Entre as implementações atuais estão `NoAuthenticationProvider`, `DenyAuthenticationProvider`, `BasicAuthenticationProvider`, `ApiKeyAuthenticationProvider`, `StaticBearerAuthenticationProvider`, `JwtAuthenticationProvider`, `OAuth2IntrospectionAuthenticationProvider` e `TrustedProxyAuthenticationProvider`.
+
+A autenticação produz um `AuthenticatedPrincipal` com `subject`, `scheme` e, quando aplicável, `claims`. A regra de negócio do agente não deve validar senha/token diretamente.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```python
+from agent_framework.security.authentication import BasicAuthenticationProvider
+
+provider = BasicAuthenticationProvider(
+ client_id="client-a",
+ secret_hash="pbkdf2_sha256:...",
+)
+result = await provider.authenticate(request)
+if not result.authenticated:
+ # negar acesso
+ ...
+```
+
+Segredos podem ser verificados em formato simples, SHA-256 ou PBKDF2; em produção, prefira hashes fortes e secret stores.
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Basic auth retornando 401: validar `Authorization: Basic ...` e o secret configurado.
+- Confundir autenticação do usuário com `OCI_AUTH_MODE`: são problemas diferentes.
+- Usar `NoAuthenticationProvider` em produção sem decisão explícita de arquitetura.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/security/authentication.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Checks who may access protected APIs, gateways, and services before the request reaches the agent.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Client/System
+ ↓
+Authentication Provider
+ ↓
+valid credential?
+ ├─ no → 401/deny
+ └─ yes → authenticated principal → agent
+```
+
+### 4. How it works internally
+
+The framework exposes an `AuthenticationProvider` abstraction with multiple implementations. Current providers include `NoAuthenticationProvider`, `DenyAuthenticationProvider`, `BasicAuthenticationProvider`, `ApiKeyAuthenticationProvider`, `StaticBearerAuthenticationProvider`, `JwtAuthenticationProvider`, `OAuth2IntrospectionAuthenticationProvider`, and `TrustedProxyAuthenticationProvider`.
+
+Authentication produces an `AuthenticatedPrincipal` containing `subject`, `scheme`, and optional `claims`. Domain code should not validate credentials directly.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```python
+from agent_framework.security.authentication import BasicAuthenticationProvider
+
+provider = BasicAuthenticationProvider(
+ client_id="client-a",
+ secret_hash="pbkdf2_sha256:...",
+)
+result = await provider.authenticate(request)
+if not result.authenticated:
+ # deny access
+ ...
+```
+
+Secrets may be verified as plain, SHA-256, or PBKDF2 values; for production, prefer strong hashes and managed secret stores.
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Basic auth returns 401: validate the `Authorization: Basic ...` header and configured secret.
+- Do not confuse API authentication with `OCI_AUTH_MODE`; they solve different problems.
+- Avoid `NoAuthenticationProvider` in production unless explicitly accepted by architecture.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/security/authentication.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/02_deterministic_transactional_workflow.md b/agent_framework_oci/docs/features/02_deterministic_transactional_workflow.md
new file mode 100644
index 0000000..5485b94
--- /dev/null
+++ b/agent_framework_oci/docs/features/02_deterministic_transactional_workflow.md
@@ -0,0 +1,183 @@
+# Workflow Transacional Determinístico / Deterministic Transactional Workflow
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `workflows/runtime.py + mcp/tool_policy.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Garante que operações que alteram estado sigam passos previsíveis, com confirmação e controle de execução, em vez de depender da criatividade do LLM.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Mensagem do cliente
+ ↓
+LLM entende intenção
+ ↓
+Tool policy = transactional
+ ↓
+Workflow determinístico
+ ↓
+confirmação
+ ↓
+execução controlada
+ ↓
+resultado
+```
+
+### 4. Como funciona internamente
+
+O LLM pode ajudar a interpretar a intenção e extrair parâmetros, mas não deve decidir a sequência crítica de uma transação. O `ToolPolicyRegistry` classifica tools, e `operation_type: transactional` ativa a política transacional. O `WorkflowRuntime` executa o workflow, mantém estado e integra pause/resume e recuperação de erro.
+
+A configuração `ENABLE_TRANSACTIONAL_WORKFLOWS` controla a capability global, e `WORKFLOWS_PATH` aponta para os YAMLs.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```yaml
+tools:
+ cancelar_servico:
+ operation_type: transactional
+ requires_confirmation: true
+```
+
+```text
+1. localizar serviço
+2. validar elegibilidade
+3. pedir confirmação
+4. PAUSE
+5. receber confirmação
+6. RESUME
+7. executar side effect
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Marcar uma tool de escrita como `read_only` elimina proteções transacionais.
+- Reexecutar steps anteriores ao pause pode duplicar side effects; use o runtime oficial.
+- Não use prompt como única garantia de confirmação.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Ensures state-changing operations follow predictable steps with confirmation and execution control instead of depending on LLM creativity.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Customer message
+ ↓
+LLM understands intent
+ ↓
+Tool policy = transactional
+ ↓
+Deterministic workflow
+ ↓
+confirmation
+ ↓
+controlled execution
+ ↓
+result
+```
+
+### 4. How it works internally
+
+The LLM may help interpret intent and extract parameters, but it should not decide the critical sequence of a transaction. `ToolPolicyRegistry` classifies tools, and `operation_type: transactional` activates transactional behavior. `WorkflowRuntime` executes the workflow, preserves state, and integrates pause/resume and error recovery.
+
+`ENABLE_TRANSACTIONAL_WORKFLOWS` controls the capability globally, while `WORKFLOWS_PATH` points to workflow YAML files.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```yaml
+tools:
+ cancel_service:
+ operation_type: transactional
+ requires_confirmation: true
+```
+
+```text
+1. locate service
+2. validate eligibility
+3. ask for confirmation
+4. PAUSE
+5. receive confirmation
+6. RESUME
+7. execute side effect
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Marking a write tool as `read_only` bypasses transactional protections.
+- Re-running steps before a pause can duplicate side effects; use the official runtime.
+- Do not use prompts as the only confirmation guarantee.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/03_domain_requested_llm_composition.md b/agent_framework_oci/docs/features/03_domain_requested_llm_composition.md
new file mode 100644
index 0000000..4d70018
--- /dev/null
+++ b/agent_framework_oci/docs/features/03_domain_requested_llm_composition.md
@@ -0,0 +1,157 @@
+# Composição por LLM Solicitada pelo Domínio / Domain Requested LLM Composition
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `runtime/agent_runtime.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Permite que a regra de negócio calcule o resultado e peça ao LLM apenas para redigir a resposta final.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Regra de negócio calcula
+ ↓
+requires_llm_composition=true
+ ↓
+framework impede resposta MCP direta
+ ↓
+LLMProvider oficial
+ ↓
+redação natural
+```
+
+### 4. Como funciona internamente
+
+O domínio retorna dados confiáveis e uma instrução de composição. O `AgentRuntimeMixin` detecta `requires_llm_composition` de forma recursiva no resultado da tool/workflow e não encerra a resposta pelo caminho direto de MCP. A composição segue pelo LLM oficial do agente, preservando profiles, tracing, usage e políticas do framework.
+
+O LLM deve redigir; ele não deve recalcular valores nem decidir regras de negócio já resolvidas.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```json
+{
+ "success": true,
+ "refund_amount": "38,00",
+ "requires_llm_composition": true,
+ "response_instruction": "Explique a devolução usando somente os valores calculados."
+}
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Instrução muito aberta pode fazer o LLM adicionar conteúdo não autorizado.
+- Não envie ao LLM a responsabilidade de recalcular valores determinísticos.
+- Se não houver necessidade de redação livre, prefira resposta determinística direta.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Lets domain logic compute the authoritative result and ask the LLM only to compose the final user-facing response.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Domain logic computes
+ ↓
+requires_llm_composition=true
+ ↓
+framework prevents direct MCP answer
+ ↓
+official LLMProvider
+ ↓
+natural-language response
+```
+
+### 4. How it works internally
+
+The domain returns authoritative data plus a composition instruction. `AgentRuntimeMixin` recursively detects `requires_llm_composition` in tool/workflow results and avoids terminating through the direct MCP-answer path. Composition then uses the agent's official LLM provider, preserving profiles, tracing, usage accounting, and framework policies.
+
+The LLM should compose language, not recalculate values or override already-resolved business rules.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```json
+{
+ "success": true,
+ "refund_amount": "38.00",
+ "requires_llm_composition": true,
+ "response_instruction": "Explain the refund using only the computed values."
+}
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- An overly broad instruction may let the LLM add unauthorized content.
+- Do not delegate deterministic calculations back to the LLM.
+- If free-form wording is unnecessary, prefer a deterministic direct response.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/04_domain_requested_rag.md b/agent_framework_oci/docs/features/04_domain_requested_rag.md
new file mode 100644
index 0000000..745397f
--- /dev/null
+++ b/agent_framework_oci/docs/features/04_domain_requested_rag.md
@@ -0,0 +1,167 @@
+# RAG Solicitado pelo Domínio / Domain Requested RAG
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `runtime/agent_runtime.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Permite que uma tool ou workflow declare que a resposta precisa consultar conhecimento externo, mesmo quando já existe resultado MCP.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Tool/Workflow
+ ↓
+requires_rag=true
+ ↓
+rag_query / rag_queries
+ ↓
+RagService do framework
+ ↓
+Retrieval Guardrails
+ ↓
+LLM/resposta
+```
+
+### 4. Como funciona internamente
+
+Normalmente o framework pode pular RAG quando MCP já trouxe informação suficiente (`SKIP_RAG_WHEN_MCP_SUFFICIENT`). Esta feature permite que o domínio substitua essa decisão para um caso específico. O resultado pode declarar `requires_rag`, `rag_query` ou `rag_queries`; o runtime usa essas queries como override e executa o `RagService`.
+
+O domínio informa **o que precisa saber**. Ele não implementa cliente de vetor, retriever ou prompt RAG paralelo.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```json
+{
+ "requires_rag": true,
+ "rag_queries": [
+ "Como cancelar YouTube Premium?",
+ "Como cancelar Aya Books?"
+ ]
+}
+```
+
+Configurações relacionadas incluem `RAG_TOP_K` e `SKIP_RAG_WHEN_MCP_SUFFICIENT`.
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Declarar RAG para fatos transacionais já resolvidos pela API pode aumentar custo e latência.
+- Query genérica demais reduz relevância.
+- Nunca confie no retrieval sem `Retrieval Guardrails` quando o dado influencia resposta crítica.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Allows a tool or workflow to declare that external knowledge retrieval is required even when an MCP result already exists.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Tool/Workflow
+ ↓
+requires_rag=true
+ ↓
+rag_query / rag_queries
+ ↓
+framework RagService
+ ↓
+Retrieval Guardrails
+ ↓
+LLM/response
+```
+
+### 4. How it works internally
+
+Normally the framework may skip RAG when MCP already provides sufficient data (`SKIP_RAG_WHEN_MCP_SUFFICIENT`). This feature lets the domain override that decision for a specific case. A result may declare `requires_rag`, `rag_query`, or `rag_queries`; the runtime uses those queries as overrides and invokes `RagService`.
+
+The domain declares **what knowledge is needed**. It does not implement its own vector client, retriever, or parallel RAG prompt stack.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```json
+{
+ "requires_rag": true,
+ "rag_queries": [
+ "How to cancel YouTube Premium?",
+ "How to cancel Aya Books?"
+ ]
+}
+```
+
+Related settings include `RAG_TOP_K` and `SKIP_RAG_WHEN_MCP_SUFFICIENT`.
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Requesting RAG for transactional facts already resolved by an API adds unnecessary cost and latency.
+- Queries that are too broad reduce relevance.
+- Do not trust retrieved content for critical responses without Retrieval Guardrails.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/05_long_term_memory.md b/agent_framework_oci/docs/features/05_long_term_memory.md
new file mode 100644
index 0000000..e9e43e3
--- /dev/null
+++ b/agent_framework_oci/docs/features/05_long_term_memory.md
@@ -0,0 +1,167 @@
+# Memória de Longo Prazo / Long Term Memory
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `memory/long_term_memory.py + memory/long_term_store.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Permite lembrar informações úteis entre sessões diferentes, sem depender do histórico completo de uma conversa.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Sessão A
+ ↓
+extração de memória relevante
+ ↓
+Long Term Memory Store
+ ↓
+... dias depois ...
+ ↓
+Sessão B
+ ↓
+recupera contexto relevante
+ ↓
+agente
+```
+
+### 4. Como funciona internamente
+
+A memória de longo prazo é diferente de histórico de mensagens e de checkpoint. Ela persiste fatos/preferências úteis e os recupera como contexto de uma nova sessão. O framework oferece providers `memory`, `sqlite`, `autonomous` e `oracle`.
+
+Configurações importantes: `ENABLE_LONG_TERM_MEMORY`, `LONG_TERM_MEMORY_PROVIDER`, `LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS`, `LONG_TERM_MEMORY_MIN_CONFIDENCE`, `LONG_TERM_MEMORY_AUTO_EXTRACT` e `LONG_TERM_MEMORY_INJECT_CONTEXT`.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```env
+ENABLE_LONG_TERM_MEMORY=true
+LONG_TERM_MEMORY_PROVIDER=oracle
+LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
+LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
+LONG_TERM_MEMORY_AUTO_EXTRACT=true
+LONG_TERM_MEMORY_INJECT_CONTEXT=true
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Não confundir LTM com replay de toda conversa.
+- Memória irrelevante ou de baixa confiança não deveria ser injetada.
+- Em múltiplas réplicas, prefira storage durável compartilhado em vez de memória local.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/memory/long_term_memory.py`
+- `libs/agent_framework/src/agent_framework/memory/long_term_store.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Allows useful information to persist across different sessions without depending on the full transcript of a previous conversation.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Session A
+ ↓
+extract relevant memory
+ ↓
+Long Term Memory Store
+ ↓
+... days later ...
+ ↓
+Session B
+ ↓
+retrieve relevant context
+ ↓
+agent
+```
+
+### 4. How it works internally
+
+Long-term memory is different from message history and checkpoints. It persists useful facts/preferences and retrieves them as context for a future session. The framework supports `memory`, `sqlite`, `autonomous`, and `oracle` providers.
+
+Important settings include `ENABLE_LONG_TERM_MEMORY`, `LONG_TERM_MEMORY_PROVIDER`, `LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS`, `LONG_TERM_MEMORY_MIN_CONFIDENCE`, `LONG_TERM_MEMORY_AUTO_EXTRACT`, and `LONG_TERM_MEMORY_INJECT_CONTEXT`.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```env
+ENABLE_LONG_TERM_MEMORY=true
+LONG_TERM_MEMORY_PROVIDER=oracle
+LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
+LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
+LONG_TERM_MEMORY_AUTO_EXTRACT=true
+LONG_TERM_MEMORY_INJECT_CONTEXT=true
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Do not confuse LTM with replaying the entire transcript.
+- Irrelevant or low-confidence memories should not be injected.
+- For multiple replicas, prefer shared durable storage over local memory.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/memory/long_term_memory.py`
+- `libs/agent_framework/src/agent_framework/memory/long_term_store.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/06_offline_workflow_regression.md b/agent_framework_oci/docs/features/06_offline_workflow_regression.md
new file mode 100644
index 0000000..5569f63
--- /dev/null
+++ b/agent_framework_oci/docs/features/06_offline_workflow_regression.md
@@ -0,0 +1,163 @@
+# Regressão Offline de Workflow / Offline Workflow Regression
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `workflows/runtime.py + Tuning-Performance/Offline_Workflow_Regression`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Permite testar a lógica de workflows sem exigir toda a infraestrutura de produção.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Teste
+ ↓
+backend determinístico explicitamente habilitado
+ ↓
+run → PAUSED
+ ↓
+resume → COMPLETED
+ ↓
+asserts de estado/side effects
+```
+
+### 4. Como funciona internamente
+
+O `WorkflowRuntime` possui um caminho determinístico/offline **explicitamente opt-in para testes**. Ele permite validar DSL, condições, pause/resume e proteção contra reexecução sem exigir LangGraph, banco, OCI ou APIs externas.
+
+O comportamento de produção continua usando LangGraph. O modo offline não deve virar fallback silencioso quando LangGraph falha ou está ausente em produção.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```text
+run(workflow)
+ action_a = 1 execução
+ status = PAUSED
+
+resume(workflow)
+ action_a continua com 1 execução
+ action_b = 1 execução
+ status = COMPLETED
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Usar o backend offline em produção mascara problemas reais.
+- Mockar tanto que o teste deixa de validar a DSL real.
+- Não verificar side effects anteriores ao pause pode esconder duplicações.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `libs/agent_framework/src/agent_framework/Tuning-Performance/Offline_Workflow_Regression`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Allows workflow logic to be regression-tested without requiring the full production infrastructure.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Test
+ ↓
+explicit deterministic test backend
+ ↓
+run → PAUSED
+ ↓
+resume → COMPLETED
+ ↓
+state/side-effect assertions
+```
+
+### 4. How it works internally
+
+`WorkflowRuntime` includes an **explicitly opt-in deterministic/offline test backend**. It can validate DSL rules, conditions, pause/resume behavior, and duplicate-execution protection without requiring LangGraph, a database, OCI, or external APIs.
+
+Production behavior still uses LangGraph. Offline mode must never become a silent fallback when LangGraph fails or is unavailable in production.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```text
+run(workflow)
+ action_a = executed once
+ status = PAUSED
+
+resume(workflow)
+ action_a remains executed once
+ action_b = executed once
+ status = COMPLETED
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Using the offline backend in production hides real issues.
+- Over-mocking can stop the test from validating real DSL behavior.
+- Failing to assert pre-pause side effects may hide duplicate execution.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `libs/agent_framework/src/agent_framework/Tuning-Performance/Offline_Workflow_Regression`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/07_pause_resume_workflow.md b/agent_framework_oci/docs/features/07_pause_resume_workflow.md
new file mode 100644
index 0000000..b600937
--- /dev/null
+++ b/agent_framework_oci/docs/features/07_pause_resume_workflow.md
@@ -0,0 +1,165 @@
+# Pause / Resume de Workflow / Pause / Resume Workflow
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `workflows/runtime.py + workflows/graph.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Permite interromper um workflow em um ponto seguro, persistir o estado e continuar depois com a resposta do usuário ou outro evento.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Workflow
+ ↓
+ações prévias
+ ↓
+PAUSE
+ ↓
+checkpoint/estado
+ ↓
+nova mensagem
+ ↓
+RESUME
+ ↓
+ações seguintes
+```
+
+### 4. Como funciona internamente
+
+`WorkflowRuntime` expõe `arun(...)` e `aresume(...)`. O nó de pause é separado da action anterior para evitar reexecutar side effects quando o workflow retoma. O mesmo `execution_id/thread_id` identifica a execução pausada e retomada.
+
+O runtime suporta condições declarativas como `all`, `any`, `not`, `eq`, `neq` e `exists`, permitindo definir quando pausar ou continuar sem colocar lógica conversacional no prompt.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```text
+status = await runtime.arun(...)
+# status == PAUSED
+
+status = await runtime.aresume(execution_id, input={"confirmed": true})
+# status == COMPLETED
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Perder o `execution_id` impede retomar a execução correta.
+- Reexecutar o workflow do zero após confirmação pode repetir side effects.
+- Pause sem storage/checkpoint compartilhado é frágil em múltiplas réplicas.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `libs/agent_framework/src/agent_framework/workflows/graph.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Allows a workflow to stop at a safe point, persist state, and continue later using user input or another event.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+Workflow
+ ↓
+pre-pause actions
+ ↓
+PAUSE
+ ↓
+checkpoint/state
+ ↓
+new message
+ ↓
+RESUME
+ ↓
+remaining actions
+```
+
+### 4. How it works internally
+
+`WorkflowRuntime` exposes `arun(...)` and `aresume(...)`. The pause node is separated from the preceding action so previous side effects are not executed again on resume. The same `execution_id/thread_id` identifies the paused and resumed execution.
+
+The runtime supports declarative conditions such as `all`, `any`, `not`, `eq`, `neq`, and `exists`, so pause/continue decisions do not need to live in the prompt.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```text
+status = await runtime.arun(...)
+# status == PAUSED
+
+status = await runtime.aresume(execution_id, input={"confirmed": true})
+# status == COMPLETED
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Losing the `execution_id` prevents resuming the right execution.
+- Restarting the workflow from scratch after confirmation may duplicate side effects.
+- Pause without shared checkpoint/state storage is fragile across multiple replicas.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `libs/agent_framework/src/agent_framework/workflows/graph.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/08_route_stickiness.md b/agent_framework_oci/docs/features/08_route_stickiness.md
new file mode 100644
index 0000000..264a17e
--- /dev/null
+++ b/agent_framework_oci/docs/features/08_route_stickiness.md
@@ -0,0 +1,151 @@
+# Aderência de Rota / Route Stickiness
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `routing/enterprise_router.py + runtime/agent_runtime.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Evita que pequenas mensagens de continuação façam a conversa trocar de agente sem necessidade.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+mensagem atual
+ + histórico curto
+ + rota anterior
+ ↓
+continuidade semântica
+ ↓
+manter rota ou handoff
+```
+
+### 4. Como funciona internamente
+
+Route Stickiness avalia se a nova mensagem continua semanticamente ligada ao assunto/agente atual. Isso reduz ping-pong de agentes em mensagens como “e esse valor?”, “sim”, “o segundo” ou “e no mês passado?”.
+
+Configurações existentes incluem `ENABLE_ROUTE_STICKINESS`, `ROUTE_STICKINESS_LLM_PROFILE`, `ROUTE_STICKINESS_CONFIDENCE_THRESHOLD`, `ROUTE_STICKINESS_HISTORY_TURNS` e `ROUTE_STICKINESS_MAX_TOKENS`. A decisão pode permitir handoff quando há evidência suficiente de mudança de assunto.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```env
+ENABLE_ROUTE_STICKINESS=true
+ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90
+ROUTE_STICKINESS_HISTORY_TURNS=2
+ROUTE_STICKINESS_MAX_TOKENS=80
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Threshold muito baixo pode prender o cliente no agente errado.
+- Threshold alto demais perde continuidade em mensagens curtas.
+- Stickiness não deve bloquear handoff explícito quando a intenção realmente mudou.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/routing/enterprise_router.py`
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Prevents short follow-up messages from unnecessarily switching the conversation to another agent.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+current message
+ + short history
+ + previous route
+ ↓
+semantic continuity
+ ↓
+keep route or handoff
+```
+
+### 4. How it works internally
+
+Route Stickiness evaluates whether a new message is semantically continuous with the current subject/agent. It reduces agent ping-pong for messages such as “what about that amount?”, “yes”, “the second one”, or “and last month?”.
+
+Existing settings include `ENABLE_ROUTE_STICKINESS`, `ROUTE_STICKINESS_LLM_PROFILE`, `ROUTE_STICKINESS_CONFIDENCE_THRESHOLD`, `ROUTE_STICKINESS_HISTORY_TURNS`, and `ROUTE_STICKINESS_MAX_TOKENS`. The decision may still allow handoff when there is enough evidence of a topic change.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```env
+ENABLE_ROUTE_STICKINESS=true
+ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90
+ROUTE_STICKINESS_HISTORY_TURNS=2
+ROUTE_STICKINESS_MAX_TOKENS=80
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- A threshold that is too low may trap the user on the wrong agent.
+- A threshold that is too high may lose continuity on short follow-ups.
+- Stickiness should not block explicit handoff when the user clearly changes intent.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/routing/enterprise_router.py`
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/09_voice_interruption_replay.md b/agent_framework_oci/docs/features/09_voice_interruption_replay.md
new file mode 100644
index 0000000..85bd363
--- /dev/null
+++ b/agent_framework_oci/docs/features/09_voice_interruption_replay.md
@@ -0,0 +1,153 @@
+# Replay em Interrupções de Voz / Voice Interruption Replay
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `channels/interruption.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Decide se um áudio recebido durante a fala do agente representa uma nova intenção, um ruído/backchannel ou algo que deve apenas repetir/continuar a última fala.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+áudio durante fala
+ ↓
+InterruptionPolicy
+ ├─ process → nova mensagem
+ ├─ classify → classificador leve
+ └─ replay → última fala
+```
+
+### 4. Como funciona internamente
+
+A política fica no framework, não no domínio. Ela diferencia sessão terminal, `idle_nudge`, fala não interrompível e fala potencialmente interrompível. Quando necessário, pode usar um classificador leve baseado no `LLMProvider`; quando a classificação falha, a política é conservadora e pode optar por replay.
+
+O objetivo é evitar que “aham”, ruído, eco ou fragmentos residuais sejam tratados como uma nova intenção completa.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```text
+Agente: "Sua fatura possui..."
+Cliente: "aham"
+→ replay/continua
+
+Agente: "Sua fatura possui..."
+Cliente: "espera, quero falar de outra coisa"
+→ processa nova intenção
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Classificar todo ruído com LLM aumenta latência e custo.
+- Permitir interrupção em fala transacional não interrompível pode corromper UX/estado.
+- Replay deve usar uma fala real anterior, não um envelope técnico.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/channels/interruption.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Decides whether audio received while the agent is speaking represents a new intent, a backchannel/noise event, or something that should simply replay/continue the previous speech.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+audio during speech
+ ↓
+InterruptionPolicy
+ ├─ process → new message
+ ├─ classify → lightweight classifier
+ └─ replay → previous speech
+```
+
+### 4. How it works internally
+
+The policy lives in the framework rather than domain code. It distinguishes terminal sessions, `idle_nudge`, non-interruptible speech, and potentially interruptible speech. When needed, it may use a lightweight classifier backed by `LLMProvider`; on classification failure, it can fail safely to replay.
+
+The goal is to prevent “uh-huh”, noise, echo, or residual audio fragments from being interpreted as a full new intent.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```text
+Agent: "Your invoice contains..."
+User: "uh-huh"
+→ replay/continue
+
+Agent: "Your invoice contains..."
+User: "wait, I want to ask something else"
+→ process new intent
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Sending every noise fragment to an LLM increases latency and cost.
+- Allowing interruption during non-interruptible transactional speech may corrupt UX/state.
+- Replay should use a real previous utterance, not a technical envelope.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/channels/interruption.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/10_workflow_error_recovery.md b/agent_framework_oci/docs/features/10_workflow_error_recovery.md
new file mode 100644
index 0000000..e5ef8d6
--- /dev/null
+++ b/agent_framework_oci/docs/features/10_workflow_error_recovery.md
@@ -0,0 +1,167 @@
+# Recuperação de Erro em Workflow / Workflow Error Recovery
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `workflows/runtime.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Preserva o estado parcial de uma execução quando um passo posterior falha, permitindo entender o que já aconteceu e evitar repetir side effects.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+passo A ✅
+passo B ✅
+passo C ❌
+ ↓
+FAILED + snapshot parcial
+ ↓
+recovery decide o que pode continuar/repetir
+```
+
+### 4. Como funciona internamente
+
+O runtime preserva o snapshot parcial do LangGraph quando uma etapa posterior falha e produz `error_details` genérico. Quando a exceção externa possui informações estruturadas, podem ser preservados status HTTP, body, número de tentativas, code e metadata.
+
+A feature não significa “tentar tudo de novo”. Recuperação segura depende de conhecer o estado já executado, a idempotência e a natureza do erro.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```json
+{
+ "status": "FAILED",
+ "error_details": {
+ "status": 503,
+ "attempts": 3,
+ "code": "UPSTREAM_UNAVAILABLE"
+ },
+ "state": {
+ "protocol_created": true,
+ "operation_completed": true,
+ "sms_sent": false
+ }
+}
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Retry indiscriminado pode repetir transações.
+- Se a exceção externa perde metadata, a recuperação fica menos precisa.
+- Combine sempre com Durable Idempotency em side effects críticos.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Preserves partial execution state when a later step fails, making it possible to know what already happened and avoid repeating side effects.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+step A ✅
+step B ✅
+step C ❌
+ ↓
+FAILED + partial snapshot
+ ↓
+recovery decides what may continue/retry
+```
+
+### 4. How it works internally
+
+The runtime preserves the partial LangGraph snapshot when a later step fails and produces generic `error_details`. When an external exception provides structured information, HTTP status, body, attempt count, code, and metadata may be preserved.
+
+This feature does not mean “retry everything”. Safe recovery depends on knowing what already executed, idempotency guarantees, and the nature of the failure.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```json
+{
+ "status": "FAILED",
+ "error_details": {
+ "status": 503,
+ "attempts": 3,
+ "code": "UPSTREAM_UNAVAILABLE"
+ },
+ "state": {
+ "protocol_created": true,
+ "operation_completed": true,
+ "sms_sent": false
+ }
+}
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Blind retries may repeat transactions.
+- If external exceptions discard metadata, recovery becomes less precise.
+- Always combine with Durable Idempotency for critical side effects.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/workflows/runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/11_clarification.md b/agent_framework_oci/docs/features/11_clarification.md
new file mode 100644
index 0000000..6ef6a15
--- /dev/null
+++ b/agent_framework_oci/docs/features/11_clarification.md
@@ -0,0 +1,169 @@
+# Clarificação / Clarification
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `runtime/agent_runtime.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Quando faltam dados ou uma tool encontra múltiplas opções, o framework pergunta ao usuário em vez de adivinhar.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+pedido ambíguo
+ ↓
+NEEDS_CLARIFICATION
+ ↓
+pergunta + opções
+ ↓
+usuário responde
+ ↓
+framework resolve
+ ↓
+retoma mesma tool/workflow
+```
+
+### 4. Como funciona internamente
+
+O runtime suporta clarificação tanto de parâmetros faltantes quanto de resultados de tools. Para tool-result clarification, um resultado com `status: NEEDS_CLARIFICATION` pode trazer opções; o runtime persiste `pending_tool_clarification`, entra em `TOOL_RESULT_CLARIFICATION` e consegue resolver respostas por ordinal ou nome.
+
+Depois da escolha, o framework reutiliza a mesma tool e injeta os argumentos resolvidos, evitando que o roteador trate a resposta curta como uma intenção nova.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```json
+{
+ "status": "NEEDS_CLARIFICATION",
+ "question": "Qual serviço?",
+ "options": [
+ {"id": "tim_music", "label": "TIM Music"},
+ {"id": "hbo_max", "label": "HBO Max"}
+ ]
+}
+```
+
+Usuário: `o segundo` → `hbo_max`.
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Não descarte `pending_tool_clarification` entre turns.
+- Uma resposta curta deve ser resolvida contra as opções antes do roteamento normal.
+- Opções sem identificador/label consistente pioram a resolução.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+When required information is missing or a tool finds multiple options, the framework asks the user instead of guessing.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+ambiguous request
+ ↓
+NEEDS_CLARIFICATION
+ ↓
+question + options
+ ↓
+user answers
+ ↓
+framework resolves
+ ↓
+resume same tool/workflow
+```
+
+### 4. How it works internally
+
+The runtime supports clarification for both missing parameters and ambiguous tool results. For tool-result clarification, a result with `status: NEEDS_CLARIFICATION` may include options; the runtime persists `pending_tool_clarification`, moves to `TOOL_RESULT_CLARIFICATION`, and can resolve responses by ordinal or name.
+
+After selection, the framework reuses the same tool and injects resolved arguments, preventing the router from treating a short reply as a brand-new intent.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```json
+{
+ "status": "NEEDS_CLARIFICATION",
+ "question": "Which service?",
+ "options": [
+ {"id": "tim_music", "label": "TIM Music"},
+ {"id": "hbo_max", "label": "HBO Max"}
+ ]
+}
+```
+
+User: `the second one` → `hbo_max`.
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Do not discard `pending_tool_clarification` between turns.
+- A short answer should be resolved against pending options before normal routing.
+- Options without stable identifiers/labels reduce resolution quality.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/12_durable_idempotency.md b/agent_framework_oci/docs/features/12_durable_idempotency.md
new file mode 100644
index 0000000..98969e3
--- /dev/null
+++ b/agent_framework_oci/docs/features/12_durable_idempotency.md
@@ -0,0 +1,163 @@
+# Idempotência Durável / Durable Idempotency
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `idempotency.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Impede que a mesma operação crítica seja executada duas vezes, inclusive quando outra réplica/pod recebe a repetição.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+requisição
+ ↓
+idempotency key
+ ↓
+store durável
+ ├─ existe → retorna resultado anterior
+ └─ não existe → executa → persiste resultado
+```
+
+### 4. Como funciona internamente
+
+`create_idempotency_store(settings, ...)` escolhe o backend conforme configuração/plataforma. O framework possui `IdempotencyStore` e `InMemoryIdempotencyStore`, mas produção distribuída deve preferir storage compartilhado. As configurações incluem `IDEMPOTENCY_PROVIDER`, `IDEMPOTENCY_REQUIRE_DURABLE` e `IDEMPOTENCY_TTL_SECONDS`.
+
+Idempotência é diferente de retry: retry repete a tentativa; idempotência garante que a repetição não produza um novo side effect.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```text
+Pod A recebe cancelamento
+→ key=cliente:servico:operacao
+→ executa
+→ grava resultado
+
+Pod A cai
+
+Pod B recebe retry
+→ mesma key
+→ encontra resultado
+→ NÃO cancela de novo
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Usar store em memória com múltiplos pods não é idempotência durável.
+- Chave ampla demais pode bloquear operações legítimas; estreita demais permite duplicidade.
+- TTL deve ser compatível com a janela real de retry/replay.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/idempotency.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Prevents the same critical operation from executing twice, including when a retry lands on another replica/pod.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+request
+ ↓
+idempotency key
+ ↓
+durable store
+ ├─ exists → return previous result
+ └─ missing → execute → persist result
+```
+
+### 4. How it works internally
+
+`create_idempotency_store(settings, ...)` chooses a backend according to configuration/platform. The framework provides `IdempotencyStore` and `InMemoryIdempotencyStore`, but distributed production should prefer shared storage. Settings include `IDEMPOTENCY_PROVIDER`, `IDEMPOTENCY_REQUIRE_DURABLE`, and `IDEMPOTENCY_TTL_SECONDS`.
+
+Idempotency is different from retry: retry repeats an attempt; idempotency guarantees that repetition does not create another side effect.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```text
+Pod A receives cancellation
+→ key=customer:service:operation
+→ executes
+→ stores result
+
+Pod A crashes
+
+Pod B receives retry
+→ same key
+→ finds stored result
+→ DOES NOT cancel again
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- An in-memory store across multiple pods is not durable idempotency.
+- A key that is too broad may block legitimate operations; too narrow may allow duplicates.
+- TTL should match the real retry/replay window.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/idempotency.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/13_dynamic_transaction_states.md b/agent_framework_oci/docs/features/13_dynamic_transaction_states.md
new file mode 100644
index 0000000..6cd06ce
--- /dev/null
+++ b/agent_framework_oci/docs/features/13_dynamic_transaction_states.md
@@ -0,0 +1,157 @@
+# Estados Transacionais Dinâmicos / Dynamic Transaction States
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `runtime/agent_runtime.py + mcp/tool_policy.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Permite criar estados de confirmação baseados no agente/domínio atual sem hardcode de todos os domínios dentro do framework.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+tool transactional
+ ↓
+agente/domínio atual
+ ↓
+WAITING__CONFIRMATION
+ ↓
+confirmação/rejeição
+ ↓
+estado seguinte
+```
+
+### 4. Como funciona internamente
+
+Em vez de manter estados fixos como `WAITING_BILLING_CONFIRMATION`, `WAITING_PRODUCT_CONFIRMATION` etc. para cada domínio conhecido, o runtime deriva o prefixo do agente atual e gera o estado dinamicamente. A função interna de estado transacional mantém o framework genérico.
+
+A classificação `operation_type` aceita `read_only`, `transactional`, `conversational` e `internal`; somente `transactional` entra no caminho de confirmação transacional.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```text
+VasAgent + cancelar_vas
+→ WAITING_VAS_CONFIRMATION
+
+AddressAgent + alterar_endereco
+→ WAITING_ADDRESS_CONFIRMATION
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Hardcode de estados no domínio reduz reutilização.
+- Classificar uma tool como `conversational` não deve ativar confirmação transacional.
+- Mudanças no identificador do agente podem mudar o prefixo; mantenha IDs estáveis.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Allows confirmation states to be derived from the current agent/domain instead of hardcoding every business domain into the framework.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+transactional tool
+ ↓
+current agent/domain
+ ↓
+WAITING__CONFIRMATION
+ ↓
+confirm/reject
+ ↓
+next state
+```
+
+### 4. How it works internally
+
+Instead of maintaining fixed states such as `WAITING_BILLING_CONFIRMATION`, `WAITING_PRODUCT_CONFIRMATION`, and so on for every known domain, the runtime derives a prefix from the current agent and builds the confirmation state dynamically. This keeps the framework generic.
+
+`operation_type` accepts `read_only`, `transactional`, `conversational`, and `internal`; only `transactional` enters the transactional confirmation path.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```text
+VasAgent + cancel_vas
+→ WAITING_VAS_CONFIRMATION
+
+AddressAgent + change_address
+→ WAITING_ADDRESS_CONFIRMATION
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Hardcoding states in domain code reduces reuse.
+- Classifying a tool as `conversational` should not trigger transactional confirmation.
+- Changing agent identifiers may change state prefixes; keep IDs stable.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py`
+- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/14_post_finalization_replay.md b/agent_framework_oci/docs/features/14_post_finalization_replay.md
new file mode 100644
index 0000000..8175336
--- /dev/null
+++ b/agent_framework_oci/docs/features/14_post_finalization_replay.md
@@ -0,0 +1,159 @@
+# Replay Após Finalização / Post Finalization Replay
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `channels/interruption.py + config/settings.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Evita que áudio residual ou mensagens tardias reabram uma sessão já finalizada.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+sessão terminal
+ ↓
+entrada residual
+ ↓
+policy detecta finalização
+ ↓
+replay última fala/fallback
+ ↓
+NÃO reabre LangGraph
+```
+
+### 4. Como funciona internamente
+
+A política de interrupção verifica metadata de sessão terminal antes de tratar uma entrada como nova intenção. Quando há texto terminal disponível, usa `last_assistant_text`/`terminal_replay_text`; caso contrário, pode usar a mensagem configurada em `POST_FINALIZE_REPLAY_MESSAGE`.
+
+O objetivo é proteger o fechamento lógico da sessão, especialmente em canais de voz onde pacotes de áudio podem chegar depois do evento de finalização.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```text
+Agente: "Atendimento concluído."
+→ sessão finalizada
+
+chega fragmento: "ã..."
+→ replay "Atendimento concluído."
+→ nenhum routing / tool / LLM novo
+```
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Se o estado terminal não for persistido, outra réplica pode reabrir a jornada.
+- Não use replay técnico/JSON como fala do cliente.
+- Essa feature não substitui política de nova sessão intencional.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/channels/interruption.py`
+- `libs/agent_framework/src/agent_framework/config/settings.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Prevents residual audio or late messages from reopening a session that has already been finalized.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+terminal session
+ ↓
+residual input
+ ↓
+policy detects finalization
+ ↓
+replay last utterance/fallback
+ ↓
+DO NOT reopen LangGraph
+```
+
+### 4. How it works internally
+
+The interruption policy checks terminal-session metadata before treating an input as a new intent. When terminal speech is available, it uses `last_assistant_text`/`terminal_replay_text`; otherwise it may use `POST_FINALIZE_REPLAY_MESSAGE`.
+
+The purpose is to protect the logical end of a session, especially on voice channels where audio packets may arrive after the finalization event.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```text
+Agent: "The interaction is complete."
+→ session finalized
+
+late fragment arrives: "uh..."
+→ replay "The interaction is complete."
+→ no new routing / tool / LLM call
+```
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- If terminal state is not persisted, another replica may reopen the journey.
+- Do not replay technical/JSON envelopes as user-facing speech.
+- This feature does not replace an intentional new-session policy.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/channels/interruption.py`
+- `libs/agent_framework/src/agent_framework/config/settings.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/15_retrieval_tool_guardrails.md b/agent_framework_oci/docs/features/15_retrieval_tool_guardrails.md
new file mode 100644
index 0000000..0a565a0
--- /dev/null
+++ b/agent_framework_oci/docs/features/15_retrieval_tool_guardrails.md
@@ -0,0 +1,171 @@
+# Guardrails de Retrieval e Tools / Retrieval / Tool Guardrails
+
+> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN.
+
+**Implementação principal / Main implementation:** `guardrails/pipeline.py + guardrails/rails.py`
+
+---
+
+## Português (PT-BR)
+
+### 1. O que é
+
+Aplica proteção não apenas na mensagem do usuário e na resposta final, mas também no conhecimento recuperado por RAG e nos argumentos/resultados de ferramentas.
+
+### 2. Problema que resolve
+
+Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio.
+
+### 3. Fluxo simplificado
+
+```text
+Usuário
+ ↓
+Input Guardrails
+ ↓
+RAG → Retrieval Guardrails
+ ↓
+LLM/Tool call → Tool Guardrails
+ ↓
+API
+ ↓
+Output Guardrails
+```
+
+### 4. Como funciona internamente
+
+O framework possui stages distintos de guardrails. Para retrieval, rails como `RAGSEC` e `RET_REL` podem validar segurança e relevância do conteúdo recuperado. Para tools, `TOOL_VAL` valida o uso/argumentos antes ou ao redor da execução.
+
+As configurações globais incluem `ENABLE_INPUT_GUARDRAILS`, `ENABLE_OUTPUT_GUARDRAILS`, `ENABLE_PARALLEL_GUARDRAILS`, `GUARDRAILS_FAIL_FAST` e `GUARDRAILS_CONFIG_PATH`. O YAML é a fonte de verdade dos rails ativados por agente.
+
+### 5. Como ativar/configurar
+
+A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow.
+
+### 6. Exemplo
+
+```yaml
+retrieval:
+ rails:
+ - RAGSEC
+ - RET_REL
+
+tool:
+ rails:
+ - TOOL_VAL
+```
+
+Exemplo: a pergunta é sobre cancelamento de um serviço, mas o RAG retorna documentação de modem. `RET_REL` pode rejeitar o contexto antes que ele seja usado na resposta.
+
+### 7. Telemetria e observabilidade
+
+Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio.
+
+### 8. Como testar
+
+1. Crie um teste unitário do comportamento principal.
+2. Crie um teste de integração do runtime quando houver estado entre turns.
+3. Verifique o caso feliz e pelo menos um caso de falha/negação.
+4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações.
+5. Em produção, valide também telemetria e correlação de IDs.
+
+### 9. Erros comuns
+
+- Ter a implementação do rail não significa que ele está ativo: confira `guardrails.yaml`.
+- Fail-fast deve ser escolhido conscientemente para cada stage.
+- Tool guardrail não substitui validação de negócio dentro da própria API/action.
+
+### 10. Relação com outras features
+
+Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**.
+
+### 11. Referências no repositório
+
+- `libs/agent_framework/src/agent_framework/guardrails/pipeline.py`
+- `libs/agent_framework/src/agent_framework/guardrails/rails.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
+
+---
+
+## English (EN)
+
+### 1. What it is
+
+Applies safety and validation not only to user input and final output, but also to RAG-retrieved knowledge and tool arguments/results.
+
+### 2. Problem it solves
+
+Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code.
+
+### 3. Simplified flow
+
+```text
+User
+ ↓
+Input Guardrails
+ ↓
+RAG → Retrieval Guardrails
+ ↓
+LLM/Tool call → Tool Guardrails
+ ↓
+API
+ ↓
+Output Guardrails
+```
+
+### 4. How it works internally
+
+The framework has distinct guardrail stages. For retrieval, rails such as `RAGSEC` and `RET_REL` can validate retrieved-content safety and relevance. For tools, `TOOL_VAL` validates usage/arguments before or around execution.
+
+Global settings include `ENABLE_INPUT_GUARDRAILS`, `ENABLE_OUTPUT_GUARDRAILS`, `ENABLE_PARALLEL_GUARDRAILS`, `GUARDRAILS_FAIL_FAST`, and `GUARDRAILS_CONFIG_PATH`. The agent YAML is the source of truth for enabled rails.
+
+### 5. How to enable/configure
+
+Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow.
+
+### 6. Example
+
+```yaml
+retrieval:
+ rails:
+ - RAGSEC
+ - RET_REL
+
+tool:
+ rails:
+ - TOOL_VAL
+```
+
+Example: the question concerns canceling a service, but RAG retrieves modem documentation. `RET_REL` can reject that context before it is used in the answer.
+
+### 7. Telemetry and observability
+
+When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain.
+
+### 8. How to test
+
+1. Add a unit test for the core behavior.
+2. Add a runtime integration test when state spans multiple turns.
+3. Test the happy path and at least one failure/rejection path.
+4. Confirm retries/replays do not duplicate side effects for transactional features.
+5. In production, also validate telemetry and ID correlation.
+
+### 9. Common mistakes
+
+- Having a rail implementation does not mean it is enabled: check `guardrails.yaml`.
+- Fail-fast behavior should be chosen intentionally for each stage.
+- Tool guardrails do not replace business validation inside the API/action itself.
+
+### 10. Relationship with other features
+
+Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**.
+
+### 11. Repository references
+
+- `libs/agent_framework/src/agent_framework/guardrails/pipeline.py`
+- `libs/agent_framework/src/agent_framework/guardrails/rails.py`
+- `Tuning-Performance/`
+- `Documentacao/`
+- `libs/agent_framework/docs/`
diff --git a/agent_framework_oci/docs/features/README.md b/agent_framework_oci/docs/features/README.md
index d09ad96..f925f85 100644
--- a/agent_framework_oci/docs/features/README.md
+++ b/agent_framework_oci/docs/features/README.md
@@ -1,10 +1,62 @@
-# Feature Guides / Guias de Features
+# Feature Guide — agent_framework_oci
-Escolha o idioma / Choose a language:
+Guia consolidado e bilíngue das principais capabilities horizontais do framework. Cada página contém **Português (PT-BR)** e **English (EN)** no mesmo arquivo.
-- [Português (PT-BR)](pt-BR/README.md)
-- [English (EN)](en/README.md)
+Consolidated bilingual guide for the framework's main horizontal capabilities. Each page contains **Portuguese (PT-BR)** and **English (EN)** in the same file.
-Os 15 arquivos bilíngues originais continuam neste diretório para compatibilidade, mas as árvores `pt-BR/` e `en/` são as versões recomendadas para leitura e distribuição.
+| # | Feature |
+|---:|---|
+| 01 | [Autenticação / Authentication](01_authentication.md) |
+| 02 | [Workflow Transacional Determinístico / Deterministic Transactional Workflow](02_deterministic_transactional_workflow.md) |
+| 03 | [Composição por LLM Solicitada pelo Domínio / Domain Requested LLM Composition](03_domain_requested_llm_composition.md) |
+| 04 | [RAG Solicitado pelo Domínio / Domain Requested RAG](04_domain_requested_rag.md) |
+| 05 | [Memória de Longo Prazo / Long Term Memory](05_long_term_memory.md) |
+| 06 | [Regressão Offline de Workflow / Offline Workflow Regression](06_offline_workflow_regression.md) |
+| 07 | [Pause / Resume de Workflow / Pause / Resume Workflow](07_pause_resume_workflow.md) |
+| 08 | [Aderência de Rota / Route Stickiness](08_route_stickiness.md) |
+| 09 | [Replay em Interrupções de Voz / Voice Interruption Replay](09_voice_interruption_replay.md) |
+| 10 | [Recuperação de Erro em Workflow / Workflow Error Recovery](10_workflow_error_recovery.md) |
+| 11 | [Clarificação / Clarification](11_clarification.md) |
+| 12 | [Idempotência Durável / Durable Idempotency](12_durable_idempotency.md) |
+| 13 | [Estados Transacionais Dinâmicos / Dynamic Transaction States](13_dynamic_transaction_states.md) |
+| 14 | [Replay Após Finalização / Post Finalization Replay](14_post_finalization_replay.md) |
+| 15 | [Guardrails de Retrieval e Tools / Retrieval / Tool Guardrails](15_retrieval_tool_guardrails.md) |
-The original 15 bilingual files remain in this directory for backward compatibility, but the `pt-BR/` and `en/` trees are the recommended versions for reading and distribution.
+## Mapa conceitual / Conceptual map
+
+```text
+LLM
+│
+├── Conversation
+│ ├── Clarification
+│ ├── Route Stickiness
+│ └── Long Term Memory
+│
+├── Knowledge
+│ ├── Domain Requested RAG
+│ └── Retrieval Guardrails
+│
+├── Transactions
+│ ├── Deterministic Transactional Workflow
+│ ├── Pause / Resume
+│ ├── Dynamic Transaction States
+│ ├── Durable Idempotency
+│ └── Workflow Error Recovery
+│
+├── Response
+│ └── Domain Requested LLM Composition
+│
+├── Voice
+│ ├── Voice Interruption Replay
+│ └── Post Finalization Replay
+│
+└── Platform
+ ├── Authentication
+ └── Offline Workflow Regression
+```
+
+## Princípio de arquitetura / Architecture principle
+
+**PT-BR:** o LLM entende e redige; o framework controla estado, segurança, memória, roteamento e transações; o domínio contém regras específicas de negócio.
+
+**EN:** the LLM understands and writes; the framework controls state, security, memory, routing, and transactions; the domain contains business-specific rules.
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc
index 91d91f9..12a77f9 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/gateway_policy_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/gateway_policy_context.cpython-313.pyc
new file mode 100644
index 0000000..83fa4b8
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/gateway_policy_context.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc
index 61332e2..dafbb1f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc
index e98a68a..7d09d87 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/runtime_mcp_gateway_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/runtime_mcp_gateway_adapter.cpython-313.pyc
new file mode 100644
index 0000000..562f4db
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/runtime_mcp_gateway_adapter.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc
index 049a94a..da880a8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc
index 20d5e8d..a4bf51c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc
index c168703..8550871 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc
index 208b638..7aa94cb 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc
index 44f98cf..9d2f0df 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc
index f5d7804..02c8aae 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc
index d644694..65c2810 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc
index d6bfed6..144ae86 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc
index a235c8b..a0b1b2e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc
index d76f4f5..4126c11 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc
index 29fb75c..8456799 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc
index 397f7c9..b7d99e4 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc
index 91cee2e..f4a8983 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc
index 865873c..67c4c0c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc
index a47cdc1..2cc5683 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc
index c70523c..1778a22 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc
index 4134a81..4e948c1 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc
index e045b4c..df8eb6f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc
index 073a05f..337545b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc
index 58373c1..3871ce3 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc
index 266a8cf..3e20c90 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc
index 5396652..ebad7ae 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc
index 209342c..c0e39fd 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc
index 52e8bdb..9111f5b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc
index 42367be..3612f13 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py
index c82da75..f060ccd 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py
@@ -26,104 +26,11 @@ from .checkpoint_repository import create_checkpoint_repository
def _jsonable(value: Any) -> Any:
- """Convert a value to a JSON-safe tree without collapsing containers to strings.
-
- The previous implementation used ``json.dumps(..., default=str)`` only as a
- probe and then returned the *original* object. Because ``default=str`` makes
- virtually every object serializable, the fallback branch was never reached.
- JSON repositories could therefore stringify arbitrary nested LangGraph objects
- later, and restored checkpoints would contain strings where mappings were
- required (for example ``metadata``, ``versions_seen`` or ``checkpoint_map``).
-
- Keep dict/list structure durable and stringify only unsupported leaf objects.
- """
- if value is None or isinstance(value, (str, int, float, bool)):
+ try:
+ json.dumps(value, default=str)
return value
- if isinstance(value, dict):
- return {str(k): _jsonable(v) for k, v in value.items()}
- if isinstance(value, (list, tuple, set, frozenset)):
- return [_jsonable(v) for v in value]
-
- model_dump = getattr(value, "model_dump", None)
- if callable(model_dump):
- try:
- return _jsonable(model_dump(mode="python"))
- except TypeError:
- return _jsonable(model_dump())
- except Exception:
- pass
-
- # Unsupported runtime objects are not durable. At this point the object is a
- # leaf; converting only the leaf to text cannot destroy an enclosing mapping.
- return str(value)
-
-
-def _mapping(value: Any, *, default: dict[str, Any] | None = None) -> dict[str, Any]:
- """Recover a mapping from native or legacy JSON-string values.
-
- Legacy rows written with permissive ``default=str`` serializers may contain a
- JSON object encoded as text. Parse that representation when possible; if the
- value is an opaque string, return a safe empty/default mapping instead of
- letting LangGraph fail with ``'str' object has no attribute 'items'``.
- """
- if isinstance(value, dict):
- return value
- if isinstance(value, str):
- try:
- decoded = json.loads(value)
- except Exception:
- decoded = None
- if isinstance(decoded, dict):
- return decoded
- return dict(default or {})
-
-
-def _normalize_checkpoint(checkpoint: Any) -> dict[str, Any]:
- """Normalize LangGraph checkpoint mapping fields after JSON persistence."""
- cp = _mapping(checkpoint)
- if not cp:
- return {}
- cp = dict(cp)
- cp["channel_values"] = _mapping(cp.get("channel_values"))
- cp["channel_versions"] = _mapping(cp.get("channel_versions"))
-
- versions_seen = _mapping(cp.get("versions_seen"))
- cp["versions_seen"] = {
- str(node): _mapping(versions) for node, versions in versions_seen.items()
- }
-
- pending_sends = cp.get("pending_sends")
- if pending_sends is None:
- cp["pending_sends"] = []
- elif not isinstance(pending_sends, list):
- cp["pending_sends"] = [pending_sends]
- return cp
-
-
-def _durable_config(config: dict[str, Any] | None) -> dict[str, Any] | None:
- """Return the persistable subset of a LangGraph RunnableConfig.
-
- LangGraph injects private execution objects under ``configurable`` (notably
- ``__pregel_runtime``). Those objects are valid only for the current graph
- invocation and must never be persisted by a checkpointer. JSON-backed
- repositories stringify them; on restore LangGraph then sees that string as
- its runtime object and calls ``.override(...)``, producing errors such as
- ``AttributeError: 'str' object has no attribute 'override'`` before the first
- workflow node runs.
-
- Durable checkpoint identity lives in the normal configurable keys
- (thread_id/checkpoint_ns/checkpoint_id). User-defined configurable values are
- preserved; only LangGraph's private ``__pregel_*`` execution keys are removed.
- """
- if config is None:
- return None
- cleaned = dict(_mapping(config))
- configurable = dict(_mapping(cleaned.get("configurable")))
- for key in list(configurable):
- if str(key).startswith("__pregel_"):
- configurable.pop(key, None)
- cleaned["configurable"] = configurable
- return cleaned
+ except TypeError:
+ return json.loads(json.dumps(value, default=str))
def _thread_id(config: dict[str, Any] | None) -> str:
@@ -178,13 +85,6 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
"""Checkpoint saver nativo para LangGraph usando os repositories do framework."""
def __init__(self, settings, repository=None):
- # BaseCheckpointSaver initializes LangGraph serializer state in current
- # checkpoint releases. Keep compatibility with the lightweight fallback
- # class used by framework-only unit tests.
- try:
- super().__init__()
- except TypeError:
- pass
self.settings = settings
self.repository = repository or create_checkpoint_repository(settings)
self._loop: asyncio.AbstractEventLoop | None = None
@@ -203,10 +103,10 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
def _make_tuple(self, payload: dict[str, Any] | None):
if not payload:
return None
- config = _durable_config(payload.get("config")) or {"configurable": {"thread_id": payload.get("thread_id")}}
- checkpoint = _normalize_checkpoint(payload.get("checkpoint"))
- metadata = _mapping(payload.get("metadata"))
- parent_config = _durable_config(payload.get("parent_config"))
+ config = payload.get("config") or {"configurable": {"thread_id": payload.get("thread_id")}}
+ checkpoint = payload.get("checkpoint") or {}
+ metadata = payload.get("metadata") or {}
+ parent_config = payload.get("parent_config")
pending_writes = _normalize_pending_writes(payload.get("pending_writes") or [])
try:
from langgraph.checkpoint.base import CheckpointTuple
@@ -229,13 +129,11 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
async def aput(self, config: dict[str, Any], checkpoint: dict[str, Any], metadata: dict[str, Any] | None = None, new_versions: dict[str, Any] | None = None):
thread_id = _thread_id(config)
checkpoint_id = _checkpoint_id(checkpoint)
- base_config = _durable_config(config) or {}
next_config = {
- **base_config,
+ **(config or {}),
"configurable": {
- **_mapping(base_config.get("configurable")),
+ **((config or {}).get("configurable") or {}),
"thread_id": thread_id,
- "checkpoint_ns": str(_mapping(base_config.get("configurable")).get("checkpoint_ns") or ""),
"checkpoint_id": checkpoint_id,
},
}
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc
index 62b2e6f..762487f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc
index 1971422..600ca2f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc
index 9a279ad..d14423b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..25db31f
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/oci_streaming.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/oci_streaming.cpython-313.pyc
new file mode 100644
index 0000000..6f0b4bf
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/oci_streaming.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc
index 59be44e..f4d573b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc
index 4c5a88a..ee7cc15 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..6bfab5a
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/client.cpython-313.pyc
new file mode 100644
index 0000000..7c4d856
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/config.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/config.cpython-313.pyc
new file mode 100644
index 0000000..16c4b37
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/config.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/models.cpython-313.pyc
new file mode 100644
index 0000000..527a4a1
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/router.cpython-313.pyc
new file mode 100644
index 0000000..ef4b2a9
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/router.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/session_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/session_store.cpython-313.pyc
new file mode 100644
index 0000000..43f4453
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/session_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc
index 011d42e..f275cd0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc
index 61d7d96..8a466f4 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc
index a8427d2..26b9c5f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc
index c59a715..1edd310 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/executor.cpython-313.pyc
new file mode 100644
index 0000000..d2a59de
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/executor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc
index ef2258f..3ac5185 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/langgraph_adapters.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/langgraph_adapters.cpython-313.pyc
new file mode 100644
index 0000000..359c587
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/langgraph_adapters.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc
index 1e613f4..f78d6e3 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc
index b1ab5e7..03df7c1 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc
index 197f9a0..1105f13 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc
index 581e246..2e0eef0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc
index 2f1e12b..083aa86 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc
index a0ceb7b..66859b2 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc
index 95396b8..d026f5c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc
index 35dd677..25d63db 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc
index b90e249..9a1b904 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc
index 9e5f483..ddeb740 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/config.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/config.cpython-313.pyc
new file mode 100644
index 0000000..1d50f43
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/config.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc
index 213ca47..9834ffd 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc
index 0076f8e..2e8ea43 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc
index f8e3ae6..d149e1a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc
index fc8a59a..bfc0458 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc
index 001cf91..d75d2ed 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc
index 1be5ca8..1336193 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc
index 29e50e6..cff1ad3 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc
index 765f023..6aef87c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py
index dc3d302..58bdc74 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py
@@ -129,6 +129,8 @@ def _is_vas_section_name(section_name: str) -> bool:
or "servicos de valor adicionado" in normalized
or "servicos valor adicionado" in normalized
or "sva detalhe total" in normalized
+ or "servicos contratados de parceiros" in normalized
+ or "servico contratado de parceiro" in normalized
)
@@ -185,13 +187,23 @@ def _extract_contestation_invoice_items(
"validatedAmount",
)
if candidate_name and candidate_amount is not None and candidate_amount > 0:
+ payload_type = str(payload.get("type") or payload.get("tipo") or "").strip()
+ payload_desc = str(payload.get("desc") or "").strip()
+ classe = str(payload.get("classe", "")).strip().lower()
+ is_vas = (
+ _is_vas_section_name(section_name)
+ or _is_vas_section_name(payload_type)
+ or classe in {"avulso", "estrategico"}
+ )
found.append(
{
"name": candidate_name,
"amount": _money(candidate_amount),
- "is_vas": _is_vas_section_name(section_name),
+ "is_vas": is_vas,
"section": section_name,
- "classe": str(payload.get("classe", "")).strip().lower(),
+ "source_type": payload_type,
+ "source_desc": payload_desc,
+ "classe": classe,
"estrategico": bool(payload.get("estrategico")),
"verb": str(payload.get("verb", "")).strip().lower(),
}
@@ -397,14 +409,25 @@ def validate_contestation_items(
"vas_estrategico": False,
"status": "em_validacao",
}
- matched_candidate = next(
- (
- candidate
- for candidate in candidates
- if _is_same_plan_name(candidate.get("name", ""), item_name)
- ),
- None,
+ matching_candidates = [
+ candidate
+ for candidate in candidates
+ if _is_same_plan_name(candidate.get("name", ""), item_name)
+ ]
+ # A mesma cobrança pode aparecer em múltiplas visões da fatura.
+ # Prefira a evidência que traz classificação explícita de VAS em vez
+ # de aceitar a primeira ocorrência genérica e concluir incorretamente
+ # que o item está fora da seção VAS.
+ matching_candidates.sort(
+ key=lambda candidate: (
+ 0 if (
+ str(candidate.get("classe", "")).strip().lower() in {"avulso", "estrategico"}
+ or bool(candidate.get("is_vas"))
+ ) else 1,
+ 0 if _normalize_match_text(candidate.get("name", "")) == _normalize_match_text(item_name) else 1,
+ )
)
+ matched_candidate = matching_candidates[0] if matching_candidates else None
if matched_candidate is None:
_record_failure(
item_log,
@@ -414,6 +437,9 @@ def validate_contestation_items(
continue
item_log["item_na_fatura"] = True
item_log["item_confirmado"] = True
+ item_log["item_fatura_resolvido"] = str(matched_candidate.get("name", "") or "")
+ item_log["secao_fatura"] = str(matched_candidate.get("section", "") or "")
+ item_log["tipo_fatura"] = str(matched_candidate.get("source_type", "") or "")
classe = str(matched_candidate.get("classe", "")).strip().lower()
is_strategic = (
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc
index 37db4aa..68e8eef 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc
index 905bf3d..3f7f8da 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc
index 592eec5..3c8735d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc
index 3481bcf..bacf727 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc
index dbea066..d3762cc 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc
index a749b18..2a3ee05 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc
index ce13289..7938ad5 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc
index 62bebad..384d9fa 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc
index 4274e7e..27f4ead 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc
index 7c189e0..c73d159 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc
index fa42f8b..791484f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc
index 902222b..cfbda6f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/safe_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/safe_out.cpython-313.pyc
new file mode 100644
index 0000000..0c08d83
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/safe_out.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc
index eb9a0ae..0bc035c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc
index 48bd092..22fd64c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..eb1d9a3
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/supervision_template.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/supervision_template.cpython-313.pyc
new file mode 100644
index 0000000..4d96758
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/supervision_template.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/tts_rules.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/tts_rules.cpython-313.pyc
new file mode 100644
index 0000000..bbf441d
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/tts_rules.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc
index 2f1934b..401d555 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc
index 5e3a917..d2b6615 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc
index 1f3bf04..6a18f89 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc
index 3402eae..c26f95a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc
index 3cf3cf8..8039453 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc
index a8f3259..1694530 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc
index 4810ab0..76358fb 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc
index ac602c5..32396bf 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc
index 5eb9f1c..023b52c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..6d92edd
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/correspondencia_item.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/correspondencia_item.cpython-313.pyc
new file mode 100644
index 0000000..693e8a8
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/correspondencia_item.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/groundedness.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/groundedness.cpython-313.pyc
new file mode 100644
index 0000000..ab54fd4
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/groundedness.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/intencao_cancelar.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/intencao_cancelar.cpython-313.pyc
new file mode 100644
index 0000000..4372f5e
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/intencao_cancelar.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/quantidade_coerente.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/quantidade_coerente.cpython-313.pyc
new file mode 100644
index 0000000..8a5d4ee
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/quantidade_coerente.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/servico_correto.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/servico_correto.cpython-313.pyc
new file mode 100644
index 0000000..8bdf382
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/servico_correto.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/verbalizacao_prematura.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/verbalizacao_prematura.cpython-313.pyc
new file mode 100644
index 0000000..d2b7d24
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/verbalizacao_prematura.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc
index 4a547bd..3f3f905 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc
index 8f3fd51..63d722a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/oos_blocklist.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/oos_blocklist.cpython-313.pyc
new file mode 100644
index 0000000..ef9f3f3
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/oos_blocklist.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc
index 35ef8dc..6836cd2 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc
index 940bc02..4e1446a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py
index 810e694..87f4d73 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py
@@ -328,47 +328,6 @@ class ProactiveOfferRail(Guardrail):
)
-def _sanitize_low_risk_phraseology(text: str, reason: str) -> str | None:
- """Remove apenas fechamentos/redirecionamentos de baixo risco.
-
- FRASEOLOGIA continua fail-closed para conteúdo material. Para B4 e ofertas
- genéricas de continuação, porém, bloquear toda uma resposta grounded piora a
- UX; nesses casos removemos somente a sentença ofensora.
- """
- normalized_reason = (reason or "").casefold()
- low_risk = any(token in normalized_reason for token in (
- "viola b4", "outro canal", "atendimento especializado",
- "realizar alguma ação", "oferta de ação", "orienta o cliente",
- ))
- if not low_risk:
- return None
-
- forbidden = (
- "entre em contato", "fale com um atendente", "procure uma loja",
- "acesse o app", "acesse o site", "atendimento especializado",
- "área de planos", "area de planos", "é só me avisar",
- "e so me avisar", "realizar alguma ação", "realizar alguma acao",
- "gerenciar esses serviços", "gerenciar esses servicos",
- )
- sentences = re.split(r"(?<=[.!?])\s+", (text or "").strip())
- kept: list[str] = []
- removed = False
- for sentence in sentences:
- normalized = sentence.casefold()
- proactive = (
- ("se quiser" in normalized or "caso queira" in normalized or "se desejar" in normalized)
- and any(token in normalized for token in ("realizar", "gerenciar", "cancelar", "contratar", "alterar", "ação", "acao"))
- )
- if proactive or any(token in normalized for token in forbidden):
- removed = True
- continue
- kept.append(sentence.strip())
- sanitized = " ".join(x for x in kept if x).strip()
- if removed and sanitized:
- return sanitized
- return None
-
-
class PhraseologyRail(Guardrail):
"""FRASEOLOGIA calibrado: bloqueia fraseados proibidos do agente."""
code = "FRASEOLOGIA"
@@ -380,26 +339,9 @@ class PhraseologyRail(Guardrail):
_llm(ctx), "FRASEOLOGIA", {"text": text or "", "context": ctx},
profile_name="grl", component_name="guardrail.fraseologia", generation_name="guardrail.fraseologia",
)
- allowed = bool(out.get("allowed", True))
- reason = str(out.get("reason") or out.get("label") or "FRASEOLOGIA avaliado")
- if not allowed:
- sanitized = _sanitize_low_risk_phraseology(text or "", reason)
- if sanitized:
- return RailDecision(
- code=self.code,
- allowed=True,
- reason=f"FRASEOLOGIA sanitizada: {reason}",
- sanitized_text=sanitized,
- metadata={
- "mechanism": "llm_rail+deterministic_sanitize",
- "data": out,
- "calibrated": True,
- "original_allowed": False,
- },
- )
return RailDecision(
- code=self.code, allowed=allowed,
- reason=reason,
+ code=self.code, allowed=bool(out.get("allowed", True)),
+ reason=str(out.get("reason") or out.get("label") or "FRASEOLOGIA avaliado"),
sanitized_text=text, metadata={"mechanism": "llm_rail", "data": out, "calibrated": True},
)
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc
index aedbf7c..d5d9eca 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc
index cd27ee8..e7dc3ac 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc
index 2d103d3..5e9d169 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc
index 40efe42..61fc64a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc
index 968dc08..93fa52b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc
index 4e3cad4..1bb78aa 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc
index adef518..23d17df 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/_compat.cpython-313.pyc
new file mode 100644
index 0000000..24ca7be
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/_compat.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc
index 0c7485d..b569f03 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/models.cpython-313.pyc
new file mode 100644
index 0000000..ca31105
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc
index e9f6114..ceebfe0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc
index acfb6af..a4145e0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc
index 1255fb1..2558fb8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc
index b9acc23..9d4070d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc
index 9847625..4bd6ec7 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc
index 6de755a..86e84c3 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc
index 680bdc2..0e67c29 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc
index 1e5bb24..56dde3e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc
index 336a5bb..2c9431a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc
index c50befd..cbab8e6 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc
index aad2b4b..7abd6a9 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc
index 0231a98..7588451 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc
index f0a67bc..f028303 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc
index fe91e27..2f4014b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc
index 2a42e4b..35e7890 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc
index e17ae95..8fe790a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py
index 98f1529..97046c3 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py
@@ -28,6 +28,11 @@ class MCPToolConfig(BaseModel):
execution_policy: dict[str, Any] = Field(default_factory=dict)
selection_keywords: list[str] = Field(default_factory=list)
+ # Política declarativa opcional de apresentação da resposta da tool.
+ # Para novos projetos prefira mode=renderer + renderer=.
+ # O framework resolve o nome no registry; a regra de negócio fica na aplicação.
+ response: dict[str, Any] = Field(default_factory=dict)
+
# Política declarativa de cache da tool, lida diretamente de config/tools.yaml.
# Exemplo:
# cache:
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py
index 6ac4308..b37efc8 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py
@@ -70,6 +70,7 @@ class MCPRegistry:
"confirmation_required": tool.confirmation_required,
"execution_policy": tool.execution_policy,
"selection_keywords": tool.selection_keywords,
+ "response": tool.response,
"cache": tool.cache,
})
return out
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc
index 2e2302f..fba6fec 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc
index 481b774..e87aa7e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc
index 579d73a..8e27b87 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc
index e18196f..334f559 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc
index d699e33..d7f8b96 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc
index 15c7fca..739b11c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc
index 0e22a97..6f30a8a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc
index 49ed10a..5580736 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc
index 468a8c2..b6e6beb 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc
index a2de671..1b50229 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc
index 19e39b4..134e279 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc
index 75e29dc..58b2177 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc
index 3c92f8d..bd4ee00 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/control_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/control_events.cpython-313.pyc
new file mode 100644
index 0000000..3e8308d
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/control_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/decorators.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/decorators.cpython-313.pyc
new file mode 100644
index 0000000..3e59d70
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/decorators.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc
index bb7f263..e1b3cb8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/grl_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/grl_events.cpython-313.pyc
new file mode 100644
index 0000000..a7c7b45
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/grl_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc
index 36a0644..a550677 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc
index 619b3cb..9f56a00 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/informational_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/informational_events.cpython-313.pyc
new file mode 100644
index 0000000..bcdbacf
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/informational_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc
index 4690c0c..bec5fe8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langfuse_enterprise.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langfuse_enterprise.cpython-313.pyc
new file mode 100644
index 0000000..1a3b13e
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langfuse_enterprise.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc
index 9e1f8ea..70ae7b1 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc
index 061ef4b..a52834f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc
index 85236a7..a65a790 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_events.cpython-313.pyc
new file mode 100644
index 0000000..8de88ae
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc
index 91103c9..a9ee1ac 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc
index 2b6482e..66abde1 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc
index b10b2ab..f9bc52a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc
index 5e0102f..f096622 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_exporter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_exporter.cpython-313.pyc
new file mode 100644
index 0000000..5fcb1d7
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_exporter.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc
index 68cf9aa..5795c3f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/tim_backoffice_contract.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/tim_backoffice_contract.cpython-313.pyc
new file mode 100644
index 0000000..34add0f
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/tim_backoffice_contract.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc
index a202423..da822db 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc
index 2b0fffa..2d5dbd6 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc
index 70847d7..a7536a0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc
index 461c790..120f556 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc
index dd7fec2..2bfe499 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/mongodb_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/mongodb_store.cpython-313.pyc
new file mode 100644
index 0000000..1f4357f
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/mongodb_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc
index 9bbbada..d299268 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc
index 5376044..f770641 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__init__.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__init__.py
new file mode 100644
index 0000000..a96f24f
--- /dev/null
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__init__.py
@@ -0,0 +1,15 @@
+from .renderers import (
+ ToolResponseRenderer,
+ ToolResponseRendererRegistry,
+ register_tool_response_renderer,
+ render_tool_response,
+ tool_response_renderers,
+)
+
+__all__ = [
+ "ToolResponseRenderer",
+ "ToolResponseRendererRegistry",
+ "register_tool_response_renderer",
+ "render_tool_response",
+ "tool_response_renderers",
+]
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..c0db803
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc
new file mode 100644
index 0000000..ea3f4c9
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/renderers.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/renderers.py
new file mode 100644
index 0000000..528359a
--- /dev/null
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/renderers.py
@@ -0,0 +1,104 @@
+from __future__ import annotations
+
+from collections.abc import Callable
+from threading import RLock
+from typing import Any, Protocol
+
+
+class ToolResponseRenderer(Protocol):
+ def __call__(
+ self,
+ *,
+ tool_name: str,
+ result: dict[str, Any],
+ state: dict[str, Any],
+ agent_label: str,
+ ) -> str | None: ...
+
+
+class ToolResponseRendererRegistry:
+ """Thread-safe registry for application/domain response renderers.
+
+ The framework stores only symbolic renderer names. Business-specific
+ formatting lives in the application that registers the renderer.
+ """
+
+ def __init__(self) -> None:
+ self._renderers: dict[str, ToolResponseRenderer] = {}
+ self._lock = RLock()
+
+ def register(
+ self,
+ name: str,
+ renderer: ToolResponseRenderer,
+ *,
+ replace: bool = True,
+ ) -> None:
+ key = str(name or "").strip()
+ if not key:
+ raise ValueError("renderer name must not be empty")
+ if not callable(renderer):
+ raise TypeError("renderer must be callable")
+ with self._lock:
+ if not replace and key in self._renderers:
+ raise KeyError(f"renderer already registered: {key}")
+ self._renderers[key] = renderer
+
+ def get(self, name: str | None) -> ToolResponseRenderer | None:
+ key = str(name or "").strip()
+ if not key:
+ return None
+ with self._lock:
+ return self._renderers.get(key)
+
+ def render(
+ self,
+ name: str | None,
+ *,
+ tool_name: str,
+ result: dict[str, Any],
+ state: dict[str, Any],
+ agent_label: str,
+ ) -> str | None:
+ renderer = self.get(name)
+ if renderer is None:
+ return None
+ value = renderer(
+ tool_name=tool_name,
+ result=result,
+ state=state,
+ agent_label=agent_label,
+ )
+ if value is None:
+ return None
+ text = str(value).strip()
+ return text or None
+
+
+tool_response_renderers = ToolResponseRendererRegistry()
+
+
+def register_tool_response_renderer(
+ name: str,
+ renderer: ToolResponseRenderer,
+ *,
+ replace: bool = True,
+) -> None:
+ tool_response_renderers.register(name, renderer, replace=replace)
+
+
+def render_tool_response(
+ name: str | None,
+ *,
+ tool_name: str,
+ result: dict[str, Any],
+ state: dict[str, Any],
+ agent_label: str,
+) -> str | None:
+ return tool_response_renderers.render(
+ name,
+ tool_name=tool_name,
+ result=result,
+ state=state,
+ agent_label=agent_label,
+ )
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc
index 94023bb..7c9bf3f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc
index fc5aed8..47239be 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc
index d2e385b..25ef9ea 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc
index 536008b..c11503f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc
index bd9a1f3..66466a4 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc
index 0d21834..bb0e699 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc
index 9c09002..5fae24b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc
index 7804b80..4d042e3 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc
index 25cb799..6a14a96 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc
index 66b593c..883a239 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc
index 51dfa9f..d8940a4 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc
index 91a52a9..b755554 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc
index 9e388b3..4068e3d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py
index 534269d..f4d228a 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py
@@ -73,7 +73,6 @@ class EnterpriseRouter:
if (
active_agent
and keyword_candidate is not None
- and keyword_candidate.agent != active_agent
and keyword_candidate.intent != previous_intent
and self._is_explicit_intent_shift(keyword_candidate)
):
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc
index e288f43..3cd7a85 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc
index 9320eaa..cfe0d87 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py
index 2580f43..af6c569 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py
@@ -4,6 +4,7 @@ import hashlib
import json
import logging
import re
+import uuid
from dataclasses import dataclass, field
from typing import Any, Iterable, Mapping
@@ -15,6 +16,21 @@ logger = logging.getLogger(__name__)
_EMPTY_VALUES = (None, "", {}, [])
+_ACTIVE_TRANSACTION_STATUSES = {
+ "COLLECTING_PARAMETERS",
+ "AWAITING_CONFIRMATION",
+ "WORKFLOW_PAUSED",
+ "TOOL_RESULT_CLARIFICATION",
+ "EXECUTING",
+}
+_TERMINAL_TRANSACTION_STATUSES = {
+ "COMPLETED",
+ "FAILED",
+ "CANCELLED",
+ "BLOCKED",
+ "OUT_OF_SCOPE",
+}
+
@dataclass(slots=True)
class RuntimeContext:
@@ -519,11 +535,42 @@ class AgentRuntimeMixin:
return str(response.get("content") or response.get("text") or response.get("answer") or "")
return str(getattr(response, "content", None) or getattr(response, "text", None) or response)
+ def _drop_stale_message_extracted_arguments(
+ self,
+ tool_name: str,
+ arguments: dict[str, Any],
+ *,
+ explicit_fields: Iterable[str] = (),
+ ) -> dict[str, Any]:
+ """Remove valores herdados para campos cujo contrato diz ``from: message``.
+
+ Em uma NOVA transação, ``context.tool_arguments`` pode ainda carregar
+ parâmetros de uma operação anterior. Campos declarados pelo mapper como
+ extraídos da mensagem corrente não podem nascer desse contexto antigo.
+ Valores explicitamente extraídos deterministicamente do turno atual são
+ preservados. Durante coleta incremental este helper não é usado.
+ """
+ router = getattr(self, "tool_router", None)
+ if not router or not hasattr(router, "parameter_extract_rules"):
+ return dict(arguments or {})
+ rules = router.parameter_extract_rules(tool_name) or {}
+ explicit = {str(name) for name in explicit_fields}
+ cleaned = dict(arguments or {})
+ for field_name, rule in rules.items():
+ if (
+ str(rule.get("from") or "message").lower() == "message"
+ and str(field_name) not in explicit
+ ):
+ cleaned.pop(str(field_name), None)
+ return cleaned
+
async def _extract_mcp_parameters(
self,
tool_name: str,
arguments: dict[str, Any],
state: dict[str, Any],
+ *,
+ overwrite_from_message: bool = False,
) -> dict[str, Any]:
"""Executa regras ``extract`` declaradas para a tool escolhida.
@@ -544,9 +591,13 @@ class AgentRuntimeMixin:
llm = getattr(self, "llm", None)
for field_name, rule in rules.items():
- if resolved.get(field_name) not in _EMPTY_VALUES:
+ from_message = str(rule.get("from") or "message").lower() == "message"
+ if not from_message:
continue
- if str(rule.get("from") or "message").lower() != "message":
+ # Em uma nova transação, a mensagem atual prevalece para campos
+ # declarados como ``from: message``. Durante COLLECTING_PARAMETERS
+ # o default permanece False para congelar valores já coletados.
+ if resolved.get(field_name) not in _EMPTY_VALUES and not overwrite_from_message:
continue
strategy = str(rule.get("strategy") or "llm").lower()
value: Any = None
@@ -1167,19 +1218,13 @@ class AgentRuntimeMixin:
return None
def _select_transactional_tool(self, tools: list[str], text: str) -> str | None:
- """Seleciona a ação transacional da intent atual.
-
- O match por ``selection_keywords`` continua tendo precedência. Porém, depois
- que o EnterpriseRouter já restringiu ``tools`` às capabilities da intent,
- uma única tool transacional é uma escolha determinística e segura. Isso
- evita perder frases naturais como ``quero cancelar TIM Fashion Mensal`` ou
- ``não contratei esse serviço`` só porque elas não repetem literalmente uma
- keyword de ``tools.yaml``.
- """
matched = self._transactional_action_match(text, tools)
if matched:
return matched
+ # Generic fallback: once routing has constrained the allowlist, a single
+ # transactional capability is unambiguous even when the user's wording
+ # does not contain one of the tool-specific selection keywords.
transactional = [
tool
for tool in tools
@@ -1348,12 +1393,112 @@ class AgentRuntimeMixin:
state["transaction_status"] = "COMPLETED" if result.get("ok") else "FAILED"
return result
+ @staticmethod
+ def _transaction_is_active(state: dict[str, Any]) -> bool:
+ return str(state.get("transaction_status") or "") in _ACTIVE_TRANSACTION_STATUSES
+
+ @staticmethod
+ def _transaction_is_terminal(state: dict[str, Any]) -> bool:
+ return str(state.get("transaction_status") or "") in _TERMINAL_TRANSACTION_STATUSES
+
+ def _active_transaction(self, state: dict[str, Any]) -> dict[str, Any] | None:
+ """Return only the operationally active transaction.
+
+ Closed transactions are history and must never provide tool/arguments for
+ a later turn. For backward compatibility, an old checkpoint that has the
+ legacy selected/pending fields but an ACTIVE status is lazily hydrated into
+ ``active_transaction``.
+ """
+ if not self._transaction_is_active(state):
+ return None
+ current = state.get("active_transaction")
+ if isinstance(current, dict) and current.get("tool_name"):
+ return current
+ legacy = state.get("pending_tool_call") or state.get("selected_tool_call") or {}
+ if not isinstance(legacy, dict) or not legacy.get("tool_name"):
+ return None
+ current = {
+ "transaction_id": str(uuid.uuid4()),
+ "tool_name": legacy.get("tool_name"),
+ "arguments": dict(legacy.get("arguments") or {}),
+ "status": state.get("transaction_status"),
+ "started_from_intent": state.get("intent"),
+ }
+ state["active_transaction"] = current
+ return current
+
+ def _set_active_transaction(
+ self,
+ state: dict[str, Any],
+ *,
+ tool_name: str,
+ arguments: dict[str, Any],
+ status: str,
+ transaction_id: str | None = None,
+ ) -> dict[str, Any]:
+ current = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {}
+ txid = transaction_id or current.get("transaction_id") or str(uuid.uuid4())
+ tx = {
+ "transaction_id": txid,
+ "tool_name": tool_name,
+ "arguments": dict(arguments or {}),
+ "status": status,
+ "started_from_intent": current.get("started_from_intent") or state.get("intent"),
+ }
+ state["active_transaction"] = tx
+ return tx
+
+ def _finish_active_transaction(self, state: dict[str, Any], status: str) -> None:
+ """Close the active transaction without leaving operational state behind."""
+ active = self._active_transaction(state)
+ if isinstance(active, dict):
+ state["last_transaction"] = {
+ **active,
+ "status": status,
+ }
+ state["active_transaction"] = None
+ state["selected_tool_call"] = {}
+ state["pending_tool_call"] = {}
+ state["missing_parameters"] = []
+ state["confirmation_required"] = False
+ state["confirmation_received"] = status == "COMPLETED"
+ state["next_state"] = None
+ state["transaction_status"] = status
+
+ def _normalize_transaction_lifecycle(self, state: dict[str, Any]) -> None:
+ """Ensure closed transactions cannot leak into a later user turn."""
+ if self._transaction_is_terminal(state):
+ # Preserve a compact audit snapshot, but remove every operational latch.
+ active = state.get("active_transaction")
+ if not isinstance(active, dict):
+ legacy = state.get("pending_tool_call") or state.get("selected_tool_call")
+ if isinstance(legacy, dict) and legacy.get("tool_name"):
+ active = {
+ "transaction_id": str(uuid.uuid4()),
+ "tool_name": legacy.get("tool_name"),
+ "arguments": dict(legacy.get("arguments") or {}),
+ "status": state.get("transaction_status"),
+ "started_from_intent": state.get("intent"),
+ }
+ if isinstance(active, dict):
+ state["last_transaction"] = {**active, "status": state.get("transaction_status")}
+ state["active_transaction"] = None
+ state["selected_tool_call"] = {}
+ state["pending_tool_call"] = {}
+ state["missing_parameters"] = []
+ state["confirmation_required"] = False
+ state["confirmation_received"] = False
+ state["next_state"] = None
+ return
+ if self._transaction_is_active(state):
+ self._active_transaction(state)
+
def transaction_state_patch(self, state: dict[str, Any]) -> dict[str, Any]:
keys = (
"available_mcp_tools", "selected_tool_call", "pending_tool_call",
"transaction_status", "confirmation_required", "confirmation_received",
"tool_policy_result", "missing_parameters", "next_state", "pending_domain_workflow", "pending_tool_clarification",
- "business_workflows_executed",
+ "business_workflows_executed", "active_transaction", "last_transaction",
)
return {key: state.get(key) for key in keys if key in state}
@@ -1411,6 +1556,9 @@ class AgentRuntimeMixin:
"next_state": collecting_state,
"tool_policy_result": {**policy, "tool_name": tool_name, "action": "collecting_parameters"},
})
+ self._set_active_transaction(
+ state, tool_name=tool_name, arguments=arguments, status="COLLECTING_PARAMETERS"
+ )
def transaction_confirmation_message(self, state: dict[str, Any]) -> str | None:
if state.get("transaction_status") != "AWAITING_CONFIRMATION":
@@ -1446,6 +1594,185 @@ class AgentRuntimeMixin:
matches.append(name)
return matches or available_tools[:1]
+ @staticmethod
+ def _response_path_get(data: Any, path: str | None) -> Any:
+ """Resolve caminho simples ``a.b.c`` em dicts sem conhecer o domínio."""
+ if not path:
+ return data
+ current = data
+ for part in str(path).split("."):
+ if isinstance(current, Mapping):
+ current = current.get(part)
+ else:
+ return None
+ return current
+
+ @staticmethod
+ def _response_format_value(value: Any, formatter: str | None) -> Any:
+ """Formatadores genéricos permitidos pela política declarativa de resposta."""
+ if formatter in (None, "", "raw"):
+ return value
+ if formatter == "decimal_2_comma":
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return value
+ if formatter == "decimal_2":
+ try:
+ return f"{float(value):.2f}"
+ except (TypeError, ValueError):
+ return value
+ if formatter == "str":
+ return "" if value is None else str(value)
+ return value
+
+ @classmethod
+ def _response_template(cls, template: str, data: Mapping[str, Any], formats: Mapping[str, Any] | None = None) -> str | None:
+ """Renderiza template somente se todos os placeholders existirem.
+
+ Isso evita respostas como ``None`` quando o contrato da tool não corresponde
+ à configuração. Nesse caso o runtime cai no fallback legado/LLM.
+ """
+ formats = formats or {}
+ names = set(re.findall(r"\{([A-Za-z_][A-Za-z0-9_]*)\}", str(template)))
+ values: dict[str, Any] = {}
+ for name in names:
+ if name not in data or data.get(name) is None:
+ return None
+ values[name] = cls._response_format_value(data.get(name), formats.get(name))
+ try:
+ return str(template).format(**values)
+ except Exception:
+ return None
+
+ def _render_declared_tool_response(self, tool_name: str | None, data: dict[str, Any], *, agent_label: str, state: dict[str, Any] | None = None) -> str | None:
+ """Renderiza resposta MCP por configuração, sem regras de negócio no core.
+
+ A configuração vive em ``tools.yaml`` e suporta primitives genéricas:
+ ``template``, ``list`` e ``lines``. Se não houver política, retorna ``None``
+ para preservar integralmente o comportamento legado.
+ """
+ router = getattr(self, "tool_router", None)
+ registry = getattr(router, "registry", None)
+ cfg = registry.get_tool(str(tool_name)) if registry and tool_name else None
+ policy = dict(getattr(cfg, "response", None) or {}) if cfg else {}
+ if not policy:
+ return None
+
+ mode = str(policy.get("mode") or "").strip().lower()
+
+ # Extensão preferencial: o core conhece apenas um nome simbólico.
+ # O código do renderer é registrado pela aplicação/domínio.
+ if mode == "renderer":
+ renderer_name = str(policy.get("renderer") or "").strip()
+ if not renderer_name:
+ return None
+ try:
+ from agent_framework.presentation import render_tool_response
+
+ return render_tool_response(
+ renderer_name,
+ tool_name=str(tool_name or ""),
+ result=data,
+ state=state or {},
+ agent_label=agent_label,
+ )
+ except Exception:
+ # Compatibilidade/fail-open: renderer ausente ou com erro não quebra
+ # agentes legados; o fluxo continua para o fallback existente.
+ return None
+
+ # Modos declarativos da versão anterior são preservados apenas por
+ # compatibilidade. Novos projetos devem usar mode=renderer.
+ base: dict[str, Any] = {**data, "agent_label": agent_label, "result": data}
+
+ if mode == "template":
+ template = policy.get("template")
+ if not template:
+ return None
+ # ``result`` pode ser usado para debug/compatibilidade; demais campos
+ # precisam existir para impedir None em texto de cliente.
+ if "{result}" in str(template):
+ try:
+ return str(template).replace("{result}", str(data)).replace("{agent_label}", agent_label)
+ except Exception:
+ return None
+ return self._response_template(str(template), base, policy.get("formats"))
+
+ if mode == "list":
+ items = self._response_path_get(data, policy.get("source"))
+ if not isinstance(items, list) or not items:
+ return str(policy.get("empty_message") or "").strip() or None
+ rendered_items: list[str] = []
+ item_template = str(policy.get("item_template") or "{item}")
+ item_formats = policy.get("item_formats") or {}
+ for raw in items:
+ if isinstance(raw, Mapping):
+ item_data = dict(raw)
+ else:
+ item_data = {"item": raw}
+ item_data["agent_label"] = agent_label
+ line = self._response_template(item_template, item_data, item_formats)
+ if line:
+ rendered_items.append(line)
+ if not rendered_items:
+ return None
+ count = len(rendered_items)
+ heading_template = policy.get("heading_singular") if count == 1 else policy.get("heading_plural")
+ heading = None
+ if heading_template:
+ heading = self._response_template(
+ str(heading_template),
+ {"agent_label": agent_label, "count": count},
+ )
+ sep = str(policy.get("separator") or "\n")
+ body = sep.join(rendered_items)
+ return f"{heading}\n{body}" if heading else body
+
+ if mode == "lines":
+ lines: list[str] = []
+ for spec in policy.get("lines") or []:
+ if not isinstance(spec, Mapping):
+ continue
+ kind = str(spec.get("kind") or "template")
+ if kind == "template":
+ when = spec.get("when_present")
+ if when and self._response_path_get(data, str(when)) is None:
+ continue
+ line = self._response_template(str(spec.get("template") or ""), base, spec.get("formats"))
+ if line:
+ lines.append(line)
+ elif kind == "list":
+ values = self._response_path_get(data, spec.get("source"))
+ if not isinstance(values, list) or not values:
+ continue
+ fields = list(spec.get("item_fields") or ["item"])
+ rendered: list[str] = []
+ for value in values:
+ if isinstance(value, Mapping):
+ chosen = next((value.get(f) for f in fields if value.get(f) not in _EMPTY_VALUES), None)
+ else:
+ chosen = value
+ if chosen not in _EMPTY_VALUES:
+ rendered.append(str(chosen))
+ if rendered:
+ lines.append(
+ str(spec.get("prefix") or "")
+ + str(spec.get("separator") or "; ").join(rendered)
+ + str(spec.get("suffix") or "")
+ )
+ if not lines:
+ return None
+ return str(policy.get("joiner") or " ").join(lines)
+
+ if mode == "field":
+ value = self._response_path_get(data, policy.get("field"))
+ return str(value).strip() if value not in _EMPTY_VALUES else None
+
+ if mode in {"llm", "none"}:
+ return None
+ return None
+
def build_direct_mcp_answer(self, state: dict[str, Any], mcp_results: list[dict[str, Any]], *, agent_label: str) -> str | None:
"""Resposta determinística para consultas estruturadas simples."""
requires_rag, _ = self._mcp_rag_directive(mcp_results)
@@ -1475,6 +1802,14 @@ class AgentRuntimeMixin:
return None
tool = ok[0].get("tool_name")
data = ok[0]["result"]
+
+ # Primeiro tenta o contrato genérico e declarativo de apresentação.
+ # Se a aplicação não o configurou, preserva exatamente o fallback legado
+ # abaixo para não quebrar projetos existentes.
+ declared = self._render_declared_tool_response(tool, data, agent_label=agent_label, state=state)
+ if declared is not None:
+ return declared
+
if tool == "consultar_pedido":
oid=data.get("order_id"); status=data.get("status"); total=data.get("valor_total")
lines=[f"[{agent_label}] Pedido {oid}: status {status}."]
@@ -1509,6 +1844,7 @@ class AgentRuntimeMixin:
available_tools = list(tools if tools is not None else (state.get("mcp_tools") or []))
state["available_mcp_tools"] = available_tools
text = state.get("sanitized_input") or state.get("user_text") or ""
+ self._normalize_transaction_lifecycle(state)
# Clarificação de resultado de tool tem precedência: reutiliza a mesma tool
# e argumentos, alterando apenas o parâmetro escolhido pelo usuário.
@@ -1524,7 +1860,7 @@ class AgentRuntimeMixin:
# Antes de confirmar, complete os parâmetros obrigatórios da ação.
if state.get("transaction_status") == "COLLECTING_PARAMETERS":
- selected = dict(state.get("selected_tool_call") or {})
+ selected = dict(self._active_transaction(state) or state.get("selected_tool_call") or {})
tool_name = selected.get("tool_name")
if tool_name:
previous_args = dict(selected.get("arguments") or {})
@@ -1535,14 +1871,20 @@ class AgentRuntimeMixin:
aliases=aliases,
extra_args=self._extract_action_arguments(text),
)
- arguments = {
- **previous_args,
- **{k: v for k, v in new_args.items() if v not in (None, "", [], {})},
- }
- # Execute parameter extraction before deciding whether the workflow
- # must enter COLLECTING_PARAMETERS. Otherwise parameters declared
- # with strategy=llm in mcp_parameter_mapping.yaml are invisible to
- # the deterministic transaction state machine.
+ # Durante coleta incremental, valores de contexto podem ainda conter
+ # parâmetros de uma operação anterior. O que já foi coletado para a
+ # transação pendente prevalece; o turno atual só preenche lacunas.
+ non_empty_new = {k: v for k, v in new_args.items() if v not in (None, "", [], {})}
+ arguments = {**non_empty_new, **previous_args}
+
+ # Campos de envelope pertencem ao turno corrente e devem permanecer
+ # atualizados, mesmo quando os parâmetros de negócio ficam congelados.
+ for per_turn_key in ("query", "operator_instructions", "interaction_key"):
+ if non_empty_new.get(per_turn_key) not in (None, "", [], {}):
+ arguments[per_turn_key] = non_empty_new[per_turn_key]
+
+ # Reutiliza o contrato declarativo para preencher somente os campos
+ # ainda faltantes; campos previamente coletados não são sobrescritos.
arguments = await self._extract_mcp_parameters(tool_name, arguments, state)
policy = self._resolve_tool_execution_policy(tool_name, arguments)
missing = self._missing_required_arguments(policy, arguments)
@@ -1562,6 +1904,9 @@ class AgentRuntimeMixin:
selected = {"tool_name": tool_name, "arguments": arguments}
state["selected_tool_call"] = selected
+ self._set_active_transaction(
+ state, tool_name=tool_name, arguments=arguments, status="COLLECTING_PARAMETERS"
+ )
state["missing_parameters"] = []
if policy.get("require_confirmation"):
waiting_state = self._waiting_state_name(state)
@@ -1573,6 +1918,9 @@ class AgentRuntimeMixin:
"next_state": waiting_state,
"tool_policy_result": {**policy, "tool_name": tool_name},
})
+ self._set_active_transaction(
+ state, tool_name=tool_name, arguments=arguments, status="AWAITING_CONFIRMATION"
+ )
return [{
"ok": True,
"executed": False,
@@ -1586,27 +1934,29 @@ class AgentRuntimeMixin:
result = await self._call_mcp_tool(tool_name, arguments, state)
self._capture_pending_domain_workflow(state, result)
self._capture_pending_tool_clarification(state, result, tool_name=tool_name, arguments=arguments)
- state.update({
- "transaction_status": ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))),
- "confirmation_required": False,
- "confirmation_received": True,
- "pending_tool_call": {},
- "missing_parameters": [],
- })
+ final_status = ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED")))
+ if final_status in _TERMINAL_TRANSACTION_STATUSES:
+ self._finish_active_transaction(state, final_status)
+ else:
+ state.update({
+ "transaction_status": final_status,
+ "confirmation_required": False,
+ "confirmation_received": True,
+ "pending_tool_call": {},
+ "missing_parameters": [],
+ })
+ self._set_active_transaction(
+ state, tool_name=tool_name, arguments=arguments, status=final_status
+ )
return [result]
- pending = state.get("pending_tool_call") or {}
+ active_tx = self._active_transaction(state)
+ pending = (active_tx if isinstance(active_tx, dict) and active_tx.get("status") == "AWAITING_CONFIRMATION" else state.get("pending_tool_call")) or {}
if pending:
decision = self._confirmation_decision(text)
if decision == "reject":
- state.update({
- "transaction_status": "CANCELLED",
- "confirmation_received": False,
- "confirmation_required": False,
- "selected_tool_call": pending,
- "pending_tool_call": {},
- "tool_policy_result": {"action": "cancelled", "tool_name": pending.get("tool_name")},
- })
+ state["tool_policy_result"] = {"action": "cancelled", "tool_name": pending.get("tool_name")}
+ self._finish_active_transaction(state, "CANCELLED")
return [{"ok": True, "tool_name": pending.get("tool_name"), "transaction_status": "CANCELLED", "cancelled": True}]
if decision == "confirm":
tool_name = pending.get("tool_name")
@@ -1616,17 +1966,26 @@ class AgentRuntimeMixin:
result = await self._call_mcp_tool(tool_name, arguments, state)
self._capture_pending_domain_workflow(state, result)
self._capture_pending_tool_clarification(state, result, tool_name=tool_name, arguments=arguments)
- state.update({
- "transaction_status": ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))),
- "confirmation_required": False,
- "selected_tool_call": pending,
- "pending_tool_call": {},
- "tool_policy_result": {"action": "executed_after_confirmation", "tool_name": tool_name},
- })
+ final_status = ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED")))
+ state["tool_policy_result"] = {"action": "executed_after_confirmation", "tool_name": tool_name}
+ if final_status in _TERMINAL_TRANSACTION_STATUSES:
+ self._finish_active_transaction(state, final_status)
+ else:
+ state.update({
+ "transaction_status": final_status,
+ "confirmation_required": False,
+ "pending_tool_call": {},
+ })
+ self._set_active_transaction(
+ state, tool_name=tool_name, arguments=arguments, status=final_status
+ )
results.append(result)
return results
state["transaction_status"] = "AWAITING_CONFIRMATION"
state["confirmation_required"] = True
+ self._set_active_transaction(
+ state, tool_name=str(pending.get("tool_name") or ""), arguments=dict(pending.get("arguments") or {}), status="AWAITING_CONFIRMATION"
+ )
return [{"ok": False, "tool_name": pending.get("tool_name"), "awaiting_confirmation": True, "transaction_status": "AWAITING_CONFIRMATION"}]
read_only_tools = [
@@ -1669,19 +2028,30 @@ class AgentRuntimeMixin:
if not selected_action:
return results
+ explicit_action_args = self._extract_action_arguments(text)
action_args = self.build_tool_arguments(
state,
tool_name=selected_action,
intent=state.get("intent"),
aliases=aliases,
- extra_args=self._extract_action_arguments(text),
+ extra_args=explicit_action_args,
+ )
+ # Nova transação: parâmetros declarados ``from: message`` não podem ser
+ # herdados de context.tool_arguments de uma operação anterior.
+ action_args = self._drop_stale_message_extracted_arguments(
+ selected_action, action_args, explicit_fields=explicit_action_args.keys()
+ )
+ # A mensagem atual é a fonte de verdade para esses campos no primeiro
+ # turno transacional.
+ action_args = await self._extract_mcp_parameters(
+ selected_action, action_args, state, overwrite_from_message=True
)
- # Extract parameters (including LLM-declared extraction rules) before
- # validating required fields and before persisting the pending call.
- action_args = await self._extract_mcp_parameters(selected_action, action_args, state)
policy = self._resolve_tool_execution_policy(selected_action, action_args)
selected = {"tool_name": selected_action, "arguments": action_args}
state["selected_tool_call"] = selected
+ self._set_active_transaction(
+ state, tool_name=selected_action, arguments=action_args, status="COLLECTING_PARAMETERS"
+ )
state["tool_policy_result"] = {**policy, "tool_name": selected_action}
missing = self._missing_required_arguments(policy, action_args)
@@ -1718,6 +2088,9 @@ class AgentRuntimeMixin:
"confirmation_required": True,
"confirmation_received": False,
})
+ self._set_active_transaction(
+ state, tool_name=selected_action, arguments=action_args, status="AWAITING_CONFIRMATION"
+ )
state["next_state"] = self._waiting_state_name(state)
if emit_events:
await self._emit_ic("IC.TRANSACTION_CONFIRMATION_REQUIRED", state, {"tool_name": selected_action, **policy}, component="agent_runtime.tool_policy")
@@ -1727,12 +2100,19 @@ class AgentRuntimeMixin:
action_args["confirmed"] = True
result = await self._call_mcp_tool(selected_action, action_args, state)
self._capture_pending_domain_workflow(state, result)
- state.update({
- "transaction_status": ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))),
- "confirmation_required": False,
- "confirmation_received": True,
- "pending_tool_call": {},
- })
+ final_status = ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED")))
+ if final_status in _TERMINAL_TRANSACTION_STATUSES:
+ self._finish_active_transaction(state, final_status)
+ else:
+ state.update({
+ "transaction_status": final_status,
+ "confirmation_required": False,
+ "confirmation_received": True,
+ "pending_tool_call": {},
+ })
+ self._set_active_transaction(
+ state, tool_name=selected_action, arguments=action_args, status=final_status
+ )
results.append(result)
return results
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..d5ac65c
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/authentication.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/authentication.cpython-313.pyc
new file mode 100644
index 0000000..ab536da
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/authentication.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/factory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/factory.cpython-313.pyc
new file mode 100644
index 0000000..1c721f3
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/factory.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/installer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/installer.cpython-313.pyc
new file mode 100644
index 0000000..2e80012
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/installer.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/middleware.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/middleware.cpython-313.pyc
new file mode 100644
index 0000000..57c4c9c
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/middleware.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc
index fe52525..a9019c8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc
index 0820be3..3064cfd 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc
index e16baff..c26f2af 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/router_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/router_supervisor.cpython-313.pyc
new file mode 100644
index 0000000..45add6e
Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/router_supervisor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc
index b8d541d..054da5c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc
index d4aef40..8a90d5e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc
index 9d19af5..90cd525 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc
index 0d5f670..bef74f6 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc
index 22a2878..32218f8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc
index ee44ad8..f1bf091 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc
index fec117d..45e15b4 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc
index 168c535..d574549 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py
index bc8a1b6..f818233 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py
@@ -434,15 +434,11 @@ class WorkflowRuntime:
"current_node": None,
}
config = {"configurable": {"thread_id": eid}}
- # Explicit offline-regression mode. When enabled, always use the
- # deterministic backend, regardless of whether LangGraph happens to be
- # installed in the current environment. This keeps regression results
- # reproducible across developer machines and CI while production
- # (the default) continues to require/use LangGraph.
if self.allow_deterministic_fallback:
- return await self._run_fallback(
- definition, initial, start_node=definition.start, execution_id=eid
- )
+ try:
+ import langgraph # noqa: F401
+ except ModuleNotFoundError:
+ return await self._run_fallback(definition, initial, start_node=definition.start, execution_id=eid)
try:
graph = self._compile(definition)
state = await graph.ainvoke(initial, config=config)
@@ -503,9 +499,10 @@ class WorkflowRuntime:
definition = self.repository.get_version(name, version) if version else self.repository.get_active(name)
config = {"configurable": {"thread_id": execution_id}}
if self.allow_deterministic_fallback:
- return await self._resume_fallback(
- name, execution_id, resume_value, version=version
- )
+ try:
+ import langgraph # noqa: F401
+ except ModuleNotFoundError:
+ return await self._resume_fallback(name, execution_id, resume_value, version=version)
try:
from langgraph.types import Command
except ModuleNotFoundError as exc:
diff --git a/agent_framework_oci/mcp/servers/retail_mcp_server/main.py b/agent_framework_oci/mcp/servers/retail_mcp_server/main.py
index 6f4e3e9..5d8d57e 100644
--- a/agent_framework_oci/mcp/servers/retail_mcp_server/main.py
+++ b/agent_framework_oci/mcp/servers/retail_mcp_server/main.py
@@ -17,6 +17,10 @@ TOOLS = {
"description": "Consulta entrega e rastreamento do pedido.",
"input_schema": {"order_id": "string"},
},
+ "cancelar_pedido": {
+ "description": "Simula o cancelamento de um pedido de varejo.",
+ "input_schema": {"order_id": "string"},
+ },
"solicitar_troca": {
"description": "Simula abertura de solicitação de troca.",
"input_schema": {"order_id": "string", "reason": "string"},
@@ -77,6 +81,13 @@ async def call_tool(call: ToolCall):
{"data": "2026-05-29", "descricao": "Em trânsito para o centro de distribuição"},
],
}
+ elif name == "cancelar_pedido":
+ result = {
+ "protocolo": "CANCEL-2026-001",
+ "order_id": args.get("order_id") or "PED-1001",
+ "status": "CANCELAMENTO_SOLICITADO",
+ "orientacao": "Cancelamento registrado para processamento.",
+ }
elif name == "solicitar_troca":
result = {
"protocolo": "TROCA-2026-001",
diff --git a/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py b/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py
index 5abf44d..7972f95 100644
--- a/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py
+++ b/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py
@@ -30,6 +30,16 @@ def consultar_entrega(order_id: str | None = None) -> dict[str, Any]:
}
+@mcp.tool()
+def cancelar_pedido(order_id: str | None = None) -> dict[str, Any]:
+ """Solicita o cancelamento de um pedido de varejo."""
+ return {
+ "order_id": order_id or "ORD-001",
+ "protocolo": "CANCEL-123456",
+ "status": "CANCELAMENTO_SOLICITADO",
+ }
+
+
@mcp.tool()
def solicitar_troca(order_id: str | None = None, reason: str | None = None) -> dict[str, Any]:
"""Abre solicitação de troca para um pedido."""
diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..f6a2b79
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc
new file mode 100644
index 0000000..df9fb43
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc
new file mode 100644
index 0000000..8d7093d
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc
new file mode 100644
index 0000000..7828b8e
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc
new file mode 100644
index 0000000..4e27b20
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc
new file mode 100644
index 0000000..803273e
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc
new file mode 100644
index 0000000..104fb20
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc
new file mode 100644
index 0000000..eda48f5
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc
new file mode 100644
index 0000000..df7bf15
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc
new file mode 100644
index 0000000..7fbc59f
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py
index e6429c4..7a1a9be 100644
--- a/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py
+++ b/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py
@@ -3,5 +3,8 @@ from __future__ import annotations
# Compatibilidade local do template/backend.
# A implementação oficial agora fica no framework para evitar duplicação entre agentes.
from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
+from app.presentation import register_tool_renderers
+
+register_tool_renderers()
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..a6540ec
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc
new file mode 100644
index 0000000..8fc575e
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc
new file mode 100644
index 0000000..8253000
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc
new file mode 100644
index 0000000..41a7208
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc
new file mode 100644
index 0000000..a8f3f88
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc
new file mode 100644
index 0000000..3b7d6a8
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..9144c5b
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc
new file mode 100644
index 0000000..9495cf2
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/templates/agent_template_backend/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/agent_framework_oci/templates/agent_template_backend/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/agent_framework_oci/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..32e74b9
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/templates/agent_template_backend/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..f77c47a
--- /dev/null
+++ b/agent_framework_oci/templates/agent_template_backend/app/presentation/tool_renderers.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ return f"[{agent_label}] Fatura consultada: {result}."
+
+
+def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ plano = result.get("plano")
+ if plano is None:
+ return None
+ parts = [f"[{agent_label}] Seu plano é {plano}"]
+ internet_gb = result.get("internet_gb")
+ status = result.get("status")
+ if internet_gb is not None:
+ parts.append(f"com {internet_gb} GB")
+ if status is not None:
+ parts.append(f"status {status}")
+ return ", ".join(parts) + "."
+
+
+def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ status = result.get("status")
+ if order_id is None or status is None:
+ return None
+ lines = [f"[{agent_label}] Pedido {order_id}: status {status}."]
+ total = result.get("valor_total")
+ if total is not None:
+ lines.append(f"Valor total: R$ {_money_brl(total)}.")
+ items = result.get("itens") or []
+ rendered_items: list[str] = []
+ if isinstance(items, list):
+ for item in items:
+ if isinstance(item, dict):
+ value = item.get("descricao") or item.get("nome") or item.get("sku")
+ else:
+ value = item
+ if value not in (None, ""):
+ rendered_items.append(str(value))
+ if rendered_items:
+ lines.append("Itens: " + "; ".join(rendered_items) + ".")
+ return " ".join(lines)
+
+
+def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None:
+ order_id = result.get("order_id")
+ transportadora = result.get("transportadora")
+ codigo = result.get("codigo_rastreio")
+ previsao = result.get("previsao_entrega")
+ if any(v is None for v in (order_id, transportadora, codigo, previsao)):
+ return None
+ return (
+ f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, "
+ f"rastreio {codigo}, previsão {previsao}."
+ )
+
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("telecom.invoice", render_telecom_invoice)
+ register_tool_response_renderer("telecom.plan", render_telecom_plan)
+ register_tool_response_renderer("retail.order", render_retail_order)
+ register_tool_response_renderer("retail.delivery", render_retail_delivery)
diff --git a/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..d217376
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc
new file mode 100644
index 0000000..9ec9aa1
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc
new file mode 100644
index 0000000..c5a3581
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/templates/agent_template_backend/config/routing.yaml
index 2dbe95e..03aeaa9 100644
--- a/agent_framework_oci/templates/agent_template_backend/config/routing.yaml
+++ b/agent_framework_oci/templates/agent_template_backend/config/routing.yaml
@@ -79,6 +79,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml
index 3c4fd05..9b1ab9e 100644
--- a/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml
+++ b/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/templates/agent_template_backend/config/tools.yaml
index d85fae1..f8a53ca 100644
--- a/agent_framework_oci/templates/agent_template_backend/config/tools.yaml
+++ b/agent_framework_oci/templates/agent_template_backend/config/tools.yaml
@@ -10,6 +10,9 @@ tools:
- fatura
- conta
- boleto
+ response:
+ mode: renderer
+ renderer: telecom.invoice
consultar_pagamentos:
description: Consulta histórico de pagamentos do cliente.
mcp_server: telecom
@@ -28,6 +31,9 @@ tools:
asset_id: string
selection_keywords:
- plano
+ response:
+ mode: renderer
+ renderer: telecom.plan
listar_servicos:
description: Lista serviços ativos e adicionais VAS.
mcp_server: telecom
@@ -49,6 +55,9 @@ tools:
- consultar pedido
- status do pedido
- pedido
+ response:
+ mode: renderer
+ renderer: retail.order
consultar_entrega:
description: Consulta entrega e rastreamento do pedido.
mcp_server: retail
@@ -61,6 +70,26 @@ tools:
- rastreamento
- transportadora
- previsão
+ response:
+ mode: renderer
+ renderer: retail.delivery
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..eafedee
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc
new file mode 100644
index 0000000..c13b42c
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc
new file mode 100644
index 0000000..a8f0280
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc
new file mode 100644
index 0000000..f856d0c
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc
new file mode 100644
index 0000000..406206d
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc
new file mode 100644
index 0000000..cbe2d5d
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc
new file mode 100644
index 0000000..d394a5b
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc
new file mode 100644
index 0000000..d96c6e4
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc
new file mode 100644
index 0000000..0d39586
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc
new file mode 100644
index 0000000..9dee705
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..359dee6
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc
new file mode 100644
index 0000000..76a03bb
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc
new file mode 100644
index 0000000..a39b585
Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
index 5b29ccf..91d57af 100644
--- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
+++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml
@@ -60,6 +60,18 @@ mcp_parameter_mapping:
mensagem.
pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
group: 1
+ cancelar_pedido:
+ map:
+ session_key: session_id
+ extract:
+ order_id:
+ from: message
+ type: string
+ strategy: hybrid
+ description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem.
+ pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b
+ group: 1
+
solicitar_troca:
map:
session_key: session_id
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml
index 34c9781..187a070 100644
--- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml
+++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml
@@ -84,6 +84,25 @@ intents:
- Quero saber sobre meu pacote de internet.
- Tenho roaming internacional?
+
+ - name: retail_order_cancel
+ domain: retail
+ agent: orders_agent
+ description: Cancelamento explícito de pedido ou compra.
+ priority: 20
+ mcp_tools:
+ - consultar_pedido
+ - cancelar_pedido
+ keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar a compra
+ - cancelar compra
+ examples:
+ - Quero cancelar meu pedido.
+ - Cancele o pedido.
+ - Quero cancelar a compra.
+
- name: retail_order_tracking
domain: retail
agent: orders_agent
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml
index 66c9854..48a83d5 100644
--- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml
+++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml
@@ -7,6 +7,12 @@ defaults:
require_confirmation: false
tool_policies:
+ cancelar_pedido:
+ operation_type: transactional
+ require_confirmation: true
+ requires: [order_id]
+
+
solicitar_troca:
operation_type: transactional
require_confirmation: true
diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml
index d85fae1..2329dc7 100644
--- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml
+++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml
@@ -61,6 +61,23 @@ tools:
- rastreamento
- transportadora
- previsão
+ cancelar_pedido:
+ description: Simula o cancelamento de um pedido de varejo.
+ mcp_server: retail
+ enabled: true
+ tool_type: action
+ requires:
+ - order_id
+ confirmation_required: true
+ args_schema:
+ order_id: string
+ selection_keywords:
+ - cancelar pedido
+ - cancelamento do pedido
+ - cancelar compra
+ - cancelar a compra
+
+
solicitar_troca:
description: Simula abertura de solicitação de troca.
mcp_server: retail
diff --git a/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc
index db53f3f..dff3d68 100644
Binary files a/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..fccf00c
Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/__pycache__/test_mcp_parameter_extraction_runtime.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_mcp_parameter_extraction_runtime.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..2c07d30
Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_mcp_parameter_extraction_runtime.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..e3e12c1
Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..5589e90
Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/test_generic_tool_response_presentation.py b/agent_framework_oci/tests/test_generic_tool_response_presentation.py
new file mode 100644
index 0000000..f57feaa
--- /dev/null
+++ b/agent_framework_oci/tests/test_generic_tool_response_presentation.py
@@ -0,0 +1,72 @@
+from __future__ import annotations
+
+from types import SimpleNamespace
+
+from agent_framework.presentation import register_tool_response_renderer
+from agent_framework.runtime.agent_runtime import AgentRuntimeMixin
+
+
+class _Registry:
+ def __init__(self, responses):
+ self.responses = responses
+
+ def get_tool(self, name):
+ response = self.responses.get(name)
+ if response is None:
+ return None
+ return SimpleNamespace(response=response)
+
+
+class _Runtime(AgentRuntimeMixin):
+ def __init__(self, responses):
+ self.tool_router = SimpleNamespace(registry=_Registry(responses))
+
+ def _mcp_rag_directive(self, results):
+ return False, None
+
+ def _mcp_llm_composition_directive(self, results):
+ return False, None
+
+ def _transactional_action_match(self, text):
+ return None
+
+ def _workflow_payload_from_tool_result(self, item):
+ return None
+
+
+def _result(tool, data):
+ return [{"tool_name": tool, "ok": True, "result": data}]
+
+
+def test_renderer_mode_uses_application_registered_renderer():
+ def renderer(*, tool_name, result, state, agent_label):
+ return f"[{agent_label}] {result['name']} / {state['intent']}"
+
+ register_tool_response_renderer("test.entity", renderer)
+ rt = _Runtime({"consultar_algo": {"mode": "renderer", "renderer": "test.entity"}})
+ answer = rt.build_direct_mcp_answer(
+ {"user_text": "consulta", "intent": "test_intent"},
+ _result("consultar_algo", {"name": "OK"}),
+ agent_label="TestAgent",
+ )
+ assert answer == "[TestAgent] OK / test_intent"
+
+
+def test_missing_renderer_falls_back_without_breaking_runtime():
+ rt = _Runtime({"consultar_plano": {"mode": "renderer", "renderer": "missing.renderer"}})
+ answer = rt.build_direct_mcp_answer(
+ {"user_text": "qual meu plano"},
+ _result("consultar_plano", {"plano": "Controle", "internet_gb": 50, "status": "ATIVO"}),
+ agent_label="ProductAgent",
+ )
+ assert answer == "[ProductAgent] Seu plano é Controle, com 50 GB e status ATIVO."
+
+
+def test_no_declared_response_keeps_legacy_fallback():
+ rt = _Runtime({})
+ answer = rt.build_direct_mcp_answer(
+ {"user_text": "qual meu plano"},
+ _result("consultar_plano", {"plano": "Controle", "internet_gb": 50, "status": "ATIVO"}),
+ agent_label="ProductAgent",
+ )
+ assert answer == "[ProductAgent] Seu plano é Controle, com 50 GB e status ATIVO."
diff --git a/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py b/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py
index a70c57e..86b8618 100644
--- a/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py
+++ b/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py
@@ -59,3 +59,54 @@ async def test_runtime_extracts_order_id_from_current_message():
)
assert result["order_id"] == "123"
assert result["contract_key"] == "3000131180"
+
+class _ContestExtractLLM:
+ async def ainvoke(self, messages, **kwargs):
+ prompt = messages[0]["content"]
+ if "Campo: subject" in prompt:
+ return {"content": '{"subject": "TIM CTRL Redes Sociais 8.0"}'}
+ if "Campo: valor" in prompt:
+ return {"content": '{"valor": null}'}
+ return {"content": '{}'}
+
+
+class _ContestExtractRouter:
+ def parameter_extract_rules(self, tool_name):
+ return {
+ "subject": {
+ "from": "message",
+ "strategy": "llm",
+ "type": "string",
+ "description": "Extraia o item contestado.",
+ },
+ "valor": {
+ "from": "message",
+ "strategy": "llm",
+ "type": "number",
+ "description": "Extraia o valor explicitamente informado.",
+ },
+ }
+
+
+class _ContestExtractRuntime(AgentRuntimeMixin):
+ def __init__(self):
+ self.tool_router = _ContestExtractRouter()
+ self.llm = _ContestExtractLLM()
+
+
+@pytest.mark.asyncio
+async def test_new_transaction_current_message_overrides_stale_subject_from_context():
+ runtime = _ContestExtractRuntime()
+ result = await runtime._extract_mcp_parameters(
+ "contestar_cobranca",
+ {"subject": "TIM Fashion Mensal", "valor": 10.0},
+ {
+ "user_text": "nao contratei TIM CTRL Redes Sociais 8.0",
+ "sanitized_input": "nao contratei TIM CTRL Redes Sociais 8.0",
+ },
+ overwrite_from_message=True,
+ )
+ assert result["subject"] == "TIM CTRL Redes Sociais 8.0"
+ # null extraction does not destroy a pre-existing value; transaction start
+ # sanitization is about explicit current-message evidence, not blind clearing.
+ assert result["valor"] == 10.0
diff --git a/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py b/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py
index 7a7b93e..fee3400 100644
--- a/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py
+++ b/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py
@@ -1,3 +1,4 @@
+import pytest
from types import SimpleNamespace
from agent_framework.routing.enterprise_router import EnterpriseRouter
@@ -12,3 +13,84 @@ def test_explicit_keyword_shift_preempts_stickiness():
def test_short_generic_keyword_does_not_preempt():
d = RouteDecision(route="x", agent="x", intent="x", method="keyword", metadata={"matched_keyword": "id"})
assert EnterpriseRouter._is_explicit_intent_shift(d) is False
+
+
+def test_same_agent_explicit_intent_shift_must_preempt_stickiness():
+ router = object.__new__(EnterpriseRouter)
+ router.intents = []
+ # This test documents the key regression: changing from a read-only intent
+ # to a transactional intent handled by the SAME agent must still be treated
+ # as an explicit intent shift. The route() integration tests exercise the
+ # full async path; here we lock the explicitness predicate itself.
+ d = RouteDecision(
+ route="orders_agent",
+ agent="orders_agent",
+ intent="retail_order_cancel",
+ method="keyword",
+ metadata={"matched_keyword": "cancelar pedido"},
+ )
+ assert EnterpriseRouter._is_explicit_intent_shift(d) is True
+
+@pytest.mark.asyncio
+async def test_same_agent_transaction_keyword_preempts_continuity(monkeypatch):
+ """Regression: tracking -> cancelar pedido must not reuse tracking intent."""
+ from agent_framework.routing.models import IntentDefinition
+
+ class _Continuity:
+ async def evaluate(self, state, *, intents):
+ raise AssertionError("continuity must be preempted by explicit intent shift")
+
+ router = object.__new__(EnterpriseRouter)
+ router.state_policies = []
+ router.intents = [
+ IntentDefinition(
+ name="retail_order_cancel",
+ domain="retail",
+ agent="orders_agent",
+ description="cancelamento",
+ priority=20,
+ mcp_tools=["consultar_pedido", "cancelar_pedido"],
+ keywords=["cancelar pedido"],
+ examples=[],
+ enabled=True,
+ ),
+ IntentDefinition(
+ name="retail_order_tracking",
+ domain="retail",
+ agent="orders_agent",
+ description="tracking",
+ priority=30,
+ mcp_tools=["consultar_pedido", "consultar_entrega"],
+ keywords=["pedido"],
+ examples=[],
+ enabled=True,
+ ),
+ ]
+ router.continuity = _Continuity()
+ router.enable_llm_router = False
+ router.llm = None
+ router.telemetry = None
+ router.fallback_agent = "billing_agent"
+
+ decision = await router.route({
+ "user_text": "quero cancelar pedido",
+ "sanitized_input": "quero cancelar pedido",
+ "active_agent": "orders_agent",
+ "intent": "retail_order_tracking",
+ "route_decision": {
+ "route": "orders_agent",
+ "agent": "orders_agent",
+ "intent": "retail_order_tracking",
+ "domain": "retail",
+ "mcp_tools": ["consultar_pedido", "consultar_entrega"],
+ },
+ "context": {"session": {}},
+ })
+
+ assert decision.agent == "orders_agent"
+ assert decision.intent == "retail_order_cancel"
+ assert decision.method == "keyword"
+ assert decision.mcp_tools == ["consultar_pedido", "cancelar_pedido"]
+ assert decision.metadata["route_stickiness_preempted"] is True
+ assert decision.metadata["previous_agent"] == "orders_agent"
+ assert decision.metadata["previous_intent"] == "retail_order_tracking"
diff --git a/agent_framework_oci/tests/test_transactional_tool_flow.py b/agent_framework_oci/tests/test_transactional_tool_flow.py
index f292bec..e2597ec 100644
--- a/agent_framework_oci/tests/test_transactional_tool_flow.py
+++ b/agent_framework_oci/tests/test_transactional_tool_flow.py
@@ -88,3 +88,202 @@ async def test_transaction_waits_then_executes_after_confirmation():
assert runtime.calls[-1][0] == "solicitar_devolucao"
assert runtime.calls[-1][1]["confirmed"] is True
assert second[-1]["ok"] is True
+
+class _ContestPolicyRouter:
+ def __init__(self):
+ from types import SimpleNamespace
+ self.registry = SimpleNamespace(
+ tools={"contestar_cobranca": object()},
+ get_tool=lambda name: SimpleNamespace(selection_keywords=["contestar", "não contratei", "nao contratei"]),
+ )
+
+ def resolve_execution_policy(self, tool_name, arguments=None):
+ return {
+ "operation_type": "transactional",
+ "require_confirmation": True,
+ "requires": ["subject", "valor"],
+ "policy_source": "test",
+ }
+
+ def validate_execution_policy(self, tool_name, arguments=None):
+ policy = self.resolve_execution_policy(tool_name, arguments)
+ return True, None, policy
+
+
+class _ContestRuntime(AgentRuntimeMixin):
+ def __init__(self):
+ self.tool_router = _ContestPolicyRouter()
+ self.calls = []
+
+ async def _call_mcp_tool(self, tool_name, arguments, state):
+ self.calls.append((tool_name, dict(arguments)))
+ return {"ok": True, "tool_name": tool_name, "result": {"status": "OPENED"}}
+
+
+@pytest.mark.asyncio
+async def test_collecting_parameters_does_not_replace_collected_subject_with_stale_context():
+ """A later value-only turn must not replace an already collected subject.
+
+ Regression reproduced from Contas: subject was collected as TIM CTRL, while
+ context/tool_arguments still exposed TIM Fashion from another transaction.
+ When the user supplied only R$ 71,99, the stale context used to overwrite
+ the collected subject before confirmation.
+ """
+ runtime = _ContestRuntime()
+ state = {
+ "user_text": "R$ 71,99",
+ "sanitized_input": "R$ 71,99",
+ "route": "contestacao_agent",
+ "intent": "state:COLLECTING_CONTESTACAO_PARAMETERS",
+ "transaction_status": "COLLECTING_PARAMETERS",
+ "selected_tool_call": {
+ "tool_name": "contestar_cobranca",
+ "arguments": {
+ "subject": "TIM CTRL Redes Sociais 8.0",
+ "motivo": "não contratei",
+ },
+ },
+ # Simulates stale contextual arguments left by another action/session turn.
+ "context": {
+ "tool_arguments": {
+ "subject": "TIM Fashion Mensal",
+ "valor": 71.99,
+ }
+ },
+ }
+
+ result = await runtime.execute_tools_for_intent(state, tools=[])
+
+ assert result[-1]["transaction_status"] == "AWAITING_CONFIRMATION"
+ assert state["pending_tool_call"]["arguments"]["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert state["pending_tool_call"]["arguments"]["valor"] == 71.99
+ assert state["pending_tool_call"]["arguments"]["motivo"] == "não contratei"
+ assert state["pending_tool_call"]["arguments"]["query"] == "R$ 71,99"
+ assert runtime.calls == []
+
+class _InitialContestLLM:
+ async def ainvoke(self, messages, **kwargs):
+ prompt = messages[0]["content"]
+ if "Campo: subject" in prompt:
+ return {"content": '{"subject": "TIM CTRL Redes Sociais 8.0"}'}
+ if "Campo: valor" in prompt:
+ return {"content": '{"valor": null}'}
+ if "Campo: motivo" in prompt:
+ return {"content": '{"motivo": "não contratei"}'}
+ return {"content": '{}'}
+
+
+class _InitialContestRouter(_ContestPolicyRouter):
+ def resolve_execution_policy(self, tool_name, arguments=None):
+ return {
+ "operation_type": "transactional",
+ "require_confirmation": True,
+ "requires": ["subject"],
+ "policy_source": "test",
+ }
+
+ def parameter_extract_rules(self, tool_name):
+ return {
+ "subject": {"from": "message", "strategy": "llm", "type": "string", "description": "item"},
+ "valor": {"from": "message", "strategy": "llm", "type": "number", "description": "valor"},
+ "motivo": {"from": "message", "strategy": "llm", "type": "string", "description": "motivo"},
+ }
+
+
+class _InitialContestRuntime(AgentRuntimeMixin):
+ def __init__(self):
+ self.tool_router = _InitialContestRouter()
+ self.llm = _InitialContestLLM()
+ self.calls = []
+
+ async def _call_mcp_tool(self, tool_name, arguments, state):
+ self.calls.append((tool_name, dict(arguments)))
+ return {"ok": True, "tool_name": tool_name, "result": {"status": "OPENED"}}
+
+
+@pytest.mark.asyncio
+async def test_new_contestation_does_not_inherit_subject_or_value_from_previous_transaction():
+ runtime = _InitialContestRuntime()
+ state = {
+ "user_text": "nao contratei TIM CTRL Redes Sociais 8.0",
+ "sanitized_input": "nao contratei TIM CTRL Redes Sociais 8.0",
+ "mcp_tools": ["contestar_cobranca"],
+ "route": "contestacao_agent",
+ "intent": "contas_contestation",
+ "context": {
+ "tool_arguments": {
+ "subject": "TIM Fashion Mensal",
+ "valor": 10.0,
+ "motivo": "contestação antiga",
+ }
+ },
+ }
+
+ result = await runtime.execute_tools_for_intent(state)
+ pending = state["pending_tool_call"]["arguments"]
+ assert result[-1]["transaction_status"] == "AWAITING_CONFIRMATION"
+ assert pending["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert pending["motivo"] == "não contratei"
+ assert "valor" not in pending
+ assert runtime.calls == []
+
+@pytest.mark.asyncio
+async def test_closed_transaction_is_not_operational_context_for_next_turn():
+ runtime = _InitialContestRuntime()
+ state = {
+ "user_text": "nao contratei TIM CTRL Redes Sociais 8.0",
+ "sanitized_input": "nao contratei TIM CTRL Redes Sociais 8.0",
+ "mcp_tools": ["contestar_cobranca"],
+ "route": "contestacao_agent",
+ "intent": "contas_contestation",
+ # Historical/closed transaction must never feed the new one.
+ "transaction_status": "COMPLETED",
+ "selected_tool_call": {
+ "tool_name": "cancelar_vas_avulso",
+ "arguments": {"subject": "TIM Fashion Mensal", "valor": 10.0},
+ },
+ "pending_tool_call": {},
+ "context": {
+ "tool_arguments": {
+ "subject": "TIM Fashion Mensal",
+ "valor": 10.0,
+ }
+ },
+ }
+
+ result = await runtime.execute_tools_for_intent(state)
+
+ assert result[-1]["transaction_status"] == "AWAITING_CONFIRMATION"
+ assert state["active_transaction"]["tool_name"] == "contestar_cobranca"
+ assert state["active_transaction"]["arguments"]["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert state["pending_tool_call"]["tool_name"] == "contestar_cobranca"
+ assert state["pending_tool_call"]["arguments"]["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert state["last_transaction"]["tool_name"] == "cancelar_vas_avulso"
+ assert state["last_transaction"]["arguments"]["subject"] == "TIM Fashion Mensal"
+
+
+@pytest.mark.asyncio
+async def test_terminal_confirmation_closes_active_transaction_and_clears_latches():
+ runtime = _Runtime()
+ state = {
+ "user_text": "Quero devolver o pedido 123 porque me arrependi",
+ "sanitized_input": "Quero devolver o pedido 123 porque me arrependi",
+ "mcp_tools": ["consultar_pedido", "solicitar_devolucao"],
+ "route": "support_agent",
+ "intent": "retail_support_exchange_return",
+ }
+ await runtime.execute_tools_for_intent(state)
+ assert state["active_transaction"]["status"] == "AWAITING_CONFIRMATION"
+
+ state["user_text"] = "sim"
+ state["sanitized_input"] = "sim"
+ await runtime.execute_tools_for_intent(state)
+
+ assert state["transaction_status"] == "COMPLETED"
+ assert state["active_transaction"] is None
+ assert state["selected_tool_call"] == {}
+ assert state["pending_tool_call"] == {}
+ assert state["missing_parameters"] == []
+ assert state["next_state"] is None
+ assert state["last_transaction"]["tool_name"] == "solicitar_devolucao"
+ assert state["last_transaction"]["status"] == "COMPLETED"
diff --git a/agent_framework_oci/tests/unit/__pycache__/test_langgraph_checkpoint_saver.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/unit/__pycache__/test_langgraph_checkpoint_saver.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..3fdd435
Binary files /dev/null and b/agent_framework_oci/tests/unit/__pycache__/test_langgraph_checkpoint_saver.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/unit/__pycache__/test_resilient_checkpointer.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/unit/__pycache__/test_resilient_checkpointer.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..8453472
Binary files /dev/null and b/agent_framework_oci/tests/unit/__pycache__/test_resilient_checkpointer.cpython-313-pytest-9.0.2.pyc differ
diff --git a/agent_framework_oci/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..454695e
Binary files /dev/null and b/agent_framework_oci/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc differ
diff --git a/app/__pycache__/state.cpython-313.pyc b/app/__pycache__/state.cpython-313.pyc
new file mode 100644
index 0000000..e4c9a18
Binary files /dev/null and b/app/__pycache__/state.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/contas_prompting.cpython-313.pyc b/app/agents/__pycache__/contas_prompting.cpython-313.pyc
new file mode 100644
index 0000000..61d4422
Binary files /dev/null and b/app/agents/__pycache__/contas_prompting.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/contestacao_agent.cpython-313.pyc b/app/agents/__pycache__/contestacao_agent.cpython-313.pyc
new file mode 100644
index 0000000..0cea819
Binary files /dev/null and b/app/agents/__pycache__/contestacao_agent.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/faturas_agent.cpython-313.pyc b/app/agents/__pycache__/faturas_agent.cpython-313.pyc
new file mode 100644
index 0000000..aaaddde
Binary files /dev/null and b/app/agents/__pycache__/faturas_agent.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/prompting.cpython-313.pyc b/app/agents/__pycache__/prompting.cpython-313.pyc
new file mode 100644
index 0000000..3e5a5bc
Binary files /dev/null and b/app/agents/__pycache__/prompting.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/runtime.cpython-313.pyc b/app/agents/__pycache__/runtime.cpython-313.pyc
new file mode 100644
index 0000000..8def84e
Binary files /dev/null and b/app/agents/__pycache__/runtime.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc b/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc
new file mode 100644
index 0000000..7667f05
Binary files /dev/null and b/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc differ
diff --git a/app/agents/__pycache__/vas_agent.cpython-313.pyc b/app/agents/__pycache__/vas_agent.cpython-313.pyc
new file mode 100644
index 0000000..5c848fd
Binary files /dev/null and b/app/agents/__pycache__/vas_agent.cpython-313.pyc differ
diff --git a/app/agents/runtime.py b/app/agents/runtime.py
index e6429c4..7a1a9be 100644
--- a/app/agents/runtime.py
+++ b/app/agents/runtime.py
@@ -3,5 +3,8 @@ from __future__ import annotations
# Compatibilidade local do template/backend.
# A implementação oficial agora fica no framework para evitar duplicação entre agentes.
from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
+from app.presentation import register_tool_renderers
+
+register_tool_renderers()
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
diff --git a/app/domain/__pycache__/__init__.cpython-313.pyc b/app/domain/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..2ad9b61
Binary files /dev/null and b/app/domain/__pycache__/__init__.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/__init__.cpython-313.pyc b/app/domain/contas/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..381f890
Binary files /dev/null and b/app/domain/contas/__pycache__/__init__.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/client.cpython-313.pyc b/app/domain/contas/__pycache__/client.cpython-313.pyc
new file mode 100644
index 0000000..c4e531a
Binary files /dev/null and b/app/domain/contas/__pycache__/client.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc b/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc
new file mode 100644
index 0000000..79a4578
Binary files /dev/null and b/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc b/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc
new file mode 100644
index 0000000..84aa2e9
Binary files /dev/null and b/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/informational_context.cpython-313.pyc b/app/domain/contas/__pycache__/informational_context.cpython-313.pyc
new file mode 100644
index 0000000..0338323
Binary files /dev/null and b/app/domain/contas/__pycache__/informational_context.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc b/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc
new file mode 100644
index 0000000..0b31ba0
Binary files /dev/null and b/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc b/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc
new file mode 100644
index 0000000..c3eb1c8
Binary files /dev/null and b/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc b/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc
new file mode 100644
index 0000000..8ae4984
Binary files /dev/null and b/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc b/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc
new file mode 100644
index 0000000..5cddda6
Binary files /dev/null and b/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc b/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc
new file mode 100644
index 0000000..6c0de27
Binary files /dev/null and b/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc b/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc
new file mode 100644
index 0000000..aef22d7
Binary files /dev/null and b/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc b/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc
new file mode 100644
index 0000000..574bf5e
Binary files /dev/null and b/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/service.cpython-313.pyc b/app/domain/contas/__pycache__/service.cpython-313.pyc
new file mode 100644
index 0000000..cbc6ad9
Binary files /dev/null and b/app/domain/contas/__pycache__/service.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc b/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc
new file mode 100644
index 0000000..93334ae
Binary files /dev/null and b/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc b/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc
new file mode 100644
index 0000000..498ed2b
Binary files /dev/null and b/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc b/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc
new file mode 100644
index 0000000..cfd99cc
Binary files /dev/null and b/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc differ
diff --git a/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc b/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc
new file mode 100644
index 0000000..2b73a38
Binary files /dev/null and b/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc differ
diff --git a/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc b/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc
new file mode 100644
index 0000000..5aaabe1
Binary files /dev/null and b/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc differ
diff --git a/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc b/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..84bb16b
Binary files /dev/null and b/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc differ
diff --git a/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc b/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc
new file mode 100644
index 0000000..3bf1bd8
Binary files /dev/null and b/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc differ
diff --git a/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc b/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc
new file mode 100644
index 0000000..4aa36a9
Binary files /dev/null and b/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc differ
diff --git a/app/domain/contas/service.py b/app/domain/contas/service.py
index ff78679..84663fc 100644
--- a/app/domain/contas/service.py
+++ b/app/domain/contas/service.py
@@ -37,6 +37,47 @@ class ContasDomainService:
def consultar_faturas(self, *, msisdn: str, **_: Any) -> Any:
return self.client.consultar_faturas(msisdn)
+ def consultar_plano(self, *, msisdn: str, **args: Any) -> dict[str, Any]:
+ """Retorna somente os planos presentes na evidência de billing analysis.
+
+ A consulta é deliberadamente determinística: não usa VAS, RAG nem o
+ workflow de invoice_explanation para responder qual é o plano do cliente.
+ """
+ billing = args.get("billing_analysis")
+ if not isinstance(billing, dict):
+ billing = self.client.billing_analysis(
+ msisdn,
+ invoice_id=args.get("invoice_id"),
+ customer_id=args.get("customer_id"),
+ )
+
+ plans: list[dict[str, Any]] = []
+ for section in billing.get("currentInvoice") or [] if isinstance(billing, dict) else []:
+ if not isinstance(section, dict):
+ continue
+ section_type = str(section.get("type") or "").strip().casefold()
+ section_desc = str(section.get("desc") or "").strip().casefold()
+ if section_type not in {"plano", "planos"} and section_desc not in {"plano", "planos"}:
+ continue
+ for item in section.get("items") or []:
+ if not isinstance(item, dict):
+ continue
+ name = str(item.get("desc") or item.get("name") or "").strip()
+ if not name:
+ continue
+ plans.append({
+ "name": name,
+ "value": item.get("value"),
+ "type": str(item.get("type") or section.get("type") or "plano"),
+ "contestable": item.get("contestable"),
+ })
+
+ return {
+ "plans": plans,
+ "count": len(plans),
+ "source": "billing_analysis.currentInvoice",
+ }
+
def invoice_explanation(self, *, msisdn: str, **args: Any) -> dict[str, Any]:
# Reuse framework-prefetched evidence when available; the domain never owns
# a second cache/session implementation. Explanation itself is produced by
diff --git a/app/observability/__pycache__/__init__.cpython-313.pyc b/app/observability/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..7f5ed4c
Binary files /dev/null and b/app/observability/__pycache__/__init__.cpython-313.pyc differ
diff --git a/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/app/observability/__pycache__/telemetry_observer.cpython-313.pyc
new file mode 100644
index 0000000..51057b8
Binary files /dev/null and b/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ
diff --git a/app/presentation/__init__.py b/app/presentation/__init__.py
new file mode 100644
index 0000000..c0eba0d
--- /dev/null
+++ b/app/presentation/__init__.py
@@ -0,0 +1,3 @@
+from .tool_renderers import register_tool_renderers
+
+__all__ = ["register_tool_renderers"]
diff --git a/app/presentation/__pycache__/__init__.cpython-313.pyc b/app/presentation/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..14330eb
Binary files /dev/null and b/app/presentation/__pycache__/__init__.cpython-313.pyc differ
diff --git a/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/app/presentation/__pycache__/tool_renderers.cpython-313.pyc
new file mode 100644
index 0000000..aadb4a7
Binary files /dev/null and b/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ
diff --git a/app/presentation/tool_renderers.py b/app/presentation/tool_renderers.py
new file mode 100644
index 0000000..510b60a
--- /dev/null
+++ b/app/presentation/tool_renderers.py
@@ -0,0 +1,108 @@
+from __future__ import annotations
+
+from typing import Any
+
+from agent_framework.presentation import register_tool_response_renderer
+
+
+def _money_brl(value: Any) -> str:
+ try:
+ return f"{float(value):.2f}".replace(".", ",")
+ except (TypeError, ValueError):
+ return str(value)
+
+
+def render_contas_plan(
+ *, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str
+) -> str | None:
+ plans = result.get("plans")
+ if not isinstance(plans, list) or not plans:
+ return None
+
+ lines: list[str] = []
+ for plan in plans:
+ if not isinstance(plan, dict):
+ continue
+ name = plan.get("name")
+ if not name:
+ continue
+ value = plan.get("value")
+ line = f"- {name}"
+ if value is not None:
+ line += f": R$ {_money_brl(value)}"
+ lines.append(line)
+
+ if not lines:
+ return None
+ if len(lines) == 1:
+ return f"[{agent_label}] Seu plano é:\n{lines[0]}"
+ return f"[{agent_label}] Existem {len(lines)} planos associados à sua conta:\n" + "\n".join(lines)
+
+
+
+def _service_value(service: dict[str, Any]) -> Any:
+ details = service.get("details") if isinstance(service.get("details"), dict) else {}
+ for candidate in (
+ details.get("valor"),
+ details.get("value"),
+ service.get("valor"),
+ service.get("value"),
+ service.get("price"),
+ ):
+ if candidate not in (None, ""):
+ return candidate
+ return None
+
+
+def _money_brl_preserve(value: Any) -> str:
+ if value is None:
+ return ""
+ text = str(value).strip()
+ if not text:
+ return ""
+ # TIM APIs may already return BRL decimal strings such as "10,00".
+ # Normalize only when needed and never invent a value.
+ if "," in text and "." not in text:
+ try:
+ return f"{float(text.replace(',', '.')):.2f}".replace(".", ",")
+ except ValueError:
+ return text
+ return _money_brl(value)
+
+
+def render_contas_vas(
+ *, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str
+) -> str | None:
+ services = result.get("products") or result.get("services")
+ if not isinstance(services, list) or not services:
+ return None
+
+ lines: list[str] = []
+ for service in services:
+ if not isinstance(service, dict):
+ continue
+ name = service.get("name") or service.get("description") or service.get("billDescription")
+ if not name:
+ continue
+
+ details = service.get("details") if isinstance(service.get("details"), dict) else {}
+ can = service.get("can") if isinstance(service.get("can"), dict) else {}
+ details_can = details.get("can") if isinstance(details.get("can"), dict) else {}
+ cancelable = bool(can.get("cancel", details_can.get("cancel", False)))
+ category = service.get("classe") or details.get("classe") or ("avulso" if cancelable else None)
+ value = _service_value(service)
+
+ line = f"- {name}"
+ if category:
+ line += f" ({category})"
+ if value not in (None, ""):
+ line += f": R$ {_money_brl_preserve(value)}"
+ lines.append(line)
+
+ if not lines:
+ return None
+ return f"[{agent_label}] Você possui os seguintes serviços ativos:\n" + "\n".join(lines)
+
+def register_tool_renderers() -> None:
+ register_tool_response_renderer("contas.plan", render_contas_plan)
+ register_tool_response_renderer("contas.vas", render_contas_vas)
diff --git a/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/app/workflows/__pycache__/agent_graph.cpython-313.pyc
index f04dab5..40457f0 100644
Binary files a/app/workflows/__pycache__/agent_graph.cpython-313.pyc and b/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ
diff --git a/config/mcp_parameter_mapping.yaml b/config/mcp_parameter_mapping.yaml
index e3dff43..6f9322a 100644
--- a/config/mcp_parameter_mapping.yaml
+++ b/config/mcp_parameter_mapping.yaml
@@ -6,6 +6,10 @@ mcp_parameter_mapping:
map:
customer_key: msisdn
session_key: session_id
+ consultar_plano:
+ map:
+ customer_key: msisdn
+ session_key: session_id
invoice_explanation:
map:
customer_key: msisdn
diff --git a/config/prompts/billing.yaml b/config/prompts/billing.yaml
index 3a8914a..1f249ae 100644
--- a/config/prompts/billing.yaml
+++ b/config/prompts/billing.yaml
@@ -6,3 +6,15 @@ system: |
Se houver resultado de invoice_explanation, preserve nomes, valores e conclusões vindas da tool.
Não prometa nem estime valores de faturas futuras.
Seja objetivo e mantenha o comportamento de negócio do Contas.
+
+ Quando o cliente perguntar pelo plano contratado, use somente itens
+ classificados como plano na fatura/tool. Não liste VAS, streamings ou benefícios
+ como se fossem o plano principal.
+
+ Se houver mais de um plano, diga que há mais de um plano associado à conta
+ e liste cada um com seu respectivo valor quando disponível.
+
+ Não use informações de uma cobrança antiga para completar a pergunta atual.
+
+ Quando houver resultado de tool, não extrapole procedimentos ou políticas
+ que não estejam nesse resultado.
\ No newline at end of file
diff --git a/config/prompts/contestation.yaml b/config/prompts/contestation.yaml
index 3929aff..3d20fdb 100644
--- a/config/prompts/contestation.yaml
+++ b/config/prompts/contestation.yaml
@@ -1,7 +1,43 @@
system: |
Você é o agente de Contestação e Ações do Contas TIM.
- Trabalhe apenas com itens explicitamente identificados pelos dados da fatura/tool.
- Cancelamento de VAS avulso, contestação e demais mutações são transações determinísticas: nunca execute ação em texto livre.
- O agent_framework_oci coleta parâmetros, mantém stickiness e exige confirmação antes da tool transacional.
- Não conceda ressarcimento em dobro quando a regra de negócio não permitir.
- Comunique o resultado real da tool, sem inventar protocolo ou sucesso.
+
+ Trabalhe somente com itens, valores, categorias e resultados explicitamente
+ presentes na mensagem atual, nas tools ou no contexto de fatura autorizado.
+
+ A referência explícita mais recente do cliente ao item da cobrança prevalece
+ sobre itens mencionados anteriormente. Nunca reutilize um serviço antigo para
+ completar uma nova contestação quando o cliente identificou outro item.
+
+ Diferencie:
+ - VAS avulso: serviço adicional classificado como avulso.
+ - VAS estratégico: serviço estratégico; não o transforme em avulso por inferência.
+ - Incluso no plano: benefício do plano; não o trate como VAS cancelável.
+ - Plano: item classificado como plano na fatura; não o trate como VAS.
+ - Juros, multas e demais categorias: preserve a classificação retornada pela fatura/tool.
+
+ "Não conheço", "não sabia que tinha" ou dúvida sobre o que é um serviço não
+ significam automaticamente que o cliente deseja contestar ou cancelar.
+ "Não contratei", "não reconheço a contratação" ou pedido explícito de contestação
+ caracterizam intenção de contestação quando houver item identificável.
+
+ Se a referência do cliente for ambígua e houver mais de um item possível,
+ não escolha um deles por inferência. Peça clarificação.
+
+ Cancelamento, contestação e demais mutações são transações determinísticas.
+ Nunca execute nem simule uma ação em texto livre.
+ Confirmação, stickiness, coleta de parâmetros e execução pertencem ao
+ agent_framework_oci.
+
+ Ao receber resultado de tool:
+ - preserve exatamente o item e o valor retornados;
+ - preserve a categoria retornada;
+ - não substitua o item por outro encontrado na fatura;
+ - não declare sucesso quando a tool falhou ou bloqueou;
+ - não invente protocolo.
+
+ Se a tool retornar OUT_OF_SCOPE, bloqueio ou categoria não tratável,
+ explique somente o motivo efetivamente retornado.
+ Não invente canal alternativo, central de atendimento, gravações, histórico,
+ prazo, estorno, recurso ou procedimento que não esteja explícito em tool ou RAG.
+
+ Seja objetivo e responda apenas sobre a solicitação atual.
\ No newline at end of file
diff --git a/config/prompts/vas.yaml b/config/prompts/vas.yaml
index 88c0d71..9f04a04 100644
--- a/config/prompts/vas.yaml
+++ b/config/prompts/vas.yaml
@@ -12,3 +12,12 @@ system: |
Quando a intent for consulta do plano principal, responda somente com os dados de consultar_plano; não substitua o plano por uma lista de VAS.
Em consultas informativas, não termine com convite para outra ação ou outro canal.
+ Preserve a classificação original do serviço retornada pela tool/fatura:
+ avulso, estratégico ou incluso.
+
+ Nunca trate plano como VAS.
+ Nunca trate benefício incluso como serviço avulso.
+ Nunca trate estratégico como avulso por similaridade de nome.
+
+ "Não conheço este serviço" é inicialmente uma dúvida sobre o serviço;
+ não converta automaticamente em pedido de cancelamento ou contestação.
\ No newline at end of file
diff --git a/config/routing.yaml b/config/routing.yaml
index 66c4a2f..6e1ba69 100644
--- a/config/routing.yaml
+++ b/config/routing.yaml
@@ -21,6 +21,30 @@ state_policies:
- state: COLLECTING_SUPORTE_CONTAS_PARAMETERS
agent: suporte_contas_agent
intents:
+- name: contas_plan_information
+ domain: telecom_contas
+ agent: faturas_agent
+ priority: 142
+ description: Consulta do nome, identificação ou valor do plano principal ou dos planos contratados da linha. Não se aplica a VAS, serviços adicionais, aplicativos, assinaturas, parceiros ou benefícios incluídos.
+ mcp_tools:
+ - consultar_plano
+ keywords:
+ - qual é meu plano
+ - qual e meu plano
+ - qual é o meu plano
+ - qual e o meu plano
+ - meu plano contratado
+ - plano contratado
+ - nome do meu plano
+ - que plano eu tenho
+ - quais são meus planos
+ - quais sao meus planos
+ examples:
+ - Qual é meu plano?
+ - Qual é o meu plano contratado?
+ - Que plano eu tenho?
+ - Qual o nome do meu plano?
+ - Quais são os planos da minha linha?
- name: contas_invoice_query
domain: telecom_contas
agent: faturas_agent
@@ -110,6 +134,12 @@ intents:
- ajuste na fatura
- estorno
- valor indevido
+ - não contratei
+ - nao contratei
+ - não reconheço a contratação
+ - nao reconheco a contratacao
+ - não contratei esse plano
+ - nao contratei esse plano
examples:
- Quero contestar essa cobrança
- Esse valor é indevido
@@ -118,7 +148,7 @@ intents:
domain: telecom_contas
agent: contestacao_agent
priority: 145
- description: Cancelamento ou retirada de serviço/VAS e sua cobrança recorrente.
+ description: Cancelamento ou retirada explicitamente solicitada de serviço/VAS. Não use para simples alegação de contratação não reconhecida; nesses casos use contestação.
mcp_tools:
- consultar_vas
- cancelar_vas_avulso
@@ -130,12 +160,9 @@ intents:
- retirar servico
- desativar serviço
- desativar servico
- - não contratei esse serviço
- - nao contratei esse servico
- tirar esse vas
examples:
- Quero cancelar esse serviço
- - Não contratei esse VAS
- Quero retirar essa assinatura
- name: contas_vas_strategic
domain: telecom_contas
@@ -169,7 +196,8 @@ intents:
agent: vas_agent
priority: 110
description: Consulta ou explicação de VAS, serviço adicional, assinatura, aplicativo,
- parceiro, bundle ou benefício do plano.
+ parceiro, bundle ou benefício adicional. Não use esta intent para perguntas sobre
+ nome, identificação ou valor do plano principal/plano contratado da linha.
mcp_tools:
- consultar_vas
- buscar_informacao
diff --git a/config/tools.yaml b/config/tools.yaml
index eda07b2..48c3e4b 100644
--- a/config/tools.yaml
+++ b/config/tools.yaml
@@ -1,7 +1,7 @@
tools:
consultar_faturas:
- description: Consulta faturas atuais/anteriores, status, vencimento, valor e existência
- de fatura aberta sem iniciar o workflow de explicação.
+ description: Consulta faturas atuais/anteriores, status, vencimento, valor e existência de fatura aberta sem iniciar o
+ workflow de explicação.
mcp_server: contas
enabled: true
args_schema:
@@ -15,9 +15,26 @@ tools:
- fatura atual
- fatura em aberto
- conta em aberto
+ consultar_plano:
+ description: Consulta exclusivamente o nome e valor do plano ou planos contratados a partir da evidência de billing analysis;
+ não retorna VAS como plano.
+ mcp_server: contas
+ enabled: true
+ args_schema:
+ msisdn: string
+ selection_keywords:
+ - qual é meu plano
+ - qual e meu plano
+ - plano contratado
+ - nome do meu plano
+ - que plano eu tenho
+ - quais são meus planos
+ response:
+ mode: renderer
+ renderer: contas.plan
invoice_explanation:
- description: Explica composição, diferença ou variação de valores de uma fatura;
- não deve ser usada apenas para consultar/obter a fatura.
+ description: Explica composição, diferença ou variação de valores de uma fatura; não deve ser usada apenas para consultar/obter
+ a fatura.
mcp_server: contas
enabled: true
args_schema:
@@ -33,8 +50,7 @@ tools:
- cobrança diferente
- por que esse valor
buscar_informacao:
- description: Conhecimento do Contas é atendido pelo RagService do agent_framework_oci,
- não pelo MCP.
+ description: Conhecimento do Contas é atendido pelo RagService do agent_framework_oci, não pelo MCP.
mcp_server: contas
enabled: false
args_schema:
@@ -52,6 +68,9 @@ tools:
enabled: true
args_schema:
msisdn: string
+ response:
+ mode: renderer
+ renderer: contas.vas
selection_keywords:
- vas
- serviço adicional
@@ -74,8 +93,7 @@ tools:
- serviços anteriores
- servicos anteriores
cancelar_vas_avulso:
- description: Executa cancelamento de VAS avulso no novo domínio Contas; confirmação
- e estado são do framework.
+ description: Executa cancelamento de VAS avulso no novo domínio Contas; confirmação e estado são do framework.
mcp_server: contas
enabled: true
tool_type: action
@@ -93,8 +111,7 @@ tools:
- desativar serviço
- desativar servico
tratar_vas_estrategico:
- description: Trata VAS estratégico/bundle no novo domínio Contas; diálogo e estado
- são do framework.
+ description: Trata VAS estratégico/bundle no novo domínio Contas; diálogo e estado são do framework.
mcp_server: contas
enabled: true
tool_type: conversational
@@ -115,8 +132,7 @@ tools:
- globoplay
- amazon prime
contestar_cobranca:
- description: Executa contestação no novo domínio Contas após confirmação transacional
- do framework.
+ description: Executa contestação no novo domínio Contas após confirmação transacional do framework.
mcp_server: contas
enabled: true
tool_type: action
@@ -138,8 +154,7 @@ tools:
- ajuste na fatura
- estorno
finalizar_atendimento:
- description: Executa apenas efeitos de domínio da finalização; encerramento, memória
- e telemetria ficam no framework.
+ description: Executa apenas efeitos de domínio da finalização; encerramento, memória e telemetria ficam no framework.
mcp_server: contas
enabled: true
tool_type: action
@@ -160,8 +175,7 @@ tools:
msisdn: string
protocol: string
enviar_sms:
- description: Envia SMS via integração TIM; use apenas quando um fluxo de domínio
- exigir.
+ description: Envia SMS via integração TIM; use apenas quando um fluxo de domínio exigir.
mcp_server: contas
enabled: true
args_schema:
diff --git a/contas_mcp/__pycache__/__init__.cpython-313.pyc b/contas_mcp/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..3a7efe4
Binary files /dev/null and b/contas_mcp/__pycache__/__init__.cpython-313.pyc differ
diff --git a/contas_mcp/servers/__pycache__/__init__.cpython-313.pyc b/contas_mcp/servers/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..ea8e9ac
Binary files /dev/null and b/contas_mcp/servers/__pycache__/__init__.cpython-313.pyc differ
diff --git a/contas_mcp/servers/contas_mcp_server/__pycache__/__init__.cpython-313.pyc b/contas_mcp/servers/contas_mcp_server/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..f5092eb
Binary files /dev/null and b/contas_mcp/servers/contas_mcp_server/__pycache__/__init__.cpython-313.pyc differ
diff --git a/contas_mcp/servers/contas_mcp_server/__pycache__/main.cpython-313.pyc b/contas_mcp/servers/contas_mcp_server/__pycache__/main.cpython-313.pyc
new file mode 100644
index 0000000..e7914c6
Binary files /dev/null and b/contas_mcp/servers/contas_mcp_server/__pycache__/main.cpython-313.pyc differ
diff --git a/contas_mcp/servers/contas_mcp_server/main.py b/contas_mcp/servers/contas_mcp_server/main.py
index bc51e36..25504e2 100644
--- a/contas_mcp/servers/contas_mcp_server/main.py
+++ b/contas_mcp/servers/contas_mcp_server/main.py
@@ -1,6 +1,8 @@
from __future__ import annotations
import sys
+import re
+import unicodedata
from pathlib import Path
from typing import Any, Callable
@@ -70,6 +72,7 @@ class ToolCall(BaseModel):
TOOLS: dict[str, dict[str, Any]] = {
"consultar_faturas": {"description": "Consulta faturas do cliente.", "input_schema": {"msisdn": "string"}},
+ "consultar_plano": {"description": "Consulta exclusivamente o plano ou planos contratados presentes na fatura.", "input_schema": {"msisdn": "string"}},
"invoice_explanation": {"description": "Executa o workflow de explicação de fatura com pause/resume pelo WorkflowRuntime do framework.", "input_schema": {"msisdn": "string"}},
"consultar_vas": {"description": "Consulta VAS ativos.", "input_schema": {"msisdn": "string"}},
"consultar_historico_vas": {"description": "Consulta histórico de VAS.", "input_schema": {"msisdn": "string"}},
@@ -111,6 +114,97 @@ def _clarification_option(item: Any) -> dict[str, Any]:
return {"label": label, "value": name, "msisdn": msisdn, "charge_date": charge_date}
+
+def _norm_invoice_name(value: Any) -> str:
+ """Normalização conservadora para comparação exata de nomes de itens."""
+ text = str(value or "").strip().casefold().replace("+", " ")
+ text = unicodedata.normalize("NFKD", text)
+ text = "".join(ch for ch in text if not unicodedata.combining(ch))
+ text = re.sub(r"[^a-z0-9]+", " ", text)
+ return re.sub(r"\s+", " ", text).strip()
+
+
+def _invoice_subject_catalog(args: dict[str, Any]) -> list[dict[str, Any]]:
+ """Catálogo mínimo de itens reais da fatura, sem fuzzy matching.
+
+ É usado como barreira de consistência antes do resolver especializado em VAS.
+ """
+ found: dict[str, dict[str, Any]] = {}
+
+ def add(name: Any, value: Any = None, category: Any = None, contestable: Any = None) -> None:
+ canonical = str(name or "").strip()
+ key = _norm_invoice_name(canonical)
+ if not key:
+ return
+ item = found.setdefault(key, {"name": canonical, "value": value, "category": str(category or "").strip(), "contestable": contestable})
+ if item.get("value") in (None, "") and value not in (None, ""):
+ item["value"] = value
+ if not item.get("category") and category:
+ item["category"] = str(category).strip()
+ if item.get("contestable") is None and contestable is not None:
+ item["contestable"] = contestable
+
+ analysis = args.get("billing_analysis")
+ if isinstance(analysis, dict):
+ for section_name in ("currentInvoice", "invoiceVariation"):
+ sections = analysis.get(section_name)
+ if not isinstance(sections, list):
+ continue
+ for section in sections:
+ if not isinstance(section, dict):
+ continue
+ category = section.get("type") or section.get("desc")
+ items = section.get("items")
+ if not isinstance(items, list):
+ continue
+ for item in items:
+ if isinstance(item, dict):
+ add(item.get("desc") or item.get("name"), item.get("value"), item.get("type") or category, item.get("contestable"))
+
+ detail = args.get("invoice_detail")
+ if isinstance(detail, dict):
+ parsed = detail.get("parsed_content") if isinstance(detail.get("parsed_content"), dict) else detail
+ if isinstance(parsed, dict):
+ # parsed_content normalmente é agrupado por MSISDN.
+ roots = list(parsed.values()) if parsed and all(isinstance(v, dict) for v in parsed.values()) else [parsed]
+ for root in roots:
+ if not isinstance(root, dict):
+ continue
+ plans = root.get("Planos") or root.get("Plano")
+ if isinstance(plans, dict):
+ for plan_name, plan_data in plans.items():
+ value = plan_data.get("valor_final") if isinstance(plan_data, dict) else None
+ add(plan_name, value, "plano", False)
+ for section_name, section_value in root.items():
+ if section_name in {"Planos", "Plano"} or not isinstance(section_value, list):
+ continue
+ for item in section_value:
+ if isinstance(item, dict):
+ add(item.get("desc") or item.get("name"), item.get("value") or item.get("valor_final"), item.get("classe") or section_name, item.get("contestable"))
+
+ return list(found.values())
+
+
+def _recover_explicit_contestation_subject(args: dict[str, Any]) -> dict[str, Any] | None:
+ """Recupera item explicitamente citado no texto original da contestação.
+
+ Na confirmação a mensagem corrente pode ser apenas ``sim``. O ``motivo``/
+ ``descricao`` preserva a fala que abriu a transação e é uma fonte mais forte
+ que um ``subject`` residual. Só corrige quando há exatamente um nome canônico
+ da fatura explicitamente presente; não faz fuzzy matching.
+ """
+ original = " ".join(str(args.get(k) or "") for k in ("motivo", "descricao")).strip()
+ if not original:
+ return None
+ norm_original = f" {_norm_invoice_name(original)} "
+ matches = []
+ for item in _invoice_subject_catalog(args):
+ name_norm = _norm_invoice_name(item.get("name"))
+ if name_norm and f" {name_norm} " in norm_original:
+ matches.append(item)
+ return matches[0] if len(matches) == 1 else None
+
+
def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None:
"""Resolve/desambigua o item contra a evidência da fatura antes do workflow.
@@ -125,6 +219,42 @@ def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None
subject = str(args.get("subject") or "").strip()
if not msisdn or not subject:
return None
+ # Contestação pode referenciar qualquer categoria da fatura. Antes do
+ # resolver especializado em serviços/VAS, preserve um item explicitamente
+ # citado no texto que abriu a transação. Isso impede que um plano explícito
+ # seja reinterpretado como outro VAS durante a confirmação.
+ if name == "contestar_cobranca":
+ explicit = _recover_explicit_contestation_subject(args)
+ if explicit is not None:
+ previous_subject = subject
+ canonical = str(explicit.get("name") or subject)
+ args["subject"] = canonical
+ subject = canonical
+ if explicit.get("value") not in (None, ""):
+ args["resolved_value"] = str(explicit.get("value"))
+ category = str(explicit.get("category") or "").strip()
+ if category:
+ args["resolved_category"] = category
+ if _norm_invoice_name(previous_subject) != _norm_invoice_name(canonical):
+ args["_subject_corrected_from"] = previous_subject
+
+ # O workflow/CVAL atual de contestar_cobranca trata VAS. Um plano
+ # existente deve ser rejeitado com o próprio nome/valor, nunca
+ # convertido em outro serviço.
+ if _norm_invoice_name(category) in {"plano", "planos"}:
+ return {
+ "status": "OUT_OF_SCOPE",
+ "success": False,
+ "subject": canonical,
+ "resolved_value": args.get("resolved_value"),
+ "category": category or "plano",
+ "error": f"O item '{canonical}' existe na fatura e é um plano, mas não pertence a uma categoria tratável por esta operação.",
+ "metadata": {
+ "subject_corrected_from": args.get("_subject_corrected_from"),
+ "subject_source": "explicit_contestation_text",
+ },
+ }
+
try:
invoice_detail = args.get("billing_analysis")
if not isinstance(invoice_detail, dict):
@@ -176,7 +306,7 @@ def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None
return None
async def _enrich_invoice_context(name: str, args: dict[str, Any]) -> None:
- if name not in {"invoice_explanation", "cancelar_vas_avulso", "tratar_vas_estrategico", "contestar_cobranca"}:
+ if name not in {"consultar_plano", "invoice_explanation", "cancelar_vas_avulso", "tratar_vas_estrategico", "contestar_cobranca"}:
return
msisdn = str(args.get("msisdn") or "").strip()
if not msisdn:
@@ -690,6 +820,7 @@ async def _run_cancelamento_com_contestacao(args: dict[str, Any]) -> dict[str, A
async def _invoke(name: str, args: dict[str, Any]) -> Any:
requirements = {
"consultar_faturas": ("msisdn",),
+ "consultar_plano": ("msisdn",),
"invoice_explanation": ("msisdn",),
"consultar_vas": ("msisdn",),
"consultar_historico_vas": ("msisdn",),
diff --git a/data/agent_framework.db b/data/agent_framework.db
new file mode 100644
index 0000000..0d835a6
Binary files /dev/null and b/data/agent_framework.db differ
diff --git a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..857919f
Binary files /dev/null and b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..80a0749
Binary files /dev/null and b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..a058388
Binary files /dev/null and b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..7c35b8e
Binary files /dev/null and b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..75d18ef
Binary files /dev/null and b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..2046841
Binary files /dev/null and b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..d11c3b1
Binary files /dev/null and b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..b0f5f4c
Binary files /dev/null and b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..9b41048
Binary files /dev/null and b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..0240ce5
Binary files /dev/null and b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..e08253f
Binary files /dev/null and b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..0fc052a
Binary files /dev/null and b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/test_mcp_contestation_subject_guard.py b/tests/migration/test_mcp_contestation_subject_guard.py
new file mode 100644
index 0000000..0cf4fc4
--- /dev/null
+++ b/tests/migration/test_mcp_contestation_subject_guard.py
@@ -0,0 +1,72 @@
+from contas_mcp.servers.contas_mcp_server.main import (
+ _preflight_subject,
+ _recover_explicit_contestation_subject,
+)
+
+
+def _args(subject="TIM Fashion Mensal"):
+ return {
+ "msisdn": "11999999999",
+ "subject": subject,
+ "motivo": "não contratei TIM CTRL Redes Sociais 8.0",
+ "valor": 71.99,
+ "billing_analysis": {
+ "currentInvoice": [
+ {
+ "type": "plano",
+ "desc": "Plano",
+ "items": [
+ {"type": "plano", "desc": "TIM Black A 8.0", "value": "104.99", "contestable": False},
+ {"type": "plano", "desc": "TIM CTRL Redes Sociais 8.0", "value": "71.99", "contestable": False},
+ ],
+ },
+ {
+ "type": "servicos_contratados_de_parceiros",
+ "desc": "Serviços de valor adicionado",
+ "items": [
+ {"type": "servicos_contratados_de_parceiros", "desc": "TIM Fashion Mensal", "value": "10.0", "contestable": True},
+ ],
+ },
+ ]
+ },
+ "invoice_detail": {
+ "parsed_content": {
+ "1199999999": {
+ "Planos": {
+ "TIM CTRL Redes Sociais 8.0": {"valor_final": 71.99}
+ },
+ "SVA Detalhe Total": [
+ {"desc": "Tim Fashion", "value": 10, "classe": "avulso"}
+ ],
+ }
+ }
+ },
+ }
+
+
+def test_recovers_explicit_contestation_subject_from_original_text():
+ item = _recover_explicit_contestation_subject(_args())
+ assert item is not None
+ assert item["name"] == "TIM CTRL Redes Sociais 8.0"
+ assert str(item["value"]) == "71.99"
+ assert item["category"] == "plano"
+
+
+def test_preflight_does_not_replace_explicit_plan_with_vas():
+ args = _args()
+ result = _preflight_subject("contestar_cobranca", args)
+ assert result is not None
+ assert result["status"] == "OUT_OF_SCOPE"
+ assert result["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert result["resolved_value"] == "71.99"
+ assert result["metadata"]["subject_corrected_from"] == "TIM Fashion Mensal"
+ assert args["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert args["resolved_value"] == "71.99"
+
+
+def test_correct_subject_is_preserved():
+ args = _args(subject="TIM CTRL Redes Sociais 8.0")
+ result = _preflight_subject("contestar_cobranca", args)
+ assert result is not None
+ assert result["subject"] == "TIM CTRL Redes Sociais 8.0"
+ assert args.get("_subject_corrected_from") is None
diff --git a/tests/migration/test_original_contestation_validation.py b/tests/migration/test_original_contestation_validation.py
index 2bc422e..2260a65 100644
--- a/tests/migration/test_original_contestation_validation.py
+++ b/tests/migration/test_original_contestation_validation.py
@@ -446,3 +446,79 @@ def test_contestation_action_calculates_totals_only_for_items_actually_contested
assert [x["itemName"] for x in result["not_contested_items"]] == ["Outro VAS"]
assert result["contested_invoice_amount_open"] == "14.99"
assert result["contested_invoice_amount"] == "14.99"
+
+
+def test_validate_contestation_items_reconhece_vas_por_type_do_billing_analysis() -> None:
+ items = [
+ {
+ "item_name": "TIM Fashion Mensal",
+ "claimed_amount": "10",
+ "validated_amount": "10",
+ }
+ ]
+ invoice = {
+ "currentInvoice": [
+ {
+ "type": "servicos_contratados_de_parceiros",
+ "desc": "Serviços de valor adicionado",
+ "value": "76.98",
+ "items": [
+ {
+ "type": "servicos_contratados_de_parceiros",
+ "desc": "TIM Fashion Mensal",
+ "value": "10.0",
+ "contestable": True,
+ }
+ ],
+ }
+ ]
+ }
+
+ validated, validation_log, error = validate_contestation_items(items, invoice)
+
+ assert error is None
+ assert validation_log[-1]["status"] == "aprovado"
+ assert validation_log[-1]["secao_vas"] is True
+ assert validation_log[-1]["item_fatura_resolvido"] == "TIM Fashion Mensal"
+ assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros"
+ assert validated[0]["validated_amount"] == "10"
+
+
+def test_validate_contestation_items_prefere_evidencia_vas_quando_item_aparece_em_multiplas_visoes() -> None:
+ items = [
+ {
+ "item_name": "TIM Fashion Mensal",
+ "claimed_amount": "10",
+ "validated_amount": "10",
+ }
+ ]
+ invoice = {
+ "invoiceVariation": [
+ {
+ "desc": "TIM Fashion Mensal",
+ "value": "10.0",
+ }
+ ],
+ "currentInvoice": [
+ {
+ "type": "servicos_contratados_de_parceiros",
+ "desc": "Serviços de valor adicionado",
+ "value": "76.98",
+ "items": [
+ {
+ "type": "servicos_contratados_de_parceiros",
+ "desc": "TIM Fashion Mensal",
+ "value": "10.0",
+ "contestable": True,
+ }
+ ],
+ }
+ ],
+ }
+
+ _, validation_log, error = validate_contestation_items(items, invoice)
+
+ assert error is None
+ assert validation_log[-1]["status"] == "aprovado"
+ assert validation_log[-1]["secao_vas"] is True
+ assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros"
diff --git a/tests/migration/test_vas_response_renderer.py b/tests/migration/test_vas_response_renderer.py
new file mode 100644
index 0000000..a03ff62
--- /dev/null
+++ b/tests/migration/test_vas_response_renderer.py
@@ -0,0 +1,24 @@
+from app.presentation.tool_renderers import render_contas_vas
+
+
+def test_vas_renderer_includes_available_values():
+ result = {
+ "products": [
+ {"name": "TIM Fashion Mensal", "can": {"cancel": True}, "details": {"valor": "10,00"}},
+ {"name": "Aya Audiobooks Premium", "can": {"cancel": True}, "details": {"valor": "9,99"}},
+ {"name": "Neymar Jr", "can": {"cancel": True}, "details": {"valor": "12,00"}},
+ {"name": "Tamboro Mensal", "can": {"cancel": True}, "details": {"valor": "14,99"}},
+ ]
+ }
+ text = render_contas_vas(tool_name="consultar_vas", result=result, state={}, agent_label="VasAgent")
+ assert "TIM Fashion Mensal (avulso): R$ 10,00" in text
+ assert "Aya Audiobooks Premium (avulso): R$ 9,99" in text
+ assert "Neymar Jr (avulso): R$ 12,00" in text
+ assert "Tamboro Mensal (avulso): R$ 14,99" in text
+
+
+def test_vas_renderer_does_not_invent_missing_value():
+ result = {"products": [{"name": "Sem Preço", "can": {"cancel": True}, "details": {}}]}
+ text = render_contas_vas(tool_name="consultar_vas", result=result, state={}, agent_label="VasAgent")
+ assert "Sem Preço (avulso)" in text
+ assert "R$" not in text