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
@@ -0,0 +1,352 @@
1
+ import { A2A_MEDIA_TYPE, AGENT_CARD_REQUIRED_V03, AGENT_CARD_REQUIRED_V1, A2A_PROTOCOL_BINDINGS, } from '../constants.js';
2
+ import { guideUrl } from '../guide-urls.js';
3
+ import { buildResult, checkContentType, isHtmlDocument, notApplicable } from './utils.js';
4
+ import { hasAgentSurface } from './surface.js';
5
+ import { WELL_KNOWN, standingNote } from './well-known.js';
6
+ /**
7
+ * "agent-card" — the A2A (Agent2Agent) Agent Card.
8
+ *
9
+ * Two things changed since this check was written, and both matter:
10
+ *
11
+ * 1. **The path moved.** A2A v0.3.0 (2025-07-30) relocated the card from
12
+ * `/.well-known/agent.json` to `/.well-known/agent-card.json`, which is now
13
+ * an IANA-registered well-known URI. A site serving only the old path is
14
+ * invisible to a current client.
15
+ *
16
+ * 2. **The shape changed.** A2A 1.0 (2026-03-12, breaking) folded the
17
+ * top-level `url`, `protocolVersion`, `preferredTransport` and
18
+ * `additionalInterfaces` fields into a single `supportedInterfaces[]` array,
19
+ * and renamed `supportsAuthenticatedExtendedCard` to
20
+ * `capabilities.extendedAgentCard`. Most deployed cards are still 0.3-shaped.
21
+ *
22
+ * Validating a 1.0 card against 0.3 rules (or the reverse) produces nonsense,
23
+ * so the check detects which generation a card belongs to from its own
24
+ * structure and applies the matching rules. `authentication`, dropped back in
25
+ * 0.2.x in favour of `securitySchemes`, is flagged wherever it appears.
26
+ *
27
+ * Spec: https://a2a-protocol.org/latest/specification/
28
+ * Normative field list: specification/a2a.proto in github.com/a2aproject/A2A
29
+ */
30
+ export const meta = {
31
+ id: 'agent-card',
32
+ name: 'Agent Card (A2A)',
33
+ description: 'Checks the A2A Agent Card at /.well-known/agent-card.json',
34
+ category: 'protocols',
35
+ aliases: ['agent-json'],
36
+ };
37
+ const CANONICAL_PATH = '/.well-known/agent-card.json';
38
+ const LEGACY_PATH = '/.well-known/agent.json';
39
+ /**
40
+ * Detect the card generation from its own structure rather than from a version
41
+ * field, because 0.3 cards state `protocolVersion` at the top level and 1.0
42
+ * cards state it per interface.
43
+ */
44
+ export function detectGeneration(data) {
45
+ if (Array.isArray(data.supportedInterfaces))
46
+ return 'v1';
47
+ if (typeof data.url === 'string' || typeof data.protocolVersion === 'string')
48
+ return 'v0.3';
49
+ return 'unknown';
50
+ }
51
+ /**
52
+ * Try the registered path first, then the pre-0.3 one. An HTML response counts
53
+ * as absent: an SPA catch-all returns its index shell for every unknown path,
54
+ * and reporting that as a malformed card would send an operator looking for a
55
+ * bug in a file they never wrote.
56
+ */
57
+ async function locateCard(ctx) {
58
+ for (const path of [CANONICAL_PATH, LEGACY_PATH]) {
59
+ const res = await ctx.fetch(`${ctx.url}${path}`);
60
+ if (res.ok && res.body.trim().length > 0 && !isHtmlDocument(res.body))
61
+ return { res, path };
62
+ }
63
+ return null;
64
+ }
65
+ export default async function check(ctx) {
66
+ const start = performance.now();
67
+ const findings = [];
68
+ let score = 100;
69
+ const found = await locateCard(ctx);
70
+ if (found === null) {
71
+ // An Agent Card describes capabilities another agent can invoke. Most sites
72
+ // have none, and asking a restaurant to publish one is noise — so the check
73
+ // only applies where the site is already agent-facing.
74
+ const surface = await hasAgentSurface(ctx);
75
+ if (!surface.found) {
76
+ findings.push({
77
+ status: 'pass',
78
+ message: 'No agent-facing surface — an Agent Card does not apply to this site',
79
+ detail: 'No API, MCP server or existing card was found. An Agent Card advertises capabilities another agent can ' +
80
+ 'invoke; a site that offers none has nothing to put in it. Run with --profile agent to audit as though it did.',
81
+ });
82
+ return notApplicable(meta, findings, start);
83
+ }
84
+ findings.push({
85
+ status: 'fail',
86
+ message: `${CANONICAL_PATH} not found`,
87
+ detail: `Site is agent-facing (${surface.reason}). Also tried the pre-0.3 path ${LEGACY_PATH}. ${standingNote(CANONICAL_PATH) ?? ''}`.trim(),
88
+ hint: 'This site offers something an agent could call, but nothing tells an agent what. Publish an A2A Agent Card ' +
89
+ 'at /.well-known/agent-card.json. A minimal 1.0 card needs name, description, version, capabilities, ' +
90
+ `supportedInterfaces, defaultInputModes, defaultOutputModes and skills. Spec: ${WELL_KNOWN[CANONICAL_PATH].specUrl}`,
91
+ learnMoreUrl: guideUrl(meta.id, 'not-found'),
92
+ });
93
+ return buildResult(meta, 0, findings, start);
94
+ }
95
+ const { res, path } = found;
96
+ if (path === LEGACY_PATH) {
97
+ findings.push({
98
+ status: 'warn',
99
+ message: `Agent Card served only from the pre-0.3 path ${LEGACY_PATH}`,
100
+ detail: standingNote(LEGACY_PATH),
101
+ hint: 'A2A moved the card to /.well-known/agent-card.json in v0.3.0 (2025-07-30), and that path is the one ' +
102
+ 'registered with IANA. Serve the card there; keep the old path as a redirect if existing clients depend on it.',
103
+ learnMoreUrl: guideUrl(meta.id, 'legacy-path'),
104
+ });
105
+ }
106
+ else {
107
+ findings.push({ status: 'pass', message: `${CANONICAL_PATH} exists` });
108
+ }
109
+ const ctFinding = checkContentType(res, ['application/json', A2A_MEDIA_TYPE], {
110
+ checkId: meta.id,
111
+ resourceLabel: path,
112
+ anchor: 'wrong-content-type',
113
+ });
114
+ if (ctFinding) {
115
+ findings.push(ctFinding);
116
+ score -= 5;
117
+ }
118
+ let data;
119
+ try {
120
+ data = JSON.parse(res.body);
121
+ }
122
+ catch {
123
+ findings.push({
124
+ status: 'fail',
125
+ message: 'Invalid JSON',
126
+ hint: 'Fix the JSON syntax in your Agent Card. Validate it with a JSON linter.',
127
+ learnMoreUrl: guideUrl(meta.id, 'invalid-json'),
128
+ });
129
+ return buildResult(meta, 10, findings, start);
130
+ }
131
+ findings.push({ status: 'pass', message: 'Valid JSON' });
132
+ const generation = detectGeneration(data);
133
+ score = validateGeneration(generation, data, ctx, findings, score);
134
+ score = validateCommon(data, findings, score);
135
+ return buildResult(meta, score, findings, start);
136
+ }
137
+ /** Apply the field rules of whichever spec generation this card was written against. */
138
+ function validateGeneration(generation, data, ctx, findings, score) {
139
+ if (generation === 'unknown') {
140
+ findings.push({
141
+ status: 'fail',
142
+ message: 'Card shape matches neither A2A 1.0 nor 0.3',
143
+ detail: 'Expected either supportedInterfaces[] (1.0) or a top-level url + protocolVersion (0.3).',
144
+ hint: 'Rebuild the card against the current specification. A 1.0 card declares every endpoint inside supportedInterfaces[].',
145
+ learnMoreUrl: guideUrl(meta.id, 'unknown-generation'),
146
+ });
147
+ return score - 30;
148
+ }
149
+ const required = generation === 'v1' ? AGENT_CARD_REQUIRED_V1 : AGENT_CARD_REQUIRED_V03;
150
+ findings.push({
151
+ status: 'pass',
152
+ message: generation === 'v1' ? 'Card follows A2A 1.0 (supportedInterfaces)' : 'Card follows A2A 0.3 (top-level url)',
153
+ ...(generation === 'v0.3'
154
+ ? {
155
+ detail: 'A2A 1.0 (2026-03-12) replaced the top-level url, protocolVersion, preferredTransport and ' +
156
+ 'additionalInterfaces fields with a single supportedInterfaces[] array.',
157
+ }
158
+ : {}),
159
+ });
160
+ for (const field of required) {
161
+ if (data[field] !== undefined && data[field] !== null) {
162
+ findings.push({ status: 'pass', message: `Required field "${field}" present` });
163
+ }
164
+ else {
165
+ findings.push({
166
+ status: 'fail',
167
+ message: `Required field "${field}" missing`,
168
+ hint: `Add "${field}" to your Agent Card. A2A ${generation === 'v1' ? '1.0' : '0.3'} requires it.`,
169
+ learnMoreUrl: guideUrl(meta.id, 'missing-field'),
170
+ });
171
+ score -= 15;
172
+ }
173
+ }
174
+ return generation === 'v1'
175
+ ? validateInterfaces(data, ctx, findings, score)
176
+ : validateLegacyUrl(data, ctx, findings, score);
177
+ }
178
+ /** A2A 1.0: every entry in `supportedInterfaces[]` needs url, binding and version. */
179
+ function validateInterfaces(data, ctx, findings, score) {
180
+ const interfaces = Array.isArray(data.supportedInterfaces)
181
+ ? data.supportedInterfaces
182
+ : [];
183
+ if (interfaces.length === 0) {
184
+ findings.push({
185
+ status: 'fail',
186
+ message: 'supportedInterfaces[] is empty',
187
+ hint: 'Declare at least one interface: { "url": "https://…", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" }.',
188
+ learnMoreUrl: guideUrl(meta.id, 'empty-interfaces'),
189
+ });
190
+ return score - 15;
191
+ }
192
+ const incomplete = interfaces.filter((i) => !i.url || !i.protocolBinding || !i.protocolVersion);
193
+ if (incomplete.length > 0) {
194
+ findings.push({
195
+ status: 'warn',
196
+ message: `${incomplete.length}/${interfaces.length} interface(s) missing url, protocolBinding or protocolVersion`,
197
+ hint: 'Each entry in supportedInterfaces[] must declare all three so a client knows where to connect and how to speak.',
198
+ learnMoreUrl: guideUrl(meta.id, 'incomplete-interface'),
199
+ });
200
+ score -= 10;
201
+ }
202
+ else {
203
+ findings.push({ status: 'pass', message: `${interfaces.length} interface(s) fully declared` });
204
+ }
205
+ const badBinding = interfaces
206
+ .map((i) => String(i.protocolBinding ?? ''))
207
+ .filter((b) => b !== '' && !A2A_PROTOCOL_BINDINGS.includes(b));
208
+ if (badBinding.length > 0) {
209
+ findings.push({
210
+ status: 'warn',
211
+ message: 'Interface with an unrecognised protocolBinding',
212
+ detail: badBinding.join(', '),
213
+ hint: `A2A 1.0 defines: ${A2A_PROTOCOL_BINDINGS.join(', ')}.`,
214
+ learnMoreUrl: guideUrl(meta.id, 'unknown-binding'),
215
+ });
216
+ score -= 5;
217
+ }
218
+ const offOrigin = interfaces
219
+ .map((i) => String(i.url ?? ''))
220
+ .filter((u) => u !== '' && sameHost(u, ctx.url) === false);
221
+ if (offOrigin.length > 0) {
222
+ findings.push({
223
+ status: 'warn',
224
+ message: `${offOrigin.length} interface URL(s) point to a different origin`,
225
+ detail: offOrigin.join(', '),
226
+ hint: 'Interfaces on another origin can be intentional, but agents treat the card as authoritative for this site. Confirm the endpoints are yours.',
227
+ learnMoreUrl: guideUrl(meta.id, 'url-mismatch'),
228
+ });
229
+ score -= 5;
230
+ }
231
+ return score;
232
+ }
233
+ /** A2A 0.3: the single top-level `url` should be absolute and on this origin. */
234
+ function validateLegacyUrl(data, ctx, findings, score) {
235
+ if (typeof data.url !== 'string' || data.url.length === 0)
236
+ return score;
237
+ const sameOrigin = sameHost(data.url, ctx.url);
238
+ if (sameOrigin === false) {
239
+ findings.push({
240
+ status: 'warn',
241
+ message: `Agent Card "url" points to a different origin: ${data.url}`,
242
+ hint: 'The url field should match the audited site origin. Pointing it elsewhere can confuse agents about the canonical agent endpoint.',
243
+ learnMoreUrl: guideUrl(meta.id, 'url-mismatch'),
244
+ });
245
+ score -= 5;
246
+ }
247
+ else if (sameOrigin === null) {
248
+ findings.push({
249
+ status: 'warn',
250
+ message: `Agent Card "url" is not a valid absolute URL: ${data.url}`,
251
+ hint: 'Provide an absolute https:// URL for the url field.',
252
+ learnMoreUrl: guideUrl(meta.id, 'url-invalid'),
253
+ });
254
+ score -= 5;
255
+ }
256
+ return score;
257
+ }
258
+ /** Rules that hold across both generations. */
259
+ function validateCommon(data, findings, score) {
260
+ if (Array.isArray(data.skills) && data.skills.length > 0) {
261
+ const skills = data.skills;
262
+ findings.push({ status: 'pass', message: `${skills.length} skill(s) defined` });
263
+ const incomplete = skills.filter((s) => !s.id || !s.description);
264
+ if (incomplete.length === 0) {
265
+ findings.push({ status: 'pass', message: 'All skills have id + description' });
266
+ }
267
+ else {
268
+ findings.push({
269
+ status: 'warn',
270
+ message: `${incomplete.length}/${skills.length} skill(s) missing id or description`,
271
+ hint: 'Each entry in skills[] should include both an id and a description so agents can address it and reason about its purpose.',
272
+ learnMoreUrl: guideUrl(meta.id, 'incomplete-skills'),
273
+ });
274
+ score -= 5;
275
+ }
276
+ }
277
+ else if (Array.isArray(data.skills)) {
278
+ findings.push({
279
+ status: 'warn',
280
+ message: 'Skills array is empty',
281
+ hint: 'Add at least one skill to the skills array describing what your agent can do.',
282
+ learnMoreUrl: guideUrl(meta.id, 'empty-skills'),
283
+ });
284
+ score -= 10;
285
+ }
286
+ // `authentication` was replaced by `securitySchemes` in A2A 0.2.x. A card
287
+ // still carrying it is describing its auth to nobody.
288
+ if (data.authentication !== undefined) {
289
+ findings.push({
290
+ status: 'warn',
291
+ message: 'Card uses "authentication", removed from the A2A spec',
292
+ hint: 'Replace it with "securitySchemes" (OpenAPI-style scheme definitions) plus the matching security requirements.',
293
+ learnMoreUrl: guideUrl(meta.id, 'deprecated-authentication'),
294
+ });
295
+ score -= 5;
296
+ }
297
+ if (data.securitySchemes !== undefined) {
298
+ findings.push({ status: 'pass', message: 'securitySchemes declared' });
299
+ }
300
+ if (Array.isArray(data.signatures) && data.signatures.length > 0) {
301
+ findings.push({
302
+ status: 'pass',
303
+ message: `Card carries ${data.signatures.length} JWS signature(s)`,
304
+ detail: 'Signed cards let a client verify the card was issued by the domain it claims.',
305
+ });
306
+ }
307
+ const capabilities = (data.capabilities ?? {});
308
+ const extensions = Array.isArray(capabilities.extensions)
309
+ ? capabilities.extensions
310
+ : [];
311
+ if (extensions.length > 0) {
312
+ findings.push({
313
+ status: 'pass',
314
+ message: `${extensions.length} capability extension(s) declared`,
315
+ detail: extensions.map((e) => String(e.uri ?? '(no uri)')).join(', '),
316
+ });
317
+ }
318
+ const optionalFields = ['provider', 'documentationUrl', 'iconUrl'];
319
+ const presentOptional = optionalFields.filter((f) => data[f] !== undefined);
320
+ if (presentOptional.length === optionalFields.length) {
321
+ findings.push({
322
+ status: 'pass',
323
+ message: 'All optional descriptive fields present (provider, documentationUrl, iconUrl)',
324
+ });
325
+ }
326
+ else if (presentOptional.length > 0) {
327
+ findings.push({
328
+ status: 'pass',
329
+ message: `${presentOptional.length}/${optionalFields.length} optional descriptive fields present`,
330
+ });
331
+ }
332
+ else {
333
+ findings.push({
334
+ status: 'warn',
335
+ message: 'No optional descriptive fields (provider, documentationUrl, iconUrl)',
336
+ hint: 'Add provider (who runs this agent), documentationUrl (where to read more) and iconUrl. Agents surface these to users when choosing between agents.',
337
+ learnMoreUrl: guideUrl(meta.id, 'missing-optional'),
338
+ });
339
+ score -= 5;
340
+ }
341
+ return score;
342
+ }
343
+ /** Returns `true` when the two URLs share host, `false` if hosts differ, `null` on parse error. */
344
+ function sameHost(a, b) {
345
+ try {
346
+ return new URL(a).host.toLowerCase() === new URL(b).host.toLowerCase();
347
+ }
348
+ catch {
349
+ return null;
350
+ }
351
+ }
352
+ //# sourceMappingURL=agent-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-card.js","sourceRoot":"","sources":["../../src/checks/agent-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { CheckContext, CheckResult, CheckMeta } from '../types.js';
2
+ /**
3
+ * "agent-operability" — can a browser agent work this page, or only look at it?
4
+ *
5
+ * Every major browser agent reads the accessibility tree, not the pixels.
6
+ * Anthropic's browser tool returns the page as `link "Documentation" [ref_1]`,
7
+ * `button "Search" [ref_4]`; ChatGPT Atlas is documented as using ARIA roles and
8
+ * labels; Chrome's own guidance for agent-friendly sites asks for real
9
+ * `<button>` and `<a>` elements, labelled controls and stable layout.
10
+ *
11
+ * Which means the failure mode is specific and old: a `<div onclick>` styled to
12
+ * look like a button has no name and no role, so it does not appear in the tree
13
+ * at all. The agent does not see a button it cannot press. It sees nothing.
14
+ * An unlabelled input is a box the agent must guess the meaning of, which is how
15
+ * an email address ends up in a search field.
16
+ *
17
+ * This is the accessibility work that has been the right thing to do for twenty
18
+ * years, with a new and less patient audience. Every finding here is a genuine
19
+ * accessibility defect too.
20
+ *
21
+ * The check is a static approximation and says so: it reads markup, not a
22
+ * rendered accessibility tree, so it cannot see labels attached by script or
23
+ * roles computed at runtime. It reports proportions rather than absolutes for
24
+ * that reason.
25
+ */
26
+ export declare const meta: CheckMeta;
27
+ export interface OperabilityReport {
28
+ buttons: {
29
+ total: number;
30
+ named: number;
31
+ unnamed: string[];
32
+ };
33
+ controls: {
34
+ total: number;
35
+ labelled: number;
36
+ unlabelled: string[];
37
+ };
38
+ fakeInteractive: number;
39
+ deadLinks: {
40
+ noHref: number;
41
+ javascriptHref: number;
42
+ };
43
+ tables: {
44
+ total: number;
45
+ withHeaders: number;
46
+ };
47
+ media: {
48
+ total: number;
49
+ sized: number;
50
+ };
51
+ iframes: {
52
+ total: number;
53
+ titled: number;
54
+ };
55
+ timeElements: {
56
+ total: number;
57
+ withDatetime: number;
58
+ };
59
+ headingSkips: string[];
60
+ lang: string | null;
61
+ blockers: string[];
62
+ }
63
+ /** Analyse the markup for everything a browser agent needs in order to act. */
64
+ export declare function analyseOperability(rawHtml: string): OperabilityReport;
65
+ export default function check(ctx: CheckContext): Promise<CheckResult>;
66
+ //# sourceMappingURL=agent-operability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-operability.d.ts","sourceRoot":"","sources":["../../src/checks/agent-operability.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAW,MAAM,aAAa,CAAC;AAIjF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,EAAE,SAKlB,CAAC;AAKF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC7D,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAoCD,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAqIrE;AAED,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8B3E"}