Files
strix/strix/tools/_decorator.py
T
0xallam c7604d2f12 feat(migration): phase 2.5 — wrap sandbox-bound SDK tools
Ten tools ported, all pure pass-throughs to post_to_sandbox:

- browser_action (1 tool): the 21-action mega-tool dispatcher kept
  intact rather than fanned out, to preserve the legacy XML shape.
- terminal_execute (1 tool): tmux session driver.
- python_action (1 tool): IPython session manager.
- proxy / Caido (7 tools): list_requests, view_request, send_request,
  repeat_request, scope_rules, list_sitemap, view_sitemap_entry.

strix_tool decorator gains a strict_mode flag (default True, matching
the SDK default). send_request and repeat_request opt out of strict
mode because their headers / modifications dicts are free-form — the
SDK's strict JSON schema rejects dict[str, X] without enumerated keys.

Tests: 12 new tests in test_sdk_sandbox_tools.py covering registration,
strict-mode opt-out verification for the two free-form tools, and
dispatch shape verification (every wrapper is asserted to forward
its full kwarg surface to post_to_sandbox so the in-container handler
sees the same payload it always has).

Per-file ruff TC002 ignores added for the four new wrapper modules.

Phase 2 (tools) is now complete: 24 SDK function tools wrapped across
think/todo/notes/web_search/file_edit/reporting/load_skill/finish_scan/
browser/terminal/python/proxy. Total: 7 local + 17 sandbox-bound. Phase
3 (multi-agent orchestration) is next.

Refs: PLAYBOOK.md §3.6.
2026-04-25 00:26:30 -07:00

79 lines
2.9 KiB
Python

"""strix_tool — function_tool factory with Strix defaults.
Every tool in the migrated harness should be decorated with ``@strix_tool``
instead of bare ``@function_tool`` so the team's defaults stay consistent
without per-tool boilerplate. Override per call when needed.
Defaults:
- ``timeout``: 120s (matches the legacy tool server's
``STRIX_SANDBOX_EXECUTION_TIMEOUT``).
- ``timeout_behavior``: ``"error_as_result"`` for idempotent tools.
Critical sandbox tools (terminal, browser, python) should pass
``timeout_behavior="raise_exception"`` explicitly so the SDK can fail
the run rather than letting the model retry the same hung call (C20).
The SDK auto-threads sync function bodies via ``asyncio.to_thread``
(``tool.py:1820-1829``), so libtmux / IPython / blocking httpx code can be
written as plain ``def`` and the decorator will not block the event loop.
References:
- PLAYBOOK.md §2.6
- AUDIT_R3.md C20 (per-tool timeout_behavior discrimination)
"""
from __future__ import annotations
from collections.abc import Callable
from typing import Any, Literal
from agents import function_tool
from agents.tool import FunctionTool
_ToolFn = Callable[..., Any]
_ToolBehavior = Literal["error_as_result", "raise_exception"]
def strix_tool(
*,
timeout: float = 120.0,
timeout_behavior: _ToolBehavior = "error_as_result",
name_override: str | None = None,
description_override: str | None = None,
strict_mode: bool = True,
) -> Callable[[_ToolFn], FunctionTool]:
"""Wrap ``agents.function_tool`` with Strix defaults.
The SDK's ``FunctionTool`` requires ``async def`` for ``timeout_seconds``
to apply (sync handlers cannot be cleanly cancelled). All Strix tools are
``async def``; sync libraries (libtmux, IPython) get wrapped in
``asyncio.to_thread`` inside the async tool body.
The SDK enforces ``strict_mode=True`` by default, which forbids
free-form ``dict[str, X]`` parameters (the strict JSON schema needs
``additionalProperties: false``). A handful of legacy tools
(``send_request``, ``repeat_request``) take arbitrary header /
modification dicts whose keys can't be enumerated, so they must
opt out of strict mode to preserve parity with the XML schema.
Usage::
@strix_tool()
async def my_tool(ctx: RunContextWrapper, x: int) -> str: ...
@strix_tool(timeout=300, timeout_behavior="raise_exception")
async def critical_tool(ctx: RunContextWrapper, ...) -> str: ...
@strix_tool(strict_mode=False)
async def free_form_dict_tool(
ctx: RunContextWrapper, headers: dict[str, str],
) -> str: ...
"""
return function_tool(
timeout=timeout,
timeout_behavior=timeout_behavior,
name_override=name_override,
description_override=description_override,
strict_mode=strict_mode,
)