feat(runner): let root_instructions_override replace the root's scan prompt (#1592)

This commit is contained in:
ian-at-strix 2026-10-26 15:49:51 -04:00 • committed by GitHub
parent 4ac5dc65e4
commit d6f4bc5ced
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 38 additions and 0 deletions

View file

@ -181,9 +181,12 @@ def _compose_root_instructions_override(
supports_images: bool,
report_tool: str,
coverage_tools: bool,
replace_scan_prompt: bool,
) -> str | None:
if root_instructions_override is None:
return None
if replace_scan_prompt:
return f"{root_instructions_override}\n\n{render_scope_prompt(system_prompt_context)}"
base_instructions = render_system_prompt(
skills=skills,
@ -225,6 +228,7 @@ async def run_strix_scan(
cleanup_on_exit: bool = True,
event_sink: StreamEventSink | None = None,
root_instructions_override: str | None = None,
replace_root_scan_prompt: bool = False,
extra_system_prompt_context: dict[str, Any] | None = None,
status_sink: StatusSink | None = None,
mcp_connection_requests: list[McpConnectionRequest] | None = None,
@ -238,6 +242,8 @@ async def run_strix_scan(
``root_instructions_override`` adds root scan instructions to the rendered
root prompt without replacing the system-verified scope block.
``replace_root_scan_prompt=True`` makes it the root's whole prompt, followed
by the scope, instead of adding it to the rendered scan prompt.
``extra_files`` entries (``{"workspace_path", "content"}``) are placed into
the sandbox workspace at session bring-up; see
:func:`strix.runtime.session_manager.create_or_reuse`.
@ -521,6 +527,7 @@ async def run_strix_scan(
supports_images=supports_images,
report_tool=report_tool.name,
coverage_tools=coverage_tools,
replace_scan_prompt=replace_root_scan_prompt,
)
root_agent = build_strix_agent(

View file

@ -68,6 +68,7 @@ def _patch_engine_scaffold(
extra_body=None,
),
runtime=types.SimpleNamespace(max_context_images=3),
telemetry=types.SimpleNamespace(run_record_timings=False),
)
monkeypatch.setattr(runner, "load_settings", lambda: settings)
monkeypatch.setattr(runner, "configure_sdk_model_defaults", lambda _settings: None)
@ -161,6 +162,36 @@ async def test_root_prompt_options_flow_into_root_agent(
}
@pytest.mark.asyncio
async def test_replace_root_scan_prompt_drops_the_scan_prompt(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Any,
) -> None:
scope_context = {
"scope_source": "user_scan_config",
"authorized_targets": [
{"type": "web_application", "value": "https://example.com", "workspace_path": ""},
],
}
captured = _patch_engine_scaffold(monkeypatch, tmp_path, scope_context)
await runner.run_strix_scan(
scan_config={"targets": [], "scan_mode": "deep"},
scan_id="scan-replace",
image="img",
coordinator=AgentCoordinator(),
root_instructions_override="CUSTOM SCAN PROMPT",
replace_root_scan_prompt=True,
)
instructions_override = captured["kwargs"]["instructions_override"]
assert instructions_override.startswith("CUSTOM SCAN PROMPT")
assert instructions_override.count("SCOPE:") == 1
assert "https://example.com" in instructions_override
assert "<root_scan_instructions_override>" not in instructions_override
assert "application security validation agent" not in instructions_override
@pytest.mark.asyncio
async def test_extra_system_prompt_context_cannot_override_scope_context(
monkeypatch: pytest.MonkeyPatch,