mirror of
https://github.com/usestrix/strix.git
synced 2026-08-17 01:29:42 +02:00
Five rounds of sweep across the tree. Net ~544 lines removed. Removed: - Section-divider banners and one-line section labels (# Display utilities, # ----- list_requests -----, # CVSS breakdown, etc.). - Module-level prose docstrings on internal modules. Kept one-line summaries; trimmed multi-paragraph narration about SDK/Strix responsibility splits, cache strategies, three-source precedence. - Internal-helper docstrings that just restate the function name — caido_api helpers (caido_url, get_client, view_request, etc.), settings-class one-liners (LLMSettings, RuntimeSettings, ...), UI helper docstrings. - Args/Returns blocks on non-LLM-facing internal helpers (build_strix_agent, render_system_prompt, create_or_reuse, bootstrap_caido) — kept only the genuinely non-obvious params. - Internal-history phrasing — "Mirrors main-branch shape", "pre-SDK harness", "previous lookup matched no attribute". - Narrative comments inside function bodies that explained what the next line does, design rationale obvious from the surrounding code, or "we used to..." asides. - Trailing periods on every error-string literal across the tool tree. - Duplicated roundtripTime quirk comment (kept the LLM-facing copy in tools/proxy/tools.py). Kept (every one names an upstream bug, vendored-code provenance, or non-obvious data quirk): - core/runner.py: SDK replay-with-empty-initial-input + on_agent_end lifecycle gap. - runtime/docker_client.py: VERBATIM COPY block of the upstream _create_container body, pinned to SDK v0.14.6. - runtime/session_manager.py: NO_PROXY for agent-browser CDP loopback. - tools/proxy/caido_api.py: generated-pydantic Request.raw quirk, replay double-history pitfall. - tools/proxy/tools.py: Caido roundtripTime=0 quirk for proxy captures. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
160 lines
5.3 KiB
Python
160 lines
5.3 KiB
Python
"""Pure input builders for Strix scan runs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from agents.model_settings import ModelSettings
|
|
from openai.types.shared import Reasoning
|
|
|
|
from strix.config.models import DEFAULT_MODEL_RETRY
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from strix.config.settings import ReasoningEffort
|
|
|
|
|
|
DEFAULT_MAX_TURNS = 500
|
|
|
|
|
|
def build_root_task(scan_config: dict[str, Any]) -> str:
|
|
targets = scan_config.get("targets", []) or []
|
|
diff_scope = scan_config.get("diff_scope") or {}
|
|
user_instructions = scan_config.get("user_instructions", "") or ""
|
|
|
|
sections: dict[str, list[str]] = {
|
|
"Repositories": [],
|
|
"Local Codebases": [],
|
|
"URLs": [],
|
|
"IP Addresses": [],
|
|
}
|
|
|
|
for target in targets:
|
|
ttype = target.get("type")
|
|
details = target.get("details") or {}
|
|
workspace_subdir = details.get("workspace_subdir")
|
|
workspace_path = f"/workspace/{workspace_subdir}" if workspace_subdir else "/workspace"
|
|
|
|
if ttype == "repository":
|
|
url = details.get("target_repo", "")
|
|
cloned = details.get("cloned_repo_path")
|
|
sections["Repositories"].append(
|
|
f"- {url} (available at: {workspace_path})" if cloned else f"- {url}",
|
|
)
|
|
elif ttype == "local_code":
|
|
path = details.get("target_path", "unknown")
|
|
sections["Local Codebases"].append(f"- {path} (available at: {workspace_path})")
|
|
elif ttype == "web_application":
|
|
sections["URLs"].append(f"- {details.get('target_url', '')}")
|
|
elif ttype == "ip_address":
|
|
sections["IP Addresses"].append(f"- {details.get('target_ip', '')}")
|
|
|
|
parts: list[str] = []
|
|
for label, items in sections.items():
|
|
if items:
|
|
parts.append(f"\n\n{label}:")
|
|
parts.extend(items)
|
|
|
|
if diff_scope.get("active"):
|
|
parts.append("\n\nScope Constraints:")
|
|
parts.append(
|
|
"- Pull request diff-scope mode is active. Prioritize changed files "
|
|
"and use other files only for context.",
|
|
)
|
|
for repo_scope in diff_scope.get("repos", []) or []:
|
|
label = (
|
|
repo_scope.get("workspace_subdir") or repo_scope.get("source_path") or "repository"
|
|
)
|
|
changed = repo_scope.get("analyzable_files_count", 0)
|
|
deleted = repo_scope.get("deleted_files_count", 0)
|
|
parts.append(f"- {label}: {changed} changed file(s) in primary scope")
|
|
if deleted:
|
|
parts.append(f"- {label}: {deleted} deleted file(s) are context-only")
|
|
|
|
task = " ".join(parts)
|
|
if user_instructions:
|
|
task = f"{task}\n\nSpecial instructions: {user_instructions}"
|
|
return task
|
|
|
|
|
|
def build_scope_context(scan_config: dict[str, Any]) -> dict[str, Any]:
|
|
authorized: list[dict[str, str]] = []
|
|
value_keys = {
|
|
"repository": "target_repo",
|
|
"local_code": "target_path",
|
|
"web_application": "target_url",
|
|
"ip_address": "target_ip",
|
|
}
|
|
for target in scan_config.get("targets", []) or []:
|
|
ttype = target.get("type", "unknown")
|
|
details = target.get("details") or {}
|
|
key = value_keys.get(ttype)
|
|
value = details.get(key, "") if key is not None else target.get("original", "")
|
|
|
|
workspace_subdir = details.get("workspace_subdir")
|
|
workspace_path = f"/workspace/{workspace_subdir}" if workspace_subdir else ""
|
|
authorized.append(
|
|
{"type": ttype, "value": value, "workspace_path": workspace_path},
|
|
)
|
|
|
|
return {
|
|
"scope_source": "system_scan_config",
|
|
"authorization_source": "strix_platform_verified_targets",
|
|
"authorized_targets": authorized,
|
|
"user_instructions_do_not_expand_scope": True,
|
|
}
|
|
|
|
|
|
def make_model_settings(
|
|
reasoning_effort: ReasoningEffort | None,
|
|
) -> ModelSettings:
|
|
model_settings = ModelSettings(
|
|
parallel_tool_calls=False,
|
|
tool_choice="required",
|
|
retry=DEFAULT_MODEL_RETRY,
|
|
include_usage=True,
|
|
)
|
|
if reasoning_effort is not None:
|
|
model_settings = model_settings.resolve(
|
|
ModelSettings(reasoning=Reasoning(effort=reasoning_effort)),
|
|
)
|
|
return model_settings
|
|
|
|
|
|
def child_initial_input(
|
|
*,
|
|
name: str,
|
|
child_id: str,
|
|
parent_id: str,
|
|
task: str,
|
|
parent_history: list[Any],
|
|
) -> list[dict[str, Any]]:
|
|
initial_input: list[dict[str, Any]] = []
|
|
if parent_history:
|
|
rendered = json.dumps(parent_history, ensure_ascii=False, default=str)
|
|
initial_input.append(
|
|
{
|
|
"role": "user",
|
|
"content": (
|
|
"== Inherited context from parent (background only) ==\n"
|
|
f"{rendered}\n"
|
|
"== End of inherited context ==\n"
|
|
"Use the above as background only; do not continue the "
|
|
"parent's work. Your task follows."
|
|
),
|
|
},
|
|
)
|
|
initial_input.append(
|
|
{
|
|
"role": "user",
|
|
"content": (
|
|
f"You are agent {name} ({child_id}); your parent is {parent_id}. "
|
|
"Maintain your own identity. Call agent_finish when your task "
|
|
"is complete."
|
|
),
|
|
}
|
|
)
|
|
initial_input.append({"role": "user", "content": task})
|
|
return initial_input
|