cyborg-hunter 0.3.0

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.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # cyborg-hunter
2
+
3
+ Detects AI-tool use during browser-based behavioral experiments. Captures paste, copy, drag, tab-away, mouse trajectories, browser sidebar openings, and other signals that compromise data quality on Prolific / MTurk / classroom studies. Produces a triage report ranking participants by suspiciousness.
4
+
5
+ ### Example: 100-participant Prolific pilot
6
+
7
+ Top of the triage list (excerpt — full ranked CSV in the report):
8
+
9
+ | Rank | Participant | Score | Hard | Reason |
10
+ |------|-------------|-------|------|--------|
11
+ | 1 | 67c1d6d98c8787be36609212 | 4078 | no | 1 tab-away 3–10s; 2 flickers <3s; fast typing on 9 trials; **1015 synthetic insertions** |
12
+ | 2 | 69bc21dd22f17b2337957511 | 179 | **YES** | 21 paste events; 3 copy events; 9 tab-aways ≥10s; 3 sidebar events; 21 layout shifts |
13
+ | 3 | 69b9f3184e0896ef41b99aa8 | 152 | no | 38 synthetic insertions |
14
+ | 4 | 5d350282cec7150015d16494 | 131 | **YES** | 5 paste events; 10 copy events; 22 tab-aways ≥10s; 5 tab-aways 3–10s; 15 flickers <3s |
15
+ | 5 | 696a9773a84c5f9d4a930874 | 128 | **YES** | 15 paste events; 23 tab-aways ≥10s; 11 tab-aways 3–10s; 9 flickers <3s |
16
+ | 6 | 69b594dccc64df571f092147 | 123 | **YES** | 12 paste events; 16 tab-aways ≥10s; 4 tab-aways 3–10s |
17
+ | 7 | 69cd5149cf6fc07126897938 | 120 | **YES** | 15 paste events; 1 copy events; 19 tab-aways ≥10s; 16 flickers <3s |
18
+ | 8 | 6314bc49055e150c92f4736e | 112 | **YES** | 12 paste events; 14 tab-aways ≥10s; 46 tab-aways 3–10s; 3 flickers <3s |
19
+
20
+ Of 100 participants, 6 hard-flagged (count thresholds crossed) and 12 soft-flagged (weighted score over threshold). Per-participant tab-away timeline below — this is rank 4, with 42 tab-away events across 15 trials. Reviewers can scan the timeline visually instead of staring at a 30-column CSV:
21
+
22
+ ![Tab-away timeline showing 42 tab-away events across 15 trials. Most trials have multiple long red bars (≥10s tab-aways), with some shorter orange (3-10s) and grey flicker (<3s) events.](docs/images/example-tab-timeline.png)
23
+
24
+ Two pieces:
25
+
26
+ - A small JS library you load in your experiment that records signals as participants work.
27
+ - A CLI that reads the resulting data files and renders an HTML report.
28
+
29
+ ## Install
30
+
31
+ For analysis (CLI), once published to npm:
32
+
33
+ ```bash
34
+ npm install -g cyborg-hunter # not yet published — clone the repo for now
35
+ ```
36
+
37
+ For the experiment page (library), copy `dist/cyborg-hunter.min.js` and `dist/jspsych-cyborg-hunter.js` from this repo into your project. The library is currently distributed as files, not via a CDN.
38
+
39
+ ## Plug into a jsPsych experiment
40
+
41
+ ```javascript
42
+ const jsPsych = initJsPsych({
43
+ extensions: [
44
+ { type: jsPsychCyborgHunter, params: { participantId: subject.id, preset: 'standard' } }
45
+ ],
46
+ on_finish: function () {
47
+ // REQUIRED: jsPsych 7 has no on_finish_experiment extension hook, so the
48
+ // session-level signals must be flushed manually before saving data.
49
+ jsPsych.extensions['cyborg-hunter'].finalize();
50
+ jsPsych.data.get().localSave('csv', 'data.csv');
51
+ }
52
+ });
53
+
54
+ // Opt every trial in to monitoring (single forEach beats editing N trials):
55
+ timeline.forEach(t => {
56
+ t.extensions = (t.extensions || []).concat([{ type: jsPsychCyborgHunter }]);
57
+ });
58
+
59
+ jsPsych.run(timeline);
60
+ ```
61
+
62
+ Full setup walk-through, including standalone (non-jsPsych) usage and per-trial parameters: [`docs/using-cyborg-hunter.md`](docs/using-cyborg-hunter.md).
63
+
64
+ ## Generate a report
65
+
66
+ After data collection, in the directory holding your data files:
67
+
68
+ ```bash
69
+ cyborg-hunter init # writes cyborg-hunter.config.json
70
+ # edit config: dataDir, filePattern, participantIdField
71
+ cyborg-hunter report # writes ./cyborg-hunter-report/
72
+ open cyborg-hunter-report/index.html
73
+ ```
74
+
75
+ The report contains:
76
+
77
+ - `summary.csv` — one row per participant, every signal as a column
78
+ - `triage.md` — ranked list with one-line "why flagged" per participant
79
+ - `event-log.csv` — chronological copy/paste/drop/tab-away events
80
+ - `images/` — per-participant mouse trajectories, tab timelines, typing-speed profiles
81
+ - `index.html` — landing page that ties it together
82
+
83
+ ## What it detects
84
+
85
+ | Signal | How | Class |
86
+ |---|---|---|
87
+ | Paste | Clipboard `paste` events | Hard (count threshold) |
88
+ | Drag-and-drop | `drop` events on inputs | Hard (count threshold) |
89
+ | Copy | Clipboard `copy` events | Soft (weighted) |
90
+ | Tab-away | `visibilitychange` + `blur`/`focus` | Soft (weighted) |
91
+ | Browser sidebar | `innerWidth` delta + layout compression | Soft (weighted) |
92
+ | Suspicious typing speed | chars/sec exceeding the preset's threshold | Soft (weighted) |
93
+ | Synthetic insertion | text appearing without keystrokes | Soft (weighted) |
94
+ | Foreign input | typing landing outside the experiment container | Soft (weighted) |
95
+ | Idle gaps | input inactivity (potential context-switch out) | Soft (weighted) |
96
+ | AI-extension content scripts | DOM scan for known extension selectors | Soft (weighted) |
97
+ | Mouse trajectories | 20Hz polling + path-efficiency / direction-change metrics | Diagnostic |
98
+ | Window/screen geometry | polled + resize-event capture, with zoom inference | Diagnostic |
99
+
100
+ Three presets — `permissive` (pilot), `standard` (default), `strict` (high-stakes). Per-signal thresholds documented in [`docs/signals-reference.md`](docs/signals-reference.md).
101
+
102
+ ## What it doesn't detect
103
+
104
+ - **Native browser AI sidebars.** Chrome's built-in Gemini panel and similar are not browser extensions and leave no extension content scripts. The `innerWidth_delta` heuristic still catches them as a generic sidebar event, but the named-extension column will be empty.
105
+ - **Screen-share / second device.** A participant reading questions on screen 1 and querying ChatGPT on a phone produces no detectable trace from the browser.
106
+ - **AI text edited and retyped.** A determined participant who copies AI output, retypes it character-by-character, and never tab-switches will look clean. The mouse-trajectory and typing-rhythm signals make this much harder than it sounds, but the tool isn't a polygraph.
107
+
108
+ These are stated up front so reviewers know what flagging really represents.
109
+
110
+ ## How it works
111
+
112
+ The library hooks browser events that are universally available (Clipboard API, Visibility API, MouseEvent, Performance API, MutationObserver) and tracks two scopes in parallel:
113
+
114
+ - **Session-scoped monitors** start at experiment load and run continuously: tab-away, sidebar/extension detection, layout shifts, window position polling.
115
+ - **Trial-scoped trackers** bracket each trial: mouse path, typing speed, paste/copy/drop within the trial's response window, idle gaps.
116
+
117
+ At the end of each trial, an "integrity" object is attached to that trial's data. At the end of the experiment, session-level rollups are attached to the last trial. The CLI ingests both standard jsPsych CSV output and project-specific JSON formats (`docs/configuration.md` describes the config-file fields).
118
+
119
+ ## Documentation
120
+
121
+ - [`docs/using-cyborg-hunter.md`](docs/using-cyborg-hunter.md) — full integration guide (jsPsych and standalone)
122
+ - [`docs/signals-reference.md`](docs/signals-reference.md) — every signal with thresholds per preset
123
+ - [`docs/configuration.md`](docs/configuration.md) — config file fields and CLI flags
124
+ - [`docs/cli-reference.md`](docs/cli-reference.md) — commands and output structure
125
+ - [`docs/plans/2026-04-03-integrity-library-and-calibration-design.md`](docs/plans/2026-04-03-integrity-library-and-calibration-design.md) — design rationale (two-tier scoring, signal taxonomy, preset choice)
126
+
127
+ ## Citation
128
+
129
+ If you use cyborg-hunter in academic work, please cite:
130
+
131
+ ```bibtex
132
+ @software{konuk2026cyborghunter,
133
+ author = {Konuk, Can},
134
+ title = {cyborg-hunter: integrity monitoring for online behavioral experiments},
135
+ year = {2026},
136
+ url = {https://github.com/konukcan/cyborg-hunter},
137
+ version = {0.3.0}
138
+ }
139
+ ```
140
+
141
+ GitHub also renders a "Cite this repository" button (sidebar of the repo page) from the CITATION.cff file in the root.
142
+
143
+ ## License
144
+
145
+ MIT
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ // CLI entry point for Cyborg Hunter.
3
+ // Usage:
4
+ // npx cyborg-hunter init # generate starter config
5
+ // npx cyborg-hunter report [--config path] ... # generate report
6
+
7
+ import { run } from '../src/cli/report.js';
8
+ import { runInit } from '../src/cli/init.js';
9
+
10
+ const args = process.argv.slice(2);
11
+ const subcommand = args[0];
12
+
13
+ if (subcommand === 'init') {
14
+ runInit().catch(err => { console.error('Error:', err.message); process.exit(1); });
15
+ } else if (subcommand === 'report' || !subcommand) {
16
+ run(args).catch(err => { console.error('Error:', err.message); process.exit(1); });
17
+ } else {
18
+ console.error(`Unknown command: ${subcommand}. Use "report" or "init".`);
19
+ process.exit(1);
20
+ }