refactor: Caido — replace ProxyManager with caido-sdk-client (host-side)

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.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
0xallam
2026-04-25 14:23:56 -07:00
parent 9b31e9fd29
commit 5449af2456
14 changed files with 614 additions and 1173 deletions
Generated
+35 -17
View File
@@ -292,6 +292,32 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" },
]
[[package]]
name = "caido-sdk-client"
version = "0.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "caido-server-auth" },
{ name = "gql", extra = ["aiohttp", "websockets"] },
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/62/d7/5381d8d94fec799bec7004decbf33a4c5581a8374941fe784e730e01cf80/caido_sdk_client-0.2.0.tar.gz", hash = "sha256:39988fe07b3fa9c69adbd49662db660d7707d60d9245109b1623def97b39bac8", size = 57436, upload-time = "2026-04-12T22:25:12.084Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/ae/3530caa6a79bafb8049374ca09515686d98532aca73c4fdbd0f6e06de5c9/caido_sdk_client-0.2.0-py3-none-any.whl", hash = "sha256:bc573651681c093ee9663c7924d38d522a89cea60e2ce00d34ba9b02942b1da1", size = 96207, upload-time = "2026-04-12T22:25:11.168Z" },
]
[[package]]
name = "caido-server-auth"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "gql", extra = ["aiohttp", "websockets"] },
]
sdist = { url = "https://files.pythonhosted.org/packages/69/be/58cc2eaf97f729124b8939a9ea1c1a664b2d96dce0448788df073fca3ac9/caido_server_auth-0.1.2.tar.gz", hash = "sha256:eb2c25e9de15062760b68112f5d8e9ad63eeb1322518b90c1a0119a69a7524a4", size = 6559, upload-time = "2026-03-14T20:41:55.119Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/7b/14d192151bcc3c1624cfb488c59ec03e96c1009d015089d729c1aecd26e9/caido_server_auth-0.1.2-py3-none-any.whl", hash = "sha256:40c6cd3728e24cdff402c4efa5d8f55bf6e6cc73ac0169bdea1ad1e34faff8ff", size = 10197, upload-time = "2026-03-14T20:41:54.091Z" },
]
[[package]]
name = "catalogue"
version = "2.0.10"
@@ -969,9 +995,11 @@ wheels = [
]
[package.optional-dependencies]
requests = [
{ name = "requests" },
{ name = "requests-toolbelt" },
aiohttp = [
{ name = "aiohttp" },
]
websockets = [
{ name = "websockets" },
]
[[package]]
@@ -3128,18 +3156,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
name = "requests-toolbelt"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
]
[[package]]
name = "rich"
version = "13.9.4"
@@ -3527,6 +3543,8 @@ name = "strix-agent"
version = "0.8.3"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
{ name = "caido-sdk-client" },
{ name = "cvss" },
{ name = "docker" },
{ name = "openai-agents", extra = ["litellm"] },
@@ -3540,7 +3558,6 @@ dependencies = [
[package.optional-dependencies]
sandbox = [
{ name = "fastapi" },
{ name = "gql", extra = ["requests"] },
{ name = "ipython" },
{ name = "libtmux" },
{ name = "openhands-aci" },
@@ -3560,10 +3577,11 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "aiohttp", specifier = ">=3.10.0" },
{ name = "caido-sdk-client", specifier = ">=0.2.0" },
{ name = "cvss", specifier = ">=3.2" },
{ name = "docker", specifier = ">=7.1.0" },
{ name = "fastapi", marker = "extra == 'sandbox'" },
{ name = "gql", extras = ["requests"], marker = "extra == 'sandbox'", specifier = ">=3.5.3" },
{ name = "ipython", marker = "extra == 'sandbox'", specifier = ">=9.3.0" },
{ name = "libtmux", marker = "extra == 'sandbox'", specifier = ">=0.46.2" },
{ name = "openai-agents", extras = ["litellm"], specifier = "==0.14.6" },