Handle large exec_command output and surface scan errors as TUI toasts#583
Handle large exec_command output and surface scan errors as TUI toasts#583mhspektr wants to merge 513 commits into
Conversation
Updates LiteLLM from ~1.80.7 to ~1.81.1 which includes full support for z.ai (Zhipu AI) provider using the 'zai/model-name' format. This enables Strix to work with z.ai subscription credentials by setting STRIX_LLM="zai/glm-4.7" with appropriate LLM_API_KEY and LLM_API_BASE environment variables. Changes: - Updated litellm version constraint in pyproject.toml - No breaking changes to Strix API or configuration Closes #ISSUE_ID (to be linked if applicable) Signed-off-by: legendevent <legendevent@users.noreply.github.com>
The memory compressor was calling litellm.completion() without passing the api_key and api_base parameters, causing authentication errors when LLM_API_KEY is set but provider-specific env vars (OPENAI_API_KEY, etc.) are not. This matches the pattern used in dedupe.py.
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.6.0 to 6.6.2. - [Release notes](https://github.com/py-pdf/pypdf/releases) - [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md) - [Commits](py-pdf/pypdf@6.6.0...6.6.2) --- updated-dependencies: - dependency-name: pypdf dependency-version: 6.6.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Remove hardcoded github.com/gitlab.com/bitbucket.org host lists from infer_target_type. Instead, detect git repositories on any host by querying the standard /info/refs?service=git-upload-pack endpoint. Works for any self-hosted git instance.
Improve the finish_scan tool schema to produce more professional pentest reports: expand parameter descriptions with structural guidance, rewrite recommendations example with proper urgency tiers instead of Priority 0/1/2, fix duplicated section titles, and clean up informal language.
Enable native text selection across tool components and agent messages with automatic clipboard copy, toast notification, and decorative icon stripping. Replace Padding wrappers with Text to support selection across multiple renderables.
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.3.0 to 12.1.1. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](python-pillow/Pillow@11.3.0...12.1.1) --- updated-dependencies: - dependency-name: pillow dependency-version: 12.1.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [cryptography](https://github.com/pyca/cryptography) from 44.0.1 to 46.0.5. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](pyca/cryptography@44.0.1...46.0.5) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.33.4 to 6.33.5. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 6.33.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
… and CVSS Replace 12 flat parameters (code_file, code_before, code_after, code_diff, and 8 CVSS fields) with structured nested XML fields: code_locations with co-located fix_before/fix_after per location, cvss_breakdown, and cwe. This enables multi-file vulnerability locations, per-location fixes with precise line numbers, data flow representation (source/sink), CWE classification, and compatibility with GitHub/GitLab PR review APIs.
- Specify encoding="utf-8" in registry.py _load_xml_schema() - Specify encoding="utf-8" in skills/__init__.py load_skills() - Prevents cp949/shift_jis/cp1252 decoding errors on non-English Windows
… multi-part suggestions Rewrote the code_locations parameter description to make fix_before/fix_after semantics explicit: they are literal block-level replacements mapped directly to GitHub/GitLab PR suggestion blocks. Added guidance for multi-part fixes (separate locations for non-contiguous changes like imports + code), common mistakes to avoid, and updated all examples to demonstrate multi-line ranges.
* feat: add to readme new keys * feat: shoutout strix models, docs * fix: mypy error * fix: base api * docs: update quickstart and models * fixes: changes to docs uniform api_key variable naming * test: git commit hook * nevermind it was nothing * docs: Update default model to claude-sonnet-4.6 and improve Strix Router docs - Replace gpt-5 and opus-4.6 defaults with claude-sonnet-4.6 across all docs and code - Rewrite Strix Router (models.mdx) page with clearer structure and messaging - Add Strix Router as recommended option in overview.mdx and quickstart prerequisites - Update stale Claude 4.5 references to 4.6 in anthropic.mdx, openrouter.mdx, bug_report.md - Fix install.sh links to point to models.strix.ai and correct docs URLs - Update error message examples in main.py to use claude-sonnet-4-6 --------- Co-authored-by: 0xallam <ahmed39652003@gmail.com>
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.6.2 to 6.7.1. - [Release notes](https://github.com/py-pdf/pypdf/releases) - [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md) - [Commits](py-pdf/pypdf@6.6.2...6.7.1) --- updated-dependencies: - dependency-name: pypdf dependency-version: 6.7.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…sestrix#562) Co-authored-by: 0xallam <ahmed39652003@gmail.com>
- Change RuntimeError to TypeError for type validation in report/writer.py - Update pyupgrade to v3.21.2 for Python 3.14 compatibility
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.
…ix#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.
An empty or whitespace-only --mount value resolves to the current working directory and would silently bind-mount it into the sandbox. Reject it.
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.
Previously a value of 0 (or negative) made every local target count as oversized, aborting all local scans. Now <= 0 disables the 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.
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.
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>
ContextWindowExceededError carries status_code=400, which matched the _INPUT_REJECTION_CODES guard and triggered image-strip retry logic. Image stripping cannot reduce token count, so the agent wasted up to 3 retry cycles before parking as failed. Add an explicit isinstance check before the status-code guard to detect context-window overflow and park the agent as 'failed' immediately.
Scan errors were stored in _scan_error and re-raised only after the user closed the TUI, making the app appear stuck while producing a confusing post-exit traceback. Extract _notify_scan_error and call it from all three error branches in _start_scan_thread so a persistent Textual toast is shown immediately when the scan thread fails.
…missal Textual's Notification expires when raised_at + timeout - time() <= 0. With timeout=0 the toast expired immediately on mount, making the error notification invisible to users. Drop the timeout argument to use Textual's default display duration (~5 s).
exec_command output is now capped at STRIX_MAX_TOOL_OUTPUT_CHARS (default 65536). JSON arrays are parsed and trimmed to the first 50 records (valid JSON preserved); plain text keeps the first 300 lines. Both include a header showing the total size and record/line count so the agent retains full awareness of what was produced. A secondary character cap prevents single very long lines from bypassing the line limit. Set STRIX_MAX_TOOL_OUTPUT_CHARS=0 to disable.
Replaces plain truncation with a parallel summarisation pipeline when exec_command output exceeds STRIX_MAX_TOOL_OUTPUT_CHARS (default 65536). Output is split at JSON record or line boundaries; each chunk is summarised via litellm.acompletion; summaries are consolidated into a single result that fits the context window. Falls back to truncate_exec_result on any compression error. Closes usestrix#579
Code review findings: - factory._wrap_exec_command: add post-compression backstop that calls truncate_exec_result when compress_exec_result returns an oversized result (happens when _split produces 1 chunk, e.g. a single very large JSON record — compress_large_output correctly returns it unchanged, but the result was never guarded against threshold after). - tests/agents/test_factory_helpers.py (new): unit tests for _resolve_model (run_config vs settings fallback, None/whitespace error cases, non-string Model object guard) and _extract_task_hint (valid cmd, missing cmd, non-string cmd, invalid JSON, non-dict JSON).
Greptile SummaryThis PR adds three self-contained features on top of PR #577: structure-aware truncation and MapReduce LLM compression for oversized
Confidence Score: 5/5Safe to merge; all three feature areas are well-isolated with dedicated tests, and the fallback chains preserve session correctness. The truncation/compression pipeline has a solid backstop so oversized outputs cannot bypass the character cap. The ContextWindowExceededError handler is tested end-to-end including the non-retry assertion. The bind-mount wiring is straightforward. The two issues noted are a misleading log message in non-interactive mode and a deprecated asyncio.wait_for(coroutine) call pattern — neither affects correctness. No files require special attention; strix/core/execution.py has a misleading log message and strix/core/mapreduce_output.py has the deprecated asyncio.wait_for(coroutine) call, but neither introduces wrong behavior. Important Files Changed
Reviews (2): Last reviewed commit: "Fix review comment" | Re-trigger Greptile |
|
I suggest merging #577, updating this branch, and rerunning greptile before reviewing. |
* 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>
* 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 (usestrix#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 ---------
NB: this builds on #577 and should be merged after
exec_commandresults are now capped atSTRIX_MAX_TOOL_OUTPUT_CHARS(default 65 536 chars). JSON arrays are trimmed to the first 50 records (valid JSON preserved); plain text keeps the first 300 lines. Both truncation paths include a header with total size and count so the agent retains full context awareness. SetSTRIX_MAX_TOOL_OUTPUT_CHARS=0to disable.timeout=0bug that caused the toast to expire on mount was also fixed (Textual's default ~5 sis now used).Closes #579