ergebnis/agent-detector
Detect whether your PHP code is running under an AI agent. This Composer package checks environment variables for 30+ agents like Claude Code, Codex, Cursor, and Copilot, exposing a simple Detector class with one isAgentPresent() method.
Architecture fit
Detector class exposing isAgentPresent(array $env): bool. It performs a pure lookup over an environment array — no I/O, no framework coupling, no service provider, no config surface.new Detector()), bound as a singleton, or hidden behind our own AgentContext abstraction without friction.putenv() juggling. This is the single most valuable property for us — we can test the behaviour matrix (local/CI/staging/prod) deterministically.Integration feasibility
composer require ergebnis/agent-detector and a single call site. Realistic effort: well under a day for the wrapper plus tests; the remaining work is entirely product/policy decisions, not engineering.getenv(), while Laravel loads .env through vlucas/phpdotenv. Values inherited from the shell (which is how agents set these variables) generally appear in both getenv() and $_SERVER, but .env-defined values do not. Decision: pass an explicit array sourced from a single, documented location (e.g. $_SERVER or Env::getRepository()) so behaviour is reproducible across CLI, queue workers, and FPM.illuminate/* constraints → no conflict with Laravel 10/11/12 upgrades and no Composer resolution risk.Technical risk
AMP_CURRENT_THREAD_ID as an agent signal, but that variable is associated with the Amp/Revolt async stack — a mainstream PHP runtime component. Any Laravel service using amphp/fibers may be misclassified as "agent present". Likewise REPL_ID is set for all Replit-hosted workloads, including production, and COPILOT_* variables can leak into CI or container images via misconfigured secrets. Guards that disable notifications, seeding, or telemetry could silently activate in production.Key questions
COPILOT_*, REPL_ID, AMP_CURRENT_THREAD_ID, and CURSOR_*? Who owns that invariant?Stack fit
ext-* requirements, no PSR coupling, Composer 2 only.Ergebnis\AgentDetector\Detector as a singleton in AppServiceProvider, wrap it in an App\Support\AgentContext service that caches the boolean for the process lifetime and exposes named helpers (shouldSuppressNotifications(), shouldUseMachineOutput()), and optionally a thin facade for ergonomics. Application code must never call new Detector() directly — the wrapper is our upgrade and exit path.config/agent.php with per-environment enablement, an allowlist of which signals we trust, and a kill switch. Default the kill switch off in production.Migration path
agent.present) and run it for a sprint to measure how often the detector fires in each environment. This converts the false-positive risk from speculation into data before any behaviour depends on it.testbench helpers. Zero production blast radius.Compatibility
^1.2 (range allows 1.x additions without pulling breaking changes; the changelog shows a clean additive 1.x history: 1.1.0 Cursor var, 1.1.1 PHP 8.6, 1.2.0 Copilot/Pi vars).Sequencing
putenv).AMP_CURRENT_THREAD_ID and REPL_ID unless the use case demands them).Maintenance
Support
Scaling
Failure modes
COPILOT_*/CURSOR_* secrets in an image, REPL_ID on a Replit-hosted deployment, or AMP_CURRENT_THREAD_ID in an amphp-based worker could disable notifications, mail, or seeding. Severity depends entirely on what we gate; this is why gating destructive/observable behaviour is discouraged.getenv() vs Laravel's .env repository vs $_SERVER (itself dependent on variables_order) can produce different answers in web vs CLI vs queue contexts. Resolve by sourcing from one documented place and asserting consistency in tests.composer install in CI.Ramp-up
How can I help you explore Laravel packages today?