[project] name = "strix-agent" version = "0.8.3" description = "Open-source AI Hackers for your apps" readme = "README.md" license = "Apache-2.0" requires-python = ">=3.12" authors = [ { name = "Strix", email = "hi@usestrix.com" }, ] keywords = [ "cybersecurity", "security", "vulnerability", "scanner", "pentest", "agent", "ai", "cli", ] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "Topic :: Security", "License :: OSI Approved :: Apache Software License", "Environment :: Console", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] dependencies = [ "openai-agents[litellm]==0.14.6", "pydantic>=2.11.3", "rich", "docker>=7.1.0", "textual>=6.0.0", "requests>=2.32.0", "cvss>=3.2", "scrubadub>=2.0.1", ] [project.scripts] strix = "strix.interface.main:main" [project.optional-dependencies] sandbox = [ "fastapi", "uvicorn", "ipython>=9.3.0", "openhands-aci>=0.3.0", "playwright>=1.48.0", "gql[requests]>=3.5.3", "libtmux>=0.46.2", ] [dependency-groups] dev = [ "mypy>=1.16.0", "ruff>=0.11.13", "pyright>=1.1.401", "bandit>=1.8.3", "pre-commit>=4.2.0", "pyinstaller>=6.17.0; python_version >= '3.12' and python_version < '3.15'", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["strix"] # ============================================================================ # Type Checking Configuration # ============================================================================ [tool.mypy] python_version = "3.12" strict = true strict_optional = true warn_redundant_casts = true warn_unused_ignores = true warn_return_any = true warn_unreachable = true disallow_untyped_defs = true disallow_any_generics = true disallow_subclassing_any = true disallow_untyped_calls = true disallow_incomplete_defs = true check_untyped_defs = true disallow_untyped_decorators = true no_implicit_optional = true warn_unused_configs = true show_error_codes = true show_column_numbers = true pretty = true # Allow some flexibility for third-party libraries [[tool.mypy.overrides]] module = [ "litellm.*", "numpydoc.*", "rich.*", "IPython.*", "openhands_aci.*", "playwright.*", "uvicorn.*", "jinja2.*", "pydantic_settings.*", "jwt.*", "httpx.*", "gql.*", "textual.*", "pyte.*", "libtmux.*", "cvss.*", "scrubadub.*", "docker.*", ] ignore_missing_imports = true # ============================================================================ # Ruff Configuration (Fast Python Linter & Formatter) # ============================================================================ [tool.ruff] target-version = "py312" line-length = 100 extend-exclude = [ ".git", ".mypy_cache", ".ruff_cache", "__pycache__", "build", "dist", "migrations", ] [tool.ruff.lint] # Enable comprehensive rule sets select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # Pyflakes "I", # isort "N", # pep8-naming "UP", # pyupgrade "YTT", # flake8-2020 "S", # flake8-bandit "BLE", # flake8-blind-except "FBT", # flake8-boolean-trap "B", # flake8-bugbear "A", # flake8-builtins "COM", # flake8-commas "C4", # flake8-comprehensions "DTZ", # flake8-datetimez "T10", # flake8-debugger "EM", # flake8-errmsg "FA", # flake8-future-annotations "ISC", # flake8-implicit-str-concat "ICN", # flake8-import-conventions "G", # flake8-logging-format "INP", # flake8-no-pep420 "PIE", # flake8-pie "T20", # flake8-print "PYI", # flake8-pyi "Q", # flake8-quotes "RSE", # flake8-raise "RET", # flake8-return "SLF", # flake8-self "SIM", # flake8-simplify "TID", # flake8-tidy-imports "TCH", # flake8-type-checking "ARG", # flake8-unused-arguments "PTH", # flake8-use-pathlib "ERA", # eradicate "PD", # pandas-vet "PGH", # pygrep-hooks "PL", # Pylint "TRY", # tryceratops "FLY", # flynt "PERF", # Perflint "RUF", # Ruff-specific rules ] ignore = [ "S101", # Use of assert "S104", # Possible binding to all interfaces "S301", # Use of pickle "COM812", # Missing trailing comma (handled by formatter) "ISC001", # Single line implicit string concatenation (handled by formatter) "PLR0913", # Too many arguments to function call "TRY003", # Avoid specifying long messages outside the exception class "EM101", # Exception must not use a string literal "EM102", # Exception must not use an f-string literal "FBT001", # Boolean positional arg in function definition "FBT002", # Boolean default positional argument in function definition "G004", # Logging statement uses f-string "PLR2004", # Magic value used in comparison "SLF001", # Private member accessed ] [tool.ruff.lint.per-file-ignores] # Lazy imports inside functions to avoid circular dependency with # strix.telemetry / strix.llm.dedupe / cvss. "strix/tools/notes/tools.py" = ["PLC0415", "TC002"] "strix/tools/finish/tool.py" = ["PLC0415", "TC002"] "strix/tools/reporting/tool.py" = ["PLC0415", "TC002"] "strix/tools/**/*.py" = [ "ARG001", # Unused function argument (tools may have unused args for interface consistency) ] # SDK lifecycle hooks have a fixed signature — unused args are part of the contract. "strix/orchestration/hooks.py" = [ "ARG002", # Unused method argument (RunHooks signature is set by SDK) "TC002", # ModelResponse, RunContextWrapper, AgentHookContext are annotation-only ] # Anthropic cache wrapper inherits from LitellmModel; signature shadows builtin `input`. "strix/llm/anthropic_cache_wrapper.py" = [ "A002", # Argument shadows builtin (parent signature uses `input`) "TC002", # Many SDK types are imports-for-annotations only "TC003", # collections.abc.AsyncIterator imported for return type ] # Custom Docker subclass duplicates parent body; some imports are for annotations. "strix/runtime/strix_docker_client.py" = [ "TC002", # Manifest, Container imported for annotations "TC003", # uuid imported for annotation ] "strix/llm/multi_provider_setup.py" = [ "TC002", # Model, ModelProvider imported for annotations ] "strix/tools/_decorator.py" = [ "TC002", # FunctionTool imported for annotation ] # SDK function-tool wrappers: the SDK calls get_type_hints() at registration # time to derive the JSON schema, which evaluates annotations at runtime — # so RunContextWrapper / Tool / TResponseInputItem must be imported eagerly, # not under TYPE_CHECKING. "strix/tools/todo/tools.py" = ["TC002"] "strix/tools/thinking/tool.py" = ["TC002"] "strix/tools/web_search/tool.py" = ["TC002"] "strix/tools/file_edit/tools.py" = ["TC002"] "strix/tools/browser/tool.py" = ["TC002"] "strix/tools/terminal/tool.py" = ["TC002"] "strix/tools/python/tool.py" = ["TC002"] "strix/tools/proxy/tools.py" = ["TC002"] "strix/tools/agents_graph/tools.py" = ["TC002"] "strix/agents/factory.py" = ["TC002"] # CaidoCapability uses agents.tool.Tool at runtime — pydantic Field # annotations and the cached _CAIDO_TOOLS tuple need it eagerly. "strix/sandbox/caido_capability.py" = ["TC002"] # Entry point: ``Path`` is used at runtime by the typing of the # session_manager call; importing under TYPE_CHECKING would defer # resolution past where mypy needs it. ``_build_root_task`` legitimately # walks every supported target type — splitting it into per-type # helpers would add indirection without simplifying anything. "strix/entry.py" = ["TC003", "PLR0912"] # Tracer carries a long event surface and a runtime ``Callable`` # annotation on ``vulnerability_found_callback``. "strix/telemetry/tracer.py" = ["TC003", "PLR0912", "PLR0915", "E501"] # Interface utility branches per scope-mode / target-type combination; # splitting would obscure the decision tree without simplifying it. "strix/interface/utils.py" = ["PLR0912", "BLE001", "PLC0415"] # CLI / TUI / main keep extensive lazy imports + broad exception # swallows for resilience around terminal-rendering errors. "strix/interface/cli.py" = ["BLE001", "PLC0415"] "strix/interface/tui.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915", "SIM105"] "strix/interface/main.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915"] "strix/interface/streaming_parser.py" = ["PLC0415"] "strix/interface/tool_components/agent_message_renderer.py" = ["PLC0415"] # Each short-circuit error return in the sandbox dispatch is a distinct, # documented failure mode the model needs to see verbatim. "strix/tools/_sandbox_dispatch.py" = ["PLR0911"] # StrixSession + StrixTracingProcessor catch broad Exception # intentionally: compressor / sanitizer / disk failures must not tear # down the run. Calls already log at exception level. "strix/llm/strix_session.py" = ["BLE001"] "strix/telemetry/strix_processor.py" = ["BLE001"] [tool.ruff.lint.isort] force-single-line = false lines-after-imports = 2 known-first-party = ["strix"] known-third-party = ["fastapi", "pydantic"] [tool.ruff.lint.pylint] max-args = 8 [tool.ruff.format] quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" # ============================================================================ # PyRight Configuration (Alternative type checker) # ============================================================================ [tool.pyright] include = ["strix"] exclude = ["**/__pycache__", "build", "dist"] pythonVersion = "3.12" pythonPlatform = "Linux" typeCheckingMode = "strict" reportMissingImports = true reportMissingTypeStubs = false reportGeneralTypeIssues = true reportPropertyTypeMismatch = true reportFunctionMemberAccess = true reportMissingParameterType = true reportMissingTypeArgument = true reportIncompatibleMethodOverride = true reportIncompatibleVariableOverride = true reportInconsistentConstructor = true reportOverlappingOverload = true reportConstantRedefinition = true reportImportCycles = true reportUnusedImport = true reportUnusedClass = true reportUnusedFunction = true reportUnusedVariable = true reportDuplicateImport = true # ============================================================================ # Black Configuration (Code Formatter) # ============================================================================ [tool.black] line-length = 100 target-version = ['py312'] include = '\\.pyi?$' extend-exclude = ''' /( # directories \.eggs | \.git | \.hg | \.mypy_cache | \.tox | \.venv | build | dist )/ ''' # ============================================================================ # isort Configuration (Import Sorting) # ============================================================================ [tool.isort] profile = "black" line_length = 100 multi_line_output = 3 include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true ensure_newline_before_comments = true known_first_party = ["strix"] known_third_party = ["fastapi", "pydantic", "litellm"] # ============================================================================ # Bandit Configuration (Security Linting) # ============================================================================ [tool.bandit] exclude_dirs = ["docs", "build", "dist"] skips = ["B101", "B601", "B404", "B603", "B607"] # Skip assert, shell injection, subprocess import and partial path checks severity = "medium"