Commit Graph
100 Commits
Author SHA1 Message Date
Ahmed Allam 5b476da52a Merge origin/main into devin/1785721011-security-process-tools
Both conflicts were additive collisions: an import block that gained a line on
each side, and two test suites appending cases at the same point in the file.
2026-08-13 12:14:26 +00:00
Ahmed AllamandAhmed Allam 7cc9fa9faa chore: release v1.5.3 2026-08-10 21:28:52 +03:00
Ahmed AllamandAhmed Allam 94a2586aaa fix(container): write the browser profile as root 2026-08-10 10:08:17 +03:00
Ahmed AllamandAhmed Allam 372e27fa17 chore(container): drop explanatory comment 2026-08-10 09:54:49 +03:00
Ahmed AllamandAhmed Allam ad727edd66 fix(container): keep the browser env alive where image ENV is dropped 2026-08-10 09:54:49 +03:00
Ahmed Allam 30f8d5fa29 coverage: drop the rendered report section
The ledger stays a machine-written artifact (coverage.json and the SARIF
non-failing results); the executive report goes back to being the agent's
document.
2026-08-09 22:58:08 +00:00
Ahmed Allam 970e8ed395 writer: drop the executive-report docstring 2026-08-09 22:50:50 +00:00
Ahmed Allam 8173deadcb sarif: trim the coverage comments 2026-08-09 12:53:13 +00:00
Ahmed AllamandAhmed Allam ae07af6159 chore: drop explanatory comment 2026-08-09 15:44:16 +03:00
Ahmed AllamandAhmed Allam 649a2e2140 fix(llm): omit parallel_tool_calls on tool-less requests 2026-08-09 15:44:16 +03:00
Ahmed Allam d2de6ea78e prompt: drop the untrusted-data rule
Also removes the root-agent line pointing at a repository's own AGENTS or
SECURITY.md as the starting point for the threat model, which only made sense
with that rule in place.
2026-08-09 12:37:22 +00:00
Ahmed Allam 9da1133885 ui: keep the amendment author line readable without its body
A shared trace strips threat-model amendment text, so the author has to render
on its own rather than as a label on an empty string.
2026-08-09 12:26:46 +00:00
Ahmed Allam e833278499 ui: render the coverage ledger, threat model, and calibration fields
The six state tools added with the coverage ledger and the threat model had
no renderer in either UI, so they fell through to the generic fallback: a raw
key/value dump of the arguments, which printed a whole threat-model document
inline as one value. The five calibration fields on a vulnerability report
were likewise reaching the markdown report and SARIF but not the screen, so
the agent's own confidence and the case against a finding were invisible to
anyone watching the scan.

Go TUI gets a coverage renderer (outcome-colored rows, state transitions, and
the ledger's history and author) and a threat-model renderer (staleness,
amendments, and a heading-level preview instead of the full document), both
registered in the dispatch switch. list_coverage and get_threat_model join the
output-heavy tools that collapse to a preview.

The React viewer gets the same two as tool families, so an unknown future
tool matching /coverage/ or /threat_model/ lands on the right renderer rather
than the fallback.

Both vulnerability renderers now show confidence, its rationale,
counterevidence, the conditions that would move severity, and how a fix was
verified.
2026-08-09 12:21:32 +00:00
Ahmed AllamandAhmed Allam 597aae6715 chore: release v1.5.2 2026-08-09 04:29:34 +03:00
Ahmed AllamandAhmed Allam 22750077da chore: release v1.5.1 2026-08-07 20:28:06 +03:00
Ahmed AllamandAhmed Allam b08662449d ci: publish nested standalone archives as release assets 2026-08-06 22:03:57 +03:00
Ahmed AllamandAhmed Allam bda0f54342 ci: tolerate repr-escaped backslashes in the release TUI-sidecar check 2026-08-06 21:55:12 +03:00
Ahmed AllamandAhmed Allam c6c8bb5ca6 ci: match Windows backslash paths in the release TUI-sidecar check 2026-08-06 21:26:23 +03:00
Ahmed AllamandAhmed Allam 28747e682e chore(image): bump sandbox tag 1.2.0 -> 1.3.0 2026-08-06 20:50:07 +03:00
Ahmed AllamandAhmed Allam e71bf127fd chore: release v1.5.0 2026-08-06 20:50:07 +03:00
Ahmed AllamandAhmed Allam 709a7a1b39 docs(skills): skipped symbol search must be disclosed in reachability evidence 2026-08-06 17:06:00 +03:00
Ahmed AllamandAhmed Allam ec07f0f68f docs(skills): require per-CVE affected-symbol matching in dependency reachability analysis 2026-08-06 17:06:00 +03:00
Ahmed AllamandAhmed Allam 51bcf70722 update readme 2026-08-06 15:47:45 +03:00
Ahmed AllamandAhmed Allam cea52cce8d prompt changes 2026-08-06 15:39:43 +03:00
Ahmed AllamandAhmed Allam 77c7b0df09 prompt changes 2026-08-06 15:39:43 +03:00
Ahmed AllamandAhmed Allam b69af37cb2 feat(report): keep dependency findings from distinct manifests separate in dedupe 2026-08-06 02:20:46 +03:00
Ahmed AllamandAhmed Allam 72cb15a20a feat(reporting): require repo-relative manifest_path on dependency CVE findings 2026-08-06 02:20:46 +03:00
Ahmed AllamandAhmed Allam 6f70b6f319 fix(tui): drop the shift+enter newline hint from the setup footer 2026-08-04 06:05:46 +03:00
Ahmed Allam 5cf49f8527 feat(agents): evidence discipline, shared coverage ledger, and target-scoped threat models
Adds the negative-path half of the analysis process: a mutable coverage ledger recording what was reviewed and cleared, a target-scoped threat model shared across agents and runs, a required counterevidence pass before filing, and a fix-verification gate on inline patches.
2026-08-03 01:41:28 +00:00
Ahmed Allam c071db79ee x 2026-08-03 01:40:02 +00:00
Ahmed AllamandAhmed Allam b6cf156e95 fix(tools): tell a waiting parent when stop_agent stops its child 2026-08-02 15:43:43 +03:00
Ahmed AllamandAhmed Allam 002712284a fix(core): wake the parent when a child ends without a completion report 2026-08-02 15:43:43 +03:00
Ahmed AllamandAhmed Allam f77805e5bc docs(prompts): text-only turns no longer end an autonomous run 2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam 1c1fa49961 refactor(tools): split wait_for_message into respond_to_user + wait_for_agents
One tool was doing three jobs (wait on the user, wait on other agents, and
- wrongly - wait for a long-running command), so the driver had to guess which
one an agent meant and used parent_id as the proxy: the root waits for a human,
everyone else waits for agents. That proxy is wrong, since the user can message
any agent from the TUI's agent tree.

Tool identity now carries the intent, and the coordinator records it as a
wait_kind that survives snapshot/restore:

  respond_to_user  -> wait_kind="user",   never auto-resumed (root or not)
  wait_for_agents  -> wait_kind="agents", auto-resumed on a 300s timer
  recovery exhaust -> wait_kind="stalled"

respond_to_user fuses the message and the yield into one call, so there is no
way to answer and then forget to stop - the two-step that gpt-4o-mini skipped
2/2 in live testing. Plain text still renders as before.

Auto-resume is also bounded now: an agent that re-parks after every timeout
burned a model turn every 300s for the rest of the scan (and, since parked
children notify their parent, spammed the parent's inbox on the same cycle).
After _MAX_IDLE_AUTO_RESUMES it stays parked until a real message arrives.
2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam 742f382836 docs(core): correct the rationale for notifying a stalled child's parent
The user can message any agent from the TUI, not only the root, so the
justification is that the parent is an agent with no other way to learn
the child parked - not that the child has no human resumer.
2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam 8f1bb64d16 fix(core): tell the parent when an interactive subagent parks
Parking is self-service only for the root, which the user is watching.
A parked child owes its parent a report it can no longer send, so the
parent would wait out its full timeout for nothing.
2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam 49057f267f fix(tools): halve the wait_for_message ceiling to 300s
A mutual wait between two agents resolves only when both hit their cap,
so the ceiling is the worst-case idle burn. Name the constants instead of
repeating the literal, and align the interactive auto-resume timeout.
2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam 6eec34df24 fix(core): persist the tool-call recovery counter across resumes
An exhausted agent parked in 'waiting' got a fresh nudge budget on every
600s auto-resume, so a wedged agent could nudge-park-nudge indefinitely.
Track the count on the coordinator, snapshot it, and reset it only on
real input or an explicit lifecycle tool.
2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam f6f9469e00 fix(core): stop interactive runs stalling on a missing tool call
Interactive turns ended by plain text left the agent parked in 'waiting'
forever. Require an explicit lifecycle tool in both modes and nudge a
text-only turn back into a tool call, bounded by a recovery limit.
2026-08-02 02:15:51 +03:00
Ahmed AllamandAhmed Allam 885b2ca5c5 test(llm): cover the full run loop against a non-streaming gateway; drop README note
Adds an integration test that drives Runner.run_streamed against a
non-streaming gateway through _NonStreamingModel: the synthetic terminal
event feeds the runner, which executes the tool call and continues to a
final answer over two non-streaming turns. Removes the README env-var note.
2026-07-30 08:30:06 +03:00
Ahmed AllamandAhmed Allam 980216860e feat(llm): opt-in LLM_DISABLE_STREAMING for non-streaming OpenAI-compatible endpoints
Some OpenAI-compatible gateways don't support Server-Sent Events (or
deliver them unreliably), but the SDK run loop Strix uses only issues
streamed requests, so such a gateway fails every turn. Add an opt-in
LLM_DISABLE_STREAMING setting that wraps the resolved model in
_NonStreamingModel: each turn makes one non-streaming get_response and
replays the completed result as a single terminal stream event, so tool
calls, usage, and the rest of the agent loop are unchanged. Subscription
(ChatGPT) models are always streamed and are not wrapped.
2026-07-30 08:30:06 +03:00
Ahmed AllamandAhmed Allam e9ebdc502f fix(llm): apply LLM_EXTRA_HEADERS on native OpenAI route even without a custom base 2026-07-30 04:13:25 +03:00
Ahmed AllamandAhmed Allam ebb3a62a99 feat(llm): custom request headers for OpenAI-compatible endpoints via LLM_EXTRA_HEADERS 2026-07-30 04:13:25 +03:00
Ahmed AllamandAhmed Allam f968f8e5a7 fix(cli): align View label spacing in final panel 2026-07-27 15:41:22 -07:00
Ahmed AllamandAhmed Allam ac0014fe65 chore: release v1.4.1 2026-07-27 12:57:39 -07:00
Ahmed AllamandAhmed Allam 37c7f5a6ba chore: release v1.4.0 2026-07-27 04:55:21 -07:00
Ahmed AllamandAhmed Allam 082d4ae62c fix(runtime): wake parent when child hits a terminal state (MaxTurnsExceeded) 2026-07-27 04:27:21 -07:00
Ahmed AllamandAhmed Allam 74f334cb93 refactor(context): trim verbose comments 2026-07-26 14:38:12 -07:00
Ahmed AllamandAhmed Allam 8e9a6bf903 fix(context): reject tool-output byte ceilings below the notice size
A configured tool_output_max_bytes smaller than the truncation notice
itself can't fit a bounded preview, so a persisted result could exceed the
ceiling. Enforce a config floor (ge=1024) so nonsensical values are
rejected at load time instead of being worked around at runtime.
2026-07-26 14:38:12 -07:00
Ahmed AllamandAhmed Allam 0ebd3c6230 fix(context): reserve notice budget so bounded output honors max_bytes
The head+tail slices could each take half of max_bytes, then the
truncation notice and its separators were appended on top, so the value
persisted to history could exceed the configured maximum. Reserve an
upper bound for the notice (and separators) out of the byte budget before
slicing so the whole joined result stays within max_bytes.
2026-07-26 14:38:12 -07:00
Ahmed AllamandAhmed Allam 6bda366065 fix(context): bound native filesystem tool output in Responses mode
Chat-completions mode converts filesystem CustomTools to FunctionTools
(which bounds their result), but the Responses-API path kept them native
and unbounded, so a large read_file could still exhaust the context
window. Always configure the Filesystem capability to head+tail bound
tool output in both modes.
2026-07-26 14:38:12 -07:00
Ahmed AllamandAhmed Allam 1f36f5d401 fix(context): clamp shell output cap and count byte-trimmed dropped lines
Treat tool_output_max_tokens as a ceiling so an explicit model-supplied
cap can't exceed it, and derive the truncation notice's dropped-line
count from the lines actually kept after the byte-trim pass. Also cast
the pygments fallback lexer so it satisfies the resolve_lexer return
type under the pre-commit mypy hook.
2026-07-26 14:38:12 -07:00
Ahmed AllamandAhmed Allam a70a87f272 feat(context): bound per-tool output before it enters agent history
Cap the size of every tool result so a single verbose command (recursive
find, noisy scanner, full page dump) can't pin the conversation near the
model's context window for the rest of a scan.

- New ContextSettings config group with env-tunable caps.
- Default the SDK shell tools' max_output_tokens so exec_command /
  write_stdin truncate head+tail instead of returning unbounded output.
- Bound Strix's own FunctionTool/CustomTool results (line + UTF-8 byte
  head+tail preview with a truncation notice) and cap error strings.
2026-07-26 14:38:12 -07:00
Ahmed AllamandAhmed Allam 8169e177de docs(skills): remove references to tools not installed in the sandbox
Skills and the agent system prompt referenced external CLIs that are not
present in containers/Dockerfile, which could lead the agent to invoke
missing binaries. Replace them with installed equivalents:

- asset_discovery: drop amass/cero and the projectdiscovery tools that are
  not installed (tlsx/dnsx/asnmap/mapcidr/uncover); rewrite around the
  installed subfinder/httpx/naabu plus curl+jq (crt.sh), openssl s_client,
  dig, and whois. Stop claiming the full projectdiscovery suite is available.
- subdomain_takeover: replace dnsx with dig in the pipeline example.
- weak_password_detection: drop hydra/cewl/patator; use ffuf for web logins
  and nmap NSE *-brute scripts for services; fix dead /usr/share/wordlists
  and /usr/share/seclists paths (nothing ships by default -> download to
  /home/pentester/tools/wordlists at runtime).
- system_prompt: replace msfconsole with sqlmap in the interactive-process
  example.

active_directory skill is left as-is: it already ships an explicit install
block for its tools.
2026-07-26 14:00:07 -07:00
Ahmed AllamandAhmed Allam 589bade39a fix: restore viewer-auth.json path in auth module docstring 2026-07-26 13:11:14 -07:00
Ahmed AllamandAhmed Allam d1e8225d5f refactor: move strix/viewer under strix/interface 2026-07-26 13:11:14 -07:00
Ahmed AllamandAhmed Allam 8157ccba27 refactor(reports): fold fence helpers into report writer 2026-07-25 13:09:57 -07:00
Ahmed AllamandAhmed Allam 95d2e5fba9 fix(reports): safe-fence code-location snippets in markdown copy 2026-07-25 13:09:57 -07:00
Ahmed AllamandAhmed Allam 21243486e2 fix(reports): safe-fence markdown PoC export and share fence helpers 2026-07-25 13:09:57 -07:00
Ahmed AllamandAhmed Allam 97ed7e79a1 fix(reports): auto-detect PoC language with Python fallback 2026-07-25 13:09:57 -07:00
Ahmed AllamandAhmed Allam 31c18f8f75 fix(reports): strip markdown code fence from poc_script_code before rendering 2026-07-25 13:09:57 -07:00
Ahmed AllamandAhmed Allam 93af2b94a2 chore(deps): bump setuptools to 83.0.0 (GHSA-h35f-9h28-mq5c) 2026-07-24 07:08:50 -07:00
Ahmed AllamandAhmed Allam a2f5e3acb6 chore: release v1.3.1 2026-07-22 11:47:02 -07:00
Ahmed AllamandAhmed Allam e8c2564595 sandbox: bump install.sh pre-pull tag to 1.1.0 (match runtime default) 2026-07-22 11:40:23 -07:00
Ahmed AllamandAhmed Allam 78594e1645 sandbox: bump default inner-sandbox image to 1.1.0 2026-07-22 11:40:23 -07:00
Ahmed AllamandAhmed Allam 59f49a1fa2 fix(prompt): make root agent orchestrate-only and fold fixing into reporting 2026-07-22 02:56:49 -07:00
Ahmed AllamandAhmed Allam 48b4821f69 chore: release v1.2.0 2026-07-21 09:05:04 -07:00
Ahmed AllamandAhmed Allam 7d5a67d234 chore(llm): shorten timeout helper docstring; update tests 2026-07-17 19:45:32 -07:00
Ahmed AllamandAhmed Allam 88ad3e4472 fix(llm): use a JSON-serializable per-turn model timeout
An httpx.Timeout in ModelSettings.extra_args crashes
ModelSettings.to_json_dict() (PydanticSerializationError) on the Chat
Completions and LiteLLM model paths, which serialize settings for their
tracing generation span — failing every model turn on those paths. Pass
the timeout as a plain float, which httpx-based clients apply as the
read (inactivity) timeout.
2026-07-17 19:45:32 -07:00
Ahmed AllamandAhmed Allam cf7689e927 fix(llm): use httpx.Timeout read-inactivity for per-turn model timeout 2026-07-17 18:40:23 -07:00
Ahmed AllamandAhmed Allam 3bb95ab43d fix(llm): add per-turn model request timeout so stalled streams fail fast and retry 2026-07-17 18:40:23 -07:00
Ahmed AllamandAhmed Allam 9aa151c687 fix(llm): retry statusless mid-stream provider errors (quota/billing)
The SDK's http_status retry policy only retries errors carrying a known
HTTP status code, but quota/billing (and other provider-side) failures
often surface inside a streamed response as a bare error with no status
code, so they were failing on the first attempt. Add a statusless retry
policy to DEFAULT_MODEL_RETRY (retry count and backoff unchanged) so they
are retried before a genuine exhaustion fails the run; user aborts are
never retried.
2026-07-17 16:47:14 -07:00
Ahmed AllamandAhmed Allam b9c2592b53 fix(llm): retry statusless mid-stream provider errors (quota/billing)
The SDK's http_status retry policy only retries errors carrying a known
HTTP status code, but quota/billing (and other provider-side) failures
often surface inside a streamed response as a bare error with no status
code, so they were failing on the first attempt. Add a statusless retry
policy to DEFAULT_MODEL_RETRY so they are retried (before any content is
streamed; user aborts are never retried), restoring the pre-SDK engine's
resilience. If the provider is genuinely exhausted, the error still
propagates and fails the scan after retries.
2026-07-17 16:47:14 -07:00
Ahmed AllamandAhmed Allam 3a50a5ab0e docs(python skill): recommend a task-unique PoC filename to avoid inter-agent collisions 2026-07-16 09:15:40 -07:00
Ahmed AllamandAhmed Allam a529d7f73a docs(python skill): use a distinctive PoC filename to avoid clobbering project files 2026-07-16 09:15:40 -07:00
Ahmed AllamandAhmed Allam f6bd617964 docs(prompts,skills): stop hardcoding /workspace/scratch path
The sandbox never creates /workspace/scratch, so guidance pointing agents
there failed on first write. Make the Python/exec_command and recon
output-hygiene guidance path-agnostic (write to a file, relative to the
working dir) instead of naming a directory that may not exist.
2026-07-16 09:15:40 -07:00
Ahmed AllamandAhmed Allam 89ee7b9e5e docs(skills): add research-backed katana output-reduction flags
Per projectdiscovery katana docs, add the flags that actually bound
crawl output size and a reduce-then-delete workflow:
- -mdp (max-domain-pages; default is unlimited), -fsu (filter-similar),
  -fs scope, -f url (URL-only), -or/-ob (omit raw/body), -mrs.
- Baseline now includes -mdp 2000 -fsu; new 'Keeping output small'
  section: bound scope/volume, shrink records, distil then delete raw
  crawls.
2026-07-16 04:47:56 -07:00
Ahmed AllamandAhmed Allam 98990bae45 docs(prompts,skills): scope cleanup to own files; dedupe JSONL by URL
Address Greptile review:
- system_prompt: only clean up your own task's files; don't delete
  another agent's files in the shared workspace unless confirmed unused.
- katana.md: extract+dedupe URLs with jq before removing raw .jsonl
  (sort -u on JSONL compares whole records, not URLs).
2026-07-16 04:47:56 -07:00
Ahmed AllamandAhmed Allam 4b619d57a0 docs(prompts,skills): bound recon output for shared-disk hygiene
Add lightweight, always-on disk-hygiene guidance so agents keep recon
artifacts bounded on the shared /workspace instead of writing very large
uncapped crawl output.

- system_prompt.jinja: DISK & SCRATCH HYGIENE note in the shared-workspace
  block; recon PHASE 1 crawl bullet asks to bound each crawl and tidy up.
- skills/tooling/katana.md: bound the baseline/deep examples with -ct,
  add a Keeping-output-manageable note (bound by -ct/-d, reserve -jsl/-kf
  all for narrowed targets, check du -sh, dedupe and remove raw .jsonl).
2026-07-16 04:47:56 -07:00
Ahmed AllamandAhmed Allam 16982646df fix(runtime): bound nano_cpus to docker's int64 NanoCPUs range 2026-07-15 18:31:03 -07:00
Ahmed AllamandAhmed Allam 575e10a404 fix(runtime): also suppress OverflowError for non-finite STRIX_SANDBOX_CPUS 2026-07-15 18:31:03 -07:00
Ahmed AllamandAhmed Allam 84185db23b feat(runtime): opt-in resource limits for docker sandbox containers
Apply cgroup caps (mem_limit, shm_size, nano_cpus, pids_limit) to the
sandbox container from STRIX_SANDBOX_* env vars. Unset values keep
docker's unbounded default, so behavior is unchanged unless opted in.
2026-07-15 18:31:03 -07:00
0xallamandAhmed Allam dcf3155a9a Use function-tool schema for non-reasoning OpenAI models
OpenAI's Responses API rejects tools[i].type="custom" on non-reasoning
models like gpt-4o (400 with code=unknown_parameter, param=tools).
Strix's SDK-native Filesystem capability registers CustomTool entries
by default, so a bare STRIX_LLM=gpt-4o run failed at the first tool
invocation even though warm-up (a tool-less call) succeeded.

uses_chat_completions_tool_schema now consults
litellm.model_cost[<name>].supports_reasoning for OpenAI routes and
flips to the chat-completions function-tool schema for models that
don't carry the reasoning flag. Same registry-lookup pattern as
is_known_openai_bare_model. Non-OpenAI prefixes and configs with
LLM_API_BASE are unchanged (still function tools).
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 36b374bd1b Bump litellm 1.83.7 -> 1.88.0 2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 1a329e8972 Suppress LiteLLM stdout banner spam
litellm.suppress_debug_info silences two unsolicited print() calls in
LiteLLM core: the "Provider List: https://docs.litellm.ai/docs/providers"
banner emitted by get_llm_provider_logic and the "Give Feedback /
Get Help" + "If you need to debug this error, use litellm._turn_on_debug()"
pair emitted by exception_mapping_utils on every LiteLLM exception.
Both are unconditional print() calls, not logger output, so log-level
config can't catch them. LiteLLM's own router and proxy_server set the
same flag for the same reason.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 143b9e7040 Pre-warm-up unknown-model warning + LiteLLM streaming hardening
Warn on bare unknown model names before warm-up. is_known_openai_bare_model
consults litellm.model_cost and matches only entries whose
litellm_provider == "openai". When the configured STRIX_LLM has no
provider prefix, isn't a known OpenAI model, and no LLM_API_BASE is
set, show a clear panel pointing the user at the <provider>/<model>
form and exit before issuing the doomed request — no more chasing an
"Incorrect API key" 401 from OpenAI when the user actually meant
deepseek/, anthropic/, etc. Custom-base configs are still allowed
through unconfirmed.

Disable LiteLLM's message-logging and streaming-logging knobs to cut
noise and skip one of the two end-of-stream submit paths. The other
path at streaming_handler.py:2206 schedules work on a global
ThreadPoolExecutor that loses to atexit shutdown when the interpreter
is winding down; the SDK's stream consumer surfaces that as a fatal
"cannot schedule new futures after shutdown" RuntimeError even though
the actual stream content was already delivered. Catch and swallow
that specific RuntimeError in _run_cycle so the scan isn't killed by
an upstream end-of-stream logging race.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 3665a7899f Strip model-aware branches from LLM configuration
Drop every hand-rolled provider table and per-model gating that had
accumulated in the model-handling layer:

  * normalize_model_name no longer auto-prefixes bare claude-* / gemini-*
    names. Users supply the full <provider>/<model> form. The function
    became literally model_name.strip(), so callers now inline that and
    the function is removed.
  * tool_choice="required" is gone everywhere. Thinking-mode endpoints
    (Anthropic, DeepSeek /beta) reject it; modern reasoning models don't
    need it; non-interactive runs already have
    _append_noninteractive_tool_required_message as the convergence
    backstop. model_supports_reasoning, model_known_to_registry, and
    _model_cost_entry were only used to gate this and follow it out.
  * Reasoning(effort=...) is now attached whenever
    STRIX_REASONING_EFFORT is non-none. litellm.drop_params=True absorbs
    it for non-reasoning models.
  * Warm-up's bare-name OpenAI 401 hint is removed (false-positive prone,
    relied on substring matching).
  * reset_tool_choice on SandboxAgent is no-op now (no tool_choice gets
    set) and is removed.
  * report/dedupe.py was still routing through stock MultiProvider, so
    non-OpenAI configs failed the dedupe LLM pass; switch it to
    StrixProvider.

Verified end-to-end against modern provider strings (openai/gpt-5.4,
anthropic/claude-opus-4-7, deepseek/deepseek-reasoner,
gemini/gemini-2.5-pro, groq/, xai/, mistral/, together_ai/, perplexity/,
openrouter/, litellm/ legacy form, and whitespace-padded input): 18/18
cases route correctly, env vars mirror via litellm.validate_environment,
and ModelSettings carries no tool_choice. mypy strict passes.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 232711be8c Stop exposing litellm/ prefix in user-facing model names
Users had to type STRIX_LLM=litellm/deepseek/deepseek-chat — the
litellm/ wrapper was Strix-internal plumbing surfacing in user config.

Add StrixProvider, a MultiProvider subclass that routes any non-OpenAI
prefix (deepseek/, anthropic/, groq/, xai/, mistral/, openrouter/, …)
through LitellmProvider with the prefix preserved. normalize_model_name
no longer adds litellm/ to anything; bare claude-* / gemini-* shorthands
expand to anthropic/<model> / gemini/<model> instead of the wrapped form.

Wire StrixProvider into warm_up_llm and RunConfig.model_provider.
litellm/<provider>/<model> and any-llm/<provider>/<model> still resolve
unchanged for users on older config.

Refresh stale model names in the env-validation messages and the
warm-up hint (gpt-5.4, claude-opus-4-7, deepseek-reasoner).

Verified 24-case end-to-end matrix: OpenAI direct vs. LitellmProvider
routing, env-var mirroring via validate_environment, supports_reasoning
detection, and tool_choice gating all behave correctly across modern
providers including the user's unknown DeepSeek SKU.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 712c64f630 Drop tool_choice for registry-unknown reasoning-effort runs
When the user opts into reasoning_effort but the configured model
isn't in litellm.model_cost at all (private SKUs, fresh releases the
registry hasn't picked up — e.g. deepseek/deepseek-v4-pro), we can't
confirm thinking support and were sending tool_choice="required",
which thinking-mode endpoints reject ("Thinking mode does not support
this tool_choice").

Add model_known_to_registry() and split the decision: when the user
wants reasoning AND the model is either confirmed-reasoning OR
unknown-to-registry, drop tool_choice. The Reasoning(effort=...) param
still only attaches for confirmed-reasoning models, so we don't send
reasoning hints to known non-reasoning models.

Known non-reasoning models (gpt-4o, registry-confirmed) keep
tool_choice="required" unchanged.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam dee2a03d07 Hint at provider prefix when bare model 401s against OpenAI
A bare model name without a provider prefix routes through the SDK's
default OpenAI provider, so configuring STRIX_LLM=deepseek-v4-pro with
LLM_API_KEY=<deepseek key> sends that key to api.openai.com and
surfaces a confusing "Incorrect API key" error pointing at the OpenAI
dashboard.

When warm-up fails with an OpenAI-shaped error AND the configured
model is still unprefixed after normalize_model_name, append a hint
that points the user at the '<provider>/<model>' form with concrete
examples.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam 1473fc7336 Use validate_environment to resolve provider env var
Naively uppercasing the routing prefix breaks for providers whose
LiteLLM env var name doesn't match the prefix verbatim:
  together_ai/...  needs TOGETHERAI_API_KEY  (no underscore)
  perplexity/...   needs PERPLEXITYAI_API_KEY

Ask LiteLLM directly via litellm.validate_environment(model=...) which
env vars it consults for the chosen provider, then setdefault each one
to LLM_API_KEY. This is the SDK-blessed lookup and stays correct for
every provider LiteLLM supports without a hand-maintained name map.

Lowercase the routed model name before lookup so mixed-case user input
(e.g. Together_AI/...) still resolves.
2026-06-07 17:36:19 -07:00
0xallamandAhmed Allam dd1f816f7c Cover bare claude-/gemini- shorthands in env mirror
normalize_model_name expands `claude-*` and `gemini-*` shorthands into
`litellm/anthropic/...` and `litellm/gemini/...` at routing time, but
the mirror helper was looking at the raw pre-normalization name — bare
shorthands had no `/` and hit the early return, so ANTHROPIC_API_KEY /
GEMINI_API_KEY were never populated for those users.

Run the same normalization inside the mirror helper so the provider
prefix is consistent with what LiteLLM actually sees downstream.
2026-06-07 17:36:19 -07:00
9ab70c6d61 Mirror LLM_API_KEY to provider env var (closes #504)
LiteLLM's per-provider branches (deepseek, anthropic, groq, etc.)
don't consult ``litellm.api_key`` (the module global Strix sets).
They only check the per-call ``api_key`` kwarg and the
``<PROVIDER>_API_KEY`` env var. The SDK's LitellmModel passes
``api_key=None`` by default, so requests went out with an empty
bearer and DeepSeek (and friends) returned 401.

Mirror the user's LLM_API_KEY into the provider-specific env var
(``DEEPSEEK_API_KEY`` for ``deepseek/...``, ``ANTHROPIC_API_KEY``
for ``anthropic/...``, etc.) using LiteLLM's documented convention.
``os.environ.setdefault`` is used so an explicit user env is never
clobbered. The OpenAI branch was already working via
``set_default_openai_key`` + the existing ``litellm.api_key`` global
fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 17:36:19 -07:00
0xallam d50827c2d4 Merge origin/main into harness-migration
Brings in 10 commits from main on top of the v1.0.0 branch.

Resolutions:
- Legacy harness files modified on main but deleted in the migration —
  kept as deleted: strix/agents/base_agent.py, strix/agents/state.py,
  strix/config/config.py, strix/llm/llm.py,
  strix/llm/memory_compressor.py, strix/llm/utils.py,
  strix/runtime/docker_runtime.py.
- tests/runtime/test_docker_runtime.py — removed; tests dead code.
- strix/skills/vulnerabilities/idor.md and ssrf.md — auto-merged.
- New skills from main kept: header_injection.md, http_request_smuggling.md,
  nosql_injection.md, ssti.md.
2026-05-26 14:30:30 -07:00
0xallamandClaude Opus 4.7 9c20a8f911 Bump to 1.0.0
- pyproject.toml + uv.lock — strix-agent package version
- strix/config/settings.py — default STRIX_IMAGE tag
- docs/advanced/configuration.mdx — documented default
- scripts/install.sh — installer default

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:15:25 -07:00
0xallamandClaude Opus 4.7 8414c59557 Strip narrative comments and module/helper docstrings
Five rounds of sweep across the tree. Net ~544 lines removed.

Removed:
- Section-divider banners and one-line section labels (# Display
  utilities, # ----- list_requests -----, # CVSS breakdown, etc.).
- Module-level prose docstrings on internal modules. Kept one-line
  summaries; trimmed multi-paragraph narration about SDK/Strix
  responsibility splits, cache strategies, three-source precedence.
- Internal-helper docstrings that just restate the function name —
  caido_api helpers (caido_url, get_client, view_request, etc.),
  settings-class one-liners (LLMSettings, RuntimeSettings, ...),
  UI helper docstrings.
- Args/Returns blocks on non-LLM-facing internal helpers
  (build_strix_agent, render_system_prompt, create_or_reuse,
  bootstrap_caido) — kept only the genuinely non-obvious params.
- Internal-history phrasing — "Mirrors main-branch shape",
  "pre-SDK harness", "previous lookup matched no attribute".
- Narrative comments inside function bodies that explained what the
  next line does, design rationale obvious from the surrounding code,
  or "we used to..." asides.
- Trailing periods on every error-string literal across the tool tree.
- Duplicated roundtripTime quirk comment (kept the LLM-facing copy in
  tools/proxy/tools.py).

Kept (every one names an upstream bug, vendored-code provenance, or
non-obvious data quirk):
- core/runner.py: SDK replay-with-empty-initial-input + on_agent_end
  lifecycle gap.
- runtime/docker_client.py: VERBATIM COPY block of the upstream
  _create_container body, pinned to SDK v0.14.6.
- runtime/session_manager.py: NO_PROXY for agent-browser CDP loopback.
- tools/proxy/caido_api.py: generated-pydantic Request.raw quirk,
  replay double-history pitfall.
- tools/proxy/tools.py: Caido roundtripTime=0 quirk for proxy
  captures.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:02:40 -07:00
0xallamandClaude Opus 4.7 054eedf53f Tighten tool surface consistency
Four passes of audit-and-patch on the tool surface, condensed.

Tool API shape:
- Todo tools collapse to a single list-based form (one arg per tool,
  always a list, no dual-mode validator). Result-field names line up
  across the family — created_count / updated_count / marked_count /
  deleted_count, and _mark returns a single "marked" key plus the new
  status instead of marked_done / marked_pending.
- list_notes splits the overloaded total_count into filtered_count
  (matches) and total_count (grand total), matching list_todos. All
  three notes mutations now echo total_count and note_id.
- finish_scan drops the machine-code error strings; a single human
  "error" key carries the reason on every failure path.
- scope_rules delete echoes a message so the renderer's success
  branch has something to surface.

Failure-key unification: every tool now uses {"success": False,
"error": "..."} on failure paths. Touched thinking, web_search,
reporting, and finish. Trailing periods on error strings swept clean
across the whole tool tree.

Tool prompts (docstring re-imports vs main):
- create_vulnerability_report re-imports the CWE reference catalog,
  multi-part fix rules, fix_before/fix_after PR-suggestion mechanics,
  the COMMON MISTAKES list, the informational-vs-actionable
  distinction, and file-path examples.
- web_search re-imports concrete example queries.
- list_sitemap docstring fixed hasDescendants -> has_descendants
  (the camelCase reference never matched our snake_case schema).
- create_agent.skills description "Comma-separated" -> "List of".
- factory.py module docstring no longer claims there's no runtime
  skill-loading tool. agents_graph module docstring lists stop_agent.
- system_prompt nudges loading the matching skill before guessing
  payloads or syntax from memory.

TUI:
- proxy_renderer was reading stale field names from the pre-SDK
  schema (requests / total_count / statusCode / matches /
  showing_lines); now reads entries / page_info / status_code / hits
  / page+total_lines. Three proxy operations were rendering empty
  before this.
- Idle-pane placeholder text trimmed to "Loading...".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
0xallamandClaude Opus 4.7 763df86f17 Collapse todo tools to a single list-based form
create_todo / update_todo / mark_todo_done / mark_todo_pending /
delete_todo used to accept either a single-item form (title, todo_id,
…) or a bulk form (todos, updates, todo_ids), reject the call if the
agent set both, and explain the rule in the docstring. The agent kept
tripping the validator. Drop the single-item form everywhere — each
tool now takes one list arg. Single calls just pass a one-item list.

While the API was being reshaped, line the result schemas up:
created_count replaces the lone "count", _mark returns a single
"marked" key plus new_status instead of marked_done / marked_pending,
and list_todos splits the overloaded total_count into filtered_count
(matches) and total_count (grand total) so a filtered call no longer
hides the real size.

Docstrings now spell out each item's fields with required/optional
and the legal status / priority values, plus a worked example.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:51:08 -07:00
0xallamandClaude Opus 4.7 d3ab3f836b Add TUI renderers for the seven previously-unstyled tools
exec_command, write_stdin, apply_patch, view_image, load_skill,
list_sitemap, and view_sitemap_entry were falling through to the
generic dict-dumper. They now render in the same visual language as
the rest of the toolset: the terminal pair uses the >_ icon with
pygments bash highlighting; apply_patch and view_image use the file-
edit diamond with colored +/- diff lines and per-language syntax
highlighting; sitemap and load_skill mirror the proxy and skill
patterns already established.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:22:56 -07:00
0xallamandClaude Opus 4.7 393548c6e8 Add Scarf telemetry alongside PostHog
Both backends share session/version/first-run helpers in
strix/telemetry/_common.py and fire from the same four call sites in
strix/interface/main.py and strix/report/state.py. STRIX_TELEMETRY is
the single toggle for both.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:22:01 -07:00