mirror of
https://github.com/usestrix/strix.git
synced 2026-08-25 04:12:37 +02:00
The `agent_browser` skill is always loaded, so its safety paragraph shipped to `off`-mode agents. Its prohibitions do not hold there — Strix only assigns a browser session in a safety mode, while multi-session browsing is a normal documented workflow — so the paragraph misdescribed the tools those agents have. Move it into the already mode-gated block in the system prompt, and pin the gating in both directions. Test changes: - `test_observe_mode_blocks_browser_click` asserted nothing about observe mode. The same call blocks in guarded for a different reason (no prior snapshot), so the observe rule was never reached. Give it a snapshot and assert the block's source and category, plus the passive-read inverse. - Neither workspace-epoch bump was pinned; removing either left the suite green. Both are now covered, along with the read-only case that must not bump, and an end-to-end pairing where a patch during review invalidates a script decision. - Cover `invoke_mutating_tool`'s observe-block and off-mode paths, the reviewer's low-confidence, block, missing-model and failed-inspection rules, the inline `bash -c` source path, and the two dependency-budget guards. - Assert browser sessions are disjoint across agents rather than freezing one agent's command string. - Fold the compound-separator and safety-config tests into the parametrized cases that already covered them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
"""Action-safety guidance reaches the agent only when a safety mode is active."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from strix.agents.prompt import render_system_prompt
|
|
|
|
|
|
# Phrased as prohibitions, so they misdescribe the tools an `off`-mode agent actually has.
|
|
_SAFETY_ONLY_PHRASES = [
|
|
"ACTION SAFETY POLICY",
|
|
"do not override",
|
|
"blocked as stale",
|
|
"must be split into a creation call",
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("phrase", _SAFETY_ONLY_PHRASES)
|
|
@pytest.mark.parametrize("context", [None, {}, {"safety_mode": "off"}])
|
|
def test_safety_guidance_is_absent_without_a_safety_mode(
|
|
phrase: str,
|
|
context: dict[str, str] | None,
|
|
) -> None:
|
|
assert phrase not in render_system_prompt(system_prompt_context=context)
|
|
|
|
|
|
@pytest.mark.parametrize("phrase", _SAFETY_ONLY_PHRASES)
|
|
@pytest.mark.parametrize("mode", ["guarded", "observe"])
|
|
def test_safety_guidance_is_present_in_a_safety_mode(phrase: str, mode: str) -> None:
|
|
assert phrase in render_system_prompt(system_prompt_context={"safety_mode": mode})
|
|
|
|
|
|
def test_browser_skill_carries_no_safety_prohibitions() -> None:
|
|
"""The browser skill is always loaded, so mode-specific rules do not belong in it."""
|
|
prompt = render_system_prompt(skills=["agent_browser"], system_prompt_context={})
|
|
|
|
assert "agent-browser snapshot" in prompt
|
|
for phrase in _SAFETY_ONLY_PHRASES:
|
|
assert phrase not in prompt
|
|
|
|
|
|
def test_observe_mode_states_its_passive_only_contract() -> None:
|
|
prompt = render_system_prompt(system_prompt_context={"safety_mode": "observe"})
|
|
|
|
assert "passive target interaction only" in prompt
|
|
assert "Guarded mode permits" not in prompt
|