@relvehq/agent-ready 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -3
  3. package/SETUP.md +354 -0
  4. package/dist/bin/agent-ready.d.ts +23 -0
  5. package/dist/bin/agent-ready.js +444 -0
  6. package/dist/cli/init.d.ts +70 -0
  7. package/dist/cli/init.js +366 -0
  8. package/dist/core/accept.d.ts +8 -0
  9. package/dist/core/accept.js +76 -0
  10. package/dist/core/config.d.ts +145 -0
  11. package/dist/core/config.js +103 -0
  12. package/dist/core/convert.d.ts +22 -0
  13. package/dist/core/convert.js +334 -0
  14. package/dist/core/detect.d.ts +30 -0
  15. package/dist/core/detect.js +82 -0
  16. package/dist/core/easy.d.ts +19 -0
  17. package/dist/core/easy.js +117 -0
  18. package/dist/core/enrich.d.ts +30 -0
  19. package/dist/core/enrich.js +155 -0
  20. package/dist/core/llms.d.ts +11 -0
  21. package/dist/core/llms.js +71 -0
  22. package/dist/core/report.d.ts +15 -0
  23. package/dist/core/report.js +46 -0
  24. package/dist/core/resolve.d.ts +43 -0
  25. package/dist/core/resolve.js +204 -0
  26. package/dist/core/respond.d.ts +18 -0
  27. package/dist/core/respond.js +59 -0
  28. package/dist/core/sitemap.d.ts +31 -0
  29. package/dist/core/sitemap.js +140 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.js +76 -0
  32. package/dist/next/dashboard.d.ts +14 -0
  33. package/dist/next/dashboard.js +56 -0
  34. package/dist/next/index.d.ts +42 -0
  35. package/dist/next/index.js +100 -0
  36. package/dist/next/route.d.ts +26 -0
  37. package/dist/next/route.js +167 -0
  38. package/dist/node/index.d.ts +8 -0
  39. package/dist/node/index.js +191 -0
  40. package/dist/proof/audit.d.ts +47 -0
  41. package/dist/proof/audit.js +253 -0
  42. package/dist/proof/bots.d.ts +24 -0
  43. package/dist/proof/bots.js +60 -0
  44. package/dist/proof/dashboard.d.ts +8 -0
  45. package/dist/proof/dashboard.js +77 -0
  46. package/dist/proof/hits.d.ts +18 -0
  47. package/dist/proof/hits.js +51 -0
  48. package/dist/proof/jsonl.d.ts +5 -0
  49. package/dist/proof/jsonl.js +80 -0
  50. package/dist/proof/stats.d.ts +39 -0
  51. package/dist/proof/stats.js +127 -0
  52. package/dist/refine/check.d.ts +17 -0
  53. package/dist/refine/check.js +134 -0
  54. package/dist/refine/extract.d.ts +7 -0
  55. package/dist/refine/extract.js +120 -0
  56. package/dist/refine/file-store.d.ts +6 -0
  57. package/dist/refine/file-store.js +87 -0
  58. package/dist/refine/model.d.ts +30 -0
  59. package/dist/refine/model.js +105 -0
  60. package/dist/refine/prompts.d.ts +49 -0
  61. package/dist/refine/prompts.js +178 -0
  62. package/dist/refine/refine.d.ts +38 -0
  63. package/dist/refine/refine.js +75 -0
  64. package/dist/refine/remote.d.ts +57 -0
  65. package/dist/refine/remote.js +238 -0
  66. package/dist/refine/run.d.ts +52 -0
  67. package/dist/refine/run.js +66 -0
  68. package/dist/refine/types.d.ts +17 -0
  69. package/dist/refine/types.js +8 -0
  70. package/dist/wp/relve-agent-ready/includes/class-relve-ar-admin.php +550 -0
  71. package/dist/wp/relve-agent-ready/includes/class-relve-ar-converter.php +762 -0
  72. package/dist/wp/relve-agent-ready/includes/class-relve-ar-llms.php +192 -0
  73. package/dist/wp/relve-agent-ready/includes/class-relve-ar-markdown-builder.php +455 -0
  74. package/dist/wp/relve-agent-ready/includes/class-relve-ar-negotiation.php +334 -0
  75. package/dist/wp/relve-agent-ready/includes/class-relve-ar-refiner.php +894 -0
  76. package/dist/wp/relve-agent-ready/includes/class-relve-ar-response.php +226 -0
  77. package/dist/wp/relve-agent-ready/includes/class-relve-ar-router.php +421 -0
  78. package/dist/wp/relve-agent-ready/includes/class-relve-ar-settings.php +201 -0
  79. package/dist/wp/relve-agent-ready/includes/class-relve-ar-store.php +583 -0
  80. package/dist/wp/relve-agent-ready/includes/index.php +2 -0
  81. package/dist/wp/relve-agent-ready/index.php +2 -0
  82. package/dist/wp/relve-agent-ready/readme.txt +108 -0
  83. package/dist/wp/relve-agent-ready/relve-agent-ready.php +81 -0
  84. package/dist/wp/relve-agent-ready/uninstall.php +35 -0
  85. package/package.json +64 -6
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Relve
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,364 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @relvehq/agent-ready
2
+
3
+ Make your site agent-ready. When an AI agent (ChatGPT, Claude, Perplexity,
4
+ Copilot…) asks for one of your pages as Markdown, it gets a clean, small
5
+ Markdown version instead of 50,000+ tokens of HTML. Browsers get exactly what
6
+ they got before.
7
+
8
+ - **Serve**: Markdown on `Accept: text/markdown`, on `<page>.md` and on
9
+ `?format=md`, plus a generated [`/llms.txt`](https://llmstxt.org). No AI
10
+ runs per request: it is plain code, in milliseconds.
11
+ - **Refine** (optional, with your own OpenAI key): one command writes a
12
+ better Markdown file for each page ahead of time with Relve's prompt, a
13
+ check compares every number and URL against the page, and unchanged pages
14
+ are skipped (free) on the next run.
15
+ - **Proof**: a free checker that scores any site out of 100, and a dashboard
16
+ of which AI agents read which pages.
17
+
18
+ On a real tool page: raw HTML 97,944 tokens, a generic converter 4,618, a
19
+ refined file 1,695.
20
+
21
+ **Start here:** the onboarding guide at <https://relvehq.com/agent-ready>
22
+ (every stack, the WordPress plugin download, and how to test it).
23
+
24
+ ## Requirements
25
+
26
+ - Node.js 20 or newer
27
+ - For the Next.js adapter: Next.js 13.4+ with the App Router
28
+ - Anything else: the core is plain `Request` / `Response` (see "Other frameworks")
29
+
30
+ ## Quick start: one command
31
+
32
+ In your project's root:
33
+
34
+ ```
35
+ npx @relvehq/agent-ready init
36
+ ```
37
+
38
+ It detects Next.js, Node servers and WordPress, and sets up the one it finds:
39
+
40
+ | It finds | It does |
41
+ |---|---|
42
+ | `app/` or `src/app/` and `next` in `package.json` | writes the Next.js files (below) |
43
+ | `wp-config.php` or `wp-content/` | copies the bundled plugin to `wp-content/plugins/relve-agent-ready/`, then tells you to activate it and tick Enabled on Settings > Agent Ready (an existing copy is kept unless you add `--force`) |
44
+ | `express`, `connect`, `polka`, `fastify`, `@nestjs/core`, `h3` or `nitropack` in `package.json` | writes nothing; prints the exact lines for that framework and the env vars |
45
+ | none of these | prints what it looked for and the guide's URL, and exits 1 |
46
+
47
+ `--stack next|node|wordpress` skips detection; `--dry-run` shows the plan and
48
+ writes nothing. Step-by-step for every stack: [SETUP.md](./SETUP.md).
49
+
50
+ ### Next.js
51
+
52
+ ```
53
+ npm install @relvehq/agent-ready
54
+ npx agent-ready init
55
+ ```
56
+
57
+ `init` looks at your project (`app/` or `src/app/`, TypeScript or JavaScript)
58
+ and writes:
59
+
60
+ | File | What it holds |
61
+ |---|---|
62
+ | `agent-ready.config.ts` | `export default createAgentReady()`, smart defaults |
63
+ | `middleware.ts` | `export default withAgentReady(agentReady)` and the matcher (only when you have no middleware yet) |
64
+ | `app/%5F%5Fmd/[[...path]]/route.ts` | the internal route that builds the Markdown (`%5F` is `_`) |
65
+ | `.env.local` | `AGENT_READY_ENABLED=1` and `NEXT_PUBLIC_SITE_URL=http://localhost:3000` |
66
+
67
+ It never edits a file you already have. If you have a middleware, it prints
68
+ the two lines to add:
69
+
70
+ ```ts
71
+ import { withAgentReady } from "@relvehq/agent-ready/next";
72
+ import agentReady from "./agent-ready.config";
73
+ export default withAgentReady(agentReady, yourMiddleware);
74
+ // and add to your matcher: "/((?!_next/).+\\.md)", "/llms.txt"
75
+ ```
76
+
77
+ Running `init` again changes nothing; `--dry-run` shows the plan.
78
+
79
+ Check it with the dev server running:
80
+
81
+ ```
82
+ curl -i -H "Accept: text/markdown" http://localhost:3000/
83
+ curl -i http://localhost:3000/index.md
84
+ curl -i http://localhost:3000/llms.txt
85
+ ```
86
+
87
+ In production set `AGENT_READY_ENABLED=1` and `NEXT_PUBLIC_SITE_URL=https://your-site.com`
88
+ on your host. Without a site URL the package stays off (it never fails your build).
89
+
90
+ ### Node server and WordPress
91
+
92
+ For a Node server, `init` prints the lines for your framework (Express,
93
+ Connect, Polka, Nest: `app.use(...)`; Fastify through `@fastify/middie`;
94
+ Nitro and h3 through `fromNodeMiddleware`), and the two env vars:
95
+ `AGENT_READY_ENABLED=1` and `SITE_URL=https://your-site.com`. See "Other
96
+ frameworks" below. For WordPress, the plugin ships inside this package
97
+ (without its tests), so `init` needs no download.
98
+
99
+ ### What `createAgentReady()` works out on its own
100
+
101
+ | Setting | Default |
102
+ |---|---|
103
+ | `site` | the first set of `NEXT_PUBLIC_SITE_URL`, `SITE_URL`, `NEXT_PUBLIC_APP_URL`, `URL` (Netlify), `VERCEL_PROJECT_PRODUCTION_URL`, `VERCEL_URL` |
104
+ | `enabled` | `AGENT_READY_ENABLED` is `1` or `true` |
105
+ | `exclude` | `/api`, `/_next`, auth and admin paths, plus common private pages (account, checkout, cart, dashboard, settings, billing…) and yours; `excludeCommon: false` drops the common list |
106
+ | `llms` | `/llms.txt` built from your `sitemap.xml`; `llms: false` for none |
107
+ | `onHit` | sends each visit to a dashboard when `AGENT_READY_KEY` and `AGENT_READY_ENDPOINT` are set |
108
+
109
+ Anything you pass wins:
110
+
111
+ ```ts
112
+ export default createAgentReady({
113
+ exclude: ["/members"],
114
+ templates: { "/blog/:slug": async ({ params }) => renderPostMarkdown(params.slug) },
115
+ });
116
+ ```
117
+
118
+ ## How an agent asks
119
+
120
+ | Ask | Example |
121
+ |---|---|
122
+ | `Accept` header | `Accept: text/markdown`, preferred over (or tied with) `text/html` |
123
+ | `.md` URL | `/pricing.md`, `/index.md` for `/` |
124
+ | query | `/pricing?format=md` |
125
+
126
+ A browser's `Accept`, a bare `*/*`, or `text/markdown` with a lower q-value
127
+ than `text/html` is not a Markdown request. The user agent is never used to
128
+ choose what to serve: that would be cloaking.
129
+
130
+ ## How it works
131
+
132
+ 1. **Detect** (middleware, Edge): is this a Markdown request? If not, nothing changes.
133
+ 2. **Rewrite** to the internal route `/__md/<path>`; the public URL stays the same.
134
+ 3. **Resolve** (Node route), first hit wins:
135
+ 1. a **template** for the route (`"/blog/:slug"`), written from your data
136
+ 2. a **stored** file (`store.get(path)`), e.g. one written by `refine`
137
+ 3. **convert**: fetch the page's own HTML (public, no cookies) and turn its
138
+ main content into Markdown (Readability + turndown, GFM tables). Under 200
139
+ characters of real text is discarded.
140
+ 4. nothing usable: an `Accept` request gets the normal HTML page, a `.md`
141
+ request gets a 404
142
+ 4. **Enrich**: YAML front matter (title, canonical, source), facts from JSON-LD, token count.
143
+ 5. **Respond**:
144
+
145
+ ```
146
+ Content-Type: text/markdown; charset=utf-8
147
+ Vary: Accept
148
+ Link: <https://your-site.com/pricing>; rel="canonical"
149
+ X-Robots-Tag: noindex (search engines keep indexing the HTML page)
150
+ x-markdown-tokens: 314
151
+ ETag: W/"…" (If-None-Match → 304)
152
+ Cache-Control: see "Caching"
153
+ x-agent-ready-source: native | stored | convert
154
+ ```
155
+
156
+ 6. **Measure**: one `HitEvent` per request to `onHit`, fire-and-forget.
157
+
158
+ ## Caching behind a CDN
159
+
160
+ Most CDNs (CloudFront included) don't vary their cache on `Accept`, so:
161
+
162
+ - Responses negotiated by `Accept` use `negotiatedCacheControl`, default
163
+ `private, max-age=300`. A shared cache never stores them, so a browser is
164
+ never served Markdown.
165
+ - `.md`, `?format=md` and `llms.txt` are their own URLs and use `cacheControl`,
166
+ default `public, s-maxage=300, stale-while-revalidate=86400`.
167
+ - If the CDN already has the HTML page cached, an `Accept` request may get that
168
+ HTML. Safe, just not Markdown. `.md` URLs always work; reliable negotiation
169
+ needs `Accept` in the CDN cache key.
170
+
171
+ ## Refine: better Markdown with your own OpenAI key
172
+
173
+ One command, run from your project root with `OPENAI_API_KEY` in `.env.local`,
174
+ `.env` or the shell:
175
+
176
+ ```
177
+ npx agent-ready refine --site https://your-site.com --sitemap --dry-run
178
+ npx agent-ready refine --site https://your-site.com --sitemap
179
+ ```
180
+
181
+ For each page the command fetches the HTML itself and sends it to the Relve
182
+ API (`relvehq.com`) with your key in a header. Relve's prompt runs on Relve's
183
+ server: it extracts the page, calls OpenAI with your key, checks the result
184
+ (every number and URL must be on the page; one retry), and returns the
185
+ Markdown. The command saves `<out>/<path>.md` plus `<out>/<path>.json`
186
+ (default out: `.agent-ready/md`).
187
+
188
+ - **Cost:** your own OpenAI usage, one model call per changed page (roughly
189
+ a few thousand tokens each with the default `gpt-4o-mini`). Relve charges nothing.
190
+ - **Your key:** used for that call only. Relve does not store or log it, and
191
+ it is never written to the output files.
192
+ - **Re-run when content changes.** Each saved `.json` holds the page's
193
+ fingerprint; it is sent along, and an unchanged page comes back as
194
+ `unchanged` with no model call (free). `--force` rewrites everything.
195
+ - **Optional in CI or deploy:** run the command before `next build` (with
196
+ `OPENAI_API_KEY` as a secret) and commit or ship `.agent-ready/md`.
197
+
198
+ | Option | |
199
+ |---|---|
200
+ | `--sitemap` / `--paths /a,/b` | which pages (sitemap at `<site>/sitemap.xml`) |
201
+ | `--model` | `gpt-4o-mini` (default, fastest), `gpt-4o`, `gpt-4.1`, `gpt-4.1-mini` |
202
+ | `--site-name` | your brand name, used in the documents |
203
+ | `--out`, `--limit`, `--concurrency` (2), `--force`, `--dry-run` | `--dry-run` lists the pages: no API call, nothing written |
204
+ | `AGENT_READY_REFINE_ENDPOINT` | another API URL (testing) |
205
+
206
+ The run ends with `written / unchanged / rejected / errors` and the token
207
+ total. Rejected and failed pages are listed with their code (for example
208
+ `too_little_text`) and nothing is saved for them; the exit code is then 1.
209
+ If OpenAI rejects the key or your account is out of quota, the run stops at
210
+ the first page and says so.
211
+
212
+ Serve the files from the route file (`fileStore` is Node only, so keep it
213
+ out of `agent-ready.config.ts`, which the Edge middleware imports):
214
+
215
+ ```ts
216
+ // app/%5F%5Fmd/[[...path]]/route.ts
217
+ import { createMarkdownRoute } from "@relvehq/agent-ready/next/route";
218
+ import { fileStore } from "@relvehq/agent-ready/refine/file-store";
219
+ import agentReady from "../../../agent-ready.config";
220
+ export const { GET, HEAD } = createMarkdownRoute({ ...agentReady, store: fileStore(".agent-ready/md") });
221
+ export const runtime = "nodejs";
222
+ export const dynamic = "force-dynamic";
223
+ ```
224
+
225
+ On serverless hosts (Vercel, Amplify) the folder must be bundled with the
226
+ route (`outputFileTracingIncludes`); see [SETUP.md](./SETUP.md).
227
+
228
+ **The check** rejects a file when a number or URL in it is not on the page, a
229
+ required section is missing, it has more or fewer than one H1, HTML tags, a
230
+ wrong fingerprint, or it is over the token budget. It cannot catch a reworded
231
+ claim, so read a sample before you switch on.
232
+
233
+ **Advanced: your own prompts.** The package ships no prompt. With
234
+ `--prompts <dir>` (several: `a,b`) or the `AGENT_READY_PROMPTS` variable
235
+ (`.env.local`, `.env` or the shell), `refine` runs your prompt files on your
236
+ machine instead of calling the Relve API, with `OPENAI_API_KEY` (default
237
+ model `gpt-4o`) or `ANTHROPIC_API_KEY` (default `claude-opus-5-5`);
238
+ `AGENT_READY_MODEL` picks another model. The steps are the same: fetch,
239
+ extract, skip unchanged, fill the prompt, one model call, check, one retry,
240
+ save. A prompt file is Markdown with `## SYSTEM`, `## USER`
241
+ (`{{PAGE_URL}}`, `{{PAGE_TEXT}}`, `{{PAGE_LINKS}}`, `{{JSON_LD}}`,
242
+ `{{SOURCE_HASH}}`) and a `## Settings` table (`page_type`, `version`,
243
+ `required_sections`, `token_budget`). A `page_type` of `page` takes every
244
+ path no other prompt claims; for it the code guesses the page's kind from its
245
+ URL and JSON-LD (home, product, pricing, article, docs, listing, event, about,
246
+ contact, legal, other) and passes the guess as `{{PAGE_KIND_HINT}}`.
247
+
248
+ ## Proof
249
+
250
+ ### Free checker
251
+
252
+ ```
253
+ npx agent-ready check https://any-site.com/pricing
254
+ npx agent-ready check any-site.com --json
255
+ ```
256
+
257
+ Read-only. Scores the site out of 100 (A to F): Markdown for `Accept` (25),
258
+ a `.md` URL (15), `/llms.txt` (15), robots.txt letting AI answer bots in (15),
259
+ a sitemap (10), JSON-LD (10), `Vary: Accept` (5), a canonical (5). Each failing
260
+ check says what to do, and it compares the tokens an agent reads today with
261
+ what agent-ready would serve. In code: `auditSite(url)`.
262
+
263
+ ### Dashboard
264
+
265
+ ```ts
266
+ // agent-ready.config.ts
267
+ import { createAgentReady, memoryHitStore } from "@relvehq/agent-ready";
268
+ export const hits = memoryHitStore();
269
+ export default createAgentReady({ hits });
270
+
271
+ // app/agent-ready-dashboard/route.ts
272
+ import { createDashboardRoute } from "@relvehq/agent-ready/next/dashboard";
273
+ import { hits } from "../../agent-ready.config";
274
+ export const { GET } = createDashboardRoute({ store: hits, site: "your-site.com" });
275
+ export const dynamic = "force-dynamic";
276
+ ```
277
+
278
+ Open `/agent-ready-dashboard?key=<AGENT_READY_DASHBOARD_KEY>` (add
279
+ `&format=json` for the data, `&days=7` for a shorter window). Without a key
280
+ configured the route answers 404. It shows visits per day, who is reading (AI
281
+ assistants, AI search, training bots, search engines), top pages, how agents
282
+ asked, where the Markdown came from, tokens served and response time.
283
+
284
+ A `HitStore` is two methods, `add` and `list`. `memoryHitStore()` suits a
285
+ long-running server; `jsonlHitStore(file)` (`@relvehq/agent-ready/proof/jsonl`)
286
+ writes a log that `npx agent-ready stats --log <file>` summarises. On
287
+ serverless hosts (Vercel, Amplify) memory and local files don't last between
288
+ requests: implement `HitStore` on your database.
289
+
290
+ ## Other frameworks
291
+
292
+ Express, Connect, Polka, Fastify (via `@fastify/middie`) or a plain `http`
293
+ server: one middleware. Mount it before your static files and routes:
294
+
295
+ ```ts
296
+ import express from "express";
297
+ import { createAgentReady } from "@relvehq/agent-ready";
298
+ import { agentReadyNode } from "@relvehq/agent-ready/node";
299
+
300
+ const app = express();
301
+ app.use(agentReadyNode(createAgentReady({ site: "https://your-site.com" })));
302
+ // app.use(express.static("public")); your routes...
303
+ ```
304
+
305
+ It answers Markdown requests and calls `next()` for everything else (and for
306
+ an `Accept` request when no Markdown is available, so the page's HTML is
307
+ served). Plain `http`: `http.createServer((req, res) => md(req, res, () => app(req, res)))`.
308
+
309
+ On other runtimes (Bun, Deno, Workers, Hono…) the core uses only `Request` and
310
+ `Response`. Detect, enrich and respond run anywhere; `resolveMarkdown` (its
311
+ convert step) needs Node, so on Edge-only runtimes use templates and a store
312
+ with `fallback: "none"`:
313
+
314
+ ```ts
315
+ import { defineAgentReady, wantsMarkdown, enrich, markdownResponse } from "@relvehq/agent-ready";
316
+ import { resolveMarkdown } from "@relvehq/agent-ready/core/resolve"; // Node only
317
+
318
+ const cfg = defineAgentReady({ site: "https://your-site.com", enabled: true });
319
+ const d = wantsMarkdown(request);
320
+ if (d.markdown) {
321
+ const r = await resolveMarkdown(cfg, { path: d.path, search: d.search, request });
322
+ if (r.doc) {
323
+ const { body, tokens } = enrich(r.doc, { canonical: cfg.site + d.path, sourceUrl: r.sourceUrl, source: r.source! });
324
+ return markdownResponse(body, { request, canonical: cfg.site + d.path, tokens, cacheControl: cfg.cacheControl });
325
+ }
326
+ }
327
+ ```
328
+
329
+ `@relvehq/agent-ready` and `@relvehq/agent-ready/next` are Edge-safe.
330
+ `/node`, `/next/route`, `/core/resolve`, `/core/convert` and `/refine/*` need Node.
331
+
332
+ ## Config reference (`defineAgentReady` / `createAgentReady`)
333
+
334
+ | Key | Default | Notes |
335
+ |---|---|---|
336
+ | `site` | required (`createAgentReady`: from env) | public origin, for canonicals and absolute links |
337
+ | `enabled` | `false` | boolean or `(request) => boolean` |
338
+ | `exclude` | `[]` | path prefixes or RegExps, added to the always-excluded list |
339
+ | `templates` | `{}` | `{ "/blog/:slug": fn }`, first matching pattern wins |
340
+ | `store` | `null` | `{ get(path) }` |
341
+ | `fallback` | `"convert"` | `"none"` serves only templates and stored files |
342
+ | `cacheControl` | `public, s-maxage=300, stale-while-revalidate=86400` | `.md`, `?format=md`, llms.txt |
343
+ | `negotiatedCacheControl` | `private, max-age=300` | `Accept` requests |
344
+ | `llms` | `null` (`createAgentReady`: from the sitemap) | `{ path, title, summary, details, entries }` |
345
+ | `onHit` | `null` | `(HitEvent) => void` |
346
+ | `internalPrefix` | `/__md` | rewrite target |
347
+ | `fetchTimeoutMs` | `8000` | page fetch for convert |
348
+ | `minChars` | `200` | quality gate for convert |
349
+
350
+ ## Commands
351
+
352
+ ```
353
+ agent-ready init [--stack next|node|wordpress] [--dry-run] [--force] [--cwd <dir>]
354
+ agent-ready check <url> [--json]
355
+ agent-ready refine --site <origin> (--paths /a,/b | --sitemap) [--model <id>] [--site-name <name>]
356
+ [--out <dir>] [--limit <n>] [--force] [--concurrency <n>] [--dry-run]
357
+ [--prompts <dir>] (advanced: your own prompts, run locally)
358
+ agent-ready stats --log <hits.jsonl> [--days <n>] [--json]
359
+ ```
360
+
361
+ ## License
362
+
363
+ MIT © [Relve](https://relvehq.com). Built by the team behind Relve, the AI
364
+ tools and market intelligence platform.