ai-agent-detect 0.1.0 → 0.2.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 +31 -6
- package/agents/aider/index.js +15 -0
- package/agents/amp/index.js +11 -0
- package/agents/antigravity/index.js +11 -0
- package/agents/augment/index.js +11 -0
- package/agents/claude-code/index.js +4 -4
- package/agents/cline/index.js +12 -0
- package/agents/codex/index.js +19 -0
- package/agents/continue/index.js +11 -0
- package/agents/cowork/index.js +16 -0
- package/agents/crush/index.js +11 -0
- package/agents/cursor/index.js +19 -0
- package/agents/devin/index.js +12 -0
- package/agents/gemini-cli/index.js +11 -0
- package/agents/github-copilot/index.js +20 -0
- package/agents/goose/index.js +14 -0
- package/agents/grok/index.js +2 -6
- package/agents/index.js +50 -1
- package/agents/junie/index.js +14 -0
- package/agents/kilo/index.js +17 -0
- package/agents/kiro/index.js +12 -0
- package/agents/opencode/index.js +16 -0
- package/agents/openhands/index.js +17 -0
- package/agents/pi/index.js +12 -0
- package/agents/qwen-code/index.js +15 -0
- package/agents/replit/index.js +12 -0
- package/agents/roo/index.js +16 -0
- package/fixtures/aider.json +21 -0
- package/fixtures/amp.json +14 -0
- package/fixtures/antigravity.json +20 -0
- package/fixtures/augment.json +20 -0
- package/fixtures/cline.json +36 -0
- package/fixtures/codex.json +31 -0
- package/fixtures/continue.json +20 -0
- package/fixtures/cowork.json +21 -0
- package/fixtures/crush.json +20 -0
- package/fixtures/cursor.json +24 -0
- package/fixtures/devin.json +16 -0
- package/fixtures/gemini-cli.json +14 -0
- package/fixtures/github-copilot.json +52 -0
- package/fixtures/goose.json +22 -0
- package/fixtures/junie.json +20 -0
- package/fixtures/kilo.json +28 -0
- package/fixtures/kiro.json +20 -0
- package/fixtures/opencode.json +16 -0
- package/fixtures/openhands.json +23 -0
- package/fixtures/pi.json +20 -0
- package/fixtures/qwen-code.json +23 -0
- package/fixtures/replit.json +20 -0
- package/fixtures/roo.json +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Detect which AI agent is running your code — structured identity, not just a boolean.
|
|
4
4
|
|
|
5
|
-
**Status:
|
|
5
|
+
**Status: early but real.** 25 agents in the registry, every signal evidence-tiered and fixture-tested (82 tests); the API may still shift before `1.0`.
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
8
|
import { detect, isAgent } from 'ai-agent-detect';
|
|
@@ -34,12 +34,37 @@ Every claim carries its evidence: `source` (which signal matched), `raw` (the ma
|
|
|
34
34
|
|
|
35
35
|
## Supported agents
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|---|---|---|
|
|
39
|
-
| claude-code | `AI_AGENT` triple, `CLAUDECODE=1`, version via `CLAUDE_CODE_EXECPATH` | live-probed 2026-07-12 |
|
|
40
|
-
| grok | **none** — sets no identity variable (and passes inherited `AI_AGENT` through) | live-probed 2026-07-12 |
|
|
37
|
+
Evidence tiers: **probed** (live env snapshot recorded by us) > **source** (read in the agent's source code) > **doc** (vendor documentation) > **community** (reports) > **vercel-rules** (adopted from detect-agent's rules, not independently verified).
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
| agent | signal | evidence |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| claude-code | `AI_AGENT` triple, `CLAUDECODE=1`, version via `CLAUDE_CODE_EXECPATH` | probed |
|
|
42
|
+
| grok | **none** — no identity var; passes inherited `AI_AGENT` through | probed |
|
|
43
|
+
| gemini-cli | `GEMINI_CLI=1` (deliberate, documented) | source+doc |
|
|
44
|
+
| qwen-code | `QWEN_CODE=1` + session-id family (fork renamed Gemini's flag) | source |
|
|
45
|
+
| opencode | `OPENCODE=1`, `AGENT=1`, `OPENCODE_PID` | source |
|
|
46
|
+
| kilo | `KILO=1`, `KILO_CLIENT`, **version via `KILOCODE_VERSION`** (+ fork-parent `OPENCODE=1`, `AGENT=1`) | source |
|
|
47
|
+
| codex | sandbox side effects only (`CODEX_SANDBOX*`); **nothing under `danger-full-access`** | source |
|
|
48
|
+
| goose | `AGENT=goose` + `GOOSE_TERMINAL=1` — on two of three execution paths only | source |
|
|
49
|
+
| cline | `CLINE_ACTIVE=true` — VS Code terminals only | source |
|
|
50
|
+
| roo | `ROO_ACTIVE=true` (terminals), `ROO_CLI_RUNTIME=1` (headless) | source |
|
|
51
|
+
| aider | `OR_APP_NAME=Aider` + `OR_SITE_URL` (accidental fingerprint) | source |
|
|
52
|
+
| openhands | sandbox furniture: `OH_*` vars, `PS1` markers | source |
|
|
53
|
+
| crush | **none** — sets no identity variable | source |
|
|
54
|
+
| continue | **none** — sets no identity variable | source |
|
|
55
|
+
| github-copilot | `COPILOT_CLI=1` (documented), `COPILOT_AGENT_SESSION_ID`; `AI_AGENT=github-copilot(-cli)` | doc |
|
|
56
|
+
| amp | `AGENT=amp` (reported, unverified) | community |
|
|
57
|
+
| cursor | `CURSOR_AGENT=1`, `CURSOR_TRACE_ID`, `CURSOR_EXTENSION_HOST_ROLE` | community |
|
|
58
|
+
| antigravity | `ANTIGRAVITY_AGENT` | vercel-rules |
|
|
59
|
+
| augment | `AUGMENT_AGENT` | vercel-rules |
|
|
60
|
+
| cowork | `CLAUDE_CODE_IS_COWORK` atop claude-code vars | vercel-rules |
|
|
61
|
+
| junie | `JUNIE_DATA` / `JUNIE_SHIM_PATH` | vercel-rules |
|
|
62
|
+
| kiro | `TERM_PROGRAM~kiro` (low confidence — ambience, not identity) | vercel-rules |
|
|
63
|
+
| pi | PATH contains `.pi/agent` (low confidence) | vercel-rules |
|
|
64
|
+
| replit | `REPL_ID` (environment marker, not agent-specific) | vercel-rules |
|
|
65
|
+
| devin | file tell only (`/opt/.devin`) — outside the env core, planned for `/node` | vercel-rules |
|
|
66
|
+
|
|
67
|
+
The no-signal entries (grok, crush, continue; codex under `danger-full-access`) are deliberate: an agent that cannot be detected from env is recorded as such — honesty over reach. Any agent that sets `AI_AGENT` or a name-valued `AGENT` is detected without a registry entry. Coverage caveats (goose's per-path signals, cline's terminal-only marker) live as comments in each detector and in the fixtures.
|
|
43
68
|
|
|
44
69
|
## Design
|
|
45
70
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// aider — evidence, tiers, and probe TODOs: .kanbento/harnesses/aider.md
|
|
2
|
+
// Accidental fingerprint (OpenRouter attribution vars) — confidence capped at medium.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'aider',
|
|
5
|
+
vendor: 'aider',
|
|
6
|
+
procNames: ['aider'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
if (env.OR_APP_NAME === 'Aider') raw.OR_APP_NAME = env.OR_APP_NAME;
|
|
11
|
+
if (env.OR_SITE_URL?.includes('aider.chat')) raw.OR_SITE_URL = env.OR_SITE_URL;
|
|
12
|
+
if (Object.keys(raw).length === 0) return null;
|
|
13
|
+
return { confidence: 'medium', raw };
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// amp — evidence, tiers, and probe TODOs: .kanbento/harnesses/amp.md
|
|
2
|
+
// AGENT=amp is generic (core rung 2); no vendor var exists — this detector never fires.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'amp',
|
|
5
|
+
vendor: 'sourcegraph',
|
|
6
|
+
procNames: ['amp'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect() {
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// antigravity — evidence, tiers, and probe TODOs: .kanbento/harnesses/antigravity.md
|
|
2
|
+
export default {
|
|
3
|
+
name: 'antigravity',
|
|
4
|
+
vendor: 'google',
|
|
5
|
+
procNames: ['antigravity'],
|
|
6
|
+
pidEnvVar: null,
|
|
7
|
+
detect(env) {
|
|
8
|
+
if (!env.ANTIGRAVITY_AGENT) return null;
|
|
9
|
+
return { confidence: 'medium', raw: { ANTIGRAVITY_AGENT: env.ANTIGRAVITY_AGENT } };
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// augment — evidence, tiers, and probe TODOs: .kanbento/harnesses/augment.md
|
|
2
|
+
export default {
|
|
3
|
+
name: 'augment',
|
|
4
|
+
vendor: 'augment',
|
|
5
|
+
procNames: ['auggie'],
|
|
6
|
+
pidEnvVar: null,
|
|
7
|
+
detect(env) {
|
|
8
|
+
if (!env.AUGMENT_AGENT) return null;
|
|
9
|
+
return { confidence: 'medium', raw: { AUGMENT_AGENT: env.AUGMENT_AGENT } };
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// claude-code
|
|
2
|
-
// Sets the full AI_AGENT triple (handled by core rung 1); this detector owns
|
|
3
|
-
// the vendor tells, which also work when AI_AGENT is stripped or inherited-over.
|
|
1
|
+
// claude-code — evidence, tiers, and probe TODOs: .kanbento/harnesses/claude-code.md
|
|
4
2
|
export default {
|
|
5
3
|
name: 'claude-code',
|
|
6
4
|
vendor: 'anthropic',
|
|
7
5
|
procNames: ['claude'],
|
|
8
6
|
pidEnvVar: null,
|
|
9
7
|
detect(env) {
|
|
10
|
-
if (env.CLAUDECODE !== '1') return null;
|
|
8
|
+
if (env.CLAUDECODE !== '1' && !env.CLAUDE_CODE) return null;
|
|
9
|
+
// agents/cowork owns the marked case (specificity wins)
|
|
10
|
+
if (env.CLAUDE_CODE_IS_COWORK) return null;
|
|
11
11
|
const raw = {};
|
|
12
12
|
for (const key of [
|
|
13
13
|
'CLAUDECODE',
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// cline — evidence, tiers, and probe TODOs: .kanbento/harnesses/cline.md
|
|
2
|
+
// Marker exists on VS Code integrated terminals only — absence proves nothing.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'cline',
|
|
5
|
+
vendor: 'cline',
|
|
6
|
+
procNames: ['cline'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
if (env.CLINE_ACTIVE !== 'true') return null;
|
|
10
|
+
return { confidence: 'high', raw: { CLINE_ACTIVE: env.CLINE_ACTIVE } };
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// codex — evidence, tiers, and probe TODOs: .kanbento/harnesses/codex.md
|
|
2
|
+
// No marker under danger-full-access; CODEX_SANDBOX is macOS-only.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'codex',
|
|
5
|
+
vendor: 'openai',
|
|
6
|
+
procNames: ['codex'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
if (env.CODEX_SANDBOX_NETWORK_DISABLED === '1') raw.CODEX_SANDBOX_NETWORK_DISABLED = env.CODEX_SANDBOX_NETWORK_DISABLED;
|
|
11
|
+
if (env.CODEX_SANDBOX) raw.CODEX_SANDBOX = env.CODEX_SANDBOX;
|
|
12
|
+
// CODEX_CI / CODEX_THREAD_ID: vercel-rules tier, hence the medium below
|
|
13
|
+
if (env.CODEX_CI) raw.CODEX_CI = env.CODEX_CI;
|
|
14
|
+
if (env.CODEX_THREAD_ID) raw.CODEX_THREAD_ID = env.CODEX_THREAD_ID;
|
|
15
|
+
if (Object.keys(raw).length === 0) return null;
|
|
16
|
+
const sourceConfirmed = raw.CODEX_SANDBOX_NETWORK_DISABLED || raw.CODEX_SANDBOX;
|
|
17
|
+
return { confidence: sourceConfirmed ? 'high' : 'medium', raw };
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// continue — evidence, tiers, and probe TODOs: .kanbento/harnesses/continue.md
|
|
2
|
+
// No env signal exists — this detector never fires.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'continue',
|
|
5
|
+
vendor: 'continue',
|
|
6
|
+
procNames: ['cn'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect() {
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// cowork — evidence, tiers, and probe TODOs: .kanbento/harnesses/cowork.md
|
|
2
|
+
// Runs Claude Code underneath; the claude-code detector defers when the marker is set.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'cowork',
|
|
5
|
+
vendor: 'anthropic',
|
|
6
|
+
procNames: [],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
if (!env.CLAUDE_CODE_IS_COWORK) return null;
|
|
10
|
+
if (env.CLAUDECODE !== '1' && !env.CLAUDE_CODE) return null;
|
|
11
|
+
const raw = { CLAUDE_CODE_IS_COWORK: env.CLAUDE_CODE_IS_COWORK };
|
|
12
|
+
if (env.CLAUDECODE !== undefined) raw.CLAUDECODE = env.CLAUDECODE;
|
|
13
|
+
if (env.CLAUDE_CODE !== undefined) raw.CLAUDE_CODE = env.CLAUDE_CODE;
|
|
14
|
+
return { confidence: 'medium', raw };
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// crush — evidence, tiers, and probe TODOs: .kanbento/harnesses/crush.md
|
|
2
|
+
// No env signal exists — this detector never fires.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'crush',
|
|
5
|
+
vendor: 'charm',
|
|
6
|
+
procNames: ['crush'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect() {
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// cursor — evidence, tiers, and probe TODOs: .kanbento/harnesses/cursor.md
|
|
2
|
+
// All tells are community-tier (none on an official reference page).
|
|
3
|
+
export default {
|
|
4
|
+
name: 'cursor',
|
|
5
|
+
vendor: 'anysphere',
|
|
6
|
+
procNames: ['cursor-agent', 'agent'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
for (const key of ['CURSOR_AGENT', 'CURSOR_TRACE_ID', 'CURSOR_CLI', 'CURSOR_EXTENSION_HOST_ROLE']) {
|
|
11
|
+
if (env[key] !== undefined) raw[key] = env[key];
|
|
12
|
+
}
|
|
13
|
+
if (env.CURSOR_AGENT === '1') return { confidence: 'high', raw };
|
|
14
|
+
// vercel-rules tier (detect-agent@1.0.1): the CLI in extension-host mode
|
|
15
|
+
if (env.CURSOR_EXTENSION_HOST_ROLE === 'agent-exec') return { confidence: 'high', raw };
|
|
16
|
+
if (env.CURSOR_TRACE_ID) return { confidence: 'medium', raw };
|
|
17
|
+
return null;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// devin — evidence, tiers, and probe TODOs: .kanbento/harnesses/devin.md
|
|
2
|
+
// Only tell is a file check (/opt/.devin) — outside the env core; never fires here.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'devin',
|
|
5
|
+
vendor: 'cognition',
|
|
6
|
+
procNames: [],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
filesTells: ['/opt/.devin'],
|
|
9
|
+
detect() {
|
|
10
|
+
return null;
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// gemini-cli — evidence, tiers, and probe TODOs: .kanbento/harnesses/gemini-cli.md
|
|
2
|
+
export default {
|
|
3
|
+
name: 'gemini-cli',
|
|
4
|
+
vendor: 'google',
|
|
5
|
+
procNames: ['gemini'],
|
|
6
|
+
pidEnvVar: null,
|
|
7
|
+
detect(env) {
|
|
8
|
+
if (env.GEMINI_CLI !== '1') return null;
|
|
9
|
+
return { confidence: 'high', raw: { GEMINI_CLI: env.GEMINI_CLI } };
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// github-copilot — evidence, tiers, and probe TODOs: .kanbento/harnesses/github-copilot.md
|
|
2
|
+
// COPILOT_CLI=1 is vendor-documented; the COPILOT_MODEL trio is an unverified fallback.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'github-copilot',
|
|
5
|
+
vendor: 'github',
|
|
6
|
+
procNames: ['copilot'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
if (env.COPILOT_CLI === '1') raw.COPILOT_CLI = env.COPILOT_CLI;
|
|
11
|
+
if (env.COPILOT_AGENT_SESSION_ID) raw.COPILOT_AGENT_SESSION_ID = env.COPILOT_AGENT_SESSION_ID;
|
|
12
|
+
if (Object.keys(raw).length > 0) return { confidence: 'high', raw };
|
|
13
|
+
// vercel-rules tier, unverified in vendor sources
|
|
14
|
+
for (const key of ['COPILOT_MODEL', 'COPILOT_ALLOW_ALL', 'COPILOT_GITHUB_TOKEN']) {
|
|
15
|
+
if (env[key]) raw[key] = env[key];
|
|
16
|
+
}
|
|
17
|
+
if (Object.keys(raw).length === 0) return null;
|
|
18
|
+
return { confidence: 'medium', raw };
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// goose — evidence, tiers, and probe TODOs: .kanbento/harnesses/goose.md
|
|
2
|
+
// Vars set on two of three execution paths — absence proves nothing.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'goose',
|
|
5
|
+
vendor: 'block',
|
|
6
|
+
procNames: ['goose'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
if (env.GOOSE_TERMINAL !== '1') return null;
|
|
10
|
+
const raw = { GOOSE_TERMINAL: env.GOOSE_TERMINAL };
|
|
11
|
+
if (env.AGENT_SESSION_ID) raw.AGENT_SESSION_ID = env.AGENT_SESSION_ID;
|
|
12
|
+
return { confidence: 'high', raw };
|
|
13
|
+
},
|
|
14
|
+
};
|
package/agents/grok/index.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
// grok
|
|
2
|
-
//
|
|
3
|
-
// carry only a non-interactive hygiene bundle (CI=true, pagers off, progress
|
|
4
|
-
// off) — too weak to name an agent; weak-signal use is parked (note:design).
|
|
5
|
-
// It also passes inherited AI_AGENT through — the live proof of nested-harness
|
|
6
|
-
// contamination. Env-undetectable, honestly: this detector never fires.
|
|
1
|
+
// grok — evidence, tiers, and probe TODOs: .kanbento/harnesses/grok.md
|
|
2
|
+
// No identity var (probed) — this detector never fires.
|
|
7
3
|
export default {
|
|
8
4
|
name: 'grok',
|
|
9
5
|
vendor: 'xai',
|
package/agents/index.js
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
// One agent = one folder = one easy PR. Keep alphabetical.
|
|
2
|
+
import aider from './aider/index.js';
|
|
3
|
+
import amp from './amp/index.js';
|
|
4
|
+
import antigravity from './antigravity/index.js';
|
|
5
|
+
import augment from './augment/index.js';
|
|
2
6
|
import claudeCode from './claude-code/index.js';
|
|
7
|
+
import cline from './cline/index.js';
|
|
8
|
+
import codex from './codex/index.js';
|
|
9
|
+
import continueDev from './continue/index.js';
|
|
10
|
+
import cowork from './cowork/index.js';
|
|
11
|
+
import crush from './crush/index.js';
|
|
12
|
+
import cursor from './cursor/index.js';
|
|
13
|
+
import devin from './devin/index.js';
|
|
14
|
+
import geminiCli from './gemini-cli/index.js';
|
|
15
|
+
import githubCopilot from './github-copilot/index.js';
|
|
16
|
+
import goose from './goose/index.js';
|
|
3
17
|
import grok from './grok/index.js';
|
|
18
|
+
import junie from './junie/index.js';
|
|
19
|
+
import kilo from './kilo/index.js';
|
|
20
|
+
import kiro from './kiro/index.js';
|
|
21
|
+
import opencode from './opencode/index.js';
|
|
22
|
+
import openhands from './openhands/index.js';
|
|
23
|
+
import pi from './pi/index.js';
|
|
24
|
+
import qwenCode from './qwen-code/index.js';
|
|
25
|
+
import replit from './replit/index.js';
|
|
26
|
+
import roo from './roo/index.js';
|
|
4
27
|
|
|
5
|
-
export const agents = [
|
|
28
|
+
export const agents = [
|
|
29
|
+
aider,
|
|
30
|
+
amp,
|
|
31
|
+
antigravity,
|
|
32
|
+
augment,
|
|
33
|
+
claudeCode,
|
|
34
|
+
cline,
|
|
35
|
+
codex,
|
|
36
|
+
continueDev,
|
|
37
|
+
cowork,
|
|
38
|
+
crush,
|
|
39
|
+
cursor,
|
|
40
|
+
devin,
|
|
41
|
+
geminiCli,
|
|
42
|
+
githubCopilot,
|
|
43
|
+
goose,
|
|
44
|
+
grok,
|
|
45
|
+
junie,
|
|
46
|
+
kilo,
|
|
47
|
+
kiro,
|
|
48
|
+
opencode,
|
|
49
|
+
openhands,
|
|
50
|
+
pi,
|
|
51
|
+
qwenCode,
|
|
52
|
+
replit,
|
|
53
|
+
roo,
|
|
54
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// junie — evidence, tiers, and probe TODOs: .kanbento/harnesses/junie.md
|
|
2
|
+
export default {
|
|
3
|
+
name: 'junie',
|
|
4
|
+
vendor: 'jetbrains',
|
|
5
|
+
procNames: ['junie'],
|
|
6
|
+
pidEnvVar: null,
|
|
7
|
+
detect(env) {
|
|
8
|
+
const raw = {};
|
|
9
|
+
if (env.JUNIE_DATA) raw.JUNIE_DATA = env.JUNIE_DATA;
|
|
10
|
+
if (env.JUNIE_SHIM_PATH) raw.JUNIE_SHIM_PATH = env.JUNIE_SHIM_PATH;
|
|
11
|
+
if (Object.keys(raw).length === 0) return null;
|
|
12
|
+
return { confidence: 'medium', raw };
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// kilo — evidence, tiers, and probe TODOs: .kanbento/harnesses/kilo.md
|
|
2
|
+
// opencode fork: both detectors fire by design (claims[] represents it; kilo wins by order).
|
|
3
|
+
export default {
|
|
4
|
+
name: 'kilo',
|
|
5
|
+
vendor: 'kilo-org',
|
|
6
|
+
procNames: ['kilo'],
|
|
7
|
+
pidEnvVar: 'KILO_PID',
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
for (const key of ['KILO', 'KILO_CLIENT', 'KILO_PLATFORM', 'KILOCODE_FEATURE', 'KILOCODE_VERSION', 'KILO_APP_VERSION', 'KILO_PID']) {
|
|
11
|
+
if (env[key] !== undefined) raw[key] = env[key];
|
|
12
|
+
}
|
|
13
|
+
if (env.KILO !== '1' && !env.KILO_CLIENT && !env.KILOCODE_FEATURE) return null;
|
|
14
|
+
const version = env.KILOCODE_VERSION || env.KILO_APP_VERSION || null;
|
|
15
|
+
return { confidence: 'high', version, raw };
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// kiro — evidence, tiers, and probe TODOs: .kanbento/harnesses/kiro.md
|
|
2
|
+
// TERM_PROGRAM is ambience, not identity (their TTY gate is impossible env-only) — low.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'kiro',
|
|
5
|
+
vendor: 'aws',
|
|
6
|
+
procNames: ['kiro'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
if (!env.TERM_PROGRAM || !env.TERM_PROGRAM.includes('kiro')) return null;
|
|
10
|
+
return { confidence: 'low', raw: { TERM_PROGRAM: env.TERM_PROGRAM } };
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// opencode — evidence, tiers, and probe TODOs: .kanbento/harnesses/opencode.md
|
|
2
|
+
export default {
|
|
3
|
+
name: 'opencode',
|
|
4
|
+
vendor: 'anomaly',
|
|
5
|
+
procNames: ['opencode'],
|
|
6
|
+
pidEnvVar: 'OPENCODE_PID',
|
|
7
|
+
detect(env) {
|
|
8
|
+
// OPENCODE_CLIENT is the vercel-rules-tier sibling (detect-agent@1.0.1)
|
|
9
|
+
if (env.OPENCODE !== '1' && !env.OPENCODE_CLIENT) return null;
|
|
10
|
+
const raw = {};
|
|
11
|
+
for (const key of ['OPENCODE', 'OPENCODE_CLIENT', 'OPENCODE_PID']) {
|
|
12
|
+
if (env[key] !== undefined) raw[key] = env[key];
|
|
13
|
+
}
|
|
14
|
+
return { confidence: 'high', raw };
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// openhands — evidence, tiers, and probe TODOs: .kanbento/harnesses/openhands.md
|
|
2
|
+
// Sandbox-furniture fingerprint, not a deliberate marker — medium.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'openhands',
|
|
5
|
+
vendor: 'all-hands',
|
|
6
|
+
procNames: [],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
for (const key of ['OH_CONVERSATIONS_PATH', 'OH_BASH_EVENTS_DIR', 'OPENVSCODE_SERVER_ROOT']) {
|
|
11
|
+
if (env[key]) raw[key] = env[key];
|
|
12
|
+
}
|
|
13
|
+
if (env.PS1?.includes('###PS1JSON###')) raw.PS1 = '<contains ###PS1JSON### marker>';
|
|
14
|
+
if (Object.keys(raw).length === 0) return null;
|
|
15
|
+
return { confidence: 'medium', raw };
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// pi — evidence, tiers, and probe TODOs: .kanbento/harnesses/pi.md
|
|
2
|
+
// PATH fingerprint — weak-signal class, low.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'pi',
|
|
5
|
+
vendor: null,
|
|
6
|
+
procNames: ['pi'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
if (!env.PATH || !/\.pi[\\/]agent/.test(env.PATH)) return null;
|
|
10
|
+
return { confidence: 'low', raw: { PATH: '<contains .pi/agent>' } };
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// qwen-code — evidence, tiers, and probe TODOs: .kanbento/harnesses/qwen-code.md
|
|
2
|
+
export default {
|
|
3
|
+
name: 'qwen-code',
|
|
4
|
+
vendor: 'alibaba',
|
|
5
|
+
procNames: ['qwen'],
|
|
6
|
+
pidEnvVar: null,
|
|
7
|
+
detect(env) {
|
|
8
|
+
if (env.QWEN_CODE !== '1') return null;
|
|
9
|
+
const raw = { QWEN_CODE: env.QWEN_CODE };
|
|
10
|
+
for (const key of ['QWEN_CODE_SESSION_ID', 'QWEN_CODE_AGENT_ID', 'QWEN_CODE_PROMPT_ID']) {
|
|
11
|
+
if (env[key]) raw[key] = env[key];
|
|
12
|
+
}
|
|
13
|
+
return { confidence: 'high', raw };
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// replit — evidence, tiers, and probe TODOs: .kanbento/harnesses/replit.md
|
|
2
|
+
// REPL_ID marks the Replit platform, not agent operation — medium.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'replit',
|
|
5
|
+
vendor: 'replit',
|
|
6
|
+
procNames: [],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
if (!env.REPL_ID) return null;
|
|
10
|
+
return { confidence: 'medium', raw: { REPL_ID: env.REPL_ID } };
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// roo — evidence, tiers, and probe TODOs: .kanbento/harnesses/roo.md
|
|
2
|
+
// The execa inline provider sets neither var — absence proves nothing.
|
|
3
|
+
export default {
|
|
4
|
+
name: 'roo',
|
|
5
|
+
vendor: 'roo-code',
|
|
6
|
+
procNames: ['roo'],
|
|
7
|
+
pidEnvVar: null,
|
|
8
|
+
detect(env) {
|
|
9
|
+
const raw = {};
|
|
10
|
+
if (env.ROO_ACTIVE === 'true') raw.ROO_ACTIVE = env.ROO_ACTIVE;
|
|
11
|
+
if (env.ROO_CLI_RUNTIME === '1') raw.ROO_CLI_RUNTIME = env.ROO_CLI_RUNTIME;
|
|
12
|
+
if (Object.keys(raw).length === 0) return null;
|
|
13
|
+
const mode = raw.ROO_CLI_RUNTIME ? 'agent' : null;
|
|
14
|
+
return { confidence: 'high', mode, raw };
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "aider",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: aider/llm.py:9-13 \u2014 OpenRouter attribution vars set at import, inherited by every /run child; accidental fingerprint, not identity by intent",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"OR_SITE_URL": "https://aider.chat",
|
|
10
|
+
"OR_APP_NAME": "Aider"
|
|
11
|
+
},
|
|
12
|
+
"expect": {
|
|
13
|
+
"isAgent": true,
|
|
14
|
+
"primary": "aider",
|
|
15
|
+
"claimNames": [
|
|
16
|
+
"aider"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "amp",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "community-derived: agents.md adoption table (issues/136) lists amp as setting AGENT=amp — not in the official manual, unverified in source; identity arrives via the generic AGENT rung, no vendor detector",
|
|
7
|
+
"tier": "community",
|
|
8
|
+
"env": {
|
|
9
|
+
"AGENT": "amp"
|
|
10
|
+
},
|
|
11
|
+
"expect": { "isAgent": true, "primary": "amp", "claimNames": ["amp"] }
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "antigravity",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified)",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"ANTIGRAVITY_AGENT": "1"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "antigravity",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"antigravity"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "augment",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified)",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"AUGMENT_AGENT": "1"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "augment",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"augment"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "cline",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: VscodeTerminalRegistry.ts:29 \u2014 CLINE_ACTIVE=true injected on VS Code integrated terminals",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"CLINE_ACTIVE": "true"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "cline",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"cline"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"date": "2026-07-12",
|
|
21
|
+
"method": "source-derived: standalone/CLI path (StandaloneTerminalProcess.ts:104-116) sets only anti-interactivity overrides, NOT the marker \u2014 the coverage gap",
|
|
22
|
+
"tier": "source",
|
|
23
|
+
"env": {
|
|
24
|
+
"TERM": "xterm-256color",
|
|
25
|
+
"PAGER": "cat",
|
|
26
|
+
"GIT_PAGER": "cat",
|
|
27
|
+
"MANPAGER": "cat"
|
|
28
|
+
},
|
|
29
|
+
"expect": {
|
|
30
|
+
"isAgent": false,
|
|
31
|
+
"primary": null,
|
|
32
|
+
"claimNames": []
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "codex",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: codex-rs/core/src/spawn.rs — network sandbox on (any OS)",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"CODEX_SANDBOX_NETWORK_DISABLED": "1"
|
|
10
|
+
},
|
|
11
|
+
"expect": { "isAgent": true, "primary": "codex", "claimNames": ["codex"] }
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"date": "2026-07-12",
|
|
15
|
+
"method": "source-derived: macOS Seatbelt — both sandbox vars present",
|
|
16
|
+
"tier": "source",
|
|
17
|
+
"env": {
|
|
18
|
+
"CODEX_SANDBOX": "seatbelt",
|
|
19
|
+
"CODEX_SANDBOX_NETWORK_DISABLED": "1"
|
|
20
|
+
},
|
|
21
|
+
"expect": { "isAgent": true, "primary": "codex", "claimNames": ["codex"] }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"date": "2026-07-12",
|
|
25
|
+
"method": "source-derived: --sandbox danger-full-access — NO env signal exists (the documented hole; ppid walk is the only rung that catches codex here)",
|
|
26
|
+
"tier": "source",
|
|
27
|
+
"env": {},
|
|
28
|
+
"expect": { "isAgent": false, "primary": null, "claimNames": [] }
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "continue",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: cn CLI spawns with no env option (runTerminalCommand.ts:192); VS Code path adds only generic color vars. Env-undetectable; upstream ticket candidate (CONTINUE=1)",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"FORCE_COLOR": "1",
|
|
10
|
+
"COLORTERM": "truecolor",
|
|
11
|
+
"CLICOLOR": "1"
|
|
12
|
+
},
|
|
13
|
+
"expect": {
|
|
14
|
+
"isAgent": false,
|
|
15
|
+
"primary": null,
|
|
16
|
+
"claimNames": []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "cowork",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified) \u2014 cowork marker atop claude-code vars; claude-code detector defers (specificity wins)",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"CLAUDE_CODE_IS_COWORK": "1",
|
|
10
|
+
"CLAUDECODE": "1"
|
|
11
|
+
},
|
|
12
|
+
"expect": {
|
|
13
|
+
"isAgent": true,
|
|
14
|
+
"primary": "cowork",
|
|
15
|
+
"claimNames": [
|
|
16
|
+
"cowork"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "crush",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: full Setenv/cmd.Env sweep of internal/ \u2014 NO self-identity var on shell children; only generic color-forcing on the PTY path. Env-undetectable; upstream ticket candidate (CRUSH=1)",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"COLORTERM": "truecolor",
|
|
10
|
+
"CLICOLOR_FORCE": "1",
|
|
11
|
+
"FORCE_COLOR": "1"
|
|
12
|
+
},
|
|
13
|
+
"expect": {
|
|
14
|
+
"isAgent": false,
|
|
15
|
+
"primary": null,
|
|
16
|
+
"claimNames": []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "cursor",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "community/staff-derived: agent shell command env (forum.cursor.com/t/132427, t/37734) — both tells present",
|
|
7
|
+
"tier": "community",
|
|
8
|
+
"env": {
|
|
9
|
+
"CURSOR_AGENT": "1",
|
|
10
|
+
"CURSOR_TRACE_ID": "0000-fixture-trace-id"
|
|
11
|
+
},
|
|
12
|
+
"expect": { "isAgent": true, "primary": "cursor", "claimNames": ["cursor"] }
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"date": "2026-07-12",
|
|
16
|
+
"method": "community-derived: trace id only (CURSOR_AGENT=1 was once accidentally dropped by a CLI release — the degraded state)",
|
|
17
|
+
"tier": "community",
|
|
18
|
+
"env": {
|
|
19
|
+
"CURSOR_TRACE_ID": "0000-fixture-trace-id"
|
|
20
|
+
},
|
|
21
|
+
"expect": { "isAgent": true, "primary": "cursor", "claimNames": ["cursor"] }
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "devin",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified) \u2014 only tell is file_exists /opt/.devin, a disk check outside the edge-safe env core; devin detection belongs to the /node rung",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {},
|
|
9
|
+
"expect": {
|
|
10
|
+
"isAgent": false,
|
|
11
|
+
"primary": null,
|
|
12
|
+
"claimNames": []
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "gemini-cli",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source+doc-derived: shellExecutionService.ts sets GEMINI_CLI=1 on every shell-tool child; documented in docs/tools/shell.md",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"GEMINI_CLI": "1"
|
|
10
|
+
},
|
|
11
|
+
"expect": { "isAgent": true, "primary": "gemini-cli", "claimNames": ["gemini-cli"] }
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "github-copilot",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "doc-derived: vendor changelog:1674 \u2014 COPILOT_CLI=1 is the documented subprocess-detection hook, plus session id (changelog:1128)",
|
|
7
|
+
"tier": "doc",
|
|
8
|
+
"env": {
|
|
9
|
+
"COPILOT_CLI": "1",
|
|
10
|
+
"COPILOT_AGENT_SESSION_ID": "fixture-session"
|
|
11
|
+
},
|
|
12
|
+
"expect": {
|
|
13
|
+
"isAgent": true,
|
|
14
|
+
"primary": "github-copilot",
|
|
15
|
+
"claimNames": [
|
|
16
|
+
"github-copilot"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"date": "2026-07-12",
|
|
22
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified) \u2014 COPILOT_MODEL trio, unverified in vendor changelog; weaker fallback",
|
|
23
|
+
"tier": "vercel-rules",
|
|
24
|
+
"env": {
|
|
25
|
+
"COPILOT_MODEL": "gpt-5"
|
|
26
|
+
},
|
|
27
|
+
"expect": {
|
|
28
|
+
"isAgent": true,
|
|
29
|
+
"primary": "github-copilot",
|
|
30
|
+
"claimNames": [
|
|
31
|
+
"github-copilot"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"date": "2026-07-12",
|
|
37
|
+
"method": "doc-derived: AI_AGENT self-identification (github-copilot-cli dialect via core rung 1)",
|
|
38
|
+
"tier": "doc",
|
|
39
|
+
"env": {
|
|
40
|
+
"AI_AGENT": "github-copilot-cli"
|
|
41
|
+
},
|
|
42
|
+
"expect": {
|
|
43
|
+
"isAgent": true,
|
|
44
|
+
"primary": "github-copilot-cli",
|
|
45
|
+
"claimNames": [
|
|
46
|
+
"github-copilot-cli"
|
|
47
|
+
],
|
|
48
|
+
"note": "verbatim rung-1 name; Vercel's scoped pkg normalizes -cli away, we preserve evidence as-is"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "goose",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: computer-controller / retry path (goose-mcp computercontroller/mod.rs:855-873) — AGENT=goose + GOOSE_TERMINAL=1, corroborating",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"AGENT": "goose",
|
|
10
|
+
"GOOSE_TERMINAL": "1"
|
|
11
|
+
},
|
|
12
|
+
"expect": { "isAgent": true, "primary": "goose", "claimNames": ["goose"] }
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"date": "2026-07-12",
|
|
16
|
+
"method": "source-derived: main developer shell tool sets NEITHER var (shell.rs sets only PATH — source contradicts the docs' blanket claim); goose is env-invisible on its most common path",
|
|
17
|
+
"tier": "source",
|
|
18
|
+
"env": {},
|
|
19
|
+
"expect": { "isAgent": false, "primary": null, "claimNames": [] }
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "junie",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified)",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"JUNIE_DATA": "/tmp/junie"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "junie",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"junie"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "kilo",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: cli/setup.ts:56 + server-manager.ts:113-152 + fork-parent index.ts:111-113 \u2014 the richest emitter surveyed; opencode detector legitimately fires too (kilo IS opencode underneath): the claims model represents it",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"KILO": "1",
|
|
10
|
+
"KILO_CLIENT": "vscode",
|
|
11
|
+
"KILOCODE_FEATURE": "vscode-extension",
|
|
12
|
+
"KILOCODE_VERSION": "4.99.0",
|
|
13
|
+
"KILO_PID": "4242",
|
|
14
|
+
"AGENT": "1",
|
|
15
|
+
"OPENCODE": "1"
|
|
16
|
+
},
|
|
17
|
+
"expect": {
|
|
18
|
+
"isAgent": true,
|
|
19
|
+
"primary": "kilo",
|
|
20
|
+
"version": "4.99.0",
|
|
21
|
+
"claimNames": [
|
|
22
|
+
"kilo",
|
|
23
|
+
"opencode"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "kiro",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified) \u2014 their rule gates on no_tty which env-only cannot see; low confidence, ambience not identity",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"TERM_PROGRAM": "kiro"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "kiro",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"kiro"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "opencode",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: startup middleware (packages/opencode/src/index.ts:75-77) — OPENCODE=1, AGENT=1 (boolean: anonymous claim), OPENCODE_PID",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"OPENCODE": "1",
|
|
10
|
+
"AGENT": "1",
|
|
11
|
+
"OPENCODE_PID": "12345"
|
|
12
|
+
},
|
|
13
|
+
"expect": { "isAgent": true, "primary": "opencode", "claimNames": ["opencode"] }
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "openhands",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: docker_sandbox_spec_service.py:38-52 + subprocess_terminal.py:150 \u2014 sandbox container furniture + PS1 markers; environment fingerprint, not identity by intent",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"OH_CONVERSATIONS_PATH": "/workspace/conversations",
|
|
10
|
+
"OH_BASH_EVENTS_DIR": "/workspace/bash_events",
|
|
11
|
+
"OPENVSCODE_SERVER_ROOT": "/openhands/.openvscode-server",
|
|
12
|
+
"PS1": "###PS1JSON###{}###PS1END###"
|
|
13
|
+
},
|
|
14
|
+
"expect": {
|
|
15
|
+
"isAgent": true,
|
|
16
|
+
"primary": "openhands",
|
|
17
|
+
"claimNames": [
|
|
18
|
+
"openhands"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
package/fixtures/pi.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "pi",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified) \u2014 PATH fingerprint, weak-signal class",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"PATH": "/usr/bin:/home/u/.pi/agent/bin"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "pi",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"pi"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "qwen-code",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: shellExecutionService.ts:723 + shellContextEnv.ts:41-55 \u2014 QWEN_CODE=1 with session-id family on every shell child; renamed from Gemini's GEMINI_CLI (fork, zero GEMINI_CLI hits)",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"QWEN_CODE": "1",
|
|
10
|
+
"QWEN_CODE_SESSION_ID": "fixture-uuid",
|
|
11
|
+
"QWEN_CODE_AGENT_ID": "",
|
|
12
|
+
"QWEN_CODE_PROMPT_ID": ""
|
|
13
|
+
},
|
|
14
|
+
"expect": {
|
|
15
|
+
"isAgent": true,
|
|
16
|
+
"primary": "qwen-code",
|
|
17
|
+
"claimNames": [
|
|
18
|
+
"qwen-code"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "replit",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "vercel-rules-derived: detect-agent@1.0.1 agents.json (adopted, not independently verified) \u2014 REPL_ID marks the Replit environment, not specifically its agent",
|
|
7
|
+
"tier": "vercel-rules",
|
|
8
|
+
"env": {
|
|
9
|
+
"REPL_ID": "fixture-repl"
|
|
10
|
+
},
|
|
11
|
+
"expect": {
|
|
12
|
+
"isAgent": true,
|
|
13
|
+
"primary": "replit",
|
|
14
|
+
"claimNames": [
|
|
15
|
+
"replit"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent": "roo",
|
|
3
|
+
"probes": [
|
|
4
|
+
{
|
|
5
|
+
"date": "2026-07-12",
|
|
6
|
+
"method": "source-derived: Terminal.ts:153-161 \u2014 ROO_ACTIVE=true on VS Code terminals",
|
|
7
|
+
"tier": "source",
|
|
8
|
+
"env": {
|
|
9
|
+
"ROO_ACTIVE": "true",
|
|
10
|
+
"PAGER": "cat",
|
|
11
|
+
"VTE_VERSION": "0"
|
|
12
|
+
},
|
|
13
|
+
"expect": {
|
|
14
|
+
"isAgent": true,
|
|
15
|
+
"primary": "roo",
|
|
16
|
+
"claimNames": [
|
|
17
|
+
"roo"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"date": "2026-07-12",
|
|
23
|
+
"method": "source-derived: extension-host.ts:182 \u2014 ROO_CLI_RUNTIME=1 process-wide in the headless CLI",
|
|
24
|
+
"tier": "source",
|
|
25
|
+
"env": {
|
|
26
|
+
"ROO_CLI_RUNTIME": "1"
|
|
27
|
+
},
|
|
28
|
+
"expect": {
|
|
29
|
+
"isAgent": true,
|
|
30
|
+
"primary": "roo",
|
|
31
|
+
"mode": "agent",
|
|
32
|
+
"claimNames": [
|
|
33
|
+
"roo"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-agent-detect",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Detect which AI agent is running your code — structured identity (name, version, mode) with evidence, not just a boolean. Reads the AI_AGENT standard (both dialects) and the signals it misses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "George Mihailov",
|