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 d0b2b90..bc07267 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 3609a1e..50fd70a 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 750a7f1..868b18d 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 abe2d9d..5297e5a 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__/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 1c44d7b..6a077d2 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__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 3e23168..70803d5 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 863d19c..c62f7b4 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 e4bd26e..dd9c3d8 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 0634093..49c70be 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 3dc9f8e..93052a6 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 da395c6..863f5c0 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 4a31c5d..67dcd0c 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 8cb01ec..0dbd89e 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 57f8c45..715230e 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 20682da..4d7ec52 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__/contestation_validation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index e88ed6e..ac2b9d0 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 2e692ed..c5bcaaf 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 7d1f44a..89c78ab 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 9772a2d..df23f35 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 caf4f1b..b6bfcaa 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 fcdc277..8b9f759 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 a36bce5..fb48f8c 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 daa206a..1b50e7c 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/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 24c0300..a1c0ae3 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 84a4672..85e6834 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 d2948c8..014d27e 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 f78d1ba..1adbe28 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 ce28e29..4c25391 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 d7b0e3d..4c834ef 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 f18b89e..ae92b20 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 87681d3..e757574 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 dbf7799..cdb2534 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 528d266..d785a3a 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 384cd68..c586b64 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 dde7d46..6b2041d 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__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc index 84f78f3..50fcb15 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 9600db0..ec8f20b 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/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 679d83d..8f3fac0 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 5001ab4..e93d71e 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 93af918..659cca0 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 5d65f58..6c7e384 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 2d893db..f157348 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 7152ce1..e437bea 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 26340d5..b62a84f 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 19b310a..aa2be86 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 d82a04f..15dcb19 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/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 a00ad76..313b48a 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 e72c6d2..86c6d7e 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__/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 65efbd1..72d4bc1 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 32b0d82..85ecb20 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 fcfb31b..9a32e5b 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 @@ -323,10 +323,28 @@ class CoherenceRail(Guardrail): "data": out, }, ) - out = await classify_with_framework_llm( - _llm(ctx), "COER", {"text": text or "", "context": ctx}, - profile_name="guardrail", component_name="guardrail.coer", generation_name="guardrail.coer", - ) + try: + out = await classify_with_framework_llm( + _llm(ctx), "COER", {"text": text or "", "context": ctx}, + profile_name="guardrail", component_name="guardrail.coer", generation_name="guardrail.coer", + ) + except Exception as exc: + # COER is a conversational-coherence rail, not a security boundary. + # If its semantic classifier/provider is unavailable, the graph must + # continue and let the normal router/workflow clarification logic own + # the turn instead of surfacing an infrastructure exception. + return RailDecision( + code=self.code, + allowed=True, + reason="Classificador de coerência indisponível; continuidade delegada ao runtime", + sanitized_text=text, + metadata={ + "mechanism": "infrastructure_fail_open", + "calibrated": True, + "error_type": type(exc).__name__, + "error": str(exc)[:500], + }, + ) return RailDecision( code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "COER avaliado"), 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 da962e4..5312258 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 a519316..9603442 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/__pycache__/transaction_parameters.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc index 48591af..b797d4f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.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 10b05ee..aeaabec 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 @@ -12,7 +12,7 @@ from typing import Any, Iterable, Mapping from agent_framework.memory.summary_memory import MemoryContext, render_recent_messages -from agent_framework.runtime.transaction_parameters import extract_transaction_parameters, parse_transaction_confirmation +from agent_framework.runtime.transaction_parameters import extract_transaction_parameters, reconcile_transaction_parameters, parse_transaction_confirmation from agent_framework.workflows.input_contract import match_expected_input @@ -1466,6 +1466,153 @@ class AgentRuntimeMixin: cfg = self._tool_config(tool_name) return str(getattr(cfg, "description", "") or "") if cfg is not None else "" + @staticmethod + def _transaction_context_newest_first(context: Any) -> str: + """Normalize bounded conversation text into newest->oldest message blocks. + + Role-labelled multi-line messages are kept intact. This avoids reversing + individual lines inside an assistant explanation while still honoring the + temporal search order required by parameter reconciliation. + """ + raw = str(context or "").strip() + if not raw: + return "" + blocks: list[str] = [] + current: list[str] = [] + for line in raw.splitlines(): + stripped = line.strip() + if not stripped: + continue + low = stripped.lower() + starts_role = low.startswith(("user:", "assistant:", "system:", "cliente:", "agente:")) + if starts_role and current: + blocks.append(" ".join(current)) + current = [stripped] + else: + current.append(stripped) + if current: + blocks.append(" ".join(current)) + if not blocks: + blocks = [line.strip() for line in raw.splitlines() if line.strip()] + newest = list(reversed(blocks)) + return "\n".join(f"history:{i+1}: {block}" for i, block in enumerate(newest)) + + async def _reconcile_transaction_parameters( + self, + state: dict[str, Any], + *, + tool_name: str, + parameter_names: list[str], + known_arguments: dict[str, Any] | None = None, + ) -> dict[str, Any]: + """Reconcile a coherent required-parameter set from text and tool schema.""" + # Compatibility/extensibility: if a domain/runtime subclass explicitly + # overrides the legacy extractor, honor that override and translate its + # candidates into reconciliation decisions. The base framework path below + # remains the coherent temporal reconciler. + override = getattr(type(self), "_extract_transaction_parameters", None) + if override is not None and override is not AgentRuntimeMixin._extract_transaction_parameters: + route_meta_compat = ((state.get("route_decision") or {}).get("metadata") or {}) if isinstance(state.get("route_decision"), dict) else {} + compat_fields = list(parameter_names) if route_meta_compat.get("transaction_parameter_values") else [ + name for name in parameter_names if (known_arguments or {}).get(name) in _EMPTY_VALUES + ] + extracted = await override( + self, state, tool_name=tool_name, missing_parameters=compat_fields, + known_arguments=dict(known_arguments or {}), + ) + values = dict(extracted or {}) + decisions = {name: ("resolved" if name in values else ("preserve" if (known_arguments or {}).get(name) not in _EMPTY_VALUES else "unresolved")) for name in parameter_names} + provenance = {name: ("current" if name in values else "state") for name in parameter_names if decisions[name] != "unresolved"} + return {"values": values, "decisions": decisions, "provenance": provenance, "clear_fields": []} + + active = self._active_transaction(state) or {} + schema = active.get("parameter_schema") if isinstance(active.get("parameter_schema"), dict) else None + if not schema: + policy = self._resolve_tool_execution_policy(tool_name, known_arguments or {}) + schema = self._transaction_parameter_schema(tool_name, policy) + description = str(active.get("tool_description") or self._transaction_tool_description(tool_name) or "") + route_meta = ((state.get("route_decision") or {}).get("metadata") or {}) if isinstance(state.get("route_decision"), dict) else {} + contextual_reentry = bool(route_meta.get("contextual_reentry")) + text = (route_meta.get("original_input") if contextual_reentry else None) or state.get("sanitized_input") or state.get("user_text") or "" + conversational_context = route_meta.get("relevant_conversation_context") if contextual_reentry else None + if not str(conversational_context or "").strip(): + conversational_context = active.get("parameter_conversational_context") + if contextual_reentry and not str(conversational_context or "").strip(): + effective = str(route_meta.get("contextual_reentry_input") or "") + prefix = "CONTEXTO DA SOLICITAÇÃO IMEDIATAMENTE ANTERIOR:\n" + suffix = "\n\nCONTINUAÇÃO ATUAL DO CLIENTE:\n" + if prefix in effective and suffix in effective: + conversational_context = effective.split(prefix, 1)[1].split(suffix, 1)[0].strip() + ordered_context = self._transaction_context_newest_first(conversational_context) + cached = route_meta.get("transaction_parameter_values") + effective_known = dict(known_arguments or {}) + if isinstance(cached, dict): + for key, value in cached.items(): + if str(key) in set(str(x) for x in parameter_names) and value not in _EMPTY_VALUES: + effective_known[str(key)] = value + return await reconcile_transaction_parameters( + getattr(self, "llm", None), + text=str(text), + tool_name=tool_name, + parameter_names=[str(x) for x in parameter_names], + known_arguments=effective_known, + parameter_schema=schema, + tool_description=description, + conversational_context=ordered_context, + ) + + async def _extract_transaction_parameters_current_only( + self, + state: dict[str, Any], + *, + tool_name: str, + missing_parameters: list[str], + known_arguments: dict[str, Any] | None = None, + ) -> dict[str, Any]: + """Collect pending parameters from the current utterance only. + + This is the normal COLLECTING_PARAMETERS path. Temporal conversation + history is deliberately excluded here; `_reconcile_transaction_parameters` + is the fallback when this direct collection cannot satisfy or validate the + declarative parameter contract. + """ + if not missing_parameters: + return {} + + # Preserve the framework extension contract: domain/test runtimes that + # explicitly override the legacy collector remain the authoritative + # implementation of the *traditional* first pass. The temporal fallback + # is still owned by the base runtime and is invoked only after failure. + override = getattr(type(self), "_extract_transaction_parameters", None) + if override is not None and override is not AgentRuntimeMixin._extract_transaction_parameters: + return await override( + self, + state, + tool_name=tool_name, + missing_parameters=list(missing_parameters), + known_arguments=dict(known_arguments or {}), + ) + + active = self._active_transaction(state) or {} + schema = active.get("parameter_schema") if isinstance(active.get("parameter_schema"), dict) else None + if not schema: + policy = self._resolve_tool_execution_policy(tool_name, known_arguments or {}) + schema = self._transaction_parameter_schema(tool_name, policy) + description = str(active.get("tool_description") or self._transaction_tool_description(tool_name) or "") + route_meta = ((state.get("route_decision") or {}).get("metadata") or {}) if isinstance(state.get("route_decision"), dict) else {} + text = route_meta.get("original_input") if route_meta.get("contextual_reentry") else None + text = text or state.get("sanitized_input") or state.get("user_text") or "" + return await extract_transaction_parameters( + getattr(self, "llm", None), + text=str(text), + tool_name=tool_name, + missing_parameters=[str(x) for x in missing_parameters], + known_arguments=dict(known_arguments or {}), + parameter_schema=schema, + tool_description=description, + conversational_context="", + ) + async def _extract_transaction_parameters( self, state: dict[str, Any], @@ -1535,6 +1682,7 @@ class AgentRuntimeMixin: suffix = "\n\nCONTINUAÇÃO ATUAL DO CLIENTE:\n" if prefix in effective and suffix in effective: conversational_context = effective.split(prefix, 1)[1].split(suffix, 1)[0].strip() + ordered_context = self._transaction_context_newest_first(conversational_context) extracted = await extract_transaction_parameters( getattr(self, "llm", None), text=str(text), @@ -1543,9 +1691,42 @@ class AgentRuntimeMixin: known_arguments={**dict(known_arguments or {}), **reused}, parameter_schema=schema, tool_description=description, - conversational_context=str(conversational_context or ""), + conversational_context=ordered_context, ) - return {**reused, **extracted} + + # Compatibility/resilience fallback: if a provider returns only a partial + # flat extraction, scan bounded prior text source-by-source from newest to + # oldest for still-unresolved fields. This preserves temporal order and + # remains schema-driven; the main COLLECTING_PARAMETERS path uses the + # coherent set reconciler above. + combined = {**reused, **extracted} + unresolved = [name for name in missing_parameters if name not in combined] + if unresolved and str(conversational_context or "").strip(): + source_lines = self._transaction_context_newest_first(conversational_context).splitlines() + for source_line in source_lines: + if not unresolved: + break + source_text = source_line.split(": ", 1)[1] if ": " in source_line else source_line + low_source = source_text.lower() + for role_prefix in ("user: ", "assistant: ", "cliente: ", "agente: ", "system: "): + if low_source.startswith(role_prefix): + source_text = source_text[len(role_prefix):] + break + recovered = await extract_transaction_parameters( + getattr(self, "llm", None), + text=source_text, + tool_name=tool_name, + missing_parameters=list(unresolved), + known_arguments={**dict(known_arguments or {}), **combined}, + parameter_schema=schema, + tool_description=description, + conversational_context="", + ) + for key, value in recovered.items(): + if key in unresolved and value not in _EMPTY_VALUES: + combined[key] = value + unresolved = [name for name in unresolved if name not in combined] + return combined def _transactional_action_match(self, text: str, tools: list[str] | None = None) -> str | None: """Detecta solicitação transacional usando metadados de tools.yaml. @@ -2827,23 +3008,48 @@ class AgentRuntimeMixin: policy = self._resolve_tool_execution_policy(tool_name, previous_args) missing_before = self._missing_required_arguments(policy, previous_args) - # Parâmetros TRANSACIONAIS são interpretados exclusivamente pelo - # extrator LLM genérico. Durante COLLECTING_PARAMETERS, a fala atual - # também pode CORRIGIR um required field já coletado em turno anterior - # (ex.: valor=19,99 e o cliente diz "desculpa, é 14,99" enquanto - # subject ainda está pendente). Por isso o contrato editável do turno - # é o conjunto completo de ``requires``; somente as chaves realmente - # extraídas pela LLM sobrescrevem ``previous_args``. Campos não citados - # permanecem intactos. Isso preserva parameter-before-intent-shift sem - # tornar valores antigos imutáveis por acidente. - editable_required = [str(name) for name in (policy.get("requires") or [])] - extracted = await self._extract_transaction_parameters( + # Primeira tentativa: coleta tradicional usando SOMENTE a fala + # atual. O histórico não deve participar enquanto a mensagem corrente + # conseguir preencher o contrato normalmente. + arguments = dict(previous_args) + extracted_current = await self._extract_transaction_parameters_current_only( state, tool_name=tool_name, - missing_parameters=editable_required, + missing_parameters=missing_before, known_arguments=previous_args, ) - arguments = {**previous_args, **extracted} + arguments.update(dict(extracted_current or {})) + state["transaction_parameter_collection"] = { + "tool_name": tool_name, + "mode": "current_turn", + "resolved_fields": sorted(str(k) for k in (extracted_current or {}).keys()), + } + + # Segunda opção: somente se a coleta tradicional ainda deixar + # required fields em aberto, reconcilie temporalmente usando o + # schema/descrições declarados em tools.yaml e o contexto bounded + # newest->oldest preservado na transação. + missing_after_current = self._missing_required_arguments(policy, arguments) + if missing_after_current: + required_fields = [str(name) for name in (policy.get("requires") or [])] + reconciliation = await self._reconcile_transaction_parameters( + state, + tool_name=tool_name, + parameter_names=required_fields, + known_arguments=arguments, + ) + for field in reconciliation.get("clear_fields") or []: + arguments.pop(str(field), None) + arguments.update(dict(reconciliation.get("values") or {})) + state["transaction_parameter_reconciliation"] = { + "tool_name": tool_name, + "trigger": "traditional_collection_unresolved", + "decisions": dict(reconciliation.get("decisions") or {}), + "provenance": dict(reconciliation.get("provenance") or {}), + "clear_fields": list(reconciliation.get("clear_fields") or []), + } + else: + state.pop("transaction_parameter_reconciliation", None) # Argumentos estruturados já presentes no contexto são aceitos de # forma genérica (não são parsing textual). Para required fields, @@ -2890,7 +3096,65 @@ class AgentRuntimeMixin: state, tool_name=tool_name, arguments=arguments, policy=policy, emit_events=emit_events ) if pre_validation_result is not None: - return [pre_validation_result] + # A pre-validation é a fronteira autoritativa da coleta + # tradicional. Se ela rejeitar um candidato de forma recuperável + # (NEEDS_PARAMETER), ainda não repromptamos o usuário: fazemos UMA + # tentativa de reconciliação temporal com o histórico bounded e + # com a semântica declarada em tools.yaml. Isso cobre referências + # como "é a de R$ 19,99" sem tornar o reconciliador o caminho + # primário de COLLECTING_PARAMETERS. + pre_meta = state.get("transaction_pre_validation") + pre_meta = pre_meta if isinstance(pre_meta, dict) else {} + retry_parameter = str(pre_meta.get("parameter") or "").strip() + recoverable = ( + str(pre_meta.get("status") or "").upper() == "NEEDS_PARAMETER" + and bool(retry_parameter) + and state.get("transaction_status") == "COLLECTING_PARAMETERS" + ) + if recoverable: + recovered_selected = dict(self._active_transaction(state) or state.get("selected_tool_call") or {}) + recovered_arguments = dict(recovered_selected.get("arguments") or {}) + required_fields = [str(name) for name in (policy.get("requires") or [])] + reconciliation = await self._reconcile_transaction_parameters( + state, + tool_name=tool_name, + parameter_names=required_fields, + known_arguments=recovered_arguments, + ) + for field in reconciliation.get("clear_fields") or []: + recovered_arguments.pop(str(field), None) + recovered_arguments.update(dict(reconciliation.get("values") or {})) + state["transaction_parameter_reconciliation"] = { + "tool_name": tool_name, + "trigger": "prevalidation_needs_parameter", + "rejected_parameter": retry_parameter, + "decisions": dict(reconciliation.get("decisions") or {}), + "provenance": dict(reconciliation.get("provenance") or {}), + "clear_fields": list(reconciliation.get("clear_fields") or []), + } + retry_policy = self._resolve_tool_execution_policy(tool_name, recovered_arguments) + retry_missing = self._missing_required_arguments(retry_policy, recovered_arguments) + if retry_parameter not in retry_missing: + state["selected_tool_call"] = {"tool_name": tool_name, "arguments": recovered_arguments} + self._set_active_transaction( + state, tool_name=tool_name, arguments=recovered_arguments, status="COLLECTING_PARAMETERS" + ) + state["missing_parameters"] = retry_missing + pre_validation_retry = await self._run_transaction_pre_validation( + state, + tool_name=tool_name, + arguments=recovered_arguments, + policy=retry_policy, + emit_events=emit_events, + ) + if pre_validation_retry is None: + arguments = recovered_arguments + policy = retry_policy + pre_validation_result = None + else: + return [pre_validation_retry] + if pre_validation_result is not None: + return [pre_validation_result] tool_name, policy, force_confirmation = self._apply_prevalidated_transaction_decision( state, tool_name=tool_name, arguments=arguments, policy=policy diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py index 1a394f7..88cfbeb 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py @@ -69,69 +69,77 @@ def parse_transaction_confirmation(text: str) -> str | None: return None -async def extract_transaction_parameters( +async def reconcile_transaction_parameters( llm: Any, *, text: str, tool_name: str, - missing_parameters: list[str], + parameter_names: list[str], known_arguments: Mapping[str, Any] | None = None, parameter_schema: Mapping[str, Any] | None = None, tool_description: str | None = None, conversational_context: str | None = None, ) -> dict[str, Any]: - """Extract values for pending transactional parameters using the LLM only. + """Rebuild a coherent parameter set from text, newest to oldest. - This component intentionally contains no domain/entity regexes and no - knowledge of parameter names such as ``order_id`` or ``reason``. The - transaction runtime supplies the pending parameter names and optional schema; - the LLM only interprets the current user turn. State/control-flow decisions - remain deterministic outside this function. + The framework is intentionally field/domain neutral. Meaning comes from the + declarative tool schema (normally tools.yaml) plus the conversation text. + The LLM may resolve a field, preserve a previously known value, explicitly + clear a stale value whose context was superseded, or leave a field unresolved. + Returned provenance is interpretive metadata only; authoritative business + validation still belongs to the configured pre-validation/tool layer. """ - pending = [str(name) for name in (missing_parameters or []) if str(name).strip()] + names = [str(name) for name in (parameter_names or []) if str(name).strip()] message = str(text or "").strip() - if not pending or not message or llm is None: - return {} + if not names or not message or llm is None: + return {"values": {}, "decisions": {}, "provenance": {}, "clear_fields": []} schema = dict(parameter_schema or {}) known = { str(key): value for key, value in dict(known_arguments or {}).items() - if value not in _EMPTY_VALUES and str(key) not in pending + if value not in _EMPTY_VALUES } field_spec = { name: { "type": schema.get(name, "string") if not isinstance(schema.get(name), dict) else schema.get(name, {}).get("type", "string"), "description": None if not isinstance(schema.get(name), dict) else schema.get(name, {}).get("description"), } - for name in pending + for name in names + } + output_shape = { + "fields": { + name: {"decision": "resolved|preserve|clear|unresolved", "value": None, "source": "current|history:N|state"} + for name in names + } } - output_shape = {name: None for name in pending} prompt = ( - "Você extrai parâmetros PENDENTES de uma transação ativa. " - "Sua única tarefa é interpretar a mensagem atual e devolver valores para os parâmetros pendentes. " - "Não decida roteamento, intenção, confirmação ou execução da transação.\n\n" - "REGRAS OBRIGATÓRIAS:\n" - "1. Extraia SOMENTE parâmetros listados em pending_parameters.\n" - "2. Não invente valores e não transforme uma nova solicitação/intenção do usuário em valor de parâmetro.\n" - "3. Se nenhum parâmetro pendente foi realmente informado, devolva null para todos.\n" - "4. Se houver apenas um parâmetro pendente, uma resposta contendo apenas um valor pode ser associada a ele quando isso for semanticamente inequívoco.\n" - "5. Se houver vários parâmetros pendentes, extraia todos os que estiverem presentes no mesmo turno.\n" - "6. O nome do parâmetro não precisa aparecer literalmente na fala. Associe semanticamente o valor usando o nome da transação e os metadados disponíveis para cada campo.\n" - "7. Para cada parâmetro, considere o nome técnico, o tipo quando disponível e principalmente a descrição semântica quando disponível. A ausência de tipo ou descrição NÃO impede a extração.\n" - "8. Se a mensagem deixar clara a correspondência entre um trecho e um parâmetro, preencha-o mesmo que o usuário não cite o nome técnico do campo.\n" - "9. Não use conhecimento externo para completar valores ausentes e não transforme aproximações ou suposições em fatos.\n" - "10. conversational_context, quando presente, serve SOMENTE para resolver referências da mensagem atual (por exemplo: 'a de 14,99' apontando para um item citado imediatamente antes). Não trate texto do contexto como uma nova afirmação do cliente nem como evidência de negócio.\n" - "11. Quando a mensagem atual identifica um valor OU nome e o contexto imediatamente anterior contém uma única entidade compatível, você pode preencher essa entidade e os atributos pendentes inequivocamente associados a ela como CANDIDATOS. Exemplo genérico: se a fala identifica uma entidade e o contexto associa unicamente essa entidade a um valor requerido, o valor pode ser retornado como candidato. A validação autoritativa ocorrerá depois; não invente se houver ambiguidade.\n" - "12. Em caso de dúvida razoável sobre a correspondência ou o valor, prefira null.\n" - "13. Responda SOMENTE JSON válido, sem markdown, sem explicação e sem chaves extras.\n\n" + "Você é o conciliador temporal de parâmetros de uma transação ativa. " + "Reconstrua UM CONJUNTO COERENTE de parâmetros; não extraia cada campo de forma isolada. " + "Não decida roteamento, confirmação nem execução.\n\n" + "CONTRATO OBRIGATÓRIO:\n" + "1. O significado de cada campo vem EXCLUSIVAMENTE de parameter_schema e transaction_description, considerando principalmente a descrição semântica quando disponível. O framework não conhece conceitos de domínio por nome de campo.\n" + "2. A ausência de tipo ou descrição NÃO impede a extração quando o restante do contrato e o texto forem semanticamente suficientes.\n" + "3. A busca textual é temporal, em ordem decrescente: user_message é a fonte mais nova; depois conversational_context já vem do texto mais novo para o mais antigo.\n" + "3. Textos do usuário e textos explicativos do assistente podem ser usados para interpretar referências e relações entre campos. Não trate texto do contexto como uma nova afirmação do cliente; eles são contexto, não evidência autoritativa de negócio.\n" + "4. Para cada campo escolha: resolved = um texto determina novo valor; preserve = nenhum texto mais novo invalida o valor conhecido; clear = existe mudança textual mais nova que torna o valor conhecido incompatível/sem vínculo seguro com o novo contexto e ainda não há substituto; unresolved = não há valor conhecido nem candidato textual seguro.\n" + "5. Se uma fonte mais nova muda uma entidade, objeto, escopo ou outro contexto que dava sentido a campos extraídos anteriormente, REAVALIE os demais campos como conjunto. Não combine automaticamente um atributo antigo com um contexto novo só porque ambos existem.\n" + "6. Um valor de texto anterior pode continuar válido após uma mudança somente quando os textos, considerados em conjunto, sustentarem de forma inequívoca que ele ainda pertence ao contexto mais recente. Caso contrário use clear para o campo dependente.\n" + "7. Se a nova menção é inválida no mundo real, NÃO volte silenciosamente ao valor antigo. Ainda assim devolva o candidato textual mais recente como resolved; a pre-validation autoritativa é responsável por rejeitá-lo.\n" + "8. known_arguments é fallback de estado. Use preserve somente quando nenhum texto mais novo o contradiz ou rompe sua associação contextual.\n" + "9. Uma expressão só pode preencher um campo se satisfizer semanticamente type/description desse campo. Não transfira um trecho para outro campo apenas por proximidade lexical.\n" + "10. Se um texto recente corrige explicitamente informação anterior, a correção prevalece para os campos que o schema permite inferir; os demais devem ser reavaliados quanto à coerência com a correção.\n" + "11. Em ambiguidade razoável, prefira clear/unresolved a inventar uma associação. Em caso de dúvida razoável sobre a correspondência ou o valor, prefira null.\n" + "12. Responda SOMENTE JSON válido no formato pedido, sem markdown e sem chaves extras.\n\n" f"transaction_tool: {tool_name}\n" f"transaction_description: {tool_description or ''}\n" - f"pending_parameters: {json.dumps(pending, ensure_ascii=False)}\n" + f"parameter_names: {json.dumps(names, ensure_ascii=False)}\n" + f"pending_parameters: {json.dumps(names, ensure_ascii=False)}\n" f"parameter_schema: {json.dumps(field_spec, ensure_ascii=False, default=str)}\n" f"known_arguments: {json.dumps(known, ensure_ascii=False, default=str)}\n" - f"conversational_context: {str(conversational_context or '').strip()}\n" + "conversation_sources_newest_to_oldest:\n" f"user_message: {message}\n" + f"conversational_context: {str(conversational_context or '').strip()}\n" f"Formato obrigatório: {json.dumps(output_shape, ensure_ascii=False)}" ) @@ -144,44 +152,98 @@ async def extract_transaction_parameters( temperature=0.0, ) except TypeError: - # Compatibilidade com doubles/testes e providers mínimos que aceitam - # apenas messages. response = await llm.ainvoke([{"role": "user", "content": prompt}]) except Exception as exc: - logger.warning( - "transaction.parameter.llm_extract_failed tool=%s pending=%s error=%s", - tool_name, - pending, - exc, - ) - return {} + logger.warning("transaction.parameter.reconcile_failed tool=%s fields=%s error=%s", tool_name, names, exc) + return {"values": {}, "decisions": {}, "provenance": {}, "clear_fields": []} raw = _response_text(response).strip() try: payload = parse_json_object(raw) except (TypeError, ValueError): - logger.warning( - "transaction.parameter.llm_invalid_structured_output tool=%s pending=%s raw=%r", - tool_name, - pending, - raw[:240], - ) - return {} + logger.warning("transaction.parameter.reconcile_invalid_output tool=%s raw=%r", tool_name, raw[:240]) + return {"values": {}, "decisions": {}, "provenance": {}, "clear_fields": []} if not isinstance(payload, dict): - return {} + return {"values": {}, "decisions": {}, "provenance": {}, "clear_fields": []} - extracted: dict[str, Any] = {} - for name in pending: - value = payload.get(name) + # Backward compatibility with existing providers/test doubles that return a + # flat {field: value} object. Non-null flat values mean ``resolved``. + fields = payload.get("fields") if isinstance(payload.get("fields"), dict) else None + if fields is None: + fields = {} + for name in names: + flat_value = payload.get(name) + if flat_value in _EMPTY_VALUES: + decision = "preserve" if name in known else "unresolved" + source = "state" if decision == "preserve" else "" + else: + decision = "resolved" + source = "current" + fields[name] = {"decision": decision, "value": flat_value, "source": source} + + values: dict[str, Any] = {} + decisions: dict[str, str] = {} + provenance: dict[str, str] = {} + clear_fields: list[str] = [] + for name in names: + item = fields.get(name) if isinstance(fields, dict) else None + if not isinstance(item, dict): + item = {"decision": "unresolved", "value": None, "source": ""} + decision = str(item.get("decision") or "unresolved").strip().lower() + if decision not in {"resolved", "preserve", "clear", "unresolved"}: + decision = "unresolved" + decisions[name] = decision + source = str(item.get("source") or "").strip() + if source: + provenance[name] = source + if decision == "clear": + clear_fields.append(name) + continue + if decision == "preserve": + if name in known: + values[name] = known[name] + continue + if decision != "resolved": + continue declared = field_spec.get(name, {}).get("type", "string") - coerced = _coerce(value, declared) + coerced = _coerce(item.get("value"), declared) if coerced not in _EMPTY_VALUES: - extracted[name] = coerced + values[name] = coerced + else: + decisions[name] = "unresolved" logger.info( - "transaction.parameter.llm_extracted tool=%s pending=%s consumed=%s", - tool_name, - pending, - sorted(extracted), + "transaction.parameter.reconciled tool=%s decisions=%s resolved=%s clear=%s provenance=%s", + tool_name, decisions, sorted(values), clear_fields, provenance, ) - return extracted + return {"values": values, "decisions": decisions, "provenance": provenance, "clear_fields": clear_fields} + + +async def extract_transaction_parameters( + llm: Any, + *, + text: str, + tool_name: str, + missing_parameters: list[str], + known_arguments: Mapping[str, Any] | None = None, + parameter_schema: Mapping[str, Any] | None = None, + tool_description: str | None = None, + conversational_context: str | None = None, +) -> dict[str, Any]: + """Compatibility facade returning only resolved candidates. + + New runtime code should use :func:`reconcile_transaction_parameters` when it + needs preserve/clear/provenance decisions. Keeping this facade avoids + breaking routers and existing extensions that only need candidate extraction. + """ + result = await reconcile_transaction_parameters( + llm, + text=text, + tool_name=tool_name, + parameter_names=missing_parameters, + known_arguments=known_arguments, + parameter_schema=parameter_schema, + tool_description=tool_description, + conversational_context=conversational_context, + ) + return dict(result.get("values") or {}) diff --git a/app/__pycache__/__init__.cpython-313.pyc b/app/__pycache__/__init__.cpython-313.pyc index 40046a3..ea9389d 100644 Binary files a/app/__pycache__/__init__.cpython-313.pyc and b/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/__pycache__/main.cpython-313.pyc b/app/__pycache__/main.cpython-313.pyc index 75df421..e291349 100644 Binary files a/app/__pycache__/main.cpython-313.pyc and b/app/__pycache__/main.cpython-313.pyc differ diff --git a/app/__pycache__/state.cpython-313.pyc b/app/__pycache__/state.cpython-313.pyc index a28ab13..8006de7 100644 Binary files a/app/__pycache__/state.cpython-313.pyc 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 index 52278b7..16fc0c5 100644 Binary files a/app/agents/__pycache__/contas_prompting.cpython-313.pyc 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 index 66aa0dd..2bf53fa 100644 Binary files a/app/agents/__pycache__/contestacao_agent.cpython-313.pyc 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 index 72a4d0f..1adacfe 100644 Binary files a/app/agents/__pycache__/faturas_agent.cpython-313.pyc 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 index 99ce6c8..9906660 100644 Binary files a/app/agents/__pycache__/prompting.cpython-313.pyc 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 index 4a568f3..2f4763b 100644 Binary files a/app/agents/__pycache__/runtime.cpython-313.pyc 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 index a2db8bc..08b6d61 100644 Binary files a/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc 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 index 0447c9e..b2cf3cf 100644 Binary files a/app/agents/__pycache__/vas_agent.cpython-313.pyc and b/app/agents/__pycache__/vas_agent.cpython-313.pyc differ diff --git a/app/agents/suporte_contas_agent.py b/app/agents/suporte_contas_agent.py index ff5262f..6cad654 100644 --- a/app/agents/suporte_contas_agent.py +++ b/app/agents/suporte_contas_agent.py @@ -49,6 +49,19 @@ class SuporteContasAgent(AgentRuntimeMixin): ) state["mcp_results"] = tool_context + + # A no-match retry is already a resolved control-flow decision: the + # framework/router concluded that the utterance is not understood. Do + # not ask another generative model to reinterpret it into a concrete + # business action (which can fabricate intent from noisy ASR). + if str(state.get("intent") or "") == "contas_no_match_retry": + return { + "answer": "Desculpe, não entendi. Poderia repetir de outra forma?", + "next_state": state.get("next_state") or "SUPORTE_CONTAS_ACTIVE", + "mcp_results": tool_context, + **self.transaction_state_patch(state), + } + clarification_message = self.transaction_clarification_message(state) if clarification_message: return { diff --git a/app/domain/__pycache__/__init__.cpython-313.pyc b/app/domain/__pycache__/__init__.cpython-313.pyc index ae38dbf..9034a6b 100644 Binary files a/app/domain/__pycache__/__init__.cpython-313.pyc 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 index ddb1b8e..9ebe6d4 100644 Binary files a/app/domain/contas/__pycache__/__init__.cpython-313.pyc 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 index 6c06776..3549f92 100644 Binary files a/app/domain/contas/__pycache__/client.cpython-313.pyc 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 index 74643a2..3234a4e 100644 Binary files a/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc and b/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/contestation_validation.cpython-313.pyc b/app/domain/contas/__pycache__/contestation_validation.cpython-313.pyc index 61cefc4..8e6f974 100644 Binary files a/app/domain/contas/__pycache__/contestation_validation.cpython-313.pyc and b/app/domain/contas/__pycache__/contestation_validation.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/conversation_policy.cpython-313.pyc b/app/domain/contas/__pycache__/conversation_policy.cpython-313.pyc index 07cf4d7..3f06305 100644 Binary files a/app/domain/contas/__pycache__/conversation_policy.cpython-313.pyc and b/app/domain/contas/__pycache__/conversation_policy.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 index 07c1b51..adf8037 100644 Binary files a/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc 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 index 0ab42f7..1e72172 100644 Binary files a/app/domain/contas/__pycache__/informational_context.cpython-313.pyc 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 index b2f9eb7..13a18a7 100644 Binary files a/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc 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 index 0045e0f..ba453a0 100644 Binary files a/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc 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 index 2386373..3b17448 100644 Binary files a/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc 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 index fe04912..9c842b8 100644 Binary files a/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc and b/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/line_reference.cpython-313.pyc b/app/domain/contas/__pycache__/line_reference.cpython-313.pyc index 551e3f5..19125a5 100644 Binary files a/app/domain/contas/__pycache__/line_reference.cpython-313.pyc and b/app/domain/contas/__pycache__/line_reference.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 index 2d6587b..1dc212c 100644 Binary files a/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc 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 index 2db44ad..4b89973 100644 Binary files a/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc 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 index 7c019eb..d34ef17 100644 Binary files a/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc 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 index af5e109..513ddd3 100644 Binary files a/app/domain/contas/__pycache__/service.cpython-313.pyc 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 index 6386806..7cba0c5 100644 Binary files a/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc 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 index d2378dc..16dbefe 100644 Binary files a/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc 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 index 1e1cf55..589b865 100644 Binary files a/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc 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 index 8741f77..f2c3ff7 100644 Binary files a/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc 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 index 3764097..17a8d70 100644 Binary files a/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc 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 index b208cb5..f61e15c 100644 Binary files a/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc 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 index 4280f88..252f8ee 100644 Binary files a/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc 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 index 6f5fa98..3434ae1 100644 Binary files a/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc and b/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc differ diff --git a/app/extensions/__pycache__/__init__.cpython-313.pyc b/app/extensions/__pycache__/__init__.cpython-313.pyc index ed789e3..31a04df 100644 Binary files a/app/extensions/__pycache__/__init__.cpython-313.pyc and b/app/extensions/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/extensions/__pycache__/tim_guardrails.cpython-313.pyc b/app/extensions/__pycache__/tim_guardrails.cpython-313.pyc index b0ae05d..99c6d05 100644 Binary files a/app/extensions/__pycache__/tim_guardrails.cpython-313.pyc and b/app/extensions/__pycache__/tim_guardrails.cpython-313.pyc differ diff --git a/app/extensions/__pycache__/tim_judges.cpython-313.pyc b/app/extensions/__pycache__/tim_judges.cpython-313.pyc index 2c8ef81..c13caca 100644 Binary files a/app/extensions/__pycache__/tim_judges.cpython-313.pyc and b/app/extensions/__pycache__/tim_judges.cpython-313.pyc differ diff --git a/app/extensions/tim_guardrails.py b/app/extensions/tim_guardrails.py index ee520be..0542491 100644 --- a/app/extensions/tim_guardrails.py +++ b/app/extensions/tim_guardrails.py @@ -147,6 +147,43 @@ def _authorized_human_handoff(context: dict[str, Any]) -> bool: return any(terminal_workflow_handoff(root) for root in roots) + + +def _deterministic_tim_domain_response(text: str, context: dict[str, Any]) -> bool: + """Conservative fast path for unmistakable TIM Contas domain output. + + This extension may know the TIM Contas vocabulary; the generic framework does + not. Human-transfer language is intentionally excluded so handoff authorization + remains exclusively structural. Ambiguous domain prose still goes to TIM_OOS LLM. + """ + normalized = re.sub(r'\s+', ' ', str(text or '').strip().lower()) + if not normalized: + return False + + handoff_markers = ( + 'encaminhar', 'encaminhado', 'transferir', 'transferido', + 'atendente', 'pessoa', 'humano', 'especialista', + ) + if any(m in normalized for m in handoff_markers): + return False + + domain_markers = ( + 'contestação', 'contestacao', 'fatura', 'cobrança', 'cobranca', + 'plano', 'serviço', 'servico', 'vas', 'boleto', 'desconto', + ) + completion_markers = ( + 'registrad', 'cancelad', 'concluíd', 'concluid', 'executad', + 'processad', 'enviad', 'emitid', 'protocolo', + ) + + # Keep the bypass intentionally narrow: an unmistakable domain noun plus an + # operational/result marker. Ordinary explanations and ambiguous responses are + # still semantically audited by TIM_OOS. + return ( + any(m in normalized for m in domain_markers) + and any(m in normalized for m in completion_markers) + ) + def _parse_json(raw: Any) -> dict[str, Any]: text = str(getattr(raw, 'content', raw) or '').strip() m = re.search(r'\{[\s\S]*\}', text) @@ -196,6 +233,22 @@ class TimOutOfScopeRail(_TimPromptRail): }, }, ) + if _deterministic_tim_domain_response(text, context): + return RailDecision( + code=self.code, + allowed=True, + reason='resposta suportada pelo contexto do domínio TIM Contas', + sanitized_text=text, + metadata={ + 'external': True, + 'domain': 'TIM_CONTAS', + 'mechanism': 'deterministic_domain_bypass', + 'data': { + 'allowed': True, + 'reason': 'resposta suportada pelo contexto do domínio TIM Contas', + }, + }, + ) return await super().evaluate(text, context) class TimProactiveOfferRail(_TimPromptRail): @@ -347,6 +400,8 @@ class TimPrematureActionRail(_TimPromptRail): 'final_answer', 'customer_message', 'customer_response', 'answer', 'text', 'epistemic_status', 'output', 'result', 'items', 'results', 'errors', 'cancelados', 'nao_cancelados', 'nao_encontrados', 'terminal_status', + 'contestation_registered', 'contested_invoice_amount', 'contested_invoice_amount_open', + 'sms_sent', 'barcode', 'itemName', 'itemsResponse', 'contested_items', 'sr', } out = {} for k, v in value.items(): @@ -414,6 +469,142 @@ class TimPrematureActionRail(_TimPromptRail): return False return candidate in cls._authoritative_output_messages(context) + @staticmethod + def _normalized_text(value: Any) -> str: + text = str(value or '').lower() + text = text.replace('r$', ' ') + text = re.sub(r'\s+', ' ', text) + return text.strip() + + @classmethod + def _candidate_has_operational_completion_claim(cls, text: str) -> bool: + """Detect completion/effect claims, not ordinary explanations. + + REVPREC exists to stop claims that an operation already happened. A + billing explanation containing values/dates is not such a claim and must + not depend on a probabilistic binary LLM classification. + """ + normalized = cls._normalized_text(text) + patterns = ( + r'\b(cancelad[oa]s?|cancelei|cancelamos|cancelou|cancelamento .*conclu)', + r'\b(contesta(?:ção|cao) .*?(?:registrad|abert|conclu|criad))', + r'\b(registrad[oa]s?|executei|executamos|executou|executad[oa]s?|conclu[ií]d[oa]s?|processad[oa]s?)\b', + r'\b(enviad[oa]s?|emitid[oa]s?|retirad[oa]s?|removid[oa]s?)\b', + r'\b(protocol(?:o)? .*?(?:abert|gerad|registrad))', + r'\b(?:foi|foram) (?:cancelad|contestad|registrad|enviad|emitid|retirad|removid)', + r'\b(?:was|were|has been) (?:cancelled|canceled|registered|sent|issued|removed)', + ) + return any(re.search(pattern, normalized, re.I) for pattern in patterns) + + @classmethod + def _current_execution_scalar_evidence(cls, context: dict[str, Any]) -> tuple[list[str], bool, bool]: + """Flatten scalar facts from current successful result/output only. + + Returns (facts, completed, positive_effect). Inputs, conversation state, + history and metadata are excluded so user claims can never prove an action. + """ + roots = (context or {}).get('mcp_results') + if roots is None: + roots = (context or {}).get('tool_result') + if isinstance(roots, dict): + roots = [roots] + if not isinstance(roots, (list, tuple)): + return [], False, False + + facts: list[str] = [] + completed = False + positive = False + positive_status = {'completed', 'opened', 'open', 'iniciada', 'iniciado', 'success', 'succeeded', 'closed', 'fechado'} + positive_keys = { + 'success', 'executed', 'contestation_registered', 'sms_sent', + 'cancelled', 'canceled', 'registered', 'created', 'updated', + } + skip = {'state', 'input', 'metadata', 'conversation_history', 'history', 'session', 'session_metadata'} + + def walk(value: Any, path: tuple[str, ...] = ()) -> None: + nonlocal completed, positive + if isinstance(value, dict): + for k, v in value.items(): + key = str(k).lower() + if key in skip: + continue + if key == 'status' and str(v).strip().lower() == 'completed': + completed = True + if key in positive_keys and v is True: + positive = True + if key == 'status' and str(v).strip().lower() in positive_status: + positive = True + # Keys such as sms_sent/barcode are themselves meaningful + # evidence and can support paraphrased customer-facing text. + if key not in {'output', 'result', 'results'}: + facts.append(key) + walk(v, path + (key,)) + elif isinstance(value, (list, tuple)): + for item in value[:50]: + walk(item, path) + elif value not in (None, ''): + facts.append(str(value)) + + for item in roots: + if not isinstance(item, dict) or item.get('ok') is False: + continue + if str(((item.get('result') or {}) if isinstance(item.get('result'), dict) else {}).get('status') or '').upper() == 'COMPLETED': + completed = True + walk(item.get('result', item)) + return facts, completed, positive + + @classmethod + def _candidate_is_structurally_grounded_completion(cls, text: str, context: dict[str, Any]) -> bool: + """Allow a paraphrased completion only when current execution proves it. + + This deliberately does not know tool names. It requires a completed + current execution, a positive side-effect marker, grounding for every + material number mentioned by the candidate, and at least one meaningful + textual fact/entity shared with the execution result. + """ + if not cls._candidate_has_operational_completion_claim(text): + return False + facts, completed, positive = cls._current_execution_scalar_evidence(context) + if not (completed and positive and facts): + return False + + candidate = cls._normalized_text(text) + evidence_text = cls._normalized_text(' '.join(facts)) + + def canon_number(raw: str) -> str: + raw = raw.strip().replace(' ', '') + if ',' in raw and '.' in raw: + if raw.rfind(',') > raw.rfind('.'): + raw = raw.replace('.', '').replace(',', '.') + else: + raw = raw.replace(',', '') + else: + raw = raw.replace(',', '.') + try: + num = float(raw) + return (f'{num:.6f}').rstrip('0').rstrip('.') + except ValueError: + return re.sub(r'\D', '', raw) + + candidate_numbers = [canon_number(x) for x in re.findall(r'(?= 2] + if not material_numbers: + return False + if any(n not in evidence_numbers for n in material_numbers): + return False + + # Require an entity/message overlap beyond generic success vocabulary. + candidate_words = set(re.findall(r'[a-zà-ÿ0-9+_-]{4,}', candidate, re.I)) + stop = { + 'sucesso', 'registrada', 'registrado', 'concluido', 'concluida', + 'cliente', 'valor', 'protocolo', 'cobranca', 'contestacao', 'servico', + 'mensal', 'novo', 'boleto', 'recebeu', 'detalhes', + } + evidence_words = set(re.findall(r'[a-zà-ÿ0-9+_-]{4,}', evidence_text, re.I)) + meaningful_overlap = (candidate_words - stop) & (evidence_words - stop) + return bool(meaningful_overlap) + async def evaluate(self, text: str, context: dict[str, Any]) -> RailDecision: # Strongest possible proof: the exact customer-facing candidate was emitted by # the successful current-turn tool/workflow itself. REVPREC is about premature @@ -433,6 +624,41 @@ class TimPrematureActionRail(_TimPromptRail): }, ) + # REVPREC is not a general factuality rail. Ordinary descriptions and + # explanations contain no claim that an operation has already completed, + # so they are deterministically outside this rail's blocking scope. + if not self._candidate_has_operational_completion_claim(text): + return RailDecision( + code=self.code, + allowed=True, + reason='no_operational_completion_claim', + sanitized_text=text, + metadata={ + 'external': True, + 'domain': 'TIM_CONTAS', + 'mechanism': 'deterministic_claim_scope', + 'terminal_action': 'retry', + }, + ) + + # A composed/paraphrased response can still be proven by structured + # current-turn execution evidence even when it is not byte-for-byte equal + # to a workflow message. This removes nondeterministic false positives + # while keeping unsupported or contradictory claims on the semantic path. + if self._candidate_is_structurally_grounded_completion(text, context): + return RailDecision( + code=self.code, + allowed=True, + reason='current_execution_structurally_grounded', + sanitized_text=text, + metadata={ + 'external': True, + 'domain': 'TIM_CONTAS', + 'mechanism': 'deterministic_structured_execution_evidence', + 'terminal_action': 'retry', + }, + ) + evidence = self._current_execution_evidence(context) llm = _llm(context) if llm is None: diff --git a/app/extensions/tim_prompts/__pycache__/__init__.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/__init__.cpython-313.pyc index cb352ae..d60c976 100644 Binary files a/app/extensions/tim_prompts/__pycache__/__init__.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/extensions/tim_prompts/__pycache__/aluc.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/aluc.cpython-313.pyc index e461acb..51b29b9 100644 Binary files a/app/extensions/tim_prompts/__pycache__/aluc.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/aluc.cpython-313.pyc differ diff --git a/app/extensions/tim_prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index ec52917..b40ec02 100644 Binary files a/app/extensions/tim_prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ diff --git a/app/extensions/tim_prompts/__pycache__/fraseologia.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/fraseologia.cpython-313.pyc index 7e2bf2c..487c5c8 100644 Binary files a/app/extensions/tim_prompts/__pycache__/fraseologia.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/fraseologia.cpython-313.pyc differ diff --git a/app/extensions/tim_prompts/__pycache__/out_of_scope.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/out_of_scope.cpython-313.pyc index 2c2ad19..b8c4c56 100644 Binary files a/app/extensions/tim_prompts/__pycache__/out_of_scope.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/out_of_scope.cpython-313.pyc differ diff --git a/app/extensions/tim_prompts/__pycache__/revprec.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/revprec.cpython-313.pyc index 8755d2d..2ab3a23 100644 Binary files a/app/extensions/tim_prompts/__pycache__/revprec.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/revprec.cpython-313.pyc differ diff --git a/app/extensions/tim_prompts/__pycache__/rqlt.cpython-313.pyc b/app/extensions/tim_prompts/__pycache__/rqlt.cpython-313.pyc index 3f69ac3..df2f5a3 100644 Binary files a/app/extensions/tim_prompts/__pycache__/rqlt.cpython-313.pyc and b/app/extensions/tim_prompts/__pycache__/rqlt.cpython-313.pyc differ diff --git a/app/observability/__pycache__/__init__.cpython-313.pyc b/app/observability/__pycache__/__init__.cpython-313.pyc index 0e2017d..4195e82 100644 Binary files a/app/observability/__pycache__/__init__.cpython-313.pyc 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 index decbfe3..af53bdb 100644 Binary files a/app/observability/__pycache__/telemetry_observer.cpython-313.pyc and b/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/app/presentation/__pycache__/__init__.cpython-313.pyc b/app/presentation/__pycache__/__init__.cpython-313.pyc index 5783322..f9fe477 100644 Binary files a/app/presentation/__pycache__/__init__.cpython-313.pyc 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 index 1031a41..8e103a4 100644 Binary files a/app/presentation/__pycache__/tool_renderers.cpython-313.pyc and b/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/app/workflows/__pycache__/agent_graph.cpython-313.pyc index 0ff8cec..9f99f56 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/judges.yaml b/config/judges.yaml index 158aa15..26faf83 100644 --- a/config/judges.yaml +++ b/config/judges.yaml @@ -4,5 +4,5 @@ profile: judge judges: - {name: tim_response_quality, type: external, class: app.extensions.tim_judges:TimResponseQualityJudge, enabled: true, threshold: 0.70} - {name: tim_groundedness, type: external, class: app.extensions.tim_judges:TimGroundednessJudge, enabled: true, threshold: 0.60} -sample_rate: 0.1 +sample_rate: 1.0 always_run_for_transactional: true 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 index 51d5c72..d3444ce 100644 Binary files a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc index 8a21abb..73e4fbb 100644 Binary files a/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_adversarial_parity.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 index 27e9ab2..4158e8a 100644 Binary files a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc 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 index b766104..7ba6df0 100644 Binary files a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc 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_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc index e79b2cb..528abd7 100644 Binary files a/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc index 8c67b7c..53cb8aa 100644 Binary files a/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc index 8b9d60e..4c4277c 100644 Binary files a/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc index 65a290e..5bbdc5f 100644 Binary files a/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contas_conversation_policy_residuals.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_conversation_policy_residuals.cpython-313-pytest-9.0.2.pyc index 6e47fb8..3766976 100644 Binary files a/tests/migration/__pycache__/test_contas_conversation_policy_residuals.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_conversation_policy_residuals.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 index 22dbfe8..7190ad0 100644 Binary files a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc index 2ffa976..1b3b7ce 100644 Binary files a/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_invoice_explanation_routing.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 index eeb9101..11d70d5 100644 Binary files a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc 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 index bf29757..8e6f510 100644 Binary files a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc index 1343d3f..7efaf62 100644 Binary files a/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc index ded15b2..63febc4 100644 Binary files a/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc index c88fc03..425be7a 100644 Binary files a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc index 0aa7add..90c2b85 100644 Binary files a/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc index 00b4b85..05390b5 100644 Binary files a/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc index e22bcc1..d8e491c 100644 Binary files a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc index 4f7f1b0..c15b13e 100644 Binary files a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc index 4bb77df..e1d9018 100644 Binary files a/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc index 1626216..15c98cf 100644 Binary files a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_llm_composition_directive.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 index 17bfba7..ea07e52 100644 Binary files a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc index 532dbed..8553453 100644 Binary files a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc index 93482be..ac07629 100644 Binary files a/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc index 4f4810c..9a74928 100644 Binary files a/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc index c518b19..8f31391 100644 Binary files a/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_llm_client_lifecycle.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_llm_client_lifecycle.cpython-313-pytest-9.0.2.pyc index 0055499..173c4df 100644 Binary files a/tests/migration/__pycache__/test_guardrail_llm_client_lifecycle.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_llm_client_lifecycle.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc index 984cc30..518bd3b 100644 Binary files a/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_runtime_stability.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_runtime_stability.cpython-313-pytest-9.0.2.pyc index 13b89e4..00a53d4 100644 Binary files a/tests/migration/__pycache__/test_guardrail_runtime_stability.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_runtime_stability.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc index 7a5ea63..1f8f69c 100644 Binary files a/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_idempotency_resilience.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_idempotency_resilience.cpython-313-pytest-9.0.2.pyc index f33edc4..d97f261 100644 Binary files a/tests/migration/__pycache__/test_idempotency_resilience.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_idempotency_resilience.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc index 0afa28b..244a39d 100644 Binary files a/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc index 2ddb92c..6b4a63a 100644 Binary files a/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc index 7b3a2f7..1379924 100644 Binary files a/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc index 30de06c..cb1303f 100644 Binary files a/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc index b96fa75..8f15373 100644 Binary files a/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_judge_context_compaction.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_judge_context_compaction.cpython-313-pytest-9.0.2.pyc index 0c20b92..7ff40e9 100644 Binary files a/tests/migration/__pycache__/test_judge_context_compaction.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_judge_context_compaction.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc index c3dab93..b1cbdb1 100644 Binary files a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_line_policy_alternatives.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 index ad21f2b..d85836e 100644 Binary files a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc 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_multiple_vas_subject_resolution.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_multiple_vas_subject_resolution.cpython-313-pytest-9.0.2.pyc index 33a9b60..1644e5f 100644 Binary files a/tests/migration/__pycache__/test_multiple_vas_subject_resolution.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_multiple_vas_subject_resolution.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc index 7f3fe1d..6b064d6 100644 Binary files a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_no_match_retry_deterministic_clarification.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_no_match_retry_deterministic_clarification.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..775abd4 Binary files /dev/null and b/tests/migration/__pycache__/test_no_match_retry_deterministic_clarification.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc index 7a7d01e..b9aa44a 100644 Binary files a/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc index 6c048e6..e7f7b10 100644 Binary files a/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc index 7ad7e92..60bbef8 100644 Binary files a/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc index 5e0d82d..15401f8 100644 Binary files a/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_operational_reset_active_transaction_precedence.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_operational_reset_active_transaction_precedence.cpython-313-pytest-9.0.2.pyc index f3278e4..c9efd5f 100644 Binary files a/tests/migration/__pycache__/test_operational_reset_active_transaction_precedence.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_operational_reset_active_transaction_precedence.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc index 6d13262..2cb1695 100644 Binary files a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_compliance_anatel.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 index 17f3da4..330bb75 100644 Binary files a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc 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_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc index 57bc99b..505c1f3 100644 Binary files a/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc index 67a604b..cec548a 100644 Binary files a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc index 0170f18..9e1294e 100644 Binary files a/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_variation.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 index 585f22c..a3b93be 100644 Binary files a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc 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_original_workflow_cases.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc index 2f30065..262b0d4 100644 Binary files a/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc index 033884d..9b96eeb 100644 Binary files a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc index 23596ce..10311ff 100644 Binary files a/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc index 04ddc9e..c4ef963 100644 Binary files a/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc index 4494940..3023a8c 100644 Binary files a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_regression_03_05_06_17_20_22_29_stability.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_regression_03_05_06_17_20_22_29_stability.cpython-313-pytest-9.0.2.pyc index 177994b..dde8a5b 100644 Binary files a/tests/migration/__pycache__/test_regression_03_05_06_17_20_22_29_stability.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_regression_03_05_06_17_20_22_29_stability.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc index 2e6e762..11c65b9 100644 Binary files a/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc index b03c9c9..61fa5a4 100644 Binary files a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc index bda24b5..5d7e6dd 100644 Binary files a/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc index 78cd7a1..35e1b2d 100644 Binary files a/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_subject_entity_resolution_all_domains.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_subject_entity_resolution_all_domains.cpython-313-pytest-9.0.2.pyc index 7441458..9181903 100644 Binary files a/tests/migration/__pycache__/test_subject_entity_resolution_all_domains.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_subject_entity_resolution_all_domains.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc index fda0622..a499a4a 100644 Binary files a/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc index 8bed1a6..8d86a2a 100644 Binary files a/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc index fd35492..8a1e5da 100644 Binary files a/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc index b8f8089..b91757b 100644 Binary files a/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc index 6e42081..6ac10d0 100644 Binary files a/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc index 46a9b84..5247e6c 100644 Binary files a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.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 index d4852eb..ebd7a8c 100644 Binary files a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc 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_transaction_state_and_vas_prevalidated_subject.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_transaction_state_and_vas_prevalidated_subject.cpython-313-pytest-9.0.2.pyc index 4195764..8e11d85 100644 Binary files a/tests/migration/__pycache__/test_transaction_state_and_vas_prevalidated_subject.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_transaction_state_and_vas_prevalidated_subject.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc index 2a75bdb..3946402 100644 Binary files a/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vaa_dispute_parity.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 index eb822d7..f98b08f 100644 Binary files a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_vloop_transaction_confirmation_bypass.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vloop_transaction_confirmation_bypass.cpython-313-pytest-9.0.2.pyc index 9c755f1..c5e4857 100644 Binary files a/tests/migration/__pycache__/test_vloop_transaction_confirmation_bypass.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vloop_transaction_confirmation_bypass.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_workflow_execution_id_transaction_scope.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_workflow_execution_id_transaction_scope.cpython-313-pytest-9.0.2.pyc index 64223a1..b28caa1 100644 Binary files a/tests/migration/__pycache__/test_workflow_execution_id_transaction_scope.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_workflow_execution_id_transaction_scope.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc index e131f1a..c75fdff 100644 Binary files a/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc index a0abce7..6570507 100644 Binary files a/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/test_no_match_retry_deterministic_clarification.py b/tests/migration/test_no_match_retry_deterministic_clarification.py new file mode 100644 index 0000000..9a76391 --- /dev/null +++ b/tests/migration/test_no_match_retry_deterministic_clarification.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +import pytest + +from app.agents.suporte_contas_agent import SuporteContasAgent + + +class MustNotRunLLM: + async def ainvoke(self, *args, **kwargs): + raise AssertionError("no_match_retry já é decisão de controle e não deve ser reinterpretado") + + +@pytest.mark.asyncio +async def test_no_match_retry_returns_safe_clarification_without_generative_reinterpretation(): + agent = SuporteContasAgent(MustNotRunLLM(), tool_router=None) + state = { + "intent": "contas_no_match_retry", + "user_text": "bota dois planos um em cima do outro pra cá", + "sanitized_input": "bota dois planos um em cima do outro pra cá", + "mcp_tools": [], + "context": {}, + } + result = await agent.run(state) + assert result["answer"] == "Desculpe, não entendi. Poderia repetir de outra forma?" diff --git a/tests/migration/test_revprec_epistemic_uncertainty.py b/tests/migration/test_revprec_epistemic_uncertainty.py index 794ad40..9e8d052 100644 --- a/tests/migration/test_revprec_epistemic_uncertainty.py +++ b/tests/migration/test_revprec_epistemic_uncertainty.py @@ -177,3 +177,61 @@ async def test_revprec_does_not_use_state_or_history_as_authoritative_exact_mess } out = await TimPrematureActionRail().evaluate(text, ctx) assert out.allowed is False + +@pytest.mark.asyncio +async def test_revprec_allows_plain_invoice_explanation_without_llm(): + text = "[FaturasAgent] Analisando a sua fatura atual, a variação foi de R$ 46,99 com Tamboro Mensal de R$ 14,99." + + class MustNotRunLLM: + async def ainvoke(self, *args, **kwargs): + raise AssertionError("explicação informacional não deve ser julgada como execução prematura") + + out = await TimPrematureActionRail().evaluate( + text, + {"guardrail_llm": MustNotRunLLM(), "mcp_results": []}, + ) + assert out.allowed is True + assert out.reason == "no_operational_completion_claim" + assert out.metadata["mechanism"] == "deterministic_claim_scope" + + +@pytest.mark.asyncio +async def test_revprec_allows_composed_contestation_when_current_execution_structurally_proves_claims(): + text = ( + "Sua contestação do valor R$ 14,99 referente ao Tamboro Mensal foi registrada com sucesso. " + "O protocolo aberto é 1234567890. Um novo boleto será emitido e você recebeu um SMS." + ) + + class MustNotRunLLM: + async def ainvoke(self, *args, **kwargs): + raise AssertionError("evidência estrutural suficiente não deve ser re-julgada probabilisticamente") + + ctx = { + "guardrail_llm": MustNotRunLLM(), + "mcp_results": [ + { + "tool_name": "qualquer_tool_transacional", + "ok": True, + "result": { + "status": "COMPLETED", + "output": { + "registrar": { + "success": True, + "contestation_registered": True, + "contested_invoice_amount": "14.99", + "sr": "1234567890", + "sms_sent": True, + "barcode": "34191.79001 01043.510047", + "items": [ + {"itemName": "Tamboro Mensal", "message": "Contestação criada com sucesso"} + ], + } + }, + }, + } + ], + } + out = await TimPrematureActionRail().evaluate(text, ctx) + assert out.allowed is True + assert out.reason == "current_execution_structurally_grounded" + assert out.metadata["mechanism"] == "deterministic_structured_execution_evidence"