Files
odysseus/tests/test_product_turn_contract_route.py
T

411 lines
15 KiB
Python

"""Reach contract construction through the real registered chat route.
Only session/context/inference boundaries are stubbed. MCP imports, singleton
registration, schema enumeration, and contract/policy resolution stay real.
"""
from copy import deepcopy
import json
import pytest
from src.tool_schemas import FUNCTION_TOOL_SCHEMAS
from src.turn_contract import active_turn_contract
from tests.test_foreground_model_routing import _RouteRequest, _chat_stream_endpoint
@pytest.fixture
def registered_mcp_manager():
from src.mcp_manager import McpManager
from src.tool_utils import get_mcp_manager, set_mcp_manager
previous = get_mcp_manager()
manager = McpManager()
# Seed only discovered metadata. The real manager never connects to or
# executes an external service in these tests.
manager._tools["email"] = [
{"name": function["name"], "description": function["description"],
"input_schema": deepcopy(function["parameters"])}
for schema in FUNCTION_TOOL_SCHEMAS
if (function := schema["function"])["name"] in {"list_email_accounts", "list_emails", "send_email"}
]
set_mcp_manager(manager)
try:
assert get_mcp_manager() is manager
yield manager
finally:
set_mcp_manager(previous)
@pytest.mark.asyncio
@pytest.mark.parametrize("case", ["calendar", "email", "plan", "denied", "no_manager",
"discovery", "identity", "discovery_denied", "mixed", "discovery_reader"])
async def test_actual_route_constructs_contract_with_real_mcp_apis(
monkeypatch, registered_mcp_manager, case,
):
from routes import chat_routes
from src import settings, tool_security
from src.tool_utils import set_mcp_manager
endpoint = _chat_stream_endpoint(monkeypatch, "agent", {})
message = "List my calendar events" if case == "calendar" else "Check my inbox"
if case.startswith("discovery"):
message = "List my email accounts"
elif case == "identity":
message = "What's my email address?"
elif case == "mixed":
message = "List my email accounts and send an email"
monkeypatch.setattr(chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"))
# Identity is independent of the regression under test; retain the actual
# owner-policy function and both route/dispatcher permission mechanisms.
monkeypatch.setattr(tool_security, "owner_is_admin_or_single_user", lambda owner: True)
if case == "no_manager":
set_mcp_manager(None)
if case == "denied":
monkeypatch.setattr(settings, "get_setting", lambda key, default=None:
["send_email"] if key == "disabled_tools" else default)
if case == "discovery_denied":
monkeypatch.setattr(settings, "get_setting", lambda key, default=None:
["list_email_accounts"] if key == "disabled_tools" else default)
observed = []
async def capture_agent(*args, **kwargs):
selected = kwargs["turn_contract"]
assert selected is not None
assert active_turn_contract() is selected
observed.append((selected, kwargs["tool_policy"], kwargs["disabled_tools"]))
yield 'data: {"delta":"Route contract constructed."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form["message"] = message
if case == "plan":
request._form["plan_mode"] = "true"
if case == "discovery_reader":
request._form["active_email_uid"] = "test-message"
response = await endpoint(request)
assert response.status_code == 200
chunks = [chunk async for chunk in response.body_iterator]
assert any("Route contract constructed." in chunk for chunk in chunks)
assert any("[DONE]" in chunk for chunk in chunks)
assert len(observed) == 1
selected, policy, disabled = observed[0]
assert "host_shell" not in selected.executable
assert selected.required <= selected.offered <= selected.executable
assert not any(policy.blocks(name) for name in selected.offered)
if case.startswith("discovery") or case == "identity":
assert selected.capabilities == {"email"}
assert selected.required_read_operation is not None
assert selected.required_read_operation.tool.endswith("list_email_accounts")
if case == "discovery_denied":
assert "list_email_accounts" in selected.unavailable
assert selected.offered == set()
else:
assert selected.required == {"mcp__email__list_email_accounts"}
assert selected.offered == {"mcp__email__list_email_accounts", "ask_user", "update_plan"}
for name in ("send_email", "mcp__email__send_email", "list_emails", "mcp__email__list_emails"):
assert not selected.permits(name)
assert policy.blocks(name)
elif case == "calendar":
assert selected.capabilities == {"calendar"}
assert selected.required_read_operation.args == {"action": "list_events"}
assert "manage_calendar" in selected.offered
assert not selected.permits("web_search")
elif case in {"plan", "no_manager"}:
assert not any(name.startswith("mcp__") for name in selected.executable)
assert "list_emails" in selected.offered
else:
# This exact qualified name proves real singleton lookup and the real
# get_all_openai_schemas API ran inside the route's construction block.
assert "mcp__email__list_emails" in selected.offered
assert "list_emails" not in selected.offered
if case == "denied":
assert not selected.permits("send_email")
assert not selected.permits("mcp__email__send_email")
assert "send_email" in disabled
else:
assert selected.permits("send_email")
@pytest.mark.asyncio
async def test_native_product_browser_turn_cannot_bypass_family_contract(
monkeypatch, registered_mcp_manager,
):
"""A desktop runtime marker must not let RAG replace browser with shell."""
from routes import chat_routes
from src import tool_security
endpoint = _chat_stream_endpoint(monkeypatch, "agent", {})
message = "Browse example.com and find the pricing page"
monkeypatch.setattr(
chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"),
)
monkeypatch.setattr(
tool_security, "owner_is_admin_or_single_user", lambda owner: True,
)
observed = []
async def capture_agent(*args, **kwargs):
observed.append(kwargs.get("turn_contract"))
yield 'data: {"delta":"Browser contract constructed."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form.update({
"message": message,
"compare_mode": "false",
"client_runtime_context": json.dumps({
"surface": "odysseus-native",
"terminal_agent": True,
}),
})
response = await endpoint(request)
async for _ in response.body_iterator:
pass
assert len(observed) == 1
contract = observed[0]
assert contract is not None
assert contract.capabilities == {"search_browser"}
assert "private_browser" in contract.offered
assert "bash" not in contract.offered
assert "python" not in contract.offered
@pytest.mark.asyncio
async def test_native_transcription_turn_does_not_offer_shell_fallbacks(
monkeypatch, registered_mcp_manager,
):
from routes import chat_routes
from src import tool_security
endpoint = _chat_stream_endpoint(
monkeypatch, "agent", {},
session_model="odysseus-qwen3.5-tools-pre-heretic",
)
message = "Transcribe the speech in /workspace/jo.wav."
monkeypatch.setattr(
chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"),
)
monkeypatch.setattr(
tool_security, "owner_is_admin_or_single_user", lambda owner: True,
)
observed = []
async def capture_agent(*args, **kwargs):
observed.append(kwargs.get("turn_contract"))
yield 'data: {"delta":"Transcription contract constructed."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form.update({
"message": message,
"cwd": "/tmp/native-workspace",
"workspace": "/tmp/native-workspace",
"client_runtime_context": json.dumps({
"surface": "odysseus-native",
"terminal_agent": True,
"unattended_mode": True,
"input_files": ["/workspace/jo.wav"],
}),
})
response = await endpoint(request)
async for _ in response.body_iterator:
pass
assert len(observed) == 1
contract = observed[0]
assert contract is not None
assert contract.capabilities == {"transcription"}
assert contract.offered == {"transcribe_media"}
assert "bash" not in contract.offered
assert "python" not in contract.offered
assert "inspect_media" not in contract.offered
@pytest.mark.asyncio
async def test_native_ocr_turn_offers_only_extract_text(
monkeypatch, registered_mcp_manager,
):
from routes import chat_routes
from src import tool_security
endpoint = _chat_stream_endpoint(
monkeypatch, "agent", {},
session_model="odysseus-qwen3.5-tools-pre-heretic",
)
message = "Extract the exact visible text from /workspace/receipt.png with OCR."
monkeypatch.setattr(
chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"),
)
monkeypatch.setattr(
tool_security, "owner_is_admin_or_single_user", lambda owner: True,
)
observed = []
async def capture_agent(*args, **kwargs):
observed.append(kwargs.get("turn_contract"))
yield 'data: {"delta":"OCR contract constructed."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form.update({
"message": message,
"cwd": "/tmp/native-workspace",
"workspace": "/tmp/native-workspace",
"client_runtime_context": json.dumps({
"surface": "odysseus-native",
"terminal_agent": True,
"unattended_mode": True,
"input_files": ["/workspace/receipt.png"],
}),
})
response = await endpoint(request)
async for _ in response.body_iterator:
pass
assert len(observed) == 1
contract = observed[0]
assert contract is not None
assert contract.capabilities == {"ocr"}
assert contract.offered == {"extract_text"}
assert "inspect_media" not in contract.offered
assert "bash" not in contract.offered
assert "python" not in contract.offered
@pytest.mark.asyncio
async def test_native_sft_owner_can_use_confined_read_file_tool(
monkeypatch, registered_mcp_manager,
):
from routes import chat_routes
from src import tool_security
endpoint = _chat_stream_endpoint(
monkeypatch, "agent", {},
session_model="odysseus-qwen3.5-tools-pre-heretic",
)
message = "Use read_file to read /workspace/sample.txt. Read only."
monkeypatch.setattr(
chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"),
)
monkeypatch.setattr(
tool_security, "owner_is_admin_or_single_user", lambda owner: True,
)
observed = []
async def capture_agent(*args, **kwargs):
observed.append(kwargs.get("turn_contract"))
yield 'data: {"delta":"Native read contract constructed."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form.update({
"message": message,
"cwd": "/tmp/native-workspace",
"workspace": "/tmp/native-workspace",
"client_runtime_context": json.dumps({
"surface": "odysseus-native",
"terminal_agent": True,
"unattended_mode": True,
"input_files": ["/workspace/sample.txt"],
}),
})
response = await endpoint(request)
async for _ in response.body_iterator:
pass
assert len(observed) == 1
contract = observed[0]
assert contract is not None
assert contract.capabilities == {"shell_files"}
assert "read_file" in contract.offered
assert contract.offered <= contract.executable
@pytest.mark.asyncio
async def test_exact_odysseus_clean_route_offers_only_requested_compact_family(
monkeypatch, registered_mcp_manager,
):
from routes import chat_routes
from src import tool_security
endpoint = _chat_stream_endpoint(
monkeypatch, "agent", {},
session_model="odysseus-qwen3.5-tools-pre-heretic",
)
message = "List my scheduled tasks. Return at most three names and statuses."
monkeypatch.setattr(
chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"),
)
monkeypatch.setattr(
tool_security, "owner_is_admin_or_single_user", lambda owner: True,
)
observed = []
async def capture_agent(*args, **kwargs):
observed.append(kwargs.get("turn_contract"))
yield 'data: {"delta":"Tasks read."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form.update({"message": message, "compare_mode": "false"})
response = await endpoint(request)
async for _ in response.body_iterator:
pass
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.required == {"manage_tasks"}
@pytest.mark.asyncio
async def test_native_terminal_workspace_keeps_environment_execution_contract(
monkeypatch,
):
from routes import chat_routes
endpoint = _chat_stream_endpoint(monkeypatch, "agent", {})
message = "Run pytest in /workspace/project"
monkeypatch.setattr(
chat_routes, "coerce_message_and_session",
lambda *args, **kwargs: (message, "session-1"),
)
observed = []
async def capture_agent(*args, **kwargs):
observed.append(kwargs.get("turn_contract"))
yield 'data: {"delta":"Workspace contract retained."}\n\n'
yield "data: [DONE]\n\n"
monkeypatch.setattr(chat_routes, "stream_agent_loop", capture_agent)
request = _RouteRequest("agent")
request._form.update({
"message": message,
"compare_mode": "false",
"client_runtime_context": json.dumps({
"surface": "odysseus-native",
"terminal_agent": True,
}),
})
response = await endpoint(request)
async for _ in response.body_iterator:
pass
assert observed == [None]