Agent Detector
@doubleagent-so/agent-detector
Classify a browser session as human, bot or AI agent, entirely inside the page.
Website · Docs · npm · Changelog · Report an issue · MIT license
The detection engine behind Double Agent. It classifies a browser session as human, bot or AI agent (Claude in Chrome, ChatGPT Atlas, Comet, Browser Use, Playwright, headless Chrome…) entirely inside the page. No dependencies, no network requests: you decide what to do with the verdict.
Docs · Getting started · How it works · API · Extending · Signals · Catalog · Contributing
Use it directly when you want the raw engine. For a drop-in script with analytics and ads integrations and plugins,
use @doubleagent-so/js.
Install
npm install @doubleagent-so/agent-detector
Quick start
import { createEngine } from '@doubleagent-so/agent-detector';
const engine = createEngine(window, {
profile: 'ecommerce', // optional; detected from the page when omitted
onVerdict: (v) => console.log(v.class, v.probability),
});
const v = await engine.ready; // after the async probes (WebGL, workers, client hints)
v.class; // 'human' | 'bot' | 'agent'
v.probability; // { human: 0.03, bot: 0.05, agent: 0.92 }
v.agent; // { family: 'claude', id: 'anthropic.claude-in-chrome', verified: false, method: 'marker.claude_active' }
v.recommendation; // 'allow' | 'tag' | 'challenge' | 'step_up' | 'rate_limit' | 'deny'
v.reasons; // [{ code: 'marker.claude_active', weight: 6.2 }, …]
engine.score('checkout'); // re-score for a specific action
engine.stop(); // remove listeners and timers
The engine keeps observing input (pointer, keys, scroll, timing) and re-scores every 2 s. onVerdict fires only when
the class, recommendation, agent, stage or probability band changes.
Server-side helpers work without a DOM:
import { matchUserAgent, CATALOG } from '@doubleagent-so/agent-detector';
matchUserAgent('Mozilla/5.0 … ChatGPT-User/1.0; +https://openai.com/bot')?.entry.id; // 'openai.chatgpt-user'
How it works
Probes and input events become signals, each a log-likelihood ratio in a capped group. Fusion adds them to
per-profile priors and softmaxes over human, bot and agent. A policy maps the result and the action to a
recommendation. Every weight lives in one versioned object, DEFAULT_SIGNATURES. Details:
How it works.
engine.payload() is a compact JSON summary you can send to your own backend. Re-score it there with evidence the
client cannot forge: examples/server.ts.
Try it
git clone https://github.com/doubleagent-so/agent-detector && cd agent-detector && npm ci
npm run example # live verdict at http://127.0.0.1:8123 — try a real browser, a headless one and an agent
npm run bench # confusion matrix on labelled synthetic sessions
Contributing
Detection gets better with more eyes. Good places to start:
- Report a misread session: a real browser labelled automated, or an agent that passed as human. Open a misclassification.
- Add an agent or bot with its operator's documentation. Request one or add it yourself.
- Harder test sessions. The synthetic traces in
test/traces.tsare easy today; realistic ones makenpm run benchmeaningful. - Issues labelled good first issue and help wanted.
Read CONTRIBUTING.md first: tests first, false positives are the worst bug, weights need evidence. Report evasions privately (SECURITY.md), not in a public issue.
Support
- Questions and bugs: open an issue.
- Private account or billing questions: [email protected]. Never post secret keys or session tokens in a public issue.
- Security problems: report them privately as described in SECURITY.md.
Maintained by Double Agent · Contributing · Code of conduct · Security · MIT license