* Enrich issue technical details with local Git blame
* Bound report history enrichment and require unambiguous repository identity
* test(history): drive attribution through the CLI scan setup and isolate git config
* Simplify Git blame attribution to existing reporting instructions
* Make local blame guidance reliable in live reporting
Extra files (knowledge trees, workspace files) reached the docker sandbox as
per-file read-only bind mounts whose parent directories docker created as
root, so the sandbox user could neither edit them nor create siblings. They
now travel as one tar archive uploaded after bring-up and unpacked as the
sandbox user, on every backend.
hatchling 1.32.1 made BuildHookInterface a two-parameter generic, so
subscripting it with one argument raises TypeError when the hook module
is imported and every from-source build fails. Subclass the unsubscripted
interface, which works on both the old and new generic signatures.
* Add api_type field to LlmSettings
Added 'api_type' field to LlmSettings for API path selection.
* Refactor API type handling in models.py
* Implement test for LlmSettings API type
Add test for API type override settings in LlmSettings.
* fix(tests): lint api_type test, cover the api_base override route, document STRIX_API_TYPE
* fix(models): keep LiteLLM chat-completions tool schema when STRIX_API_TYPE=responses
---------
Co-authored-by: RAJVARDHAN <95933896+vardhans07@users.noreply.github.com>
Exa /search is a neural search endpoint, not a chat model, so prepending
the Perplexity system prompt made Exa match the prompt's own vocabulary
(Kali, OWASP, apt, NIST) instead of the query. The system prompt stays
on the Perplexity path where it is a chat system message; the Exa
summary instruction is unchanged.
Add Vercel AI Gateway as an LLM provider option, mirroring the existing
provider pages. New guide, an overview card, and a nav entry after
OpenRouter. Docs only.
`platform = "linux"` was a workaround for six Windows-only attribute
errors on `fcntl` and `os.getuid`. Review pushed back, correctly: it
makes local runs deterministic but checks no platform-specific branch
under its own platform.
#1301 fixes those six at the source and adds a workflow running mypy
under --platform linux, darwin and win32, so the pin is no longer
needed. Removing it here leaves this PR scoped to test assumptions and
lets the two merge in either order.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review feedback, both valid.
The control-character fixture caught every OSError, so an unwritable
temp directory or a full disk would have reported these security tests
as skipped instead of failing. It now skips only for errors that mean
the name itself is unrepresentable (EINVAL, EILSEQ, ENAMETOOLONG) and
re-raises everything else.
The mount-policy test derived the protected directory from the
checkout's drive, so a repo cloned to D: would look for D:\Windows,
miss, and skip the branch the test exists to cover. Ask Windows where
it is installed via SYSTEMROOT instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A full run on Windows 11 failed 31 tests, almost all because a fixture
assumed POSIX behaviour or read the developer's own Git configuration
rather than because Strix misbehaved. That noise hides real regressions.
- Resolve git with `shutil.which` instead of `/usr/bin/env git`, and skip
the module when no git is present. This single line accounted for 19 of
the 31 failures.
- Isolate every git subprocess from `GIT_CONFIG_GLOBAL`/`GIT_CONFIG_SYSTEM`
and supply a commit identity, so a global `core.excludesFile`,
`commit.gpgSign`, or `core.hooksPath` cannot change what a test sees.
- Create control-character filenames through a fixture that skips where
the filesystem rejects them, instead of failing to build the fixture.
- Choose the protected system directory for the platform in the mount
policy test. `check_mountable_dir` already handles both families, so on
Windows this now exercises the real policy instead of failing early.
- Assert secret-file permissions through a fixture that keeps the POSIX
0o600 check exact and, on Windows, skips with the reason stated rather
than weakening the assertion. The device-identity test is split so its
identity contract still runs everywhere.
- Pin mypy to `platform = "linux"` so type checking resolves the same
APIs as CI and the container target. This removes the six
`fcntl.flock`/`os.getuid`/`os.getgid` attribute errors reported on
Windows without adding ignores that `warn_unused_ignores` would then
flag on Linux.
Windows now reports 12 failures, all owned elsewhere: #1258 (7-8,
intermittent), #648/#652, #1288, and #1285.
Refs #1259
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Agents mostly left http_exchange_ids empty because the only nudge was the
parameter docstring. The REPORTING rules now state that a finding validated
through the proxy is not fully filed until the ids of its proving exchanges
(exploit plus baseline) are attached, copied from list_requests/view_request,
omitted only for findings with no captured HTTP at all, and attached after
the fact with update_vulnerability_report when needed. The Caido section
tells agents to note the ids as they test.
error events now carry phase (startup/preflight/sandbox_init/agent_setup/
agent_loop) and the exception class name (plus its cause), never the message
or trace. Startup and preflight failures that exit(1) before the scan starts
are beaconed with a stable error_type instead of vanishing. scan_ended
distinguishes budget_exceeded, rate_limited, and headless agent_stopped
from user_exit.
Report the distinct set of skills used once on scan_ended instead of one
skill_loaded event per skill per prompt render. Mark PostHog events with
$process_person_profile=false (distinct_id is a throwaway session id, so
person profiles were never useful) and tag them with $lib/$lib_version.
The Docker checks import the Docker SDK on the main thread before the
warm-up join, so warming it saves nothing and leaves one module shared
between the two threads during the startup window.
The warm-up thread imports strix.core.runner while warm_up_llm and
preflight_model_connection import agents.models.interface. Both walk the
agents SDK graph from different entry points, CPython fails one side to
break the import-lock cycle, and the orphan purge then removes agents.*
from sys.modules while the main thread is still importing it, crashing
strix -n with KeyError: 'agents.models'.
- Every payment-required error now ends with a "Next step" line: the
platform hint when one is sent, else the topup command and the billing
URL for the configured platform. JSON output gets the same text as
next_step. The platform hint is no longer repeated inside the error.
- An archive file passed to --source is rejected with guidance to pass
the directory instead, which packs and excludes deps/build output.
- An oversize archive names its largest files and points to --exclude
and --dry-run --show-files.
- uploads request help points to scans start --source for local code.
A non-interactive agent's loop returns after its terminal state, yet
send_message_to_agent kept reporting messages to it as delivered and the
parent then waited out wait_for_agents on a reply that could never come.
- AgentRuntime.resumable records whether the loop parks for wake-ups after a
terminal state; run_agent_loop / _start_child_runner set it from interactive.
- AgentCoordinator.send returns False (nothing queued) for a terminal agent
that is not resumable; send_message_to_agent surfaces target_status and
delivery_status=not_delivered with a pointer to list_reports / get_report.
- wait_for_agents returns wait_outcome=no_active_agents at once when no other
agent is running or waiting in a non-interactive run.
- agent_finish reads the reports the finishing agent filed from the report
state and puts their ids in the completion report, the parent message
(filed_report_ids) and its own return payload, so parents no longer have to
infer what was filed from prose.
Add Claude Fable 5.1, Gemini 3.7 Flash, and Z.ai GLM-5.3 / GLM-5.3-Flash
to RECOMMENDED_MODEL_NAMES, add a Z.ai GLM frontier family so GLM-5.x is
accepted through OpenRouter and Novita routes, and drop the superseded
GPT-5.4, GPT-5.3-codex, Opus 4.8, Sonnet 4.6, Gemini 3.6 Flash, and
Qwen3.7 entries. Update the README, docs provider pages, quickstart, and
CLI hint strings to the same current models, including DeepSeek V4,
Kimi K3, and GLM-5.3.
pydantic-settings takes the first alias present in the environment, even
when it is empty. persist_current() must save that same alias, so an empty
LLM_API_KEY does not let a non-empty OPENAI_API_KEY sibling land in the
file and restore a credential the run did not use.
A new STRIX_LLM, LLM_API_KEY, or LLM_API_BASE exported in the shell must not
be combined with the key, base, or model still stored in cli-config.json.
Restore the pre-refactor rule: when any linked LLM connection var differs
from the stored value, discard the whole stored connection before loading
and before persisting. Unrelated stored settings are kept.
persist_current rewrote the config file with only the env vars set in the
shell, so a run whose STRIX_LLM or LLM_API_KEY came from the file erased
them and the next launch failed with MISSING REQUIRED ENVIRONMENT
VARIABLES. Start from the stored env block, let a set env var override or
replace the aliases of its field, and let an empty env var clear it.
The interactive start screen skipped validate_environment() entirely, and
a bare prompt sent verify=false so the model preflight never ran. Both
kinds of setup launch now verify the model before leaving the start
screen, environment validation runs for every mode, and quitting setup
without a scan still shows the update notice.