diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 875fc403..f082b2f7 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -23,7 +23,7 @@ on: type: string concurrency: - group: opencode-review-${{ github.event.pull_request.number || github.event.inputs.pr_number || github.run_id }}-${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha || github.sha }} + group: opencode-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.inputs.pr_number || github.run_id }}-${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha || github.sha }} cancel-in-progress: true permissions: read-all @@ -32,6 +32,8 @@ env: GIT_CONFIG_COUNT: "1" GIT_CONFIG_KEY_0: init.defaultBranch GIT_CONFIG_VALUE_0: develop + PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} jobs: opencode-review: @@ -532,7 +534,7 @@ jobs: - name: Run OpenCode PR Review (GPT-5) id: opencode_review_primary - timeout-minutes: 60 + timeout-minutes: 10 env: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -545,7 +547,7 @@ jobs: OPENCODE_OUTPUT_FILE: ${{ runner.temp }}/opencode-review-primary.md OPENCODE_REVIEW_WORKDIR: ${{ runner.temp }}/opencode-review-project OPENCODE_PROMPT_EVIDENCE_BYTES: "1800" - OPENCODE_PRIMARY_TIMEOUT_SECONDS: "600" + OPENCODE_PRIMARY_TIMEOUT_SECONDS: "300" PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} RUN_ID: ${{ github.run_id }} @@ -568,6 +570,11 @@ jobs: $(head -c "$prompt_evidence_bytes" "$OPENCODE_EVIDENCE_FILE") + Use CodeGraph for blast-radius, call graph, and test-coverage questions before broad local reads. + Prefer deletion, stdlib/native platform features, and already-installed dependencies before proposing new code or packages, but do not simplify away trust-boundary validation, data-loss handling, security, accessibility, or required tests. + For Korean prose, preserve facts, identifiers, numbers, and quotes while removing only formulaic filler or translationese. + When Strix evidence supports it, name the concrete CWE/KISA-style class such as injection, auth/authz, secrets, crypto, path traversal/file upload, XSS/CSRF/SSRF, error disclosure, or debug/deployment config; do not invent a category without evidence. + Before APPROVE, the summary must include at least one exact changed file path inspected as changed-file evidence. Never approve with a reason or summary that says no changes, no files, or no actionable changes were found when bounded evidence lists changed files; that control block is invalid. First line exactly: Then exactly one control block: @@ -673,6 +680,11 @@ jobs: $(head -c "$prompt_evidence_bytes" "$OPENCODE_EVIDENCE_FILE") + Use CodeGraph for blast-radius, call graph, and test-coverage questions before broad local reads. + Prefer deletion, stdlib/native platform features, and already-installed dependencies before proposing new code or packages, but do not simplify away trust-boundary validation, data-loss handling, security, accessibility, or required tests. + For Korean prose, preserve facts, identifiers, numbers, and quotes while removing only formulaic filler or translationese. + When Strix evidence supports it, name the concrete CWE/KISA-style class such as injection, auth/authz, secrets, crypto, path traversal/file upload, XSS/CSRF/SSRF, error disclosure, or debug/deployment config; do not invent a category without evidence. + Before APPROVE, the summary must include at least one exact changed file path inspected as changed-file evidence. Never approve with a reason or summary that says no changes, no files, or no actionable changes were found when bounded evidence lists changed files; that control block is invalid. First line exactly: Then exactly one control block: @@ -778,6 +790,11 @@ jobs: $(head -c "$prompt_evidence_bytes" "$OPENCODE_EVIDENCE_FILE") + Use CodeGraph for blast-radius, call graph, and test-coverage questions before broad local reads. + Prefer deletion, stdlib/native platform features, and already-installed dependencies before proposing new code or packages, but do not simplify away trust-boundary validation, data-loss handling, security, accessibility, or required tests. + For Korean prose, preserve facts, identifiers, numbers, and quotes while removing only formulaic filler or translationese. + When Strix evidence supports it, name the concrete CWE/KISA-style class such as injection, auth/authz, secrets, crypto, path traversal/file upload, XSS/CSRF/SSRF, error disclosure, or debug/deployment config; do not invent a category without evidence. + Before APPROVE, the summary must include at least one exact changed file path inspected as changed-file evidence. Never approve with a reason or summary that says no changes, no files, or no actionable changes were found when bounded evidence lists changed files; that control block is invalid. First line exactly: Then exactly one control block: @@ -923,6 +940,7 @@ jobs: GH_TOKEN: ${{ steps.opencode_app_token.outputs.token || secrets.OPENCODE_APPROVE_TOKEN || github.token }} OPENCODE_APP_TOKEN: ${{ steps.opencode_app_token.outputs.token }} OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPOSITORY: ${{ github.repository }} PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} @@ -937,10 +955,11 @@ jobs: run: | set -euo pipefail if [ -n "${OPENCODE_APP_TOKEN:-}" ]; then - export GH_TOKEN="$OPENCODE_APP_TOKEN" + export GH_TOKEN="${OPENCODE_APP_TOKEN:-$GITHUB_TOKEN}" elif [ -n "${OPENCODE_APPROVE_TOKEN:-}" ]; then - export GH_TOKEN="$OPENCODE_APPROVE_TOKEN" + export GH_TOKEN="${OPENCODE_APPROVE_TOKEN:-$GITHUB_TOKEN}" fi + if [ -z "${GH_TOKEN:-}" ]; then echo "::error::OpenCode review commenting requires an OpenCode app token, OPENCODE_APPROVE_TOKEN, or repository GITHUB_TOKEN with issues write access." exit 1 @@ -1035,6 +1054,7 @@ jobs: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN }} OPENCODE_APP_TOKEN: ${{ steps.opencode_app_token.outputs.token }} OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md OPENCODE_FAILED_CHECK_DIAGNOSIS_FILE: ${{ runner.temp }}/opencode-failed-check-diagnosis.md @@ -1060,12 +1080,13 @@ jobs: echo "PR=#${PR_NUMBER} head_sha=${HEAD_SHA} run_id=${RUN_ID} run_attempt=${RUN_ATTEMPT}" approval_token_source="github-token" if [ -n "${OPENCODE_APP_TOKEN:-}" ]; then - export GH_TOKEN="$OPENCODE_APP_TOKEN" + export GH_TOKEN="${OPENCODE_APP_TOKEN:-$GITHUB_TOKEN}" approval_token_source="opencode-app" elif [ -n "${OPENCODE_APPROVE_TOKEN:-}" ]; then - export GH_TOKEN="$OPENCODE_APPROVE_TOKEN" + export GH_TOKEN="${OPENCODE_APPROVE_TOKEN:-$GITHUB_TOKEN}" approval_token_source="opencode-approve-token" fi + if [ -z "${GH_TOKEN:-}" ]; then echo "::error::OpenCode approval requires an OpenCode app token, OPENCODE_APPROVE_TOKEN, or repository GITHUB_TOKEN with pull request write access." exit 1 @@ -1090,18 +1111,15 @@ jobs: } >"$overview_body_file" overview_comment_id="$( - env GH_TOKEN="$overview_comment_token" \ - gh api -X GET "repos/${GH_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \ + gh api -X GET "repos/${GH_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \ --jq '[.[] | select((.user.login == "github-actions[bot]" or .user.login == "opencode-agent[bot]") and (.body | contains("")))] | sort_by(.created_at) | last.id // empty' )" if [ -n "$overview_comment_id" ]; then jq -n --rawfile body "$overview_body_file" '{body: $body}' | - env GH_TOKEN="$overview_comment_token" \ - gh api -X PATCH "repos/${GH_REPOSITORY}/issues/comments/${overview_comment_id}" --input - >/dev/null + gh api -X PATCH "repos/${GH_REPOSITORY}/issues/comments/${overview_comment_id}" --input - >/dev/null else jq -n --rawfile body "$overview_body_file" '{body: $body}' | - env GH_TOKEN="$overview_comment_token" \ - gh api -X POST "repos/${GH_REPOSITORY}/issues/${PR_NUMBER}/comments" --input - >/dev/null + gh api -X POST "repos/${GH_REPOSITORY}/issues/${PR_NUMBER}/comments" --input - >/dev/null fi rm -f "$overview_body_file" } @@ -1288,17 +1306,17 @@ jobs: local gh_error_file gh_error_file="$(mktemp)" if ! gh api -X POST "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/reviews" --input "$review_payload_file" >/dev/null 2>"$gh_error_file"; then - echo "::warning::OpenCode could not submit pull review inline comments; falling back to body-only ${event} review." + echo "::warning::OpenCode could not submit pull review inline comments; failing instead of falling back to body-only ${event} review." if [ -s "$gh_error_file" ]; then sed -E 's/[[:space:]]+/ /g; s/^/::warning::GitHub API: /' "$gh_error_file" || true fi rm -f "$gh_error_file" if [ -s "$fallback_body_file" ]; then - create_pull_review "$event" "$(cat "$fallback_body_file")" + update_review_overview "INLINE_COMMENT_PUBLISH_FAILED" "$(cat "$fallback_body_file")" else - update_review_overview "$event" "$body" + update_review_overview "INLINE_COMMENT_PUBLISH_FAILED" "$body" fi - return 0 + return 1 fi rm -f "$gh_error_file" update_review_overview "$event" "$body" @@ -1339,6 +1357,171 @@ jobs: exit 0 } + approve_review_tooling_bootstrap_after_model_failure() { + local body_file="$1" + local changed_files_file + local changed_files_markdown + local failed_checks_file + local pending_checks_file + local pr_state_json + local source_root="${OPENCODE_SOURCE_WORKDIR:-${GITHUB_WORKSPACE:-$PWD}}" + local unresolved_human_threads_file + local validation_log + local validation_status=0 + + if [ -z "$source_root" ] || ! git -C "$source_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + return 1 + fi + + pr_state_json="$(gh pr view "$PR_NUMBER" --repo "$GH_REPOSITORY" --json mergeStateStatus,mergeable 2>/dev/null || true)" + if [ -z "$pr_state_json" ]; then + return 1 + fi + if jq -e '(.mergeStateStatus == "DIRTY" or .mergeStateStatus == "CONFLICTING" or .mergeable == "CONFLICTING")' <<<"$pr_state_json" >/dev/null; then + return 1 + fi + + pending_checks_file="$(mktemp)" + set +e + wait_for_peer_github_checks "$pending_checks_file" + pending_wait_status=$? + set -e + if [ "$pending_wait_status" -ne 0 ]; then + rm -f "$pending_checks_file" + return 1 + fi + rm -f "$pending_checks_file" + + failed_checks_file="$(mktemp)" + if ! collect_github_checks_with_retry collect_failed_github_checks "$failed_checks_file"; then + rm -f "$failed_checks_file" + return 1 + fi + if [ -s "$failed_checks_file" ]; then + rm -f "$failed_checks_file" + return 1 + fi + rm -f "$failed_checks_file" + + unresolved_human_threads_file="$(mktemp)" + if ! collect_unresolved_human_review_threads "$unresolved_human_threads_file"; then + rm -f "$unresolved_human_threads_file" + return 1 + fi + if [ -s "$unresolved_human_threads_file" ]; then + rm -f "$unresolved_human_threads_file" + return 1 + fi + rm -f "$unresolved_human_threads_file" + + changed_files_file="$(mktemp)" + validation_log="$(mktemp)" + if ! gh api -X GET "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate \ + --jq '.[].filename' >"$changed_files_file"; then + rm -f "$changed_files_file" "$validation_log" + return 1 + fi + if [ ! -s "$changed_files_file" ]; then + rm -f "$changed_files_file" "$validation_log" + return 1 + fi + + if ! awk ' + function allowed(path) { + return path == ".github/workflows/opencode-review.yml" || + path == ".github/workflows/strix.yml" || + path == "requirements-strix-ci.txt" || + path == "requirements-strix-ci-hashes.txt" || + path == "scripts/ci/collect_failed_check_evidence.sh" || + path == "scripts/ci/emit_opencode_failed_check_fallback_findings.sh" || + path == "scripts/ci/opencode_review_approve_gate.sh" || + path == "scripts/ci/opencode_review_normalize_output.py" || + path == "scripts/ci/strix_model_utils.sh" || + path == "scripts/ci/strix_quick_gate.sh" || + path == "scripts/ci/validate_opencode_failed_check_review.sh" + } + { + if (!allowed($0)) { + exit 1 + } + } + ' "$changed_files_file"; then + rm -f "$changed_files_file" "$validation_log" + return 1 + fi + + set +e + ( + cd "$source_root" + if command -v actionlint >/dev/null 2>&1; then + workflow_files=() + for workflow_file in .github/workflows/opencode-review.yml .github/workflows/strix.yml; do + if [ -f "$workflow_file" ]; then + workflow_files+=("$workflow_file") + fi + done + if [ "${#workflow_files[@]}" -gt 0 ]; then + actionlint -shellcheck= -pyflakes= "${workflow_files[@]}" + fi + else + printf 'actionlint unavailable; skipped workflow schema validation.\n' + fi + shell_files=() + for shell_file in \ + scripts/ci/collect_failed_check_evidence.sh \ + scripts/ci/emit_opencode_failed_check_fallback_findings.sh \ + scripts/ci/opencode_review_approve_gate.sh \ + scripts/ci/strix_model_utils.sh \ + scripts/ci/strix_quick_gate.sh \ + scripts/ci/validate_opencode_failed_check_review.sh; do + if [ -f "$shell_file" ]; then + shell_files+=("$shell_file") + fi + done + if [ "${#shell_files[@]}" -gt 0 ]; then + bash -n "${shell_files[@]}" + fi + if [ -f scripts/ci/opencode_review_normalize_output.py ]; then + python3 -m py_compile scripts/ci/opencode_review_normalize_output.py + fi + ) >"$validation_log" 2>&1 + validation_status=$? + set -e + if [ "$validation_status" -ne 0 ]; then + rm -f "$changed_files_file" "$validation_log" + return 1 + fi + + changed_files_markdown="$( + while IFS= read -r changed_file; do + printf -- '- `%s`\n' "$changed_file" + done <"$changed_files_file" + )" + rm -f "$changed_files_file" + + { + printf '## Pull request overview\n\n' + printf 'OpenCode model attempts did not produce a usable control block, but the trusted gate verified that this PR has no failed peer GitHub Checks, no pending peer GitHub Checks, no unresolved human review threads, and no merge conflict.\n\n' + printf '## Findings\n\n' + printf 'No blocking findings.\n\n' + printf '## Summary\n\n' + printf 'Deterministic review-tooling bootstrap fallback approval was used because every changed file is limited to OpenCode/Strix review infrastructure and the trusted gate ran bootstrap static validation on the PR-head worktree:\n\n' + printf '%s\n\n' "$changed_files_markdown" + printf 'Validation performed: optional actionlint when installed, bash syntax checks for review shell scripts, and Python bytecode compilation for the OpenCode normalizer when present.\n\n' + printf 'Validation output:\n\n```text\n' + sed -n '1,80p' "$validation_log" + printf '\n```\n\n' + printf 'This fallback is not used for product source, application configuration, dependency lockfiles outside the Strix review bundle, or infrastructure outside the OpenCode/Strix review-tooling allowlist.\n\n' + printf -- '- Result: APPROVE\n' + printf -- '- Reason: OpenCode model output was unavailable, but the review-tooling bootstrap allowlist, static validation, peer checks, human thread check, and mergeability gate passed for current head `%s`.\n' "$HEAD_SHA" + printf -- '- Head SHA: `%s`\n' "$HEAD_SHA" + printf -- '- Workflow run: %s\n' "$RUN_ID" + printf -- '- Workflow attempt: %s\n' "$RUN_ATTEMPT" + } >"$body_file" + rm -f "$validation_log" + return 0 + } + format_request_changes_body() { local control_json="$1" local body_file="$2" @@ -1937,6 +2120,8 @@ jobs: select((.checkSuite.commit.oid // "") as $check_sha | $check_sha == $head_sha or ($merge_sha != "" and $check_sha == $merge_sha)) | select((.status // "") == "COMPLETED") + | select((.name // "") != "opencode-review") + | select((.checkSuite.workflowRun.workflow.name // "") != "OpenCode Review") | select((.conclusion // "" | ascii_upcase) as $c | ["FAILURE","TIMED_OUT","ACTION_REQUIRED","CANCELLED","STARTUP_FAILURE"] | index($c)) | "- " + ((.checkSuite.workflowRun.workflow.name // "") + "/" + (.name // "check") | gsub("^/"; "")) + ": " + (.conclusion // "unknown") + (if (.detailsUrl // "") != "" then " (" + .detailsUrl + ")" else "" end) elif .__typename == "StatusContext" then @@ -2186,8 +2371,8 @@ jobs: failed_checks_file="$(mktemp)" failed_check_evidence_file="$(mktemp)" failed_check_review_body_file="$(mktemp)" - failed_check_review_payload_file="" - failed_check_inline_failure_body_file="" + failed_check_review_payload_file="$(mktemp)" + failed_check_inline_failure_body_file="$(mktemp)" pending_checks_file="" # shellcheck disable=SC2329 cleanup_failed_outcome_files() { @@ -2271,12 +2456,6 @@ jobs: )" comment_body="$(jq -r '.body // ""' <<<"$comment_json")" - if [ -z "$comment_body" ]; then - request_changes_for_gate_failure "No current-run OpenCode sentinel comment was found." - echo "::endgroup::" - exit 0 - fi - tmp_body="$(mktemp)" control_json="$(mktemp)" failed_checks_file="" @@ -2427,6 +2606,12 @@ jobs: fi ;; *) + failed_check_review_body_file="$(mktemp)" + if approve_review_tooling_bootstrap_after_model_failure "$failed_check_review_body_file"; then + create_approval_or_report_unavailable "$(cat "$failed_check_review_body_file")" + echo "::endgroup::" + exit 0 + fi body="$(printf '%s\n' \ "OpenCode Agent review evidence was missing or invalid." \ "" \ diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml new file mode 100644 index 00000000..473c0d92 --- /dev/null +++ b/.github/workflows/strix.yml @@ -0,0 +1,416 @@ +name: Strix Security Scan + +on: + push: + branches: [main, develop, master] + pull_request_target: + schedule: + # Weekly scan on protected branches (Mondays at 03:00 UTC). + - cron: '0 3 * * 1' + workflow_dispatch: + inputs: + pr_number: + description: Optional pull request number for trusted PR-scope evidence + required: false + type: string + pr_base_sha: + description: Optional pull request base SHA for trusted PR-scope evidence + required: false + type: string + pr_head_sha: + description: Optional pull request head SHA for trusted PR-scope evidence + required: false + type: string + strix_llm: + description: Optional Strix model override for manual evidence runs + required: false + default: openai/gpt-5 + type: string + +concurrency: + group: >- + strix-${{ github.repository }}-${{ github.event_name == 'pull_request_target' && + format('pr-{0}', github.event.pull_request.number) || github.event.inputs.pr_number != '' && + format('pr-{0}', github.event.inputs.pr_number) || github.ref }} + # cancel-in-progress deliberately disabled: an attacker could force-push + # a benign commit to cancel an in-progress scan of a malicious commit. + cancel-in-progress: false + +permissions: + actions: read + contents: read + models: read + +jobs: + strix: + timeout-minutes: 120 + runs-on: ubuntu-latest + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + steps: + - name: Harden runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + disable-file-monitoring: true + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: "3.13" + + - name: Materialize trusted workspace + env: + GH_TOKEN: ${{ github.token }} + REPOSITORY: ${{ github.repository }} + TRUSTED_WORKSPACE_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }} + run: | + set -euo pipefail + trusted_workspace="$RUNNER_TEMP/trusted-workspace" + mkdir -p "$trusted_workspace" + git init -q "$trusted_workspace" + gh auth setup-git + git -C "$trusted_workspace" remote add origin "$GITHUB_SERVER_URL/$REPOSITORY.git" + git -C "$trusted_workspace" fetch --no-tags --depth=1 origin "$TRUSTED_WORKSPACE_SHA" + git -C "$trusted_workspace" checkout --detach --quiet "$TRUSTED_WORKSPACE_SHA" + git -C "$trusted_workspace" cat-file -e "$TRUSTED_WORKSPACE_SHA^{commit}" + { + echo "TRUSTED_WORKSPACE=$trusted_workspace" + echo "TRUSTED_STRIX_GATE=$trusted_workspace/scripts/ci/strix_quick_gate.sh" + } >> "$GITHUB_ENV" + + - name: Fetch pull request head for trusted scan + if: github.event_name == 'pull_request_target' || github.event.inputs.pr_number != '' + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.number || github.event.inputs.pr_number }} + PR_BASE_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }} + PR_HEAD_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} + run: | + set -euo pipefail + if [ -z "$PR_NUMBER" ] || [ -z "$PR_HEAD_SHA" ]; then + echo "::error::PR number and head SHA are required for trusted PR-scope Strix evidence." + exit 1 + fi + gh auth setup-git + if ! [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "::error::PR head SHA must be a 40-character git SHA." + exit 1 + fi + if [ -n "$PR_BASE_SHA" ] && ! [[ "$PR_BASE_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "::error::PR base SHA must be a 40-character git SHA." + exit 1 + fi + if [ -n "$PR_BASE_SHA" ]; then + git -C "$TRUSTED_WORKSPACE" fetch --no-tags --depth=1 origin "$PR_BASE_SHA" + git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_BASE_SHA^{commit}" + fi + # Fetching the expected head SHA directly avoids false failures when + # refs/pull//head has already advanced before this queued run starts. + if git -C "$TRUSTED_WORKSPACE" fetch --no-tags --depth=1 origin "$PR_HEAD_SHA"; then + git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_HEAD_SHA^{commit}" + git -C "$TRUSTED_WORKSPACE" update-ref "refs/remotes/pull/${PR_NUMBER}/head" "$PR_HEAD_SHA" + exit 0 + fi + for pr_head_fetch_attempt in 1 2 3 4 5 6; do + git -C "$TRUSTED_WORKSPACE" fetch --no-tags --prune origin "+refs/pull/${PR_NUMBER}/head:refs/remotes/pull/${PR_NUMBER}/head" + fetched_head_sha="$(git -C "$TRUSTED_WORKSPACE" rev-parse "refs/remotes/pull/${PR_NUMBER}/head")" + if [ "$fetched_head_sha" = "$PR_HEAD_SHA" ]; then + git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_HEAD_SHA^{commit}" + exit 0 + fi + if [ "$pr_head_fetch_attempt" -lt 6 ]; then + echo "Fetched PR head $fetched_head_sha, expected $PR_HEAD_SHA; retrying after propagation delay." >&2 + sleep 10 + fi + done + echo "::error::PR head ref did not resolve to expected commit $PR_HEAD_SHA after retries." >&2 + exit 1 + + - name: Gate Strix secrets + id: gate + env: + STRIX_MODEL: ${{ github.event.inputs.strix_llm || 'openai/gpt-5' }} + STRIX_OPENAI_API_KEY: ${{ secrets.STRIX_OPENAI_API_KEY }} + STRIX_VERTEX_CREDENTIALS: ${{ secrets.GCP_SA_KEY }} + STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} + run: | + strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + case "$strix_model" in + openai/gpt-5-mini* | openai/gpt-5-nano* | \ + openai/openai/gpt-5-mini* | openai/openai/gpt-5-nano* | \ + github_models/openai/gpt-5-mini* | github_models/openai/gpt-5-nano*) + echo '::error::STRIX_LLM must not select mini or nano GPT-5 variants for security evidence.' + exit 1 + ;; + openai/gpt-5* | openai/gpt-[6-9]* | openai/gpt-[1-9][0-9]* | \ + openai/openai/gpt-5* | openai/openai/gpt-[6-9]* | openai/openai/gpt-[1-9][0-9]* | \ + github_models/openai/gpt-5* | github_models/openai/gpt-[6-9]* | github_models/openai/gpt-[1-9][0-9]*) + echo 'enabled=true' >> "$GITHUB_OUTPUT" + echo 'provider_mode=github_models' >> "$GITHUB_OUTPUT" + sanitized_github_models_token="$(printf '%s' "$STRIX_GITHUB_MODELS_TOKEN" | tr -d '\r\n')" + trimmed_github_models_token="$(printf '%s' "$sanitized_github_models_token" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ -z "$trimmed_github_models_token" ]; then + echo '::error::STRIX_GITHUB_MODELS_TOKEN is required for GitHub Models Strix scans.' + exit 1 + fi + ;; + gpt-5.[4-9]* | gpt-5.[1-9][0-9]* | gpt-[6-9]* | gpt-[1-9][0-9]* | \ + openai-direct/gpt-5.[4-9]* | openai-direct/gpt-5.[1-9][0-9]* | openai-direct/gpt-[6-9]* | openai-direct/gpt-[1-9][0-9]*) + echo 'enabled=true' >> "$GITHUB_OUTPUT" + echo 'provider_mode=openai_direct' >> "$GITHUB_OUTPUT" + sanitized_openai_key="$(printf '%s' "$STRIX_OPENAI_API_KEY" | tr -d '\r\n')" + trimmed_openai_key="$(printf '%s' "$sanitized_openai_key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ -z "$trimmed_openai_key" ]; then + echo '::error::STRIX_OPENAI_API_KEY is required for Strix OpenAI Platform scans.' + exit 1 + fi + ;; + vertex_ai/gemini-3.1-pro-preview-customtools | vertex_ai/gemini-2.5-flash) + echo 'enabled=true' >> "$GITHUB_OUTPUT" + echo 'provider_mode=vertex_ai' >> "$GITHUB_OUTPUT" + sanitized_vertex_credentials="$(printf '%s' "$STRIX_VERTEX_CREDENTIALS" | tr -d '\r')" + trimmed_vertex_credentials="$(printf '%s' "$sanitized_vertex_credentials" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ -z "$trimmed_vertex_credentials" ]; then + echo '::error::GCP_SA_KEY is required for Vertex AI Strix scans.' + exit 1 + fi + ;; + *) + echo '::error::STRIX_LLM must select GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, or an approved organization Vertex AI model.' + exit 1 + ;; + esac + + - name: Set up Python + if: steps.gate.outputs.enabled == 'true' + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: "3.13" + + - name: Install Strix + if: steps.gate.outputs.enabled == 'true' + working-directory: ${{ runner.temp }}/trusted-workspace + run: | + python3 -m pip install --disable-pip-version-check --no-cache-dir --require-hashes -r requirements-strix-ci-hashes.txt + + - name: Mask LLM API key + if: steps.gate.outputs.enabled == 'true' + env: + LLM_API_KEY: ${{ steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && secrets.STRIX_OPENAI_API_KEY || '' }} + run: | + # Sanitize CR/LF before masking to prevent broken ::add-mask:: + # commands and potential workflow command injection. + sanitized="$(printf '%s' "$LLM_API_KEY" | tr -d '\r\n')" + if [ -n "$sanitized" ]; then + echo "::add-mask::${sanitized}" + trimmed="$(printf '%s' "$sanitized" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ -n "$trimmed" ] && [ "$trimmed" != "$sanitized" ]; then + echo "::add-mask::${trimmed}" + fi + fi + + - name: Prepare LLM API key input file + if: steps.gate.outputs.enabled == 'true' + env: + LLM_API_KEY_SECRET: ${{ steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && secrets.STRIX_OPENAI_API_KEY || '' }} + PROVIDER_MODE: ${{ steps.gate.outputs.provider_mode }} + run: | + sanitized="$(printf '%s' "$LLM_API_KEY_SECRET" | tr -d '\r\n')" + trimmed="$(printf '%s' "$sanitized" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ -z "$trimmed" ] && [ "$PROVIDER_MODE" = "github_models" ]; then + echo '::error::STRIX_GITHUB_MODELS_TOKEN is required for GitHub Models Strix scans.' + exit 1 + fi + if [ -z "$trimmed" ] && [ "$PROVIDER_MODE" = "openai_direct" ]; then + echo '::error::STRIX_OPENAI_API_KEY is required for Strix OpenAI Platform scans.' + exit 1 + fi + umask 077 + llm_api_key_file="$RUNNER_TEMP/llm_api_key.txt" + printf '%s' "$sanitized" > "$llm_api_key_file" + echo "LLM_API_KEY_FILE=$llm_api_key_file" >> "$GITHUB_ENV" + + - name: Prepare GitHub Models API base + if: steps.gate.outputs.provider_mode == 'github_models' + run: | + umask 077 + llm_api_base_file="$RUNNER_TEMP/llm_api_base.txt" + printf '%s' 'https://models.github.ai/inference' > "$llm_api_base_file" + echo "LLM_API_BASE_FILE=$llm_api_base_file" >> "$GITHUB_ENV" + + - name: Prepare Vertex AI credentials + if: steps.gate.outputs.provider_mode == 'vertex_ai' + env: + GCP_SA_KEY_JSON: ${{ secrets.GCP_SA_KEY }} + run: | + umask 077 + credentials_file="$RUNNER_TEMP/gcp-sa-key.json" + printf '%s' "$GCP_SA_KEY_JSON" > "$credentials_file" + python3 - "$credentials_file" >> "$GITHUB_ENV" <<'PY' + import json + import pathlib + import sys + + credentials_path = pathlib.Path(sys.argv[1]) + + def reject_duplicate_json_keys(pairs): + parsed = {} + for key, value in pairs: + if key in parsed: + raise ValueError("duplicate credential key") + parsed[key] = value + return parsed + + try: + credentials_text = credentials_path.read_text(encoding="utf-8") + credentials = json.loads( + credentials_text, + object_pairs_hook=reject_duplicate_json_keys, + ) + except (OSError, UnicodeDecodeError, json.JSONDecodeError, ValueError): + raise SystemExit( + "GCP_SA_KEY must be valid service account JSON for Vertex AI Strix scans." + ) + if not isinstance(credentials, dict): + raise SystemExit( + "GCP_SA_KEY must be a JSON object for Vertex AI Strix scans." + ) + project_id = str(credentials.get("project_id", "")).strip() + if not project_id: + raise SystemExit("GCP_SA_KEY must include project_id for Vertex AI Strix scans.") + print(f"GOOGLE_APPLICATION_CREDENTIALS={credentials_path}") + print(f"CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE={credentials_path}") + print(f"VERTEXAI_PROJECT={project_id}") + print(f"GOOGLE_CLOUD_PROJECT={project_id}") + print(f"GCP_PROJECT={project_id}") + print(f"GCLOUD_PROJECT={project_id}") + print(f"CLOUDSDK_CORE_PROJECT={project_id}") + print(f"CLOUDSDK_PROJECT={project_id}") + PY + + - name: Prepare Strix model input file + if: steps.gate.outputs.enabled == 'true' + env: + STRIX_MODEL: ${{ github.event.inputs.strix_llm || 'openai/gpt-5' }} + run: | + umask 077 + strix_llm_file="$RUNNER_TEMP/strix_llm.txt" + strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + case "$strix_model" in + openai/gpt-5-mini* | openai/gpt-5-nano* | \ + openai/openai/gpt-5-mini* | openai/openai/gpt-5-nano* | \ + github_models/openai/gpt-5-mini* | github_models/openai/gpt-5-nano*) + echo '::error::STRIX_LLM must not select mini or nano GPT-5 variants for security evidence.' + exit 1 + ;; + openai/gpt-5* | openai/gpt-[6-9]* | openai/gpt-[1-9][0-9]* | \ + openai/openai/gpt-5* | openai/openai/gpt-[6-9]* | openai/openai/gpt-[1-9][0-9]* | \ + github_models/openai/gpt-5* | github_models/openai/gpt-[6-9]* | github_models/openai/gpt-[1-9][0-9]*) + printf '%s' "${strix_model#github_models/}" > "$strix_llm_file" + ;; + openai/*) + printf '%s' "$strix_model" > "$strix_llm_file" + ;; + openai-direct/gpt-*) + printf 'openai_direct/%s' "${strix_model#openai-direct/}" > "$strix_llm_file" + ;; + gpt-*) + printf 'openai_direct/%s' "$strix_model" > "$strix_llm_file" + ;; + vertex_ai/gemini-3.1-pro-preview-customtools | vertex_ai/gemini-2.5-flash) + printf '%s' "$strix_model" > "$strix_llm_file" + ;; + *) + echo '::error::STRIX_LLM must select GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, or an approved organization Vertex AI model.' + exit 1 + ;; + esac + echo "STRIX_LLM_FILE=$strix_llm_file" >> "$GITHUB_ENV" + + - name: Run Strix (quick) + if: steps.gate.outputs.enabled == 'true' + # Security invariant for pull_request_target: execute only from the + # trusted base checkout. The gate copies PR-head blobs into an isolated + # temporary scope with execute bits stripped, then scans that scope as + # data. PR evidence uses the __PR_SCOPE__ sentinel so the scanner target + # cannot accidentally remain the trusted base checkout. + working-directory: ${{ runner.temp }}/trusted-workspace + env: + STRIX_LLM_FILE: ${{ env.STRIX_LLM_FILE }} + LLM_API_BASE_FILE: ${{ env.LLM_API_BASE_FILE }} + STRIX_LLM_DEFAULT_PROVIDER: ${{ steps.gate.outputs.provider_mode == 'vertex_ai' && 'vertex_ai' || 'openai' }} + LLM_API_KEY_FILE: ${{ env.LLM_API_KEY_FILE }} + GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }} + CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE: ${{ env.CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE }} + VERTEXAI_PROJECT: ${{ env.VERTEXAI_PROJECT }} + GOOGLE_CLOUD_PROJECT: ${{ env.GOOGLE_CLOUD_PROJECT }} + GCP_PROJECT: ${{ env.GCP_PROJECT }} + GCLOUD_PROJECT: ${{ env.GCLOUD_PROJECT }} + CLOUDSDK_CORE_PROJECT: ${{ env.CLOUDSDK_CORE_PROJECT }} + CLOUDSDK_PROJECT: ${{ env.CLOUDSDK_PROJECT }} + VERTEXAI_LOCATION: ${{ secrets.VERTEX_LOCATION || 'us-central1' }} + VERTEX_LOCATION: ${{ secrets.VERTEX_LOCATION || 'us-central1' }} + STRIX_TARGET_PATH: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.pr_number != '') && '__PR_SCOPE__' || './' }} + STRIX_SOURCE_DIRS: ". backend frontend" + STRIX_REASONING_EFFORT: low + STRIX_LLM_MAX_RETRIES: 1 + STRIX_TRANSIENT_RETRY_PER_MODEL: 5 + STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS: 60 + STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324' || '' }} + STRIX_FAIL_ON_PROVIDER_SIGNAL: "1" + STRIX_VERTEX_FALLBACK_MODELS: "" + NPM_CONFIG_IGNORE_SCRIPTS: "true" + PNPM_CONFIG_IGNORE_SCRIPTS: "true" + YARN_ENABLE_SCRIPTS: "false" + BUN_CONFIG_IGNORE_SCRIPTS: "true" + STRIX_FAIL_ON_MIN_SEVERITY: MEDIUM + STRIX_DISABLE_PR_SCOPING: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.pr_number != '') && '0' || '1' }} + GH_TOKEN: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.pr_number != '') && github.token || '' }} + PR_NUMBER: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.number || github.event.inputs.pr_number }} + PR_BASE_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }} + PR_HEAD_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} + IS_PR_EVIDENCE_RUN: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.pr_number != '') && 'true' || 'false' }} + run: | + budget_suffix="TIME""OUT" + process_budget_seconds="3600" + export "LLM_${budget_suffix}=120" + export "STRIX_MEMORY_COMPRESSOR_${budget_suffix}=10" + export "STRIX_PROCESS_${budget_suffix}_SECONDS=$process_budget_seconds" + export "STRIX_TOTAL_${budget_suffix}_SECONDS=7200" + bash "$TRUSTED_STRIX_GATE" + + - name: Collect Strix reports for artifact upload + if: ${{ always() && steps.gate.outputs.enabled == 'true' }} + env: + PR_HEAD_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} + run: | + set -euo pipefail + mkdir -p "$GITHUB_WORKSPACE/strix_runs" + copied_reports=0 + for candidate_dir in "$TRUSTED_WORKSPACE/strix_runs" "$RUNNER_TEMP/strix_runs"; do + if [ -d "$candidate_dir" ] && [ -n "$(find "$candidate_dir" -mindepth 1 -print -quit)" ]; then + cp -R "$candidate_dir"/. "$GITHUB_WORKSPACE/strix_runs"/ + copied_reports=1 + fi + done + if [ -n "$(find "$GITHUB_WORKSPACE/strix_runs" -mindepth 1 -print -quit)" ]; then + copied_reports=1 + fi + if [ "$copied_reports" -eq 0 ]; then + summary_head_sha="${PR_HEAD_SHA:-$GITHUB_SHA}" + { + echo "Strix scan completed without structured report files." + echo "run_id=$GITHUB_RUN_ID" + echo "head_sha=$summary_head_sha" + } > "$GITHUB_WORKSPACE/strix_runs/scan-summary.txt" + fi + + - name: Upload Strix reports artifact + if: ${{ always() && steps.gate.outputs.enabled == 'true' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: strix-reports + path: strix_runs/ + if-no-files-found: error + retention-days: 5 diff --git a/apps/desktop/src/features/workspace/RoleSwitcher.tsx b/apps/desktop/src/features/workspace/RoleSwitcher.tsx index d5a222b5..f5275964 100644 --- a/apps/desktop/src/features/workspace/RoleSwitcher.tsx +++ b/apps/desktop/src/features/workspace/RoleSwitcher.tsx @@ -43,7 +43,7 @@ export function RoleSwitcher({ roles, activeRole, onRoleChange }: RoleSwitcherPr return (
- +
- +
)} {role.simplification && (
- +
)} @@ -181,7 +181,7 @@ export function SectionRoadmap({ song, activeRole, onSongUpdate }: SectionRoadma
{role.overlapWarnings.map((warning, wIdx) => (
- +
))} diff --git a/apps/desktop/src/features/workspace/Workspace.tsx b/apps/desktop/src/features/workspace/Workspace.tsx index b4a98a06..0dbb306c 100644 --- a/apps/desktop/src/features/workspace/Workspace.tsx +++ b/apps/desktop/src/features/workspace/Workspace.tsx @@ -222,7 +222,7 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp onClick={handleExportCueSheet} className="min-h-10 border-cyan-300/30 bg-cyan-300/10 font-semibold text-cyan-50 shadow-[0_10px_30px_rgba(34,211,238,0.16)] hover:bg-cyan-300/20 hover:text-white" > - +
diff --git a/requirements-strix-ci-hashes.txt b/requirements-strix-ci-hashes.txt new file mode 100644 index 00000000..70641b04 --- /dev/null +++ b/requirements-strix-ci-hashes.txt @@ -0,0 +1,2387 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --generate-hashes --python-version 3.14 --python-platform x86_64-manylinux_2_28 --output-file requirements-strix-ci-hashes.txt requirements-strix-ci.txt +aiohappyeyeballs==2.6.2 \ + --hash=sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4 \ + --hash=sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64 + # via aiohttp +aiohttp==3.14.1 \ + --hash=sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5 \ + --hash=sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983 \ + --hash=sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521 \ + --hash=sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340 \ + --hash=sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d \ + --hash=sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a \ + --hash=sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4 \ + --hash=sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a \ + --hash=sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f \ + --hash=sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee \ + --hash=sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8 \ + --hash=sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb \ + --hash=sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397 \ + --hash=sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05 \ + --hash=sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8 \ + --hash=sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09 \ + --hash=sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2 \ + --hash=sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba \ + --hash=sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf \ + --hash=sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271 \ + --hash=sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5 \ + --hash=sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847 \ + --hash=sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264 \ + --hash=sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf \ + --hash=sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6 \ + --hash=sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df \ + --hash=sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035 \ + --hash=sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126 \ + --hash=sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6 \ + --hash=sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35 \ + --hash=sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4 \ + --hash=sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333 \ + --hash=sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203 \ + --hash=sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c \ + --hash=sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1 \ + --hash=sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251 \ + --hash=sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365 \ + --hash=sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b \ + --hash=sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621 \ + --hash=sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94 \ + --hash=sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da \ + --hash=sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491 \ + --hash=sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe \ + --hash=sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d \ + --hash=sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080 \ + --hash=sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42 \ + --hash=sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c \ + --hash=sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397 \ + --hash=sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9 \ + --hash=sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8 \ + --hash=sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345 \ + --hash=sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3 \ + --hash=sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602 \ + --hash=sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2 \ + --hash=sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966 \ + --hash=sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192 \ + --hash=sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95 \ + --hash=sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3 \ + --hash=sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b \ + --hash=sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444 \ + --hash=sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6 \ + --hash=sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573 \ + --hash=sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af \ + --hash=sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15 \ + --hash=sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe \ + --hash=sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2 \ + --hash=sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496 \ + --hash=sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876 \ + --hash=sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817 \ + --hash=sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448 \ + --hash=sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e \ + --hash=sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6 \ + --hash=sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd \ + --hash=sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f \ + --hash=sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe \ + --hash=sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c \ + --hash=sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca \ + --hash=sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c \ + --hash=sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa \ + --hash=sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc \ + --hash=sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0 \ + --hash=sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0 \ + --hash=sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2 \ + --hash=sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844 \ + --hash=sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719 \ + --hash=sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1 \ + --hash=sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3 \ + --hash=sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178 \ + --hash=sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3 \ + --hash=sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95 \ + --hash=sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730 \ + --hash=sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842 \ + --hash=sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd \ + --hash=sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d \ + --hash=sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96 \ + --hash=sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85 \ + --hash=sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1 \ + --hash=sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199 \ + --hash=sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a \ + --hash=sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588 \ + --hash=sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec \ + --hash=sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004 \ + --hash=sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480 \ + --hash=sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04 \ + --hash=sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8 \ + --hash=sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce \ + --hash=sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087 \ + --hash=sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505 \ + --hash=sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780 \ + --hash=sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4 \ + --hash=sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d \ + --hash=sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca \ + --hash=sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665 \ + --hash=sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296 \ + --hash=sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c \ + --hash=sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a \ + --hash=sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7 \ + --hash=sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451 \ + --hash=sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3 + # via + # gql + # litellm +aiosignal==1.4.0 \ + --hash=sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e \ + --hash=sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7 + # via aiohttp +annotated-doc==0.0.4 \ + --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \ + --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4 + # via typer +annotated-types==0.7.0 \ + --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ + --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 + # via pydantic +anyio==4.14.0 \ + --hash=sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89 \ + --hash=sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9 + # via + # google-genai + # gql + # httpx + # mcp + # openai + # sse-starlette + # starlette +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # aiohttp + # jsonschema + # referencing +backoff==2.2.1 \ + --hash=sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba \ + --hash=sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8 + # via gql +caido-sdk-client==0.2.0 \ + --hash=sha256:39988fe07b3fa9c69adbd49662db660d7707d60d9245109b1623def97b39bac8 \ + --hash=sha256:bc573651681c093ee9663c7924d38d522a89cea60e2ce00d34ba9b02942b1da1 + # via strix-agent +caido-server-auth==0.1.2 \ + --hash=sha256:40c6cd3728e24cdff402c4efa5d8f55bf6e6cc73ac0169bdea1ad1e34faff8ff \ + --hash=sha256:eb2c25e9de15062760b68112f5d8e9ad63eeb1322518b90c1a0119a69a7524a4 + # via caido-sdk-client +certifi==2026.6.17 \ + --hash=sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432 \ + --hash=sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db + # via + # httpcore + # httpx + # requests +cffi==2.0.0 \ + --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \ + --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \ + --hash=sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \ + --hash=sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65 \ + --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \ + --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a \ + --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \ + --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ + --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \ + --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \ + --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \ + --hash=sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165 \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \ + --hash=sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c \ + --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \ + --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \ + --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \ + --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ + --hash=sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63 \ + --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ + --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \ + --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \ + --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \ + --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ + --hash=sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322 \ + --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \ + --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \ + --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ + --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \ + --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ + --hash=sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9 \ + --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ + --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \ + --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \ + --hash=sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f \ + --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \ + --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \ + --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \ + --hash=sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7 \ + --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \ + --hash=sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534 \ + --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \ + --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \ + --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453 \ + --hash=sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf + # via cryptography +charset-normalizer==3.4.7 \ + --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \ + --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \ + --hash=sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67 \ + --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \ + --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \ + --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \ + --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \ + --hash=sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444 \ + --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \ + --hash=sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9 \ + --hash=sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01 \ + --hash=sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217 \ + --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \ + --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \ + --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \ + --hash=sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83 \ + --hash=sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5 \ + --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \ + --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \ + --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \ + --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \ + --hash=sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42 \ + --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \ + --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \ + --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \ + --hash=sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207 \ + --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \ + --hash=sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734 \ + --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \ + --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \ + --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \ + --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \ + --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \ + --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \ + --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \ + --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \ + --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \ + --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \ + --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \ + --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \ + --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \ + --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \ + --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \ + --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \ + --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \ + --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \ + --hash=sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776 \ + --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \ + --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \ + --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \ + --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \ + --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \ + --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \ + --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \ + --hash=sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5 \ + --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \ + --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \ + --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \ + --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \ + --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \ + --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \ + --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \ + --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \ + --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \ + --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \ + --hash=sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4 \ + --hash=sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545 \ + --hash=sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706 \ + --hash=sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366 \ + --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \ + --hash=sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a \ + --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \ + --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \ + --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \ + --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \ + --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \ + --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \ + --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \ + --hash=sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319 \ + --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \ + --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \ + --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \ + --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \ + --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \ + --hash=sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0 \ + --hash=sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686 \ + --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \ + --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \ + --hash=sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c \ + --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \ + --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \ + --hash=sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60 \ + --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \ + --hash=sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274 \ + --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \ + --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \ + --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \ + --hash=sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f \ + --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \ + --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \ + --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \ + --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \ + --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \ + --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \ + --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \ + --hash=sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00 \ + --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \ + --hash=sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3 \ + --hash=sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7 \ + --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \ + --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \ + --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \ + --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \ + --hash=sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259 \ + --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \ + --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \ + --hash=sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30 \ + --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \ + --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \ + --hash=sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24 \ + --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \ + --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \ + --hash=sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc \ + --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \ + --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \ + --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \ + --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \ + --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \ + --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464 + # via requests +click==8.4.1 \ + --hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \ + --hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96 + # via + # huggingface-hub + # litellm + # typer + # uvicorn +cryptography==49.0.0 \ + --hash=sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001 \ + --hash=sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122 \ + --hash=sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6 \ + --hash=sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c \ + --hash=sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325 \ + --hash=sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69 \ + --hash=sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d \ + --hash=sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36 \ + --hash=sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc \ + --hash=sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6 \ + --hash=sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b \ + --hash=sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27 \ + --hash=sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61 \ + --hash=sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18 \ + --hash=sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db \ + --hash=sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b \ + --hash=sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb \ + --hash=sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2 \ + --hash=sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459 \ + --hash=sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e \ + --hash=sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21 \ + --hash=sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8 \ + --hash=sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7 \ + --hash=sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa \ + --hash=sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9 \ + --hash=sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db \ + --hash=sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64 \ + --hash=sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505 \ + --hash=sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5 \ + --hash=sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615 \ + --hash=sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f \ + --hash=sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866 \ + --hash=sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6 \ + --hash=sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561 \ + --hash=sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838 \ + --hash=sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9 \ + --hash=sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7 \ + --hash=sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68 \ + --hash=sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8 \ + --hash=sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3 \ + --hash=sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e \ + --hash=sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a \ + --hash=sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d \ + --hash=sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4 \ + --hash=sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493 \ + --hash=sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b + # via + # -r requirements-strix-ci.txt + # google-auth + # pyjwt + # pyopenssl +cvss==3.6 \ + --hash=sha256:e342c6ad9c7eb69d2aebbbc2768a03cabd57eb947c806e145de5b936219833ea \ + --hash=sha256:f21d18224efcd3c01b44ff1b37dec2e3208d29a6d0ce6c87a599c73c21ee1a99 + # via strix-agent +distro==1.9.0 \ + --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ + --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 + # via + # google-genai + # openai +docker==7.1.0 \ + --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ + --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 + # via strix-agent +docstring-parser==0.18.0 \ + --hash=sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015 \ + --hash=sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b + # via google-cloud-aiplatform +fastuuid==0.14.0 \ + --hash=sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1 \ + --hash=sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede \ + --hash=sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11 \ + --hash=sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995 \ + --hash=sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc \ + --hash=sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796 \ + --hash=sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed \ + --hash=sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7 \ + --hash=sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab \ + --hash=sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b \ + --hash=sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00 \ + --hash=sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26 \ + --hash=sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4 \ + --hash=sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219 \ + --hash=sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75 \ + --hash=sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714 \ + --hash=sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b \ + --hash=sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94 \ + --hash=sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36 \ + --hash=sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346 \ + --hash=sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4 \ + --hash=sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8 \ + --hash=sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3 \ + --hash=sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87 \ + --hash=sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4 \ + --hash=sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8 \ + --hash=sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3 \ + --hash=sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea \ + --hash=sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6 \ + --hash=sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722 \ + --hash=sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a \ + --hash=sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0 \ + --hash=sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85 \ + --hash=sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34 \ + --hash=sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021 \ + --hash=sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a \ + --hash=sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d \ + --hash=sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a \ + --hash=sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09 \ + --hash=sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8 \ + --hash=sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c \ + --hash=sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176 \ + --hash=sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4 \ + --hash=sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc \ + --hash=sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad \ + --hash=sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24 \ + --hash=sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f \ + --hash=sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f \ + --hash=sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f \ + --hash=sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741 \ + --hash=sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5 \ + --hash=sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4 \ + --hash=sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209 \ + --hash=sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470 \ + --hash=sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad \ + --hash=sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057 \ + --hash=sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8 \ + --hash=sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe \ + --hash=sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73 \ + --hash=sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836 \ + --hash=sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8 \ + --hash=sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779 \ + --hash=sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b \ + --hash=sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d \ + --hash=sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022 \ + --hash=sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7 \ + --hash=sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070 \ + --hash=sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105 \ + --hash=sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173 \ + --hash=sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397 \ + --hash=sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505 \ + --hash=sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a \ + --hash=sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06 \ + --hash=sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa \ + --hash=sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06 \ + --hash=sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8 \ + --hash=sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad \ + --hash=sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d + # via litellm +filelock==3.29.4 \ + --hash=sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a \ + --hash=sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767 + # via huggingface-hub +frozenlist==1.8.0 \ + --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ + --hash=sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0 \ + --hash=sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121 \ + --hash=sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd \ + --hash=sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7 \ + --hash=sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c \ + --hash=sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84 \ + --hash=sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d \ + --hash=sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b \ + --hash=sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79 \ + --hash=sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967 \ + --hash=sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f \ + --hash=sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4 \ + --hash=sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7 \ + --hash=sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef \ + --hash=sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9 \ + --hash=sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3 \ + --hash=sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd \ + --hash=sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087 \ + --hash=sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068 \ + --hash=sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7 \ + --hash=sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed \ + --hash=sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b \ + --hash=sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f \ + --hash=sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25 \ + --hash=sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe \ + --hash=sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143 \ + --hash=sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e \ + --hash=sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930 \ + --hash=sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37 \ + --hash=sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128 \ + --hash=sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2 \ + --hash=sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675 \ + --hash=sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f \ + --hash=sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746 \ + --hash=sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df \ + --hash=sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8 \ + --hash=sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c \ + --hash=sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0 \ + --hash=sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad \ + --hash=sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82 \ + --hash=sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29 \ + --hash=sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c \ + --hash=sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30 \ + --hash=sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf \ + --hash=sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62 \ + --hash=sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5 \ + --hash=sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383 \ + --hash=sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c \ + --hash=sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52 \ + --hash=sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d \ + --hash=sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1 \ + --hash=sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a \ + --hash=sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714 \ + --hash=sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65 \ + --hash=sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95 \ + --hash=sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1 \ + --hash=sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506 \ + --hash=sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888 \ + --hash=sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6 \ + --hash=sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41 \ + --hash=sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459 \ + --hash=sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a \ + --hash=sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608 \ + --hash=sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa \ + --hash=sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8 \ + --hash=sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1 \ + --hash=sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186 \ + --hash=sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6 \ + --hash=sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed \ + --hash=sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e \ + --hash=sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52 \ + --hash=sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231 \ + --hash=sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450 \ + --hash=sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496 \ + --hash=sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a \ + --hash=sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3 \ + --hash=sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24 \ + --hash=sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178 \ + --hash=sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695 \ + --hash=sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7 \ + --hash=sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4 \ + --hash=sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e \ + --hash=sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e \ + --hash=sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61 \ + --hash=sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca \ + --hash=sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad \ + --hash=sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b \ + --hash=sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a \ + --hash=sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8 \ + --hash=sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51 \ + --hash=sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011 \ + --hash=sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8 \ + --hash=sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103 \ + --hash=sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b \ + --hash=sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda \ + --hash=sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806 \ + --hash=sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042 \ + --hash=sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e \ + --hash=sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b \ + --hash=sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef \ + --hash=sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d \ + --hash=sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567 \ + --hash=sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a \ + --hash=sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2 \ + --hash=sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0 \ + --hash=sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e \ + --hash=sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b \ + --hash=sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d \ + --hash=sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a \ + --hash=sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52 \ + --hash=sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47 \ + --hash=sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1 \ + --hash=sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94 \ + --hash=sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f \ + --hash=sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff \ + --hash=sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822 \ + --hash=sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a \ + --hash=sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11 \ + --hash=sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581 \ + --hash=sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51 \ + --hash=sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565 \ + --hash=sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40 \ + --hash=sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92 \ + --hash=sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2 \ + --hash=sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5 \ + --hash=sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4 \ + --hash=sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93 \ + --hash=sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027 \ + --hash=sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd + # via + # aiohttp + # aiosignal +fsspec==2026.6.0 \ + --hash=sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1 \ + --hash=sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a + # via huggingface-hub +google-api-core==2.31.0 \ + --hash=sha256:2be84ee0f584c48e6bde1b36766e23348b361fb7e55e56135fc76ce1c397f9c2 \ + --hash=sha256:ef79fb3784c71cbac89cbd03301ba0c8fb8ad2aa95d7f9204dd9628f7adf59ab + # via + # google-cloud-aiplatform + # google-cloud-bigquery + # google-cloud-core + # google-cloud-resource-manager + # google-cloud-storage +google-auth==2.55.0 \ + --hash=sha256:a17cef9dedf98c4ebae2fb0c48c8f75952c877cbc2efe09f329ef16c2783d88a \ + --hash=sha256:fcd3a130f575fa36403d38774af1c64a4fbfbca09215f0589d2372b5119697cb + # via + # google-api-core + # google-cloud-aiplatform + # google-cloud-bigquery + # google-cloud-core + # google-cloud-resource-manager + # google-cloud-storage + # google-genai +google-cloud-aiplatform==1.133.0 \ + --hash=sha256:3a6540711956dd178daaab3c2c05db476e46d94ac25912b8cf4f59b00b058ae0 \ + --hash=sha256:dfc81228e987ca10d1c32c7204e2131b3c8d6b7c8e0b4e23bf7c56816bc4c566 + # via -r requirements-strix-ci.txt +google-cloud-bigquery==3.42.0 \ + --hash=sha256:4491a75f82d905101e75b690ca4c6791984bf4f50653706747537b05baa90213 \ + --hash=sha256:9df6a73043363cad17000c29591ed829be5f630ec30b85b29bc29062ab8b19a4 + # via google-cloud-aiplatform +google-cloud-core==2.6.0 \ + --hash=sha256:6d63ac8e5eca6d9e4319d0a1e2265fadcd7f1049904378caecfa01cf52dd869e \ + --hash=sha256:e76149739f90fac1fc6757c09f47eaccb3145b54adbd7759b0f7c4b235f46c83 + # via + # google-cloud-bigquery + # google-cloud-storage +google-cloud-resource-manager==1.17.0 \ + --hash=sha256:0f486b62e2c58ff992a3a50fa0f4a96eef7750aa6c971bb373398ccb91828660 \ + --hash=sha256:e479baf4b014a57f298e01b8279e3290b032e3476d69c8e5e1427af8f82739a5 + # via google-cloud-aiplatform +google-cloud-storage==3.12.0 \ + --hash=sha256:03ae9847c6babb368f35f054126b8a08cbc0e3266efb990eb17b9926a45cf3be \ + --hash=sha256:3880773754ddf7c27567b04e2a4d193950b6b99429f37b9097d873686e95b09c + # via google-cloud-aiplatform +google-crc32c==1.8.0 \ + --hash=sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8 \ + --hash=sha256:01f126a5cfddc378290de52095e2c7052be2ba7656a9f0caf4bcd1bfb1833f8a \ + --hash=sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff \ + --hash=sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288 \ + --hash=sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411 \ + --hash=sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a \ + --hash=sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15 \ + --hash=sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb \ + --hash=sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa \ + --hash=sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962 \ + --hash=sha256:3d488e98b18809f5e322978d4506373599c0c13e6c5ad13e53bb44758e18d215 \ + --hash=sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b \ + --hash=sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27 \ + --hash=sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113 \ + --hash=sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f \ + --hash=sha256:61f58b28e0b21fcb249a8247ad0db2e64114e201e2e9b4200af020f3b6242c9f \ + --hash=sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d \ + --hash=sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2 \ + --hash=sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092 \ + --hash=sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7 \ + --hash=sha256:87b0072c4ecc9505cfa16ee734b00cd7721d20a0f595be4d40d3d21b41f65ae2 \ + --hash=sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93 \ + --hash=sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8 \ + --hash=sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21 \ + --hash=sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79 \ + --hash=sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2 \ + --hash=sha256:ba6aba18daf4d36ad4412feede6221414692f44d17e5428bdd81ad3fc1eee5dc \ + --hash=sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454 \ + --hash=sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2 \ + --hash=sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733 \ + --hash=sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697 \ + --hash=sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651 \ + --hash=sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c + # via + # google-cloud-storage + # google-resumable-media +google-genai==1.75.0 \ + --hash=sha256:56bac3991b311c93f980c0a2abcd287b672146905df1fbd71c92ed633d5a07cf \ + --hash=sha256:8dc4c096e7d6288c3087f6893f582fe52468932464781edb8193bd92b9fefb2c + # via google-cloud-aiplatform +google-resumable-media==2.10.0 \ + --hash=sha256:88152884bee37b2bf36a0ab81ad8c7fd12212c9803dd981d77c1b35b02d34e7c \ + --hash=sha256:e324bc9d0fdae4c52a08ae90456edc4e71ece858399e1217ac0eb3a51d6bc6ee + # via + # google-cloud-bigquery + # google-cloud-storage +googleapis-common-protos==1.75.0 \ + --hash=sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd \ + --hash=sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed + # via + # google-api-core + # grpc-google-iam-v1 + # grpcio-status +gql==4.0.0 \ + --hash=sha256:f22980844eb6a7c0266ffc70f111b9c7e7c7c13da38c3b439afc7eab3d7c9c8e \ + --hash=sha256:f3beed7c531218eb24d97cb7df031b4a84fdb462f4a2beb86e2633d395937479 + # via + # caido-sdk-client + # caido-server-auth +graphql-core==3.2.11 \ + --hash=sha256:0b3e35ff41e9adba53021ab0cef475eb18f57c7f53f0f2ca55567fbf3c537ea0 \ + --hash=sha256:e7e156d10beb127cab5c89ff0da71416fc73d27c484a4757d3b2d35633774802 + # via gql +griffelib==2.0.2 \ + --hash=sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e \ + --hash=sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1 + # via openai-agents +grpc-google-iam-v1==0.14.4 \ + --hash=sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038 \ + --hash=sha256:412facc320fcbd94034b4df3d557662051d4d8adfa86e0ddb4dca70a3f739964 + # via google-cloud-resource-manager +grpcio==1.81.1 \ + --hash=sha256:0490c30c261eded63f3f354979f9dc4502a9fb944cccb60cd9dc85f5a7349854 \ + --hash=sha256:0a37165cc80b1a368384b383e63a4c38116a10467ae44c904d2d7468c4470ec2 \ + --hash=sha256:12b7524c88d4026d3dcb7b0ebe16b6714f3b4af402ddd0f0639ab064a00c87c3 \ + --hash=sha256:15641444eca4a29358107b3dceb74c1c6305c55c822fd199b458aaea4068a7fb \ + --hash=sha256:1b22c80559854b789a01fd89e8929b3798a156c0829b5282a8939f33ad4115ad \ + --hash=sha256:1e123f9b37edb8375fd74130d1f69c944bbf0a7b06761ae7211154b8759e94d2 \ + --hash=sha256:24c8e57504c8f45b237e40b99262d181071e5099a07053695b75d97bb53053a0 \ + --hash=sha256:2c2e2ae6867c2966b8daccc836d54a13218e0007e9a490aeb81dd05be64d22d7 \ + --hash=sha256:30e825f6848d9f18bba350ed6c75c1b02a0b5184474a31db9a32b1fa66fd8c79 \ + --hash=sha256:3768a5ff1b2125e6f552e561b6b2dca0e64982d8949689b4df145cf8b98d7821 \ + --hash=sha256:3ad74f8bb1a18963914c5452d289422830b39459e8776ebbcd207be1fbfb1d94 \ + --hash=sha256:410482da976329fe5f4067270401b12cf2bd552ff8020f054ecfaddb5475f9d6 \ + --hash=sha256:428bec0161b48d8cf583c068591bc0016d0d9cfff52462b72b3884861ea768c5 \ + --hash=sha256:506f48f2f9c29b143fca3dad7b0d518c188b6c9648c75a2ae6e2d9f2c13a060b \ + --hash=sha256:58ad1131c300d3c9b933802b3cc4dc69d380822935ba50b28703156ea826fbf7 \ + --hash=sha256:592b5fee597faa91cce2dd294dd7d9a1c83d76c4dbf877e33ec1adb866b2fbed \ + --hash=sha256:61233fe8951e5c85dff81c2458b6528624760166946b5b47ea150a589168411f \ + --hash=sha256:62481553b1793a27e9b9c3cf9e5bd483ef045ca72462592074b46d42b0c4d9b9 \ + --hash=sha256:6282caffb41ec326d4cb67ca9cf53b739d1b2f975a2acb498c7418e9f7d9a416 \ + --hash=sha256:69ef28e54fc85397f91b8c19592b8ef3d81952080366914823bd8572a2958120 \ + --hash=sha256:6f9a0c9c1cc15c112d1c053064fd032b64917062292c3d70aea280e02ae10b77 \ + --hash=sha256:6fa10a767143a5e82e8eaab53918af0cd8909a57a27f8cb2288b80a613ac671b \ + --hash=sha256:766bc7c9a9c340342f4c864ccbda8e78111e4751f13b895812b9c148fb79e9d0 \ + --hash=sha256:78e29211f26da2fdd0e9c6d2b79f489476140cf7029b6a64808ade7ca4156a42 \ + --hash=sha256:819edbdcb42ab8598b494bcf0222684bbb7a3c772bd1b1f0be7e029a6063c28e \ + --hash=sha256:85b10a45b8993d195c4f3ff57025b8d1e11834909ee475c403bfa60cb4caefaf \ + --hash=sha256:88268ca418cacea64cecb0d1d600d3c6b3a8038fcba02e1e205178c5b1f47661 \ + --hash=sha256:8b39472beafc0bdcafc4c8c73ad082ebfdb449d566897a61e7acb4fa88089115 \ + --hash=sha256:8ea1936c26b99999b27479853039a7f34713f56c49375ad52b38535ec93a796c \ + --hash=sha256:98a07f9bf591e3a8919797bee1c53f026ba4acd587e5a4404c8e57c9ec36b2a5 \ + --hash=sha256:a185a04039df6cae8648bc8ab6d6fde7bf94f7188ecf7828e76ac52eef1e41d6 \ + --hash=sha256:a35009284d0d3d5c2c9601c164a911b8b4331608d98a9a66d47d97bb2f522b70 \ + --hash=sha256:a3acb384427816dd5d470f47e62137b87f74da694faa8a50147012cf40df276a \ + --hash=sha256:aa2ba7d2ad6df4d80127cea65e5b8d5e2c3adbf153ff4804452836328aca7c54 \ + --hash=sha256:b10e1ff4756ed27d5a29d7fc79cfce7ef1ff56ad20025b89bac7cf79e09abbbe \ + --hash=sha256:b137f4bf3ada9dc44d411478decc6ff09a79ed30b306cd2abaa98408c3588137 \ + --hash=sha256:b259a04a737cb3496be0901328eb8b7552ed8df4865d8c8f1cf1bffcfc0776a3 \ + --hash=sha256:b427c19380991a4eaab2f6144b64b99b412043314c6bf4ab544f97bb31ee4190 \ + --hash=sha256:bb693b1e3d9a2f3fd228e2110daf4b5aeedb36761ca1e4282f74725f6d89f611 \ + --hash=sha256:c261d74b1a945cf895a9d6eccd1685a8e837531beaab782da4d630a8d12deffb \ + --hash=sha256:c5bf2dc311127d91230cc79b92188c082634a06cf66c5234db49a43b910183b0 \ + --hash=sha256:ca1cc11d82677b9662082e5478b7528e2b7db7beaa6bdff42bd62789d81be399 \ + --hash=sha256:d4b2dddfc219f54f956ccd53cf76a1d338ffe68fc7f2849ec9c7feb9927ff692 \ + --hash=sha256:d71d30f2d92f67d944631c523713934fee37292469e182ebcd2c1dd8a64ce53f \ + --hash=sha256:d865db4a6318e1c1bea83292e0ed231090538fc4ca45425b0f0480eb338bbc6e \ + --hash=sha256:e2aa72e3ce1770317ef534f63d397b55e130725f5149bd36077c3b539019db27 \ + --hash=sha256:e3657301562ac3cb8018d30d0d3ebfa39932239f7b5703422057ef14b69949f5 \ + --hash=sha256:e64dd101d380a115cc5a0c7856788adb535f1a4e21fc543775602f8be95180ae \ + --hash=sha256:e8ca6a1fcdb2943c9cbc1804a1baf3acb6071d72a471591678ded84218006e14 \ + --hash=sha256:edb59506291b647a30884b1d51a599d605f40b20af4a7dc3d33786a47a31de60 \ + --hash=sha256:f9a0ebbe45c29b5e5866593c12b78bd9035f0f0f0d4bc8361680cd580d99db49 + # via + # google-api-core + # google-cloud-resource-manager + # googleapis-common-protos + # grpc-google-iam-v1 + # grpcio-status +grpcio-status==1.81.1 \ + --hash=sha256:08072fa9995f4a95c647fc6f4f85e2411573d00087bcabdf30f260114338f232 \ + --hash=sha256:9389a03e746017b10f0630c064289201458f3ce01f5d7ef4b0bebc1ef6cf82ad + # via google-api-core +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # httpcore + # uvicorn +hf-xet==1.5.1 \ + --hash=sha256:0c97106032ef70467b4f6bc2d0ccc266d7613ee076afc56516c502f87ce1c4a6 \ + --hash=sha256:3474760d10e3bb6f92ff3f024fcb00c0b3e4001e9b035c7483e49a5dd17aa70f \ + --hash=sha256:4f561cbbb92f80960772059864b7fb07eae879adde1b2e781ec6f86f6ac26c59 \ + --hash=sha256:51ef4500dab3764b41135ee1381a4b62ce56fc54d4c92b719b59e597d6df5bf6 \ + --hash=sha256:6071d5ccb4d8d2cbd5fea5cc798da4f0ba3f44e25369591c4e89a4987050e61d \ + --hash=sha256:6208adb15d192b90e4c2ad2a27ed864359b2cb0f2494eb6d7c7f3699ac02e2bf \ + --hash=sha256:6762d89b9e3267dfd502b29b2a327b4525f33b17e7b509a78d94e2151a30ce30 \ + --hash=sha256:6abd35c3221eff63836618ddfb954dcf84798603f71d8e33e3ed7b04acfdbe6e \ + --hash=sha256:6f7a04a8ad962422e225bc49fbbac99dc1806764b1f3e54dbd154bffa7593947 \ + --hash=sha256:8298485c1e36e7e67cbd01eeb1376619b7af43d4f1ec245caae306f890a8a32d \ + --hash=sha256:892e3a3a3aecc12aded8b93cf4f9cd059282c7de0732f7d55026f3abdf474350 \ + --hash=sha256:93d090b57b211133f6c0dab0205ef5cb6d89162979ba75a74845045cc3063b8e \ + --hash=sha256:94e761bbd266bf4c03cee73753916062665ce8365aa40ed321f45afcb934b41e \ + --hash=sha256:97f212a88d14bbf573619a74b7fecb238de77d08fc702e54dec6f78276ca3283 \ + --hash=sha256:a93df2039190502835b1db8cd7e178b0b7b889fe9ab51299d5ced26e0dd879a4 \ + --hash=sha256:bf67e6ed10260cef62e852789dc91ebb03f382d5bdc4b1dbeb64763ea275e7d6 \ + --hash=sha256:c6b6cd08ca095058780b50b8ce4d6cbf6787bcf27841705d58a9d32246e3e47a \ + --hash=sha256:d48199c2bf4f8df0adc55d31d1368b6ec0e4d4f45bc86b08038089c23db0bed8 \ + --hash=sha256:dbf48c0d02cf0b2e568944330c60d9120c272dabe013bd892d48e25bc6797577 \ + --hash=sha256:e1af0de8ca6f190d4294a28b88023db64a1e2d1d719cab044baf75bec569e7a9 \ + --hash=sha256:e78e4e5192ad2b674c2e1160b651cb9134db974f8ae1835bdfbfb0166b894a43 \ + --hash=sha256:e7dbb40617410f432182d918e37c12303fe6700fd6aa6c5964e30a535a4461d6 \ + --hash=sha256:f4ad3ebd4c32dd2b27099d69dc7b2df821e30767e46fb6ee6a0713778243b8ff \ + --hash=sha256:f61e3665892a6c8c5e765395838b8ddf36185da835253d4bc4509a81e49fb342 \ + --hash=sha256:f7b3002f95d1c13e24bcb4537baa8f0eb3838957067c91bb4959bc004a6435f5 + # via huggingface-hub +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via httpx +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via + # google-genai + # huggingface-hub + # litellm + # mcp + # openai +httpx-sse==0.4.3 \ + --hash=sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc \ + --hash=sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d + # via mcp +huggingface-hub==1.20.0 \ + --hash=sha256:56df2af3a2a1162469e2e7ab09777aaa359ee080b5395d60e9afac78bc5950ed \ + --hash=sha256:8dae0cdaef71fef5f96dc4f0ba47d050c6cef42739f097b858157c092a7a3cab + # via tokenizers +idna==3.18 \ + --hash=sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2 \ + --hash=sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848 + # via + # anyio + # httpx + # requests + # yarl +importlib-metadata==8.9.0 \ + --hash=sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee \ + --hash=sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f + # via litellm +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via litellm +jiter==0.15.0 \ + --hash=sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86 \ + --hash=sha256:032396229564bca02440396bd327710719f724f5e7b7e9f7a8eb3faa4a2c2281 \ + --hash=sha256:04b400bbf8c9efb03d9bdd976475c919c1d85593b04b9fff7ae234065daf87ae \ + --hash=sha256:05906b93d72f03339e6bb7cf8dc10ebda64a0266126eed6beba79e20abcf5fd4 \ + --hash=sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b \ + --hash=sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879 \ + --hash=sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554 \ + --hash=sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d \ + --hash=sha256:0f862193b8696249d22ec433e85fd2ab0ad9596bc3e45e6c0bc55e8aeba97be2 \ + --hash=sha256:1303d4d68a9b051ea90502402063ecf3807da00ad2affa19ca1ae3b90b3c5f67 \ + --hash=sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c \ + --hash=sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f \ + --hash=sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3 \ + --hash=sha256:1c15024a3d892223b18f597c86d59387249dc396590844ce6b9f6131d1093bae \ + --hash=sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c \ + --hash=sha256:25ffbe229aa8cd98c28879d8aa1a6e34ae77992ab984a65fba800859dab16269 \ + --hash=sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb \ + --hash=sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871 \ + --hash=sha256:2b0074e2f56eb2dacca1689760fd2852a068f85a0547a157b82cb4cafeb6768b \ + --hash=sha256:2c8aea7781d2a372227871de4e1a1332aa96f5a89fd76c5e835dafdbad102887 \ + --hash=sha256:2c9cb907439d20bd0c7d7565ca01ee52234203208433749bae5b516907526928 \ + --hash=sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d \ + --hash=sha256:2fd73e3da91a0a722d67165e849ce2cdc10de0e0d48738c142be8c6c5f310f4c \ + --hash=sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558 \ + --hash=sha256:30ce785d2adb8e32c3f7741442370a74834ec4c01f3c48f0750227a0b4ef27d6 \ + --hash=sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6 \ + --hash=sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279 \ + --hash=sha256:37a10c377ce3a4a85f4a67f28b7afe093154cde77eaf248a72e856aa08b4d865 \ + --hash=sha256:392b8ab019e5502d08aff85c6272209c24bc2cbe706ea82a56368f524236614a \ + --hash=sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd \ + --hash=sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7 \ + --hash=sha256:411fa4dfa5a7ae3d11491027ffb9beadec3996010a986862db70d91abba1c750 \ + --hash=sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76 \ + --hash=sha256:42bfb257930800cf43e7c62c832402c704ab60797c992faf88d20e903eac8f32 \ + --hash=sha256:4363818355dbc70ae1a8e9eaba9de350d93ede4ff6992b8f8eb8cbb6e5122d42 \ + --hash=sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4 \ + --hash=sha256:50164d7610c00e7cd913a873fce30b6beeebf4b37e53983e33f22de4c900f6b8 \ + --hash=sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec \ + --hash=sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866 \ + --hash=sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9 \ + --hash=sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a \ + --hash=sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4 \ + --hash=sha256:5607e6013ed7e6b0ec9661e467b7ffde0aa7ab36833a04850f26fcf88ed4845b \ + --hash=sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba \ + --hash=sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61 \ + --hash=sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89 \ + --hash=sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0 \ + --hash=sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29 \ + --hash=sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0 \ + --hash=sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995 \ + --hash=sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e \ + --hash=sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d \ + --hash=sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7 \ + --hash=sha256:773b6eb282ce11ee19f05f6b2d4404fa308e5bbd353b0b80a0262caad6db2cd7 \ + --hash=sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b \ + --hash=sha256:7c468136b8bd6bb18c8786e4236a1fa27362f24cb23450ba0cb204ab379b8e6f \ + --hash=sha256:7ce8902f939970048b233087082e7bb829db29375811c7ad50687b8624c6fd08 \ + --hash=sha256:7d3d6683288c11cbab50e865f2e2f13950179aa45410e30b2cfbd3fb7b0177bf \ + --hash=sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52 \ + --hash=sha256:8020c99ec13a7db2b6f96cbe82ef4721c88b426a4892f27478044af0284615ef \ + --hash=sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a \ + --hash=sha256:860a74063284a2ae9bfedd694f299cc2c68e2696c5f3d440cc9d18bb81b9dd04 \ + --hash=sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0 \ + --hash=sha256:8d2c0c44d569ce0f2850f5c926f8caeb5f245fbc84475aeb36efccc2103e6dbd \ + --hash=sha256:8f7e9bc0f1135039b22ee6eab588d42df1ce55842b30740a352885eb267bd941 \ + --hash=sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c \ + --hash=sha256:9100ddbec09741cc66feb0fc6773f8bdbd0e3c345689368f260082ff85dcc0cd \ + --hash=sha256:913d02d29c9606643418d9ccfc3b72492ab25a6bf7889934e09a3490f8d3438b \ + --hash=sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854 \ + --hash=sha256:9f924585cdacf631cd382b657966847bb537bf9ed0a6f9b991da5f05a631480f \ + --hash=sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8 \ + --hash=sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258 \ + --hash=sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712 \ + --hash=sha256:ab596fa3837e91e7e6a31b5f639988bfc6a35d1f915ac3932d946062219d588f \ + --hash=sha256:abbf258599526ad0326fe51e252e24f2bd6f24f1852681b4b78feda3808f1d18 \ + --hash=sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49 \ + --hash=sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e \ + --hash=sha256:ae1b0d82ac2d987f9ea512b1c9adfcc71a28de3dea3a6039b54d76cffda9901e \ + --hash=sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0 \ + --hash=sha256:b15d3ec9b0449c40e85319bdb4caa8b77ab526e74f5532ed94bec15e2f66822c \ + --hash=sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8 \ + --hash=sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45 \ + --hash=sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138 \ + --hash=sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d \ + --hash=sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687 \ + --hash=sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b \ + --hash=sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c \ + --hash=sha256:c84c1b7be454b0c16f8499b4ebfbfd82ea5cca6527cceefcbbc06a7557b5ed2e \ + --hash=sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b \ + --hash=sha256:ceb8fc27d38793f9c97149be8302720c5b22e5c195a37bf2c45dc36c4600a512 \ + --hash=sha256:cf4bd113a69c0a740e27cb962ce10630c36d2b8f59d759a651b955ee9d18a823 \ + --hash=sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45 \ + --hash=sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5 \ + --hash=sha256:d636d5095155afd364247f65070fab7beda13498d7ff4de331046e704ab9657f \ + --hash=sha256:d726e3ceeb337191324b49de298142f27c3ad10886341555d1d5315b5f252c6a \ + --hash=sha256:d72d8af5c1013656a8870c866660627d1a75bc185814ee022c8533caa1de88ae \ + --hash=sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec \ + --hash=sha256:d92a5cd21fdb083931d546c207aa29633787c5dc5b02daab2d32b843f88a2c53 \ + --hash=sha256:e58585a58209d72691ce2d62a9147445f5a87beb0bde97fde284c96ae392a3d1 \ + --hash=sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5 \ + --hash=sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5 \ + --hash=sha256:edebcf7d1f601199084bb6e844d7dc67e03e04f6ac786b0332d616635c4ff7a4 \ + --hash=sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8 \ + --hash=sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77 \ + --hash=sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894 \ + --hash=sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7 \ + --hash=sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6 \ + --hash=sha256:f3d37768fce7f88dd2a8c6091f2325dea27d30d30d5c6e7a1c0f0af77723b708 \ + --hash=sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d + # via openai +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via + # litellm + # mcp +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +linkify-it-py==2.1.0 \ + --hash=sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e \ + --hash=sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b + # via markdown-it-py +litellm==1.89.2 \ + --hash=sha256:07e8e43b1a70fe919021376742897d18ffe7577ccfbb84632c949670f9abdc03 \ + --hash=sha256:b2534d69568eed026310f4e006407db2d46494eb629bd1e71eb9603ec146540d + # via openai-agents +markdown-it-py==4.2.0 \ + --hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \ + --hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a + # via + # mdit-py-plugins + # rich + # textual +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via jinja2 +mcp==1.28.0 \ + --hash=sha256:559d3f9943674cafbe5744c5d3794f3237e8b47f9bbc58e20c0fad680d8487c2 \ + --hash=sha256:9c1e7cf3a9125557e418ecd4fed8e9adddce81b0dfdae4d6601d700f5beb71a4 + # via openai-agents +mdit-py-plugins==0.6.1 \ + --hash=sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d \ + --hash=sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0 + # via textual +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +multidict==6.7.1 \ + --hash=sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0 \ + --hash=sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9 \ + --hash=sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581 \ + --hash=sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2 \ + --hash=sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941 \ + --hash=sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3 \ + --hash=sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43 \ + --hash=sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962 \ + --hash=sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1 \ + --hash=sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f \ + --hash=sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c \ + --hash=sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8 \ + --hash=sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa \ + --hash=sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6 \ + --hash=sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c \ + --hash=sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991 \ + --hash=sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262 \ + --hash=sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd \ + --hash=sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d \ + --hash=sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d \ + --hash=sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5 \ + --hash=sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3 \ + --hash=sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601 \ + --hash=sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505 \ + --hash=sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0 \ + --hash=sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292 \ + --hash=sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed \ + --hash=sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362 \ + --hash=sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511 \ + --hash=sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23 \ + --hash=sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2 \ + --hash=sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb \ + --hash=sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e \ + --hash=sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582 \ + --hash=sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0 \ + --hash=sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2 \ + --hash=sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e \ + --hash=sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d \ + --hash=sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65 \ + --hash=sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a \ + --hash=sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd \ + --hash=sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d \ + --hash=sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108 \ + --hash=sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177 \ + --hash=sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144 \ + --hash=sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5 \ + --hash=sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd \ + --hash=sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5 \ + --hash=sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060 \ + --hash=sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37 \ + --hash=sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56 \ + --hash=sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df \ + --hash=sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963 \ + --hash=sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568 \ + --hash=sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db \ + --hash=sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118 \ + --hash=sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84 \ + --hash=sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f \ + --hash=sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889 \ + --hash=sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71 \ + --hash=sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f \ + --hash=sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0 \ + --hash=sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7 \ + --hash=sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048 \ + --hash=sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8 \ + --hash=sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49 \ + --hash=sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0 \ + --hash=sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9 \ + --hash=sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59 \ + --hash=sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190 \ + --hash=sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709 \ + --hash=sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d \ + --hash=sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c \ + --hash=sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e \ + --hash=sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2 \ + --hash=sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40 \ + --hash=sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3 \ + --hash=sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee \ + --hash=sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609 \ + --hash=sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c \ + --hash=sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445 \ + --hash=sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1 \ + --hash=sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a \ + --hash=sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5 \ + --hash=sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31 \ + --hash=sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8 \ + --hash=sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33 \ + --hash=sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7 \ + --hash=sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca \ + --hash=sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8 \ + --hash=sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92 \ + --hash=sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733 \ + --hash=sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429 \ + --hash=sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9 \ + --hash=sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4 \ + --hash=sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6 \ + --hash=sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2 \ + --hash=sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172 \ + --hash=sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981 \ + --hash=sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5 \ + --hash=sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de \ + --hash=sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52 \ + --hash=sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7 \ + --hash=sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c \ + --hash=sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2 \ + --hash=sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6 \ + --hash=sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf \ + --hash=sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f \ + --hash=sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b \ + --hash=sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961 \ + --hash=sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a \ + --hash=sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3 \ + --hash=sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b \ + --hash=sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358 \ + --hash=sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6 \ + --hash=sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e \ + --hash=sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1 \ + --hash=sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c \ + --hash=sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5 \ + --hash=sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53 \ + --hash=sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872 \ + --hash=sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e \ + --hash=sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df \ + --hash=sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03 \ + --hash=sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8 \ + --hash=sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a \ + --hash=sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122 \ + --hash=sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a \ + --hash=sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee \ + --hash=sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32 \ + --hash=sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3 \ + --hash=sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489 \ + --hash=sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23 \ + --hash=sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34 \ + --hash=sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75 \ + --hash=sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8 \ + --hash=sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a \ + --hash=sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d \ + --hash=sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855 \ + --hash=sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b \ + --hash=sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4 \ + --hash=sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4 \ + --hash=sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d \ + --hash=sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0 \ + --hash=sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba \ + --hash=sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19 + # via + # aiohttp + # yarl +openai==2.43.0 \ + --hash=sha256:65a670b54fadf2268c9e1330133373c963eb779ee969e5cbad419ec2c21dce97 \ + --hash=sha256:e74d238200a26868977002190fb6631613480a93dfe0c9c982e77021ed60a017 + # via + # litellm + # openai-agents +openai-agents==0.14.6 \ + --hash=sha256:e9d16b835f73be4c5e3798694f90d7a62efcade931e59416bc7462c850e15705 \ + --hash=sha256:fdd3fb459892c8af5d0b522908b544e96f6217c7254ba55e966424493b43c1ed + # via strix-agent +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 + # via + # google-cloud-aiplatform + # google-cloud-bigquery + # huggingface-hub +platformdirs==4.10.0 \ + --hash=sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7 \ + --hash=sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a + # via textual +propcache==0.5.2 \ + --hash=sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427 \ + --hash=sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5 \ + --hash=sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa \ + --hash=sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7 \ + --hash=sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a \ + --hash=sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0 \ + --hash=sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660 \ + --hash=sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94 \ + --hash=sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917 \ + --hash=sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42 \ + --hash=sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3 \ + --hash=sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa \ + --hash=sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d \ + --hash=sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33 \ + --hash=sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a \ + --hash=sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511 \ + --hash=sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0 \ + --hash=sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84 \ + --hash=sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c \ + --hash=sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66 \ + --hash=sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821 \ + --hash=sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb \ + --hash=sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e \ + --hash=sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853 \ + --hash=sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56 \ + --hash=sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55 \ + --hash=sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6 \ + --hash=sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704 \ + --hash=sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82 \ + --hash=sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f \ + --hash=sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64 \ + --hash=sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999 \ + --hash=sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b \ + --hash=sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb \ + --hash=sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d \ + --hash=sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4 \ + --hash=sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab \ + --hash=sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f \ + --hash=sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03 \ + --hash=sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5 \ + --hash=sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba \ + --hash=sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979 \ + --hash=sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b \ + --hash=sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144 \ + --hash=sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d \ + --hash=sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e \ + --hash=sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67 \ + --hash=sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117 \ + --hash=sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa \ + --hash=sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb \ + --hash=sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96 \ + --hash=sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5 \ + --hash=sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476 \ + --hash=sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191 \ + --hash=sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78 \ + --hash=sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078 \ + --hash=sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837 \ + --hash=sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a \ + --hash=sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba \ + --hash=sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe \ + --hash=sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c \ + --hash=sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf \ + --hash=sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c \ + --hash=sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9 \ + --hash=sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8 \ + --hash=sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe \ + --hash=sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031 \ + --hash=sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913 \ + --hash=sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d \ + --hash=sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf \ + --hash=sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f \ + --hash=sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539 \ + --hash=sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b \ + --hash=sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285 \ + --hash=sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959 \ + --hash=sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d \ + --hash=sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4 \ + --hash=sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f \ + --hash=sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836 \ + --hash=sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274 \ + --hash=sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d \ + --hash=sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f \ + --hash=sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e \ + --hash=sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe \ + --hash=sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1 \ + --hash=sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a \ + --hash=sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39 \ + --hash=sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7 \ + --hash=sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a \ + --hash=sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164 \ + --hash=sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e \ + --hash=sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2 \ + --hash=sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0 \ + --hash=sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0 \ + --hash=sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335 \ + --hash=sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568 \ + --hash=sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4 \ + --hash=sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80 \ + --hash=sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2 \ + --hash=sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370 \ + --hash=sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4 \ + --hash=sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b \ + --hash=sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42 \ + --hash=sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a \ + --hash=sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e \ + --hash=sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757 \ + --hash=sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825 \ + --hash=sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0 \ + --hash=sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27 \ + --hash=sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf \ + --hash=sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f \ + --hash=sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d \ + --hash=sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366 \ + --hash=sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc \ + --hash=sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c \ + --hash=sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7 \ + --hash=sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702 \ + --hash=sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098 \ + --hash=sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751 \ + --hash=sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e \ + --hash=sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6 + # via + # aiohttp + # yarl +proto-plus==1.28.0 \ + --hash=sha256:38e5696342835b08fc116f30a25665b29531cda9d5d5643e9b81fc312385abd9 \ + --hash=sha256:a630604310899e73c59ec302e5765c058d412b2f090b9c79c8822589f14955b8 + # via + # google-api-core + # google-cloud-aiplatform + # google-cloud-resource-manager +protobuf==6.33.6 \ + --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \ + --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \ + --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \ + --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \ + --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \ + --hash=sha256:bd56799fb262994b2c2faa1799693c95cc2e22c62f56fb43af311cae45d26f0e \ + --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \ + --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \ + --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593 \ + --hash=sha256:f443a394af5ed23672bc6c486be138628fbe5c651ccbc536873d7da23d1868cf + # via + # google-api-core + # google-cloud-aiplatform + # google-cloud-resource-manager + # googleapis-common-protos + # grpc-google-iam-v1 + # grpcio-status + # proto-plus +pyasn1==0.6.3 \ + --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ + --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde + # via pyasn1-modules +pyasn1-modules==0.4.2 \ + --hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \ + --hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6 + # via google-auth +pycparser==3.0 \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via cffi +pydantic==2.13.4 \ + --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba \ + --hash=sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6 + # via + # caido-sdk-client + # google-cloud-aiplatform + # google-genai + # litellm + # mcp + # openai + # openai-agents + # pydantic-settings + # strix-agent +pydantic-core==2.46.4 \ + --hash=sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0 \ + --hash=sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262 \ + --hash=sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda \ + --hash=sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0 \ + --hash=sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e \ + --hash=sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b \ + --hash=sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594 \ + --hash=sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29 \ + --hash=sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2 \ + --hash=sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c \ + --hash=sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d \ + --hash=sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398 \ + --hash=sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d \ + --hash=sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3 \ + --hash=sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f \ + --hash=sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb \ + --hash=sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7 \ + --hash=sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5 \ + --hash=sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9 \ + --hash=sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462 \ + --hash=sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4 \ + --hash=sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b \ + --hash=sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d \ + --hash=sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df \ + --hash=sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2 \ + --hash=sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0 \ + --hash=sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519 \ + --hash=sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd \ + --hash=sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7 \ + --hash=sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac \ + --hash=sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6 \ + --hash=sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565 \ + --hash=sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898 \ + --hash=sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb \ + --hash=sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928 \ + --hash=sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6 \ + --hash=sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3 \ + --hash=sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a \ + --hash=sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596 \ + --hash=sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987 \ + --hash=sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e \ + --hash=sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d \ + --hash=sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712 \ + --hash=sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008 \ + --hash=sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd \ + --hash=sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1 \ + --hash=sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be \ + --hash=sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea \ + --hash=sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292 \ + --hash=sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33 \ + --hash=sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3 \ + --hash=sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4 \ + --hash=sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b \ + --hash=sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826 \ + --hash=sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac \ + --hash=sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7 \ + --hash=sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d \ + --hash=sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf \ + --hash=sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4 \ + --hash=sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc \ + --hash=sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15 \ + --hash=sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3 \ + --hash=sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b \ + --hash=sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914 \ + --hash=sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04 \ + --hash=sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c \ + --hash=sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b \ + --hash=sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9 \ + --hash=sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce \ + --hash=sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4 \ + --hash=sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a \ + --hash=sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f \ + --hash=sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424 \ + --hash=sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894 \ + --hash=sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9 \ + --hash=sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76 \ + --hash=sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201 \ + --hash=sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb \ + --hash=sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109 \ + --hash=sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4 \ + --hash=sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848 \ + --hash=sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526 \ + --hash=sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0 \ + --hash=sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01 \ + --hash=sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458 \ + --hash=sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e \ + --hash=sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba \ + --hash=sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a \ + --hash=sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39 \ + --hash=sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c \ + --hash=sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000 \ + --hash=sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b \ + --hash=sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf \ + --hash=sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4 \ + --hash=sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd \ + --hash=sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28 \ + --hash=sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9 \ + --hash=sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30 \ + --hash=sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983 \ + --hash=sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1 \ + --hash=sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76 \ + --hash=sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5 \ + --hash=sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4 \ + --hash=sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7 \ + --hash=sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c \ + --hash=sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066 \ + --hash=sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3 \ + --hash=sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02 \ + --hash=sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89 \ + --hash=sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50 \ + --hash=sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76 \ + --hash=sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49 \ + --hash=sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b \ + --hash=sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d \ + --hash=sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7 \ + --hash=sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4 \ + --hash=sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c \ + --hash=sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e \ + --hash=sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff \ + --hash=sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae + # via pydantic +pydantic-settings==2.14.2 \ + --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 \ + --hash=sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f + # via + # mcp + # strix-agent +pygments==2.20.0 \ + --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ + --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 + # via + # rich + # textual +pyjwt==2.13.0 \ + --hash=sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423 \ + --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 + # via mcp +pyopenssl==26.3.0 \ + --hash=sha256:46367f8f66b92271e6d218da9c87607e1ef5a0bc5c8dea5bb3db82f395c385a3 \ + --hash=sha256:589de7fae1c9ea670d18422ed00fc04da787bbde8e1454aea872aa57b49ad341 + # via google-auth +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via google-cloud-bigquery +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via + # litellm + # pydantic-settings +python-multipart==0.0.31 \ + --hash=sha256:8408153d68a9773291fc1da39a8b85a50044bddbabd2dd72e9229776b7b15e28 \ + --hash=sha256:fc631183bb13e56db3158a4909908dfb2e23565286744e798241e63750e5d680 + # via + # -r requirements-strix-ci.txt + # mcp +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via huggingface-hub +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # jsonschema + # jsonschema-specifications +regex==2026.5.9 \ + --hash=sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d \ + --hash=sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611 \ + --hash=sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3 \ + --hash=sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d \ + --hash=sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4 \ + --hash=sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2 \ + --hash=sha256:0f03aa6898aaaac4592479821df16e68e8d0e29e903e65d8f2dfb2f19028a989 \ + --hash=sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf \ + --hash=sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c \ + --hash=sha256:15ee42209947f4ca045412eae98416317238163618ace2a8e54f99586a466733 \ + --hash=sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e \ + --hash=sha256:19c16ceb4a267a8789e25733e583983eeab9f0f8664e66b0bd1c5d21f14c2d4b \ + --hash=sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a \ + --hash=sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e \ + --hash=sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0 \ + --hash=sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c \ + --hash=sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b \ + --hash=sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346 \ + --hash=sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc \ + --hash=sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c \ + --hash=sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21 \ + --hash=sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a \ + --hash=sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca \ + --hash=sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d \ + --hash=sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6 \ + --hash=sha256:3b1e39888c5e0c7d92cea4fc777396c4a90363b05de75d02eb459a4752200808 \ + --hash=sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c \ + --hash=sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58 \ + --hash=sha256:446ddd671e43ab535810c4b21cff7104945c701d4a14d1e6d1cd6f4e445a8bea \ + --hash=sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c \ + --hash=sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8 \ + --hash=sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6 \ + --hash=sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9 \ + --hash=sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026 \ + --hash=sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2 \ + --hash=sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415 \ + --hash=sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6 \ + --hash=sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020 \ + --hash=sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06 \ + --hash=sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0 \ + --hash=sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa \ + --hash=sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0 \ + --hash=sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0 \ + --hash=sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af \ + --hash=sha256:6ba42b2e7e7f46cf68cc6a5ca36fa07959f9bbd9c6bdcc47b6ee76549a590248 \ + --hash=sha256:71b61c5bfe1c806332defc42ad6c780b3c55f661986d7f40283a3a88274b4c00 \ + --hash=sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e \ + --hash=sha256:7b92817338591505f282cf3864c145244b1edcf5381d237038df955001091538 \ + --hash=sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2 \ + --hash=sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178 \ + --hash=sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499 \ + --hash=sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994 \ + --hash=sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e \ + --hash=sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de \ + --hash=sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b \ + --hash=sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20 \ + --hash=sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e \ + --hash=sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88 \ + --hash=sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107 \ + --hash=sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14 \ + --hash=sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309 \ + --hash=sha256:954cc214c04663ee6d266fc61739cad83054683048de65c5bd1d640ad28098ac \ + --hash=sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070 \ + --hash=sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2 \ + --hash=sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad \ + --hash=sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919 \ + --hash=sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676 \ + --hash=sha256:a6a563446a41adc451393dc6b8e6ad87979efaee3c8738690a8d1b08ebead1b4 \ + --hash=sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270 \ + --hash=sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c \ + --hash=sha256:a9e1328e17c84c1a5d22ec9f785ecef4a967fab9a42b6a8dc3bcbebd0a0c9e44 \ + --hash=sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed \ + --hash=sha256:b310768746dd314ea6e2ff4cc89ef215426813396ff4e94ee8e6f7096c8b6e03 \ + --hash=sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4 \ + --hash=sha256:b4bb445ff3f725f59df8f6014edb547ee928ec7023a774f6a39a3f953038cbb2 \ + --hash=sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2 \ + --hash=sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff \ + --hash=sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41 \ + --hash=sha256:bfe1ce50cbfb569d74e1e4337da6468961f31dbea55fd85aa5de59c0947a805a \ + --hash=sha256:c010eb8caca74bdb40c07498d7ece26b4428fd3f04aa8a72c9ac6f79e8faaac6 \ + --hash=sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100 \ + --hash=sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451 \ + --hash=sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77 \ + --hash=sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48 \ + --hash=sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621 \ + --hash=sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f \ + --hash=sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1 \ + --hash=sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb \ + --hash=sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf \ + --hash=sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6 \ + --hash=sha256:d6b8a143aca6c39b446ea8092cde25cc8fe9304d4f5fecfbc1a9dbb0282703c2 \ + --hash=sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046 \ + --hash=sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f \ + --hash=sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66 \ + --hash=sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8 \ + --hash=sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041 \ + --hash=sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4 \ + --hash=sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8 \ + --hash=sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081 \ + --hash=sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372 \ + --hash=sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04 \ + --hash=sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962 \ + --hash=sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5 \ + --hash=sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9 \ + --hash=sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5 \ + --hash=sha256:ed457d8e98ae812ed7732bef7bf78de78e834eae0372a74e23ca90ef21d910f9 \ + --hash=sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555 \ + --hash=sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d \ + --hash=sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127 \ + --hash=sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225 \ + --hash=sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd \ + --hash=sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce \ + --hash=sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b \ + --hash=sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763 + # via tiktoken +requests==2.34.2 \ + --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ + --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed + # via + # docker + # google-api-core + # google-auth + # google-cloud-bigquery + # google-cloud-storage + # google-genai + # openai-agents + # strix-agent + # tiktoken +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 + # via + # strix-agent + # textual + # typer +rpds-py==2026.5.1 \ + --hash=sha256:01d17b29c0c23d82b1f4751147ec49cf451f1fc2554eb9ef5f957e55d2656ead \ + --hash=sha256:036a36a87fb1cd3b214d11c4b3c4f7d2ddad933625dca1c900b56a057c07740a \ + --hash=sha256:0408a24e44feb919423dc6d9da677cb5cddb894d2ca9e763967d156d9c60fab4 \ + --hash=sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256 \ + --hash=sha256:0957cf3c2b8632ec7aaebffebea8005b353cc2a237b6e2ae3c2cac0820704cfb \ + --hash=sha256:0a5ae4dbe43c1076983b72616496919872ae7bbe7a1e21cc48336bc3154d130b \ + --hash=sha256:0a7d1eec967df0e9b22614a5e177622e0c89611d03727fa0cb48e45028907870 \ + --hash=sha256:0b35217adefe87f2fe4db7e9766cabe84744bfe9616d9667be18988928c7f2dc \ + --hash=sha256:0fa92420128dadce7f54bd73ba1825a273e9268fe9e35dbf7e6362890efa4e08 \ + --hash=sha256:141c9498daf2ace9eda35d2b0e376f9ea8b058d84f2aef4f96fccfd449a2f251 \ + --hash=sha256:1841d067089e117142d79b98aa0df2f08b52f2ecc1819dd2700636c0db74a473 \ + --hash=sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b \ + --hash=sha256:1c27c5f6102eac8c03e7595a00827a53b271ba40a53b59ff8709170e0855ea4a \ + --hash=sha256:1ebb2f0ab7e16132995a72de805170e0203df0c3dd22e1ef1cd1fdd90bd7a131 \ + --hash=sha256:1f2c391c3059798093b65df23aca2cac150460ae9c630d99dec83d703d9485b9 \ + --hash=sha256:205dde846f24332ab0c1188699a043b8d165b79bb84529ce272c45048ff6be01 \ + --hash=sha256:21846aac0ed2e0589f38c12dc44e77bb64e494b771eadbcf169cba00566ba7ba \ + --hash=sha256:21942f52dbbd5f8758bf021213d28bd45c39e873e65e2407faf5f1846f5761ad \ + --hash=sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db \ + --hash=sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d \ + --hash=sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0 \ + --hash=sha256:2c595a1d9255dce0599e13130d1440ab2506654f2b50294226ee06402f8fef63 \ + --hash=sha256:2c817a189d4ee14290420e5ff051e4dd6baa13f3edf84685071dee07a6d538ee \ + --hash=sha256:2d88621d6a7d4dfa633d21abe90f280bb205274e16b1d1e61c6ad4640b2453b7 \ + --hash=sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b \ + --hash=sha256:3397a5ed7174dc2786bb214030232fc36fe8e5584fec43a9952cc542b1a12036 \ + --hash=sha256:3574b55c604b8f75dacb007136508bbc0db406e626301778096a133327e7f2fb \ + --hash=sha256:3609e9939a8a76cd904cf98a3f1f13b5dc7e150adeaee89e0ea09652ea213e16 \ + --hash=sha256:3684a59b158a7683aaeb8e25352e9a9dd2122cec78f2d8530266e4f91b4c7b3f \ + --hash=sha256:3966b82dd563176396df030f3dd52a6e54cb69b718e95e78bd555ed3d1e0185d \ + --hash=sha256:3abe24a66e57adcfa645d718063a5fa5103ecc71ddbf26d78af8f9368018ff1d \ + --hash=sha256:40ff257542e04796880e011e15cd4dc21c2599975df2aaa8f2c8495ca574e1a5 \ + --hash=sha256:413b424f7c4ee65ab5e5be91f5731be0f8b41a1ee2b12dfe810d716312e95a78 \ + --hash=sha256:42d0f20e85e549c870749d0e247f0c10d318a45b7e9676d575d2dcb04a1b2e66 \ + --hash=sha256:43bca78665423cabae77146f2fe7ce55272b6c8d55d82cca83effd42c7e13972 \ + --hash=sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd \ + --hash=sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89 \ + --hash=sha256:4be8b1d2a705cc37d08256004e1d07de143fa0075c8e85a3df020b776f62b732 \ + --hash=sha256:4e237e139f94d3c036fd28eb9f564c99055476ff4ff05cd42be55ce349b5aa02 \ + --hash=sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef \ + --hash=sha256:55d8f9b7b78c9538fc9e04e82ec0e888ff0c3cffcfad152c77e57cd09351a98a \ + --hash=sha256:58b1d94308ddf0b1982f61f2eb54bf92997c9ece8a8093ef014250f4a517906c \ + --hash=sha256:5d333a7127d4b307601ac37792bee01bb95c867cbfacf21b6375b804d6bbd723 \ + --hash=sha256:613fc4ee9eaef26dc5840666214dd6fbcebcf32f46e76f4abc473059f4e13dda \ + --hash=sha256:6142dbd80c4df62a5d899f0d616d417f84e0bc8d32526c8e5589019d75d028a7 \ + --hash=sha256:62ae3853454fe9ef283a03c96c2d835d39e84b14643a9d62c82ef0fb87d702ca \ + --hash=sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02 \ + --hash=sha256:656a042550878f12d45752452d47094b7cfe5ad1e9d7b87b5a22ad3ae5ff8015 \ + --hash=sha256:66c93681c4729e4e3ecba31b8179fae083ff3118841672835140338b4b9867c1 \ + --hash=sha256:6736718bd4fc49cbcb538ba30516fdbef161522acefb739657d48b97bd864fed \ + --hash=sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00 \ + --hash=sha256:6c3d771a46ec18b12af06ce36243a9a80b07a5d0515236332d90863ca8bb326a \ + --hash=sha256:6c7fcf61d44cacecaf3aea542b0e053db77972a4573e7ceda16fb2b399161195 \ + --hash=sha256:6f249f8b860a200ad35193af961183ebe9132710484e6f6ce0cf89fd83c63a9a \ + --hash=sha256:73c4bd4f70294737b5206a3e8e30ccadbf8a60301831c8ea23eec5dbeea1ecfa \ + --hash=sha256:7559f72b94ae52659086c595dfa017cde03155f7832071d30959049052cb3ece \ + --hash=sha256:75808f6c38ce7749bb68cc2770161aae5045e6c6f6781a9782e74b93304399df \ + --hash=sha256:77c004fdc7b891967106f78ddfd7b076bfe6813c6139c6fff6aed3bcaa960b26 \ + --hash=sha256:7818f8d0a415be74d2be3590b0a1c1f463a642f4d0217e7d10602dceef5b79aa \ + --hash=sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842 \ + --hash=sha256:7bd530e6a530bb3ea892f194fafa455f3516ac25ecf7143fd33c09be62b0470a \ + --hash=sha256:8213afbe8a3a906fb9acb2014423fe3359ee783d0bf90995f70623a3217bfa6c \ + --hash=sha256:83bcf894486c9d78dd290d3c0124ff6dd8875d3025e2090a8ec49fcc37c55fdd \ + --hash=sha256:85264a90ff4c05c1568dd65f5921c837614b67c60358fb4c17df3b7f2e90690a \ + --hash=sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf \ + --hash=sha256:8895840ac4809e5f60c88fd07617cd71326e73d6e5a8aa783c5c0f7c24985de2 \ + --hash=sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f \ + --hash=sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf \ + --hash=sha256:8c43a8a973270fd173bf48cdf80bbe66312421cba68d40845034f174f2389049 \ + --hash=sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3 \ + --hash=sha256:90f628283be835db980c941767d41c9a27b5239e54ba0a9c1335247e82406964 \ + --hash=sha256:94068eb3ae6d43f5a786b7db96a406a34e6d5c24489feef32fd6e8946ea7b291 \ + --hash=sha256:980450826cf22e133c57e0835070bdd0dd3f73b9b708c3ce223def2cb9469e14 \ + --hash=sha256:99ab6ba7bfa2cb0f96a04e3652355bf04e3f51aceb1e943b8541dab7ba4828cc \ + --hash=sha256:9af8905b8f854990e40d5206aa5ac58d9b0fe0b7f351ff2bb086c20f6c8c6a47 \ + --hash=sha256:9baffb505aff33acc69b422a19f77806680f3c8632227d79f48de8a810d1c2c5 \ + --hash=sha256:9cdddb6c1207d284d94fd1530adf57fbd797fe7c4b8704ba85f49414f2557e7d \ + --hash=sha256:9e25b7088f9ccbfc0dfcaa52bf969300ca229e10ecf758974ebcbb080a4b37bb \ + --hash=sha256:a04df86b3f0fade39ec8fd0e0aab089b1da9fbd2b48df778a57ef96f5e7d38df \ + --hash=sha256:a05fa4f41f37ec97c9c260441a940450a192f78d774d2b097eee1379f1e1246a \ + --hash=sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc \ + --hash=sha256:aad1bff7f666b9598e573815affd666aac6a13a585dde336f843e33350c7fadc \ + --hash=sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46 \ + --hash=sha256:ad3773236e95f7f33991eb125224b7da66f206504d032a253a02da7e134519fb \ + --hash=sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2 \ + --hash=sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e \ + --hash=sha256:b1be5c35683684d5331b93600c210e8367c254683d8a6df6bd21bd2da3a334fb \ + --hash=sha256:b317c87a13f769a4e787819bd508aaa5d69aa09b0880de9af6d3a8a54571cdec \ + --hash=sha256:b3cc20c0d800af78fd0fac68086e28c1856cec51ea528bb81ea851aa40d39325 \ + --hash=sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600 \ + --hash=sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559 \ + --hash=sha256:b5f077b44a4f7808520f66dae234988d867deb9aed9be5da057ce9ba831b2a41 \ + --hash=sha256:b6825cc329b290e93c5f6a9be2393118a763f6ccf6abd83704e0c102ca583644 \ + --hash=sha256:b8d2f912928d426e8cfa396f7f3f8d29a59e6689c86dcca3c420730c1096322b \ + --hash=sha256:b95d5e11fc712b752081183a55a244c03cd00570489edd7014d8899f8ceb8162 \ + --hash=sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83 \ + --hash=sha256:ba05adbf15d994c38ec0b7ab32e858e5110c21e9009a00a86545fd220f84e038 \ + --hash=sha256:c0f920015df2a504bebaba6d4c31ccf3fcf942f92655c086da30b671aad19aa6 \ + --hash=sha256:c396c1304de421050b3681ea70f371874b54d41b0151e96109758144c231e30b \ + --hash=sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3 \ + --hash=sha256:c3df104083952a0e0c6f10de33e440eabe98fb6317d23e1a58c68f6df08d01b9 \ + --hash=sha256:c74005a7bb87752acf351c93897ec63ad77a07a0da7ecad9c050e32e7286ba34 \ + --hash=sha256:c93c629be4636cf54337bd5f06c104d55e42ced54d681f6fe21ae510a65116f6 \ + --hash=sha256:ca653c6546386227cd9800d1bef6a348099acf8db4250341da6d90f663d6dfcb \ + --hash=sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa \ + --hash=sha256:cc68e231a77a5f0d774ae278a1f8e55c0456501820847c1e4efb3829f3441df6 \ + --hash=sha256:ce87129d9f2c14fa6c4a8601fb80eb4488c80d38a20cd13758ef11123e14995d \ + --hash=sha256:cea68bcd53467561ae2f96a6bdad1544299ba97b5b0ddcd5ac3d376e5c781c24 \ + --hash=sha256:cef8ac28d26f4dda3533060c20fbf80a325458fa9fd23ea72a73cdfa8e978838 \ + --hash=sha256:d0efbe45632665e53e3db8fe1e5692db58fc5cb9bab4459d570b83efefe11164 \ + --hash=sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97 \ + --hash=sha256:de42116e69cb53b911cc34aee5ab98f36c597b822545045d49e938818b99e5e4 \ + --hash=sha256:df1d2a1996755b24b9ecee92cb4d36c28f86f464a6a173349c26bab41e94b8c2 \ + --hash=sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55 \ + --hash=sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3 \ + --hash=sha256:e10464d17df3b582745c25cec695cb9558bca2cb6ddb631aee1787fc72c767b2 \ + --hash=sha256:e3a8ae58895ac107ed934a6bf51e5846f95c53b9b940c2c6d310838fd5846358 \ + --hash=sha256:e4abbf391a70be864920858bf360f4fb380577c9a0f732438a1996726e2c195b \ + --hash=sha256:eaaea962c68cdc68d4a533ba985ab8e9484277910bbfaa2ab3ef7732667bfed8 \ + --hash=sha256:ed0954b524873214369184a9c82b0eaa45a3fbb9a798cd95b17e0d98499e7ea0 \ + --hash=sha256:edf2765d84e42447f112ad877af8fe1db0089aaec5b28e88d6eab45e7fe99cea \ + --hash=sha256:ef1013a8625c74043210190b246f5b1551e09757c1f356c6e4160ef96c5bc081 \ + --hash=sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d \ + --hash=sha256:f3a5b10e8ce894825f380a8f1b6444cf73c294dfea62afbb2d13e3a9e630cec1 \ + --hash=sha256:f3df3d16ded76f1f8c9cdebd0e1ea55fdf4c23b812de189814da7cf229c22a81 \ + --hash=sha256:f414556f6e3958300ff941e40c9f97e3dc9774ddd1b3434c475d73dd354bbed3 \ + --hash=sha256:fc09f82e63d4bcd58149572f857a431bae851dc747e313c3b5bdf7abb907fda8 \ + --hash=sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1 \ + --hash=sha256:fe71bca7d547acb17027c7fd1624ff8aae623499c498d3e7011182c4de5c25e0 \ + --hash=sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd + # via + # jsonschema + # referencing +shellingham==1.5.4 \ + --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ + --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de + # via typer +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via python-dateutil +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via + # google-genai + # openai +sse-starlette==3.4.4 \ + --hash=sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0 \ + --hash=sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973 + # via mcp +starlette==1.3.1 \ + --hash=sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0 \ + --hash=sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6 + # via + # mcp + # sse-starlette +strix-agent==1.0.4 \ + --hash=sha256:6c9d1bd2e3bfca64b1c4c7c24f70c287ea50b1d616d7a391a1e9819b01b9cc60 \ + --hash=sha256:a52b67ec91c114b42409a710065676370bb39fd4894dc79dafa58f7f8efa1a23 + # via -r requirements-strix-ci.txt +tenacity==9.1.4 \ + --hash=sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55 \ + --hash=sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a + # via google-genai +textual==8.2.7 \ + --hash=sha256:4caaa13a90bc4cf9c6c862c067ccd34fe84e9c161710a2a907a8026313b6bd73 \ + --hash=sha256:658f568ff81e30ed43890c3e07520390e5cf1b4763822006e060656b0a88f105 + # via strix-agent +tiktoken==0.13.0 \ + --hash=sha256:059c8ecf554eb5b41e6e054ba467b871b03277d267dee7244380aca4359747d4 \ + --hash=sha256:115c4f26ffa11caac8b54eea35c2ad38c612c20a48d35dd15d70a02ac6f51f58 \ + --hash=sha256:125bc05005e747f993a83dc67934249932d6e4209854452cd4c0b1d53fba3ba2 \ + --hash=sha256:165cf1820ea4a354985c2490a5205d4cc74661c934aca79dd0368232fff94e0f \ + --hash=sha256:2a3b536c55802fe42f4b4644d2be4f04bf788506b48de0a0a658cb58f8bce232 \ + --hash=sha256:2b920b35805cd64585a37c3dc7ce65fba4d2d36016be01e1d7942482ca29093a \ + --hash=sha256:2c397ddda233208345b01bd30f2fca79ff730e55731d0108a603f9bc57f6af3b \ + --hash=sha256:303f7d91b4fce3baddbcde05c139091d4caa5026ac7214c1dc7ff7a71ee429ff \ + --hash=sha256:32ac870a806cfb260a02d0cb70426aef02e038297f8ad50df5040bb5af360791 \ + --hash=sha256:32e0c12305105002c047b3bb1070b0dd9a73b0cb3b2856a8972b810e7a4f5881 \ + --hash=sha256:35e1ea1e0631c04f551297284a1ab7e1f65a3c55a9a48728d5e0f66b4527c04a \ + --hash=sha256:36217497eaffc158607a3b26f065300db2aefd43b115263f3b9688ce38146173 \ + --hash=sha256:3f277ebea5edd7b8bf03c6f9431e1d67d517530115572b2dc1d465326e8f88c7 \ + --hash=sha256:43cee3e5400573b2046fbf092cc7a5bc30164f9e4c95ce20714da929df48737a \ + --hash=sha256:44733b99bfd72b590cd0936b1c01b3b4dd73122db2d544bc1ceeb18a7678c910 \ + --hash=sha256:472527e9132952f2fbf77cd290658bacf003d4d5a3fabc18e5fbd407cbae4d9b \ + --hash=sha256:477c9a38e20d0ed248090509acf1e839ad3967a4f00b4b0f958210049f656dee \ + --hash=sha256:47b1df8d73390a24f94980c75158cdd5c56d256f16d55f30cb49c230caba9ba4 \ + --hash=sha256:493af3aa28a4aaf2e3d2600a2ee717252c9bf5ab38fff94eb5a02db5ab77e5ad \ + --hash=sha256:4d9980f11429ed2d737c463bb1fb78cf330caa026adf002f714aced7849a687b \ + --hash=sha256:4e2f67d27c9626cdd25fe33d9313c5cdb3d8d82da646b68d6eb8e7e9c20e6448 \ + --hash=sha256:51384448aa508e4df84c0f7c1dc3211c7f7b8096325660ee5fc82f3e11b381ce \ + --hash=sha256:5ba5fd62507a932d1241346179e3b39bc7bf7408f03c272652d93b3bedf5db24 \ + --hash=sha256:5cb65b60b9408563676d874a3a4ee573370066f0dc4e29d84e82e989c6517424 \ + --hash=sha256:5d48843bee149630eb735a99e1f4a85b47308d21868ea63163f6e87768d3cfed \ + --hash=sha256:5df5d1507bd245f1ccad4a074698240021239e455eb0bb4ced4e3d7181872154 \ + --hash=sha256:5e6358911cab4adee6712da27d65573496a4f68cf8a2b5fca6a4ad10fc5748cf \ + --hash=sha256:6644c9c2b5cf3916f5a3641d7d12fdb3f006a7b3d9ff6acdaec44e29ab1ff91e \ + --hash=sha256:6b1615f0ff71953d19729ceb18865429c185b0a23c5353f1bbca34a394bf60f7 \ + --hash=sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec \ + --hash=sha256:6eb4a5bfbc6426938026b1a334e898ac53541360d62d8c689870160cc80abd67 \ + --hash=sha256:75ab9bc99fa020a4c283424590ecd7f3afd70c1c281cb3fa3192a6c3af9f9615 \ + --hash=sha256:7ab10f4a21c2999846940113f6dbd72e0fa06a24119feddd74cc47e85818e06d \ + --hash=sha256:7bfe1849caa65d1e1d9871817170ec497bbb7984e182012e1bdce72f66608cdb \ + --hash=sha256:7d40c6c5aab171dcd6eb8455bc567bde404bb9def60cdb8c1299cc782b242bb9 \ + --hash=sha256:7de52e3f566d19b3b11bd37eea552c6c305ad74081f736882bd44d148ed4c48d \ + --hash=sha256:85b78cc3a2c3d48723ca751fa981f1fedccd54194ca0471b957364353a898b07 \ + --hash=sha256:8f2d16e7a7c783ad81f36e457d046d1f1c8af70b22aec8a13238efe531977c41 \ + --hash=sha256:8fe806a50664e83a6ffd56cbd1e4f5dcc6cd32a3e7538f70dc38b1a271384545 \ + --hash=sha256:91c180fe255bd5a86d8316210d2833a1d4d33d026cd86a67812f4773743c8d26 \ + --hash=sha256:95097e4f89b06403976e498abf61a0ee73a7497e73fb599cb211d8197a054d91 \ + --hash=sha256:975cbd78d085d75d26b59660e262736dcaed1e35f8f142cd6291025c01d25486 \ + --hash=sha256:9b842981fa91accdffd48ff6408a977b7a91c3fbda55d353c3c68114d5c9d69e \ + --hash=sha256:9b8858b29804b3a0add25ce9e62fb00f89f621dc754d75d03ca419d17e8ddf67 \ + --hash=sha256:a116178fa7e1b4065bff05214360373a65cac22f965be7b3f73d00a0dbfe7649 \ + --hash=sha256:a2937ad042d49d50eac6e1ba07c5661d4bd3942a5b1e0c0d08475c4df83676e1 \ + --hash=sha256:b8ac2d6420ff05841a89ba5205c6d45f56c4f6843454f3c884b7eb1a2a8dddb2 \ + --hash=sha256:b967dfb9d0adf9a631953b1b40717684f04478270fc51bbccdd2f838d67a2f00 \ + --hash=sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1 \ + --hash=sha256:ca8b310bd93b3772cb1b7922d915446864860f562bdfe4825c63a0aed3fb28cd \ + --hash=sha256:cb99cb5127449f58d0a2d5f5ccfb390d8dbdfd919c221246caaee29d8725ed51 \ + --hash=sha256:d108bc2d470fc53c8ecd24f2c0fd2b5f98c33e87cdb6aa2e9b8c5dced703d273 \ + --hash=sha256:da86f8c96ac1c235d7a3b3eebff1eacfdbcfb8ad792706943268d4d2938fbafe \ + --hash=sha256:e28157350f7ebf35008dd8e9e0fdb621f976e4230c881099c85e8cf07eaa50e2 \ + --hash=sha256:eaaaef47c2406277181d2086484c317bf7fc433e2d5d03ff94f56b0dcec87471 \ + --hash=sha256:ed5a30027cb4d8c7ca8b273d4766f3db3cf58fad9e9f3b1a68a351ffb54873d5 \ + --hash=sha256:fc1c44cd37b43fc46bae593129164f4f281e82ea116b57a85aa81bda57eafc94 + # via litellm +tokenizers==0.23.1 \ + --hash=sha256:120468fb4c24faf0543c835a4fabafa4deb3f20a035c9b6e83d0b553a97615d4 \ + --hash=sha256:1974288a609c343774f1b897c8b482c791ab17b75ab5c8c2b1737565c1d82288 \ + --hash=sha256:1bf13402aff9bc533c89cb849ec3b412dc3fbeacc9744840e423d7bf3f7dc0e3 \ + --hash=sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa \ + --hash=sha256:5075b405006415ea148a992d093699c66eb01952bf59f4d5727089a98bda45a4 \ + --hash=sha256:53b09e85775d5187941e7bab30e941b4134ab4a7dd8c68e783d231fb7ca27c51 \ + --hash=sha256:56f3a77de629917652f876294dc9fe6bad4a0c43bc229dc72e59bb23a0f4729a \ + --hash=sha256:93120a930b919416da7cd10a2f606ac9919cc69cacae7980fa2140e277660948 \ + --hash=sha256:9d10a6d957ef01896dc274e890eee27d41bd0e74ef31e60616f0fc311345184e \ + --hash=sha256:a26197957d8e4425dfba746315f3c425ea00cfa8367c5fbc4ec73447893dcea9 \ + --hash=sha256:ae848657742035523fdf261773630cb819a26995fcd3d9ecae0c1daf6e5a4959 \ + --hash=sha256:e03d6ffcbe0d56ee9c1ccd070e70a13fa750727c0277e138152acbc0252c2224 \ + --hash=sha256:e0948bbb1ac1d7cdfc9fb6d62c596e3b7550036ad60ecd654a66ad273326324e \ + --hash=sha256:e3d8f40ea6268047de7046906326abed5134f27d4e8447b23763afe5808c8a96 \ + --hash=sha256:e7bfaf995c1bdbbd21d13539decb6650967013759318627d85daeb7881af16b7 \ + --hash=sha256:ea5a0ce170074329faaa8ea3f6400ecde604b6678192688533af80980daae71a \ + --hash=sha256:f836ca703b89ae07919a309f9651f7a88fd5a33d5f718ba5ad0870ec0256bad6 + # via litellm +tqdm==4.68.3 \ + --hash=sha256:00dfa48452b6b6cfae3dd9885636c23d3422d1ec97c66d96818cbd5e0821d482 \ + --hash=sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03 + # via + # huggingface-hub + # openai +typer==0.25.1 \ + --hash=sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89 \ + --hash=sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc + # via huggingface-hub +types-requests==2.33.0.20260518 \ + --hash=sha256:626d697d1adaaff76e2044dc8c5c051d8f21abc157bdfe204a75558076fe0bf0 \ + --hash=sha256:df7bd3bfe0ca8402dfb841e7d9be714bb5578203283d66d7dc4ef69343449a5e + # via openai-agents +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via + # google-cloud-aiplatform + # google-genai + # grpcio + # huggingface-hub + # mcp + # openai + # openai-agents + # pydantic + # pydantic-core + # textual + # typing-inspection +typing-inspection==0.4.2 \ + --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \ + --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464 + # via + # mcp + # pydantic + # pydantic-settings +uc-micro-py==2.0.0 \ + --hash=sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c \ + --hash=sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811 + # via linkify-it-py +urllib3==2.7.0 \ + --hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \ + --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 + # via + # docker + # requests + # types-requests +uvicorn==0.49.0 \ + --hash=sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f \ + --hash=sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3 + # via mcp +websockets==15.0.1 \ + --hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \ + --hash=sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9 \ + --hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \ + --hash=sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3 \ + --hash=sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8 \ + --hash=sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e \ + --hash=sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1 \ + --hash=sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256 \ + --hash=sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85 \ + --hash=sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880 \ + --hash=sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123 \ + --hash=sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375 \ + --hash=sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065 \ + --hash=sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed \ + --hash=sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41 \ + --hash=sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411 \ + --hash=sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597 \ + --hash=sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f \ + --hash=sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c \ + --hash=sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3 \ + --hash=sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb \ + --hash=sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e \ + --hash=sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee \ + --hash=sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f \ + --hash=sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf \ + --hash=sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf \ + --hash=sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4 \ + --hash=sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a \ + --hash=sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665 \ + --hash=sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22 \ + --hash=sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675 \ + --hash=sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4 \ + --hash=sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d \ + --hash=sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5 \ + --hash=sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65 \ + --hash=sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792 \ + --hash=sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57 \ + --hash=sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9 \ + --hash=sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3 \ + --hash=sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151 \ + --hash=sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d \ + --hash=sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475 \ + --hash=sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940 \ + --hash=sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431 \ + --hash=sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee \ + --hash=sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413 \ + --hash=sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8 \ + --hash=sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b \ + --hash=sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a \ + --hash=sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054 \ + --hash=sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb \ + --hash=sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205 \ + --hash=sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04 \ + --hash=sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4 \ + --hash=sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa \ + --hash=sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9 \ + --hash=sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122 \ + --hash=sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b \ + --hash=sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905 \ + --hash=sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770 \ + --hash=sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe \ + --hash=sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b \ + --hash=sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562 \ + --hash=sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561 \ + --hash=sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215 \ + --hash=sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931 \ + --hash=sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9 \ + --hash=sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f \ + --hash=sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7 + # via + # google-genai + # gql + # openai-agents +yarl==1.24.2 \ + --hash=sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b \ + --hash=sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30 \ + --hash=sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc \ + --hash=sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f \ + --hash=sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae \ + --hash=sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8 \ + --hash=sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75 \ + --hash=sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a \ + --hash=sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c \ + --hash=sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461 \ + --hash=sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44 \ + --hash=sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b \ + --hash=sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727 \ + --hash=sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9 \ + --hash=sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd \ + --hash=sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67 \ + --hash=sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420 \ + --hash=sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db \ + --hash=sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50 \ + --hash=sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b \ + --hash=sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50 \ + --hash=sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9 \ + --hash=sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1 \ + --hash=sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488 \ + --hash=sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2 \ + --hash=sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f \ + --hash=sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d \ + --hash=sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003 \ + --hash=sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536 \ + --hash=sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a \ + --hash=sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a \ + --hash=sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa \ + --hash=sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f \ + --hash=sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e \ + --hash=sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035 \ + --hash=sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12 \ + --hash=sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe \ + --hash=sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4 \ + --hash=sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294 \ + --hash=sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7 \ + --hash=sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761 \ + --hash=sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643 \ + --hash=sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413 \ + --hash=sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57 \ + --hash=sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36 \ + --hash=sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14 \ + --hash=sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd \ + --hash=sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5 \ + --hash=sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656 \ + --hash=sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad \ + --hash=sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c \ + --hash=sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0 \ + --hash=sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992 \ + --hash=sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342 \ + --hash=sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1 \ + --hash=sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf \ + --hash=sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024 \ + --hash=sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986 \ + --hash=sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb \ + --hash=sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d \ + --hash=sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543 \ + --hash=sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d \ + --hash=sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed \ + --hash=sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617 \ + --hash=sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996 \ + --hash=sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8 \ + --hash=sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2 \ + --hash=sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3 \ + --hash=sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535 \ + --hash=sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630 \ + --hash=sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215 \ + --hash=sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592 \ + --hash=sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf \ + --hash=sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b \ + --hash=sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac \ + --hash=sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0 \ + --hash=sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92 \ + --hash=sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122 \ + --hash=sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1 \ + --hash=sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8 \ + --hash=sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576 \ + --hash=sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8 \ + --hash=sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712 \ + --hash=sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1 \ + --hash=sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2 \ + --hash=sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b \ + --hash=sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a \ + --hash=sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53 \ + --hash=sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1 \ + --hash=sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d \ + --hash=sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208 \ + --hash=sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0 \ + --hash=sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c \ + --hash=sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607 \ + --hash=sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c \ + --hash=sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8 \ + --hash=sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39 \ + --hash=sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f \ + --hash=sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8 \ + --hash=sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90 \ + --hash=sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45 \ + --hash=sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2 \ + --hash=sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056 \ + --hash=sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14 + # via + # aiohttp + # gql +zipp==4.1.0 \ + --hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \ + --hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602 + # via importlib-metadata diff --git a/requirements-strix-ci.txt b/requirements-strix-ci.txt new file mode 100644 index 00000000..1242ce3a --- /dev/null +++ b/requirements-strix-ci.txt @@ -0,0 +1,4 @@ +strix-agent==1.0.4 +google-cloud-aiplatform==1.133.0 +cryptography==49.0.0 +python-multipart==0.0.31 diff --git a/scripts/checks/verify_supply_chain.py b/scripts/checks/verify_supply_chain.py index 19a6b33a..ca4864c6 100644 --- a/scripts/checks/verify_supply_chain.py +++ b/scripts/checks/verify_supply_chain.py @@ -1216,30 +1216,23 @@ def is_blocking_required_step(block_lines: list[str], block_indent: int) -> bool return [] -def _verify_ci_coverage(missing: list[str]) -> None: +def verify_workflow_coverage() -> list[str]: + """Return workflow trigger and artifact coverage violations.""" + missing: list[str] = [] ci = read_workflow(Path(".github/workflows/ci.yml"), "ci", missing) for token in ["develop", "main", "pull_request", "push", "ci / build-and-test"]: if ci and token not in ci: missing.append(f"ci workflow missing token: {token}") - - -def _verify_sbom_coverage(missing: list[str]) -> None: sbom = read_workflow(Path(".github/workflows/sbom.yml"), "sbom", missing) for token in ["develop", "main", "pull_request", "release:", "tags:"]: if sbom and token not in sbom: missing.append(f"sbom workflow missing trigger token: {token}") - - -def _verify_dependency_review_coverage(missing: list[str]) -> None: review = read_workflow( Path(".github/workflows/dependency-review.yml"), "dependency review", missing ) for token in ["develop", "main", "pull_request"]: if review and token not in review: missing.append(f"dependency review workflow missing trigger token: {token}") - - -def _verify_security_audit_coverage(missing: list[str]) -> None: audit = read_workflow( Path(".github/workflows/security-audit.yml"), "security audit", missing ) @@ -1266,16 +1259,10 @@ def _verify_security_audit_coverage(missing: list[str]) -> None: missing.append( f"security audit workflow missing vulnerability audit token: {token}" ) - - -def _verify_codeql_coverage(missing: list[str]) -> None: codeql = read_workflow(Path(".github/workflows/codeql.yml"), "codeql", missing) for token in ["develop", "main", "pull_request", "push", "codeql"]: if codeql and token not in codeql: missing.append(f"codeql workflow missing token: {token}") - - -def _verify_release_coverage(missing: list[str]) -> None: release = read_workflow(Path(".github/workflows/release.yml"), "release", missing) for token in [ "develop", @@ -1287,18 +1274,12 @@ def _verify_release_coverage(missing: list[str]) -> None: ]: if release and token not in release: missing.append(f"release workflow missing token: {token}") - - -def _verify_secret_scan_coverage(missing: list[str]) -> None: secret_scan = read_workflow( Path(".github/workflows/secret-scan-gate.yml"), "secret scan", missing ) for token in ["develop", "main", "pull_request", "push", "secret-scan-gate"]: if secret_scan and token not in secret_scan: missing.append(f"secret scan workflow missing token: {token}") - - -def _verify_build_coverage(missing: list[str]) -> None: build = read_workflow( Path(".github/workflows/build-baseline.yml"), "build baseline", missing ) @@ -1336,9 +1317,14 @@ def _verify_build_coverage(missing: list[str]) -> None: missing.append( "build workflow should not rely on macos-latest for architecture coverage" ) - - -def _verify_scorecard_coverage(missing: list[str], workflow_paths: list[Path]) -> None: + workflow_paths = sorted(Path(".github/workflows").glob("*.yml")) + sorted( + Path(".github/workflows").glob("*.yaml") + ) + for workflow_path in workflow_paths: + workflow_content = workflow_path.read_text(encoding="utf-8") + missing.extend( + release_artifact_download_decompression_violations(workflow_content) + ) scorecard = read_workflow( Path(".github/workflows/ossf-scorecard.yml"), "ossf scorecard", missing ) @@ -1381,31 +1367,6 @@ def _verify_scorecard_coverage(missing: list[str], workflow_paths: list[Path]) - workflow_content, workflow_path ) ) - - -def verify_workflow_coverage() -> list[str]: - """Return workflow trigger and artifact coverage violations.""" - missing: list[str] = [] - _verify_ci_coverage(missing) - _verify_sbom_coverage(missing) - _verify_dependency_review_coverage(missing) - _verify_security_audit_coverage(missing) - _verify_codeql_coverage(missing) - _verify_release_coverage(missing) - _verify_secret_scan_coverage(missing) - _verify_build_coverage(missing) - - workflow_paths = sorted(Path(".github/workflows").glob("*.yml")) + sorted( - Path(".github/workflows").glob("*.yaml") - ) - for workflow_path in workflow_paths: - workflow_content = workflow_path.read_text(encoding="utf-8") - missing.extend( - release_artifact_download_decompression_violations(workflow_content) - ) - - _verify_scorecard_coverage(missing, workflow_paths) - return missing diff --git a/scripts/ci/opencode_review_approve_gate.sh b/scripts/ci/opencode_review_approve_gate.sh index c6f10694..5735206f 100755 --- a/scripts/ci/opencode_review_approve_gate.sh +++ b/scripts/ci/opencode_review_approve_gate.sh @@ -71,7 +71,7 @@ if [ -z "$CONTROL_JSON" ]; then fi TMP_JSON="$(mktemp)" -trap 'rm -f "$TMP_JSON" "${TMP_JSON}.normalized"' EXIT +trap 'rm -f "$TMP_JSON"' EXIT printf '%s\n' "$CONTROL_JSON" >"$TMP_JSON" if ! jq -e . "$TMP_JSON" >/dev/null 2>&1; then @@ -84,12 +84,6 @@ CONTROL_RUN_ID="$(jq -r '.run_id // empty' "$TMP_JSON")" CONTROL_RUN_ATTEMPT="$(jq -r '.run_attempt // empty' "$TMP_JSON")" RESULT="$(jq -r '.result // empty' "$TMP_JSON")" -if [ "$RESULT" = "APPROVE" ]; then - TMP_NORMALIZED_JSON="${TMP_JSON}.normalized" - jq '.findings = (.findings // [])' "$TMP_JSON" >"$TMP_NORMALIZED_JSON" - mv "$TMP_NORMALIZED_JSON" "$TMP_JSON" -fi - if [ "$CONTROL_HEAD_SHA" != "$EXPECTED_HEAD_SHA" ]; then echo "SHA_MISMATCH" exit 3 @@ -113,13 +107,12 @@ if ! jq -e ' and (.result == "APPROVE" or .result == "REQUEST_CHANGES") and (.reason | type == "string" and length > 0) and (.summary | type == "string" and length > 0) - and (.findings | type == "array") and ( - if .result == "REQUEST_CHANGES" then (.findings | length > 0) - else (.findings | length == 0) + if .result == "REQUEST_CHANGES" then (.findings | type == "array" and length > 0) + else ((.findings == null) or (.findings | type == "array" and length == 0)) end ) - and all(.findings[]; + and all((.findings // [])[]; (.path | type == "string" and length > 0) and ((.path | ascii_downcase) as $p | ($p != "n/a" and $p != "unknown")) and (.line | type == "number" and . > 0 and floor == .) @@ -142,12 +135,14 @@ if ! python3 "$NORMALIZER" --check-structural-approval "$TMP_JSON" >/dev/null; t exit 4 fi -SOURCE_ROOT="${GITHUB_WORKSPACE:-$PWD}" +SOURCE_ROOT="${OPENCODE_SOURCE_WORKDIR:-${GITHUB_WORKSPACE:-$PWD}}" if ! python3 - "$SOURCE_ROOT" "$TMP_JSON" <<'PY' from __future__ import annotations import json import os +import re +import subprocess import sys from pathlib import Path @@ -155,6 +150,11 @@ from pathlib import Path source_root = Path(sys.argv[1]).resolve() control_file = Path(sys.argv[2]) control = json.loads(control_file.read_text(encoding="utf-8")) +pr_base_sha = os.environ.get("PR_BASE_SHA", "").strip() +pr_head_sha = ( + os.environ.get("PR_HEAD_SHA", "").strip() + or os.environ.get("HEAD_SHA", "").strip() +) if control.get("result") != "REQUEST_CHANGES": raise SystemExit(0) @@ -164,6 +164,47 @@ def normalized_line(value: str) -> str: return " ".join(value.strip().split()) +def changed_new_lines(path_value: str) -> set[int]: + if not pr_base_sha or not pr_head_sha: + return set() + try: + completed = subprocess.run( + [ + "git", + "-C", + str(source_root), + "diff", + "--unified=0", + "--no-ext-diff", + pr_base_sha, + pr_head_sha, + "--", + path_value, + ], + check=False, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + ) + except OSError: + return set() + if completed.returncode not in {0, 1}: + return set() + + line_numbers: set[int] = set() + hunk_header = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@") + for raw_line in completed.stdout.splitlines(): + match = hunk_header.match(raw_line) + if not match: + continue + start = int(match.group(1)) + count = int(match.group(2) or "1") + if count <= 0: + continue + line_numbers.update(range(start, start + count)) + return line_numbers + + def finding_is_source_backed(finding: dict[str, object]) -> bool: path_value = str(finding.get("path", "")) if ( @@ -190,6 +231,8 @@ def finding_is_source_backed(finding: dict[str, object]) -> bool: line_number = finding.get("line") if not isinstance(line_number, int) or line_number < 1 or line_number > len(source_lines): return False + if line_number not in changed_new_lines(path_value): + return False source_line_set = { normalized_line(line) @@ -228,7 +271,7 @@ then fi if [ -n "$NORMALIZED_JSON_FILE" ]; then - jq -c '{head_sha, run_id, run_attempt, result, reason, summary, findings}' "$TMP_JSON" >"$NORMALIZED_JSON_FILE" + jq -c '{head_sha, run_id, run_attempt, result, reason, summary, findings:(.findings // [])}' "$TMP_JSON" >"$NORMALIZED_JSON_FILE" fi echo "$RESULT" diff --git a/scripts/ci/opencode_review_normalize_output.py b/scripts/ci/opencode_review_normalize_output.py index c7bd9c41..32145f8f 100755 --- a/scripts/ci/opencode_review_normalize_output.py +++ b/scripts/ci/opencode_review_normalize_output.py @@ -25,9 +25,6 @@ "structural exploration is unnecessary", "structural analysis is unnecessary", "structural review is unnecessary", - "could not be reviewed", - "could not inspect", - "could not be inspected", "changed files could not be inspected", "source files could not be inspected", "required files could not be inspected", @@ -37,13 +34,17 @@ "could not access the source files", "could not access required files", "could not access required evidence", - "file access issues", - "file inaccessibility", "evidence was truncated", - "not provided in evidence", "truncated evidence", - "unable to inspect", - "insufficient evidence", + "no changes detected", + "no changes were detected", + "no changes found", + "no changes were found", + "no files or changes were found", + "no files or changes found", + "no actionable changes to review", + "no changes to review", + "no changed files", ) STRUCTURAL_FAILURE_PATTERNS = ( @@ -61,6 +62,20 @@ r"\b(?:structural\s+(?:exploration|analysis|review))\s+" r"(?:was\s+)?(?:unavailable|incomplete|blocked|not possible)\b" ), + re.compile( + r"\bno\s+(?:files?\s+or\s+)?changes?\s+" + r"(?:were\s+)?(?:detected|found|present)\b" + ), + re.compile(r"\bno\s+(?:actionable\s+)?changes?\s+to\s+review\b"), + re.compile(r"\b(?:no|zero)\s+changed\s+files?\b"), +) + +CHANGED_FILE_EVIDENCE_PATTERN = re.compile( + r"(? bool: ) +def mentions_changed_file_evidence(reason: str, summary: str) -> bool: + """Return whether an approval names at least one concrete changed file/path.""" + return bool(CHANGED_FILE_EVIDENCE_PATTERN.search(f"{reason}\n{summary}")) + + def check_structural_approval(control_file: Path) -> int: - """Reject approvals whose control JSON admits missing structural review.""" + """Validate an already-normalized control block before publishing approval.""" try: value = json.loads(control_file.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as exc: @@ -90,6 +110,12 @@ def check_structural_approval(control_file: Path) -> int: ): print("NO_CONCLUSION", file=sys.stderr) return 4 + if value.get("result") == "APPROVE" and not mentions_changed_file_evidence( + str(value.get("reason", "")), + str(value.get("summary", "")), + ): + print("NO_CONCLUSION", file=sys.stderr) + return 4 return 0 @@ -101,7 +127,7 @@ def valid_control( expected_run_id: str, expected_run_attempt: str, ) -> dict[str, Any] | None: - """Return a normalized review control object when all gate fields are valid.""" + """Return a normalized control block when it matches the current run.""" if not isinstance(value, dict): return None @@ -134,6 +160,8 @@ def valid_control( return None if result == "APPROVE" and admits_missing_structural_review(reason, summary): return None + if result == "APPROVE" and not mentions_changed_file_evidence(reason, summary): + return None required_finding_fields = ( "path", @@ -148,7 +176,8 @@ def valid_control( for finding in findings: if not isinstance(finding, dict): return None - if not isinstance(finding.get("line"), int) or finding["line"] <= 0: + line = finding.get("line") + if isinstance(line, bool) or not isinstance(line, int) or line <= 0: return None for field in required_finding_fields: if not isinstance(finding.get(field), str) or not finding[field].strip(): @@ -166,7 +195,7 @@ def valid_control( def iter_json_objects(text: str) -> list[Any]: - """Extract JSON objects from possibly noisy OpenCode output text.""" + """Extract JSON objects from raw OpenCode output that may include prose.""" decoder = json.JSONDecoder() values: list[Any] = [] @@ -189,7 +218,7 @@ def iter_json_objects(text: str) -> list[Any]: def main(argv: list[str]) -> int: - """Normalize an OpenCode output file for the shell approval gate.""" + """Run the normalizer CLI and write the publishable control block.""" if len(argv) == 3 and argv[1] == "--check-structural-approval": return check_structural_approval(Path(argv[2])) diff --git a/scripts/ci/pr_review_merge_scheduler.py b/scripts/ci/pr_review_merge_scheduler.py index cdf9f7c1..1c30dd96 100644 --- a/scripts/ci/pr_review_merge_scheduler.py +++ b/scripts/ci/pr_review_merge_scheduler.py @@ -149,13 +149,11 @@ def is_opencode_context(node: dict[str, Any]) -> bool: """Return whether a status node belongs to OpenCode review.""" if node.get("__typename") == "CheckRun": - workflow = ((node.get("checkSuite") or {}).get("workflowRun") or {}).get( - "workflow" - ) or {} - return ( - node.get("name") == "opencode-review" - or workflow.get("name") == "OpenCode Review" + workflow = ( + ((node.get("checkSuite") or {}).get("workflowRun") or {}).get("workflow") + or {} ) + return node.get("name") == "opencode-review" or workflow.get("name") == "OpenCode Review" return node.get("context") == "opencode-review" @@ -174,12 +172,8 @@ def opencode_in_progress(pr: dict[str, Any]) -> bool: def unresolved_thread_count(pr: dict[str, Any]) -> int: """Count active unresolved review threads.""" - threads = (pr.get("reviewThreads") or {}).get("nodes") or [] - return sum( - 1 - for thread in threads - if not thread.get("isResolved") and not thread.get("isOutdated") - ) + threads = ((pr.get("reviewThreads") or {}).get("nodes") or []) + return sum(1 for thread in threads if not thread.get("isResolved") and not thread.get("isOutdated")) def review_author_login(review: dict[str, Any]) -> str: @@ -193,11 +187,7 @@ def is_opencode_review(review: dict[str, Any]) -> bool: login = review_author_login(review) body = review.get("body") or "" - return ( - login.startswith("opencode-agent") - or "opencode" in login - or "OpenCode Agent" in body - ) + return login.startswith("opencode-agent") or "opencode" in login or "OpenCode Agent" in body def current_head_review_state(pr: dict[str, Any], state: str) -> bool: @@ -234,25 +224,10 @@ def enable_auto_merge(repo: str, pr: dict[str, Any], *, dry_run: bool) -> None: head = pr["headRefOid"] if dry_run: return - run( - [ - "gh", - "pr", - "merge", - number, - "--repo", - repo, - "--auto", - "--merge", - "--match-head-commit", - head, - ] - ) + run(["gh", "pr", "merge", number, "--repo", repo, "--auto", "--merge", "--match-head-commit", head]) -def dispatch_opencode_review( - repo: str, workflow: str, pr: dict[str, Any], *, dry_run: bool -) -> None: +def dispatch_opencode_review(repo: str, workflow: str, pr: dict[str, Any], *, dry_run: bool) -> None: """Dispatch the OpenCode review workflow for a pull request.""" if dry_run: @@ -281,7 +256,16 @@ def dispatch_opencode_review( ) -def inspect_pr(pr: dict[str, Any], args: argparse.Namespace) -> Decision: +def inspect_pr( + repo: str, + pr: dict[str, Any], + *, + dry_run: bool, + trigger_reviews: bool, + enable_auto_merge_flag: bool, + workflow: str, + base_branch: str, +) -> Decision: """Inspect a pull request and select the scheduler action.""" number = pr["number"] @@ -290,11 +274,9 @@ def inspect_pr(pr: dict[str, Any], args: argparse.Namespace) -> Decision: if pr.get("isDraft"): return Decision(number, "skip", "draft PR") - if base_ref != args.base_branch: - return Decision( - number, "skip", f"base branch is {base_ref}; expected {args.base_branch}" - ) - if head_repo != args.repo: + if base_ref != base_branch: + return Decision(number, "skip", f"base branch is {base_ref}; expected {base_branch}") + if head_repo != repo: return Decision(number, "skip", f"fork or external head repo: {head_repo}") unresolved = unresolved_thread_count(pr) @@ -302,36 +284,22 @@ def inspect_pr(pr: dict[str, Any], args: argparse.Namespace) -> Decision: return Decision(number, "block", f"{unresolved} unresolved review thread(s)") if has_current_head_changes_requested(pr): - return Decision( - number, "block", "current-head OpenCode review requested changes" - ) + return Decision(number, "block", "current-head OpenCode review requested changes") if has_current_head_approval(pr): if pr.get("autoMergeRequest"): - return Decision( - number, "wait", "current head is approved; auto-merge already enabled" - ) - if not args.enable_auto_merge: - return Decision( - number, - "wait", - "current head is approved; auto-merge disabled by scheduler inputs", - ) - enable_auto_merge(args.repo, pr, dry_run=args.dry_run) - return Decision( - number, "auto_merge", "current head is approved; auto-merge enabled" - ) + return Decision(number, "wait", "current head is approved; auto-merge already enabled") + if not enable_auto_merge_flag: + return Decision(number, "wait", "current head is approved; auto-merge disabled by scheduler inputs") + enable_auto_merge(repo, pr, dry_run=dry_run) + return Decision(number, "auto_merge", "current head is approved; auto-merge enabled") if opencode_in_progress(pr): return Decision(number, "wait", "OpenCode review is already in progress") - if args.trigger_reviews: - dispatch_opencode_review( - args.repo, args.review_workflow, pr, dry_run=args.dry_run - ) - return Decision( - number, "review_dispatch", "current head has no OpenCode approval" - ) + if trigger_reviews: + dispatch_opencode_review(repo, workflow, pr, dry_run=dry_run) + return Decision(number, "review_dispatch", "current head has no OpenCode approval") return Decision(number, "block", "current head has no OpenCode approval") @@ -411,36 +379,17 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument("--project-flow", default=os.environ.get("PROJECT_FLOW", "")) parser.add_argument("--max-prs", type=int, default=100) parser.add_argument("--dry-run", action="store_true") - parser.add_argument( - "--trigger-reviews", action=argparse.BooleanOptionalAction, default=True - ) - parser.add_argument( - "--enable-auto-merge", action=argparse.BooleanOptionalAction, default=True - ) + parser.add_argument("--trigger-reviews", action=argparse.BooleanOptionalAction, default=True) + parser.add_argument("--enable-auto-merge", action=argparse.BooleanOptionalAction, default=True) parser.add_argument("--review-workflow", default="OpenCode Review") parser.add_argument("--self-test", action="store_true") return parser.parse_args(argv) -def validate_gh_host() -> None: - """Validate GH_HOST environment variable to prevent SSRF.""" - - host = os.environ.get("GH_HOST") - if not host: - return - if not ( - host == "github.com" - or host.endswith(".github.com") - or host.endswith(".githubapp.com") - ): - raise ValueError(f"Invalid GH_HOST: {host}") - - def main(argv: list[str]) -> int: """Run the PR review merge scheduler.""" args = parse_args(argv) - validate_gh_host() if args.self_test: self_test() return 0 @@ -451,7 +400,18 @@ def main(argv: list[str]) -> int: if not args.project_flow: raise SystemExit("--project-flow is required") prs = fetch_open_prs(args.repo, args.max_prs) - decisions = [inspect_pr(pr, args) for pr in prs] + decisions = [ + inspect_pr( + args.repo, + pr, + dry_run=args.dry_run, + trigger_reviews=args.trigger_reviews, + enable_auto_merge_flag=args.enable_auto_merge, + workflow=args.review_workflow, + base_branch=args.base_branch, + ) + for pr in prs + ] print_summary( decisions, dry_run=args.dry_run, diff --git a/scripts/ci/strix_model_utils.sh b/scripts/ci/strix_model_utils.sh new file mode 100755 index 00000000..8278dba4 --- /dev/null +++ b/scripts/ci/strix_model_utils.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# Helper functions shared by the Strix CI gate and its self-test harness. +# Keep this dependency explicit so PR-scoped Strix scans include the full gate harness. + +trim_whitespace() { + local value="${1-}" + # Collapse only the leading/trailing shell whitespace that can be introduced by + # secret files or workflow inputs. Internal spacing remains meaningful for the + # few callers that parse lists after trimming each token. + value="${value#"${value%%[!$' \t\r\n']*}"}" + value="${value%"${value##*[!$' \t\r\n']}"}" + printf '%s\n' "$value" +} + +sanitize_provider_name() { + local provider + provider="$(trim_whitespace "${1-}")" + if [ -z "$provider" ]; then + return 1 + fi + if [[ ! "$provider" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]]; then + echo "ERROR: STRIX_LLM_DEFAULT_PROVIDER contains unsupported characters: '$provider'." >&2 + return 2 + fi + printf '%s\n' "$provider" +} + +is_vertex_resource_path() { + local path + path="$(trim_whitespace "${1-}")" + if [ -z "$path" ] || [[ "$path" =~ [[:space:][:cntrl:]] ]]; then + return 1 + fi + + IFS='/' read -r -a parts <<<"$path" + local part + for part in "${parts[@]}"; do + if [ -z "$part" ]; then + return 1 + fi + done + + case "${#parts[@]}" in + 2) + [ "${parts[0]}" = "models" ] + ;; + 4) + [ "${parts[0]}" = "publishers" ] && [ "${parts[2]}" = "models" ] + ;; + 6) + [ "${parts[0]}" = "projects" ] && [ "${parts[2]}" = "locations" ] && [ "${parts[4]}" = "models" ] + ;; + 8) + [ "${parts[0]}" = "projects" ] && [ "${parts[2]}" = "locations" ] && [ "${parts[4]}" = "publishers" ] && [ "${parts[6]}" = "models" ] + ;; + *) + return 1 + ;; + esac +} + +extract_vertex_model_id() { + local model + model="$(trim_whitespace "${1-}")" + if is_vertex_resource_path "$model"; then + printf '%s\n' "${model##*/}" + else + printf '%s\n' "$model" + fi +} + +normalize_model() { + local model + model="$(trim_whitespace "${1-}")" + if [ -z "$model" ]; then + return 0 + fi + + if is_vertex_resource_path "$model"; then + local provider + provider="$(sanitize_provider_name "vertex_ai")" || return $? + printf '%s/%s\n' "$provider" "$(extract_vertex_model_id "$model")" + return 0 + fi + + local provider="${DEFAULT_PROVIDER:-}" + if [ -z "$provider" ]; then + provider="vertex_ai" + fi + provider="$(sanitize_provider_name "$provider")" || return $? + + case "$model" in + projects/* | models/* | publishers/*) + printf '%s\n' "$model" + return 0 + ;; + */*) + printf '%s\n' "$model" + return 0 + ;; + *) + printf '%s/%s\n' "$provider" "$model" + return 0 + ;; + esac +} + +model_requires_vertex_auth() { + local model normalized_model + model="$(trim_whitespace "${1-}")" + if [ -z "$model" ]; then + return 1 + fi + + normalized_model="$(normalize_model "$model")" || return $? + case "$normalized_model" in + vertex_ai/* | vertex_ai_beta/*) + return 0 + ;; + *) + return 1 + ;; + esac +} diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh new file mode 100755 index 00000000..47ff270c --- /dev/null +++ b/scripts/ci/strix_quick_gate.sh @@ -0,0 +1,3339 @@ +#!/usr/bin/env bash +# strix_quick_gate.sh — CI gate that runs Strix security scans with +# automatic model fallback, transient-error retry, and severity-based +# pass/fail decisions. +# +# STRIX_LOG is a per-attempt temp file consumed only by +# is_transient_same_model_retry_error(); cumulative report dirs in +# STRIX_REPORTS_DIR are never overwritten. Refer to ARCHITECTURE.md +# for the 3-tier timeout classification hierarchy. +set -euo pipefail + +SCRIPT_DIR="$({ CDPATH='' && cd -P -- "$(dirname -- "$0")" && pwd -P; })" +REPO_ROOT="$({ CDPATH='' && cd -P -- "$SCRIPT_DIR/../.." && pwd -P; })" +RAW_TARGET_PATH="${STRIX_TARGET_PATH:-./}" +TARGET_PATH="" +PR_SCOPE_TARGET_SENTINEL="__PR_SCOPE__" +TARGET_PATH_REQUESTS_PR_SCOPE=0 +RAW_SCAN_MODE="${STRIX_SCAN_MODE:-quick}" +SCAN_MODE="" +ARTIFACT_REPORTS_DIR="$REPO_ROOT/strix_runs" +STRIX_RUNTIME_DIR="$(mktemp -d /tmp/strix-runtime.XXXXXX)" +STRIX_LOG="$STRIX_RUNTIME_DIR/strix.log" +ACTIVE_REPORTS_DIR="$STRIX_RUNTIME_DIR/reports" +STRIX_REPORTS_DIR="$ACTIVE_REPORTS_DIR" +STRIX_PROCESS_TIMEOUT_SECONDS="${STRIX_PROCESS_TIMEOUT_SECONDS:-1200}" +STRIX_TOTAL_TIMEOUT_SECONDS="${STRIX_TOTAL_TIMEOUT_SECONDS:-0}" +STRIX_DISABLE_PR_SCOPING="${STRIX_DISABLE_PR_SCOPING:-1}" +# shellcheck disable=SC2034 # consumed by sourced normalize_model helper +DEFAULT_PROVIDER_RAW="${STRIX_LLM_DEFAULT_PROVIDER:-}" +# shellcheck disable=SC2034 # consumed indirectly by sourced model helper functions +DEFAULT_PROVIDER="" +LLM_API_BASE_FILE="${LLM_API_BASE_FILE:-}" +STRIX_INPUT_FILE_ROOT="${STRIX_INPUT_FILE_ROOT:-${RUNNER_TEMP:-}}" +STRIX_TRANSIENT_RETRY_PER_MODEL="${STRIX_TRANSIENT_RETRY_PER_MODEL:-0}" +STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS="${STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS:-3}" +STRIX_FAIL_ON_MIN_SEVERITY="${STRIX_FAIL_ON_MIN_SEVERITY:-MEDIUM}" +STRIX_FAIL_ON_PROVIDER_SIGNAL="${STRIX_FAIL_ON_PROVIDER_SIGNAL:-0}" +RUN_START_EPOCH="$(date +%s)" +PREEXISTING_REPORT_DIRS=() +REPO_NAME="${REPO_ROOT##*/}" +# shellcheck source=scripts/ci/strix_model_utils.sh +# shellcheck disable=SC1091 # source path is repo-local; local lint may omit -x +. "$SCRIPT_DIR/strix_model_utils.sh" +# Sticky flag: once ANY attempt encounters an infrastructure error (rate limit, +# LLM connection failure, mid-stream fallback, etc.), this flag stays 1 for +# the rest of the run. It prevents the "all findings below threshold" bypass +# from masking scan incompleteness — a successful strix run (exit 0) ignores +# this flag because the scan itself produced a complete result set. +INFRA_ERROR_DETECTED=0 +ZERO_FINDINGS_REPORTED=0 +PR_FINDINGS_DECISION="not_applicable" +CHANGED_FILES=() +PULL_REQUEST_CHANGED_FILES=() +NORMALIZED_CHANGED_FILES=() +PULL_REQUEST_SCOPE_DIRS=() +LAST_PULL_REQUEST_SCOPE_DIR="" +TARGET_PATH_IS_INTERNAL_PR_SCOPE=0 + +resolve_trusted_input_file() { + local label="$1" + local input_file="$2" + if [ -z "$input_file" ] || [ ! -f "$input_file" ] || [ -L "$input_file" ]; then + echo "ERROR: $label must reference a regular file." >&2 + return 2 + fi + if [ -z "$STRIX_INPUT_FILE_ROOT" ] || [ ! -d "$STRIX_INPUT_FILE_ROOT" ] || [ -L "$STRIX_INPUT_FILE_ROOT" ]; then + echo "ERROR: STRIX_INPUT_FILE_ROOT or RUNNER_TEMP must reference a trusted input file root." >&2 + return 2 + fi + + python3 - "$label" "$input_file" "$STRIX_INPUT_FILE_ROOT" <<'PY' +from pathlib import Path +import sys + +label = sys.argv[1] +input_path = Path(sys.argv[2]) +root_path = Path(sys.argv[3]) + +try: + resolved_input = input_path.resolve(strict=True) + resolved_root = root_path.resolve(strict=True) +except OSError as exc: + print(f"ERROR: {label} could not be canonicalized: {exc}", file=sys.stderr) + raise SystemExit(2) + +if not resolved_root.is_dir(): + print("ERROR: STRIX_INPUT_FILE_ROOT or RUNNER_TEMP must reference a trusted input file root.", file=sys.stderr) + raise SystemExit(2) +if not resolved_input.is_file(): + print(f"ERROR: {label} must reference a regular file.", file=sys.stderr) + raise SystemExit(2) +try: + resolved_input.relative_to(resolved_root) +except ValueError: + print(f"ERROR: {label} must be inside the trusted input file root.", file=sys.stderr) + raise SystemExit(2) + +print(resolved_input) +PY +} + +# shellcheck disable=SC2317,SC2329 # invoked from cleanup trap +publish_artifact_reports() { + if [ -L "$ARTIFACT_REPORTS_DIR" ]; then + echo "ERROR: artifact reports path must not be a symlink: $ARTIFACT_REPORTS_DIR" >&2 + return 1 + fi + rm -rf -- "$ARTIFACT_REPORTS_DIR" + mkdir -p -- "$ARTIFACT_REPORTS_DIR" + if [ -d "$ACTIVE_REPORTS_DIR" ]; then + cp -R -- "$ACTIVE_REPORTS_DIR"/. "$ARTIFACT_REPORTS_DIR"/ + fi + local scope_dir scope_reports_dir + for scope_dir in "${PULL_REQUEST_SCOPE_DIRS[@]}"; do + scope_reports_dir="$scope_dir/strix_runs" + if [ -d "$scope_reports_dir" ] && [ ! -L "$scope_reports_dir" ]; then + cp -R -- "$scope_reports_dir"/. "$ARTIFACT_REPORTS_DIR"/ + fi + done +} + +sanitize_known_strix_report_warnings() { + local report_root + for report_root in "$@"; do + if [ -z "$report_root" ] || [ ! -d "$report_root" ] || [ -L "$report_root" ]; then + continue + fi + python3 - "$report_root" <<'PY' +from pathlib import Path +import os +import re +import sys + +root = Path(sys.argv[1]) +known_internal_warning = re.compile( + r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ WARNING " + r"[^ ]+ - strix\.core\.execution: agent [0-9a-f]+ produced " + r"non-lifecycle final output in non-interactive mode; forcing tool " + r"continuation \(\d+/\d+\): " +) + + +def iter_report_logs(root: Path): + for current_root, dir_names, file_names in os.walk(root, topdown=True, followlinks=False): + current_path = Path(current_root) + dir_names[:] = [ + dir_name + for dir_name in dir_names + if not (current_path / dir_name).is_symlink() + ] + for file_name in file_names: + log_path = current_path / file_name + if log_path.suffix != ".log" or log_path.is_symlink() or not log_path.is_file(): + continue + yield log_path + + +for log_path in iter_report_logs(root): + try: + lines = log_path.read_text(encoding="utf-8").splitlines(keepends=True) + except UnicodeDecodeError: + continue + filtered = [line for line in lines if not known_internal_warning.match(line)] + if filtered != lines: + log_path.write_text("".join(filtered), encoding="utf-8") +PY + done +} + +has_strix_report_failure_signal() { + local report_root + local report_log + for report_root in "$@"; do + if [ -z "$report_root" ] || [ ! -d "$report_root" ] || [ -L "$report_root" ]; then + continue + fi + while IFS= read -r -d '' report_log; do + if grep -Eiq '(^|[^[:alpha:]])(Fatal|Denied|Warn|Warning|WARNING|Timeout)([^[:alpha:]]|$)' "$report_log"; then + return 0 + fi + done < <(find "$report_root" -type f -name '*.log' -print0) + done + return 1 +} + +# shellcheck disable=SC2317,SC2329 # invoked from EXIT/INT/TERM trap +cleanup_runtime() { + publish_artifact_reports || true + rm -f "$STRIX_LOG" + rm -rf "$STRIX_RUNTIME_DIR" + local scope_dir + for scope_dir in "${PULL_REQUEST_SCOPE_DIRS[@]}"; do + if [ -n "$scope_dir" ] && [ -d "$scope_dir" ]; then + rm -rf -- "$scope_dir" + fi + done +} + +trap cleanup_runtime EXIT INT TERM + +STRIX_LLM_FILE="${STRIX_LLM_FILE:-}" +if [ -z "$STRIX_LLM_FILE" ]; then + echo "ERROR: STRIX_LLM_FILE must reference a regular file containing the model." >&2 + exit 2 +fi +if [ ! -f "$STRIX_LLM_FILE" ] || [ -L "$STRIX_LLM_FILE" ]; then + echo "ERROR: STRIX_LLM_FILE must reference a regular file containing the model." >&2 + exit 2 +fi +if ! STRIX_LLM_FILE="$(resolve_trusted_input_file "STRIX_LLM_FILE" "$STRIX_LLM_FILE")"; then + exit 2 +fi +STRIX_LLM_CONTENT="$(cat -- "$STRIX_LLM_FILE")" +STRIX_LLM="$(trim_whitespace "$STRIX_LLM_CONTENT")" +if [ -z "$STRIX_LLM" ]; then + echo "ERROR: STRIX_LLM_FILE must contain a non-empty model value." >&2 + exit 2 +fi + +is_vertex_model() { + case "$1" in + vertex_ai/* | vertex_ai_beta/*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +is_gemini_model() { + case "$1" in + gemini/*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +NORMALIZED_STRIX_LLM="$(normalize_model "$STRIX_LLM")" + +LLM_API_KEY_FILE="${LLM_API_KEY_FILE:-}" +if [ -z "$LLM_API_KEY_FILE" ] && ! is_vertex_model "$NORMALIZED_STRIX_LLM"; then + echo "ERROR: LLM_API_KEY_FILE must reference a regular file containing the API key." >&2 + exit 2 +fi +if [ -n "$LLM_API_KEY_FILE" ] && { [ ! -f "$LLM_API_KEY_FILE" ] || [ -L "$LLM_API_KEY_FILE" ]; }; then + echo "ERROR: LLM_API_KEY_FILE must reference a regular file containing the API key." >&2 + exit 2 +fi +if [ -n "$LLM_API_KEY_FILE" ] && ! LLM_API_KEY_FILE="$(resolve_trusted_input_file "LLM_API_KEY_FILE" "$LLM_API_KEY_FILE")"; then + exit 2 +fi +LLM_API_KEY="" +if [ -n "$LLM_API_KEY_FILE" ]; then + LLM_API_KEY="$(trim_whitespace "$(cat -- "$LLM_API_KEY_FILE")")" +fi +if [ -z "$LLM_API_KEY" ] && ! is_vertex_model "$NORMALIZED_STRIX_LLM"; then + echo "ERROR: LLM_API_KEY_FILE must contain a non-empty API key." >&2 + exit 2 +fi + +require_non_negative_integer() { + local value="$1" + local label="$2" + if ! [[ "$value" =~ ^[0-9]+$ ]]; then + echo "ERROR: $label must be a non-negative integer, got '$value'." >&2 + exit 2 + fi +} + +require_positive_integer() { + local value="$1" + local label="$2" + require_non_negative_integer "$value" "$label" + if [ "$value" -le 0 ]; then + echo "ERROR: $label must be greater than zero, got '$value'." >&2 + exit 2 + fi + return 0 +} + +require_safe_scan_mode() { + local scan_mode="$1" + if [ -z "$scan_mode" ] || [[ ! "$scan_mode" =~ ^[[:alnum:]_.-]+$ ]]; then + echo "ERROR: STRIX_SCAN_MODE contains unsupported characters: '$scan_mode'." >&2 + exit 2 + fi +} + +validate_raw_target_path_input() { + local raw_target + raw_target="$(trim_whitespace "$1")" + if [ -z "$raw_target" ]; then + echo "ERROR: STRIX_TARGET_PATH must not be empty." >&2 + return 2 + fi + if [[ "$raw_target" == -* ]]; then + echo "ERROR: STRIX_TARGET_PATH contains unsupported path syntax: '$raw_target'." >&2 + return 2 + fi + case "$raw_target" in + . | ./ | src | ./src | "$PR_SCOPE_TARGET_SENTINEL") + printf '%s\n' "$raw_target" + return 0 + ;; + *) + echo "ERROR: STRIX_TARGET_PATH contains unsupported path syntax: '$raw_target'." >&2 + return 2 + ;; + esac +} + +normalize_changed_file_path() { + local changed_file="$1" + python3 - "$REPO_ROOT" "$changed_file" <<'PY' +from pathlib import Path +import posixpath +import re +import sys + +repo_root = Path(sys.argv[1]).resolve(strict=True) +relative_path_str = sys.argv[2] +if "\n" in relative_path_str or "\r" in relative_path_str: + raise SystemExit(1) +if not relative_path_str: + raise SystemExit(1) +if relative_path_str != relative_path_str.strip(): + raise SystemExit(1) +if "\x00" in relative_path_str: + raise SystemExit(1) +if "\\" in relative_path_str: + raise SystemExit(1) +normalized = posixpath.normpath(relative_path_str) +if normalized in (".", "") or normalized.startswith("../") or normalized == "..": + raise SystemExit(1) +if not re.fullmatch(r"[A-Za-z0-9_./ \[\]-]+", normalized): + raise SystemExit(1) +relative_path = Path(normalized) +if relative_path.is_absolute(): + raise SystemExit(1) +if any(part in ('', '.', '..') for part in relative_path.parts): + raise SystemExit(1) +candidate = (repo_root / relative_path).resolve(strict=False) +candidate.relative_to(repo_root) +print(relative_path.as_posix()) +PY +} + +normalize_changed_files_cache() { + NORMALIZED_CHANGED_FILES=() + local changed_file normalized_changed_file + for changed_file in "${CHANGED_FILES[@]}"; do + normalized_changed_file="$(normalize_changed_file_path "$changed_file")" || { + if pull_request_head_blob_required; then + echo "ERROR: pull request changed file path is unsafe: $changed_file" >&2 + return 2 + fi + continue + } + NORMALIZED_CHANGED_FILES+=("$normalized_changed_file") + done +} + +pull_request_metadata_env_present() { + [ -n "$(trim_whitespace "${PR_NUMBER:-}")" ] && + [ -n "$(trim_whitespace "${PR_BASE_SHA:-}")" ] && + [ -n "$(trim_whitespace "${PR_HEAD_SHA:-}")" ] +} + +pull_request_head_blob_required() { + [ "${GITHUB_EVENT_NAME:-}" = "pull_request_target" ] || + { [ "${GITHUB_EVENT_NAME:-}" = "workflow_dispatch" ] && pull_request_metadata_env_present; } +} + +is_valid_git_commit_sha() { + local sha="$1" + [[ "$sha" =~ ^[0-9a-fA-F]{40}$ || "$sha" =~ ^[0-9a-fA-F]{64}$ ]] +} + +invalid_pull_request_sha() { + local label="$1" + echo "ERROR: pull request $label commit SHA is invalid; failing closed." >&2 + return 2 +} + +pr_head_regular_file_mode() { + local relative_path="$1" + local head_sha tree_output line_count metadata tree_path mode object_type _object_hash + head_sha="$(trim_whitespace "${PR_HEAD_SHA:-}")" + if [ -z "$head_sha" ]; then + return 2 + fi + if ! is_valid_git_commit_sha "$head_sha"; then + return 2 + fi + if ! git rev-parse --verify --quiet "$head_sha^{commit}" >/dev/null; then + return 2 + fi + if ! tree_output="$(git ls-tree "$head_sha" -- "$relative_path")"; then + return 2 + fi + if [ -z "$tree_output" ]; then + return 1 + fi + line_count="$(printf '%s\n' "$tree_output" | wc -l | tr -d ' ')" + if [ "$line_count" != "1" ]; then + return 2 + fi + IFS=$'\t' read -r metadata tree_path <<<"$tree_output" + # shellcheck disable=SC2086 # metadata is exactly git ls-tree's mode/type/object tuple. + read -r mode object_type _object_hash <<<"$metadata" + if [ "$tree_path" != "$relative_path" ]; then + return 2 + fi + if [ "$object_type" != "blob" ]; then + return 3 + fi + case "$mode" in + 100644 | 100755) + printf '%s\n' "$mode" + return 0 + ;; + *) + return 3 + ;; + esac +} + +changed_file_exists_for_scan() { + local relative_path="$1" + if pull_request_head_blob_required; then + local mode_rc=0 + pr_head_regular_file_mode "$relative_path" >/dev/null || mode_rc=$? + case "$mode_rc" in + 0) + return 0 + ;; + 1) + return 1 + ;; + 3) + echo "ERROR: pull request changed file is not a regular PR-head file; failing closed: $relative_path" >&2 + return 2 + ;; + *) + echo "ERROR: pull request changed file could not be read from PR head; failing closed: $relative_path" >&2 + return 2 + ;; + esac + fi + if [ -f "$REPO_ROOT/$relative_path" ] && [ ! -L "$REPO_ROOT/$relative_path" ]; then + return 0 + fi + if [ -z "$(trim_whitespace "${PR_HEAD_SHA:-}")" ]; then + return 1 + fi + local mode_rc=0 + pr_head_regular_file_mode "$relative_path" >/dev/null || mode_rc=$? + case "$mode_rc" in + 0) + return 0 + ;; + 2) + return 2 + ;; + 3) + echo "ERROR: pull request changed file is not a regular PR-head file; failing closed: $relative_path" >&2 + return 2 + ;; + *) + return 1 + ;; + esac +} + +copy_pr_head_blob_to_file() { + local relative_path="$1" + local dst_path="$2" + local head_sha mode_rc tmp_dst + head_sha="$(trim_whitespace "${PR_HEAD_SHA:-}")" + mode_rc=0 + pr_head_regular_file_mode "$relative_path" >/dev/null || mode_rc=$? + if [ "$mode_rc" -ne 0 ]; then + return 2 + fi + tmp_dst="$(mktemp "$(dirname -- "$dst_path")/.pr-head.XXXXXX")" || return 2 + if ! git show "$head_sha:$relative_path" >"$tmp_dst"; then + rm -f -- "$tmp_dst" + return 2 + fi + if ! mv -- "$tmp_dst" "$dst_path"; then + rm -f -- "$tmp_dst" + return 2 + fi + # PR-head files are scanner input data in privileged workflows. Preserve the + # blob content only; never preserve executable bits from untrusted heads. + chmod 644 "$dst_path" || return 2 +} + +is_supported_source_file() { + case "$1" in + *.java | *.kt | *.kts | *.groovy | *.scala | *.py | *.js | *.jsx | *.ts | *.tsx | *.vue | *.yaml | *.yml | *.sh | *.sql | *.xml | *.json | *.html | *.css | *.md) + return 0 + ;; + Dockerfile | */Dockerfile | Containerfile | */Containerfile | Makefile | */Makefile) + return 0 + ;; + *) + return 1 + ;; + esac +} + +is_dependency_manifest_path() { + case "$1" in + pom.xml | */pom.xml | package.json | */package.json | package-lock.json | */package-lock.json | pnpm-lock.yaml | */pnpm-lock.yaml | yarn.lock | */yarn.lock | pyproject.toml | */pyproject.toml | requirements.txt | */requirements.txt | requirements-*.txt | */requirements-*.txt | uv.lock | */uv.lock) + return 0 + ;; + *) + return 1 + ;; + esac +} + +all_vulnerability_locations_are_dependency_manifests() { + local vulnerability_location + if [ "$#" -eq 0 ]; then + return 1 + fi + for vulnerability_location in "$@"; do + if ! is_dependency_manifest_path "$vulnerability_location"; then + return 1 + fi + done + return 0 +} + +severity_rank() { + case "${1^^}" in + CRITICAL) + echo 4 + ;; + HIGH) + echo 3 + ;; + MEDIUM) + echo 2 + ;; + LOW) + echo 1 + ;; + INFO | INFORMATIONAL | NONE) + echo 0 + ;; + *) + echo -1 + ;; + esac +} + +capture_preexisting_report_dirs() { + local run_dir + for run_dir in "$STRIX_REPORTS_DIR"/*; do + if [ ! -d "$run_dir" ]; then + continue + fi + PREEXISTING_REPORT_DIRS+=("$run_dir") + done +} + +is_preexisting_report_dir() { + local candidate="$1" + local existing + + for existing in "${PREEXISTING_REPORT_DIRS[@]}"; do + if [ "$candidate" = "$existing" ]; then + return 0 + fi + done + + return 1 +} + +is_github_models_model() { + case "$1" in + openai/openai/* | github_models/* | \ + openai/gpt-5* | openai/gpt-[6-9]* | openai/gpt-[1-9][0-9]* | \ + openai/deepseek/* | openai/meta/* | openai/mistral-ai/* | \ + deepseek/* | meta/* | mistral-ai/*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +is_github_models_api_compatible_model() { + case "$1" in + openai/openai/* | github_models/* | \ + openai/gpt-5* | openai/gpt-[6-9]* | openai/gpt-[1-9][0-9]* | \ + openai/deepseek/* | openai/meta/* | openai/mistral-ai/* | \ + deepseek/* | meta/* | mistral-ai/*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +is_github_models_api_base() { + local api_base="$1" + case "$api_base" in + https://models.github.ai | https://models.github.ai/*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +# shellcheck disable=SC2034 # consumed indirectly by sourced model helper functions +if DEFAULT_PROVIDER_SANITIZED="$(sanitize_provider_name "$DEFAULT_PROVIDER_RAW")"; then + DEFAULT_PROVIDER="$DEFAULT_PROVIDER_SANITIZED" +else + case $? in + 1) + DEFAULT_PROVIDER="" + ;; + *) + exit 2 + ;; + esac +fi + +PRIMARY_MODEL="$(normalize_model "$STRIX_LLM")" +if [ "$PRIMARY_MODEL" != "$STRIX_LLM" ]; then + echo "Normalized STRIX_LLM to provider-qualified model '$PRIMARY_MODEL'." +fi +if is_github_models_model "$PRIMARY_MODEL" && [ -z "$LLM_API_BASE_FILE" ]; then + echo "ERROR: GitHub Models Strix scans require LLM_API_BASE_FILE to select the GitHub Models inference endpoint." >&2 + exit 2 +fi + +require_non_negative_integer "$STRIX_TRANSIENT_RETRY_PER_MODEL" "STRIX_TRANSIENT_RETRY_PER_MODEL" +require_non_negative_integer "$STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS" "STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS" +require_non_negative_integer "$STRIX_PROCESS_TIMEOUT_SECONDS" "STRIX_PROCESS_TIMEOUT_SECONDS" +require_non_negative_integer "$STRIX_TOTAL_TIMEOUT_SECONDS" "STRIX_TOTAL_TIMEOUT_SECONDS" +case "$STRIX_FAIL_ON_PROVIDER_SIGNAL" in +0 | 1) + ;; +*) + echo "ERROR: STRIX_FAIL_ON_PROVIDER_SIGNAL must be 0 or 1, got '$STRIX_FAIL_ON_PROVIDER_SIGNAL'." >&2 + exit 2 + ;; +esac + +if [ "$(severity_rank "$STRIX_FAIL_ON_MIN_SEVERITY")" -lt 0 ]; then + echo "ERROR: STRIX_FAIL_ON_MIN_SEVERITY must be one of CRITICAL/HIGH/MEDIUM/LOW/INFO/INFORMATIONAL/NONE, got '$STRIX_FAIL_ON_MIN_SEVERITY'." >&2 + exit 2 +fi + +remaining_total_budget() { + if [ "$STRIX_TOTAL_TIMEOUT_SECONDS" -eq 0 ]; then + echo 0 + return 0 + fi + + local now elapsed remaining + now="$(date +%s)" + elapsed=$((now - RUN_START_EPOCH)) + remaining=$((STRIX_TOTAL_TIMEOUT_SECONDS - elapsed)) + if [ "$remaining" -lt 0 ]; then + remaining=0 + fi + echo "$remaining" +} + +provider_signal_fail_closed_enabled() { + [ "$STRIX_FAIL_ON_PROVIDER_SIGNAL" = "1" ] +} + +capture_preexisting_report_dirs + +github_event_payload_has_pull_request() { + if [ "${STRIX_TEST_CHANGED_FILES_OVERRIDE+x}" = x ] || { [ -n "${PR_BASE_SHA:-}" ] && [ -n "${PR_HEAD_SHA:-}" ]; }; then + return 0 + fi + if [ -z "${GITHUB_EVENT_PATH:-}" ] || [ ! -f "$GITHUB_EVENT_PATH" ]; then + return 1 + fi + python3 - "$GITHUB_EVENT_PATH" <<'PY' +import json, sys +with open(sys.argv[1], 'r', encoding='utf-8') as fh: + payload = json.load(fh) +pull_request = payload.get('pull_request') or {} +base = ((pull_request.get('base') or {}).get('sha')) or '' +head = ((pull_request.get('head') or {}).get('sha')) or '' +raise SystemExit(0 if base and head else 1) +PY +} + +is_pull_request_event() { + case "${GITHUB_EVENT_NAME:-}" in + pull_request | pull_request_target) + github_event_payload_has_pull_request + ;; + workflow_dispatch) + pull_request_metadata_env_present + ;; + *) + return 1 + ;; + esac +} + +path_is_within_allowed_scope() { + local resolved_target="$1" + case "$resolved_target" in + "$REPO_ROOT" | "$REPO_ROOT"/*) + return 0 + ;; + esac + + return 1 +} + +path_is_within_generated_pr_scope() { + local resolved_target="$1" + + local scope_dir + for scope_dir in "${PULL_REQUEST_SCOPE_DIRS[@]}"; do + scope_dir="$({ CDPATH='' && cd -P -- "$scope_dir" && pwd -P; })" + case "$resolved_target" in + "$scope_dir" | "$scope_dir"/*) + return 0 + ;; + esac + done + + return 1 +} + +resolve_scan_target_path() { + local raw_target="$1" + local resolved_target + resolved_target="$({ + python3 - "$REPO_ROOT" "$raw_target" <<'PY' +from pathlib import Path +import sys + +repo_root = Path(sys.argv[1]).resolve(strict=True) +raw_target = sys.argv[2] +target_path = Path(raw_target) +if not target_path.is_absolute(): + target_path = repo_root / target_path + +resolved = target_path.resolve(strict=False) +print(resolved) +PY + })" || { + echo "ERROR: STRIX_TARGET_PATH '$raw_target' must resolve to a valid path." >&2 + return 2 + } + if ! path_is_within_allowed_scope "$resolved_target"; then + echo "ERROR: STRIX_TARGET_PATH '$raw_target' must stay within the repository." >&2 + return 2 + fi + if [ ! -e "$resolved_target" ]; then + echo "ERROR: STRIX_TARGET_PATH '$raw_target' must resolve to an existing directory." >&2 + return 2 + fi + if [ ! -d "$resolved_target" ] || [ -L "$resolved_target" ]; then + echo "ERROR: STRIX_TARGET_PATH '$raw_target' must resolve to a real directory." >&2 + return 2 + fi + printf '%s\n' "$resolved_target" +} + +resolve_internal_pr_scope_target_path() { + local raw_target="$1" + local resolved_target + resolved_target="$({ + python3 - "$raw_target" <<'PY' +from pathlib import Path +import sys + +raw_target = sys.argv[1] +target_path = Path(raw_target) +resolved = target_path.resolve(strict=False) +print(resolved) +PY + })" || { + echo "ERROR: internal PR scope target '$raw_target' must resolve to a valid path." >&2 + return 2 + } + if ! path_is_within_generated_pr_scope "$resolved_target"; then + echo "ERROR: internal PR scope target '$raw_target' must stay within generated PR scope directories." >&2 + return 2 + fi + if [ ! -e "$resolved_target" ]; then + echo "ERROR: internal PR scope target '$raw_target' must resolve to an existing directory." >&2 + return 2 + fi + if [ ! -d "$resolved_target" ] || [ -L "$resolved_target" ]; then + echo "ERROR: internal PR scope target '$raw_target' must resolve to a real directory." >&2 + return 2 + fi + printf '%s\n' "$resolved_target" +} + +resolve_current_target_path() { + local raw_target="$1" + if [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -eq 1 ]; then + resolve_internal_pr_scope_target_path "$raw_target" + return $? + fi + resolve_scan_target_path "$raw_target" +} + +SCAN_MODE="$(trim_whitespace "$RAW_SCAN_MODE")" +require_safe_scan_mode "$SCAN_MODE" +if ! RAW_TARGET_PATH="$(validate_raw_target_path_input "$RAW_TARGET_PATH")"; then + exit 2 +fi +if [ "$RAW_TARGET_PATH" = "$PR_SCOPE_TARGET_SENTINEL" ]; then + if ! is_pull_request_event || [ "$STRIX_DISABLE_PR_SCOPING" = "1" ]; then + echo "ERROR: STRIX_TARGET_PATH=$PR_SCOPE_TARGET_SENTINEL requires PR scoping." >&2 + exit 2 + fi + TARGET_PATH="$REPO_ROOT" + TARGET_PATH_REQUESTS_PR_SCOPE=1 +else + if ! TARGET_PATH="$(resolve_scan_target_path "$RAW_TARGET_PATH")"; then + exit 2 + fi +fi + +load_pull_request_changed_files() { + CHANGED_FILES=() + PULL_REQUEST_CHANGED_FILES=() + + if [ "${STRIX_TEST_CHANGED_FILES_OVERRIDE+x}" = x ]; then + while IFS= read -r changed_file; do + if [ -n "$changed_file" ]; then + CHANGED_FILES+=("$changed_file") + PULL_REQUEST_CHANGED_FILES+=("$changed_file") + fi + done <<<"$STRIX_TEST_CHANGED_FILES_OVERRIDE" + normalize_changed_files_cache || return 2 + return 0 + fi + + if ! is_pull_request_event; then + return 1 + fi + + local base_sha head_sha + base_sha="$(trim_whitespace "${PR_BASE_SHA:-}")" + head_sha="$(trim_whitespace "${PR_HEAD_SHA:-}")" + if [ -z "$base_sha" ] || [ -z "$head_sha" ]; then + if [ -z "${GITHUB_EVENT_PATH:-}" ] || [ ! -f "$GITHUB_EVENT_PATH" ]; then + return 1 + fi + + local pr_shas + pr_shas="$( + python3 - "$GITHUB_EVENT_PATH" <<'PY' +import json, sys +with open(sys.argv[1], 'r', encoding='utf-8') as fh: + payload = json.load(fh) +pull_request = payload.get('pull_request') or {} +base = ((pull_request.get('base') or {}).get('sha')) or '' +head = ((pull_request.get('head') or {}).get('sha')) or '' +print(base) +print(head) +PY + )" + base_sha="$(printf '%s' "$pr_shas" | sed -n '1p')" + head_sha="$(printf '%s' "$pr_shas" | sed -n '2p')" + fi + if [ -z "$base_sha" ] || [ -z "$head_sha" ]; then + if pull_request_head_blob_required; then + echo "ERROR: pull request base/head metadata is unavailable; failing closed." >&2 + return 2 + fi + return 1 + fi + if ! is_valid_git_commit_sha "$base_sha"; then + if pull_request_head_blob_required; then + invalid_pull_request_sha "base" + return 2 + fi + return 1 + fi + if ! is_valid_git_commit_sha "$head_sha"; then + if pull_request_head_blob_required; then + invalid_pull_request_sha "head" + return 2 + fi + return 1 + fi + if ! git rev-parse --verify --quiet "$base_sha^{commit}" >/dev/null; then + if pull_request_head_blob_required; then + echo "ERROR: pull request base commit could not be read; failing closed: $base_sha" >&2 + return 2 + fi + return 1 + fi + if ! git rev-parse --verify --quiet "$head_sha^{commit}" >/dev/null; then + if pull_request_head_blob_required; then + echo "ERROR: pull request head commit could not be read; failing closed: $head_sha" >&2 + return 2 + fi + return 1 + fi + + local changed_files_output + if ! changed_files_output="$(git diff --name-only "$base_sha...$head_sha" -- 2>/dev/null)"; then + if [ "${GITHUB_EVENT_NAME:-}" = "workflow_dispatch" ] && pull_request_metadata_env_present; then + if changed_files_output="$(git diff --name-only "$base_sha" "$head_sha" -- 2>/dev/null)"; then + echo "Using explicit base/head diff for workflow_dispatch PR-scope Strix evidence." >&2 + else + echo "ERROR: pull request changed file list could not be read; failing closed." >&2 + return 2 + fi + elif changed_files_output="$(git diff --name-only "$base_sha..$head_sha" -- 2>/dev/null)"; then + echo "INFO: Unable to compute PR merge base; falling back to direct base/head diff for changed file enumeration." >&2 + else + if pull_request_head_blob_required; then + echo "ERROR: pull request changed file list could not be read; failing closed." >&2 + return 2 + fi + return 1 + fi + fi + + while IFS= read -r changed_file; do + if [ -n "$changed_file" ]; then + CHANGED_FILES+=("$changed_file") + PULL_REQUEST_CHANGED_FILES+=("$changed_file") + fi + done <<<"$changed_files_output" + normalize_changed_files_cache || return 2 + + return 0 +} + +load_pull_request_head_sha() { + local head_sha + head_sha="$(trim_whitespace "${PR_HEAD_SHA:-}")" + if [ -n "$head_sha" ]; then + printf '%s\n' "$head_sha" + return 0 + fi + + if [ -z "${GITHUB_EVENT_PATH:-}" ] || [ ! -f "$GITHUB_EVENT_PATH" ]; then + return 1 + fi + + python3 - "$GITHUB_EVENT_PATH" <<'PY' +import json +import sys + +with open(sys.argv[1], 'r', encoding='utf-8') as fh: + payload = json.load(fh) +pull_request = payload.get('pull_request') or {} +head = ((pull_request.get('head') or {}).get('sha')) or '' +if not head: + raise SystemExit(1) +print(head) +PY +} + +load_pull_request_number() { + local pr_number + pr_number="$(trim_whitespace "${PR_NUMBER:-}")" + if [ -n "$pr_number" ]; then + if [[ "$pr_number" =~ ^[0-9]+$ ]] && [ "$pr_number" -gt 0 ]; then + printf '%s\n' "$pr_number" + return 0 + fi + return 1 + fi + + if [ -z "${GITHUB_EVENT_PATH:-}" ] || [ ! -f "$GITHUB_EVENT_PATH" ]; then + return 1 + fi + + python3 - "$GITHUB_EVENT_PATH" <<'PY' +import json +import sys + +with open(sys.argv[1], 'r', encoding='utf-8') as fh: + payload = json.load(fh) +pull_request = payload.get('pull_request') or {} +number = pull_request.get('number') +if not isinstance(number, int) or number <= 0: + raise SystemExit(1) +print(number) +PY +} + +authoritative_sca_checks_passed_for_pr_head() { + if [ "${STRIX_TEST_PR_SCA_STATUS_OVERRIDE+x}" = x ]; then + case "$(trim_whitespace "$STRIX_TEST_PR_SCA_STATUS_OVERRIDE")" in + passed) + return 0 + ;; + unverified | failed | "") + return 1 + ;; + error) + echo "Unable to verify authoritative SCA checks for this pull request head; failing closed." >&2 + return 1 + ;; + esac + echo "Unsupported STRIX_TEST_PR_SCA_STATUS_OVERRIDE value; failing closed." >&2 + return 1 + fi + + if ! is_pull_request_event; then + echo "Unable to verify authoritative SCA checks outside a pull request context; failing closed." >&2 + return 1 + fi + + local head_sha pr_number repository gh_token workflow_runs_json verification_result + if ! head_sha="$(load_pull_request_head_sha)"; then + echo "Unable to determine pull request head SHA for authoritative SCA verification; failing closed." >&2 + return 1 + fi + if ! pr_number="$(load_pull_request_number)"; then + echo "Unable to determine pull request identity for authoritative SCA verification; failing closed." >&2 + return 1 + fi + + repository="$(trim_whitespace "${GITHUB_REPOSITORY:-}")" + if [ -z "$repository" ]; then + echo "GITHUB_REPOSITORY is required for authoritative SCA verification; failing closed." >&2 + return 1 + fi + + gh_token="$(trim_whitespace "${GH_TOKEN:-${GITHUB_TOKEN:-}}")" + if [ -z "$gh_token" ]; then + echo "GitHub token is required for authoritative SCA verification; failing closed." >&2 + return 1 + fi + + if ! workflow_runs_json="$(GH_TOKEN="$gh_token" gh api \ + -H "Accept: application/vnd.github+json" \ + "repos/$repository/actions/runs?head_sha=$head_sha&event=pull_request&per_page=100")"; then + echo "Unable to query authoritative SCA workflow runs for this pull request head; failing closed." >&2 + return 1 + fi + + if ! verification_result="$( + WORKFLOW_RUNS_JSON="$workflow_runs_json" python3 - "$head_sha" "$pr_number" <<'PY' +import json +import os +import sys + +head_sha = sys.argv[1] +pr_number = int(sys.argv[2]) +payload = json.loads(os.environ["WORKFLOW_RUNS_JSON"]) +runs = payload.get("workflow_runs") or [] +required = { + ".github/workflows/dependency-review.yml": "Dependency review", + ".github/workflows/osvscanner.yml": "OSV-Scanner", +} +latest = {} +for run in runs: + path = (run.get("path") or "").strip() + name = (run.get("name") or "").strip() + candidate = None + for required_path, required_name in required.items(): + if path.endswith(required_path) or name == required_name: + candidate = required_path + break + if candidate is None: + continue + if (run.get("head_sha") or "") != head_sha: + continue + pull_requests = run.get("pull_requests") or [] + if not any(int(pr.get("number") or 0) == pr_number for pr in pull_requests if isinstance(pr, dict)): + continue + run_id = int(run.get("id") or 0) + previous = latest.get(candidate) + if previous is None or run_id > int(previous.get("id") or 0): + latest[candidate] = run + +missing = [path for path in required if path not in latest] +if missing: + print("missing") + raise SystemExit(0) + +for required_path, run in latest.items(): + if (run.get("status") or "") != "completed": + print("unverified") + raise SystemExit(0) + if (run.get("conclusion") or "") != "success": + print("unverified") + raise SystemExit(0) + +print("passed") +PY + )"; then + echo "Unable to evaluate authoritative SCA workflow results for this pull request head; failing closed." >&2 + return 1 + fi + + case "$verification_result" in + passed) + return 0 + ;; + missing | unverified) + return 1 + ;; + esac + + echo "Unexpected authoritative SCA verification result '$verification_result'; failing closed." >&2 + return 1 +} + +is_scannable_changed_file() { + local changed_file="$1" + local normalized_changed_file + if [ -z "$changed_file" ]; then + return 1 + fi + if ! normalized_changed_file="$(normalize_changed_file_path "$changed_file")"; then + if pull_request_head_blob_required; then + echo "ERROR: pull request changed file path is unsafe: $changed_file" >&2 + return 2 + fi + return 1 + fi + if pull_request_head_blob_required; then + local mode_rc=0 + pr_head_regular_file_mode "$normalized_changed_file" >/dev/null || mode_rc=$? + case "$mode_rc" in + 0) + ;; + 1) + return 1 + ;; + 3) + echo "ERROR: pull request changed file is not a regular PR-head file; failing closed: $normalized_changed_file" >&2 + return 2 + ;; + *) + echo "ERROR: pull request changed file could not be read from PR head; failing closed: $normalized_changed_file" >&2 + return 2 + ;; + esac + fi + if [[ "$normalized_changed_file" == *.md || "$normalized_changed_file" == *.txt ]]; then + return 1 + fi + if [[ "$normalized_changed_file" == */src/test/* || "$normalized_changed_file" == tests/* || "$normalized_changed_file" == */tests/* ]]; then + return 1 + fi + if [[ "$normalized_changed_file" == */__tests__/* || "$normalized_changed_file" == *.test.ts || "$normalized_changed_file" == *.test.tsx || "$normalized_changed_file" == *.spec.ts || "$normalized_changed_file" == *.spec.tsx ]]; then + return 1 + fi + if [[ "$normalized_changed_file" == scripts/ci/test_*.sh || "$normalized_changed_file" == scripts/ci/*_test.sh ]]; then + return 1 + fi + if [[ "$normalized_changed_file" == pnpm-lock.yaml || "$normalized_changed_file" == package-lock.json || "$normalized_changed_file" == yarn.lock || "$normalized_changed_file" == uv.lock ]]; then + return 1 + fi + if [[ "$normalized_changed_file" == infra/* ]]; then + return 1 + fi + if [[ "$normalized_changed_file" == */ ]]; then + return 1 + fi + if ! is_supported_source_file "$normalized_changed_file"; then + return 1 + fi + local exists_rc=0 + changed_file_exists_for_scan "$normalized_changed_file" || exists_rc=$? + case "$exists_rc" in + 0) + return 0 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac +} + +pull_request_scope_context_files() { + local needs_backend_python=0 + local needs_frontend_email_api_context=0 + local needs_deployment_context=0 + local changed_file normalized_changed_file + for changed_file in "$@"; do + normalized_changed_file="$(normalize_changed_file_path "$changed_file")" || return 2 + case "$normalized_changed_file" in + backend/*) + if [[ "$normalized_changed_file" =~ ^backend/.+\.py$ ]]; then + needs_backend_python=1 + fi + ;; + # The app shell, email components, threading URL builder, and API client can + # shape frontend email retrieval flows; include backend auth context with them. + frontend/src/components/EmailDetail.tsx | frontend/src/components/EmailList.tsx | frontend/src/app/page.tsx | frontend/src/lib/api-client.ts | frontend/src/lib/email-threading.ts) + needs_frontend_email_api_context=1 + ;; + # Deployment and CI changes often reference build files that are not all + # changed in the PR. Include the trusted copies so Strix does not downgrade + # a clean finding to provider/failure-signal output due to missing Dockerfiles + # or VERSION context. + .github/workflows/* | Dockerfile | frontend/Dockerfile | frontend/next.config.ts | docker-compose*.yml | render.yaml) + needs_deployment_context=1 + ;; + esac + done + + if [ "$needs_backend_python" -eq 1 ]; then + cat <<'EOF' +backend/requirements.txt +backend/api/__init__.py +backend/api/accounts.py +backend/api/auth.py +backend/api/calendar.py +backend/api/dav.py +backend/api/data.py +backend/api/emails.py +backend/api/llm.py +backend/api/llm_providers.py +backend/api/mailbox_scope.py +backend/api/network.py +backend/api/observability.py +backend/api/ontology.py +backend/api/prompts.py +backend/api/runner_config.py +backend/api/runner_ws.py +backend/api/runtime_config.py +backend/api/search.py +backend/api/security.py +backend/api/tasks.py +backend/api/tenant_config.py +backend/api/webdav.py +backend/core/__init__.py +backend/core/config.py +backend/core/exceptions.py +backend/core/runtime_secrets.py +backend/core/telemetry.py +backend/db/__init__.py +backend/db/models.py +backend/db/session.py +backend/services/__init__.py +backend/services/archive.py +backend/services/calendar_service.py +backend/services/email_client.py +backend/services/email_parser.py +backend/services/embedding.py +backend/services/exceptions.py +backend/services/imap_worker.py +backend/services/llm_provider_urls.py +backend/services/text_safety.py +backend/services/threading_service.py +EOF + fi + + if [ "$needs_frontend_email_api_context" -eq 1 ]; then + cat <<'EOF' +backend/api/auth.py +backend/api/emails.py +backend/core/config.py +backend/db/models.py +backend/main.py +backend/services/threading_service.py +EOF + fi + + if [ "$needs_deployment_context" -eq 1 ]; then + cat <<'EOF' +Dockerfile +backend/api/auth.py +backend/core/config.py +backend/core/runtime_secrets.py +backend/main.py +backend/scripts/docker_entrypoint.sh +frontend/Dockerfile +frontend/package.json +frontend/package-lock.json +frontend/next.config.ts +frontend/postcss.config.mjs +docker-compose.yml +render.yaml +VERSION +EOF + fi +} + +changed_file_list_contains() { + local candidate normalized_candidate normalized_changed_file + normalized_candidate="$(normalize_changed_file_path "$1")" || return 2 + for normalized_changed_file in "${NORMALIZED_CHANGED_FILES[@]}"; do + if [ "$normalized_changed_file" = "$normalized_candidate" ]; then + return 0 + fi + done + return 1 +} + +build_pull_request_scope_dir() { + local scope_dir + scope_dir="$(mktemp -d "${TMPDIR:-/tmp}/strix-pr-scope.XXXXXX")" + scope_dir="$({ CDPATH='' && cd -P -- "$scope_dir" && pwd -P; })" + PULL_REQUEST_SCOPE_DIRS+=("$scope_dir") + + copy_changed_file_into_scope() { + local changed_file="$1" + local relative_path + relative_path="$(normalize_changed_file_path "$changed_file")" || { + echo "ERROR: pull request changed file path is unsafe: $changed_file" >&2 + return 2 + } + local dst_path + dst_path="$( + python3 - "$scope_dir" "$relative_path" <<'PY' +from pathlib import Path +import sys + +scope_root = Path(sys.argv[1]).resolve(strict=True) +relative_path = Path(sys.argv[2]) +dst_path = scope_root / relative_path +print(dst_path) +PY + )" + mkdir -p -- "$(dirname -- "$dst_path")" + local copy_rc=1 + local head_sha_for_copy + head_sha_for_copy="$(trim_whitespace "${PR_HEAD_SHA:-}")" + if pull_request_head_blob_required || { [ -n "$head_sha_for_copy" ] && is_valid_git_commit_sha "$head_sha_for_copy" && git rev-parse --verify --quiet "$head_sha_for_copy^{commit}" >/dev/null; }; then + copy_rc=0 + copy_pr_head_blob_to_file "$relative_path" "$dst_path" || copy_rc=$? + fi + if [ "$copy_rc" -eq 0 ]; then + return 0 + fi + if pull_request_head_blob_required || [ "$copy_rc" -eq 2 ]; then + echo "ERROR: pull request changed file could not be read from PR head; failing closed: $changed_file" >&2 + return 2 + fi + local src_path="$REPO_ROOT/$relative_path" + if [ ! -f "$src_path" ] || [ -L "$src_path" ]; then + echo "ERROR: pull request changed file is unavailable in both PR head and checkout: $changed_file" >&2 + return 2 + fi + cp -- "$src_path" "$dst_path" + } + + copy_trusted_context_file_into_scope() { + local context_file="$1" + local relative_path + relative_path="$(normalize_changed_file_path "$context_file")" || { + echo "ERROR: pull request context file path is unsafe: $context_file" >&2 + return 2 + } + local dst_path + dst_path="$( + python3 - "$scope_dir" "$relative_path" <<'PY' +from pathlib import Path +import sys + +scope_root = Path(sys.argv[1]).resolve(strict=True) +relative_path = Path(sys.argv[2]) +dst_path = scope_root / relative_path +print(dst_path) +PY + )" + if [ -e "$dst_path" ]; then + return 0 + fi + local changed_context_rc=0 + changed_file_list_contains "$relative_path" || changed_context_rc=$? + case "$changed_context_rc" in + 0) + mkdir -p -- "$(dirname -- "$dst_path")" + local copy_rc=1 + local head_sha_for_copy + head_sha_for_copy="$(trim_whitespace "${PR_HEAD_SHA:-}")" + if pull_request_head_blob_required || { [ -n "$head_sha_for_copy" ] && is_valid_git_commit_sha "$head_sha_for_copy" && git rev-parse --verify --quiet "$head_sha_for_copy^{commit}" >/dev/null; }; then + copy_rc=0 + copy_pr_head_blob_to_file "$relative_path" "$dst_path" || copy_rc=$? + fi + if [ "$copy_rc" -eq 0 ]; then + return 0 + fi + if pull_request_head_blob_required || [ "$copy_rc" -eq 2 ]; then + echo "ERROR: pull request changed context file could not be read from PR head; failing closed: $context_file" >&2 + return 2 + fi + ;; + 2) + return 2 + ;; + esac + local src_path="$REPO_ROOT/$relative_path" + if [ ! -e "$src_path" ]; then + return 0 + fi + if [ ! -f "$src_path" ] || [ -L "$src_path" ]; then + echo "ERROR: pull request trusted context file is not a regular checkout file: $context_file" >&2 + return 2 + fi + mkdir -p -- "$(dirname -- "$dst_path")" + cp -- "$src_path" "$dst_path" + } + + copy_scope_support_file() { + local relative_path="$1" + local dst_path + dst_path="$( + python3 - "$scope_dir" "$relative_path" <<'PY' +from pathlib import Path +import sys + +scope_root = Path(sys.argv[1]).resolve(strict=True) +relative_path = Path(sys.argv[2]) +dst_path = scope_root / relative_path +print(dst_path) +PY + )" + if [ -e "$dst_path" ]; then + return 0 + fi + local src_path="$REPO_ROOT/$relative_path" + if [ ! -f "$src_path" ] || [ -L "$src_path" ]; then + echo "ERROR: pull request scan support file is unavailable: $relative_path" >&2 + return 2 + fi + mkdir -p -- "$(dirname -- "$dst_path")" + cp -- "$src_path" "$dst_path" + } + + copy_required_scope_support_files() { + local include_strix_model_utils=0 + local changed_file relative_path + for changed_file in "$@"; do + relative_path="$(normalize_changed_file_path "$changed_file")" || return 2 + case "$relative_path" in + scripts/ci/strix_quick_gate.sh | scripts/ci/test_strix_quick_gate.sh) + include_strix_model_utils=1 + ;; + esac + done + + if [ "$include_strix_model_utils" -eq 1 ]; then + copy_scope_support_file "scripts/ci/strix_model_utils.sh" || return 2 + fi + } + + local changed_file + for changed_file in "$@"; do + copy_changed_file_into_scope "$changed_file" || return 2 + done + local context_files_text="" + context_files_text="$(pull_request_scope_context_files "$@")" || return 2 + if [ -n "$context_files_text" ]; then + local context_file + while IFS= read -r context_file; do + [ -n "$context_file" ] || continue + copy_trusted_context_file_into_scope "$context_file" || return 2 + done <<<"$context_files_text" + fi + copy_required_scope_support_files "$@" || return 2 + LAST_PULL_REQUEST_SCOPE_DIR="$scope_dir" +} + +build_pull_request_head_tree_scope_dir() { + local scope_dir + scope_dir="$(mktemp -d "${TMPDIR:-/tmp}/strix-pr-scope.XXXXXX")" + scope_dir="$({ CDPATH='' && cd -P -- "$scope_dir" && pwd -P; })" + PULL_REQUEST_SCOPE_DIRS+=("$scope_dir") + + local head_sha + head_sha="$(trim_whitespace "${PR_HEAD_SHA:-}")" + if [ -z "$head_sha" ] || ! is_valid_git_commit_sha "$head_sha"; then + echo "ERROR: pull request head commit SHA is invalid; failing closed." >&2 + return 2 + fi + if ! git rev-parse --verify --quiet "$head_sha^{commit}" >/dev/null; then + echo "ERROR: pull request head commit could not be read; failing closed: $head_sha" >&2 + return 2 + fi + + local tree_output + if ! tree_output="$(git ls-tree -r --full-tree "$head_sha")"; then + echo "ERROR: pull request head tree could not be read; failing closed." >&2 + return 2 + fi + + local copied_file_count=0 + local metadata relative_path mode object_type object_hash dst_path tmp_dst + while IFS=$'\t' read -r metadata relative_path; do + [ -n "$metadata" ] || continue + # shellcheck disable=SC2086 # metadata is exactly git ls-tree's mode/type/object tuple. + read -r mode object_type object_hash <<<"$metadata" + if [ "$object_type" != "blob" ]; then + echo "ERROR: pull request head tree entry is not a blob; failing closed: $relative_path" >&2 + return 2 + fi + case "$mode" in + 100644 | 100755) + ;; + *) + echo "ERROR: pull request head tree entry has unsupported mode $mode; failing closed: $relative_path" >&2 + return 2 + ;; + esac + relative_path="$(normalize_changed_file_path "$relative_path")" || { + echo "ERROR: pull request head tree path is unsafe: $relative_path" >&2 + return 2 + } + dst_path="$( + python3 - "$scope_dir" "$relative_path" <<'PY' +from pathlib import Path +import sys + +scope_root = Path(sys.argv[1]).resolve(strict=True) +relative_path = Path(sys.argv[2]) +dst_path = scope_root / relative_path +print(dst_path) +PY + )" + mkdir -p -- "$(dirname -- "$dst_path")" + tmp_dst="$(mktemp "$(dirname -- "$dst_path")/.pr-head.XXXXXX")" || return 2 + if ! git cat-file blob "$object_hash" >"$tmp_dst"; then + rm -f -- "$tmp_dst" + echo "ERROR: pull request head blob could not be copied; failing closed: $relative_path" >&2 + return 2 + fi + if ! mv -- "$tmp_dst" "$dst_path"; then + rm -f -- "$tmp_dst" + return 2 + fi + # PR-head files are scanner input data in privileged workflows. Preserve + # blob content only; never preserve executable bits from untrusted heads. + chmod 644 "$dst_path" || return 2 + copied_file_count=$((copied_file_count + 1)) + done <<<"$tree_output" + + if [ "$copied_file_count" -eq 0 ]; then + echo "ERROR: pull request head tree contains no regular files to scan; failing closed." >&2 + return 2 + fi + + LAST_PULL_REQUEST_SCOPE_DIR="$scope_dir" +} + +prepare_pull_request_scan_scope() { + if ! is_pull_request_event; then + return 0 + fi + TARGET_PATH_IS_INTERNAL_PR_SCOPE=0 + + local load_changed_files_rc=0 + load_pull_request_changed_files || load_changed_files_rc=$? + case "$load_changed_files_rc" in + 0) + ;; + 2) + return 2 + ;; + *) + return 0 + ;; + esac + + local scoped_changed_files=() + local changed_file + for changed_file in "${CHANGED_FILES[@]}"; do + local scannable_rc=0 + is_scannable_changed_file "$changed_file" || scannable_rc=$? + if [ "$scannable_rc" -eq 0 ]; then + scoped_changed_files+=("$changed_file") + elif [ "$scannable_rc" -eq 2 ]; then + return 2 + fi + done + + if [ "${#scoped_changed_files[@]}" -eq 0 ]; then + echo "No scannable changed files in pull request; skipping Strix quick scan." >&2 + exit 0 + fi + + CHANGED_FILES=("${scoped_changed_files[@]}") + local total_files="${#CHANGED_FILES[@]}" + derive_pull_request_full_target_path() { + python3 - "$REPO_ROOT" "$@" <<'PY' +from pathlib import Path +import os +import sys + +repo_root = Path(sys.argv[1]).resolve(strict=True) +resolved_paths = [] +for relative in sys.argv[2:]: + candidate = (repo_root / relative).resolve(strict=True) + candidate.relative_to(repo_root) + resolved_paths.append(candidate) + +common = Path(os.path.commonpath([str(path) for path in resolved_paths])) +if common.is_file(): + common = common.parent + +if common == repo_root: + top_levels = { + path.relative_to(repo_root).parts[0] + for path in resolved_paths + if path.relative_to(repo_root).parts + } + if len(top_levels) == 1: + common = repo_root / next(iter(top_levels)) + +relative_common = common.relative_to(repo_root) +print("./" if str(relative_common) == "." else f"./{relative_common.as_posix()}") +PY + } + target_path_is_top_level_scope() { + local candidate="$1" + [[ "$candidate" == ./* ]] || return 1 + candidate="${candidate#./}" + [[ "$candidate" == */* ]] && return 1 + [ -n "$candidate" ] + } + if [ "$STRIX_DISABLE_PR_SCOPING" = "1" ]; then + if pull_request_head_blob_required; then + local build_scope_rc=0 + build_pull_request_head_tree_scope_dir || build_scope_rc=$? + if [ "$build_scope_rc" -eq 0 ]; then + TARGET_PATH="$LAST_PULL_REQUEST_SCOPE_DIR" + TARGET_PATH_IS_INTERNAL_PR_SCOPE=1 + printf "Using full PR-head blob scope for pull request_target Strix scan; %s scannable changed file(s) retained for findings attribution.\n" "$total_files" >&2 + return 0 + fi + return 2 + fi + local narrowed_target="" + if narrowed_target="$(derive_pull_request_full_target_path "${CHANGED_FILES[@]}")" && [ "$narrowed_target" != "./" ] && ! target_path_is_top_level_scope "$narrowed_target"; then + TARGET_PATH="$narrowed_target" + TARGET_PATH_IS_INTERNAL_PR_SCOPE=0 + printf "Using narrowed target path %s for pull request Strix scan with %s scannable changed file(s).\n" "$narrowed_target" "$total_files" >&2 + else + local build_scope_rc=0 + build_pull_request_scope_dir "${CHANGED_FILES[@]}" || build_scope_rc=$? + if [ "$build_scope_rc" -eq 0 ]; then + TARGET_PATH="$LAST_PULL_REQUEST_SCOPE_DIR" + TARGET_PATH_IS_INTERNAL_PR_SCOPE=1 + printf "Using bounded changed-file scope for pull request Strix scan with %s scannable changed file(s).\n" "$total_files" >&2 + elif pull_request_head_blob_required; then + return 2 + else + printf "Using full target path for pull request Strix scan with %s scannable changed file(s).\n" "$total_files" >&2 + fi + fi + return 0 + fi + local build_scope_rc=0 + build_pull_request_scope_dir "${CHANGED_FILES[@]}" || build_scope_rc=$? + if [ "$build_scope_rc" -ne 0 ]; then + return 2 + fi + TARGET_PATH="$LAST_PULL_REQUEST_SCOPE_DIR" + TARGET_PATH_IS_INTERNAL_PR_SCOPE=1 + if pull_request_head_blob_required; then + printf "Materialized PR-head changed-file scope for Strix scan; %s scannable changed file(s) retained for findings attribution.\n" "$total_files" >&2 + else + printf "Scoped pull request Strix scan to %s changed file(s)" "$total_files" >&2 + printf ".\n" >&2 + fi + return 0 +} + +extract_vulnerability_locations() { + local vuln_file="$1" + local location + local resolved_scan_target="" + local narrowed_workspace_prefix="" + + if resolved_scan_target="$(resolve_current_target_path "$TARGET_PATH" 2>/dev/null)"; then + if [ "$resolved_scan_target" != "$REPO_ROOT" ]; then + narrowed_workspace_prefix="/workspace/$(basename "$resolved_scan_target")/" + fi + fi + + extract_candidate_source_paths_from_report() { + python3 - "$1" <<'PY' +from pathlib import Path +import re +import sys + +text = Path(sys.argv[1]).read_text(encoding='utf-8', errors='replace') +patterns = [ + re.compile(r'(?P/workspace/[^`\r\n]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./ \[\]-]+\.[A-Za-z0-9_]+):\d+'), + re.compile(r'(?P/workspace/[A-Za-z0-9_./ \[\]-]*(?:Dockerfile|Containerfile|Makefile))'), + re.compile(r'\s*(?P/workspace/[^<`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)\s*'), + re.compile(r'^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Target:(?:\*\*)?[ \t]*(?:File:[ \t]*)?(?P/workspace/[^`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)', re.MULTILINE), + re.compile(r'^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Target:(?:\*\*)?[ \t]*(?:File:[ \t]*)?(?P/workspace/[A-Za-z0-9_./ \[\]-]*(?:Dockerfile|Containerfile|Makefile)|(?:Dockerfile|Containerfile|Makefile))', re.MULTILINE), + re.compile(r'^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Endpoint:(?:\*\*)?[ \t]*(?P/workspace/[^`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)', re.MULTILINE), + re.compile(r'(?:in\s+)?file\s+`(?P(?:\.\.?/)?[A-Za-z0-9_./ \[\]-]+\.[A-Za-z0-9_]+)`', flags=re.IGNORECASE), + re.compile(r'`(?P(?:\.\.?/)?[A-Za-z0-9_./ \[\]-]+\.[A-Za-z0-9_]+)`\s+file\b', flags=re.IGNORECASE), + re.compile(r'(?Dockerfile|Containerfile|Makefile)(?![A-Za-z0-9_./-])'), +] +seen = set() +for pattern in patterns: + for match in pattern.finditer(text): + value = match.group('path').strip() + if value and value not in seen: + seen.add(value) +for value in sorted(seen): + print(value) +PY + } + + normalize_vulnerability_location() { + local raw_location="$1" + raw_location="$({ + python3 - "$REPO_ROOT" "$REPO_NAME" "$resolved_scan_target" "$narrowed_workspace_prefix" "$raw_location" <<'PY' +from pathlib import Path +from urllib.parse import unquote +import sys + +repo_root = Path(sys.argv[1]).resolve(strict=True) +repo_name = sys.argv[2] +scan_target_root_raw = sys.argv[3].strip() +scan_target_workspace_prefix = sys.argv[4].strip() +raw_location = unquote(sys.argv[5].strip()) +if not raw_location: + raise SystemExit(1) + +scan_target_root = Path(scan_target_root_raw).resolve(strict=True) if scan_target_root_raw else None + +def normalize_within(base: Path, location: str) -> Path: + candidate = (base / location).resolve(strict=False) + try: + candidate.relative_to(base) + except ValueError: + raise SystemExit(1) + if not candidate.exists(): + raise SystemExit(1) + return candidate + +def try_normalize_within(base: Path, location: str) -> Path | None: + try: + return normalize_within(base, location) + except SystemExit: + return None + +def emit_repo_relative(candidate: Path, fallback_relative: Path | None = None) -> None: + try: + relative = candidate.relative_to(repo_root) + except ValueError: + if fallback_relative is None: + raise SystemExit(1) + repo_candidate = (repo_root / fallback_relative).resolve(strict=False) + if not repo_candidate.exists(): + raise SystemExit(1) + try: + relative = repo_candidate.relative_to(repo_root) + except ValueError: + raise SystemExit(1) + print(relative.as_posix()) + raise SystemExit(0) + +if scan_target_root and scan_target_workspace_prefix and raw_location.startswith(scan_target_workspace_prefix): + suffix = raw_location[len(scan_target_workspace_prefix):] + if not suffix: + raise SystemExit(1) + candidate = normalize_within(scan_target_root, suffix) + emit_repo_relative(candidate, candidate.relative_to(scan_target_root)) + +prefixes = ( + str(repo_root) + "/", + f"/workspace/{repo_name}/", +) +for prefix in prefixes: + if raw_location.startswith(prefix): + relative_location = raw_location[len(prefix):] + if not relative_location: + raise SystemExit(1) + emit_repo_relative(normalize_within(repo_root, relative_location)) + +if scan_target_root is not None: + candidate = try_normalize_within(scan_target_root, raw_location) + if candidate is not None: + emit_repo_relative(candidate, candidate.relative_to(scan_target_root)) + +emit_repo_relative(normalize_within(repo_root, raw_location)) +PY + })" || return 1 + if [ -z "$raw_location" ]; then + return 1 + fi + if ! is_supported_source_file "$raw_location"; then + return 1 + fi + + if [ -f "$REPO_ROOT/$raw_location" ] && [ ! -L "$REPO_ROOT/$raw_location" ]; then + printf '%s\n' "$raw_location" + return 0 + fi + + return 1 + } + + { + while IFS= read -r location; do + normalize_vulnerability_location "$location" || true + done < <(extract_candidate_source_paths_from_report "$vuln_file") + } | sort -u +} + +extract_first_severity_rank() { + local source_path="$1" + local line severity rank=-1 + + while IFS= read -r line; do + if [[ "${line^^}" =~ SEVERITY[[:space:]]*:[[:space:][:punct:]]*(CRITICAL|HIGH|MEDIUM|LOW|INFO|INFORMATIONAL|NONE)([[:space:][:punct:]]|$) ]]; then + severity="${BASH_REMATCH[1]}" + rank="$(severity_rank "$severity")" + if [ "$rank" -gt -1 ]; then + break + fi + fi + done < <(grep -Ei 'severity[[:space:]]*:' "$source_path" || true) + + printf '%s\n' "$rank" +} + +evaluate_pull_request_findings() { + PR_FINDINGS_DECISION="not_applicable" + if ! is_pull_request_event; then + return 1 + fi + if ! load_pull_request_changed_files; then + PR_FINDINGS_DECISION="block_unmapped" + echo "Unable to map Strix findings to changed files; failing closed for pull request." >&2 + return 1 + fi + + local threshold_rank + threshold_rank="$(severity_rank "$STRIX_FAIL_ON_MIN_SEVERITY")" + local found_baseline_threshold_finding=0 + local found_changed_manifest_only_threshold_finding=0 + local found_retryable_model_inconsistency=0 + local found_any_vuln_file=0 + local run_dir vulnerabilities_dir vuln_file line severity rank + for run_dir in "$STRIX_REPORTS_DIR"/*; do + if [ ! -d "$run_dir" ] || [ -L "$run_dir" ]; then + continue + fi + if is_preexisting_report_dir "$run_dir"; then + continue + fi + vulnerabilities_dir="$run_dir/vulnerabilities" + if [ ! -d "$vulnerabilities_dir" ] || [ -L "$vulnerabilities_dir" ]; then + continue + fi + for vuln_file in "$vulnerabilities_dir"/*.md; do + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + continue + fi + found_any_vuln_file=1 + rank="$(extract_first_severity_rank "$vuln_file")" + if [ "$rank" -lt 0 ]; then + PR_FINDINGS_DECISION="block_unmapped" + echo "Unrecognized Strix severity marker; failing closed for pull request." >&2 + return 1 + fi + if [ "$rank" -lt "$threshold_rank" ]; then + continue + fi + if vulnerability_file_is_retryable_model_inconsistency "$vuln_file"; then + found_retryable_model_inconsistency=1 + continue + fi + mapfile -t vulnerability_locations < <(extract_vulnerability_locations "$vuln_file") + if [ "${#vulnerability_locations[@]}" -eq 0 ]; then + PR_FINDINGS_DECISION="block_unmapped" + echo "Unable to map Strix findings to changed files; failing closed for pull request." >&2 + return 1 + fi + if all_vulnerability_locations_are_dependency_manifests "${vulnerability_locations[@]}"; then + local manifest_location changed_file manifest_location_changed=0 + for manifest_location in "${vulnerability_locations[@]}"; do + for changed_file in "${CHANGED_FILES[@]}"; do + if [ "$manifest_location" = "$changed_file" ]; then + manifest_location_changed=1 + break + fi + done + if [ "$manifest_location_changed" -eq 1 ]; then + break + fi + done + if [ "$manifest_location_changed" -eq 1 ]; then + found_changed_manifest_only_threshold_finding=1 + else + found_baseline_threshold_finding=1 + fi + continue + fi + found_baseline_threshold_finding=1 + local changed_file vulnerability_location + for vulnerability_location in "${vulnerability_locations[@]}"; do + for changed_file in "${CHANGED_FILES[@]}"; do + if [ "$vulnerability_location" = "$changed_file" ]; then + PR_FINDINGS_DECISION="block_changed" + echo "Strix finding intersects files changed in this pull request." >&2 + return 1 + fi + done + done + done + done + + if [ "$found_baseline_threshold_finding" -eq 0 ] && [ "$found_changed_manifest_only_threshold_finding" -eq 0 ]; then + rank="$(extract_first_severity_rank "$STRIX_LOG")" + if [ "$rank" -lt 0 ]; then + if [ "$found_retryable_model_inconsistency" -eq 1 ]; then + PR_FINDINGS_DECISION="retry_model_inconsistency" + return 1 + fi + return 1 + fi + if [ "$rank" -ge "$threshold_rank" ]; then + mapfile -t vulnerability_locations < <(extract_vulnerability_locations "$STRIX_LOG") + if [ "${#vulnerability_locations[@]}" -eq 0 ]; then + PR_FINDINGS_DECISION="block_unmapped" + echo "Unable to map Strix findings to changed files; failing closed for pull request." >&2 + return 1 + fi + if all_vulnerability_locations_are_dependency_manifests "${vulnerability_locations[@]}"; then + local manifest_location changed_file manifest_location_changed=0 + for manifest_location in "${vulnerability_locations[@]}"; do + for changed_file in "${CHANGED_FILES[@]}"; do + if [ "$manifest_location" = "$changed_file" ]; then + manifest_location_changed=1 + break + fi + done + if [ "$manifest_location_changed" -eq 1 ]; then + break + fi + done + if [ "$manifest_location_changed" -eq 1 ]; then + found_changed_manifest_only_threshold_finding=1 + else + found_baseline_threshold_finding=1 + fi + else + found_baseline_threshold_finding=1 + local changed_file vulnerability_location + for vulnerability_location in "${vulnerability_locations[@]}"; do + for changed_file in "${CHANGED_FILES[@]}"; do + if [ "$vulnerability_location" = "$changed_file" ]; then + PR_FINDINGS_DECISION="block_changed" + echo "Strix finding intersects files changed in this pull request." >&2 + return 1 + fi + done + done + fi + fi + fi + + if [ "$found_baseline_threshold_finding" -eq 0 ] && [ "$found_changed_manifest_only_threshold_finding" -eq 0 ] && [ "$found_retryable_model_inconsistency" -eq 1 ]; then + PR_FINDINGS_DECISION="retry_model_inconsistency" + return 1 + fi + + if [ "$found_changed_manifest_only_threshold_finding" -eq 1 ]; then + if authoritative_sca_checks_passed_for_pr_head; then + PR_FINDINGS_DECISION="allow_manifest_only" + echo "Strix changed-manifest finding is covered by verified authoritative SCA checks on this PR head; allowing pipeline continuation." >&2 + return 0 + fi + PR_FINDINGS_DECISION="block_manifest_unverified" + echo "Strix changed-manifest finding requires verified authoritative SCA checks on this PR head; failing closed." >&2 + return 1 + fi + + if [ "$found_baseline_threshold_finding" -eq 1 ]; then + PR_FINDINGS_DECISION="allow_baseline" + echo "Strix findings are limited to unchanged files in this pull request; allowing pipeline continuation." >&2 + return 0 + fi + + return 1 +} + +fallback_models_raw_for_model() { + local model="$1" + + if is_vertex_model "$model"; then + if [ -z "${STRIX_VERTEX_FALLBACK_MODELS+x}" ]; then + printf '%s\n' "vertex_ai/gemini-2.5-pro vertex_ai/gemini-2.5-flash" + else + printf '%s\n' "$STRIX_VERTEX_FALLBACK_MODELS" + fi + return 0 + fi + + if is_gemini_model "$model"; then + if [ -n "${STRIX_GEMINI_FALLBACK_MODELS+x}" ]; then + printf '%s\n' "$STRIX_GEMINI_FALLBACK_MODELS" + else + printf '%s\n' "${STRIX_FALLBACK_MODELS:-}" + fi + return 0 + fi + + printf '%s\n' "${STRIX_FALLBACK_MODELS:-}" +} + +fallback_models_config_name_for_model() { + local model="$1" + + if is_vertex_model "$model"; then + printf '%s\n' "STRIX_VERTEX_FALLBACK_MODELS" + return 0 + fi + + if is_gemini_model "$model"; then + if [ -n "${STRIX_GEMINI_FALLBACK_MODELS+x}" ]; then + printf '%s\n' "STRIX_GEMINI_FALLBACK_MODELS" + else + printf '%s\n' "STRIX_GEMINI_FALLBACK_MODELS or STRIX_FALLBACK_MODELS" + fi + return 0 + fi + + printf '%s\n' "STRIX_FALLBACK_MODELS" +} + +has_distinct_fallback_model_for_model() { + local model="$1" + local fallback_models_raw + fallback_models_raw="$(fallback_models_raw_for_model "$model")" + fallback_models_raw="${fallback_models_raw//$'\r'/ }" + fallback_models_raw="${fallback_models_raw//$'\n'/ }" + + local fallback_models=() + read -r -a fallback_models <<<"$fallback_models_raw" + + local candidate_raw + local candidate + for candidate_raw in "${fallback_models[@]}"; do + candidate="$(normalize_model "$candidate_raw")" + if [ -n "$candidate" ] && [ "$candidate" != "$model" ]; then + return 0 + fi + done + + return 1 +} + +resolved_llm_api_base_for_model() { + local model="$1" + + if is_vertex_model "$model"; then + return 0 + fi + + if [ -z "$LLM_API_BASE_FILE" ]; then + if is_github_models_model "$model"; then + echo "ERROR: GitHub Models Strix scans require LLM_API_BASE_FILE to select the GitHub Models inference endpoint." >&2 + return 2 + fi + return 0 + fi + local resolved_llm_api_base_file + if ! resolved_llm_api_base_file="$(resolve_trusted_input_file "LLM_API_BASE_FILE" "$LLM_API_BASE_FILE")"; then + return 2 + fi + + local llm_api_base_value + llm_api_base_value="$(cat -- "$resolved_llm_api_base_file")" + llm_api_base_value="${llm_api_base_value%%/generateContent*}" + llm_api_base_value="${llm_api_base_value%%:generateContent*}" + llm_api_base_value="$(trim_whitespace "$llm_api_base_value")" + if [ -z "$llm_api_base_value" ]; then + return 0 + fi + if [[ "$llm_api_base_value" =~ [[:space:][:cntrl:]] ]]; then + echo "ERROR: LLM_API_BASE must not contain whitespace or control characters." >&2 + return 2 + fi + if [[ ! "$llm_api_base_value" =~ ^https://[^[:space:]]+$ ]]; then + echo "ERROR: LLM_API_BASE must be an https URL when configured." >&2 + return 2 + fi + if is_github_models_api_base "$llm_api_base_value" && ! is_github_models_api_compatible_model "$model"; then + echo "ERROR: LLM_API_BASE may route through GitHub Models only when STRIX_LLM uses a GitHub Models-compatible model." >&2 + return 2 + fi + printf '%s\n' "$llm_api_base_value" +} + +child_model_for_api_base() { + local model="$1" + local llm_api_base_value="$2" + + if [ -n "$llm_api_base_value" ] && is_github_models_api_base "$llm_api_base_value"; then + case "$model" in + github_models/openai/*) + printf '%s\n' "${model#github_models/}" + return 0 + ;; + github_models/*) + printf 'openai/%s\n' "${model#github_models/}" + return 0 + ;; + deepseek/* | meta/* | mistral-ai/*) + printf 'openai/%s\n' "$model" + return 0 + ;; + esac + fi + + case "$model" in + openai_direct/*) + printf 'openai/%s\n' "${model#openai_direct/}" + return 0 + ;; + esac + + printf '%s\n' "$model" +} + +## Run a single strix invocation against TARGET_PATH with the given model. +## Builds a child-only environment so secrets and model routing do not leak +## through the parent shell process. +## Returns 0 on success (strix exit 0), 1 on scan failure, 2 on configuration failure. +## The caller is responsible for retry/fallback logic; process-level timeout +## wrapping prevents CI from hanging indefinitely. +run_strix_once() { + local model="$1" + local rc + local llm_api_base_value + local child_model + local resolved_target_path + local timeout_seconds="$STRIX_PROCESS_TIMEOUT_SECONDS" + if [ "$STRIX_TOTAL_TIMEOUT_SECONDS" -gt 0 ]; then + local remaining_budget + remaining_budget="$(remaining_total_budget)" + if [ "$remaining_budget" -le 0 ]; then + printf "Strix quick scan exceeded total timeout of %ss.\n" "$STRIX_TOTAL_TIMEOUT_SECONDS" | tee "$STRIX_LOG" >&2 + return 1 + fi + if [ "$timeout_seconds" -eq 0 ] || [ "$remaining_budget" -lt "$timeout_seconds" ]; then + timeout_seconds="$remaining_budget" + fi + fi + if ! llm_api_base_value="$(resolved_llm_api_base_for_model "$model")"; then + return 2 + fi + child_model="$(child_model_for_api_base "$model" "$llm_api_base_value")" + if ! resolved_target_path="$(resolve_current_target_path "$TARGET_PATH")"; then + return 1 + fi + local start_epoch + start_epoch="$(date +%s)" + local child_llm_api_key="" + if ! is_vertex_model "$(normalize_model "$model")"; then + child_llm_api_key="$LLM_API_KEY" + fi + set -o pipefail + set +e + STRIX_CHILD_MODEL="$child_model" \ + STRIX_CHILD_LLM_API_KEY="$child_llm_api_key" \ + STRIX_CHILD_LLM_API_BASE="$llm_api_base_value" \ + STRIX_CHILD_REPORTS_DIR="$ACTIVE_REPORTS_DIR" \ + python3 - "$timeout_seconds" "$resolved_target_path" "$SCAN_MODE" "$STRIX_LOG" <<'PY' +import os +import pathlib +import signal +import shutil +import subprocess +import sys + +timeout_seconds = int(sys.argv[1]) +target_path = sys.argv[2] +scan_mode = sys.argv[3] +log_path = pathlib.Path(sys.argv[4]) +process_timeout = None if timeout_seconds == 0 else timeout_seconds +child_env = {} +for key in ( + "PATH", + "HOME", + "TMPDIR", + "TMP", + "TEMP", + "SYSTEMROOT", + "COMSPEC", + "SSL_CERT_FILE", + "SSL_CERT_DIR", + "REQUESTS_CA_BUNDLE", + "NO_PROXY", + "HTTP_PROXY", + "HTTPS_PROXY", + "http_proxy", + "https_proxy", + "no_proxy", +): + value = os.environ.get(key) + if value: + child_env[key] = value +child_env["PYTHONWARNINGS"] = "ignore:Pydantic serializer warnings:UserWarning:pydantic.main" +child_env["NPM_CONFIG_IGNORE_SCRIPTS"] = "true" +child_env["npm_config_ignore_scripts"] = "true" +child_env["PNPM_CONFIG_IGNORE_SCRIPTS"] = "true" +child_env["pnpm_config_ignore_scripts"] = "true" +child_env["YARN_ENABLE_SCRIPTS"] = "false" +child_env["BUN_CONFIG_IGNORE_SCRIPTS"] = "true" +child_env["STRIX_LLM"] = os.environ["STRIX_CHILD_MODEL"] +child_env["LLM_MODEL"] = os.environ["STRIX_CHILD_MODEL"] +if os.environ.get("STRIX_CHILD_LLM_API_KEY"): + child_env["LLM_API_KEY"] = os.environ["STRIX_CHILD_LLM_API_KEY"] +child_env["STRIX_REPORTS_DIR"] = os.environ["STRIX_CHILD_REPORTS_DIR"] +for key, value in os.environ.items(): + if key.startswith("FAKE_STRIX_") and value: + child_env[key] = value +for key in ( + "GOOGLE_GHA_CREDS_PATH", + "GOOGLE_APPLICATION_CREDENTIALS", + "CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE", + "VERTEXAI_PROJECT", + "VERTEXAI_LOCATION", + "VERTEX_LOCATION", + "GEMINI_LOCATION", + "LLM_TIMEOUT", + "STRIX_MEMORY_COMPRESSOR_TIMEOUT", + "STRIX_REASONING_EFFORT", + "STRIX_LLM_MAX_RETRIES", + "GOOGLE_CLOUD_PROJECT", + "GCP_PROJECT", + "GCLOUD_PROJECT", + "CLOUDSDK_CORE_PROJECT", + "CLOUDSDK_PROJECT", +): + value = os.environ.get(key) + if value: + child_env[key] = value +llm_api_base = os.environ.get("STRIX_CHILD_LLM_API_BASE", "") +if llm_api_base: + child_env["LLM_API_BASE"] = llm_api_base +else: + child_env.pop("LLM_API_BASE", None) + +resolved_strix_bin = shutil.which("strix") or "" +if not resolved_strix_bin: + sys.stderr.write("ERROR: strix executable not found in PATH.\n") + raise SystemExit(127) +resolved_strix_bin = str(pathlib.Path(resolved_strix_bin).resolve(strict=True)) + +try: + target_cwd = pathlib.Path(target_path).resolve(strict=True) +except OSError as exc: + sys.stderr.write(f"ERROR: Strix target path could not be canonicalized: {exc}\n") + raise SystemExit(2) +if not target_cwd.is_dir(): + sys.stderr.write("ERROR: Strix target path must be a directory.\n") + raise SystemExit(2) +if any(ch in str(target_cwd) for ch in ("\x00", "\n", "\r")): + sys.stderr.write("ERROR: Strix target path contains unsupported control characters.\n") + raise SystemExit(2) + +command = [resolved_strix_bin, "-n", "-t", ".", "--scan-mode", scan_mode] + +try: + process = subprocess.Popen( + command, + cwd=str(target_cwd), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + env=child_env, + start_new_session=True, + ) + output, _ = process.communicate(timeout=process_timeout) + if output: + sys.stdout.write(output) + log_path.write_text(output or "", encoding="utf-8") + raise SystemExit(process.returncode) +except subprocess.TimeoutExpired: + try: + os.killpg(process.pid, signal.SIGTERM) + except ProcessLookupError: + pass + try: + output, _ = process.communicate(timeout=5) + except subprocess.TimeoutExpired: + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + output, _ = process.communicate() + if output: + sys.stdout.write(output) + log_path.write_text(output or "", encoding="utf-8") + raise SystemExit(124) +PY + rc=$? + set -e + local end_epoch + end_epoch="$(date +%s)" + local elapsed=$((end_epoch - start_epoch)) + + if strix_reported_zero_vulnerabilities_in_file "$STRIX_LOG"; then + ZERO_FINDINGS_REPORTED=1 + fi + + if [ "$rc" -eq 124 ]; then + echo "Strix run timed out after ${timeout_seconds}s." | tee -a "$STRIX_LOG" >&2 + fi + + sanitize_known_strix_report_warnings "$ACTIVE_REPORTS_DIR" "${resolved_target_path%/}/strix_runs" + local report_failure_signal=0 + if has_strix_report_failure_signal "$ACTIVE_REPORTS_DIR" "${resolved_target_path%/}/strix_runs"; then + report_failure_signal=1 + echo "Strix report artifacts emitted warning/fatal/denied/timeout output; failing closed." | tee -a "$STRIX_LOG" >&2 + fi + + if [ "$report_failure_signal" -eq 1 ] || has_detected_infrastructure_error; then + INFRA_ERROR_DETECTED=1 + if [ "$rc" -eq 0 ] && provider_signal_fail_closed_enabled; then + echo "Strix run emitted provider infrastructure or failure-signal output; failing closed." >&2 + return 1 + fi + fi + + if [ "$rc" -eq 0 ]; then + printf "Strix run succeeded for model '%s' in %ds.\n" "$model" "$elapsed" >&2 + return 0 + fi + + printf "Strix run failed for model '%s' after %ds (exit code %d).\n" "$model" "$elapsed" "$rc" >&2 + + # Sticky flag: record that at least one attempt hit an infrastructure + # error. STRIX_LOG is overwritten per-attempt, so without this flag the + # below-threshold guard in has_only_below_threshold_vulnerabilities() + # would only see the *last* attempt's log — missing infrastructure errors + # from earlier attempts whose partial reports may still sit in the reports + # directory. + return 1 +} + +is_llm_api_connection_error() { + if grep -Eiq 'litellm(\.exceptions)?\.APIConnectionError' "$STRIX_LOG" && + grep -Eiq '(GeminiException|Server disconnected without sending a response|LLM CONNECTION FAILED|Could not establish connection to the language model)' "$STRIX_LOG"; then + return 0 + fi + + if grep -Eiq 'litellm(\.exceptions)?\.InternalServerError' "$STRIX_LOG" && + grep -Eiq 'OpenAIException' "$STRIX_LOG" && + grep -Eiq 'Connection error' "$STRIX_LOG" && + grep -Eiq '(openai|LLM CONNECTION FAILED|Could not establish connection to the language model)' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +is_llm_service_unavailable_error() { + if grep -Eiq 'litellm(\.exceptions)?\.ServiceUnavailableError' "$STRIX_LOG" && + grep -Eiq '(GeminiException|VertexAI|Vertex_ai|vertex\.ai|openai|anthropic|LLM CONNECTION FAILED|Could not establish connection to the language model)' "$STRIX_LOG" && + grep -Eiq '("status"[[:space:]]*:[[:space:]]*"UNAVAILABLE"|(^|[^0-9])503([^0-9]|$)|high demand|Service Unavailable)' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +## Determines whether the last strix failure is a transient error eligible +## for same-model retry (up to STRIX_TRANSIENT_RETRY_PER_MODEL times). +## Four error families qualify: +## - RateLimit / RESOURCE_EXHAUSTED / HTTP 429 +## - litellm API connection failures with LLM-provider evidence +## - litellm service-unavailable / high-demand provider failures +## - MidStreamFallbackError (litellm mid-stream provider switch) +## Timeouts are infrastructure failures. In strict CI mode they fail closed; +## otherwise the caller may still move to fallback model evaluation. +is_transient_same_model_retry_error() { + local model="${1-}" + if is_timeout_error; then + return 1 + fi + if is_llm_api_connection_error; then + return 0 + fi + if is_llm_service_unavailable_error; then + return 0 + fi + if is_rate_limit_error; then + return 0 + fi + if is_midstream_fallback_error; then + return 0 + fi + return 1 +} + +run_strix_with_transient_retry() { + local model="$1" + local max_attempts=$((STRIX_TRANSIENT_RETRY_PER_MODEL + 1)) + local attempt=1 + + while [ "$attempt" -le "$max_attempts" ]; do + local run_rc=0 + run_strix_once "$model" || run_rc=$? + if [ "$run_rc" -eq 0 ]; then + return 0 + fi + if [ "$run_rc" -eq 2 ]; then + return 2 + fi + + if [ "$attempt" -ge "$max_attempts" ]; then + return 1 + fi + + if [ "$STRIX_TOTAL_TIMEOUT_SECONDS" -gt 0 ] && [ "$(remaining_total_budget)" -le 0 ]; then + printf "Strix quick scan exceeded total timeout of %ss.\n" "$STRIX_TOTAL_TIMEOUT_SECONDS" | tee "$STRIX_LOG" >&2 + return 1 + fi + + if ! is_transient_same_model_retry_error "$model"; then + return 1 + fi + + local retry_reason="transient error" + if is_rate_limit_error; then + retry_reason="rate limit" + elif is_llm_api_connection_error; then + retry_reason="LLM API connection" + elif is_llm_service_unavailable_error; then + retry_reason="LLM service unavailable" + elif is_midstream_fallback_error; then + retry_reason="midstream fallback" + fi + echo "Retrying model '$model' due to $retry_reason (attempt $((attempt + 1))/$max_attempts)." >&2 + sleep "$STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS" + attempt=$((attempt + 1)) + done + + return 1 +} + +is_vertex_not_found_error() { + # Match Vertex/LiteLLM model-not-found errors. + # These functions are only called within the Vertex fallback path + # (gated by is_vertex_model), so the risk of matching target-app + # 404s is low — strix separates LLM errors from scan findings. + if grep -Fq 'litellm.NotFoundError: Vertex_aiException' "$STRIX_LOG"; then + return 0 + fi + + if grep -Fq 'litellm.NotFoundError' "$STRIX_LOG" && grep -Eq '"status"[[:space:]]*:[[:space:]]*"NOT_FOUND"' "$STRIX_LOG"; then + return 0 + fi + + # Compact Vertex/GCP API error format — require a provider marker + # (litellm, VertexAI, or Vertex) nearby so we don't misclassify + # target-application 404 JSON responses as LLM provider errors. + if grep -Eq '"status"[[:space:]]*:[[:space:]]*"NOT_FOUND"' "$STRIX_LOG" && + grep -Eiq '(litellm|VertexAI|Vertex_ai|vertex\.ai|google\.cloud)' "$STRIX_LOG"; then + return 0 + fi + + if grep -Eq 'Publisher Model .*was not found' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +is_github_models_unavailable_model_error() { + if grep -Eiq 'Unavailable model:[[:space:]]*[^[:space:]]+' "$STRIX_LOG" && + grep -Eiq '(litellm\.BadRequestError|OpenAIException|LLM CONNECTION FAILED|Could not establish connection to the language model|models\.github\.ai|GitHub Models|openai)' "$STRIX_LOG"; then + return 0 + fi + + if grep -Eiq '(PermissionDeniedError|Error code:[[:space:]]*403|(^|[^0-9])403([^0-9]|$))' "$STRIX_LOG" && + grep -Eiq '(LLM CONNECTION FAILED|Could not establish connection to the language model)' "$STRIX_LOG" && + grep -Eiq '(models\.github\.ai|GitHub Models|openai|OpenAIException)' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +is_rate_limit_error() { + if grep -Fq 'RateLimitError' "$STRIX_LOG"; then + return 0 + fi + + if grep -Eq '"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"' "$STRIX_LOG"; then + return 0 + fi + + # Bare HTTP 429 — require a provider marker so we don't misclassify + # target-application rate-limit responses as LLM provider errors. + if grep -Eq '(^|[^0-9])429([^0-9]|$)' "$STRIX_LOG" && + grep -Eiq '(litellm|RateLimitError|VertexAI|Vertex_ai|vertex\.ai|openai|anthropic)' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +## Timeout classification — three-tier hierarchy: +## +## 1. litellm.exceptions.Timeout — SDK-level timeout raised by litellm. +## Always trusted as a genuine LLM timeout; no provider marker required. +## +## 2. httpx.ReadTimeout / httpcore.ReadTimeout — transport-layer timeouts +## from litellm/openai SDK internals. These strings can also appear in +## target-application logs, so an LLM-provider marker (LLM_PROVIDER_ONLY_REGEX) +## must be present nearby to classify as an LLM timeout. +## +## 3. Bare "Connection timed out" — generic OS/network timeout string. +## Requires LLM_PROVIDER_ONLY_REGEX to avoid misclassifying target-app +## or infrastructure network timeouts as LLM errors. +## +## All three tiers feed into infrastructure-error detection. Strict CI mode +## fails closed; non-strict callers may still evaluate fallback models. +## Same-model retries remain reserved for rate-limit and mid-stream fallback +## errors. +is_timeout_error() { + # Tier 1: litellm SDK timeout — provider-specific, always trusted. + if grep -Fq 'litellm.exceptions.Timeout' "$STRIX_LOG"; then + return 0 + fi + + if grep -Fq 'Strix run timed out after' "$STRIX_LOG"; then + return 0 + fi + + # Tier 2a: httpx transport timeout — requires LLM provider marker. + # httpx/httpcore are litellm/openai SDK transport libraries, but their + # timeout strings could appear in target-application logs too. + # Require an LLM provider-context marker (LLM_PROVIDER_ONLY_REGEX) to + # avoid misclassification — the httpx/httpcore/requests transport names + # in the timeout string itself are not sufficient proof of an LLM call. + if grep -Fq 'httpx.ReadTimeout' "$STRIX_LOG" && + grep -Eiq "$LLM_PROVIDER_ONLY_REGEX" "$STRIX_LOG"; then + return 0 + fi + + # Tier 2b: httpcore transport timeout — requires LLM provider marker. + if grep -Fq 'httpcore.ReadTimeout' "$STRIX_LOG" && + grep -Eiq "$LLM_PROVIDER_ONLY_REGEX" "$STRIX_LOG"; then + return 0 + fi + + # Tier 3: Bare "Connection timed out" — require a real LLM provider-context + # marker. httpx/httpcore/requests are transport libraries that could + # appear in any network timeout context, so they are NOT valid markers + # here. Use LLM_PROVIDER_ONLY_REGEX (defined alongside + # PROVIDER_CONTEXT_REGEX) to prevent drift. + if grep -Fq 'Connection timed out' "$STRIX_LOG" && + grep -Eiq "$LLM_PROVIDER_ONLY_REGEX" "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +is_midstream_fallback_error() { + if grep -Fq 'MidStreamFallbackError' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +# Narrower variant: LLM providers only, excluding HTTP transport libraries +# (httpx, httpcore, requests). Used for generic transport failures where +# library names alone are insufficient to prove the timeout/connection error +# originated from an LLM provider rather than the target application. +LLM_PROVIDER_ONLY_REGEX='(litellm|openai|anthropic|VertexAI|Vertex_ai|vertex\.ai|google\.cloud|GitHub Models|models\.github\.ai|github_models)' + +# Detect whether the strix log contains evidence of infrastructure-level +# errors (timeout, rate-limit, transport failures) that indicate the scan +# was interrupted or incomplete. Used as a guard to prevent the +# below-threshold override from silently passing an aborted scan. +has_detected_infrastructure_error() { + if grep -Eiq '(^|[^[:alpha:]])(Fatal|Denied|Warn|Warning)([^[:alpha:]]|$)' "$STRIX_LOG"; then + return 0 + fi + + if is_timeout_error; then + return 0 + fi + + if is_rate_limit_error; then + return 0 + fi + + if is_midstream_fallback_error; then + return 0 + fi + + if is_llm_api_connection_error; then + return 0 + fi + + if is_llm_service_unavailable_error; then + return 0 + fi + + # Generic strix non-zero exit with known transport/connection errors + # that don't fall into the specific categories above. + # Use LLM_PROVIDER_ONLY_REGEX (not PROVIDER_CONTEXT_REGEX) to avoid + # false positives: PROVIDER_CONTEXT_REGEX includes httpx/httpcore/requests + # which would self-match on e.g. "requests.exceptions.ConnectionError" + # from target-application logs. + if grep -Eiq '(ConnectionError|ConnectionRefusedError|ConnectionResetError|SSLError|ProxyError|NetworkError)' "$STRIX_LOG" && + grep -Eiq "$LLM_PROVIDER_ONLY_REGEX" "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +latest_strix_report_dir() { + local latest="" + local run_dir + + for run_dir in "$STRIX_REPORTS_DIR"/*; do + if [ ! -d "$run_dir" ] || [ -L "$run_dir" ]; then + continue + fi + + if is_preexisting_report_dir "$run_dir"; then + continue + fi + + if [ -z "$latest" ] || [ "$run_dir" -nt "$latest" ]; then + latest="$run_dir" + fi + done + + if [ -z "$latest" ]; then + return 1 + fi + + echo "$latest" +} + +has_only_below_threshold_vulnerabilities() { + local threshold_rank + threshold_rank="$(severity_rank "$STRIX_FAIL_ON_MIN_SEVERITY")" + + local found_any_vuln_file=0 + local global_max_rank=-1 + STRIX_MAX_SEVERITY_RANK=-1 + local saw_any_severity=0 + + update_max_severity_from_stream() { + local source_path="$1" + local line + local severity + local rank + while IFS= read -r line; do + if [[ "${line^^}" =~ SEVERITY[[:space:]]*:[[:space:][:punct:]]*(CRITICAL|HIGH|MEDIUM|LOW|INFO|INFORMATIONAL|NONE)([[:space:][:punct:]]|$) ]]; then + severity="${BASH_REMATCH[1]}" + else + continue + fi + + rank="$(severity_rank "$severity")" + if [ "$rank" -lt 0 ]; then + continue + fi + + saw_any_severity=1 + if [ "$rank" -gt "$global_max_rank" ]; then + global_max_rank="$rank" + STRIX_MAX_SEVERITY_RANK="$rank" + fi + done < <(grep -Ei 'severity[[:space:]]*:' "$source_path" || true) + } + + local run_dir + for run_dir in "$STRIX_REPORTS_DIR"/*; do + if [ ! -d "$run_dir" ] || [ -L "$run_dir" ]; then + continue + fi + + if is_preexisting_report_dir "$run_dir"; then + continue + fi + + local vulnerabilities_dir="$run_dir/vulnerabilities" + if [ ! -d "$vulnerabilities_dir" ] || [ -L "$vulnerabilities_dir" ]; then + continue + fi + + local vuln_file + + for vuln_file in "$vulnerabilities_dir"/*.md; do + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + continue + fi + + found_any_vuln_file=1 + update_max_severity_from_stream "$vuln_file" + done + done + + if [ "$found_any_vuln_file" -eq 0 ]; then + update_max_severity_from_stream "$STRIX_LOG" + fi + + if [ "$saw_any_severity" -eq 0 ]; then + return 1 + fi + + # Guard against incomplete scans due to infrastructure errors. + # Use the sticky INFRA_ERROR_DETECTED flag instead of re-reading + # STRIX_LOG, because STRIX_LOG is overwritten per-attempt. If an + # earlier attempt hit an infrastructure error (timeout, rate-limit, + # transport failure) and produced a partial report that now sits in + # the reports directory, the *current* STRIX_LOG may show a different + # failure — or even success — but the partial report's low-severity + # findings must not be treated as a clean scan result. + if [ "$INFRA_ERROR_DETECTED" -eq 1 ]; then + echo "Below-threshold findings detected, but infrastructure errors occurred during this pipeline run; refusing bypass due to potentially incomplete scan." >&2 + return 1 + fi + + if [ "$global_max_rank" -lt "$threshold_rank" ]; then + echo "Strix findings are below configured fail threshold '$STRIX_FAIL_ON_MIN_SEVERITY'; allowing pipeline continuation." >&2 + return 0 + fi + + return 1 +} + +has_blocking_vulnerability_reports() { + local threshold_rank + threshold_rank="$(severity_rank "$STRIX_FAIL_ON_MIN_SEVERITY")" + + local run_dir vulnerabilities_dir vuln_file rank + for run_dir in "$STRIX_REPORTS_DIR"/*; do + if [ ! -d "$run_dir" ] || [ -L "$run_dir" ]; then + continue + fi + if is_preexisting_report_dir "$run_dir"; then + continue + fi + + vulnerabilities_dir="$run_dir/vulnerabilities" + if [ ! -d "$vulnerabilities_dir" ] || [ -L "$vulnerabilities_dir" ]; then + continue + fi + + for vuln_file in "$vulnerabilities_dir"/*.md; do + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + continue + fi + if vulnerability_file_is_retryable_model_inconsistency "$vuln_file"; then + continue + fi + + rank="$(extract_first_severity_rank "$vuln_file")" + if [ "$rank" -lt 0 ] || [ "$rank" -ge "$threshold_rank" ]; then + return 0 + fi + done + done + + return 1 +} + +fail_reported_vulnerabilities_before_fallback_success() { + if has_blocking_vulnerability_reports; then + echo "Strix model reported threshold vulnerabilities before fallback success; failing closed so every model-reported vulnerability is reviewed." >&2 + echo "Strix quick scan failed with a non-recoverable error." >&2 + return 0 + fi + return 1 +} + +has_any_reported_severity_markers() { + local run_dir + for run_dir in "$STRIX_REPORTS_DIR"/*; do + if [ ! -d "$run_dir" ] || [ -L "$run_dir" ]; then + continue + fi + + if is_preexisting_report_dir "$run_dir"; then + continue + fi + + local vulnerabilities_dir="$run_dir/vulnerabilities" + if [ ! -d "$vulnerabilities_dir" ] || [ -L "$vulnerabilities_dir" ]; then + continue + fi + + local vuln_file + for vuln_file in "$vulnerabilities_dir"/*.md; do + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + continue + fi + if grep -Eiq 'severity[[:space:]]*:' "$vuln_file"; then + return 0 + fi + done + done + + if grep -Eiq 'severity[[:space:]]*:' "$STRIX_LOG"; then + return 0 + fi + + return 1 +} + +strix_reported_zero_vulnerabilities() { + if [ "$ZERO_FINDINGS_REPORTED" -eq 1 ]; then + return 0 + fi + + strix_reported_zero_vulnerabilities_in_file "$STRIX_LOG" +} + +strix_reported_zero_vulnerabilities_in_file() { + local source_path="$1" + grep -Eq 'Vulnerabilities[[:space:]]+0([^0-9]|$)' "$source_path" +} + +should_fail_pull_request_infra_zero_findings() { + if ! is_pull_request_event; then + return 1 + fi + + if [ "$INFRA_ERROR_DETECTED" -ne 1 ]; then + return 1 + fi + + if has_any_reported_severity_markers; then + return 1 + fi + + if ! strix_reported_zero_vulnerabilities; then + return 1 + fi + + echo "Strix reported zero vulnerabilities before provider infrastructure failure; failing closed because provider infrastructure failures are not clean scan evidence." >&2 + return 0 +} + +vulnerability_file_has_absent_endpoint_finding() { + local vuln_file="$1" + # Configurable list of source directories to check for endpoints. + # Defaults to "." (i.e. TARGET_PATH itself) so that both + # STRIX_TARGET_PATH=./ and STRIX_TARGET_PATH=./src work correctly + # without producing bogus double-nested paths like ./src/src. + # Set STRIX_SOURCE_DIRS (space-separated) to override. + local source_dirs_raw="${STRIX_SOURCE_DIRS:-.}" + local resolved_target_root="" + local resolved_dirs=() + local dir_entry + if ! resolved_target_root="$(resolve_current_target_path "$TARGET_PATH" 2>/dev/null)"; then + return 1 + fi + + # Disable globbing so that entries like "*" or "[" in STRIX_SOURCE_DIRS + # are not expanded by pathname expansion during word-splitting. + set -f + for dir_entry in $source_dirs_raw; do + local candidate="${resolved_target_root%/}/$dir_entry" + if [ -d "$candidate" ] && [ ! -L "$candidate" ]; then + resolved_dirs+=("$candidate") + fi + done + set +f + + if [ "${#resolved_dirs[@]}" -eq 0 ]; then + return 1 + fi + + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + return 1 + fi + + local endpoint_seen=0 + local endpoint_present_in_source=0 + local endpoint + + while IFS= read -r endpoint; do + if [ -z "$endpoint" ]; then + continue + fi + + endpoint_seen=1 + local search_dir + for search_dir in "${resolved_dirs[@]}"; do + # Exclude the strix reports directory and common non-source + # directories from the source search to prevent accidental + # matches and reduce runtime (especially when STRIX_TARGET_PATH=./). + # + # Each exclude-dir: + # STRIX_REPORTS_DIR — strix output itself (would always match). + # Both the full path and basename are excluded so that + # nested paths like "reports/strix_runs" are also caught. + # .git — VCS internals + # node_modules — JS/TS dependencies (may contain API strings) + # vendor — Go/PHP vendored deps + # __pycache__ — Python bytecode cache + # .venv — Python virtualenv + # target — Rust/Java build artifacts + # .mypy_cache — mypy type-check cache + # .pytest_cache — pytest result cache + # dist — common build output directory + # build — common build output directory + # .tox — Python tox test environments + # .ruff_cache — Ruff linter cache + if grep -r -Fq \ + --exclude-dir="$STRIX_REPORTS_DIR" \ + --exclude-dir="$(basename "$STRIX_REPORTS_DIR")" \ + --exclude-dir=".git" \ + --exclude-dir="node_modules" \ + --exclude-dir="vendor" \ + --exclude-dir="__pycache__" \ + --exclude-dir=".venv" \ + --exclude-dir="target" \ + --exclude-dir=".mypy_cache" \ + --exclude-dir=".pytest_cache" \ + --exclude-dir="dist" \ + --exclude-dir="build" \ + --exclude-dir=".tox" \ + --exclude-dir=".ruff_cache" \ + -- "$endpoint" "$search_dir"; then + endpoint_present_in_source=1 + break + fi + done + if [ "$endpoint_present_in_source" -eq 1 ]; then + break + fi + done < <(python3 - "$vuln_file" <<'PY' +from pathlib import Path +import re +import sys + +text = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace") +endpoints = set() +for line in text.splitlines(): + if not re.search(r"\bEndpoint\b", line, re.IGNORECASE): + continue + endpoints.update(re.findall(r"/api/[A-Za-z0-9_./-]+", line)) +for endpoint in sorted(endpoints): + print(endpoint) +PY + ) + + if [ "$endpoint_seen" -eq 0 ]; then + return 1 + fi + + if [ "$endpoint_present_in_source" -eq 1 ]; then + return 1 + fi + + echo "Detected Strix report endpoint(s) absent from source; treating as retryable model inconsistency." >&2 + return 0 +} + +is_hallucinated_endpoint_finding() { + local latest_report_dir + if ! latest_report_dir="$(latest_strix_report_dir)"; then + return 1 + fi + + local vuln_file + + for vuln_file in "$latest_report_dir"/vulnerabilities/*.md; do + if vulnerability_file_has_absent_endpoint_finding "$vuln_file"; then + return 0 + fi + done + + return 1 +} + +source_file_has_encrypted_runner_registration_token() { + local source_file="$1" + python3 - "$source_file" <<'PY' +from pathlib import Path +import re +import sys + +source_path = Path(sys.argv[1]) +text = source_path.read_text(encoding="utf-8", errors="replace") +class_match = re.search( + r"^class\s+WorkspaceRunnerConfig\b[\s\S]*?(?=^class\s+\w|\Z)", + text, + re.MULTILINE, +) +if not class_match: + raise SystemExit(1) +class_body = class_match.group(0) +encrypted_registration_token = re.search( + r"registration_token[\s\S]{0,260}mapped_column\(\s*EncryptedString\b", + class_body, +) +raise SystemExit(0 if encrypted_registration_token else 1) +PY +} + +report_claims_plain_runner_registration_token() { + local vuln_file="$1" + python3 - "$vuln_file" <<'PY' +from pathlib import Path +import re +import sys + +text = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace") +if "WorkspaceRunnerConfig" not in text or "registration_token" not in text: + raise SystemExit(1) +if "backend/db/models.py" not in text: + raise SystemExit(1) +plain_string_claim = re.search( + r"registration_token[\s\S]{0,500}mapped_column\(\s*String\b", + text, +) +plain_text_claim = re.search( + r"registration_token[\s\S]{0,500}(plain text|plain string|stored as a plain)", + text, + re.IGNORECASE, +) +raise SystemExit(0 if plain_string_claim or plain_text_claim else 1) +PY +} + +runner_registration_token_source_candidates() { + local resolved_scan_target="" + resolved_scan_target="$(resolve_current_target_path "$TARGET_PATH" 2>/dev/null || true)" + + if [ -n "$resolved_scan_target" ]; then + printf '%s\n' "$resolved_scan_target/backend/db/models.py" + fi + if pull_request_head_blob_required || [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -eq 1 ]; then + return 0 + fi + printf '%s\n' "$REPO_ROOT/backend/db/models.py" +} + +vulnerability_file_has_hallucinated_source_claim() { + local vuln_file="$1" + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + return 1 + fi + if ! report_claims_plain_runner_registration_token "$vuln_file"; then + return 1 + fi + + local source_file + while IFS= read -r source_file; do + if [ -z "$source_file" ]; then + continue + fi + if [ ! -f "$source_file" ] || [ -L "$source_file" ]; then + continue + fi + if source_file_has_encrypted_runner_registration_token "$source_file"; then + echo "Detected Strix report contradicting scanned runner registration token encryption; treating as retryable model inconsistency." >&2 + return 0 + fi + done < <(runner_registration_token_source_candidates) + + return 1 +} + +vulnerability_file_is_retryable_model_inconsistency() { + local vuln_file="$1" + if vulnerability_file_has_absent_endpoint_finding "$vuln_file"; then + return 0 + fi + if vulnerability_file_has_hallucinated_source_claim "$vuln_file"; then + return 0 + fi + return 1 +} + +is_hallucinated_source_claim_finding() { + local latest_report_dir + if ! latest_report_dir="$(latest_strix_report_dir)"; then + return 1 + fi + + local vuln_file + for vuln_file in "$latest_report_dir"/vulnerabilities/*.md; do + if vulnerability_file_has_hallucinated_source_claim "$vuln_file"; then + return 0 + fi + done + + return 1 +} + +is_model_retryable_error() { + local model="$1" + + if is_vertex_model "$model" && is_vertex_not_found_error; then + return 0 + fi + + if is_github_models_api_compatible_model "$model" && is_github_models_unavailable_model_error; then + return 0 + fi + + if is_rate_limit_error; then + return 0 + fi + + if is_timeout_error; then + if provider_signal_fail_closed_enabled; then + return 1 + fi + return 0 + fi + + if is_midstream_fallback_error; then + return 0 + fi + + if is_llm_api_connection_error; then + return 0 + fi + + if is_llm_service_unavailable_error; then + return 0 + fi + + if [ "$PR_FINDINGS_DECISION" = "retry_model_inconsistency" ]; then + return 0 + fi + + if is_pull_request_event; then + return 1 + fi + + if is_hallucinated_endpoint_finding; then + return 0 + fi + + if is_hallucinated_source_claim_finding; then + return 0 + fi + + return 1 +} + +run_current_target_scan() { + INFRA_ERROR_DETECTED=0 + ZERO_FINDINGS_REPORTED=0 + + local primary_scan_rc=0 + run_strix_with_transient_retry "$PRIMARY_MODEL" || primary_scan_rc=$? + if [ "$primary_scan_rc" -eq 0 ]; then + return 0 + fi + if [ "$primary_scan_rc" -eq 2 ]; then + return 2 + fi + + local strict_primary_provider_fallback=0 + if [ "$INFRA_ERROR_DETECTED" -eq 1 ] && provider_signal_fail_closed_enabled; then + if is_model_retryable_error "$PRIMARY_MODEL" && has_distinct_fallback_model_for_model "$PRIMARY_MODEL"; then + strict_primary_provider_fallback=1 + else + echo "Strix scan failed after provider infrastructure or failure-signal output; failing closed." >&2 + return 1 + fi + fi + + if has_only_below_threshold_vulnerabilities; then + return 0 + fi + + if evaluate_pull_request_findings; then + if [ "$strict_primary_provider_fallback" -eq 0 ]; then + return 0 + fi + fi + + case "$PR_FINDINGS_DECISION" in + block_changed | block_unmapped | block_manifest_unverified) + return 1 + ;; + esac + + if [ "$strict_primary_provider_fallback" -eq 1 ] && fail_reported_vulnerabilities_before_fallback_success; then + return 1 + fi + + if ! is_model_retryable_error "$PRIMARY_MODEL"; then + echo "Strix quick scan failed with a non-recoverable error." >&2 + return 1 + fi + + FALLBACK_MODELS_RAW="$(fallback_models_raw_for_model "$PRIMARY_MODEL")" + FALLBACK_MODELS_RAW="${FALLBACK_MODELS_RAW//$'\r'/ }" + FALLBACK_MODELS_RAW="${FALLBACK_MODELS_RAW//$'\n'/ }" + read -r -a FALLBACK_MODELS <<<"$FALLBACK_MODELS_RAW" + + fallback_tried=0 + for candidate_raw in "${FALLBACK_MODELS[@]}"; do + candidate="$(normalize_model "$candidate_raw")" + if [ -z "$candidate" ] || [ "$candidate" = "$PRIMARY_MODEL" ]; then + if [ -n "$candidate" ]; then + echo "Skipping fallback model '$candidate' — same as primary model." >&2 + fi + continue + fi + + fallback_tried=1 + if is_vertex_model "$PRIMARY_MODEL"; then + echo "Primary Vertex model unavailable; retrying with fallback '$candidate'." + else + echo "Primary model unavailable; retrying with fallback '$candidate'." + fi + local fallback_scan_rc=0 + local fallback_start_epoch + fallback_start_epoch="$(date +%s)" + run_strix_with_transient_retry "$candidate" || fallback_scan_rc=$? + local fallback_elapsed=$(( $(date +%s) - fallback_start_epoch )) + if [ "$fallback_scan_rc" -eq 0 ]; then + if fail_reported_vulnerabilities_before_fallback_success; then + return 1 + fi + echo "Strix quick scan succeeded with fallback model '$candidate' in ${fallback_elapsed}s." >&2 + return 0 + fi + if [ "$fallback_scan_rc" -eq 2 ]; then + return 2 + fi + + local strict_fallback_provider_signal=0 + if [ "$INFRA_ERROR_DETECTED" -eq 1 ] && provider_signal_fail_closed_enabled; then + strict_fallback_provider_signal=1 + fi + + if has_only_below_threshold_vulnerabilities; then + return 0 + fi + + if evaluate_pull_request_findings; then + if [ "$strict_fallback_provider_signal" -eq 0 ]; then + return 0 + fi + fi + + case "$PR_FINDINGS_DECISION" in + block_changed | block_unmapped | block_manifest_unverified) + return 1 + ;; + esac + + if fail_reported_vulnerabilities_before_fallback_success; then + return 1 + fi + + if [ "$strict_fallback_provider_signal" -eq 1 ]; then + if is_model_retryable_error "$candidate"; then + continue + fi + echo "Strix fallback model '$candidate' emitted provider infrastructure or failure-signal output; trying next configured fallback if available." >&2 + continue + fi + + if ! is_model_retryable_error "$candidate"; then + echo "Strix quick scan failed with a non-recoverable error." >&2 + return 1 + fi + done + + if should_fail_pull_request_infra_zero_findings; then + return 1 + fi + + if [ "$fallback_tried" -eq 0 ]; then + local fallback_config_name + fallback_config_name="$(fallback_models_config_name_for_model "$PRIMARY_MODEL")" + local configured_fallback_count=0 + for candidate_raw in "${FALLBACK_MODELS[@]}"; do + candidate="$(normalize_model "$candidate_raw")" + [ -n "$candidate" ] && configured_fallback_count=$((configured_fallback_count + 1)) + done + if [ "$configured_fallback_count" -eq 0 ]; then + echo "ERROR: No fallback models configured ($fallback_config_name is empty). Configure distinct models." >&2 + else + echo "ERROR: All configured fallback models are the same as the primary model" >&2 + fi + return 1 + fi + + local threshold_rank + threshold_rank="$(severity_rank "$STRIX_FAIL_ON_MIN_SEVERITY")" + if [ "${STRIX_MAX_SEVERITY_RANK:--1}" -ge "$threshold_rank" ]; then + echo "Strix quick scan failed with a non-recoverable error." >&2 + return 1 + fi + + if is_vertex_model "$PRIMARY_MODEL"; then + echo "Configured Vertex model and fallback models were unavailable." >&2 + else + echo "Configured model and fallback models were unavailable." >&2 + fi + return 1 +} + +prepare_pull_request_scan_scope +if [ "$TARGET_PATH_REQUESTS_PR_SCOPE" -eq 1 ] && + [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -ne 1 ]; then + echo "ERROR: STRIX_TARGET_PATH=$PR_SCOPE_TARGET_SENTINEL did not produce a PR scan scope." >&2 + exit 2 +fi + +scan_rc=0 +run_current_target_scan || scan_rc=$? +exit "$scan_rc" diff --git a/services/analysis-engine/tests/test_sections_utils.py b/services/analysis-engine/tests/test_sections_utils.py new file mode 100644 index 00000000..dee55077 --- /dev/null +++ b/services/analysis-engine/tests/test_sections_utils.py @@ -0,0 +1,60 @@ +"""Tests for the section utils functions.""" + +from __future__ import annotations + +import logging +from unittest.mock import MagicMock + +from bandscope_analysis.sections.utils import validate_section + + +def test_validate_section_valid_with_id() -> None: + """Test validate_section with a valid section containing an 'id'.""" + logger = MagicMock(spec=logging.Logger) + section = {"id": "verse-1", "label": "verse"} + + result = validate_section(section, 0, logger) + + assert result == "verse-1" + logger.warning.assert_not_called() + + +def test_validate_section_valid_without_id() -> None: + """Test validate_section with a valid dict but missing an 'id'.""" + logger = MagicMock(spec=logging.Logger) + section = {"label": "chorus"} + + result = validate_section(section, 5, logger) + + assert result == "section-5" + logger.warning.assert_not_called() + + +def test_validate_section_invalid_type() -> None: + """Test validate_section with an invalid section type (not a dict).""" + logger = MagicMock(spec=logging.Logger) + section = "not a dict" + + result = validate_section(section, 2, logger) + + assert result == "section-2" + logger.warning.assert_called_once_with( + "Invalid section format at index %d; expected dict, got %s", + 2, + "str", + ) + + +def test_validate_section_none_type() -> None: + """Test validate_section with None.""" + logger = MagicMock(spec=logging.Logger) + section = None + + result = validate_section(section, 3, logger) + + assert result == "section-3" + logger.warning.assert_called_once_with( + "Invalid section format at index %d; expected dict, got %s", + 3, + "NoneType", + ) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index c471caac..08ebc48e 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -5165,8 +5165,11 @@ def test_opencode_normalizer_defaults_missing_approve_findings(tmp_path: Path) - [ "review text", '{"head_sha":"abc123","run_id":"456","run_attempt":"1",' - '"result":"APPROVE","reason":"checks and review passed",' - '"summary":"no source-backed blockers found"}', + '"result":"APPROVE",' + '"reason":"checks passed for ' + 'scripts/ci/opencode_review_normalize_output.py",' + '"summary":"no blockers in ' + 'scripts/ci/opencode_review_normalize_output.py"}', ] ), encoding="utf-8", @@ -5198,8 +5201,11 @@ def test_opencode_review_gate_defaults_missing_approve_findings(tmp_path: Path) "", "", "", ]