#!/usr/bin/env python3 # /// script # requires-python = ">=3.10" # dependencies = [] # /// """run_tests.py — Test harness for codeact plugin. Runs prompts through copilot CLI, captures JSONL output, extracts metrics, and compares baseline vs codeact arms. Usage: # Full end-to-end run (creates temp workspace, runs all tests, cleans up) python3 run_tests.py all # Sub-runs (caller supplies workspace + plugin dir) python3 run_tests.py functional --prompts prompts/functional.json \\ --workspace /tmp/test --plugin-dir ./plugins/codeact python3 run_tests.py perf --prompts prompts/perf.json \\ --workspace /tmp/test --plugin-dir ./plugins/codeact \\ --min-token-reduction 40 """ from __future__ import annotations import argparse import json import os import re import subprocess import sys import time from dataclasses import dataclass, field from pathlib import Path from typing import Any @dataclass class RunMetrics: """Metrics extracted from a copilot CLI JSONL run.""" prompt_id: str = "" arm: str = "" output_tokens: int = 0 input_tokens: int = 0 api_turns: int = 0 premium_requests: int = 0 api_duration_ms: int = 0 session_duration_ms: int = 0 tool_calls: list[dict[str, Any]] = field(default_factory=list) tool_names: list[str] = field(default_factory=list) assistant_text: str = "" codeact_invoked: bool = False codeact_evidence: list[str] = field(default_factory=list) success: bool = False raw_events: list[dict[str, Any]] = field(default_factory=list) raw_stdout: str = "" raw_stderr: str = "" # Context bloat: total bytes of tool results returned to the conversation. # Each turn re-sends prior results as context, so this directly correlates # with input token cost. Lower = less context replay per turn. tool_result_bytes: int = 0 # Module-level switch flipped by --verbose. VERBOSE = False # Default cap for evidence / tool-call printout. Long enough to show the # command up to the start of inline `--code` payloads, short enough to keep # normal-mode output scannable. --verbose disables truncation entirely. EVIDENCE_TRUNCATE = 300 def _truncate(s: str, limit: int = EVIDENCE_TRUNCATE) -> str: if VERBOSE or len(s) <= limit: return s return s[: limit - 3] + "..." def run_copilot( prompt: str, workspace: str, plugin_dir: str | None = None, no_custom_instructions: bool = False, timeout: int = 180, log_label: str | None = None, agent: str | None = None, disable_mcp_servers: list[str] | None = None, allow_tools: list[str] | None = None, deny_tools: list[str] | None = None, ) -> tuple[list[dict[str, Any]], str, str]: """Run a prompt through copilot CLI. Returns (parsed JSONL events, stdout, stderr). Stdout + stderr are also written to ``/.copilot-logs/