Files
strix/strix/run_config_factory.py
T
0xallam 295d43b3ab refactor: collapse strix/sandbox into strix/runtime; in-sandbox Caido bootstrap
The split between ``strix/sandbox/`` and ``strix/runtime/`` was
artificial — both were managing the same backend. ``strix/sandbox/``
also collided uncomfortably with the SDK's ``agents.sandbox.*``
namespace. ``runtime/`` (which matches ``STRIX_RUNTIME_BACKEND``) is
the canonical home for everything Docker / Daytona / K8s lifecycle.

While merging, also rip out two pieces of Docker-specific coupling:

- ``caido_bootstrap`` was POSTing ``loginAsGuest`` from the host via
  ``aiohttp`` to ``http://127.0.0.1:{forwarded_port}``. That assumed
  Docker port forwarding; Daytona / K8s expose ports differently.
  Now we ``session.exec`` curl from *inside* the container — the
  SDK's runtime-agnostic exec primitive — so any backend works as
  long as it implements ``exec``. The host-side Caido ``Client``
  still uses the runtime's exposed-port URL for post-bootstrap calls,
  but that goes through the SDK's own ``resolve_exposed_port``
  abstraction (also runtime-agnostic).

- The bootstrap retry loop now doubles as the readiness probe, so
  ``healthcheck.wait_for_tcp_ready`` (and the entire
  ``healthcheck.py`` module) goes away.

Drive-by simplification: drop ``caido_host_port`` plumbing entirely.
It was only piped through ``make_agent_context`` → child contexts
without ever being read; only ``caido_client`` is consumed.

Drops ``aiohttp`` runtime dep (it stays only as a transitive of the
Caido SDK).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:55:44 -07:00

157 lines
5.2 KiB
Python

"""``make_run_config`` — assemble a Strix-flavored ``RunConfig`` for ``Runner.run``.
Every scan goes through here so defaults apply uniformly. Per-call
overrides land via ``model_settings_override``.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Literal
from agents import RunConfig
from agents.model_settings import ModelSettings
from agents.retry import (
ModelRetryBackoffSettings,
ModelRetrySettings,
retry_policies,
)
from agents.sandbox import SandboxRunConfig
from openai.types.shared import Reasoning
from strix.llm.multi_provider_setup import build_multi_provider
from strix.orchestration.filter import inject_messages_filter
if TYPE_CHECKING:
from agents.sandbox.session.base_sandbox_session import BaseSandboxSession
from strix.orchestration.bus import AgentMessageBus
#: Default ``max_turns`` callers should pass to ``Runner.run``.
STRIX_DEFAULT_MAX_TURNS = 300
# Retry: 5 attempts with ``min(90, 2*2^n)`` backoff. 4xx auth/validation
# errors are excluded from the retryable status list — they can't be
# fixed by retrying and should fail fast.
_DEFAULT_RETRY = ModelRetrySettings(
max_retries=5,
backoff=ModelRetryBackoffSettings(
initial_delay=2.0,
max_delay=90.0,
multiplier=2.0,
jitter=False,
),
policy=retry_policies.any(
retry_policies.provider_suggested(),
retry_policies.network_error(),
retry_policies.http_status((429, 500, 502, 503, 504)),
),
)
def make_run_config(
*,
sandbox_session: BaseSandboxSession | None,
model: str = "anthropic/claude-sonnet-4-6",
reasoning_effort: Literal["low", "medium", "high"] | None = None,
model_settings_override: ModelSettings | None = None,
sandbox_client: Any | None = None,
) -> RunConfig:
"""Build a ``RunConfig`` with Strix defaults.
Note: ``max_turns`` is not a ``RunConfig`` field — pass it directly
to ``Runner.run``. ``STRIX_DEFAULT_MAX_TURNS`` is the budget Strix
uses.
Args:
sandbox_session: Live sandbox session shared by every agent in
this scan (one container per scan; see
:mod:`strix.runtime.session_manager`). ``None`` is allowed
for unit tests and dry runs.
model: Model alias passed to ``MultiProvider``. Defaults to the
production Anthropic alias.
reasoning_effort: ``"low" | "medium" | "high"``; routes to
``ModelSettings.reasoning``.
model_settings_override: Optional per-run ``ModelSettings``
merged over factory defaults.
sandbox_client: Optional pre-built sandbox client (Strix Docker
subclass). The SDK instantiates its built-in if a session is
supplied without a client.
"""
base_settings = ModelSettings(
parallel_tool_calls=False,
tool_choice="required",
retry=_DEFAULT_RETRY,
)
if reasoning_effort is not None:
base_settings = base_settings.resolve(
ModelSettings(reasoning=Reasoning(effort=reasoning_effort)),
)
if model_settings_override is not None:
base_settings = base_settings.resolve(model_settings_override)
sandbox_config = (
SandboxRunConfig(client=sandbox_client, session=sandbox_session)
if sandbox_session is not None
else None
)
return RunConfig(
model=model,
model_provider=build_multi_provider(),
model_settings=base_settings,
sandbox=sandbox_config,
call_model_input_filter=inject_messages_filter,
tracing_disabled=False,
trace_include_sensitive_data=False,
)
def make_agent_context(
*,
bus: AgentMessageBus,
sandbox_session: BaseSandboxSession | None,
agent_id: str,
parent_id: str | None,
tracer: Any | None,
model: str = "anthropic/claude-sonnet-4-6",
model_settings: ModelSettings | None = None,
max_turns: int = 300,
is_whitebox: bool = False,
diff_scope: dict[str, Any] | None = None,
run_id: str | None = None,
sandbox_client: Any | None = None,
agent_factory: Any | None = None,
caido_client: Any | None = None,
) -> dict[str, Any]:
"""Build the per-agent ``context`` dict passed to ``Runner.run(context=...)``.
The canonical place where bus, sandbox handles, identity, tracer
reference, and per-agent toggles live. Tools, hooks, and
``inject_messages_filter`` reach in via ``ctx.context.get(...)``.
``agent_factory`` is a callable ``(name, skills) -> agents.Agent`` —
the ``create_agent`` graph tool uses it to spin up children that
inherit the same wiring. ``sandbox_client`` is the host-side Docker
subclass, reused across child runs.
"""
return {
"bus": bus,
"sandbox_session": sandbox_session,
"sandbox_client": sandbox_client,
"caido_client": caido_client,
"agent_id": agent_id,
"parent_id": parent_id,
"tracer": tracer,
"model": model,
"model_settings": model_settings,
"max_turns": max_turns,
"turn_count": 0,
"agent_finish_called": False,
"is_whitebox": is_whitebox,
"diff_scope": diff_scope,
"run_id": run_id,
"agent_factory": agent_factory,
}