ax-audit 3.6.0 → 4.0.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.
Files changed (201) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/LICENSE +1 -1
  3. package/README.md +58 -36
  4. package/dist/baseline.d.ts +2 -0
  5. package/dist/baseline.d.ts.map +1 -1
  6. package/dist/baseline.js +42 -4
  7. package/dist/baseline.js.map +1 -1
  8. package/dist/check-ids.d.ts +19 -0
  9. package/dist/check-ids.d.ts.map +1 -0
  10. package/dist/check-ids.js +53 -0
  11. package/dist/check-ids.js.map +1 -0
  12. package/dist/checks/agent-access.d.ts +23 -6
  13. package/dist/checks/agent-access.d.ts.map +1 -1
  14. package/dist/checks/agent-access.js +200 -54
  15. package/dist/checks/agent-access.js.map +1 -1
  16. package/dist/checks/agent-card.d.ts +37 -0
  17. package/dist/checks/agent-card.d.ts.map +1 -0
  18. package/dist/checks/agent-card.js +352 -0
  19. package/dist/checks/agent-card.js.map +1 -0
  20. package/dist/checks/agent-operability.d.ts +66 -0
  21. package/dist/checks/agent-operability.d.ts.map +1 -0
  22. package/dist/checks/agent-operability.js +383 -0
  23. package/dist/checks/agent-operability.js.map +1 -0
  24. package/dist/checks/agent-skills.d.ts +24 -0
  25. package/dist/checks/agent-skills.d.ts.map +1 -0
  26. package/dist/checks/agent-skills.js +316 -0
  27. package/dist/checks/agent-skills.js.map +1 -0
  28. package/dist/checks/ai-catalog.d.ts +28 -0
  29. package/dist/checks/ai-catalog.d.ts.map +1 -0
  30. package/dist/checks/ai-catalog.js +254 -0
  31. package/dist/checks/ai-catalog.js.map +1 -0
  32. package/dist/checks/ai-directives.d.ts +57 -0
  33. package/dist/checks/ai-directives.d.ts.map +1 -0
  34. package/dist/checks/ai-directives.js +263 -0
  35. package/dist/checks/ai-directives.js.map +1 -0
  36. package/dist/checks/api-discovery.d.ts +26 -0
  37. package/dist/checks/api-discovery.d.ts.map +1 -0
  38. package/dist/checks/api-discovery.js +432 -0
  39. package/dist/checks/api-discovery.js.map +1 -0
  40. package/dist/checks/auth-discovery.d.ts +28 -0
  41. package/dist/checks/auth-discovery.d.ts.map +1 -0
  42. package/dist/checks/auth-discovery.js +213 -0
  43. package/dist/checks/auth-discovery.js.map +1 -0
  44. package/dist/checks/commerce-discovery.d.ts +40 -0
  45. package/dist/checks/commerce-discovery.d.ts.map +1 -0
  46. package/dist/checks/commerce-discovery.js +295 -0
  47. package/dist/checks/commerce-discovery.js.map +1 -0
  48. package/dist/checks/content-negotiation.d.ts.map +1 -1
  49. package/dist/checks/content-negotiation.js +135 -20
  50. package/dist/checks/content-negotiation.js.map +1 -1
  51. package/dist/checks/crawl-efficiency.d.ts +13 -1
  52. package/dist/checks/crawl-efficiency.d.ts.map +1 -1
  53. package/dist/checks/crawl-efficiency.js +65 -1
  54. package/dist/checks/crawl-efficiency.js.map +1 -1
  55. package/dist/checks/frontmatter.d.ts +34 -0
  56. package/dist/checks/frontmatter.d.ts.map +1 -0
  57. package/dist/checks/frontmatter.js +100 -0
  58. package/dist/checks/frontmatter.js.map +1 -0
  59. package/dist/checks/html-rendering.d.ts.map +1 -1
  60. package/dist/checks/html-rendering.js +0 -1
  61. package/dist/checks/html-rendering.js.map +1 -1
  62. package/dist/checks/html-utils.d.ts +10 -0
  63. package/dist/checks/html-utils.d.ts.map +1 -1
  64. package/dist/checks/html-utils.js +19 -0
  65. package/dist/checks/html-utils.js.map +1 -1
  66. package/dist/checks/http-headers.d.ts.map +1 -1
  67. package/dist/checks/http-headers.js +82 -10
  68. package/dist/checks/http-headers.js.map +1 -1
  69. package/dist/checks/http-hygiene.d.ts +26 -0
  70. package/dist/checks/http-hygiene.d.ts.map +1 -0
  71. package/dist/checks/http-hygiene.js +257 -0
  72. package/dist/checks/http-hygiene.js.map +1 -0
  73. package/dist/checks/index.d.ts.map +1 -1
  74. package/dist/checks/index.js +24 -8
  75. package/dist/checks/index.js.map +1 -1
  76. package/dist/checks/llms-txt.d.ts +15 -0
  77. package/dist/checks/llms-txt.d.ts.map +1 -1
  78. package/dist/checks/llms-txt.js +162 -2
  79. package/dist/checks/llms-txt.js.map +1 -1
  80. package/dist/checks/mcp-discovery.d.ts +30 -0
  81. package/dist/checks/mcp-discovery.d.ts.map +1 -0
  82. package/dist/checks/mcp-discovery.js +523 -0
  83. package/dist/checks/mcp-discovery.js.map +1 -0
  84. package/dist/checks/meta-tags.d.ts.map +1 -1
  85. package/dist/checks/meta-tags.js +6 -5
  86. package/dist/checks/meta-tags.js.map +1 -1
  87. package/dist/checks/robots-parser.d.ts +110 -0
  88. package/dist/checks/robots-parser.d.ts.map +1 -0
  89. package/dist/checks/robots-parser.js +277 -0
  90. package/dist/checks/robots-parser.js.map +1 -0
  91. package/dist/checks/robots-txt.d.ts +2 -20
  92. package/dist/checks/robots-txt.d.ts.map +1 -1
  93. package/dist/checks/robots-txt.js +219 -120
  94. package/dist/checks/robots-txt.js.map +1 -1
  95. package/dist/checks/rsl.d.ts +0 -2
  96. package/dist/checks/rsl.d.ts.map +1 -1
  97. package/dist/checks/rsl.js +1 -11
  98. package/dist/checks/rsl.js.map +1 -1
  99. package/dist/checks/security-txt.d.ts.map +1 -1
  100. package/dist/checks/security-txt.js +0 -1
  101. package/dist/checks/security-txt.js.map +1 -1
  102. package/dist/checks/seo-basics.d.ts.map +1 -1
  103. package/dist/checks/seo-basics.js +0 -1
  104. package/dist/checks/seo-basics.js.map +1 -1
  105. package/dist/checks/sitemap.d.ts.map +1 -1
  106. package/dist/checks/sitemap.js +0 -1
  107. package/dist/checks/sitemap.js.map +1 -1
  108. package/dist/checks/structured-data.d.ts.map +1 -1
  109. package/dist/checks/structured-data.js +215 -4
  110. package/dist/checks/structured-data.js.map +1 -1
  111. package/dist/checks/structured-fields.d.ts +46 -0
  112. package/dist/checks/structured-fields.d.ts.map +1 -0
  113. package/dist/checks/structured-fields.js +112 -0
  114. package/dist/checks/structured-fields.js.map +1 -0
  115. package/dist/checks/surface.d.ts +59 -0
  116. package/dist/checks/surface.d.ts.map +1 -0
  117. package/dist/checks/surface.js +106 -0
  118. package/dist/checks/surface.js.map +1 -0
  119. package/dist/checks/tls-https.d.ts.map +1 -1
  120. package/dist/checks/tls-https.js +0 -1
  121. package/dist/checks/tls-https.js.map +1 -1
  122. package/dist/checks/usage-policy.d.ts +53 -0
  123. package/dist/checks/usage-policy.d.ts.map +1 -0
  124. package/dist/checks/usage-policy.js +339 -0
  125. package/dist/checks/usage-policy.js.map +1 -0
  126. package/dist/checks/utils.d.ts +25 -1
  127. package/dist/checks/utils.d.ts.map +1 -1
  128. package/dist/checks/utils.js +33 -1
  129. package/dist/checks/utils.js.map +1 -1
  130. package/dist/checks/waf.d.ts +75 -0
  131. package/dist/checks/waf.d.ts.map +1 -0
  132. package/dist/checks/waf.js +203 -0
  133. package/dist/checks/waf.js.map +1 -0
  134. package/dist/checks/webmcp.d.ts +55 -0
  135. package/dist/checks/webmcp.d.ts.map +1 -0
  136. package/dist/checks/webmcp.js +209 -0
  137. package/dist/checks/webmcp.js.map +1 -0
  138. package/dist/checks/well-known.d.ts +38 -0
  139. package/dist/checks/well-known.d.ts.map +1 -0
  140. package/dist/checks/well-known.js +202 -0
  141. package/dist/checks/well-known.js.map +1 -0
  142. package/dist/cli.d.ts +14 -0
  143. package/dist/cli.d.ts.map +1 -1
  144. package/dist/cli.js +124 -4
  145. package/dist/cli.js.map +1 -1
  146. package/dist/constants.d.ts +195 -14
  147. package/dist/constants.d.ts.map +1 -1
  148. package/dist/constants.js +598 -70
  149. package/dist/constants.js.map +1 -1
  150. package/dist/fetcher.d.ts.map +1 -1
  151. package/dist/fetcher.js +44 -14
  152. package/dist/fetcher.js.map +1 -1
  153. package/dist/guide-urls.js +1 -1
  154. package/dist/guide-urls.js.map +1 -1
  155. package/dist/index.d.ts +2 -1
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +1 -0
  158. package/dist/index.js.map +1 -1
  159. package/dist/orchestrator.d.ts.map +1 -1
  160. package/dist/orchestrator.js +5 -1
  161. package/dist/orchestrator.js.map +1 -1
  162. package/dist/reporter/html.d.ts +9 -0
  163. package/dist/reporter/html.d.ts.map +1 -1
  164. package/dist/reporter/html.js +49 -11
  165. package/dist/reporter/html.js.map +1 -1
  166. package/dist/reporter/markdown.d.ts.map +1 -1
  167. package/dist/reporter/markdown.js +36 -6
  168. package/dist/reporter/markdown.js.map +1 -1
  169. package/dist/reporter/terminal.d.ts.map +1 -1
  170. package/dist/reporter/terminal.js +36 -1
  171. package/dist/reporter/terminal.js.map +1 -1
  172. package/dist/scorer.d.ts +10 -0
  173. package/dist/scorer.d.ts.map +1 -1
  174. package/dist/scorer.js +22 -5
  175. package/dist/scorer.js.map +1 -1
  176. package/dist/types.d.ts +90 -3
  177. package/dist/types.d.ts.map +1 -1
  178. package/docs/architecture.md +27 -11
  179. package/docs/checks.md +285 -52
  180. package/docs/cli.md +36 -0
  181. package/docs/concepts.md +27 -13
  182. package/docs/faq.md +18 -6
  183. package/docs/getting-started.md +20 -13
  184. package/docs/roadmap.md +367 -0
  185. package/package.json +13 -5
  186. package/dist/checks/agent-json.d.ts +0 -14
  187. package/dist/checks/agent-json.d.ts.map +0 -1
  188. package/dist/checks/agent-json.js +0 -167
  189. package/dist/checks/agent-json.js.map +0 -1
  190. package/dist/checks/mcp.d.ts +0 -4
  191. package/dist/checks/mcp.d.ts.map +0 -1
  192. package/dist/checks/mcp.js +0 -162
  193. package/dist/checks/mcp.js.map +0 -1
  194. package/dist/checks/openapi.d.ts +0 -4
  195. package/dist/checks/openapi.d.ts.map +0 -1
  196. package/dist/checks/openapi.js +0 -121
  197. package/dist/checks/openapi.js.map +0 -1
  198. package/dist/checks/well-known-ai.d.ts +0 -17
  199. package/dist/checks/well-known-ai.d.ts.map +0 -1
  200. package/dist/checks/well-known-ai.js +0 -123
  201. package/dist/checks/well-known-ai.js.map +0 -1
@@ -1,18 +1,50 @@
1
- import { CORE_AI_CRAWLERS } from '../constants.js';
1
+ import { PROBEABLE_CORE_CRAWLERS, crawlerInfo } from '../constants.js';
2
2
  import { guideUrl } from '../guide-urls.js';
3
3
  import { buildResult } from './utils.js';
4
- import { extractVisibleText } from './html-utils.js';
5
- import { parseUserAgents } from './robots-txt.js';
4
+ import { extractVisibleText, findJsonLdBlocks } from './html-utils.js';
5
+ import { parseUserAgents, intentBlocked } from './robots-parser.js';
6
+ import { classifyResponse, INCONCLUSIVE_CAVEAT } from './waf.js';
7
+ /**
8
+ * "agent-access" — the gap between what robots.txt permits and what the server
9
+ * actually does.
10
+ *
11
+ * The failure this catches is invisible from the inside: robots.txt says
12
+ * `Allow: /` for GPTBot, and the WAF returns 403 to anything whose user agent
13
+ * says GPTBot. The operator sees a permissive robots.txt and assumes the site
14
+ * is reachable. Cloudflare's "Block AI Crawlers" toggle produces exactly this,
15
+ * and so does a hand-written rule that outlived its reason.
16
+ *
17
+ * The check probes the homepage once per core crawler and compares the result
18
+ * against the default-user-agent baseline. Since 3.7 it classifies *how* a
19
+ * request was turned away, because the remedies are completely different: a
20
+ * JavaScript challenge needs a bot-management exception, a hard 403 needs a
21
+ * firewall rule change, a 402 is a deliberate price, and a signature demand is
22
+ * the site working as designed.
23
+ *
24
+ * Honesty constraint: this probe is unsigned and comes from the auditor's own
25
+ * network. An edge that verifies crawlers by IP range or Web Bot Auth signature
26
+ * will reject it while admitting the genuine crawler. Those outcomes are scored
27
+ * as inconclusive and labelled as such, never as "blocks AI crawlers".
28
+ */
6
29
  export const meta = {
7
30
  id: 'agent-access',
8
31
  name: 'Agent Access',
9
32
  description: 'Checks that AI crawler user-agents are not blocked or served reduced content (cloaking)',
10
- weight: 0, // Informational in 3.x — will gain weight in v4.0 (score-affecting changes are treated as breaking).
33
+ category: 'access',
11
34
  };
12
35
  /** Content below this fraction of the baseline visible text counts as "reduced". */
13
36
  const REDUCED_CONTENT_RATIO = 0.5;
14
37
  /** Baselines with less visible text than this are too small for meaningful content comparison. */
15
38
  const MIN_BASELINE_TEXT = 200;
39
+ /** Credit each outcome contributes to the access score. */
40
+ const OUTCOME_CREDIT = {
41
+ ok: 1,
42
+ 'blocked-consistent': 1,
43
+ conditional: 1,
44
+ inconclusive: 0.75,
45
+ reduced: 0.5,
46
+ blocked: 0,
47
+ };
16
48
  /**
17
49
  * Build a realistic crawler User-Agent for a given bot token. WAF and
18
50
  * bot-management rules match on the token substring, which is what we need
@@ -21,16 +53,31 @@ const MIN_BASELINE_TEXT = 200;
21
53
  export function crawlerUserAgent(token) {
22
54
  return `Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ${token}/1.0)`;
23
55
  }
24
- /**
25
- * Whether robots.txt expresses the intent to block this crawler: an explicit
26
- * full Disallow for it, or a full wildcard Disallow with no explicit entry.
27
- */
28
- export function intentBlocked(entries, crawler) {
29
- const explicit = entries.find((e) => e.name.toLowerCase() === crawler.toLowerCase());
30
- if (explicit)
31
- return explicit.disallowed;
32
- const wildcard = entries.find((e) => e.name === '*');
33
- return wildcard?.disallowed ?? false;
56
+ // `intentBlocked` moved to robots-parser.ts in 3.7; re-exported for compatibility.
57
+ export { intentBlocked } from './robots-parser.js';
58
+ function shapeOf(html) {
59
+ const title = html.match(/<title\b[^>]*>([\s\S]*?)<\/title>/i);
60
+ const h1 = html.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i);
61
+ return {
62
+ textLength: extractVisibleText(html).length,
63
+ title: title ? extractVisibleText(title[1]) : null,
64
+ h1: h1 ? extractVisibleText(h1[1]) : null,
65
+ jsonLdBlocks: findJsonLdBlocks(html).length,
66
+ };
67
+ }
68
+ /** Differences between a crawler's page and the baseline, in reader-facing terms. */
69
+ function shapeDiff(baseline, actual) {
70
+ const diffs = [];
71
+ if (baseline.title !== null && actual.title !== baseline.title) {
72
+ diffs.push(`title differs ("${actual.title ?? 'none'}" vs "${baseline.title}")`);
73
+ }
74
+ if (baseline.h1 !== null && actual.h1 !== baseline.h1) {
75
+ diffs.push(`h1 differs ("${actual.h1 ?? 'none'}" vs "${baseline.h1}")`);
76
+ }
77
+ if (baseline.jsonLdBlocks > 0 && actual.jsonLdBlocks < baseline.jsonLdBlocks) {
78
+ diffs.push(`${baseline.jsonLdBlocks - actual.jsonLdBlocks} JSON-LD block(s) missing`);
79
+ }
80
+ return diffs;
34
81
  }
35
82
  export default async function check(ctx) {
36
83
  const start = performance.now();
@@ -45,66 +92,165 @@ export default async function check(ctx) {
45
92
  });
46
93
  return buildResult(meta, 0, findings, start);
47
94
  }
48
- const baselineText = extractVisibleText(baseline.body).length;
95
+ const baselineShape = shapeOf(baseline.body);
96
+ const baselineText = baselineShape.textLength;
49
97
  const robotsRes = await ctx.fetch(`${ctx.url}/robots.txt`);
50
98
  const robotsEntries = robotsRes.ok ? parseUserAgents(robotsRes.body) : [];
51
99
  const outcomes = new Map();
52
- for (const crawler of CORE_AI_CRAWLERS) {
100
+ for (const crawler of PROBEABLE_CORE_CRAWLERS) {
53
101
  const res = await ctx.fetch(ctx.url, { headers: { 'User-Agent': crawlerUserAgent(crawler) } });
102
+ const cls = classifyResponse(res);
54
103
  const blockedByRobots = intentBlocked(robotsEntries, crawler);
55
- if (!res.ok) {
56
- outcomes.set(crawler, blockedByRobots ? 'blocked-consistent' : 'blocked');
57
- const detail = `HTTP ${res.status || 'network error'} for User-Agent containing "${crawler}"`;
104
+ if (cls.kind === 'ok') {
105
+ const shape = shapeOf(res.body);
106
+ const diffs = shapeDiff(baselineShape, shape);
107
+ const textDropped = baselineText >= MIN_BASELINE_TEXT && shape.textLength < baselineText * REDUCED_CONTENT_RATIO;
108
+ if (textDropped || diffs.length > 0) {
109
+ outcomes.set(crawler, 'reduced');
110
+ findings.push({
111
+ status: 'warn',
112
+ message: `${crawler} receives a different page than a regular client`,
113
+ detail: [textDropped ? `${shape.textLength} vs ${baselineText} chars of visible text` : null, ...diffs]
114
+ .filter(Boolean)
115
+ .join('; '),
116
+ hint: 'The server returns 200 but serves this crawler different content — often an interstitial, a consent ' +
117
+ 'wall, or conditional rendering. Agents index exactly what they receive, so the version they see is the ' +
118
+ 'version that gets cited.',
119
+ learnMoreUrl: guideUrl(meta.id, 'reduced-content'),
120
+ });
121
+ }
122
+ else {
123
+ outcomes.set(crawler, 'ok');
124
+ }
125
+ continue;
126
+ }
127
+ outcomes.set(crawler, recordTurnedAway(crawler, cls, blockedByRobots, robotsRes.ok, findings));
128
+ }
129
+ const okCount = [...outcomes.values()].filter((o) => o === 'ok').length;
130
+ if (okCount === PROBEABLE_CORE_CRAWLERS.length) {
131
+ findings.unshift({
132
+ status: 'pass',
133
+ message: `All ${PROBEABLE_CORE_CRAWLERS.length} core AI crawler user-agents receive the same page as a regular client`,
134
+ });
135
+ }
136
+ const inconclusive = [...outcomes.entries()].filter(([, o]) => o === 'inconclusive');
137
+ if (inconclusive.length > 0) {
138
+ findings.push({
139
+ status: 'warn',
140
+ message: `${inconclusive.length} crawler probe(s) could not be settled from outside`,
141
+ detail: inconclusive.map(([name]) => name).join(', '),
142
+ hint: INCONCLUSIVE_CAVEAT,
143
+ learnMoreUrl: guideUrl(meta.id, 'inconclusive-probe'),
144
+ });
145
+ }
146
+ const credit = [...outcomes.values()].reduce((acc, o) => acc + OUTCOME_CREDIT[o], 0);
147
+ const score = Math.round((credit / PROBEABLE_CORE_CRAWLERS.length) * 100);
148
+ return buildResult(meta, score, findings, start);
149
+ }
150
+ /**
151
+ * Turn a non-200 crawler response into a finding and an outcome. The
152
+ * classification decides both the wording and the credit: a challenge is not a
153
+ * block, a price is not a refusal, and a signature demand is a site working as
154
+ * designed.
155
+ */
156
+ function recordTurnedAway(crawler, cls, blockedByRobots, robotsAvailable, findings) {
157
+ const info = crawlerInfo(crawler);
158
+ const evidence = cls.evidence.length > 0 ? cls.evidence.join('; ') : cls.label;
159
+ switch (cls.kind) {
160
+ case 'challenge':
161
+ findings.push({
162
+ status: 'warn',
163
+ message: `${crawler} receives a ${cls.vendor ?? 'JavaScript'} challenge instead of the page`,
164
+ detail: evidence,
165
+ hint: 'Challenge pages require running JavaScript. Crawlers that only fetch HTML — which is most of them — never ' +
166
+ 'get past one, so the page is effectively unavailable to them even though nothing is "blocked". ' +
167
+ 'Add a bot-management exception for verified AI crawlers. ' +
168
+ INCONCLUSIVE_CAVEAT,
169
+ learnMoreUrl: guideUrl(meta.id, 'challenge-page'),
170
+ });
171
+ return 'inconclusive';
172
+ case 'needs-signature':
173
+ findings.push({
174
+ status: 'pass',
175
+ message: `${crawler} must present a Web Bot Auth signature`,
176
+ detail: evidence,
177
+ hint: 'The origin asks unverified clients to re-request with an HTTP Message Signature. Vendors that sign their ' +
178
+ 'requests will pass; this probe does not sign, so it cannot confirm the outcome for the real crawler.',
179
+ learnMoreUrl: guideUrl(meta.id, 'needs-signature'),
180
+ });
181
+ return 'inconclusive';
182
+ case 'paywall':
183
+ findings.push({
184
+ status: 'pass',
185
+ message: `${crawler} is offered priced access${cls.price ? ` at ${cls.price}` : ''}`,
186
+ detail: evidence,
187
+ hint: 'Content is monetised for crawlers rather than blocked. Crawlers that support the payment flow can still reach it.',
188
+ learnMoreUrl: guideUrl(meta.id, 'pay-per-crawl'),
189
+ });
190
+ return 'conditional';
191
+ case 'license-required':
192
+ findings.push({
193
+ status: 'pass',
194
+ message: `${crawler} is asked to obtain a licence before use`,
195
+ detail: evidence,
196
+ hint: 'The origin implements the RSL Open Licensing Protocol. Clients that negotiate a licence can reach the content.',
197
+ learnMoreUrl: guideUrl(meta.id, 'license-required'),
198
+ });
199
+ return 'conditional';
200
+ case 'rate-limited':
201
+ findings.push({
202
+ status: 'warn',
203
+ message: `${crawler} is rate limited on a single request`,
204
+ detail: evidence,
205
+ hint: 'One probe should not hit a rate limit. A limit this tight will stall any crawl. Always answer 429 with a ' +
206
+ 'Retry-After header so well-behaved crawlers back off correctly instead of giving up.',
207
+ learnMoreUrl: guideUrl(meta.id, 'rate-limited'),
208
+ });
209
+ return 'inconclusive';
210
+ case 'server-error':
211
+ case 'network-error':
212
+ findings.push({
213
+ status: 'warn',
214
+ message: `${crawler} probe failed: ${cls.label}`,
215
+ detail: evidence,
216
+ hint: 'The request did not complete, so access for this crawler is unknown. Re-run the audit; if it persists, check origin health for this user agent.',
217
+ learnMoreUrl: guideUrl(meta.id, 'probe-failed'),
218
+ });
219
+ return 'inconclusive';
220
+ default: {
221
+ // A plain refusal. Whether it is a finding depends on robots.txt intent.
58
222
  if (blockedByRobots) {
59
223
  findings.push({
60
224
  status: 'pass',
61
225
  message: `${crawler} blocked at the server — consistent with its robots.txt Disallow`,
62
- detail,
226
+ detail: evidence,
63
227
  });
228
+ return 'blocked-consistent';
64
229
  }
65
- else {
230
+ if (cls.inconclusive) {
66
231
  findings.push({
67
232
  status: 'warn',
68
- message: `${crawler} is ${robotsRes.ok ? 'allowed in robots.txt' : 'not restricted'} but its User-Agent is blocked`,
69
- detail,
70
- hint: 'Your WAF or bot management rejects this crawler token even though robots.txt permits it — the block is invisible to you but fatal for the agent. ' +
71
- 'Check your firewall rules and AI-bot toggles (e.g., Cloudflare "Block AI Crawlers"). ' +
72
- 'Note: if your WAF verifies bots cryptographically (Web Bot Auth / verified-bots lists), the real crawler may still pass while this unverified probe is rejected — verify against your WAF logs.',
233
+ message: `${crawler} is refused by ${cls.vendor ?? 'the origin'} while robots.txt permits it`,
234
+ detail: evidence,
235
+ hint: (info ? `${info.impact} ` : '') +
236
+ 'This edge verifies crawlers by IP range or signature, so the refusal may be correct anti-spoofing rather ' +
237
+ 'than a policy block. ' +
238
+ INCONCLUSIVE_CAVEAT,
73
239
  learnMoreUrl: guideUrl(meta.id, 'blocked-crawler'),
74
240
  });
241
+ return 'inconclusive';
75
242
  }
76
- continue;
77
- }
78
- const text = extractVisibleText(res.body).length;
79
- if (baselineText >= MIN_BASELINE_TEXT && text < baselineText * REDUCED_CONTENT_RATIO) {
80
- outcomes.set(crawler, 'reduced');
81
243
  findings.push({
82
244
  status: 'warn',
83
- message: `${crawler} receives reduced content (${text} vs ${baselineText} chars of visible text)`,
84
- hint: 'The server returns 200 but serves this crawler substantially less content than a regular client — ' +
85
- 'often an interstitial, a challenge page, or conditional rendering. Agents index what they receive.',
86
- learnMoreUrl: guideUrl(meta.id, 'reduced-content'),
245
+ message: `${crawler} is ${robotsAvailable ? 'allowed in robots.txt' : 'not restricted'} but its User-Agent is refused`,
246
+ detail: evidence,
247
+ hint: (info ? `${info.impact} ` : '') +
248
+ 'Your firewall rejects this crawler token even though robots.txt permits it — the block is invisible to you ' +
249
+ 'but fatal for the agent. Check your firewall rules and AI-bot toggles (for example Cloudflare "Block AI Crawlers").',
250
+ learnMoreUrl: guideUrl(meta.id, 'blocked-crawler'),
87
251
  });
88
- }
89
- else {
90
- outcomes.set(crawler, 'ok');
252
+ return 'blocked';
91
253
  }
92
254
  }
93
- const okCount = [...outcomes.values()].filter((o) => o === 'ok').length;
94
- if (okCount === CORE_AI_CRAWLERS.length) {
95
- findings.unshift({
96
- status: 'pass',
97
- message: `All ${CORE_AI_CRAWLERS.length} core AI crawler user-agents receive equivalent responses`,
98
- });
99
- }
100
- const credit = [...outcomes.values()].reduce((acc, o) => {
101
- if (o === 'ok' || o === 'blocked-consistent')
102
- return acc + 1;
103
- if (o === 'reduced')
104
- return acc + 0.5;
105
- return acc;
106
- }, 0);
107
- const score = Math.round((credit / CORE_AI_CRAWLERS.length) * 100);
108
- return buildResult(meta, score, findings, start);
109
255
  }
110
256
  //# sourceMappingURL=agent-access.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-access.js","sourceRoot":"","sources":["../../src/checks/agent-access.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-access.js","sourceRoot":"","sources":["../../src/checks/agent-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAsB,MAAM,UAAU,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,IAAI,GAAc;IAC7B,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,yFAAyF;IACtG,QAAQ,EAAE,QAAQ;CACnB,CAAC;AAEF,oFAAoF;AACpF,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,kGAAkG;AAClG,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAgB9B,2DAA2D;AAC3D,MAAM,cAAc,GAA4B;IAC9C,EAAE,EAAE,CAAC;IACL,oBAAoB,EAAE,CAAC;IACvB,WAAW,EAAE,CAAC;IACd,YAAY,EAAE,IAAI;IAClB,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,CAAC;CACX,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,kEAAkE,KAAK,OAAO,CAAC;AACxF,CAAC;AAED,mFAAmF;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUnD,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAC/D,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACtD,OAAO;QACL,UAAU,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC,MAAM;QAC3C,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;QAClD,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;QACzC,YAAY,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,MAAM;KAC5C,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,SAAS,SAAS,CAAC,QAAmB,EAAE,MAAiB;IACvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,KAAK,IAAI,MAAM,SAAS,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,QAAQ,CAAC,EAAE,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,EAAE,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,EAAE,IAAI,MAAM,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,QAAQ,CAAC,YAAY,GAAG,CAAC,IAAI,MAAM,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC7E,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,2BAA2B,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,KAAK,CAAC,GAAiB;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,kEAAkE;YAC3E,MAAM,EAAE,QAAQ,QAAQ,CAAC,MAAM,IAAI,eAAe,EAAE;YACpD,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,sBAAsB,CAAC;SACxD,CAAC,CAAC;QACH,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC;IAE9C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC;IAC3D,MAAM,aAAa,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE5C,KAAK,MAAM,OAAO,IAAI,uBAAuB,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/F,MAAM,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAE9D,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YAC9C,MAAM,WAAW,GAAG,YAAY,IAAI,iBAAiB,IAAI,KAAK,CAAC,UAAU,GAAG,YAAY,GAAG,qBAAqB,CAAC;YAEjH,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,GAAG,OAAO,kDAAkD;oBACrE,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,OAAO,YAAY,wBAAwB,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC;yBACpG,MAAM,CAAC,OAAO,CAAC;yBACf,IAAI,CAAC,IAAI,CAAC;oBACb,IAAI,EACF,sGAAsG;wBACtG,yGAAyG;wBACzG,0BAA0B;oBAC5B,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC;iBACnD,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC9B,CAAC;YACD,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjG,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;IACxE,IAAI,OAAO,KAAK,uBAAuB,CAAC,MAAM,EAAE,CAAC;QAC/C,QAAQ,CAAC,OAAO,CAAC;YACf,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,OAAO,uBAAuB,CAAC,MAAM,wEAAwE;SACvH,CAAC,CAAC;IACL,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC;IACrF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,GAAG,YAAY,CAAC,MAAM,qDAAqD;YACpF,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACrD,IAAI,EAAE,mBAAmB;YACzB,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,oBAAoB,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;IAE1E,OAAO,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CACvB,OAAe,EACf,GAAkB,EAClB,eAAwB,EACxB,eAAwB,EACxB,QAAmB;IAEnB,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IAE/E,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,WAAW;YACd,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,eAAe,GAAG,CAAC,MAAM,IAAI,YAAY,gCAAgC;gBAC5F,MAAM,EAAE,QAAQ;gBAChB,IAAI,EACF,4GAA4G;oBAC5G,iGAAiG;oBACjG,2DAA2D;oBAC3D,mBAAmB;gBACrB,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,gBAAgB,CAAC;aAClD,CAAC,CAAC;YACH,OAAO,cAAc,CAAC;QAExB,KAAK,iBAAiB;YACpB,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,wCAAwC;gBAC3D,MAAM,EAAE,QAAQ;gBAChB,IAAI,EACF,2GAA2G;oBAC3G,sGAAsG;gBACxG,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC;aACnD,CAAC,CAAC;YACH,OAAO,cAAc,CAAC;QAExB,KAAK,SAAS;YACZ,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,4BAA4B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBACpF,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,mHAAmH;gBACzH,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,eAAe,CAAC;aACjD,CAAC,CAAC;YACH,OAAO,aAAa,CAAC;QAEvB,KAAK,kBAAkB;YACrB,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,0CAA0C;gBAC7D,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,gHAAgH;gBACtH,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,kBAAkB,CAAC;aACpD,CAAC,CAAC;YACH,OAAO,aAAa,CAAC;QAEvB,KAAK,cAAc;YACjB,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,sCAAsC;gBACzD,MAAM,EAAE,QAAQ;gBAChB,IAAI,EACF,2GAA2G;oBAC3G,sFAAsF;gBACxF,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC;aAChD,CAAC,CAAC;YACH,OAAO,cAAc,CAAC;QAExB,KAAK,cAAc,CAAC;QACpB,KAAK,eAAe;YAClB,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,kBAAkB,GAAG,CAAC,KAAK,EAAE;gBAChD,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,iJAAiJ;gBACvJ,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC;aAChD,CAAC,CAAC;YACH,OAAO,cAAc,CAAC;QAExB,OAAO,CAAC,CAAC,CAAC;YACR,yEAAyE;YACzE,IAAI,eAAe,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,GAAG,OAAO,kEAAkE;oBACrF,MAAM,EAAE,QAAQ;iBACjB,CAAC,CAAC;gBACH,OAAO,oBAAoB,CAAC;YAC9B,CAAC;YAED,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;gBACrB,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,GAAG,OAAO,kBAAkB,GAAG,CAAC,MAAM,IAAI,YAAY,8BAA8B;oBAC7F,MAAM,EAAE,QAAQ;oBAChB,IAAI,EACF,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC/B,2GAA2G;wBAC3G,uBAAuB;wBACvB,mBAAmB;oBACrB,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC;iBACnD,CAAC,CAAC;gBACH,OAAO,cAAc,CAAC;YACxB,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,GAAG,OAAO,OAAO,eAAe,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,gBAAgB,gCAAgC;gBACtH,MAAM,EAAE,QAAQ;gBAChB,IAAI,EACF,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/B,6GAA6G;oBAC7G,qHAAqH;gBACvH,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC;aACnD,CAAC,CAAC;YACH,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { CheckContext, CheckResult, CheckMeta } from '../types.js';
2
+ /**
3
+ * "agent-card" — the A2A (Agent2Agent) Agent Card.
4
+ *
5
+ * Two things changed since this check was written, and both matter:
6
+ *
7
+ * 1. **The path moved.** A2A v0.3.0 (2025-07-30) relocated the card from
8
+ * `/.well-known/agent.json` to `/.well-known/agent-card.json`, which is now
9
+ * an IANA-registered well-known URI. A site serving only the old path is
10
+ * invisible to a current client.
11
+ *
12
+ * 2. **The shape changed.** A2A 1.0 (2026-03-12, breaking) folded the
13
+ * top-level `url`, `protocolVersion`, `preferredTransport` and
14
+ * `additionalInterfaces` fields into a single `supportedInterfaces[]` array,
15
+ * and renamed `supportsAuthenticatedExtendedCard` to
16
+ * `capabilities.extendedAgentCard`. Most deployed cards are still 0.3-shaped.
17
+ *
18
+ * Validating a 1.0 card against 0.3 rules (or the reverse) produces nonsense,
19
+ * so the check detects which generation a card belongs to from its own
20
+ * structure and applies the matching rules. `authentication`, dropped back in
21
+ * 0.2.x in favour of `securitySchemes`, is flagged wherever it appears.
22
+ *
23
+ * Spec: https://a2a-protocol.org/latest/specification/
24
+ * Normative field list: specification/a2a.proto in github.com/a2aproject/A2A
25
+ */
26
+ export declare const meta: CheckMeta;
27
+ /** Which generation of the A2A spec a card was written against. */
28
+ type Generation = 'v1' | 'v0.3' | 'unknown';
29
+ /**
30
+ * Detect the card generation from its own structure rather than from a version
31
+ * field, because 0.3 cards state `protocolVersion` at the top level and 1.0
32
+ * cards state it per interface.
33
+ */
34
+ export declare function detectGeneration(data: Record<string, unknown>): Generation;
35
+ export default function check(ctx: CheckContext): Promise<CheckResult>;
36
+ export {};
37
+ //# sourceMappingURL=agent-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-card.d.ts","sourceRoot":"","sources":["../../src/checks/agent-card.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAKhG;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,EAAE,SAMlB,CAAC;AAKF,mEAAmE;AACnE,KAAK,UAAU,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAI1E;AAqBD,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAkF3E"}