Combined commits 2+3 of the migration plan because the FastAPI sidecar
removal in commit 2 broke ``browser_action`` (which lived in the
sidecar); they have to land together.
Sandbox tool layer (commit 2 piece):
- ``build_strix_agent`` now returns a ``SandboxAgent`` with
``capabilities=[Filesystem(), Shell()]``. The SDK runtime binds the
capabilities to the live sandbox session per-run; agents get
``exec_command``, ``write_stdin``, ``apply_patch``, ``view_image``
function tools auto-merged into their tool list. Plain ``Agent``
short-circuits capability binding (``agents/sandbox/runtime.py:190``).
- Drop ``Compaction`` from the default capability set — it's
OpenAI-Responses-API-only and useless for our litellm-routed
Anthropic setup.
- Delete the entire custom in-container tool layer:
- ``strix/tools/terminal/`` (5 files, 748 LoC libtmux)
- ``strix/tools/file_edit/`` (3 files, 276 LoC)
- ``strix/tools/python/`` (5 files, 459 LoC)
- ``strix/runtime/tool_server.py`` (163 LoC FastAPI sidecar)
- ``strix/tools/_sandbox_dispatch.py`` (117 LoC)
- ``strix/tools/registry.py`` (109 LoC)
- ``strix/tools/context.py`` (12 LoC)
- Drop the corresponding TUI renderers (``terminal_renderer.py``,
``file_edit_renderer.py``, ``python_renderer.py``) and update
``interface/tool_components/__init__.py``.
Browser → agent-browser CLI (commit 3 piece):
- Install ``agent-browser@0.26.0`` globally in the Dockerfile right
after the existing ``npm install -g`` block. Run
``agent-browser install --with-deps`` (apt, root) and
``agent-browser install`` (Chrome download, pentester) +
``agent-browser doctor --offline --quick`` smoke test.
- Drop the explicit Playwright system-deps apt list (replaced by
``--with-deps``) and ``RUN .venv/bin/python -m playwright install
chromium``.
- Vendor ``agent-browser/skill-data/core/SKILL.md`` →
``strix/skills/tooling/agent_browser.md`` (476 lines). Adapt
frontmatter to Strix format; strip the install/Quickstart and the
``agent-browser skills get electron|slack|...`` specialized-skills
block; add the "Caido proxy is wired via env vars; do not pass
``--proxy``" note.
- ``_resolve_skills`` now eagerly loads ``tooling/agent_browser`` for
every agent (matches the previous unconditional ``browser_action``
in ``_BASE_TOOLS``).
- Delete ``strix/tools/browser/`` (5 files, 1338 LoC) and the
``browser_renderer.py`` TUI render.
Sandbox plumbing:
- Drop ``bearer`` token, ``tool_server_host_port`` resolution + bundle
keys, ``TOOL_SERVER_TOKEN``/``TOOL_SERVER_PORT``/
``STRIX_SANDBOX_EXECUTION_TIMEOUT`` from the manifest env in
``session_manager.create_or_reuse``. Caido proxy env vars
(``http_proxy``, ``https_proxy``, ``ALL_PROXY``) stay; manifest
applies them to every ``docker exec``-spawned process.
- Drop ``sandbox_token`` and ``tool_server_host_port`` params from
``make_agent_context`` and the ``create_agent`` graph tool.
- Drop the tool-server health-check from ``entry.py`` (only Caido's
``wait_for_tcp_ready`` remains).
- ``docker-entrypoint.sh``: delete the ~30 line
``Starting tool server...`` block (sudo + uvicorn launch + curl
/health poll). Add ``NO_PROXY=localhost,127.0.0.1`` to
``/etc/profile.d/proxy.sh`` and ``/etc/environment`` so the
agent-browser daemon's CDP traffic on localhost isn't routed
through Caido.
pyproject.toml:
- ``[project.optional-dependencies] sandbox = []`` (every member of
the previous list — fastapi, uvicorn, ipython, openhands-aci,
playwright, libtmux — is gone with the sidecar).
- Drop ``numpydoc.*``, ``IPython.*``, ``openhands_aci.*``,
``playwright.*``, ``uvicorn.*``, ``pyte.*``, ``libtmux.*`` from
the missing-imports module list.
- Drop the per-file ruff ignores for the deleted modules.
Net delta: −5512 LoC. ruff drops to 3 errors (was 21 baseline). mypy
falls to 69 errors over 3 files (was 84 over 8 — the drop comes from
deleting the modules with the worst untyped-import problems).
Drop our 797-LoC manual GraphQL ``ProxyManager`` and the in-container
sandbox dispatch. Caido goes host-side via the official async Python
SDK. The Caido CLI still runs as a sidecar in the container — only the
control-plane moves.
Bootstrap moves host-side:
- New ``strix/sandbox/caido_bootstrap.py``: ``loginAsGuest`` via
aiohttp (5 retries), then ``client.project.create(temporary=True)``
+ ``client.project.select(...)``, then return the connected
``caido_sdk_client.Client``. Drop the equivalent bash from
``docker-entrypoint.sh`` (~60 lines of curl + jq).
- ``entry.py`` calls ``bootstrap_caido_client`` after the
``wait_for_tcp_ready`` healthcheck, stashes the client in the bundle
and threads it through ``make_agent_context(caido_client=...)``.
``agents_graph.create_agent`` propagates the same client to children.
- ``session_manager.cleanup`` ``await``s ``client.aclose()`` before
tearing down the container.
- Drop ``CAIDO_PORT`` from the manifest env (only the in-container
ProxyManager read it) and ``CAIDO_API_TOKEN`` from the entrypoint's
``/etc/profile.d/proxy.sh`` + ``/etc/environment`` heredocs.
Tools (``strix/tools/proxy/tools.py``):
- ``list_requests`` → ``client.request.list().filter().first().after()``
with ascending/descending order. **Pagination changes from
start_page/end_page (1-indexed) to first/after cursors** matching the
SDK's native shape; response includes ``page_info.end_cursor`` for
the model to thread.
- ``view_request`` → ``client.request.get(id, RequestGetOptions(...))``;
decode raw bytes locally; existing regex-search and line-pagination
modes preserved.
- ``send_request`` → synthesize raw HTTP bytes, parse URL into
``ConnectionInfoInput(host, port, is_tls)``, create a replay session
via ``client.replay.sessions.create(CreateReplaySessionFromRaw(...))``,
then ``client.replay.send(session_id, ReplaySendOptions(...))``.
- ``repeat_request`` → ``client.request.get(id, request_raw=True)`` →
port the existing parse/_apply_modifications/build helpers verbatim →
send via the same replay flow as ``send_request``.
- ``scope_rules`` → direct mapping to ``client.scope.{list, get, create,
update, delete}``.
- **Drop ``list_sitemap`` + ``view_sitemap_entry``** — the official SDK
has no sitemap module. The model uses HTTPQL filters
(``req.host.eq:"X" AND req.path.cont:"/api/"``) for the same
drill-down workflow.
Deletions:
- ``strix/tools/proxy/proxy_manager.py`` (797 LoC)
- ``strix/tools/proxy/proxy_actions.py`` (113 LoC)
- The 6-line proxy_actions pre-import in ``python_instance.py``
(broken once proxy_actions is gone; that file is queued for deletion
in commit 2 anyway).
Deps:
- Add ``caido-sdk-client>=0.2.0`` and ``aiohttp>=3.10.0`` to runtime
``[project] dependencies``.
- Drop ``gql[requests]>=3.5.3`` from ``[project.optional-dependencies]
sandbox`` — only the in-container ProxyManager used the sync transport
variant; the SDK pulls in ``gql[aiohttp]`` transitively for us.
- ``[[tool.mypy.overrides]]``: add ``caido_sdk_client.*`` and
``aiohttp.*`` to the missing-imports list with
``disable_error_code=["import-untyped"]`` (neither ships ``py.typed``).
- ``[tool.ruff.lint.per-file-ignores]``: bump the proxy/tools.py
ignore to also include ``PLR0911`` (the scope_rules action dispatcher
has many short-circuit returns).
ruff drops from 21 → 12 errors; mypy moves from 82 → 84 (the +2 are in
already-flaky files unrelated to this change). All touched files mypy
clean.
Users can now access the Caido web UI from their browser to inspect traffic,
replay requests, and perform manual testing alongside the automated scan.
- Map Caido port (48080) to a random host port in DockerRuntime
- Add caido_port to SandboxInfo and track across container lifecycle
- Display Caido URL in TUI sidebar stats panel with selectable text
- Bind Caido to 0.0.0.0 in entrypoint (requires image rebuild)
- Bump sandbox image to 0.1.12
- Restore discord link in exit screen
- Move tool server startup from Python to entrypoint script
- Hardcode Caido port (48080) in entrypoint, remove from Python
- Use /app/venv/bin/python directly instead of poetry run
- Fix env var passing through sudo with sudo -E and explicit vars
- Add Caido process monitoring and logging during startup
- Add retry logic with exponential backoff for token fetch
- Add tool server process validation before declaring ready
- Simplify docker_runtime.py (489 -> 310 lines)
- DRY up container state recovery into _recover_container_state()
- Add container creation retry logic (3 attempts)
- Fix GraphQL health check URL (/graphql/ with trailing slash)