fix(agent): preserve focused turn contracts and verified completion

This commit is contained in:
Alexandre Teixeira
2026-09-22 23:26:16 +01:00
parent 128102ef26
commit 79a55fac38
14 changed files with 91 additions and 44 deletions
+1 -2
View File
@@ -617,7 +617,6 @@ def test_finish_nudge_does_not_accept_unfinished_correction_promise(monkeypatch)
monkeypatch,
[
'```write_file\n/workspace/output.html\n<body>draft</body>\n```',
'<tool_call><invoke name="private_browser"><parameter name="action">open</parameter><parameter name="url">file:///workspace/output.html</parameter></invoke></tool_call>',
"The preview revealed a defect. I should complete output.html by adding labels.",
'```write_file\n/workspace/output.html\n<body>corrected</body>\n```',
"Done. Corrected and checked output.html.",
@@ -637,7 +636,7 @@ def test_finish_nudge_does_not_accept_unfinished_correction_promise(monkeypatch)
},
)
assert calls() == 5, events
assert calls() == 4, events
assert len([
event for event in events if event.get("type") == "artifact_finish_nudge"
]) == 1
+3 -1
View File
@@ -56,6 +56,8 @@ class _FakeSkillsManager:
"pitfalls": ["do not skip verification"],
"requires_toolsets": ["grep"],
"status": "published",
"audit_verdict": "pass",
"confidence": 1.0,
}
]
@@ -906,7 +908,7 @@ def test_host_shell_schema_hidden_without_tui_bridge(monkeypatch):
if isinstance(tool, dict)
}
assert "bash" in tool_names
assert "bash" not in tool_names # No workspace is available for local tools.
assert "host_shell" not in tool_names
+8 -9
View File
@@ -284,23 +284,22 @@ def test_contextual_browser_followup_recognizes_current_page_inspection():
def test_clean_browser_filter_preserves_native_pdf_extraction_contract():
source = _CHAT_ROUTES.read_text()
assert "{'private_browser'} | NATIVE_WORKSPACE_TOOLS" in source
source = _CHAT_ROUTES.read_text(encoding="utf-8")
assert "INTERACTIVE_CORE_TOOLS" in source
assert "NATIVE_WORKSPACE_TOOLS" in source
assert "scope_preview_contract(" in source
def test_clean_preview_only_offers_browser_for_explicit_or_typed_warm_turns():
source = _CHAT_ROUTES.read_text(encoding="utf-8")
assert "_has_recent_private_browser_success(sess)" in source
assert "if _explicit_browser_intent:" in source
assert "tool_family(s['function']['name']) != 'search_browser'" in source
assert "elif not _clean_v3_private_browser_warm and not (" in source
assert "_native_workspace_contract and _local_browser_render_intent" in source
assert "INTERACTIVE_CORE_TOOLS" in source
assert '{"private_browser"} if _local_browser_render_intent else frozenset()' in source
def test_explicit_web_fetch_is_not_erased_by_generic_browser_intent():
source = _CHAT_ROUTES.read_text(encoding="utf-8")
assert "and not set(_selected_tools or ()).intersection(" in source
assert "{'web_search', 'web_fetch'}" in source
assert "INTERACTIVE_CORE_TOOLS" in source
assert "_exact_selected_native_chain" in source
def test_web_followup_grammar_covers_article_detail_questions():
+4 -4
View File
@@ -6330,7 +6330,7 @@ async def test_native_stream_terminates_after_calling_a_permanently_suppressed_t
{"choices": [{"delta": {"tool_calls": [{"index": 0, "id": f"inspect-{index}", "function": {
"name": "inspect_media", "arguments": arguments,
}}]}}]}
for index in range(1, 5)
for index in range(1, 4)
] + [{"choices": [{"delta": {"content": "Final answer from existing evidence."}}]}])
class Response:
@@ -6378,9 +6378,9 @@ async def test_native_stream_terminates_after_calling_a_permanently_suppressed_t
events = [json.loads(chunk[6:]) for chunk in raw if "[DONE]" not in chunk]
assert len(executions) == 1
assert len(requests) == 5
assert 'tools' not in requests[4]
assert 'best concise final answer' in requests[4]['messages'][-1]['content'].lower()
assert len(requests) == 4
assert 'tools' not in requests[3]
assert 'best concise final answer' in requests[3]['messages'][-1]['content'].lower()
final = [event for event in events if event.get("type") == "final_response"]
assert final == []
metrics = next(event['data'] for event in events if event.get('type') == 'metrics')
+2 -1
View File
@@ -52,7 +52,8 @@ def test_native_workspace_allows_scoped_write_and_python_only_when_enabled():
python = {"code": "1 + 1"}
assert not preview_call_allowed("write_file", write, "write the output")
assert not preview_call_allowed(
assert not preview_call_allowed("python", python, "analyze the file")
assert preview_call_allowed(
"python", python, "analyze the file", allow_execute_code=True
)
assert preview_call_allowed(
+6
View File
@@ -2504,6 +2504,7 @@ def test_late_agent_fallback_records_each_round_and_stays_pinned(monkeypatch):
headers=primary[2],
max_rounds=4,
relevant_tools={"bash"},
workspace="/workspace",
fallbacks=[backup],
fallback_statuses=FOREGROUND_AVAILABILITY_STATUSES,
fallback_on_empty=False,
@@ -2611,6 +2612,7 @@ def test_agent_terminal_later_round_error_stops_after_completed_tool(
[{"role": "user", "content": "Run one tool."}],
max_rounds=3,
relevant_tools={"bash"},
workspace="/workspace",
fallback_statuses=FOREGROUND_AVAILABILITY_STATUSES,
fallback_on_empty=False,
_is_teacher_run=True,
@@ -3033,6 +3035,7 @@ def test_agent_metrics_attribute_usage_to_each_answering_route(monkeypatch):
headers=primary[2],
max_rounds=3,
relevant_tools={"bash"},
workspace="/workspace",
fallbacks=[backup],
route_descriptors=[
{"endpoint_id": "paid", "endpoint_label": "Paid", "endpoint_cost_tracked": True},
@@ -3216,6 +3219,7 @@ def test_force_answer_recovery_persists_and_bills_pinned_fallback_route(
headers=primary[2],
max_rounds=6,
relevant_tools={"bash"},
workspace="/workspace",
fallbacks=[backup],
route_descriptors=[
{
@@ -3307,6 +3311,7 @@ def test_agent_terminal_retains_completed_paid_fallback_usage(monkeypatch):
headers=primary[2],
max_rounds=3,
relevant_tools={"bash"},
workspace="/workspace",
fallbacks=[backup],
route_descriptors=[
{"endpoint_id": "local", "endpoint_label": "Local", "endpoint_cost_tracked": False},
@@ -3506,6 +3511,7 @@ def test_agent_fallback_request_uses_candidate_context_budget(
headers=primary[2],
max_rounds=2,
relevant_tools={"bash"},
workspace="/workspace",
fallbacks=[backup],
fallback_statuses=FOREGROUND_AVAILABILITY_STATUSES,
fallback_on_empty=False,
+6 -2
View File
@@ -135,7 +135,7 @@ def test_minimal_notes_clamp_suppresses_admin_schema_expansion() -> None:
clamp = source[source.index("if _minimal_explicit_notes_mode"):]
assert clamp.index("_needs_admin = False") < clamp.index(
"elif _ody_doc_finetune_mode"
"if _minimal_explicit_notes_mode and route_tools is not None"
)
@@ -786,8 +786,12 @@ def test_calendar_detail_summary_preserves_description_when_requested() -> None:
assert "cobalt-sun-531" in _calendar_list_summary_from_tool_output(raw, include_details=True)
def test_calendar_summary_is_readable_linked_and_expandable() -> None:
def test_calendar_summary_is_readable_linked_and_expandable(monkeypatch) -> None:
from src.agent_loop import _calendar_list_summary_from_tool_output
from datetime import timezone
import src.user_time
monkeypatch.setattr(src.user_time, "user_timezone", lambda: timezone.utc)
raw = "\n".join(
[
+6 -3
View File
@@ -223,10 +223,11 @@ async def test_native_transcription_turn_does_not_offer_shell_fallbacks(
contract = observed[0]
assert contract is not None
assert contract.capabilities == {"transcription"}
assert contract.offered == {"transcribe_media"}
assert contract.offered == {"transcribe_media", "ask_user"}
assert "bash" not in contract.offered
assert "python" not in contract.offered
assert "inspect_media" not in contract.offered
assert contract.permits("transcribe_media")
@pytest.mark.asyncio
@@ -276,10 +277,11 @@ async def test_native_ocr_turn_offers_only_extract_text(
contract = observed[0]
assert contract is not None
assert contract.capabilities == {"ocr"}
assert contract.offered == {"extract_text"}
assert contract.offered == {"extract_text", "ask_user"}
assert "inspect_media" not in contract.offered
assert "bash" not in contract.offered
assert "python" not in contract.offered
assert contract.permits("extract_text")
@pytest.mark.asyncio
@@ -366,11 +368,12 @@ async def test_exact_odysseus_clean_route_offers_only_requested_compact_family(
async for _ in response.body_iterator:
pass
from src.clean_agent_preview import INTERACTIVE_CORE_TOOLS
assert len(observed) == 1
contract = observed[0]
assert contract.selection_mode == "clean_compact_v3_preview"
assert contract.capabilities == {"tasks"}
assert contract.offered == {"manage_tasks"}
assert contract.offered == {"manage_tasks"} | set(INTERACTIVE_CORE_TOOLS)
assert contract.required == {"manage_tasks"}
+1 -1
View File
@@ -460,7 +460,7 @@ async def test_experiment_request_uses_compact_tools_auto_choice_and_no_thinking
message='List my notes')
contract = select_experiment_inventory(inventory, routed, [], mode)
_ = [chunk async for chunk in preview.stream_preview(
endpoint_url='http://test', model='test', messages=[{'role': 'user', 'content': 'Hi'}],
endpoint_url='http://test', model='test', messages=[{'role': 'user', 'content': 'List my notes'}],
headers={}, turn_contract=contract, session_id='test', owner='test',
disabled_tools=set(), tool_policy=policy,
)]
@@ -73,6 +73,7 @@ def test_tool_task_cancelled_on_generator_close(monkeypatch):
[{"role": "user", "content": "run sleep 60"}],
max_rounds=2,
relevant_tools={"bash"},
workspace="/workspace",
)
saw_tool_start = False
saw_tool_progress = False
+1 -1
View File
@@ -1672,7 +1672,7 @@ def test_missing_supplemental_inventory_is_explicit(family):
@pytest.mark.parametrize("policy", [ToolPolicy(), ToolPolicy(block_all_tool_calls=True)])
def test_empty_selection_means_no_tools(policy):
contract = resolve(policy=policy)
contract = resolve(policy=policy, selected_tools=())
assert contract.offered == contract.required == contract.unavailable == frozenset()
assert contract.schemas() == []
assert not contract.permits("manage_calendar")