benchflow 0.5.3.dev956__tar.gz → 0.5.3.dev976__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/PKG-INFO +1 -1
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/pyproject.toml +1 -1
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/task_authoring.py +33 -1
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/cli/main.py +40 -0
- benchflow-0.5.3.dev976/src/benchflow/sandbox/_cache_reclaim.py +97 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/lockdown.py +8 -14
- benchflow-0.5.3.dev976/tests/fixtures/digest/README.md +9 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/alpha-task/environment/Dockerfile +1 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/alpha-task/instruction.md +3 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/alpha-task/task.toml +4 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/alpha-task/tests/test.sh +2 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/beta-task/data/unicode.txt +1 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/beta-task/instruction.md +1 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/beta-task/task.toml +1 -0
- benchflow-0.5.3.dev976/tests/fixtures/digest/not-a-task/notes.txt +1 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_hardening.py +106 -50
- benchflow-0.5.3.dev976/tests/test_task_digest.py +161 -0
- benchflow-0.5.3.dev976/tests/trajectories/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/.gitignore +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/CHANGELOG.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/LICENSE +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/README.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_dotenv.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_paths.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_run.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_types.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/benchmark_repos.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/evaluation_results.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/json_safe.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/learner_memory.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/result_metadata.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/reward_events.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/scoring.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/source_provenance.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/_utils/yaml_loader.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/client.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/container_transport.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/session.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/transport.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/acp/types.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/adapters/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/adapters/harbor.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/adapters/inbound.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/adapters/inspect_ai.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/adapters/ors.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/adapters/terminal_bench.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/codex_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/credentials.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/errors.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/harvey_lab_acp_shim.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/install.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/openclaw_acp_shim.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/pi_acp_launcher.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/providers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/agents/registry.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/branch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/cli/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/cli/continue_cmd.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/cli/trace_import.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/compat/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/compat/harbor_registry.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/continue_run/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/continue_run/batch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/continue_run/orchestrator.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/continue_run/replay_proxy.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/continue_run/run_folder.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/continue_run/sandbox_proxy.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/contracts/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/contracts/planes.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/contracts/user.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/demo_task/environment/Dockerfile +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/demo_task/instruction.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/demo_task/task.toml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/demo_task/tests/test.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/diagnostics.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/environment/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/environment/manifest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/environment/manifest_env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/environment/protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/environment/readiness.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/eval_sharding.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/eval_worker.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/evaluation.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/experimental/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/experimental/mcp/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/experimental/mcp/hooks.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/experimental/mcp/reviewer_server.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/hosted_env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/learner_skills.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/learner_store.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/metrics.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/models.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/monitor.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/litellm_bedrock_patch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/litellm_bedrock_preflight.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/litellm_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/litellm_logging.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/litellm_runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/providers/runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/py.typed +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/README.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/builtins.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/events.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/file_readers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/llm.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/memory_scorer.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/node.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/rubric.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/rubric_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rewards/validation.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rollout.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rollout_branch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/rollout_planes.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_base.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_compose.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_compose_files/docker-compose-base.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_compose_files/docker-compose-build.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_compose_files/docker-compose-no-network.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_compose_files/docker-compose-prebuilt.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/_sdk_ops.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/daytona.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/docker.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/metadata.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/modal_impl.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/process.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/services.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/setup.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/snapshot.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sandbox/user.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/scenes.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/sdk.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/self_gen.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/skill_eval/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/skill_eval/_core.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/skill_eval/gepa_export.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/skill_eval/schema.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/skill_policy.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/skills.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/task/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/task/config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/task/env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/task/paths.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/task/task.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/task/verifier.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/templates/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/templates/judge.py.tmpl +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/templates/test.sh.tmpl +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/traces/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/traces/huggingface.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/traces/local.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/traces/models.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/traces/parsers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/traces/task_gen.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/_capture.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/export.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/metrics.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/otel.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/tree.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/types.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/trajectories/viewer.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/src/benchflow/usage_tracking.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/agents/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/agents/test_protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/README.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/environment/Dockerfile +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/environment/docker-compose.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/environment/skills/conformance-writer/SKILL.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/instruction.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/solution/solve.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/task.toml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/acp_smoke/tests/test.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/conformance-results.json +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/proof_multi_agent.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/proof_snapshot.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/run_conformance.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conformance/self_gen_smoke_skills/skill-creator/SKILL.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/conftest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/continue_run/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/continue_run/_helpers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/continue_run/test_batch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/continue_run/test_orchestrator.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/continue_run/test_replay_proxy.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/continue_run/test_run_folder.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/__init__.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/test_chibench_manifest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/test_clawsbench_manifest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/test_manifest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/test_manifest_env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/test_protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/environment/test_readiness.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/hello-world-task/environment/Dockerfile +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/hello-world-task/instruction.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/hello-world-task/solution/solve.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/hello-world-task/task.toml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/hello-world-task/tests/test.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/terminal-bench-smoke-task/environment/Dockerfile +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/terminal-bench-smoke-task/instruction.md +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/terminal-bench-smoke-task/solution/solve.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/terminal-bench-smoke-task/task.toml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/terminal-bench-smoke-task/tests/test.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/terminal-bench-smoke-task/tests/test_state.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/test_claude.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/test_codex.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/test_codex_custom_provider.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/test_gemini.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/test_openclaw.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/traces/minimal-claude.jsonl +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/examples/traces/minimal-opentraces.jsonl +0 -0
- /benchflow-0.5.3.dev956/tests/trajectories/__init__.py → /benchflow-0.5.3.dev976/tests/fixtures/digest/beta-task/data/empty.bin +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/fixtures/mock_acp_agent.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/fixtures/mock_acp_agent_interleaved.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/fixtures/mock_acp_agent_multi_turn.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/fixtures/mock_openai_responses_server.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/check_adapter_evidence.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/check_hosted_env_evidence.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/check_results.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/check_skillsbench_harbor_parity.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/check_trace_to_task_evidence.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/claude-agent-acp.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/codex-acp.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/gemini.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/harvey-lab-harness.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/openclaw.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/opencode.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/openhands.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/configs/pi-acp.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/run.sh +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/run_suite.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/integration/suites/release.yaml +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_acp.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_acp_capability_advertising.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_acp_model_config_dispatch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_acp_pinned_protocol_guard.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_acp_setup_failure_propagation.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_adapter_scripts.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_adapters.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_cli.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_env_resolution.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_gemini_defaults.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_idle_timeout_cli.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_model_decouple.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_registry.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_setup.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_agent_spec.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_base_install_imports.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_bedrock_thinking.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_branch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_capture_trajectory.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_clawsbench_slice.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_cli_daytona.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_cli_docs_drift.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_cli_misc.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_compat_harbor_registry.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_config_redaction.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_connect_as_env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_continuallearningbench_adapter.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_credential_env_scrub.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_daytona_key.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_no_host_paths.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_release_evidence.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_roadmap.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_symlink_ingestion.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_dashboard_sync.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_daytona_command_polling.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_daytona_litellm_runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_daytona_status.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_docker_prune_scoping.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_docker_uploads.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_docs_examples.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eng50_capabilities.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_env_setup.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_environment_manifest_controls.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eval_filters_applied.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eval_sharding.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eval_single_task_summary.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eval_source_provenance.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eval_worker_retry.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_eval_zero_task_guard.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_evaluation_environment_manifest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_exclude_tasks.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_experiments_status.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_harvey_lab_shim.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_hf_scores.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_hilbench_adapter.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_hosted_env.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_hosted_env_rollout_contract.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_inbound_adapter_manifest.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_inbound_adapters.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_integration_check_results.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_integration_run_suite.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_internet_policy.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_job.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_job_sequential_shared.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_job_sequential_shared_resume.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_judge_symlink_ingestion.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_learner_skills.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_learner_skills_traversal.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_learner_store.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_learner_store_persistence.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_litellm_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_litellm_hardening.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_litellm_logging.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_litellm_runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_litellm_smoke.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_llm_judge.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_llm_judge_event_tags.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_llm_judge_verifier.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_memory_scorer.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_metrics.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_mock_openai_responses_server.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_monitor_scaffold.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_native_acp_usage.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_no_cross_provider_fallback.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_notification_order_real.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_oracle.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_oracle_chokepoint.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_paths_safe.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_paths_symlink_helpers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_pi_acp_launcher.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_process.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_provider_auth_detection.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_providers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_reexport.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_registry_invariants.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_release_version.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_resolve_env_helpers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_reward_node.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_reward_unified_contract.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rewards.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rewards_jsonl.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_architecture.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_branch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_config_path_coercion.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_environment.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_import_no_side_effects.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_on_ask_user_wiring.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_probe_sandbox_health.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rollout_upload.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_rubric_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_runtime_config_wired.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_runtime_live_sandbox.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_exec_secret_handling.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_isolation_copy_traversal.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_multi_service.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_protocol.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_setup.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_snapshot_contract.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_upload_symlink.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sandbox_verifier_workspace.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_scene.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_scene_outbox_trial.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_scene_parallel_group.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_scene_result_aggregation.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_scoring.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sdk_internals.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_sdk_lockdown.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_self_gen_cli.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_self_gen_export_error_channel.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_self_gen_export_failures.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_self_gen_orchestration.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_session_request_permission_dispatch.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_eval.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_eval_dryrun.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_eval_integration.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_eval_sweep.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_eval_traversal.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_invocation_artifacts.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skill_policy.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skills.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skills_dir_agent_home_link.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skillsbench_harbor_parity.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skillsbench_harbor_run_suite.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_skillsbench_publish_scrub.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_smoke.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_subscription_auth.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_task_check_eval_consistency.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_task_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_task_download.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_tasks.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_token_usage_normalization.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trace_import_cli.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trace_task_gen_traversal.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trace_to_task_evidence.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_traces_huggingface.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_traces_parsers.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_traces_task_gen.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_train_mode_artifact_emission.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trajectory_integration.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trajectory_streaming.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trial_agent_timeout_verify.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trial_install_agent_timeout.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_trial_litellm_runtime.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_usage_litellm.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_usage_required.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_usage_tracking.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_user.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_verifier_multi_container.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_verifier_output.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_verifier_output_freshness.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_verify.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_workflow_action_pinning.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/test_yaml_config.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/trajectories/test_export.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/trajectories/test_export_nan_handling.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/trajectories/test_redaction.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/trajectories/test_step_granularity.py +0 -0
- {benchflow-0.5.3.dev956 → benchflow-0.5.3.dev976}/tests/trajectories/test_tree.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: benchflow
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev976
|
|
4
4
|
Summary: Multi-turn agent benchmarking with ACP — run any agent, any model, any provider.
|
|
5
5
|
Project-URL: Homepage, https://github.com/benchflow-ai/benchflow
|
|
6
6
|
Project-URL: Repository, https://github.com/benchflow-ai/benchflow
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
"""Task authoring — init and
|
|
1
|
+
"""Task authoring — init, check, and digest benchmark tasks."""
|
|
2
2
|
|
|
3
|
+
import hashlib
|
|
3
4
|
import logging
|
|
5
|
+
import os
|
|
4
6
|
import re
|
|
5
7
|
import tomllib
|
|
6
8
|
from pathlib import Path
|
|
@@ -94,6 +96,36 @@ def check_task(task_dir: Path) -> list[str]:
|
|
|
94
96
|
return issues
|
|
95
97
|
|
|
96
98
|
|
|
99
|
+
def task_digest(task_dir: Path) -> str:
|
|
100
|
+
"""Content digest pinning a task's files, independent of git.
|
|
101
|
+
|
|
102
|
+
sha256 over every regular file under ``task_dir``, sorted by POSIX
|
|
103
|
+
relative path; each file contributes
|
|
104
|
+
``path_utf8 + b"\\x00" + sha256(file_bytes).digest()``. Symlinks and
|
|
105
|
+
file modes are excluded, so the digest is reproducible from a plain
|
|
106
|
+
checkout. Must byte-match the reference digests in the skillsbench
|
|
107
|
+
dataset registry (``registry.json`` / ``docs/dataset-versioning.md``,
|
|
108
|
+
skillsbench PR #922).
|
|
109
|
+
"""
|
|
110
|
+
if not task_dir.is_dir():
|
|
111
|
+
raise NotADirectoryError(f"Not a directory: {task_dir}")
|
|
112
|
+
files: list[tuple[str, Path]] = []
|
|
113
|
+
# os.walk never descends into symlinked directories (unlike pre-3.13
|
|
114
|
+
# Path.rglob), keeping "symlinks are excluded" true for whole subtrees.
|
|
115
|
+
for dirpath, _dirnames, filenames in os.walk(task_dir):
|
|
116
|
+
for filename in filenames:
|
|
117
|
+
path = Path(dirpath) / filename
|
|
118
|
+
if path.is_symlink() or not path.is_file():
|
|
119
|
+
continue
|
|
120
|
+
files.append((path.relative_to(task_dir).as_posix(), path))
|
|
121
|
+
digest = hashlib.sha256()
|
|
122
|
+
for rel, path in sorted(files):
|
|
123
|
+
digest.update(rel.encode("utf-8"))
|
|
124
|
+
digest.update(b"\x00")
|
|
125
|
+
digest.update(hashlib.sha256(path.read_bytes()).digest())
|
|
126
|
+
return f"sha256:{digest.hexdigest()}"
|
|
127
|
+
|
|
128
|
+
|
|
97
129
|
_CTRF_STANDARD_PATH = "/logs/verifier/ctrf.json"
|
|
98
130
|
|
|
99
131
|
|
|
@@ -674,6 +674,46 @@ def tasks_check(
|
|
|
674
674
|
raise typer.Exit(1)
|
|
675
675
|
|
|
676
676
|
|
|
677
|
+
@tasks_app.command("digest")
|
|
678
|
+
def tasks_digest(
|
|
679
|
+
path: Annotated[
|
|
680
|
+
Path,
|
|
681
|
+
typer.Argument(
|
|
682
|
+
help="Path to a task directory, or a directory of task directories"
|
|
683
|
+
),
|
|
684
|
+
],
|
|
685
|
+
) -> None:
|
|
686
|
+
"""Compute the content digest that pins a task's files, independent of git.
|
|
687
|
+
|
|
688
|
+
Matches the digests in the skillsbench dataset registry (registry.json).
|
|
689
|
+
Given a single task directory (contains task.toml), prints the digest;
|
|
690
|
+
given a directory of tasks, prints one "<name> <digest>" line per task.
|
|
691
|
+
"""
|
|
692
|
+
from benchflow._utils.task_authoring import task_digest
|
|
693
|
+
|
|
694
|
+
if not path.is_dir():
|
|
695
|
+
console.print(f"[red]Not a directory: {path}[/red]")
|
|
696
|
+
raise typer.Exit(1)
|
|
697
|
+
|
|
698
|
+
if (path / "task.toml").is_file():
|
|
699
|
+
# typer.echo, not console.print: Rich wraps lines at terminal width,
|
|
700
|
+
# which would corrupt piped machine-readable output.
|
|
701
|
+
typer.echo(task_digest(path))
|
|
702
|
+
return
|
|
703
|
+
|
|
704
|
+
task_dirs = sorted(
|
|
705
|
+
d for d in path.iterdir() if d.is_dir() and (d / "task.toml").is_file()
|
|
706
|
+
)
|
|
707
|
+
if not task_dirs:
|
|
708
|
+
console.print(
|
|
709
|
+
f"[red]No tasks under {path} — expected task.toml in it "
|
|
710
|
+
f"or in its immediate subdirectories[/red]"
|
|
711
|
+
)
|
|
712
|
+
raise typer.Exit(1)
|
|
713
|
+
for task_dir in task_dirs:
|
|
714
|
+
typer.echo(f"{task_dir.name} {task_digest(task_dir)}")
|
|
715
|
+
|
|
716
|
+
|
|
677
717
|
compat_app = typer.Typer(help="Third-party framework compatibility checks.")
|
|
678
718
|
app.add_typer(compat_app, name="compat")
|
|
679
719
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Pre-verifier cache reclaim command construction.
|
|
2
|
+
|
|
3
|
+
The sandbox cannot import benchflow internals, so the reclaim implementation is
|
|
4
|
+
an embedded Python snippet passed to ``python3 -c``. Keep it here instead of in
|
|
5
|
+
``lockdown.py`` so the hardening orchestrator stays readable.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import shlex
|
|
9
|
+
from textwrap import dedent
|
|
10
|
+
|
|
11
|
+
# Symlink- and workspace-safe cache reclaim for #601.
|
|
12
|
+
#
|
|
13
|
+
# The previous shell form (`rm -rf "$u/.cache/uv" ...; rm -rf /tmp/uv-*`) could
|
|
14
|
+
# delete agent-visible state before scoring: `rm -rf` traverses intermediate
|
|
15
|
+
# symlinks (an agent-planted `~/.cache -> /app` turned the cache delete into
|
|
16
|
+
# `rm -rf /app/uv`), and the `/tmp/uv-*` glob matched a legitimate
|
|
17
|
+
# `/tmp/uv-workspace`. The textual `$WS`/`$u` guard saw none of that. Since
|
|
18
|
+
# ``restore_workspace`` defaults to False, nothing undid the damage.
|
|
19
|
+
#
|
|
20
|
+
# This snippet hardens every deletion candidate:
|
|
21
|
+
# - rejects any candidate with a symlink path component, including the final
|
|
22
|
+
# one, so agent-controlled parents can no longer redirect the delete;
|
|
23
|
+
# - resolves protected roots (workspace + /logs, covering /logs/artifacts and
|
|
24
|
+
# /logs/verifier) via realpath and skips candidates overlapping in either
|
|
25
|
+
# direction, applied uniformly to per-user caches, /tmp globs, and apt;
|
|
26
|
+
# - deletes with shutil.rmtree, which removes child symlinks without following
|
|
27
|
+
# them.
|
|
28
|
+
#
|
|
29
|
+
# argv[1] = active workspace (or /nonexistent). argv[2] = optional filesystem
|
|
30
|
+
# prefix, "" in production; tests pass a temp root so the exact production code
|
|
31
|
+
# runs hermetically against a fake filesystem.
|
|
32
|
+
#
|
|
33
|
+
# Fail-safe by construction: if python3 is unavailable the reclaim simply does
|
|
34
|
+
# not run (the trailing `true` swallows it). Losing best-effort ENOSPC mitigation
|
|
35
|
+
# is strictly better than deleting the wrong state.
|
|
36
|
+
RECLAIM_CACHES_PY = dedent(
|
|
37
|
+
"""
|
|
38
|
+
import glob, os, shutil, sys
|
|
39
|
+
|
|
40
|
+
ws = sys.argv[1]
|
|
41
|
+
px = sys.argv[2] if len(sys.argv) > 2 else ''
|
|
42
|
+
|
|
43
|
+
def linked(path):
|
|
44
|
+
p = px or ''
|
|
45
|
+
for part in path[len(px):].strip('/').split('/'):
|
|
46
|
+
p = p + '/' + part
|
|
47
|
+
if os.path.islink(p):
|
|
48
|
+
return True
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
protected = []
|
|
52
|
+
for root in (ws, px + '/logs'):
|
|
53
|
+
try:
|
|
54
|
+
if root and os.path.exists(root):
|
|
55
|
+
protected.append(os.path.realpath(root))
|
|
56
|
+
except OSError:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
def overlaps(path):
|
|
60
|
+
return any(
|
|
61
|
+
path == r or path.startswith(r + '/') or r.startswith(path + '/')
|
|
62
|
+
for r in protected
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
cands = [
|
|
66
|
+
u + '/.cache/' + name
|
|
67
|
+
for u in [px + '/root'] + sorted(glob.glob(px + '/home/*'))
|
|
68
|
+
for name in ('uv', 'pip', 'uv_build')
|
|
69
|
+
]
|
|
70
|
+
cands += sorted(glob.glob(px + '/tmp/uv-*'))
|
|
71
|
+
cands += sorted(glob.glob(px + '/tmp/.uv-*'))
|
|
72
|
+
cands += sorted(glob.glob(px + '/var/cache/apt/archives/*.deb'))
|
|
73
|
+
for c in cands:
|
|
74
|
+
try:
|
|
75
|
+
if not os.path.lexists(c) or linked(c):
|
|
76
|
+
continue
|
|
77
|
+
# linked() proved no symlink components below the prefix, so
|
|
78
|
+
# realpath only normalizes the prefix itself - consistent with
|
|
79
|
+
# the realpath'd protected roots.
|
|
80
|
+
if overlaps(os.path.realpath(c)):
|
|
81
|
+
continue
|
|
82
|
+
if os.path.isdir(c):
|
|
83
|
+
shutil.rmtree(c, ignore_errors=True)
|
|
84
|
+
else:
|
|
85
|
+
os.remove(c)
|
|
86
|
+
except OSError:
|
|
87
|
+
pass
|
|
88
|
+
"""
|
|
89
|
+
).strip()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_reclaim_caches_cmd(workspace: str | None) -> str:
|
|
93
|
+
"""Build the best-effort cache reclaim shell command."""
|
|
94
|
+
workspace_arg = shlex.quote(workspace) if workspace else "/nonexistent"
|
|
95
|
+
return (
|
|
96
|
+
f"python3 -c {shlex.quote(RECLAIM_CACHES_PY)} {workspace_arg} 2>/dev/null; true"
|
|
97
|
+
)
|
|
@@ -20,6 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from typing import TYPE_CHECKING, Any
|
|
21
21
|
|
|
22
22
|
from benchflow.agents.registry import get_sandbox_home_dirs
|
|
23
|
+
from benchflow.sandbox._cache_reclaim import build_reclaim_caches_cmd
|
|
23
24
|
|
|
24
25
|
if TYPE_CHECKING:
|
|
25
26
|
from benchflow.task import Task
|
|
@@ -877,22 +878,15 @@ async def harden_before_verify(
|
|
|
877
878
|
# installed tools, or task assets — and a failure here never blocks the
|
|
878
879
|
# verifier. Runs on `main` only, consistent with the hardening policy above.
|
|
879
880
|
#
|
|
880
|
-
# Workspace-aware: a task can legitimately use
|
|
881
|
-
#
|
|
882
|
-
#
|
|
883
|
-
#
|
|
884
|
-
#
|
|
885
|
-
|
|
881
|
+
# Workspace-aware AND symlink-safe (#601): a task can legitimately use
|
|
882
|
+
# /root, /home/<user>, or /tmp/uv-* as its workspace, and an agent can
|
|
883
|
+
# plant `~/.cache -> /app` so a naive `rm -rf "$u/.cache/uv"` traverses
|
|
884
|
+
# into workspace/output state. The reclaim command rejects symlinked
|
|
885
|
+
# candidates and realpath-guards every deletion against the workspace and
|
|
886
|
+
# /logs — this matters because restore_workspace defaults to False.
|
|
886
887
|
try:
|
|
887
888
|
await env.exec(
|
|
888
|
-
|
|
889
|
-
"for u in /root /home/*; do "
|
|
890
|
-
' case "$WS" in "$u" | "$u"/*) continue ;; esac; '
|
|
891
|
-
' case "$u" in "$WS"/*) continue ;; esac; '
|
|
892
|
-
' rm -rf "$u/.cache/uv" "$u/.cache/pip" "$u/.cache/uv_build" 2>/dev/null; '
|
|
893
|
-
"done; "
|
|
894
|
-
"rm -rf /tmp/uv-* /tmp/.uv-* /var/cache/apt/archives/*.deb 2>/dev/null; "
|
|
895
|
-
"true",
|
|
889
|
+
build_reclaim_caches_cmd(workspace),
|
|
896
890
|
user="root",
|
|
897
891
|
timeout_sec=30,
|
|
898
892
|
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Digest test fixtures
|
|
2
|
+
|
|
3
|
+
Golden-test fixtures for `bench tasks digest`. The expected digests are
|
|
4
|
+
hardcoded in `tests/test_task_digest.py` — do not modify any file under the
|
|
5
|
+
task directories here without updating those golden values.
|
|
6
|
+
|
|
7
|
+
Filenames are ASCII on purpose: macOS checks out unicode filenames in NFD,
|
|
8
|
+
which would change the hashed path bytes across platforms. Unicode coverage
|
|
9
|
+
lives in file *content* (`beta-task/data/unicode.txt`).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FROM ubuntu:24.04
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
naïve résumé — ünïcödé
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# beta-task
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "1.1"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Not a task — no task.toml. Multi-task digest mode must skip this directory.
|
|
@@ -280,37 +280,36 @@ class TestVerifierDirWipe:
|
|
|
280
280
|
async def test_reclaims_redownloadable_caches_before_verify(self):
|
|
281
281
|
"""Frees uv/pip/apt download caches so the verifier's own deps fit on
|
|
282
282
|
disk-constrained sandboxes (e.g. Daytona's 10GB cap), without ever
|
|
283
|
-
touching the workspace, agent outputs, installed tools, or task assets.
|
|
283
|
+
touching the workspace, agent outputs, installed tools, or task assets.
|
|
284
|
+
Guards PR #669."""
|
|
284
285
|
from benchflow.sandbox.lockdown import harden_before_verify
|
|
285
286
|
|
|
286
287
|
env = _make_env()
|
|
287
288
|
await harden_before_verify(env, _make_task(), sandbox_user=None)
|
|
288
289
|
|
|
289
290
|
reclaim = next(
|
|
290
|
-
(c for c in env.exec.call_args_list if "
|
|
291
|
+
(c for c in env.exec.call_args_list if "/.cache/" in c.args[0]),
|
|
291
292
|
None,
|
|
292
293
|
)
|
|
293
294
|
assert reclaim is not None, "expected a pre-verifier disk reclaim exec"
|
|
294
295
|
cmd = reclaim.args[0]
|
|
295
|
-
# clears only re-downloadable caches ...
|
|
296
|
-
assert "
|
|
297
|
-
# ... and
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
# clears only re-downloadable caches (uv/pip/apt) ...
|
|
297
|
+
assert "uv_build" in cmd and "pip" in cmd and "apt/archives" in cmd
|
|
298
|
+
# ... guarded against symlinks and the workspace/output roots (#601)
|
|
299
|
+
assert "islink" in cmd and "realpath" in cmd and "/logs" in cmd
|
|
300
|
+
# no active workspace -> the guard placeholder is plumbed through
|
|
301
|
+
assert "/nonexistent" in cmd
|
|
300
302
|
# best-effort: swallows errors and never aborts hardening
|
|
301
303
|
assert "2>/dev/null" in cmd and cmd.rstrip().endswith("true")
|
|
302
304
|
# runs as root so it can clear every user's cache
|
|
303
305
|
assert reclaim.kwargs.get("user") == "root"
|
|
304
306
|
|
|
305
307
|
@pytest.mark.asyncio
|
|
306
|
-
@pytest.mark.parametrize("workspace", ["/root", "/home/agent"])
|
|
307
|
-
async def
|
|
308
|
-
"""
|
|
309
|
-
|
|
310
|
-
must see untouched. restore_workspace defaults False, so nothing would
|
|
311
|
-
restore a stray deletion. Guards the workspace-fidelity review blocker."""
|
|
308
|
+
@pytest.mark.parametrize("workspace", ["/root", "/home/agent", "/app"])
|
|
309
|
+
async def test_reclaim_plumbs_workspace_into_guard(self, workspace):
|
|
310
|
+
"""The active workspace is passed to the reclaim snippet as argv[1] so
|
|
311
|
+
the realpath overlap guard can protect it. Guards PR #669."""
|
|
312
312
|
import shlex
|
|
313
|
-
import subprocess
|
|
314
313
|
|
|
315
314
|
from benchflow.sandbox.lockdown import harden_before_verify
|
|
316
315
|
|
|
@@ -320,47 +319,104 @@ class TestVerifierDirWipe:
|
|
|
320
319
|
)
|
|
321
320
|
|
|
322
321
|
reclaim = next(
|
|
323
|
-
(c for c in env.exec.call_args_list if "
|
|
322
|
+
(c for c in env.exec.call_args_list if "/.cache/" in c.args[0]), None
|
|
324
323
|
)
|
|
325
324
|
assert reclaim is not None
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
assert 'case "$WS" in "$u" | "$u"/*) continue' in cmd
|
|
331
|
-
assert 'case "$u" in "$WS"/*) continue' in cmd
|
|
332
|
-
|
|
333
|
-
# behaviorally: the guard skips the workspace dir itself (hermetic check)
|
|
334
|
-
decide = subprocess.run(
|
|
335
|
-
[
|
|
336
|
-
"sh",
|
|
337
|
-
"-c",
|
|
338
|
-
f"WS={shlex.quote(workspace)}; u={shlex.quote(workspace)}; "
|
|
339
|
-
'case "$WS" in "$u" | "$u"/*) echo skip; exit ;; esac; '
|
|
340
|
-
'case "$u" in "$WS"/*) echo skip; exit ;; esac; echo clear',
|
|
341
|
-
],
|
|
342
|
-
capture_output=True,
|
|
343
|
-
text=True,
|
|
344
|
-
).stdout.strip()
|
|
345
|
-
assert decide == "skip", (
|
|
346
|
-
f"workspace {workspace} should be skipped, got {decide}"
|
|
325
|
+
assert (
|
|
326
|
+
reclaim.args[0]
|
|
327
|
+
.rstrip()
|
|
328
|
+
.endswith(f"{shlex.quote(workspace)} 2>/dev/null; true")
|
|
347
329
|
)
|
|
348
330
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
cache reclaim fully active."""
|
|
353
|
-
from benchflow.sandbox.lockdown import harden_before_verify
|
|
331
|
+
# ── Behavior tests: run the REAL reclaim snippet against a temp root ──────
|
|
332
|
+
# (#601 regression suite — the old string-shape tests passed while the
|
|
333
|
+
# shell command deleted agent state through symlinks and /tmp globs.)
|
|
354
334
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
335
|
+
@staticmethod
|
|
336
|
+
def _run_reclaim(workspace: str, prefix) -> None:
|
|
337
|
+
"""Execute the production reclaim snippet hermetically under
|
|
338
|
+
``prefix`` - the same code, same interpreter contract as the sandbox."""
|
|
339
|
+
import subprocess
|
|
340
|
+
import sys
|
|
341
|
+
|
|
342
|
+
from benchflow.sandbox._cache_reclaim import RECLAIM_CACHES_PY
|
|
343
|
+
|
|
344
|
+
result = subprocess.run(
|
|
345
|
+
[sys.executable, "-c", RECLAIM_CACHES_PY, workspace, str(prefix)],
|
|
346
|
+
capture_output=True,
|
|
347
|
+
text=True,
|
|
348
|
+
timeout=60,
|
|
349
|
+
)
|
|
350
|
+
assert result.returncode == 0, result.stderr
|
|
351
|
+
|
|
352
|
+
def test_reclaim_does_not_traverse_cache_symlink_into_workspace(self, tmp_path):
|
|
353
|
+
"""Guards PR #669 for issue #601 (repro 1): an agent-planted
|
|
354
|
+
``~/.cache -> /app`` must not turn the cache delete into
|
|
355
|
+
``rm -rf /app/uv`` — the old shell form did exactly that."""
|
|
356
|
+
(tmp_path / "app" / "uv").mkdir(parents=True)
|
|
357
|
+
(tmp_path / "app" / "uv" / "state.txt").write_text("agent state")
|
|
358
|
+
(tmp_path / "root").mkdir()
|
|
359
|
+
(tmp_path / "root" / ".cache").symlink_to(tmp_path / "app")
|
|
360
|
+
|
|
361
|
+
self._run_reclaim(str(tmp_path / "app"), tmp_path)
|
|
362
|
+
|
|
363
|
+
assert (tmp_path / "app" / "uv" / "state.txt").exists()
|
|
364
|
+
|
|
365
|
+
def test_reclaim_does_not_traverse_symlink_into_artifacts(self, tmp_path):
|
|
366
|
+
"""Guards PR #669 for issue #601 (repro 2): ``.cache -> /logs/artifacts``
|
|
367
|
+
must not delete artifact state — /logs is a protected root even when it
|
|
368
|
+
is not the workspace."""
|
|
369
|
+
(tmp_path / "logs" / "artifacts" / "uv").mkdir(parents=True)
|
|
370
|
+
(tmp_path / "logs" / "artifacts" / "uv" / "state.txt").write_text("x")
|
|
371
|
+
(tmp_path / "home" / "agent").mkdir(parents=True)
|
|
372
|
+
(tmp_path / "home" / "agent" / ".cache").symlink_to(
|
|
373
|
+
tmp_path / "logs" / "artifacts"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
self._run_reclaim("/nonexistent", tmp_path)
|
|
377
|
+
|
|
378
|
+
assert (tmp_path / "logs" / "artifacts" / "uv" / "state.txt").exists()
|
|
379
|
+
|
|
380
|
+
def test_reclaim_spares_tmp_workspace_matching_glob(self, tmp_path):
|
|
381
|
+
"""Guards PR #669 for issue #601 (repro 3): a legitimate
|
|
382
|
+
``/tmp/uv-workspace`` workspace must survive the ``/tmp/uv-*`` glob —
|
|
383
|
+
the old form deleted it unconditionally."""
|
|
384
|
+
ws = tmp_path / "tmp" / "uv-workspace"
|
|
385
|
+
ws.mkdir(parents=True)
|
|
386
|
+
(ws / "state.txt").write_text("answer")
|
|
387
|
+
|
|
388
|
+
self._run_reclaim(str(ws), tmp_path)
|
|
389
|
+
|
|
390
|
+
assert (ws / "state.txt").exists()
|
|
391
|
+
|
|
392
|
+
def test_reclaim_still_clears_real_caches_outside_workspace(self, tmp_path):
|
|
393
|
+
"""Guards PR #669 for issue #601 (repro 4): real, non-symlinked caches
|
|
394
|
+
outside the workspace are still reclaimed, so the ENOSPC mitigation
|
|
395
|
+
the reclaim exists for stays effective."""
|
|
396
|
+
(tmp_path / "root" / ".cache" / "uv" / "blob").mkdir(parents=True)
|
|
397
|
+
(tmp_path / "home" / "u1" / ".cache" / "pip").mkdir(parents=True)
|
|
398
|
+
(tmp_path / "tmp" / "uv-build123").mkdir(parents=True)
|
|
399
|
+
(tmp_path / "var" / "cache" / "apt" / "archives").mkdir(parents=True)
|
|
400
|
+
(tmp_path / "var" / "cache" / "apt" / "archives" / "x.deb").write_text("d")
|
|
401
|
+
|
|
402
|
+
self._run_reclaim(str(tmp_path / "app"), tmp_path)
|
|
403
|
+
|
|
404
|
+
assert not (tmp_path / "root" / ".cache" / "uv").exists()
|
|
405
|
+
assert not (tmp_path / "home" / "u1" / ".cache" / "pip").exists()
|
|
406
|
+
assert not (tmp_path / "tmp" / "uv-build123").exists()
|
|
407
|
+
assert not (tmp_path / "var" / "cache" / "apt" / "archives" / "x.deb").exists()
|
|
408
|
+
|
|
409
|
+
def test_reclaim_skips_caches_inside_the_workspace(self, tmp_path):
|
|
410
|
+
"""When a task uses /root as its workspace, "$ws/.cache" is workspace
|
|
411
|
+
state the verifier must see untouched (the pre-#601 guarantee, now
|
|
412
|
+
enforced by realpath overlap instead of string comparison). Guards PR #669."""
|
|
413
|
+
cache = tmp_path / "root" / ".cache" / "uv"
|
|
414
|
+
cache.mkdir(parents=True)
|
|
415
|
+
(cache / "state.txt").write_text("workspace state")
|
|
416
|
+
|
|
417
|
+
self._run_reclaim(str(tmp_path / "root"), tmp_path)
|
|
418
|
+
|
|
419
|
+
assert (cache / "state.txt").exists()
|
|
364
420
|
|
|
365
421
|
|
|
366
422
|
# ── TestBuildConfigSnapshot ───────────────────────────────────────────────────
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Tests for `bench tasks digest` and task_digest().
|
|
2
|
+
|
|
3
|
+
The digest pins a task's content independent of git and must byte-match the
|
|
4
|
+
reference digests published in the skillsbench dataset registry
|
|
5
|
+
(registry.json / docs/dataset-versioning.md, skillsbench PR #922):
|
|
6
|
+
|
|
7
|
+
task_digest = sha256( for each regular file under the task directory,
|
|
8
|
+
sorted by POSIX relative path:
|
|
9
|
+
update( path_utf8 + b"\\x00"
|
|
10
|
+
+ sha256(file_bytes).digest() ) )
|
|
11
|
+
|
|
12
|
+
Symlinks and file modes are excluded; the hex digest is prefixed "sha256:".
|
|
13
|
+
|
|
14
|
+
The implementation was cross-validated against all 87 published digests of
|
|
15
|
+
the skillsbench v1.1 registry entry (git tag v1.1, commit 14f33967) — e.g.
|
|
16
|
+
tasks/3d-scan-calc reproduces
|
|
17
|
+
sha256:587a79f13a31098b5f7b4d1f99582ac6d4184e2cca1f02e2679fc98b81861220.
|
|
18
|
+
The golden values below freeze the same algorithm onto an offline fixture.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import hashlib
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
import pytest
|
|
25
|
+
from typer.testing import CliRunner
|
|
26
|
+
|
|
27
|
+
from benchflow._utils.task_authoring import task_digest
|
|
28
|
+
from benchflow.cli.main import app
|
|
29
|
+
|
|
30
|
+
FIXTURES = Path(__file__).parent / "fixtures" / "digest"
|
|
31
|
+
|
|
32
|
+
# Frozen against the checked-in fixture tasks. If these fail without a
|
|
33
|
+
# deliberate fixture change, the algorithm drifted from the published
|
|
34
|
+
# skillsbench registry digests — fix the code, not the constants.
|
|
35
|
+
ALPHA_DIGEST = "sha256:6a0c023a27c1dd1b68b16ed23a41cdd9b3ea3401599bc6abbb95ed77d41e08f9"
|
|
36
|
+
BETA_DIGEST = "sha256:8f5a4f868cef8c5c50b700183d0b9bd7405d92245eed79a0d50494b7daa0de16"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _spec_digest(file_entries: list[tuple[str, bytes]]) -> str:
|
|
40
|
+
"""The registry construction, written out literally, in the given order."""
|
|
41
|
+
h = hashlib.sha256()
|
|
42
|
+
for rel_posix, content in file_entries:
|
|
43
|
+
h.update(rel_posix.encode("utf-8"))
|
|
44
|
+
h.update(b"\x00")
|
|
45
|
+
h.update(hashlib.sha256(content).digest())
|
|
46
|
+
return f"sha256:{h.hexdigest()}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TestTaskDigest:
|
|
50
|
+
def test_golden_fixture_digests(self):
|
|
51
|
+
assert task_digest(FIXTURES / "alpha-task") == ALPHA_DIGEST
|
|
52
|
+
assert task_digest(FIXTURES / "beta-task") == BETA_DIGEST
|
|
53
|
+
|
|
54
|
+
def test_matches_spec_construction(self, tmp_path):
|
|
55
|
+
task = tmp_path / "task"
|
|
56
|
+
(task / "environment").mkdir(parents=True)
|
|
57
|
+
(task / "task.toml").write_bytes(b'version = "1.1"\n')
|
|
58
|
+
(task / "instruction.md").write_bytes(b"do the thing\n")
|
|
59
|
+
(task / "environment" / "Dockerfile").write_bytes(b"FROM scratch\n")
|
|
60
|
+
expected = _spec_digest(
|
|
61
|
+
[
|
|
62
|
+
("environment/Dockerfile", b"FROM scratch\n"),
|
|
63
|
+
("instruction.md", b"do the thing\n"),
|
|
64
|
+
("task.toml", b'version = "1.1"\n'),
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
assert task_digest(task) == expected
|
|
68
|
+
|
|
69
|
+
def test_sorts_by_posix_path_string_not_path_parts(self, tmp_path):
|
|
70
|
+
"""'a.b/c' sorts before 'a/c' as a string ('.' < '/'), after it as
|
|
71
|
+
Path parts — the registry orders by the POSIX path string."""
|
|
72
|
+
task = tmp_path / "task"
|
|
73
|
+
(task / "a").mkdir(parents=True)
|
|
74
|
+
(task / "a.b").mkdir()
|
|
75
|
+
(task / "a" / "c").write_bytes(b"slash")
|
|
76
|
+
(task / "a.b" / "c").write_bytes(b"dot")
|
|
77
|
+
string_order = _spec_digest([("a.b/c", b"dot"), ("a/c", b"slash")])
|
|
78
|
+
parts_order = _spec_digest([("a/c", b"slash"), ("a.b/c", b"dot")])
|
|
79
|
+
assert string_order != parts_order
|
|
80
|
+
assert task_digest(task) == string_order
|
|
81
|
+
|
|
82
|
+
def test_symlinks_excluded(self, tmp_path):
|
|
83
|
+
task = tmp_path / "task"
|
|
84
|
+
task.mkdir()
|
|
85
|
+
(task / "task.toml").write_bytes(b'version = "1.1"\n')
|
|
86
|
+
before = task_digest(task)
|
|
87
|
+
|
|
88
|
+
outside = tmp_path / "outside"
|
|
89
|
+
outside.mkdir()
|
|
90
|
+
(outside / "payload.txt").write_bytes(b"must not be hashed")
|
|
91
|
+
(task / "file-link.toml").symlink_to(task / "task.toml")
|
|
92
|
+
(task / "dir-link").symlink_to(outside, target_is_directory=True)
|
|
93
|
+
(task / "broken-link").symlink_to(task / "does-not-exist")
|
|
94
|
+
|
|
95
|
+
assert task_digest(task) == before
|
|
96
|
+
|
|
97
|
+
def test_file_modes_excluded(self, tmp_path):
|
|
98
|
+
task = tmp_path / "task"
|
|
99
|
+
task.mkdir()
|
|
100
|
+
script = task / "solve.sh"
|
|
101
|
+
script.write_bytes(b"#!/bin/bash\n")
|
|
102
|
+
before = task_digest(task)
|
|
103
|
+
script.chmod(0o755)
|
|
104
|
+
assert task_digest(task) == before
|
|
105
|
+
|
|
106
|
+
def test_empty_directories_excluded(self, tmp_path):
|
|
107
|
+
task = tmp_path / "task"
|
|
108
|
+
task.mkdir()
|
|
109
|
+
(task / "task.toml").write_bytes(b'version = "1.1"\n')
|
|
110
|
+
before = task_digest(task)
|
|
111
|
+
(task / "empty" / "nested").mkdir(parents=True)
|
|
112
|
+
assert task_digest(task) == before
|
|
113
|
+
|
|
114
|
+
def test_sensitive_to_content_and_path(self, tmp_path):
|
|
115
|
+
task = tmp_path / "task"
|
|
116
|
+
task.mkdir()
|
|
117
|
+
f = task / "task.toml"
|
|
118
|
+
f.write_bytes(b'version = "1.1"\n')
|
|
119
|
+
original = task_digest(task)
|
|
120
|
+
|
|
121
|
+
f.write_bytes(b'version = "1.2"\n')
|
|
122
|
+
changed_content = task_digest(task)
|
|
123
|
+
assert changed_content != original
|
|
124
|
+
|
|
125
|
+
f.write_bytes(b'version = "1.1"\n')
|
|
126
|
+
f.rename(task / "renamed.toml")
|
|
127
|
+
assert task_digest(task) not in (original, changed_content)
|
|
128
|
+
|
|
129
|
+
def test_not_a_directory_raises(self, tmp_path):
|
|
130
|
+
with pytest.raises(NotADirectoryError):
|
|
131
|
+
task_digest(tmp_path / "missing")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class TestTasksDigestCli:
|
|
135
|
+
def test_single_task_prints_digest(self):
|
|
136
|
+
result = CliRunner().invoke(
|
|
137
|
+
app, ["tasks", "digest", str(FIXTURES / "alpha-task")]
|
|
138
|
+
)
|
|
139
|
+
assert result.exit_code == 0
|
|
140
|
+
assert result.output == f"{ALPHA_DIGEST}\n"
|
|
141
|
+
|
|
142
|
+
def test_directory_of_tasks_prints_name_digest_lines(self):
|
|
143
|
+
"""One '<name> <digest>' line per task, sorted; non-task entries
|
|
144
|
+
(not-a-task/, README.md) are skipped."""
|
|
145
|
+
result = CliRunner().invoke(app, ["tasks", "digest", str(FIXTURES)])
|
|
146
|
+
assert result.exit_code == 0
|
|
147
|
+
assert result.output.splitlines() == [
|
|
148
|
+
f"alpha-task {ALPHA_DIGEST}",
|
|
149
|
+
f"beta-task {BETA_DIGEST}",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
def test_missing_path_fails(self, tmp_path):
|
|
153
|
+
result = CliRunner().invoke(app, ["tasks", "digest", str(tmp_path / "nope")])
|
|
154
|
+
assert result.exit_code == 1
|
|
155
|
+
|
|
156
|
+
def test_directory_without_tasks_fails(self):
|
|
157
|
+
result = CliRunner().invoke(
|
|
158
|
+
app, ["tasks", "digest", str(FIXTURES / "not-a-task")]
|
|
159
|
+
)
|
|
160
|
+
assert result.exit_code == 1
|
|
161
|
+
assert "No tasks under" in result.output
|
|
File without changes
|
|
File without changes
|
|
File without changes
|