Commit Graph
110 Commits
Author SHA1 Message Date
Ahmed Allam d97dc1b2ef docs(prompts): text-only turns no longer end an autonomous run 2026-08-01 22:16:00 +00:00
Ahmed Allam 8b464dae5d 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-01 22:13:07 +00:00
Ahmed Allam 69a60f3b7a 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-01 19:16:53 +00: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 74f334cb93 refactor(context): trim verbose comments 2026-07-26 14:38:12 -07:00
Ahmed 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 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 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
devin-ai-integration[bot] d2fbcb726d feat(reporting): add read-only list_reports + get_report tools (#889) 2026-07-26 14:05:53 -07:00
Ahmed 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
seanturner83 89a707ff51 sandbox: shrink image 7.2GB → 3.8GB (cache cleanup, multi-stage Go build, drop ZAP) (#474) 2026-07-22 07:25:38 -07:00
Ahmed Allam 59f49a1fa2 fix(prompt): make root agent orchestrate-only and fold fixing into reporting 2026-07-22 02:56:49 -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
df97c86f8f fix(prompt): down-rate or skip findings on demo data / demo environments (#793)
* fix(prompt): treat demo/sample data and demo environments as low severity or skip

* Update system_prompt.jinja

* fix(prompt): use demo context as a skip signal, not a CVSS override

* fix(prompt): let demo context honestly inform CVSS impact metrics

* fix(prompt): focus on detecting demo environments to inform CVSS impact

* fix(prompt): keep demo-environment check concise

* fix(prompt): trim demo-environment check to a short addendum

---------

Co-authored-by: Alex Schapiro <bearsyankees@gmail.com>
Co-authored-by: alex s <46074070+bearsyankees@users.noreply.github.com>
2026-07-16 22:14:36 -04:00
Ahmed 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 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 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
alex sandAhmed Allam e69c8f6633 Default sandbox exec commands to Bash (#764)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-14 03:44:05 -07:00
devin-ai-integration[bot]andAhmed Allam d6cefc176a docs(prompts): strengthen report guidance (severity, chaining, report structure) (#754)
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
2026-07-12 20:37:58 -07:00
alex s 4537f33f11 Add dependency reporting fields (#751) 2026-07-12 15:30:33 -04:00
alex s 4b46a748e4 Add skill directory registration (#746) 2026-07-12 12:05:58 -04:00
alex s 205e0b3707 Allow scan agent tool registration (#733) 2026-07-11 23:58:51 -04: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
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
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 a451766d97 Restore load_skill + surface skill catalog in system prompt
Main's load_skill tool was deleted during the SDK migration along
with the prompt-mutation pattern it relied on. Re-add the capability
without the mutation: load_skill(skills=[...]) now returns the skill
markdown bodies as a tool result, so the content lands in conversation
history as in-context reference rather than as patched-in system
prompt content. Same source of truth (load_skills + skill files),
same validation (validate_requested_skills) as create_agent.

Tool result format is plain markdown (## Skill: <name> headers joined
with ---), not the <specialized_knowledge> XML wrapping used at
agent-build time. The XML framing was deliberately reserved for
prompt-level privileged context; tool-loaded skills are honestly
labelled as just-fetched reference material.

Close the discovery loop by surfacing the full skill catalog in the
system prompt. Without it the model could only guess skill names —
discovering them via validation errors on misses. Now every agent
sees a categorised <available_skills> block right after the
<specialized_knowledge> block with a short hint pointing at
create_agent / load_skill.

Skills module: factored _iter_user_skill_files() so get_all_skill_names
(set, for validation) and get_available_skills (dict by category,
for the prompt) share one source of truth on what counts as
user-selectable. Internal categories (scan_modes, coordination) stay
excluded from both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:02:24 -07:00
0xallamandClaude Opus 4.7 418eedcd41 Clean up SDK shell tool failure modes
Three concrete wraps on exec_command / write_stdin via the existing
Shell capability configure_tools mechanism, plus one skill-doc fix.
All wraps fire on both Responses and chat-completions paths; the
chat-completions error-as-result wrap still stacks on top when needed.

- write_stdin: decode the common escape forms in `chars` (\uXXXX,
  \xXX, \n \t \r \0 \a \b \v \f \\). Models routinely send the
  literal six-char string `` intending the ASCII control byte;
  the SDK takes chars verbatim so the byte never reaches the PTY and
  documented mechanisms like Ctrl-C, arrows, and Escape silently
  don't work. Allowlist regex over recognized escapes only —
  unrecognized sequences like `\p` pass through untouched.

- exec_command: catch InvalidManifestPathError and rewrite to a
  model-actionable message ("workdir must be a path inside
  /workspace") using the exception's structured `context["rel"]` so
  we don't need to string-match the SDK's wording.

- Both tools: catch pydantic ValidationError once at the wrap and
  reformat into a short "{tool}: invalid arguments — {field}: {msg}"
  string. Covers empty cmd, missing required fields, ge/min_length
  violations on max_output_tokens and yield_time_ms — and any future
  schema field the SDK adds.

Updated python.md guidance: the `shell=` parameter is for swapping
POSIX shells (bash/zsh/sh). Interpreters belong in `cmd` —
`cmd="python3 -c '...'"`, not `shell=python3`. The `shell=interpreter`
shortcut breaks in interpreter-specific ways (python needs `-c`,
node/ruby/perl need `-e`) so there's no clean code fix and we don't
try one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:05:42 -07:00
0xallamandClaude Opus 4.7 4f62adf820 Agents graph sweep: status taxonomy, stop_agent safety, skill validation
- view_agent_graph status summary now derives buckets from the canonical
  Status literal via get_args, so adding a new status in core.agents
  auto-flows into the summary. The previous hardcoded five-bucket list
  silently omitted "failed" — buckets stopped summing to total whenever
  an agent failed.

- stop_agent rejects targets that are already in a terminal status
  (completed / stopped / crashed / failed) with a model-readable error
  pointing at view_agent_graph and send_message_to_agent. request_stop
  unconditionally overwrites status, so without this guard calling
  stop_agent on a completed agent erased the "completed" history.

- StopAgentRenderer added — was falling back to the generic key/value
  renderer; the rest of the agents_graph tools have purpose-built ones.

- agent_finish root-rejection payload trimmed from
  {success, agent_completed, error, parent_notified} to {success, error}.
  The lifecycle gate only reads success+agent_completed and they were
  always False/False on this branch, so the extra fields were dead weight.

- wait_for_message renames its top-level outcome field from "status" to
  "wait_outcome" — "status" overloaded with the coordinator's agent
  status literal (which also has "stopped" as a value, different
  meaning). Redundant "agent_waiting" boolean dropped (true iff
  wait_outcome == "waiting"). Consumer at factory._wait_tool_parked
  updated to match.

- send_message_to_agent now refuses self-send with a pointer at think /
  agent_finish / finish_scan instead of looping a message into your
  own session.

- SendMessageToAgentRenderer read args.get("agent_id") but the tool's
  param is target_agent_id, so the TUI silently never showed the target.
  Fixed.

- Restored skill validation lost during the SDK migration: skills
  module re-exports get_all_skill_names and validate_requested_skills
  (excluding internal scan_modes/coordination categories from the
  user-selectable set). create_agent now validates skills before
  spawning instead of silently accepting unknown names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 01:35:57 -07:00
0xallamandClaude Opus 4.7 a51e7820f9 Restore sitemap tools + unify proxy I/O contract
Re-add list_sitemap and view_sitemap_entry from main, ported to the
new caido-sdk-client layout via raw GraphQL queries (the typed SDK
doesn't expose sitemap operations, but the Caido server still
supports sitemapRootEntries / sitemapDescendantEntries / sitemapEntry).
Wired through caido_api (sandbox-importable helpers), the host-side
@function_tool wrappers, factory _BASE_TOOLS, the system prompt, the
python skill doc, and the public proxy docs.

While threading these through, lock down the output contract across
every proxy tool so the model sees one consistent shape:

- All tools wrap success/failure in {"success": bool, "error"?: str}
- Canonical field names: status_code, length, roundtrip_ms (omitted
  when 0), is_tls, has_descendants. snake_case everywhere on output;
  camelCase stays only on the input side where it's the GraphQL
  schema.
- repeat_request now returns a structured response that matches
  list_requests' response_summary shape (parse_raw_response parses
  the raw bytes into status_code / length / headers / body), with
  body capped at 8KB and a body_truncated flag so the model knows
  when to fetch the full body via view_request.
- RepeatRequestRenderer was reading non-existent top-level keys
  (status_code, response_time_ms, body) and silently displaying
  nothing useful — now reads the structured response shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 00:23:46 -07:00
0xallamandClaude Opus 4.7 5921fec16c Proxy tool sweep: drop send_request, fix Caido SDK gotchas
send_request was a thin wrapper over the Caido Replay API that the model
could replicate with a one-liner `curl` via exec_command. The sandbox's
HTTP_PROXY env captures all such traffic for free, so the tool was
adding bugs (duplicate dispatch, dropped responses) without adding
capability. Removed across factory, tools module, sandbox-importable
caido_api helper, TUI renderer, prompt template, skill doc, and public
docs. repeat_request stays — it operates on captured request IDs with
structured modifications, which curl can't replicate cleanly.

Three caido-sdk-client workarounds that were hitting us through both
send_request and repeat_request:

- replay_send_raw used to pass CreateReplaySessionFromRaw to
  sessions.create(), which seeds a stored entry server-side, then
  called send() — producing two history rows per call. Empty-create +
  send produces one dispatched request.
- The same helper read result.entry.response_raw, an attribute that
  doesn't exist on ReplayEntry, so response bytes were silently
  dropped. Fixed to walk result.entry.response.raw with proper None
  guards.
- get_request_with_client passed include_request_raw / include_response_raw
  based on the requested part, but the SDK's generated pydantic models
  declare raw as required even though the GraphQL fragment makes it
  conditional via @include. Passing False crashed view_request with a
  pydantic validation error. Always request both raw bodies; the caller
  picks which to surface.

Also wrapped replay.send() in asyncio.wait_for(30s) so a stalled Caido
dispatch (notably loopback targets that don't route cleanly through the
sandbox proxy) fails fast with a model-readable error instead of
hanging the agent until the function_tool 120s budget expires.

Finally, list_requests now omits the roundtrip_ms field when Caido
reports 0 — proxy-captured unscoped traffic consistently reports 0
while scoped/replay traffic carries real measurements, so the absence
of the field is now informative ("Caido didn't measure this") rather
than misleading ("this request took 0ms").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:16:06 -07:00
0xallam c4d76d72bc Simplify Python proxy automation 2026-04-27 00:21:54 -07:00
0xallam a61b5a02c5 Fix sandbox tool error wrapper 2026-04-26 17:00:02 -07:00
0xallam 756457f108 Support chat-compatible sandbox patch tool 2026-04-26 16:54:34 -07:00
0xallam c163ef882b refactor: remove custom llm provider layer 2026-04-26 14:04:32 -07:00
0xallam 9f45121dce Fix interactive lifecycle and resume history 2026-04-26 12:26:48 -07:00
0xallam e8b172bd2a Enforce lifecycle completion in non-interactive runs 2026-04-26 12:06:06 -07:00
0xallam 1d0da89090 Simplify TUI SDK event rendering 2026-04-26 11:53:20 -07:00
0xallam bd40884fcf Simplify SDK-native orchestration 2026-04-26 11:30:00 -07:00
0xallam 5ec1e0786f Simplify SDK agent orchestration 2026-04-26 09:25:47 -07:00
0xallamandClaude Opus 4.7 81703e286f refactor(notes): drop disk persistence + shared-wiki prose
The notes tool no longer touches disk. ``_notes_storage`` lives in
memory for the lifetime of one scan process, shared across every
agent in that process via the existing RLock. Process exit clears
the lot — no notes.jsonl event log, no wiki/<slug>.md Markdown
rendering, no replay-on-startup hydration.

Removed ~10 internal helpers (``_get_run_dir``,
``_get_notes_jsonl_path``, ``_append_note_event``,
``_load_notes_from_jsonl``, ``_ensure_notes_loaded``,
``_persist_wiki_note``, ``_remove_wiki_note``,
``_get_wiki_directory``, ``_get_wiki_note_path``,
``_sanitize_wiki_title``) plus the ``_loaded_notes_run_dir`` module
state, ``wiki_filename`` per-note field, and the ``OSError`` branches
that only existed for the wiki write path.

The ``wiki`` category is preserved as a free-form long-form bucket;
it just no longer has any special persistence behaviour.

Skill prompts scrubbed of every "shared wiki memory" / "repo wiki" /
"append a delta before agent_finish" instruction:
``coordination/source_aware_whitebox.md``,
``custom/source_aware_sast.md``,
``scan_modes/{quick,standard,deep}.md``, plus the WHITE-BOX TESTING
block in ``agents/prompts/system_prompt.jinja``.

HARNESS_WIKI.md updated to drop the wiki-as-shared-knowledge-base
description, the per-run output-tree references to ``notes/notes.jsonl``
and ``wiki/{note_id}-{slug}.md``, and the ``is_whitebox`` toggle prose.

Net: -178 LoC in notes/tools.py, -45 LoC across skills/system_prompt
and the wiki doc. The notes tool surface (5 ``@function_tool``s) is
unchanged for the agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:56:22 -07:00
0xallamandClaude Opus 4.7 46ff025209 feat(logging): per-scan `{run_dir}/strix.log` with scan/agent context tagging
Every scan now writes a complete log file at ``{run_dir}/strix.log``
captured from the moment ``run_dir`` is resolved through teardown.
Stdlib ``logging`` only — no parallel framework.

New ``strix/telemetry/logging.py``:
  * ``setup_scan_logging(run_dir, debug=)`` attaches a ``FileHandler``
    (DEBUG, all ``strix.*``) plus a ``StreamHandler`` (ERROR by
    default; DEBUG via ``STRIX_DEBUG=1``).
  * ``ContextVar``-backed ``scan_id`` and ``agent_id`` injected by a
    ``Filter`` so every line is auto-tagged across asyncio tasks
    without callers passing them explicitly.
  * Third-party noise (``httpx``, ``litellm``, ``openai``,
    ``anthropic``, ``urllib3``, ``httpcore``) capped at WARNING.
  * Returns a teardown handle for ``finally`` cleanup.

Wiring:
  * ``orchestration/scan.py`` calls ``setup_scan_logging`` once per
    scan after ``run_dir`` resolves; sets scan_id; tears down in
    ``finally``. Adds INFO logs for sandbox bring-up + scan
    start/end.
  * ``orchestration/hooks.py`` sets/clears ``agent_id`` ContextVar in
    ``on_agent_start`` / ``on_agent_end`` and emits INFO for agent
    lifecycle, DEBUG for every tool start/end and LLM call.
  * ``interface/main.py`` drops the ``setLevel(ERROR)`` silencer.

Coverage expanded across ~20 files (orchestration, agents, runtime,
llm, tools, interface, config, skills) with INFO for lifecycle and
DEBUG for verbose detail. Per the system instructions in
``logger.warning(f"…{e}")`` were converted to module logger calls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:35:01 -07:00
0xallamandClaude Opus 4.7 9d7f754b59 feat(tools): python_action — stateless Python execution with proxy helpers
Restores the legacy persistent-IPython tool's *ergonomics* (proxy
helpers pre-bound, structured stdout/stderr/error returns) without the
in-container daemon: each call ships ``strix.tools.proxy._calls`` source
into ``/tmp`` alongside a per-call driver, runs ``python3 -u`` against
it, and parses a sentinel-delimited JSON payload back from stdout. The
driver fetches its own guest token from Caido at ``localhost:48080``
and binds ``list_requests`` / ``view_request`` / ``send_request`` /
``repeat_request`` / ``scope_rules`` to that client; user code runs
inside an ``async def`` wrapper so top-level ``await`` works.

The proxy SDK call sequences live in one file —
``strix/tools/proxy/_calls.py`` — and are reused by both the host-side
``@function_tool`` wrappers (which add JSON serialization for the LLM)
and the in-container kernel (which exposes the bare async functions).
No code duplication; the helper logic itself is host-shipped, so
tweaking the proxy helpers does not require an image rebuild.

Image: a single ``pip install caido-sdk-client`` line so the driver's
``import caido_sdk_client`` resolves. Skill ``tooling/python`` is
always-loaded alongside ``tooling/agent_browser``.

Trade-off accepted: state does not persist across calls (no kernel).
For multi-step workflows the agent combines into one ``code`` block or
writes a script to ``/workspace/scratch/`` and runs via
``exec_command``. If a workflow surfaces that genuinely needs
persistence, the same tool surface migrates to a kernel-backed
executor without changing the LLM contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:58:53 -07:00
0xallamandClaude Opus 4.7 25decb0685 chore(orchestration): drop XML wrappers + close remaining audit gaps
Final pass after re-audit. Three sub-specs landed:

**XML simplification** — the legacy XML envelopes were prompt-engineering
ceremony, not parser primitives (the SDK uses native tool-calling). Drop
the verbose wrappers in favor of one-liner labeled headers. Side benefit:
fixes the unescaped-content XML-injection bug the audit caught (peer
content containing ``</content>`` no longer breaks the wrapper).

- ``_format_inter_agent_message``: ``<inter_agent_message><sender>...
  <content>...`` 9-line XML → ``[Message from {name} ({id}) | type=... |
  priority=...]\n{content}``.
- ``_render_completion_report``: ``<agent_completion_report><agent_info>
  ...<results>...`` XML → human-readable structured text with section
  headers and bulleted lists.
- ``inherited_context``: ``<inherited_context_from_parent>...`` →
  ``== Inherited context from parent (background only) ==``.

**MG1: TUI stop-agent uses graceful cancel.** ``tui.py`` was calling
``bus.cancel_descendants`` (hard, ``task.cancel()`` mid-stream) for the
stop-agent button. Switched to ``bus.cancel_descendants_graceful``, which
uses ``RunResultStreaming.cancel(mode="after_turn")`` to let each agent
finish its current turn (and save to session) before honoring the cancel.
The hard path remains in ``entry.py`` for KeyboardInterrupt where
graceful isn't possible.

**MG2: Document hook lock-free stats mutation.** Added a comment in
``hooks.on_llm_start`` explaining why ``warned_85`` / ``warned_final``
are mutated lock-free: SDK serializes ``on_llm_start`` per agent, so this
hook is the sole writer to those keys; ``record_usage`` only writes
disjoint keys (in/out/cached/calls).

**AG3: Auto-load ``coordination/root_agent`` skill for the root.**
Legacy auto-loaded the orchestration-guidance skill for root agents
only. Threaded ``is_root`` through ``render_system_prompt`` →
``_resolve_skills``; root agents now get the skill, children don't.

Skipped (per user direction): whitebox-wiki integration (CG2-4) — the
auto-injection / auto-update of the shared repo wiki was a pre-migration
feature; user opted not to restore it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:48:55 -07:00
0xallamandClaude Opus 4.7 f4834cd6f7 feat(orchestration): full parity with legacy harness — 8 gaps closed via SDK natives
Audit found 8 behavioral gaps between post-migration and the legacy
``BaseAgent.agent_loop``. All 8 are now closed using SDK-native
primitives — no custom workarounds, no shadow state machines.

What was broken / different:

- G1: ``inherit_context`` was dead code; children always started fresh.
- G2: TUI user message couldn't interrupt an in-flight LLM/tool turn.
- G3: ``llm_failed`` state never set; hard failures propagated as crashes.
- G4: No graceful ``stop_agent`` tool.
- G5: Parked subagents waited forever (no auto-resume timeout).
- G6: Inter-agent messages used a plain header instead of legacy XML.
- G7: Completion reports used JSON instead of legacy XML.
- G11/G12: Turn counter reset per cycle; budget warnings could re-fire.

What we did:

Bus extensions (``orchestration/bus.py``):
- ``streams`` registry + ``attach_stream`` ctx manager + ``request_interrupt``
  for SDK-native ``RunResultStreaming.cancel(mode="after_turn")``.
- ``mark_llm_failed`` + ``wait_for_user_message`` (filtered: only ``from="user"``
  satisfies; peer messages don't unstick a stuck model).
- ``stopping: set[str]`` for graceful programmatic exit.
- ``cancel_descendants_graceful`` — leaves-first via ``request_interrupt``.
- ``record_usage`` increments ``calls`` unconditionally so it doubles as the
  per-agent-lifetime turn counter (legacy ``state.iteration`` parity).
- ``warned_85`` / ``warned_final`` flags on ``stats_live`` for once-fire
  budget warnings.

Run loop rewrite (``orchestration/run_loop.py``):
- ``Runner.run`` → ``Runner.run_streamed`` with ``bus.attach_stream`` so
  cancel has a target. Catch ``(AgentsException, APIError)`` after retries
  exhaust; in interactive mode call ``mark_llm_failed`` + wait for user.
- ``UserError`` / ``MaxTurnsExceeded`` / ``CancelledError`` propagate.
- Outer loop: ``asyncio.wait_for(bus.wait_for_message, timeout=300)`` for
  interactive subagents (root waits forever). ``TimeoutError`` injects
  ``"Waiting timeout reached. Resuming execution."``.
- Honors ``bus.stopping`` at top of each iteration.

Hooks (``orchestration/hooks.py``):
- Counter source moved from per-cycle ``ctx["turn_count"]`` to
  per-lifetime ``bus.stats_live[agent_id]["calls"]``.
- Warnings guarded by once-flags — exactly-once across all cycles.

Filter (``orchestration/filter.py``):
- Restored legacy ``<inter_agent_message>`` XML envelope with the
  ``<delivery_notice>DO NOT echo back</delivery_notice>`` instruction.

Agents-graph (``tools/agents_graph/tools.py``):
- G1: ``create_agent`` reads ``ctx.turn_input`` (SDK populates it before
  tool execution at ``run_internal/turn_resolution.py:806``). Wraps as
  one ``<inherited_context_from_parent>`` block.
- G7: ``agent_finish`` emits the legacy ``<agent_completion_report>``
  XML. ``child_ctx["task"] = task`` threaded so the report echoes the
  original task.
- G4: New ``stop_agent`` tool — refuses self-stop, refuses already-
  finalized targets, ``cascade=True`` uses ``cancel_descendants_graceful``.

TUI (``interface/tui.py``):
- ``_send_user_message`` schedules ``bus.send`` AND
  ``bus.request_interrupt(target, mode="after_turn")`` — SDK finishes
  current turn cleanly, next cycle picks up the user's message.

Factory (``agents/factory.py``):
- Registered ``stop_agent`` in ``_BASE_TOOLS``.

Out of scope:
- G8 (``[ABORTED BY USER]`` marker) is auto-resolved by G2 — the SDK
  saves the full assistant message before honoring
  ``cancel(mode="after_turn")``, so partial content is preserved in the
  session.

Verified all bus behaviors with a smoke test. Lint at baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:30:29 -07:00
0xallamandClaude Opus 4.7 b3f7cfd040 refactor: nuke `strix_tool` shim + dead package re-exports
``@strix_tool`` was passing through every kwarg to ``@function_tool``
with the same defaults — zero Strix-specific value-add. The docstring
also still claimed terminal/browser/python tools opted into
``timeout_behavior="raise_exception"``, but those tools were all
deleted in the recent migrations.

- Replace 30 ``@strix_tool(...)`` callsites with ``@function_tool(...)``.
- Inline ``dump_tool_result(x)`` as ``json.dumps(x, ensure_ascii=False,
  default=str)`` at all 64 callsites — no helper.
- Delete ``strix/tools/_decorator.py``.

Drive-by: gut dead package re-exports.

- ``strix/{agents,orchestration,tools}/__init__.py`` re-exported
  symbols nobody imports via the package — every consumer uses deep
  paths (``from strix.agents.factory import build_strix_agent``).
- The 8 ``strix/tools/<sub>/__init__.py`` re-exports only fed the
  splat ``from .agents_graph import *`` etc. in the parent package
  init, which is also gone now.
- Reduced to docstrings (or empty) so ``import strix.tools`` doesn't
  drag every tool's transitive deps in eagerly.

Drive-by: drop dead helpers in ``runtime.session_manager``
(``cached_scan_ids``, ``_reset_cache_for_tests``) — zero callers since
``tests/`` was nuked in ``a6d578c``.

Verified all tool timeouts preserved (think=10, list_requests=120,
finish_scan=60, web_search=330) and ruff/mypy at baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:17:46 -07:00
0xallamandClaude Opus 4.7 2c2ab13c8f refactor: SandboxAgent + SDK Shell/Filesystem; agent-browser CLI; nuke FastAPI sidecar
Combined commits 2+3 of the migration plan because the FastAPI sidecar
removal in commit 2 broke ``browser_action`` (which lived in the
sidecar); they have to land together.

Sandbox tool layer (commit 2 piece):
- ``build_strix_agent`` now returns a ``SandboxAgent`` with
  ``capabilities=[Filesystem(), Shell()]``. The SDK runtime binds the
  capabilities to the live sandbox session per-run; agents get
  ``exec_command``, ``write_stdin``, ``apply_patch``, ``view_image``
  function tools auto-merged into their tool list. Plain ``Agent``
  short-circuits capability binding (``agents/sandbox/runtime.py:190``).
- Drop ``Compaction`` from the default capability set — it's
  OpenAI-Responses-API-only and useless for our litellm-routed
  Anthropic setup.
- Delete the entire custom in-container tool layer:
  - ``strix/tools/terminal/`` (5 files, 748 LoC libtmux)
  - ``strix/tools/file_edit/`` (3 files, 276 LoC)
  - ``strix/tools/python/`` (5 files, 459 LoC)
  - ``strix/runtime/tool_server.py`` (163 LoC FastAPI sidecar)
  - ``strix/tools/_sandbox_dispatch.py`` (117 LoC)
  - ``strix/tools/registry.py`` (109 LoC)
  - ``strix/tools/context.py`` (12 LoC)
- Drop the corresponding TUI renderers (``terminal_renderer.py``,
  ``file_edit_renderer.py``, ``python_renderer.py``) and update
  ``interface/tool_components/__init__.py``.

Browser → agent-browser CLI (commit 3 piece):
- Install ``agent-browser@0.26.0`` globally in the Dockerfile right
  after the existing ``npm install -g`` block. Run
  ``agent-browser install --with-deps`` (apt, root) and
  ``agent-browser install`` (Chrome download, pentester) +
  ``agent-browser doctor --offline --quick`` smoke test.
- Drop the explicit Playwright system-deps apt list (replaced by
  ``--with-deps``) and ``RUN .venv/bin/python -m playwright install
  chromium``.
- Vendor ``agent-browser/skill-data/core/SKILL.md`` →
  ``strix/skills/tooling/agent_browser.md`` (476 lines). Adapt
  frontmatter to Strix format; strip the install/Quickstart and the
  ``agent-browser skills get electron|slack|...`` specialized-skills
  block; add the "Caido proxy is wired via env vars; do not pass
  ``--proxy``" note.
- ``_resolve_skills`` now eagerly loads ``tooling/agent_browser`` for
  every agent (matches the previous unconditional ``browser_action``
  in ``_BASE_TOOLS``).
- Delete ``strix/tools/browser/`` (5 files, 1338 LoC) and the
  ``browser_renderer.py`` TUI render.

Sandbox plumbing:
- Drop ``bearer`` token, ``tool_server_host_port`` resolution + bundle
  keys, ``TOOL_SERVER_TOKEN``/``TOOL_SERVER_PORT``/
  ``STRIX_SANDBOX_EXECUTION_TIMEOUT`` from the manifest env in
  ``session_manager.create_or_reuse``. Caido proxy env vars
  (``http_proxy``, ``https_proxy``, ``ALL_PROXY``) stay; manifest
  applies them to every ``docker exec``-spawned process.
- Drop ``sandbox_token`` and ``tool_server_host_port`` params from
  ``make_agent_context`` and the ``create_agent`` graph tool.
- Drop the tool-server health-check from ``entry.py`` (only Caido's
  ``wait_for_tcp_ready`` remains).
- ``docker-entrypoint.sh``: delete the ~30 line
  ``Starting tool server...`` block (sudo + uvicorn launch + curl
  /health poll). Add ``NO_PROXY=localhost,127.0.0.1`` to
  ``/etc/profile.d/proxy.sh`` and ``/etc/environment`` so the
  agent-browser daemon's CDP traffic on localhost isn't routed
  through Caido.

pyproject.toml:
- ``[project.optional-dependencies] sandbox = []`` (every member of
  the previous list — fastapi, uvicorn, ipython, openhands-aci,
  playwright, libtmux — is gone with the sidecar).
- Drop ``numpydoc.*``, ``IPython.*``, ``openhands_aci.*``,
  ``playwright.*``, ``uvicorn.*``, ``pyte.*``, ``libtmux.*`` from
  the missing-imports module list.
- Drop the per-file ruff ignores for the deleted modules.

Net delta: −5512 LoC. ruff drops to 3 errors (was 21 baseline). mypy
falls to 69 errors over 3 files (was 84 over 8 — the drop comes from
deleting the modules with the worst untyped-import problems).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:33:38 -07:00
0xallamandClaude Opus 4.7 5449af2456 refactor: Caido — replace ProxyManager with caido-sdk-client (host-side)
Drop our 797-LoC manual GraphQL ``ProxyManager`` and the in-container
sandbox dispatch. Caido goes host-side via the official async Python
SDK. The Caido CLI still runs as a sidecar in the container — only the
control-plane moves.

Bootstrap moves host-side:
- New ``strix/sandbox/caido_bootstrap.py``: ``loginAsGuest`` via
  aiohttp (5 retries), then ``client.project.create(temporary=True)``
  + ``client.project.select(...)``, then return the connected
  ``caido_sdk_client.Client``. Drop the equivalent bash from
  ``docker-entrypoint.sh`` (~60 lines of curl + jq).
- ``entry.py`` calls ``bootstrap_caido_client`` after the
  ``wait_for_tcp_ready`` healthcheck, stashes the client in the bundle
  and threads it through ``make_agent_context(caido_client=...)``.
  ``agents_graph.create_agent`` propagates the same client to children.
- ``session_manager.cleanup`` ``await``s ``client.aclose()`` before
  tearing down the container.
- Drop ``CAIDO_PORT`` from the manifest env (only the in-container
  ProxyManager read it) and ``CAIDO_API_TOKEN`` from the entrypoint's
  ``/etc/profile.d/proxy.sh`` + ``/etc/environment`` heredocs.

Tools (``strix/tools/proxy/tools.py``):
- ``list_requests`` → ``client.request.list().filter().first().after()``
  with ascending/descending order. **Pagination changes from
  start_page/end_page (1-indexed) to first/after cursors** matching the
  SDK's native shape; response includes ``page_info.end_cursor`` for
  the model to thread.
- ``view_request`` → ``client.request.get(id, RequestGetOptions(...))``;
  decode raw bytes locally; existing regex-search and line-pagination
  modes preserved.
- ``send_request`` → synthesize raw HTTP bytes, parse URL into
  ``ConnectionInfoInput(host, port, is_tls)``, create a replay session
  via ``client.replay.sessions.create(CreateReplaySessionFromRaw(...))``,
  then ``client.replay.send(session_id, ReplaySendOptions(...))``.
- ``repeat_request`` → ``client.request.get(id, request_raw=True)`` →
  port the existing parse/_apply_modifications/build helpers verbatim →
  send via the same replay flow as ``send_request``.
- ``scope_rules`` → direct mapping to ``client.scope.{list, get, create,
  update, delete}``.
- **Drop ``list_sitemap`` + ``view_sitemap_entry``** — the official SDK
  has no sitemap module. The model uses HTTPQL filters
  (``req.host.eq:"X" AND req.path.cont:"/api/"``) for the same
  drill-down workflow.

Deletions:
- ``strix/tools/proxy/proxy_manager.py`` (797 LoC)
- ``strix/tools/proxy/proxy_actions.py`` (113 LoC)
- The 6-line proxy_actions pre-import in ``python_instance.py``
  (broken once proxy_actions is gone; that file is queued for deletion
  in commit 2 anyway).

Deps:
- Add ``caido-sdk-client>=0.2.0`` and ``aiohttp>=3.10.0`` to runtime
  ``[project] dependencies``.
- Drop ``gql[requests]>=3.5.3`` from ``[project.optional-dependencies]
  sandbox`` — only the in-container ProxyManager used the sync transport
  variant; the SDK pulls in ``gql[aiohttp]`` transitively for us.
- ``[[tool.mypy.overrides]]``: add ``caido_sdk_client.*`` and
  ``aiohttp.*`` to the missing-imports list with
  ``disable_error_code=["import-untyped"]`` (neither ships ``py.typed``).
- ``[tool.ruff.lint.per-file-ignores]``: bump the proxy/tools.py
  ignore to also include ``PLR0911`` (the scope_rules action dispatcher
  has many short-circuit returns).

ruff drops from 21 → 12 errors; mypy moves from 82 → 84 (the +2 are in
already-flaky files unrelated to this change). All touched files mypy
clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:23:56 -07:00
0xallamandClaude Opus 4.7 df51eeedd0 refactor: flatten CaidoCapability into direct wiring
The custom ``Capability`` subclass was 207 LoC bundling four tiny
concerns (env-var injection, tool exposure, system-prompt block,
healthcheck) — and three of them were dead code: the SDK's
``SandboxRunConfig`` doesn't accept capabilities, so
``process_manifest``, ``tools()``, and ``instructions()`` were never
called. Only ``bind()`` ran, because we invoked it manually.

Replace each piece with the obvious direct equivalent:

- **Env vars**: inject ``http_proxy`` / ``https_proxy`` / ``ALL_PROXY``
  directly into the manifest in ``session_manager.create_or_reuse``.
  This *also fixes a latent bug* — the proxy env vars in
  ``CaidoCapability.process_manifest`` weren't being applied to live
  containers, so shelled-out HTTP traffic from terminal/python tools
  wasn't actually flowing through Caido.
- **Tool exposure**: add the seven Caido tools (``list_requests``,
  ``view_request``, ``send_request``, ``repeat_request``,
  ``scope_rules``, ``list_sitemap``, ``view_sitemap_entry``) to
  ``_BASE_TOOLS`` in ``agents/factory.py`` like every other sandbox
  tool. They were already defined in ``tools/proxy/tools.py``.
- **Healthcheck**: ``entry.py`` now ``await``s
  ``wait_for_http_ready`` + ``wait_for_tcp_ready`` inline after
  ``session_manager.create_or_reuse`` returns, before any agent runs.
  No more capability state, ``configure_host_ports`` plumbing, or
  ``on_agent_start`` await-the-task indirection.
- **Instructions block**: dropped. The seven proxy tools' docstrings
  cover the HTTPQL syntax and usage already; the duplicate prompt
  fragment was overhead.

Cascade cleanups:
- Drop ``caido_capability`` from the agent context (was passed to
  every ``make_agent_context`` call but only used by the now-deleted
  ``on_agent_start`` await).
- Strip the capability await branch from
  ``StrixOrchestrationHooks.on_agent_start``; that hook now does only
  the ``tracer.agents`` mirroring it always should have.
- Drop the ``capability`` key from the session bundle.
- Drop ``strix/sandbox/caido_capability.py`` — entire file (207 LoC).
- Drop the per-file ruff ignore for the deleted file.

mypy clean on every touched file. Net -217 LoC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:32:11 -07:00