Commit Graph

512 Commits

Author SHA1 Message Date
Felix-Ayush 7e808f7d34 Add five security skills: OAuth, AWS, prototype pollution, deserialization, Django (#617)
* Add five community security skills for agent specialization

Expand coverage with OAuth flow testing, AWS misconfigurations, prototype
pollution, insecure deserialization, and Django framework playbooks.

* Address Greptile review feedback on AWS and deserialization skills

- Use head-bucket for S3 existence checks instead of duplicating s3 ls
- Add Node.js to insecure_deserialization frontmatter description

* Clarify S3 existence vs public listing checks in aws skill

Split unauthenticated enumeration into separate head-bucket/HTTP
and s3 ls steps with interpretation guidance per review.

* some tools ads

---------

Co-authored-by: bearsyankees <bearsyankees@gmail.com>
2026-07-03 00:15:53 -04:00
Sonai Biswas c3997cdb35 fix: report cost for streamed OpenRouter calls (#634)
* fix: capture cost for streamed LiteLLM responses

* docs: note LiteLLM streaming metadata callbacks
2026-07-03 00:10:28 -04:00
Sadovoi Grigorii dc8b790cf8 fix: avoid note ID collisions (#630) 2026-07-02 22:54:44 -04:00
Chirag Singhal 5a1e63aef7 fix grammer (#642)
Co-authored-by: Alex Schapiro <46074070+bearsyankees@users.noreply.github.com>
2026-07-02 22:47:02 -04:00
Alex Schapiro e6ca4d2be6 fix(report): correct csv_path indentation in write_vulnerabilities (#637)
Line 72 was over-indented, causing an IndentationError on import of strix/report/writer.py and breaking main. Also bump the mirrors-mypy pre-commit hook to v1.17.1 to avoid the mypy 1.16.0 internal crash (python/mypy#19412) on openai/_client.py.
2026-07-02 15:27:24 -04:00
ASTITVA BHARDWAJ 5ee34481fe Fix non-atomic CSV and MD writes to prevent corruption on crash (#628) (#631) 2026-07-02 07:53:30 -07:00
Rome Thorstenson f342808d2b test: add unit tests for config loader (strix/config/loader.py) (#596)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 04:31:29 -07:00
Dominic White f554523378 Remove collection of unhandled exception error messages from telemetry (#585) 2026-06-29 19:22:06 -07:00
Ahmed Allam 69e82f0258 chore(deps): refresh uv.lock to latest compatible versions (#606) 2026-06-29 19:10:18 -07:00
Ahmed Allam 52ca641679 Update readme (#607) 2026-06-29 19:09:58 -07:00
Ahmed Allam 60d68d85f3 Readme update 2026-06-29 19:00:46 -07:00
Rome Thorstenson 777005a42b fix: stop gracefully with resume hint on persistent RateLimitError (#261) (#593) 2026-06-29 07:31:54 -07:00
Rome Thorstenson 8cdf0683a3 fix(core): collapse child agent initial input into a single user message (#589) 2026-06-29 06:51:47 -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 Hvelplund 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
Rudra Dudhat 11e5d1c2b3 fix: route ollama models through ollama_chat so tool calling works (#562)
Co-authored-by: 0xallam <ahmed39652003@gmail.com>
2026-06-15 17:39:21 -07:00
Ahmed Allam cc23eeb65d Bump 1.0.3 -> 1.0.4 (#557) v1.0.4 2026-06-09 09:41:44 -07:00
Ahmed Allam 7217abfe23 Strip ANSI escapes and control bytes from terminal tool output (#554) 2026-06-09 09:22:50 -07:00
Ahmed Allam f7e3af49bd Strip all images from session on vision-rejection, not just the latest (#553) 2026-06-09 02:48:30 -07:00
Ahmed Allam 6202131028 Swallow sandbox container races in the stream consumer (#552) 2026-06-09 01:46:23 -07:00
Ahmed Allam 45409cef0d Make TUI quit instant by SIGKILL-ing the sandbox container (#548) 2026-06-08 23:40:45 -07:00
Ahmed Allam 250fe2cf3e Bump 1.0.2 -> 1.0.3 (#537) v1.0.3 2026-06-08 18:05:02 -07:00
Ahmed Allam 6c99829325 Simplify cost ledger to one bucket (#531) 2026-06-08 15:56:28 -07:00
Ahmed Allam 1c9ab993bb Use observed LiteLLM cost for LiteLLM-routed calls (#529)
Register a litellm.success_callback that captures kwargs['response_cost']
into a new observed-cost bucket on LLMUsageLedger. record() skips the
tokens-times-registry estimate for LiteLLM-routed models so we do not
double-count with the callback; OpenAI direct routes keep estimating
since LiteLLM is not invoked for them. Per-agent attribution for
LiteLLM-routed calls is apportioned by token share at to_record() time.
2026-06-08 15:01:48 -07: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
Ahmed Allam ac0fef2ed7 Show "Send message to resume" on the left of the status bar (#525) 2026-06-07 17:41:06 -07:00
0xallam 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
0xallam 36b374bd1b Bump litellm 1.83.7 -> 1.88.0 2026-06-07 17:36:19 -07:00
0xallam 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
0xallam 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
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
0xallam 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
0xallam 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
0xallam 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
0xallam 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
0xallam 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
Ahmed Allam 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 Allam 1aad460f6e fix: SDK tracing leak + orphan docker on TUI quit (closes #512) (#522)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 11:28:06 -07:00
Ahmed Allam 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>
v1.0.2
2026-05-28 11:55:10 -07:00
Ahmed Allam 3bd9d56814 fix: PyInstaller bundle is broken (missing agents SDK data + wrongly excluded gql); bump to 1.0.1 (#502) v1.0.1 2026-05-26 20:04:01 -07:00
Ahmed Allam 63faecd3b5 Strix v1.0.0 release
Strix v1.0.0 — Native tool calling, save & resume, multi-agent control
v1.0.0
2026-05-26 14:42:13 -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
0xallam 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
0xallam 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 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
0xallam 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
0xallam 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
0xallam 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
0xallam 867a0c81fc Document the SDK-provided tools as stub dirs under strix/tools/
Every agent-facing tool now has a corresponding directory: the
strix-implemented ones already do, and the SDK-provided ones
(exec_command/write_stdin shell, apply_patch, view_image) plus the
sandbox-CLI agent-browser get README-only stubs. Each README names the
implementation source, where the tool is wired up, the strix-specific
config it inherits, and the skill that teaches its usage. Listing
strix/tools/ now gives a new reader the full agent toolset at a glance.

The stub dirs intentionally have no __init__.py — they are not Python
packages, just documentation. Nothing in the codebase auto-discovers
strix.tools.* as packages (all imports are explicit), so the stubs
cannot accidentally affect runtime behavior.
2026-05-25 22:23:14 -07:00