From 53464e95e835675a16552c6a30fde0105c3bfb5a Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Tue, 27 Oct 2026 23:24:24 +0530 Subject: [PATCH 1/2] test(harness): pin the actionable error for unrepairable tool args With ArgRecoveryMiddleware installed, an unrepairable provider-invalid call must still reach admission with its raw string and be answered with the "not valid JSON ... Re-send it as well-formed JSON" corrective. --- .../src/agent_loop/mod_tests.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/tinyagents-harness/src/agent_loop/mod_tests.rs b/crates/tinyagents-harness/src/agent_loop/mod_tests.rs index d8dc41532..6b30ed90d 100644 --- a/crates/tinyagents-harness/src/agent_loop/mod_tests.rs +++ b/crates/tinyagents-harness/src/agent_loop/mod_tests.rs @@ -3763,6 +3763,47 @@ async fn unrepairable_arguments_are_answered_with_an_actionable_error() { ); } +/// With the host's `ArgRecoveryMiddleware` installed, an unrepairable call +/// still reaches admission with its raw string and is answered with the +/// actionable corrective, word for word: the parse failure plus the re-send +/// instruction the model can follow. +#[tokio::test] +async fn arg_recovery_keeps_the_actionable_error_text_for_unrepairable_arguments() { + let tool = Arc::new(crate::testkit::FakeTool::returning("lookup", "found it")); + let mut harness: AgentHarness<()> = AgentHarness::new(); + harness.with_model_resolver(Arc::new(crate::host::FixedModelResolver::named( + "mock", + Arc::new(MockModel::with_responses(vec![ + invalid_tool_call_response("call-x", "lookup", "{\"query\": [1 2]}"), + text_response("gave up", 1, 1), + ])), + ))); + harness.register_tool(tool.clone()); + harness.push_middleware(Arc::new(crate::middleware::ArgRecoveryMiddleware::new( + Vec::new(), + ))); + + let run = harness + .invoke_in_context( + &(), + RunContext::new(RunConfig::new("arg-recovery-unrepairable"), ()), + vec![Message::user("lookup")], + ) + .await + .expect("unparseable arguments do not fail the run"); + + assert!(tool.calls().is_empty(), "the tool must not run"); + let transcript = format!("{:?}", run.messages); + assert!( + transcript.contains("Tool call `lookup` was not run: its arguments are not valid JSON"), + "{transcript}" + ); + assert!( + transcript.contains("Re-send it as well-formed JSON that matches the tool's argument schema"), + "{transcript}" + ); +} + #[tokio::test] async fn structured_output_is_extracted() { let mut harness: AgentHarness<()> = AgentHarness::new(); From f8bdfcc09f14e85a73c4322f3a6aca2552f26cc3 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Tue, 27 Oct 2026 23:36:09 +0530 Subject: [PATCH 2/2] test(harness): apply rustfmt and scope the arg-recovery test doc --- .../tinyagents-harness/src/agent_loop/mod_tests.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/tinyagents-harness/src/agent_loop/mod_tests.rs b/crates/tinyagents-harness/src/agent_loop/mod_tests.rs index 6b30ed90d..35641cb86 100644 --- a/crates/tinyagents-harness/src/agent_loop/mod_tests.rs +++ b/crates/tinyagents-harness/src/agent_loop/mod_tests.rs @@ -3763,10 +3763,11 @@ async fn unrepairable_arguments_are_answered_with_an_actionable_error() { ); } -/// With the host's `ArgRecoveryMiddleware` installed, an unrepairable call -/// still reaches admission with its raw string and is answered with the -/// actionable corrective, word for word: the parse failure plus the re-send -/// instruction the model can follow. +/// With the host's `ArgRecoveryMiddleware` installed (as the host stack runs +/// it), an unrepairable call is not executed and is answered with the +/// actionable corrective word for word: the parse failure plus the re-send +/// instruction the model can follow. Pins the wording of +/// `unparseable_tool_call_message`, not which layer produces it. #[tokio::test] async fn arg_recovery_keeps_the_actionable_error_text_for_unrepairable_arguments() { let tool = Arc::new(crate::testkit::FakeTool::returning("lookup", "found it")); @@ -3799,7 +3800,8 @@ async fn arg_recovery_keeps_the_actionable_error_text_for_unrepairable_arguments "{transcript}" ); assert!( - transcript.contains("Re-send it as well-formed JSON that matches the tool's argument schema"), + transcript + .contains("Re-send it as well-formed JSON that matches the tool's argument schema"), "{transcript}" ); }