Commit Graph
30 Commits
Author SHA1 Message Date
Ahmed Allam 4a455b1e62 fix(core): recover from hallucinated tool names instead of ending the scan
A tool call for a name Strix does not register raised ModelBehaviorError
from the SDK turn resolver, which nothing retries: the root agent's raise
tore down the whole scan and a sub-agent died before its status was set.
Opt into the SDK's tool_not_found_behavior="return_error_to_model" so the
unknown call comes back as a tool result and the agent self-corrects.

The setting landed in openai-agents 0.19.0, which requires openai>=2.45,
so both pins move.
2026-08-04 06:14:54 +03:00
devin-ai-integration[bot]andAhmed Allam dbc427d816 feat(runtime): mount local targets instead of copying them in (#958)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-08-02 07:45:10 -07:00
devin-ai-integration[bot]andAhmed Allam 797b37467e perf(cli): ~10x faster startup via lazy imports (#920)
* perf(cli): fast startup — lazy heavy imports + onedir standalone build

* perf(cli): drop legacy single-file compat from install/self-update

* perf(cli): simplify — drop constants module and extra lazy-import refactors

* refactor(update): strix --update just re-runs the install script

* perf(cli): drop packaging/install/update changes; deepen lazy imports instead

Reverts the onedir build, install.sh, and self-update changes so release
mechanics stay untouched. Startup cost is addressed purely by deferring
heavy imports (agents/openai, config.models, report state/writer, docker)
until a scan actually runs; DEFAULT_MAX_TURNS moves to strix.config.settings
so argparse no longer pulls the agents SDK.

---------

Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-08-02 06:24:31 +03:00
devin-ai-integration[bot] 5602bc23ca fix: pre-v1-style lifecycle resilience — mailbox delivery, uniform revival, unexitable runner, waiting timeout, broader retries, crash-safe identity (#923) 2026-08-01 11:17:08 -07:00
devin-ai-integration[bot] d4e58b2cd0 fix(llm): pass LLM_EXTRA_HEADERS through ModelSettings so they reach the agent loop (#937) 2026-07-29 19:38:06 -07:00
devin-ai-integration[bot]andAhmed Allam e037d8d727 fix: recoverable guardrail blocks and decoupled crash-notify (#919)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-27 16:28:48 -07:00
devin-ai-integration[bot]andAhmed Allam c55a8fa4ba feat(runtime): graduated wrap-up warnings, budget reserve, and interactive budget pause/continue (#893)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-26 20:37:14 -07:00
27f9750cdc feat(llm): enable Bedrock/Anthropic prompt caching for Claude models (#772)
Co-authored-by: Sean Turner <sean.turner@zerohash.com>
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-26 17:12:57 -07:00
devin-ai-integration[bot]andAhmed Allam 3b79e97f00 feat(context): spill oversized tool output into the sandbox workspace (#882)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-26 14:42:22 -07:00
Ahmed Allam f23fadfbff Rename root agent to Strix 2026-07-25 11:40:05 -07:00
Ahmed 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
devin-ai-integration[bot]andAhmed Allam 96ca7e544d revert(proxy): drop overfit Caido reconnect/HTTPQL band-aids, keep serialization lock (#799)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-17 13:18:40 -07:00
devin-ai-integration[bot]andAlex Schapiro e4548cb28c fix(proxy,tooling): serialize+reconnect Caido client, actionable HTTPQL errors, sandbox tool guidance (#794)
* fix(proxy,tooling): serialize+reconnect Caido client, actionable HTTPQL errors, sandbox tool guidance

Addresses the top recurring agent tool-call failures observed in telemetry:

- proxy: the shared Caido client had no locking or reconnect, so concurrent
  agent calls raced ("Transport is already connected") and a dead transport
  poisoned the rest of the run ("Connector is closed"/"Server disconnected").
  Add an asyncio lock + bounded reconnect in caido_api.call_with_client (sandbox
  path) and a scan-wide caido_lock in the run context that host-side proxy tools
  hold around every call. Deterministic errors are not retried.
- proxy: list_requests now returns Caido's exact parser message, echoes the
  offending query, and includes a corrected-syntax hint so agents self-correct
  instead of retrying a broken HTTPQL filter.
- shell/prompt: document that write_stdin requires a process started with
  tty=true; nudge toward writing Python to a file over deeply-nested one-liners;
  note the venv pre-installs common libs.
- agent-browser: distinguish daemon/connection failures (run doctor, don't loop)
  from malformed commands; invoke directly (no sh -c wrapper).
- containers: use POSIX '.' instead of the bashism 'source' in generated rc
  files (fixes 'sh: source: not found'); add file + xxd and pre-install
  requests/httpx/beautifulsoup4/lxml/pyjwt/cryptography in the sandbox venv.
- tests: cover proxy serialization/reconnect/no-retry and HTTPQL errors.

* fix(proxy): host-side reconnect, close stale clients, don't retry mutations

Addresses Greptile review on the reconnect logic:

- Host path had no reconnect: a dead shared context client (Caido restart /
  network blip) previously disabled proxy tools for the rest of the scan. Add
  SharedCaidoClient, a serialized reconnect-safe holder stored once per scan in
  the run context and shared across agents. On a dead transport it rebuilds via
  reconnect_caido, which re-selects the SAME Caido project (preserving captured
  traffic) instead of creating a new empty one.
- Don't repeat completed mutations: call_with_client / SharedCaidoClient.call
  take idempotent=. Reads retry once on reconnect; replay + scope
  create/update/delete heal the client but re-raise instead of risking a
  double-apply.
- Don't leak replaced clients: the stale client is aclose()d (best-effort) on
  every reconnect.
- Extend tests to cover close-on-reconnect, non-idempotent re-raise, and the
  SharedCaidoClient holder.

* fix(proxy): close replacement Caido client when project.select fails

Addresses Greptile P1: in reconnect_caido (and bootstrap_caido) a successful
connect() followed by a failing project.select()/create() discarded the
connected client without closing it, so a missing/unavailable project could
leak a transport on every retry. Close the client before re-raising.

---------

Co-authored-by: Alex Schapiro <bearsyankees@gmail.com>
2026-07-17 13:31:57 -04:00
devin-ai-integration[bot] 899e07d3a2 fix(core): bound per-agent image memory (proactive budget + inherited-context scrub) (#779) 2026-07-15 18:13:42 -07:00
alex s a5f28c6b4b Add root scan prompt options (#750) 2026-07-12 12:31:15 -04:00
alex s 22d327d21f feat(settings): add force_required_tool_choice to LlmSettings (#730)
feat(inputs): implement logic for required tool choice based on model

test(inputs): add tests for force_required_tool_choice behavior

test(runner): update tests to include force_required_tool_choice in settings
2026-07-10 18:36:33 -04:00
Rome Thorstenson 777005a42b fix: stop gracefully with resume hint on persistent RateLimitError (#261) (#593) 2026-06-29 07:31:54 -07:00
Mads Hvelplund 7141ccff62 Support large target repos with with bind-mount option. (#577)
* fix: resolve pre-commit check failures

- Change RuntimeError to TypeError for type validation in report/writer.py
- Update pyupgrade to v3.21.2 for Python 3.14 compatibility

* chore: add pytest test infrastructure

Mirror the layout introduced on feature/438-token_budget: pytest +
pytest-asyncio dev deps, asyncio_mode auto, a tests.* mypy override, and
pytest in the mypy pre-commit hook deps so the tests/ package type-checks.

* feat: add --mount and large-target pre-flight for local repos (#492)

Large local targets were copied into the sandbox file-by-file via the SDK
LocalDir entry, which stalls on big repos and could leave /workspace empty.

- --mount <path> bind-mounts a host directory read-only at /workspace/<subdir>
  instead of copying it, bypassing the per-file stream.
- A size pre-flight (STRIX_MAX_LOCAL_COPY_MB, default 1024) fails fast with a
  clear message suggesting --mount when a non-mounted local target is too big.

* fix: reject empty --mount paths

An empty or whitespace-only --mount value resolves to the current working
directory and would silently bind-mount it into the sandbox. Reject it.

* fix: dedupe local targets so a dir is never both copied and mounted

If the same directory is passed via --target and --mount (or as duplicate
values), it previously produced two targets — copied AND bind-mounted, and
the copied one could trip the size pre-flight. Dedupe by resolved path,
preferring the bind mount.

* fix: treat non-positive STRIX_MAX_LOCAL_COPY_MB as disabled

Previously a value of 0 (or negative) made every local target count as
oversized, aborting all local scans. Now <= 0 disables the pre-flight.

* fix: log unreadable subtrees during size pre-flight

os.walk silently swallowed directory-listing errors, so a permission-denied
subtree could make a large repo under-count and slip past the pre-flight.
Surface such omissions via an onerror warning.

* docs: document --mount and STRIX_MAX_LOCAL_COPY_MB

Add CLI reference + example for --mount, document the size pre-flight env var,
note the read-only-is-not-a-hard-boundary caveat and that remote repos are not
size-checked, and clarify the backends docstring on when bind mounts apply.

* Update strix/interface/main.py


* Update strix/runtime/docker_client.py


---------
2026-06-22 12:41:42 -04:00
Mads Hvelplundandgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> 962d4459d9 Add configurable token / cost usage limits (#576)
* fix: resolve pre-commit check failures

- Change RuntimeError to TypeError for type validation in report/writer.py
- Update pyupgrade to v3.21.2 for Python 3.14 compatibility

* feat(cli): add --max-budget-usd flag

Raises BudgetExceededError in ReportUsageHooks after each LLM call when
accumulated cost reaches the limit, with clean "stopped" status and
child-agent cancellation in non-interactive mode.

* test: add budget enforcement unit tests

7 tests covering no-budget, under-budget, at-limit, over-limit, error
message content, None report state, and exception hierarchy.
Also adds pytest/pytest-asyncio to dev deps and a mypy override for tests.

* fix(budget): validate positive budget and check the live cost ledger

Two hardening fixes for --max-budget-usd enforcement:

- Reject non-positive budgets. ReportUsageHooks now raises ValueError for
  max_budget_usd <= 0, and the CLI validates the flag via a custom argparse
  type so '--max-budget-usd 0' fails fast with a friendly message instead of
  silently killing the scan on the first model response.
- Read the live cost. The budget check now reads ReportState.get_total_llm_cost()
  (the live ledger) instead of the persisted run-record snapshot, so it stays
  accurate even when a usage save fails after a model call.

* fix(budget): stop the entire scan deterministically when the limit is hit

Previously a BudgetExceededError was handled per-agent: it was swallowed in
interactive mode (the loop kept waiting), a child's error escaped its detached
task as an unretrieved-exception warning, the parent was never released from
wait_for_message, and the stop was logged at ERROR with a traceback as if the
agent had failed.

Replace that with a single scan-wide signal on the coordinator:

- AgentCoordinator.trigger_budget_stop() sets a flag and wakes every parked
  agent; wait_for_message returns as soon as the flag is set.
- The run loops check coordinator.budget_stopped and raise to exit cleanly,
  marking themselves 'stopped'. The root's exception reaches run_strix_scan's
  handler, which cancels descendants and tears the scan down once; child
  exceptions are swallowed in their detached task.
- The budget stop is logged at INFO, not as a failure.

This is deterministic regardless of tree depth or which agent first sees the
limit, fixing the interactive/TUI hang where a deep agent's stop never reached
a parked root. Also re-raises BudgetExceededError explicitly in the stream
handler so it can't be mistaken for the LiteLLM 'after shutdown' race.

* fix(budget): treat a budget stop as a clean stop in the TUI

Add an explicit BudgetExceededError handler in the TUI scan thread so that, if
the error ever reaches it, the budget stop is logged as a graceful stop rather
than surfaced as a red scan error by the broad 'except Exception'. The runner
normally absorbs the error and returns cleanly, so this is defensive depth for
a money-spending feature.

* docs(cli): document --max-budget-usd behavior and limitations

Clarify that the budget is cumulative across all agents, checked after each
model response, that the scan stops cleanly (not as a failure), that the value
must be > 0, and that spend can slightly overshoot due to in-flight calls and
best-effort cost estimation.

* Apply suggestions from code review

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-22 11:17:08 -04:00
Ahmed Allam 04eb03febe Gate Reasoning(effort=...) on registry support (#528)
OpenAI's Responses API rejects reasoning.effort on non-reasoning
models like gpt-4o with `unsupported_parameter`, so any scan with
the default STRIX_REASONING_EFFORT=high against gpt-4o crashed at
the first model call. drop_params=True absorbs the rejected param
on LiteLLM-routed models but the SDK's native OpenAI path has no
equivalent.

Lift model_supports_reasoning to a public helper that strips
litellm/, any-llm/, openai/ prefixes and falls back to last-segment
lookup so prefixed forms like anthropic/claude-opus-4-7 resolve
through the bare model_cost entry. make_model_settings regains
model_name and skips Reasoning() when the registry doesn't confirm
support. uses_chat_completions_tool_schema reuses the same helper
(was duplicating the lookup under a misleading name).
2026-06-08 13:18:07 -07:00
0xallam 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
0xallam 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
Ahmed AllamandClaude Opus 4.7 13046cc74a fix: gate reasoning_effort by LiteLLM model registry (closes #517) (#523)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 12:24:48 -07:00
Ahmed AllamandClaude Opus 4.7 d0321510d2 fix: reasoning models reject tool_choice=required; bump to 1.0.2 (closes #503, #505) (#508)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 11:55:10 -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
0xallam 756457f108 Support chat-compatible sandbox patch tool 2026-04-26 16:54:34 -07:00
0xallam ef50c2dfa6 Record usage per SDK LLM response 2026-04-26 15:54:45 -07:00
0xallam 4791feb08e Track SDK LLM usage 2026-04-26 15:38:41 -07:00
0xallam af826e1281 refactor: consolidate run state layout 2026-04-26 15:01:35 -07:00
0xallam 629ea60b02 refactor: reorganize core report and tui modules 2026-04-26 14:28:50 -07:00