@farming-labs/docs 0.2.120 → 0.2.121

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 (188) hide show
  1. package/README.md +8 -0
  2. package/dist/access.d.mts +13 -0
  3. package/dist/access.mjs +56 -0
  4. package/dist/agent-conformance.d.mts +60 -0
  5. package/dist/agent-conformance.mjs +538 -0
  6. package/dist/agent-contract.d.mts +47 -0
  7. package/dist/agent-contract.mjs +645 -0
  8. package/dist/agent-detection.d.mts +6 -0
  9. package/dist/agent-detection.mjs +83 -0
  10. package/dist/agent-evals.d.mts +224 -0
  11. package/dist/{agent-evals-BLNLvXbG.mjs → agent-evals.mjs} +7 -565
  12. package/dist/agent-feedback-evaluations-node.mjs +38 -0
  13. package/dist/agent-feedback-evaluations.d.mts +91 -0
  14. package/dist/{agent-feedback-evaluations-CcMGlgsO.mjs → agent-feedback-evaluations.mjs} +2 -2
  15. package/dist/agent-feedback-loop.d.mts +45 -0
  16. package/dist/{agent-feedback-loop-CcZL_sb1.mjs → agent-feedback-loop.mjs} +1 -1
  17. package/dist/agent-maintenance.d.mts +71 -0
  18. package/dist/{agent-maintenance-CuZ7iAKq.mjs → agent-maintenance.mjs} +1 -1
  19. package/dist/agent-provenance.d.mts +23 -0
  20. package/dist/agent-provenance.mjs +77 -0
  21. package/dist/agent-scope.mjs +321 -0
  22. package/dist/agent-skills-archive.mjs +86 -0
  23. package/dist/agent-skills-bundle.d.mts +1 -1
  24. package/dist/{agent-skills-progressive-disclosure-PjPtTYL-.mjs → agent-skills-progressive-disclosure.mjs} +2 -2
  25. package/dist/{agent-skills-server-D5IuvwM7.d.mts → agent-skills-server.d.mts} +3 -3
  26. package/dist/{agent-skills-server-BC7md56k.mjs → agent-skills-server.mjs} +2 -2
  27. package/dist/agent-skills-vite.d.mts +3 -3
  28. package/dist/agent-skills-vite.mjs +1 -2
  29. package/dist/agent-surface-drift.mjs +236 -0
  30. package/dist/{golden-evaluations-CLUViycJ.mjs → agent-usefulness.mjs} +5 -290
  31. package/dist/{content-change-hydration-wqXIZHp6.d.mts → agent.d.mts} +10 -411
  32. package/dist/agent.mjs +2729 -0
  33. package/dist/{analytics-w3FDOksr.d.mts → analytics.d.mts} +2 -2
  34. package/dist/{analytics-p8Vhhu2P.mjs → analytics.mjs} +2 -2
  35. package/dist/{sitemap-server-BUdPBcGs.d.mts → api-reference.d.mts} +2 -6
  36. package/dist/api-reference.mjs +1481 -0
  37. package/dist/{audience-exposure-DBrFWULA.d.mts → audience-exposure.d.mts} +1 -1
  38. package/dist/audience-exposure.mjs +8 -0
  39. package/dist/audience.d.mts +44 -0
  40. package/dist/audience.mjs +1654 -0
  41. package/dist/authoring-mcp.d.mts +44 -0
  42. package/dist/{authoring-mcp-CiCUQe3o.mjs → authoring-mcp.mjs} +3 -3
  43. package/dist/browser.d.mts +6 -4
  44. package/dist/browser.mjs +6 -7
  45. package/dist/build.d.mts +8 -0
  46. package/dist/build.mjs +8 -0
  47. package/dist/changelog.d.mts +20 -0
  48. package/dist/changelog.mjs +39 -0
  49. package/dist/{agent-export-CA6IUQ7T.mjs → cli/agent-export.mjs} +19 -45
  50. package/dist/{agent-CYk2DxY7.mjs → cli/agent.mjs} +6 -35
  51. package/dist/{agents-BUiVmj8i.mjs → cli/agents.mjs} +5 -18
  52. package/dist/cli/api-doctor.mjs +206 -0
  53. package/dist/{authoring-C2ymvYWl.mjs → cli/authoring.mjs} +2 -8
  54. package/dist/{cloud-6eKrlmKd.mjs → cli/cloud.mjs} +3 -3
  55. package/dist/{codeblocks-CHpgAwY-.mjs → cli/codeblocks.mjs} +2 -2
  56. package/dist/cli/config.d.mts +11 -0
  57. package/dist/{config-BZnKAj_Y.mjs → cli/config.mjs} +1 -1
  58. package/dist/cli/dev-banner.mjs +58 -0
  59. package/dist/{dev-Bnn-NUNa.mjs → cli/dev.mjs} +3 -57
  60. package/dist/{doctor-Bfkrkzad.mjs → cli/doctor.mjs} +41 -226
  61. package/dist/{downgrade-CHMta85e.mjs → cli/downgrade.mjs} +2 -2
  62. package/dist/cli/errors.d.mts +4 -0
  63. package/dist/{errors-BtnVLhNF.mjs → cli/errors.mjs} +1 -1
  64. package/dist/{feedback-evals-DMilg9pY.mjs → cli/feedback-evals.mjs} +3 -36
  65. package/dist/{feedback-DL4T5oIO.mjs → cli/feedback.mjs} +1 -1
  66. package/dist/cli/git-dates.mjs +35 -0
  67. package/dist/cli/golden-evaluations.mjs +52 -0
  68. package/dist/cli/index.d.mts +2 -3
  69. package/dist/cli/index.mjs +38 -38
  70. package/dist/{init-K0LmXDZ5.mjs → cli/init.mjs} +3 -3
  71. package/dist/{mcp-iLCmnPJI.mjs → cli/mcp.mjs} +7 -19
  72. package/dist/{openapi-diff-HcpUNjA-.mjs → cli/openapi-diff.mjs} +2 -7
  73. package/dist/{openapi-sdk-BQKC6H-_.mjs → cli/openapi-sdk.mjs} +2 -7
  74. package/dist/{openapi-source-DK6ts_Bk.mjs → cli/openapi-source.mjs} +2 -2
  75. package/dist/{package-version-HJhYeq4M.mjs → cli/package-version.mjs} +2 -2
  76. package/dist/{propose-JmUv_BTt.mjs → cli/propose.mjs} +1 -1
  77. package/dist/{review-C6EmbV0l.mjs → cli/review.mjs} +16 -15
  78. package/dist/{robots-DteZB8vl.mjs → cli/robots.mjs} +3 -7
  79. package/dist/{search-Bhuv_YsL.mjs → cli/search.mjs} +7 -20
  80. package/dist/{sitemap-DW5LDKDa.mjs → cli/sitemap.mjs} +7 -42
  81. package/dist/{skills-BAPQLSCm.mjs → cli/skills.mjs} +5 -19
  82. package/dist/{templates-BuQ8YsGv.mjs → cli/templates.mjs} +1 -1
  83. package/dist/{upgrade-DlQMOFV1.mjs → cli/upgrade.mjs} +2 -2
  84. package/dist/{utils-C7KDrPpA.mjs → cli/utils.mjs} +1 -1
  85. package/dist/client/react.d.mts +2 -18
  86. package/dist/client/react.mjs +1 -157
  87. package/dist/{cloud-analytics-D14HplI7.mjs → cloud-analytics.mjs} +1 -1
  88. package/dist/{cloud-ask-ai-BrsTXQf6.d.mts → cloud-ask-ai.d.mts} +2 -2
  89. package/dist/{cloud-ask-ai-BqKhjpS5.mjs → cloud-ask-ai.mjs} +3 -3
  90. package/dist/code-blocks.d.mts +51 -0
  91. package/dist/{code-blocks-GYdvS3BH.mjs → code-blocks.mjs} +2 -2
  92. package/dist/code-group-mdx.d.mts +11 -0
  93. package/dist/code-group-mdx.mjs +57 -0
  94. package/dist/content-change-hydration.d.mts +87 -0
  95. package/dist/{openapi-mcp-BIOTLiA7.mjs → content-change-hydration.mjs} +5 -210
  96. package/dist/content-changes.d.mts +47 -0
  97. package/dist/content-changes.mjs +216 -0
  98. package/dist/create-theme.d.mts +48 -0
  99. package/dist/create-theme.mjs +57 -0
  100. package/dist/date.d.mts +5 -0
  101. package/dist/date.mjs +17 -0
  102. package/dist/define-docs.d.mts +9 -0
  103. package/dist/define-docs.mjs +46 -0
  104. package/dist/docs-cloud-client.d.mts +18 -0
  105. package/dist/docs-cloud-client.mjs +161 -0
  106. package/dist/docs-cloud-server.d.mts +2 -2
  107. package/dist/docs-cloud-server.mjs +2 -3
  108. package/dist/heading-toc.d.mts +10 -0
  109. package/dist/heading-toc.mjs +23 -0
  110. package/dist/http-body.d.mts +14 -0
  111. package/dist/{http-body-DkAPk6D5.mjs → http-body.mjs} +1 -1
  112. package/dist/http-cache.d.mts +25 -0
  113. package/dist/http-cache.mjs +140 -0
  114. package/dist/http-link.mjs +81 -0
  115. package/dist/i18n.d.mts +18 -0
  116. package/dist/{i18n-B1gJ9EW0.mjs → i18n.mjs} +1 -1
  117. package/dist/index.d.mts +42 -674
  118. package/dist/index.mjs +40 -14
  119. package/dist/markdown-rendering.mjs +1 -1
  120. package/dist/markdown-section-cache.mjs +49 -0
  121. package/dist/markdown-sections.d.mts +47 -0
  122. package/dist/{markdown-sections-9Oxr33b-.mjs → markdown-sections.mjs} +29 -703
  123. package/dist/mcp-auth.d.mts +14 -0
  124. package/dist/mcp-auth.mjs +99 -0
  125. package/dist/mcp.d.mts +5 -3
  126. package/dist/mcp.mjs +21 -9
  127. package/dist/mdx.d.mts +6 -0
  128. package/dist/mdx.mjs +7 -0
  129. package/dist/metadata.d.mts +60 -0
  130. package/dist/{metadata-BOHn0Y5K.mjs → metadata.mjs} +2 -2
  131. package/dist/native-api-reference.mjs +968 -0
  132. package/dist/okf.d.mts +42 -0
  133. package/dist/okf.mjs +145 -0
  134. package/dist/openapi-contract.mjs +154 -0
  135. package/dist/{openapi-mcp-node-C4jUpZG6.mjs → openapi-mcp-node.mjs} +1 -1
  136. package/dist/openapi-mcp.d.mts +26 -0
  137. package/dist/openapi-mcp.mjs +132 -0
  138. package/dist/openapi-operations.d.mts +71 -0
  139. package/dist/{openapi-operations-DFpaqOqA.mjs → openapi-operations.mjs} +1 -1
  140. package/dist/openapi-overlays.mjs +221 -0
  141. package/dist/openapi-references.mjs +233 -0
  142. package/dist/pagination.d.mts +7 -0
  143. package/dist/pagination.mjs +100 -0
  144. package/dist/prompt-references.d.mts +12 -0
  145. package/dist/prompt-references.mjs +132 -0
  146. package/dist/prompt-utils.d.mts +36 -0
  147. package/dist/{prompt-utils-B5g-bv7o.mjs → prompt-utils.mjs} +1 -57
  148. package/dist/reading-time.d.mts +23 -0
  149. package/dist/reading-time.mjs +58 -0
  150. package/dist/related.d.mts +7 -0
  151. package/dist/related.mjs +50 -0
  152. package/dist/retrieval-digest.d.mts +16 -0
  153. package/dist/{retrieval-digest-CT-eIEcp.mjs → retrieval-digest.mjs} +1 -1
  154. package/dist/review.d.mts +69 -0
  155. package/dist/review.mjs +562 -0
  156. package/dist/robots.d.mts +54 -0
  157. package/dist/{robots-BYJAVeI6.mjs → robots.mjs} +5 -3
  158. package/dist/runtime.d.mts +3 -3
  159. package/dist/runtime.mjs +3 -9
  160. package/dist/search.d.mts +210 -0
  161. package/dist/search.mjs +3401 -0
  162. package/dist/server.d.mts +22 -198
  163. package/dist/server.mjs +21 -19
  164. package/dist/sidebar.d.mts +15 -0
  165. package/dist/sidebar.mjs +73 -0
  166. package/dist/sitemap-server.d.mts +7 -0
  167. package/dist/{sitemap-server-Lg3YhUx4.mjs → sitemap-server.mjs} +2 -2
  168. package/dist/{sitemap-Cl8vrixd.d.mts → sitemap.d.mts} +3 -3
  169. package/dist/sitemap.mjs +254 -0
  170. package/dist/{standards-discovery-BHPc7YIX.d.mts → standards-discovery.d.mts} +2 -2
  171. package/dist/{standards-discovery-sSEojU80.mjs → standards-discovery.mjs} +3 -139
  172. package/dist/telemetry-runtime.d.mts +42 -0
  173. package/dist/telemetry-runtime.mjs +238 -0
  174. package/dist/telemetry.d.mts +15 -0
  175. package/dist/telemetry.mjs +117 -0
  176. package/dist/{types-BJFeMXWZ.d.mts → types.d.mts} +3 -2
  177. package/dist/urls.d.mts +7 -0
  178. package/dist/urls.mjs +21 -0
  179. package/dist/utils.d.mts +8 -0
  180. package/dist/utils.mjs +22 -0
  181. package/package.json +21 -3
  182. package/dist/agent-DkAidqtV.mjs +0 -9026
  183. package/dist/api-reference-BYee4zMo.mjs +0 -3046
  184. package/dist/prompt-references-DMgbu_dz.mjs +0 -1082
  185. package/dist/retrieval-digest-AOx2-LbC.d.mts +0 -367
  186. package/dist/sidebar-CFHBj29f.mjs +0 -421
  187. package/dist/sidebar-CMPbVfGP.d.mts +0 -63
  188. /package/dist/{openapi-CfCzX_a1.mjs → cli/openapi.mjs} +0 -0
package/dist/agent.mjs ADDED
@@ -0,0 +1,2729 @@
1
+ import { readDocsJsonBody } from "./http-body.mjs";
2
+ import { filterDocsPagesByAccess, isDocsPageAccessAllowed } from "./access.mjs";
3
+ import { DOCS_AI_AGENT_USER_AGENT_HEADER_PATTERN, DOCS_AI_AGENT_USER_AGENT_PATTERNS, DOCS_BOT_LIKE_USER_AGENT_HEADER_PATTERN, DOCS_BOT_LIKE_USER_AGENT_PATTERN, DOCS_TRADITIONAL_BOT_USER_AGENT_HEADER_PATTERN, DOCS_TRADITIONAL_BOT_USER_AGENT_PATTERNS } from "./agent-detection.mjs";
4
+ import { normalizeDocsUrlPath, toDocsMarkdownUrl } from "./urls.mjs";
5
+ import { resolveDocsOkfConfig, resolveDocsOkfTrustMetadata } from "./okf.mjs";
6
+ import { DEFAULT_MCP_PROTECTED_RESOURCE_METADATA_ROUTE, DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE, buildDocsMcpProtectedResourceMetadataRoute, getDocsMcpProtectedResourceMetadataRoutes, getDocsMcpResourcePaths, hasDocsMcpProtectedResourceConfig, isDocsMcpRequest } from "./mcp-auth.mjs";
7
+ import { PAGE_AGENT_CONTRACT_FIELD_SCHEMA, renderPageAgentFrontmatterYamlLines, upsertPageAgentContractMarkdown } from "./agent-contract.mjs";
8
+ import { renderDocsRelatedMarkdownLines } from "./related.mjs";
9
+ import { findDocsMarkdownSection, parseDocsMarkdownSections } from "./markdown-sections.mjs";
10
+ import { resolveDocsAudienceExposure } from "./audience-exposure.mjs";
11
+ import { findDocsAudienceMdxIssues, resolveDocsAgentMdxContent, resolveDocsAudienceMdxContent } from "./audience.mjs";
12
+ import { DOCS_CONTENT_CHANGES_FORMAT, DOCS_CONTENT_CHANGES_RESPONSE_VALUE, resolveDocsContentChangesConfig } from "./content-changes.mjs";
13
+ import { createDocsCacheableResponse, resolveDocsHttpDate } from "./http-cache.mjs";
14
+ import { AGENT_SKILL_COMPATIBILITY_MAX_LENGTH, AGENT_SKILL_DESCRIPTION_MAX_LENGTH, AGENT_SKILL_FRONTMATTER_FIELDS, AGENT_SKILL_NAME_MAX_LENGTH, DocsAgentSkillFrontmatterError, isDocsAgentSkillName, parseDocsAgentSkillFrontmatter, validateDocsAgentSkillFrontmatter } from "./agent-skills-spec.mjs";
15
+ import { AGENT_SKILLS_DISCOVERY_SCHEMA_URI, API_CATALOG_MEDIA_TYPE, API_CATALOG_PROFILE_URI, DEFAULT_A2A_AGENT_CARD_FORMAT, DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_A2A_PROTOCOL_BINDING, DEFAULT_A2A_PROTOCOL_VERSION, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_FORMAT, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_AGENT_SKILL_ARCHIVE_FORMAT, DEFAULT_AGENT_SKILL_FILE_FORMAT, DEFAULT_AGENT_SKILL_FORMAT, DEFAULT_AGENT_SKILL_RESOURCE_FORMAT, DEFAULT_API_CATALOG_FORMAT, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_FORMAT, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_LEGACY_SKILLS_ROUTE_PREFIX, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_MEDIA_TYPE, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, appendDocsDiscoveryLinkHeader, buildDocsA2AAgentCard, buildDocsAgentSkillsIndex, buildDocsApiCatalog, buildDocsLegacySkillsIndex, createDocsStandardsResponse, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, isDocsStandardsDiscoveryRequest, resolveDocsDiscoveryApiRoute, resolveDocsPublishedAgentSkill, resolveDocsStandardsDiscoveryRequest, sha256DocsDiscoveryContent } from "./standards-discovery.mjs";
16
+ import { DEFAULT_SITEMAP_MD_DOCS_ROUTE, DEFAULT_SITEMAP_MD_ROUTE, DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE, DEFAULT_SITEMAP_XML_ROUTE, resolveDocsSitemapConfig, resolveDocsSitemapRequest } from "./sitemap.mjs";
17
+
18
+ //#region src/agent.ts
19
+ const DEFAULT_DOCS_API_ROUTE = "/api/docs";
20
+ const DEFAULT_DOCS_CONFIG_FORMAT = "docs-config-map.v1";
21
+ const DEFAULT_DOCS_CONFIG_ROUTE = `${DEFAULT_DOCS_API_ROUTE}?format=config`;
22
+ const DEFAULT_DOCS_DIAGNOSTICS_FORMAT = "docs-diagnostics.v1";
23
+ const DEFAULT_DOCS_DIAGNOSTICS_ROUTE = `${DEFAULT_DOCS_API_ROUTE}?format=diagnostics`;
24
+ const DEFAULT_OPENAPI_SCHEMA_ROUTE = `${DEFAULT_DOCS_API_ROUTE}?format=openapi`;
25
+ const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
26
+ const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
27
+ const DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE = "/.well-known/agent.json";
28
+ const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
29
+ const DEFAULT_LLMS_FULL_TXT_ROUTE = "/llms-full.txt";
30
+ const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
31
+ const DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms-full.txt";
32
+ const DEFAULT_LLMS_TXT_MAX_CHARS = 5e4;
33
+ const DEFAULT_SKILL_MD_ROUTE = "/skill.md";
34
+ const DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE = "/.well-known/skill.md";
35
+ const DEFAULT_AGENTS_MD_ROUTE = "/AGENTS.md";
36
+ const DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE = "/.well-known/AGENTS.md";
37
+ const DEFAULT_AGENT_MD_ROUTE = "/AGENT.md";
38
+ const DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE = "/.well-known/AGENT.md";
39
+ const DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE = "/robots.txt";
40
+ const DEFAULT_AGENT_FEEDBACK_ROUTE = "/api/docs/agent/feedback";
41
+ const DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA = {
42
+ type: "object",
43
+ additionalProperties: false,
44
+ properties: {
45
+ task: {
46
+ type: "string",
47
+ description: "Short description of what the agent was trying to do."
48
+ },
49
+ understanding: {
50
+ type: "string",
51
+ description: "How well the docs supported the task, e.g. \"partial\" or \"clear\"."
52
+ },
53
+ outcome: {
54
+ type: "string",
55
+ description: "What happened after reading the docs, e.g. \"implemented\" or \"blocked\"."
56
+ },
57
+ confidence: {
58
+ type: "number",
59
+ minimum: 0,
60
+ maximum: 1,
61
+ description: "Confidence score from 0 to 1."
62
+ },
63
+ neededCodeReading: {
64
+ type: "boolean",
65
+ description: "Whether the agent still needed to inspect repository code."
66
+ },
67
+ missingContext: {
68
+ type: "array",
69
+ items: { type: "string" },
70
+ description: "Important details the docs did not provide clearly enough."
71
+ },
72
+ docIssues: {
73
+ type: "array",
74
+ items: { type: "string" },
75
+ description: "Specific documentation problems encountered during the task."
76
+ },
77
+ suggestedImprovement: {
78
+ type: "string",
79
+ description: "Concrete suggestion for improving the docs page or examples."
80
+ }
81
+ },
82
+ required: ["task", "outcome"]
83
+ };
84
+ const DEFAULT_DOCS_DIAGNOSTICS_MCP_TOOLS = {
85
+ listDocs: true,
86
+ listPages: true,
87
+ listPageSections: true,
88
+ readPage: true,
89
+ readPages: true,
90
+ submitFeedback: true,
91
+ listTasks: true,
92
+ readTask: true,
93
+ searchDocs: true,
94
+ getNavigation: true,
95
+ getCodeExamples: true,
96
+ getConfigSchema: true,
97
+ getContext: true
98
+ };
99
+ const DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER = "Signature-Agent";
100
+ const DOCS_MARKDOWN_SECTION_INDEX_FORMAT = "docs-markdown-sections.v2";
101
+ const DOCS_LLMS_TXT_DIRECTIVE_LINE = "LLM index: /llms.txt";
102
+ const DOCS_MARKDOWN_GENERATED_PREAMBLE_FIELD = "x_farming_labs_generated_preamble";
103
+ const DOCS_MCP_SERVICE_SUBDOMAIN_LABELS = new Set([
104
+ "api",
105
+ "developer",
106
+ "developers",
107
+ "dev",
108
+ "docs",
109
+ "help",
110
+ "mcp",
111
+ "reference"
112
+ ]);
113
+ const COMMON_SECOND_LEVEL_PUBLIC_SUFFIX_LABELS = new Set([
114
+ "ac",
115
+ "co",
116
+ "com",
117
+ "edu",
118
+ "go",
119
+ "gov",
120
+ "mil",
121
+ "ne",
122
+ "net",
123
+ "or",
124
+ "org"
125
+ ]);
126
+ const DOCS_CONFIG_MAP_TOP_LEVEL_KEYS = [
127
+ "entry",
128
+ "docsPath",
129
+ "contentDir",
130
+ "i18n",
131
+ "staticExport",
132
+ "theme",
133
+ "analytics",
134
+ "telemetry",
135
+ "cloud",
136
+ "observability",
137
+ "github",
138
+ "nav",
139
+ "themeToggle",
140
+ "breadcrumb",
141
+ "sidebar",
142
+ "components",
143
+ "onCopyClick",
144
+ "codeBlocks",
145
+ "feedback",
146
+ "mcp",
147
+ "icons",
148
+ "pageActions",
149
+ "search",
150
+ "lastUpdated",
151
+ "readingTime",
152
+ "ai",
153
+ "ordering",
154
+ "llmsTxt",
155
+ "sitemap",
156
+ "robots",
157
+ "changelog",
158
+ "apiReference",
159
+ "agent",
160
+ "review",
161
+ "metadata",
162
+ "og"
163
+ ];
164
+ const DOCS_MARKDOWN_SECTION_MAX_BUDGET = 1e6;
165
+ function normalizeDocsPathSegment(value) {
166
+ return value.replace(/^\/+|\/+$/g, "");
167
+ }
168
+ function isPlainObject(value) {
169
+ return typeof value === "object" && value !== null && !Array.isArray(value);
170
+ }
171
+ function isDocsConfigRequest(url) {
172
+ return url.searchParams.get("format")?.trim() === "config";
173
+ }
174
+ function isDocsDiagnosticsRequest(url) {
175
+ return url.searchParams.get("format")?.trim() === "diagnostics";
176
+ }
177
+ /** Prefer an explicitly configured API route; otherwise infer a query-form route from the request. */
178
+ function resolveDocsRequestApiRoute(url, configuredApiRoute) {
179
+ const configured = configuredApiRoute?.trim();
180
+ if (configured) return resolveDocsDiscoveryApiRoute(configured);
181
+ const fallback = resolveDocsDiscoveryApiRoute();
182
+ const pathname = normalizeDocsUrlPath(url.pathname);
183
+ const hasPublicRepresentationSuffix = /\.(?:md|txt|xml)$/i.test(pathname);
184
+ if (pathname.startsWith("/.well-known/") || hasPublicRepresentationSuffix) return fallback;
185
+ return Boolean(url.searchParams.get("format")?.trim()) || url.searchParams.get("agent")?.trim() === "spec" || url.searchParams.has("query") || url.searchParams.get("feedback")?.trim() === "agent" ? resolveDocsDiscoveryApiRoute(pathname) : fallback;
186
+ }
187
+ function buildDocsConfigMap(config, options = {}) {
188
+ const pointers = {};
189
+ const values = serializeDocsConfigMapValue(pickDocsConfigMapValues(config), [], /* @__PURE__ */ new WeakSet(), pointers);
190
+ const mappedValues = isPlainObject(values) ? values : {};
191
+ const language = containsDocsConfigKind(mappedValues, "jsx") ? "tsx" : "ts";
192
+ return {
193
+ schemaVersion: 1,
194
+ format: DEFAULT_DOCS_CONFIG_FORMAT,
195
+ source: {
196
+ file: options.file ?? (language === "tsx" ? "docs.config.tsx" : "docs.config.ts"),
197
+ language
198
+ },
199
+ serialization: {
200
+ mode: "json-safe",
201
+ redacted: "Scalar values under secret-like keys such as apiKey, token, secret, password, authorization, and credential are redacted unless the key ends with Env.",
202
+ nonSerializable: "described"
203
+ },
204
+ values: mappedValues,
205
+ pointers
206
+ };
207
+ }
208
+ function buildDocsDiagnostics(config, options = {}) {
209
+ const input = config;
210
+ const entry = normalizeDocsPathSegment(stringConfigValue(input.entry) ?? options.entry ?? "docs");
211
+ const docsRoute = routeFromConfigPath(stringConfigValue(input.docsPath) ?? entry);
212
+ const cloud = isPlainObject(input.cloud) ? input.cloud : void 0;
213
+ const apiRoute = resolveDocsDiscoveryApiRoute(options.apiRoute ?? stringConfigValue(cloud?.apiRoute));
214
+ const apiQueryRoute = (query) => `${apiRoute}?${query}`;
215
+ const staticExport = input.staticExport === true;
216
+ const i18n = options.i18n ?? null;
217
+ const localesEnabled = Boolean(i18n?.locales.length);
218
+ const search = resolveDocsDiagnosticsSearch(input.search, staticExport);
219
+ const contentChanges = resolveDocsContentChangesConfig((isPlainObject(input.agent) ? input.agent : void 0)?.contentChanges, { staticExport });
220
+ const ai = resolveDocsDiagnosticsAi(input.ai, staticExport);
221
+ const llms = resolveDocsDiagnosticsLlms(input.llmsTxt);
222
+ const apiCatalog = resolveDocsDiagnosticsApiCatalog(input.llmsTxt, staticExport, options.apiCatalog);
223
+ const sitemapConfig = resolveDocsSitemapConfig(input.sitemap);
224
+ const robotsEnabled = isRobotsDiscoveryEnabled(input.robots);
225
+ const openapiConfig = resolveDocsOpenApiDiscoveryConfig(options.openapi ?? input.apiReference);
226
+ const openapiUrl = resolveDocsOpenApiDiscoveryUrl(openapiConfig, apiRoute);
227
+ const apiReferenceRoute = resolveDocsDiagnosticsApiReferenceRoute(input.apiReference);
228
+ const mcp = options.mcp ?? resolveDocsDiagnosticsMcp(input.mcp);
229
+ const feedback = options.feedback ?? resolveDocsAgentFeedbackConfig(input.feedback);
230
+ const agentFeedbackEnabled = feedback.enabled === true;
231
+ const agentFeedbackRoute = feedback.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
232
+ const agentFeedbackSchemaRoute = feedback.schemaRoute ?? `${agentFeedbackRoute}/schema`;
233
+ const humanFeedback = isDocsDiagnosticsHumanFeedbackEnabled(input.feedback);
234
+ const warnings = [];
235
+ const errors = [];
236
+ if (staticExport) warnings.push({
237
+ severity: "warning",
238
+ code: "static-export-runtime-api",
239
+ path: "/staticExport",
240
+ message: `staticExport is enabled; runtime API-backed capabilities at ${apiRoute} are unavailable in production static export builds.`
241
+ });
242
+ if (staticExport && ai.configured) errors.push({
243
+ severity: "error",
244
+ code: "ai-static-export",
245
+ path: "/ai/enabled",
246
+ message: `Ask AI requires the runtime ${apiRoute} POST handler and will not run in static export builds.`
247
+ });
248
+ if (ai.enabled && !search.enabled) warnings.push({
249
+ severity: "warning",
250
+ code: "ai-without-search",
251
+ path: "/ai",
252
+ message: "Ask AI is enabled while docs search is disabled, so retrieval context may be unavailable."
253
+ });
254
+ const adapter = options.adapter?.trim() || null;
255
+ if (adapter && adapter !== "next") {
256
+ if (!stringConfigValue(input.contentDir)) warnings.push({
257
+ severity: "warning",
258
+ code: "missing-content-dir",
259
+ path: "/contentDir",
260
+ message: `${adapter} docs usually need contentDir so the server can find markdown content.`
261
+ });
262
+ if (!isPlainObject(input.nav)) warnings.push({
263
+ severity: "warning",
264
+ code: "missing-nav",
265
+ path: "/nav",
266
+ message: `${adapter} docs usually need nav.title and nav.url for sidebar branding and root navigation.`
267
+ });
268
+ }
269
+ validateDocsDiagnosticsSearchConfig(input.search, errors);
270
+ return {
271
+ schemaVersion: 1,
272
+ format: DEFAULT_DOCS_DIAGNOSTICS_FORMAT,
273
+ ok: errors.length === 0,
274
+ adapter,
275
+ routes: {
276
+ docs: docsRoute,
277
+ api: apiRoute,
278
+ config: apiQueryRoute("format=config"),
279
+ diagnostics: apiQueryRoute("format=diagnostics"),
280
+ agentSpec: apiRoute === DEFAULT_DOCS_API_ROUTE ? DEFAULT_AGENT_SPEC_ROUTE : apiQueryRoute("agent=spec"),
281
+ apiCatalog: apiCatalog.enabled ? DEFAULT_API_CATALOG_ROUTE : null,
282
+ agentSkillsIndex: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
283
+ agentSkillsArtifact: DEFAULT_AGENT_SKILLS_ROUTE_PATTERN,
284
+ agents: apiQueryRoute("format=agents"),
285
+ skill: apiQueryRoute("format=skill"),
286
+ search: search.enabled ? apiQueryRoute("query={query}") : null,
287
+ agentSearch: search.enabled ? apiQueryRoute("query={query}&audience=agent") : null,
288
+ contentChanges: contentChanges.enabled ? apiQueryRoute(`audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}`) : null,
289
+ askAi: ai.enabled ? apiRoute : null,
290
+ mcp: mcp.enabled ? mcp.route : null,
291
+ llmsTxt: llms.enabled ? DEFAULT_LLMS_TXT_ROUTE : null,
292
+ llmsFullTxt: llms.enabled ? DEFAULT_LLMS_FULL_TXT_ROUTE : null,
293
+ sitemapXml: sitemapConfig.enabled && sitemapConfig.xml.enabled ? sitemapConfig.xml.route : null,
294
+ sitemapMarkdown: sitemapConfig.enabled && sitemapConfig.markdown.enabled ? sitemapConfig.markdown.route : null,
295
+ robots: robotsEnabled ? DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE : null,
296
+ openapi: openapiConfig.enabled ? openapiUrl ?? apiQueryRoute("format=openapi") : null,
297
+ apiReference: openapiConfig.enabled ? apiReferenceRoute : null
298
+ },
299
+ features: {
300
+ staticExport: { status: staticExport ? "enabled" : "disabled" },
301
+ config: {
302
+ status: "enabled",
303
+ route: apiQueryRoute("format=config")
304
+ },
305
+ diagnostics: {
306
+ status: "enabled",
307
+ route: apiQueryRoute("format=diagnostics")
308
+ },
309
+ apiCatalog: {
310
+ status: apiCatalog.enabled ? "enabled" : "disabled",
311
+ reason: apiCatalog.reason,
312
+ route: apiCatalog.enabled ? DEFAULT_API_CATALOG_ROUTE : null,
313
+ transport: "GET/HEAD"
314
+ },
315
+ search: {
316
+ status: search.enabled ? "enabled" : "disabled",
317
+ reason: search.reason,
318
+ route: search.enabled ? apiQueryRoute("query={query}") : null,
319
+ routes: {
320
+ human: search.enabled ? apiQueryRoute("query={query}") : null,
321
+ agent: search.enabled ? apiQueryRoute("query={query}&audience=agent") : null,
322
+ structuredAgent: search.enabled ? apiQueryRoute("query={query}&audience=agent&response=structured") : null,
323
+ explainedAgent: search.enabled ? apiQueryRoute("query={query}&audience=agent&response=structured&explain=true") : null,
324
+ facets: search.enabled ? apiQueryRoute("audience=agent&response=facets") : null
325
+ },
326
+ agentEndpoint: search.enabled ? apiQueryRoute("query={query}&audience=agent") : null,
327
+ structuredAgentEndpoint: search.enabled ? apiQueryRoute("query={query}&audience=agent&response=structured") : null,
328
+ explainedAgentEndpoint: search.enabled ? apiQueryRoute("query={query}&audience=agent&response=structured&explain=true") : null,
329
+ facetsEndpoint: search.enabled ? apiQueryRoute("audience=agent&response=facets") : null,
330
+ audienceParam: "audience",
331
+ defaultAudience: "human",
332
+ supportedAudiences: ["human", "agent"],
333
+ filterParams: {
334
+ framework: "framework",
335
+ version: "version",
336
+ package: "package",
337
+ tags: "tags"
338
+ },
339
+ responseFormat: "docs-search.v1",
340
+ explainParam: "explain",
341
+ explainValue: "true",
342
+ explanationField: "explanation",
343
+ explanationFormat: "docs-search-explanation.v1",
344
+ facetsResponseFormat: "docs-search-facets.v1",
345
+ warningsField: "warnings",
346
+ facetParam: "facet",
347
+ limitParam: "limit",
348
+ cursorParam: "cursor",
349
+ nextCursorField: "nextCursor",
350
+ hasMoreField: "hasMore",
351
+ totalField: "total",
352
+ provider: search.provider,
353
+ transport: "GET"
354
+ },
355
+ contentChanges: {
356
+ status: contentChanges.enabled ? "enabled" : "disabled",
357
+ reason: staticExport ? "Runtime content-change feeds are unavailable in static exports." : contentChanges.enabled ? void 0 : "configured-disabled",
358
+ route: contentChanges.enabled ? apiQueryRoute(`audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}`) : null,
359
+ transport: "GET",
360
+ format: DOCS_CONTENT_CHANGES_FORMAT,
361
+ audienceParam: "audience",
362
+ defaultAudience: "agent",
363
+ supportedAudiences: ["human", "agent"],
364
+ sinceParam: "since",
365
+ generationField: "indexGeneration",
366
+ resetRequiredField: "resetRequired"
367
+ },
368
+ ai: {
369
+ status: ai.enabled ? "enabled" : "disabled",
370
+ reason: ai.reason,
371
+ route: ai.enabled ? apiRoute : null,
372
+ mode: ai.mode,
373
+ transport: "POST"
374
+ },
375
+ mcp: {
376
+ status: mcp.enabled ? "enabled" : "disabled",
377
+ route: mcp.enabled ? mcp.route : null,
378
+ transport: "GET/POST",
379
+ tools: mcp.tools,
380
+ prompts: mcp.prompts ?? {
381
+ enabled: false,
382
+ contracts: false,
383
+ goldenTasks: []
384
+ }
385
+ },
386
+ feedback: {
387
+ status: agentFeedbackEnabled || humanFeedback ? "enabled" : "disabled",
388
+ human: humanFeedback,
389
+ agent: agentFeedbackEnabled,
390
+ routes: {
391
+ agentSubmit: agentFeedbackEnabled ? agentFeedbackRoute : null,
392
+ agentSchema: agentFeedbackEnabled ? agentFeedbackSchemaRoute : null
393
+ }
394
+ },
395
+ llmsTxt: {
396
+ status: llms.enabled ? "enabled" : "disabled",
397
+ reason: llms.reason,
398
+ routes: {
399
+ txt: llms.enabled ? DEFAULT_LLMS_TXT_ROUTE : null,
400
+ full: llms.enabled ? DEFAULT_LLMS_FULL_TXT_ROUTE : null,
401
+ wellKnownTxt: llms.enabled ? DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE : null,
402
+ wellKnownFull: llms.enabled ? DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE : null
403
+ }
404
+ },
405
+ sitemap: {
406
+ status: sitemapConfig.enabled ? "enabled" : "disabled",
407
+ routes: {
408
+ xml: sitemapConfig.enabled && sitemapConfig.xml.enabled ? sitemapConfig.xml.route : null,
409
+ markdown: sitemapConfig.enabled && sitemapConfig.markdown.enabled ? sitemapConfig.markdown.route : null,
410
+ docsMarkdown: sitemapConfig.enabled && sitemapConfig.markdown.enabled ? sitemapConfig.markdown.docsRoute ?? null : null,
411
+ wellKnownMarkdown: sitemapConfig.enabled && sitemapConfig.markdown.enabled ? sitemapConfig.markdown.wellKnownRoute : null
412
+ }
413
+ },
414
+ robots: {
415
+ status: robotsEnabled ? "enabled" : "disabled",
416
+ route: robotsEnabled ? DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE : null
417
+ },
418
+ apiReference: {
419
+ status: openapiConfig.enabled ? "enabled" : "disabled",
420
+ route: openapiConfig.enabled ? apiReferenceRoute : null,
421
+ routes: { openapi: openapiConfig.enabled ? openapiUrl ?? apiQueryRoute("format=openapi") : null },
422
+ provider: openapiConfig.source
423
+ },
424
+ agents: {
425
+ status: "enabled",
426
+ routes: {
427
+ default: DEFAULT_AGENTS_MD_ROUTE,
428
+ wellKnown: DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE,
429
+ api: apiQueryRoute("format=agents")
430
+ }
431
+ },
432
+ skills: {
433
+ status: "enabled",
434
+ transport: "GET/HEAD",
435
+ routes: {
436
+ default: DEFAULT_SKILL_MD_ROUTE,
437
+ wellKnown: DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE,
438
+ api: apiQueryRoute("format=skill"),
439
+ index: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
440
+ artifact: DEFAULT_AGENT_SKILLS_ROUTE_PATTERN
441
+ }
442
+ },
443
+ locales: {
444
+ status: localesEnabled ? "enabled" : "disabled",
445
+ provider: localesEnabled ? "query-param" : void 0
446
+ }
447
+ },
448
+ warnings,
449
+ errors
450
+ };
451
+ }
452
+ function pickDocsConfigMapValues(config) {
453
+ const output = {};
454
+ for (const key of DOCS_CONFIG_MAP_TOP_LEVEL_KEYS) if (Object.prototype.hasOwnProperty.call(config, key)) output[key] = config[key];
455
+ return output;
456
+ }
457
+ function serializeDocsConfigMapValue(value, path, seen, pointers, depth = 0) {
458
+ const key = path.at(-1) ?? "";
459
+ const pointerPath = toDocsConfigJsonPointer(path);
460
+ const setPointer = (kind) => {
461
+ if (path.length > 0) pointers[pointerPath] = {
462
+ path: toDocsConfigDotPath(path),
463
+ kind
464
+ };
465
+ };
466
+ if (value === void 0) return void 0;
467
+ if (shouldRedactDocsConfigKey(key) && value !== null && typeof value !== "object") {
468
+ setPointer("secret");
469
+ return {
470
+ $kind: "secret",
471
+ value: "[redacted]"
472
+ };
473
+ }
474
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
475
+ setPointer(typeof value);
476
+ return value;
477
+ }
478
+ if (typeof value === "number") {
479
+ setPointer("number");
480
+ return Number.isFinite(value) ? value : String(value);
481
+ }
482
+ if (typeof value === "bigint") {
483
+ setPointer("bigint");
484
+ return {
485
+ $kind: "bigint",
486
+ value: value.toString()
487
+ };
488
+ }
489
+ if (typeof value === "symbol") {
490
+ setPointer("symbol");
491
+ return {
492
+ $kind: "symbol",
493
+ value: String(value)
494
+ };
495
+ }
496
+ if (typeof value === "function") {
497
+ setPointer("function");
498
+ return {
499
+ $kind: "function",
500
+ name: getDocsConfigFunctionName(value)
501
+ };
502
+ }
503
+ if (value instanceof Date) {
504
+ setPointer("date");
505
+ return {
506
+ $kind: "date",
507
+ value: value.toISOString()
508
+ };
509
+ }
510
+ if (value instanceof URL) {
511
+ setPointer("url");
512
+ return value.toString();
513
+ }
514
+ if (depth >= 10) {
515
+ setPointer("truncated");
516
+ return {
517
+ $kind: "truncated",
518
+ reason: "Exceeded max depth of 10."
519
+ };
520
+ }
521
+ if (Array.isArray(value)) {
522
+ setPointer("array");
523
+ return value.map((entry, index) => serializeDocsConfigMapValue(entry, [...path, String(index)], seen, pointers, depth + 1)).filter((entry) => entry !== void 0);
524
+ }
525
+ if (isDocsConfigReactElement(value)) {
526
+ setPointer("jsx");
527
+ return {
528
+ $kind: "jsx",
529
+ component: getDocsConfigReactElementName(value)
530
+ };
531
+ }
532
+ if (seen.has(value)) {
533
+ setPointer("circular");
534
+ return { $kind: "circular" };
535
+ }
536
+ seen.add(value);
537
+ const output = {};
538
+ for (const [childKey, childValue] of Object.entries(value)) {
539
+ const serialized = serializeDocsConfigMapValue(childValue, [...path, childKey], seen, pointers, depth + 1);
540
+ if (serialized !== void 0) output[childKey] = serialized;
541
+ }
542
+ seen.delete(value);
543
+ if (path.length === 1 && path[0] === "theme") {
544
+ setPointer("theme");
545
+ return {
546
+ $kind: "theme",
547
+ ...output
548
+ };
549
+ }
550
+ setPointer("object");
551
+ return output;
552
+ }
553
+ function shouldRedactDocsConfigKey(key) {
554
+ const normalized = key.replace(/[-_]/g, "").toLowerCase();
555
+ if (!normalized || normalized === "env" || normalized.endsWith("env")) return false;
556
+ return normalized.includes("apikey") || normalized.includes("token") || normalized.includes("secret") || normalized.includes("password") || normalized.includes("authorization") || normalized.includes("credential");
557
+ }
558
+ function isDocsConfigReactElement(value) {
559
+ if (!isPlainObject(value) || !("$$typeof" in value)) return false;
560
+ return String(value.$$typeof).includes("react.");
561
+ }
562
+ function getDocsConfigReactElementName(value) {
563
+ const type = value.type;
564
+ if (typeof type === "string") return type;
565
+ if (typeof type === "function") return getDocsConfigFunctionName(type);
566
+ if (isPlainObject(type)) {
567
+ const displayName = type.displayName;
568
+ if (typeof displayName === "string") return displayName;
569
+ const render = type.render;
570
+ if (typeof render === "function") return getDocsConfigFunctionName(render);
571
+ }
572
+ return "unknown";
573
+ }
574
+ function getDocsConfigFunctionName(value) {
575
+ const displayName = value.displayName;
576
+ return typeof displayName === "string" ? displayName : value.name || "anonymous";
577
+ }
578
+ function toDocsConfigJsonPointer(path) {
579
+ return path.length === 0 ? "" : `/${path.map(escapeDocsConfigJsonPointerPart).join("/")}`;
580
+ }
581
+ function escapeDocsConfigJsonPointerPart(value) {
582
+ return value.replace(/~/g, "~0").replace(/\//g, "~1");
583
+ }
584
+ function toDocsConfigDotPath(path) {
585
+ return path.map((part, index) => /^\d+$/.test(part) ? `[${part}]` : index === 0 ? part : `.${part}`).join("");
586
+ }
587
+ function containsDocsConfigKind(value, kind) {
588
+ if (!isPlainObject(value)) {
589
+ if (!Array.isArray(value)) return false;
590
+ }
591
+ if (Array.isArray(value)) return value.some((entry) => containsDocsConfigKind(entry, kind));
592
+ if (value["$kind"] === kind) return true;
593
+ return Object.values(value).some((entry) => containsDocsConfigKind(entry, kind));
594
+ }
595
+ function stringConfigValue(value) {
596
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
597
+ }
598
+ function routeFromConfigPath(value) {
599
+ const normalized = normalizeDocsPathSegment(value);
600
+ return normalized ? `/${normalized}` : "/";
601
+ }
602
+ function resolveDocsDiagnosticsSearch(search, staticExport) {
603
+ const provider = isPlainObject(search) ? stringConfigValue(search.provider) : "simple";
604
+ const configured = search !== false && !(isPlainObject(search) && search.enabled === false);
605
+ if (staticExport) return {
606
+ enabled: false,
607
+ configured,
608
+ provider,
609
+ reason: "static-export"
610
+ };
611
+ if (!configured) return {
612
+ enabled: false,
613
+ configured,
614
+ provider,
615
+ reason: "configured-disabled"
616
+ };
617
+ return {
618
+ enabled: true,
619
+ configured,
620
+ provider
621
+ };
622
+ }
623
+ function resolveDocsDiagnosticsAi(ai, staticExport) {
624
+ const configured = isPlainObject(ai) && ai.enabled === true;
625
+ const mode = isPlainObject(ai) ? stringConfigValue(ai.mode) : void 0;
626
+ if (!configured) return {
627
+ enabled: false,
628
+ configured,
629
+ reason: "not-configured"
630
+ };
631
+ if (staticExport) return {
632
+ enabled: false,
633
+ configured,
634
+ mode,
635
+ reason: "static-export"
636
+ };
637
+ return {
638
+ enabled: true,
639
+ configured,
640
+ mode
641
+ };
642
+ }
643
+ function resolveDocsDiagnosticsLlms(llmsTxt) {
644
+ return llmsTxt !== false && !(isPlainObject(llmsTxt) && llmsTxt.enabled === false) ? { enabled: true } : {
645
+ enabled: false,
646
+ reason: "configured-disabled"
647
+ };
648
+ }
649
+ function resolveDocsDiagnosticsApiCatalog(llmsTxt, staticExport, explicit) {
650
+ if (staticExport) return {
651
+ enabled: false,
652
+ reason: "static-export"
653
+ };
654
+ if (explicit !== void 0) return explicit ? { enabled: true } : {
655
+ enabled: false,
656
+ reason: "configured-disabled"
657
+ };
658
+ if (isPlainObject(llmsTxt) && llmsTxt.apiCatalog === false) return {
659
+ enabled: false,
660
+ reason: "llms-txt-api-catalog-disabled"
661
+ };
662
+ return { enabled: true };
663
+ }
664
+ function resolveDocsDiagnosticsMcp(mcp) {
665
+ const config = isPlainObject(mcp) ? mcp : {};
666
+ const tools = isPlainObject(config.tools) ? config.tools : {};
667
+ return {
668
+ enabled: typeof mcp === "boolean" ? mcp : config.enabled !== false,
669
+ route: normalizeDocsDiagnosticsMcpRoute(config.route),
670
+ name: stringConfigValue(config.name) ?? "Documentation",
671
+ version: stringConfigValue(config.version) ?? "0.0.0",
672
+ tools: {
673
+ ...DEFAULT_DOCS_DIAGNOSTICS_MCP_TOOLS,
674
+ listDocs: tools.listDocs !== false,
675
+ listPages: tools.listPages !== false,
676
+ listPageSections: tools.listPageSections !== false,
677
+ readPage: tools.readPage !== false,
678
+ listTasks: tools.listTasks !== false,
679
+ readTask: tools.readTask !== false,
680
+ searchDocs: tools.searchDocs !== false,
681
+ getNavigation: tools.getNavigation !== false,
682
+ getCodeExamples: tools.getCodeExamples !== false,
683
+ getConfigSchema: tools.getConfigSchema !== false,
684
+ getContext: tools.getContext !== false
685
+ }
686
+ };
687
+ }
688
+ function normalizeDocsDiagnosticsMcpRoute(route) {
689
+ const value = stringConfigValue(route);
690
+ if (!value) return DEFAULT_MCP_ROUTE;
691
+ const normalized = `/${value}`.replace(/\/+/g, "/");
692
+ return normalized !== "/" ? normalized.replace(/\/+$/, "") : DEFAULT_MCP_ROUTE;
693
+ }
694
+ function isDocsDiagnosticsHumanFeedbackEnabled(feedback) {
695
+ return feedback === true || isPlainObject(feedback) && feedback.enabled !== false;
696
+ }
697
+ function resolveDocsDiagnosticsApiReferenceRoute(apiReference) {
698
+ if (isPlainObject(apiReference)) return routeFromConfigPath(stringConfigValue(apiReference.path) ?? "api-reference");
699
+ return "/api-reference";
700
+ }
701
+ function validateDocsDiagnosticsSearchConfig(search, errors) {
702
+ if (!isPlainObject(search) || search.enabled === false) return;
703
+ const provider = stringConfigValue(search.provider) ?? "simple";
704
+ const requireString = (key) => {
705
+ if (!stringConfigValue(search[key])) errors.push({
706
+ severity: "error",
707
+ code: `missing-search-${key.replace(/[A-Z]/g, (value) => `-${value.toLowerCase()}`)}`,
708
+ path: `/search/${key}`,
709
+ message: `Search provider "${provider}" requires search.${key}.`
710
+ });
711
+ };
712
+ if (provider === "algolia") {
713
+ requireString("appId");
714
+ requireString("indexName");
715
+ requireString("searchApiKey");
716
+ }
717
+ if (provider === "typesense") {
718
+ requireString("baseUrl");
719
+ requireString("collection");
720
+ requireString("apiKey");
721
+ }
722
+ if (provider === "mcp") requireString("endpoint");
723
+ const syncNamespace = stringConfigValue(search.syncNamespace);
724
+ if (syncNamespace && syncNamespace.length > 1024) errors.push({
725
+ severity: "error",
726
+ code: "invalid-search-sync-namespace",
727
+ path: "/search/syncNamespace",
728
+ message: "search.syncNamespace must be 1024 characters or fewer."
729
+ });
730
+ if (provider === "custom" && !("adapter" in search)) errors.push({
731
+ severity: "error",
732
+ code: "missing-search-adapter",
733
+ path: "/search/adapter",
734
+ message: "Search provider \"custom\" requires search.adapter."
735
+ });
736
+ }
737
+ function normalizeDocsAgentFeedbackRoute(route, fallback = DEFAULT_AGENT_FEEDBACK_ROUTE) {
738
+ if (!route || route.trim().length === 0) return fallback;
739
+ const normalized = `/${route}`.replace(/\/+/g, "/");
740
+ return normalized !== "/" ? normalized.replace(/\/+$/, "") : fallback;
741
+ }
742
+ function buildDocsAgentFeedbackSchema(payloadSchema) {
743
+ return {
744
+ type: "object",
745
+ additionalProperties: false,
746
+ properties: {
747
+ context: {
748
+ type: "object",
749
+ additionalProperties: false,
750
+ properties: {
751
+ page: { type: "string" },
752
+ url: { type: "string" },
753
+ slug: { type: "string" },
754
+ locale: { type: "string" },
755
+ source: { type: "string" }
756
+ }
757
+ },
758
+ payload: payloadSchema
759
+ },
760
+ required: ["payload"]
761
+ };
762
+ }
763
+ function resolveDocsAgentFeedbackConfig(feedback) {
764
+ const route = normalizeDocsAgentFeedbackRoute();
765
+ const enabled = {
766
+ enabled: true,
767
+ route,
768
+ schemaRoute: `${route}/schema`,
769
+ payloadSchema: DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA,
770
+ schema: buildDocsAgentFeedbackSchema(DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA)
771
+ };
772
+ const disabled = {
773
+ enabled: false,
774
+ route,
775
+ schemaRoute: `${route}/schema`,
776
+ payloadSchema: DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA,
777
+ schema: buildDocsAgentFeedbackSchema(DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA)
778
+ };
779
+ if (feedback === false) return disabled;
780
+ if (!feedback || feedback === true || typeof feedback !== "object") return enabled;
781
+ const agent = feedback.agent;
782
+ if (agent === false) return disabled;
783
+ if (!agent) return enabled;
784
+ if (agent === true) return enabled;
785
+ const resolvedRoute = normalizeDocsAgentFeedbackRoute(agent.route, route);
786
+ const resolvedSchemaRoute = normalizeDocsAgentFeedbackRoute(agent.schemaRoute, `${resolvedRoute}/schema`);
787
+ const payloadSchema = agent.schema ?? DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA;
788
+ return {
789
+ enabled: agent.enabled !== false,
790
+ route: resolvedRoute,
791
+ schemaRoute: resolvedSchemaRoute,
792
+ payloadSchema,
793
+ schema: buildDocsAgentFeedbackSchema(payloadSchema),
794
+ onFeedback: agent.onFeedback
795
+ };
796
+ }
797
+ function resolveDocsAgentFeedbackRequest(url, feedback) {
798
+ if (!feedback.enabled) return null;
799
+ const feedbackMode = url.searchParams.get("feedback")?.trim();
800
+ const schemaMode = url.searchParams.get("schema")?.trim();
801
+ if (feedbackMode === "agent") return { kind: schemaMode === "1" || schemaMode === "true" ? "schema" : "submit" };
802
+ const pathname = normalizeDocsUrlPath(url.pathname);
803
+ if (pathname === feedback.schemaRoute) return { kind: "schema" };
804
+ if (pathname === feedback.route) return { kind: "submit" };
805
+ return null;
806
+ }
807
+ function normalizeDocsAgentFeedbackContext(value) {
808
+ if (!isPlainObject(value)) return void 0;
809
+ const context = {};
810
+ if (typeof value.page === "string") context.page = value.page;
811
+ if (typeof value.url === "string") context.url = value.url;
812
+ if (typeof value.slug === "string") context.slug = value.slug;
813
+ if (typeof value.locale === "string") context.locale = value.locale;
814
+ if (typeof value.source === "string") context.source = value.source;
815
+ return Object.keys(context).length > 0 ? context : void 0;
816
+ }
817
+ async function parseDocsAgentFeedbackData(request) {
818
+ const parsed = await readDocsJsonBody(request);
819
+ if (!parsed.ok) {
820
+ if (parsed.reason === "request_too_large") return {
821
+ ok: false,
822
+ response: Response.json({
823
+ error: `Request body exceeds the ${parsed.maxBodyBytes} byte limit`,
824
+ code: parsed.reason,
825
+ maxBodyBytes: parsed.maxBodyBytes
826
+ }, { status: 413 })
827
+ };
828
+ return {
829
+ ok: false,
830
+ response: Response.json({ error: "Agent feedback body must be valid JSON" }, { status: 400 })
831
+ };
832
+ }
833
+ const body = parsed.value;
834
+ if (!isPlainObject(body)) return {
835
+ ok: false,
836
+ response: Response.json({ error: "Agent feedback body must be an object" }, { status: 400 })
837
+ };
838
+ if (!isPlainObject(body.payload)) return {
839
+ ok: false,
840
+ response: Response.json({ error: "Agent feedback body must include a payload object" }, { status: 400 })
841
+ };
842
+ return {
843
+ ok: true,
844
+ data: {
845
+ context: normalizeDocsAgentFeedbackContext(body.context),
846
+ payload: body.payload
847
+ }
848
+ };
849
+ }
850
+ function validateDocsAgentFeedbackPayload(value, schema, valuePath = "payload") {
851
+ const schemaType = typeof schema.type === "string" ? schema.type : void 0;
852
+ if (Array.isArray(schema.enum) && !schema.enum.some((entry) => Object.is(entry, value))) return `${valuePath} must be one of the configured enum values`;
853
+ if (schemaType === "object" || !schemaType && (schema.properties || schema.required)) {
854
+ if (!isPlainObject(value)) return `${valuePath} must be an object`;
855
+ const properties = isPlainObject(schema.properties) ? schema.properties : {};
856
+ const required = Array.isArray(schema.required) ? schema.required.filter((entry) => typeof entry === "string") : [];
857
+ for (const key of required) if (!(key in value)) return `${valuePath}.${key} is required`;
858
+ if (schema.additionalProperties === false) {
859
+ for (const key of Object.keys(value)) if (!(key in properties)) return `${valuePath}.${key} is not allowed`;
860
+ }
861
+ for (const [key, propertySchema] of Object.entries(properties)) {
862
+ if (!(key in value)) continue;
863
+ if (!isPlainObject(propertySchema)) continue;
864
+ const error = validateDocsAgentFeedbackPayload(value[key], propertySchema, `${valuePath}.${key}`);
865
+ if (error) return error;
866
+ }
867
+ return;
868
+ }
869
+ if (schemaType === "array") {
870
+ if (!Array.isArray(value)) return `${valuePath} must be an array`;
871
+ if (!isPlainObject(schema.items)) return void 0;
872
+ for (const [index, item] of value.entries()) {
873
+ const error = validateDocsAgentFeedbackPayload(item, schema.items, `${valuePath}[${index}]`);
874
+ if (error) return error;
875
+ }
876
+ return;
877
+ }
878
+ if (schemaType === "string") {
879
+ if (typeof value !== "string") return `${valuePath} must be a string`;
880
+ if (typeof schema.minLength === "number" && value.length < schema.minLength) return `${valuePath} must be at least ${schema.minLength} characters`;
881
+ if (typeof schema.maxLength === "number" && value.length > schema.maxLength) return `${valuePath} must be at most ${schema.maxLength} characters`;
882
+ return;
883
+ }
884
+ if (schemaType === "number") {
885
+ if (typeof value !== "number" || !Number.isFinite(value)) return `${valuePath} must be a number`;
886
+ if (typeof schema.minimum === "number" && value < schema.minimum) return `${valuePath} must be >= ${schema.minimum}`;
887
+ if (typeof schema.maximum === "number" && value > schema.maximum) return `${valuePath} must be <= ${schema.maximum}`;
888
+ return;
889
+ }
890
+ if (schemaType === "boolean") {
891
+ if (typeof value !== "boolean") return `${valuePath} must be a boolean`;
892
+ return;
893
+ }
894
+ }
895
+ function resolveDocsMarkdownCanonicalUrl({ origin, entry = "docs", requestedPath, locale }) {
896
+ const pathname = normalizeRequestedMarkdownPath(normalizeDocsPathSegment(entry) || "docs", requestedPath);
897
+ const canonicalUrl = new URL(pathname, origin);
898
+ if (locale) canonicalUrl.searchParams.set("lang", locale);
899
+ return canonicalUrl.toString();
900
+ }
901
+ function getDocsMarkdownCanonicalLinkHeader(options) {
902
+ return `<${resolveDocsMarkdownCanonicalUrl(options)}>; rel="canonical"`;
903
+ }
904
+ function joinDocsPublicRoute(prefix, suffix) {
905
+ const normalizedPrefix = normalizeDocsUrlPath(`/${normalizeDocsPathSegment(prefix)}`);
906
+ const normalizedSuffix = `/${normalizeDocsPathSegment(suffix)}`;
907
+ if (normalizedPrefix === "/") return normalizedSuffix;
908
+ return normalizeDocsUrlPath(`${normalizedPrefix}${normalizedSuffix}`);
909
+ }
910
+ function normalizeLlmsTxtMatch(value) {
911
+ const trimmed = value.trim();
912
+ if (!trimmed) return "/";
913
+ return normalizeDocsUrlPath(trimmed.startsWith("/") ? trimmed : `/${trimmed}`);
914
+ }
915
+ function llmsTxtMatchPrefix(value) {
916
+ const normalized = normalizeLlmsTxtMatch(value);
917
+ const wildcardIndex = normalized.indexOf("*");
918
+ return normalizeDocsUrlPath((wildcardIndex >= 0 ? normalized.slice(0, wildcardIndex) : normalized).replace(/\/+$/, "") || "/");
919
+ }
920
+ function deriveLlmsTxtSectionRoute(match) {
921
+ return joinDocsPublicRoute(llmsTxtMatchPrefix(match[0] ?? "/"), "llms.txt");
922
+ }
923
+ function deriveLlmsTxtSectionFullRoute(route) {
924
+ const normalized = normalizeDocsUrlPath(route);
925
+ if (normalized.endsWith("/llms.txt")) return `${normalized.slice(0, -9)}/llms-full.txt`;
926
+ return joinDocsPublicRoute(normalized, "llms-full.txt");
927
+ }
928
+ function resolveDocsBaseLlmsTxtRoute(basePath) {
929
+ if (!basePath) return null;
930
+ return joinDocsPublicRoute(normalizeDocsPathSegment(basePath) || "docs", "llms.txt");
931
+ }
932
+ function resolveDocsBaseLlmsFullTxtRoute(basePath) {
933
+ const route = resolveDocsBaseLlmsTxtRoute(basePath);
934
+ return route ? deriveLlmsTxtSectionFullRoute(route) : null;
935
+ }
936
+ function normalizeLlmsTxtMaxChars(value, fallback) {
937
+ const mode = value?.mode ?? fallback?.mode ?? "warn";
938
+ const chars = value?.chars ?? fallback?.chars ?? DEFAULT_LLMS_TXT_MAX_CHARS;
939
+ return {
940
+ mode,
941
+ chars: Number.isFinite(chars) && chars > 0 ? Math.floor(chars) : DEFAULT_LLMS_TXT_MAX_CHARS
942
+ };
943
+ }
944
+ function resolveDocsLlmsTxtSections(llms) {
945
+ if (!llms || typeof llms !== "object" || !Array.isArray(llms.sections)) return [];
946
+ const rootMaxChars = normalizeLlmsTxtMaxChars(llms.maxChars);
947
+ return llms.sections.flatMap((section) => {
948
+ const match = (Array.isArray(section.match) ? section.match : [section.match]).map(normalizeLlmsTxtMatch).filter(Boolean);
949
+ if (match.length === 0) return [];
950
+ const route = deriveLlmsTxtSectionRoute(match);
951
+ const resolved = {
952
+ title: section.title,
953
+ match,
954
+ route,
955
+ fullRoute: deriveLlmsTxtSectionFullRoute(route),
956
+ maxChars: normalizeLlmsTxtMaxChars(section.maxChars, rootMaxChars)
957
+ };
958
+ if (section.description !== void 0) resolved.description = section.description;
959
+ return [resolved];
960
+ });
961
+ }
962
+ function matchesDocsLlmsTxtSection(pageUrl, section) {
963
+ const pathname = normalizeDocsUrlPath(pageUrl.split(/[?#]/, 1)[0] || "/");
964
+ return section.match.some((pattern) => {
965
+ const normalized = normalizeLlmsTxtMatch(pattern);
966
+ if (normalized.endsWith("/**")) {
967
+ const prefix = normalizeDocsUrlPath(normalized.slice(0, -3) || "/");
968
+ if (prefix === "/") return true;
969
+ return pathname === prefix || pathname.startsWith(`${prefix}/`);
970
+ }
971
+ if (normalized.endsWith("/*")) {
972
+ const prefix = normalizeDocsUrlPath(normalized.slice(0, -2) || "/");
973
+ if (prefix === "/") {
974
+ const directChild = pathname.slice(1);
975
+ return directChild.length > 0 && !directChild.includes("/");
976
+ }
977
+ if (!pathname.startsWith(`${prefix}/`)) return false;
978
+ return !pathname.slice(prefix.length + 1).includes("/");
979
+ }
980
+ return pathname === normalized;
981
+ });
982
+ }
983
+ function resolveDocsLlmsTxtRequest(url, llms, basePath, options = {}) {
984
+ const pathname = normalizeDocsUrlPath(url.pathname);
985
+ const apiRoute = resolveDocsDiscoveryApiRoute(options.apiRoute);
986
+ const sections = resolveDocsLlmsTxtSections(llms);
987
+ for (const section of sections) {
988
+ if (pathname === section.route) return {
989
+ format: "llms",
990
+ section
991
+ };
992
+ if (pathname === section.fullRoute) return {
993
+ format: "llms-full",
994
+ section
995
+ };
996
+ }
997
+ const format = url.searchParams.get("format");
998
+ if (pathname === apiRoute && (format === "llms" || format === "llms-full")) {
999
+ const sectionRoute = url.searchParams.get("section")?.trim();
1000
+ const normalizedSectionRoute = sectionRoute ? normalizeDocsUrlPath(sectionRoute) : void 0;
1001
+ return {
1002
+ format,
1003
+ section: normalizedSectionRoute ? sections.find((candidate) => candidate.route === normalizedSectionRoute || candidate.fullRoute === normalizedSectionRoute) ?? {
1004
+ title: "",
1005
+ match: [],
1006
+ route: normalizedSectionRoute,
1007
+ fullRoute: normalizedSectionRoute,
1008
+ maxChars: normalizeLlmsTxtMaxChars(void 0)
1009
+ } : void 0
1010
+ };
1011
+ }
1012
+ if (pathname === DEFAULT_LLMS_TXT_ROUTE || pathname === DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE) return { format: "llms" };
1013
+ if (pathname === resolveDocsBaseLlmsTxtRoute(basePath)) return { format: "llms" };
1014
+ if (pathname === DEFAULT_LLMS_FULL_TXT_ROUTE || pathname === DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE) return { format: "llms-full" };
1015
+ if (pathname === resolveDocsBaseLlmsFullTxtRoute(basePath)) return { format: "llms-full" };
1016
+ return null;
1017
+ }
1018
+ function renderLlmsTxtPageList(pages, baseUrl) {
1019
+ let content = "";
1020
+ for (const page of pages) {
1021
+ content += `- [${page.title}](${baseUrl}${page.markdownUrl ?? toDocsMarkdownUrl(page.url)})`;
1022
+ if (page.description) content += `: ${page.description}`;
1023
+ content += "\n";
1024
+ }
1025
+ return content;
1026
+ }
1027
+ function resolveDocsResourceUrl(baseUrl, url) {
1028
+ if (/^[a-z][a-z0-9+.-]*:/i.test(url)) return url;
1029
+ return `${baseUrl}${url.startsWith("/") ? url : `/${url}`}`;
1030
+ }
1031
+ function formatOpenApiOperationCount(count) {
1032
+ if (count === void 0) return void 0;
1033
+ return `${count.toLocaleString("en-US")} ${count === 1 ? "operation" : "operations"}`;
1034
+ }
1035
+ function renderLlmsFullTxtPages(pages, baseUrl) {
1036
+ let content = "";
1037
+ for (const page of pages) {
1038
+ content += `## ${page.title}\n\n`;
1039
+ content += `URL: ${baseUrl}${page.url}\n\n`;
1040
+ if (page.description) content += `${page.description}\n\n`;
1041
+ const agentContent = page.agentRawContent ?? page.agentFallbackRawContent ?? page.agentContent ?? page.agentFallbackContent ?? page.rawContent ?? page.content;
1042
+ content += `${agentContent}\n\n---\n\n`;
1043
+ }
1044
+ return content;
1045
+ }
1046
+ function renderDocsLlmsTxt(pages, options = {}) {
1047
+ pages = filterDocsPagesByAccess(pages);
1048
+ const siteTitle = options.siteTitle ?? "Documentation";
1049
+ const siteDescription = options.siteDescription;
1050
+ const baseUrl = options.baseUrl ?? "";
1051
+ const maxChars = normalizeLlmsTxtMaxChars(options.maxChars);
1052
+ const apiCatalogEnabled = options.apiCatalog ?? true;
1053
+ const sections = resolveDocsLlmsTxtSections(options);
1054
+ const openapi = resolveDocsOpenApiDiscoveryConfig(options.openapi);
1055
+ const resolvedApiRoute = resolveDocsDiscoveryApiRoute(options.apiRoute);
1056
+ const openapiUrl = resolveDocsOpenApiDiscoveryUrl(openapi, resolvedApiRoute);
1057
+ const matchedPageUrls = /* @__PURE__ */ new Set();
1058
+ const generatedSections = sections.map((section) => {
1059
+ const sectionPages = pages.filter((page) => matchesDocsLlmsTxtSection(page.url, section));
1060
+ for (const page of sectionPages) matchedPageUrls.add(normalizeDocsUrlPath(page.url));
1061
+ let llmsTxt = `# ${siteTitle} - ${section.title}\n\n`;
1062
+ if (section.description) llmsTxt += `> ${section.description}\n\n`;
1063
+ else if (siteDescription) llmsTxt += `> ${siteDescription}\n\n`;
1064
+ llmsTxt += "## Pages\n\n";
1065
+ llmsTxt += renderLlmsTxtPageList(sectionPages, baseUrl);
1066
+ let llmsFullTxt = `# ${siteTitle} - ${section.title}\n\n`;
1067
+ if (section.description) llmsFullTxt += `> ${section.description}\n\n`;
1068
+ else if (siteDescription) llmsFullTxt += `> ${siteDescription}\n\n`;
1069
+ llmsFullTxt += renderLlmsFullTxtPages(sectionPages, baseUrl);
1070
+ return {
1071
+ ...section,
1072
+ llmsTxt,
1073
+ llmsFullTxt
1074
+ };
1075
+ });
1076
+ const rootPages = generatedSections.length > 0 ? pages.filter((page) => !matchedPageUrls.has(normalizeDocsUrlPath(page.url))) : pages;
1077
+ let llmsTxt = `# ${siteTitle}\n\n`;
1078
+ if (siteDescription) llmsTxt += `> ${siteDescription}\n\n`;
1079
+ llmsTxt += "## Agent Discovery\n\n";
1080
+ llmsTxt += `- [Agent manifest](${resolveDocsResourceUrl(baseUrl, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE)}): Farming Labs discovery manifest\n`;
1081
+ if (apiCatalogEnabled) llmsTxt += `- [API catalog](${resolveDocsResourceUrl(baseUrl, DEFAULT_API_CATALOG_ROUTE)}): RFC 9727 API catalog\n`;
1082
+ llmsTxt += `- [Agent Skills index](${resolveDocsResourceUrl(baseUrl, DEFAULT_AGENT_SKILLS_INDEX_ROUTE)}): Hashed Agent Skills discovery\n\n`;
1083
+ if (generatedSections.length > 0) {
1084
+ llmsTxt += "## Sections\n\n";
1085
+ for (const section of generatedSections) {
1086
+ llmsTxt += `- [${section.title}](${baseUrl}${section.route})`;
1087
+ if (section.description) llmsTxt += `: ${section.description}`;
1088
+ llmsTxt += "\n";
1089
+ }
1090
+ llmsTxt += "\n";
1091
+ }
1092
+ if (openapi.enabled && openapiUrl) {
1093
+ llmsTxt += "## API Schemas\n\n";
1094
+ llmsTxt += `- [OpenAPI schema](${resolveDocsResourceUrl(baseUrl, openapiUrl)}): Machine-readable API schema for tool use and API clients`;
1095
+ const operationCount = formatOpenApiOperationCount(openapi.operationCount);
1096
+ if (operationCount) llmsTxt += `; ${operationCount}`;
1097
+ if (openapi.apiReferencePath) llmsTxt += `; rendered API reference at ${resolveDocsResourceUrl(baseUrl, openapi.apiReferencePath)}`;
1098
+ llmsTxt += "\n";
1099
+ for (const version of openapi.versions ?? []) {
1100
+ const versionOperationCount = formatOpenApiOperationCount(version.operationCount);
1101
+ llmsTxt += `- [OpenAPI schema – ${version.label}](${resolveDocsResourceUrl(baseUrl, resolveDocsOpenApiVersionUrl(openapi, version, resolvedApiRoute))}): Version \`${version.id}\`${version.default ? " (default)" : ""}${versionOperationCount ? `; ${versionOperationCount}` : ""}; rendered API reference at ${resolveDocsResourceUrl(baseUrl, version.apiReferencePath)}\n`;
1102
+ }
1103
+ llmsTxt += "\n";
1104
+ }
1105
+ if (rootPages.length > 0 || generatedSections.length === 0) {
1106
+ llmsTxt += "## Pages\n\n";
1107
+ llmsTxt += renderLlmsTxtPageList(rootPages, baseUrl);
1108
+ }
1109
+ let llmsFullTxt = `# ${siteTitle}\n\n`;
1110
+ if (siteDescription) llmsFullTxt += `> ${siteDescription}\n\n`;
1111
+ llmsFullTxt += renderLlmsFullTxtPages(pages, baseUrl);
1112
+ return {
1113
+ llmsTxt,
1114
+ llmsFullTxt,
1115
+ maxChars,
1116
+ sections: generatedSections
1117
+ };
1118
+ }
1119
+ function selectDocsLlmsTxtContent(content, request) {
1120
+ if (request.section) {
1121
+ const section = content.sections.find((candidate) => candidate.route === request.section?.route);
1122
+ if (!section) return null;
1123
+ return {
1124
+ content: request.format === "llms-full" ? section.llmsFullTxt : section.llmsTxt,
1125
+ label: request.format === "llms-full" ? section.fullRoute : section.route,
1126
+ maxChars: request.format === "llms-full" ? {
1127
+ ...section.maxChars,
1128
+ mode: "off"
1129
+ } : section.maxChars
1130
+ };
1131
+ }
1132
+ return {
1133
+ content: request.format === "llms-full" ? content.llmsFullTxt : content.llmsTxt,
1134
+ label: request.format === "llms-full" ? DEFAULT_LLMS_FULL_TXT_ROUTE : DEFAULT_LLMS_TXT_ROUTE,
1135
+ maxChars: request.format === "llms-full" ? {
1136
+ ...content.maxChars,
1137
+ mode: "off"
1138
+ } : content.maxChars
1139
+ };
1140
+ }
1141
+ function getDocsLlmsTxtMaxCharsIssue(label, content, maxChars) {
1142
+ if (maxChars.mode === "off" || content.length <= maxChars.chars) return null;
1143
+ return {
1144
+ mode: maxChars.mode,
1145
+ chars: maxChars.chars,
1146
+ actual: content.length,
1147
+ label,
1148
+ message: `${label} is ${content.length.toLocaleString()} chars, above the configured ${maxChars.chars.toLocaleString()} char llms.txt budget.`
1149
+ };
1150
+ }
1151
+ function resolveDocsStandardsOrigin(preferred, fallback) {
1152
+ for (const candidate of [preferred, fallback]) {
1153
+ if (!candidate) continue;
1154
+ try {
1155
+ const url = new URL(candidate);
1156
+ if (url.protocol === "http:" || url.protocol === "https:") return url.origin;
1157
+ } catch {}
1158
+ }
1159
+ return new URL(fallback).origin;
1160
+ }
1161
+ /** Build and serve standards-based discovery without replacing the custom agent manifest. */
1162
+ async function createDocsStandardsDiscoveryResponse({ request, preferredSkillDocument, fallbackSkillDocument, origin, entry = "docs", docsPath, apiCatalog: explicitApiCatalog, apiRoute, i18n: _i18n, search: _search, mcp, feedback, llms, sitemap, robots, openapi, markdown: _markdown, publishedSkills, agentCard, lastModified }) {
1163
+ const url = new URL(request.url);
1164
+ const resolvedApiRoute = resolveDocsDiscoveryApiRoute(apiRoute);
1165
+ if (!isDocsStandardsDiscoveryRequest(url, { apiRoute: resolvedApiRoute })) return null;
1166
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
1167
+ const normalizedDocsPath = normalizeDocsPathSegment(docsPath ?? normalizedEntry);
1168
+ const apiCatalogEnabled = explicitApiCatalog ?? llms?.apiCatalog ?? true;
1169
+ const catalogOrigin = resolveDocsStandardsOrigin(llms?.baseUrl, origin || url.origin);
1170
+ const sitemapConfig = resolveDocsSitemapConfig(sitemap, { baseUrl: llms?.baseUrl });
1171
+ const openapiConfig = resolveDocsOpenApiDiscoveryConfig(openapi);
1172
+ const openapiUrl = resolveDocsOpenApiDiscoveryUrl(openapiConfig, resolvedApiRoute);
1173
+ const feedbackRoute = feedback?.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
1174
+ const feedbackSchemaRoute = feedback?.schemaRoute ?? `${feedbackRoute}/schema`;
1175
+ const llmsEnabled = llms?.enabled ?? true;
1176
+ const robotsEnabled = isRobotsDiscoveryEnabled(robots);
1177
+ const llmsSections = resolveDocsLlmsTxtSections(llms);
1178
+ const llmsRoutes = llmsEnabled ? [
1179
+ DEFAULT_LLMS_TXT_ROUTE,
1180
+ DEFAULT_LLMS_FULL_TXT_ROUTE,
1181
+ DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE,
1182
+ DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE,
1183
+ ...llmsSections.flatMap((section) => [section.route, section.fullRoute])
1184
+ ] : [];
1185
+ const sitemapRoutes = sitemapConfig.enabled ? [...sitemapConfig.xml.enabled ? [sitemapConfig.xml.route] : [], ...sitemapConfig.markdown.enabled ? [
1186
+ sitemapConfig.markdown.route,
1187
+ sitemapConfig.markdown.docsRoute,
1188
+ sitemapConfig.markdown.wellKnownRoute
1189
+ ].filter((route) => Boolean(route)) : []] : [];
1190
+ const protectedResourceMetadataRoutes = mcp.enabled && mcp.security?.authenticate && mcp.security.protectedResource ? getDocsMcpProtectedResourceMetadataRoutes(mcp.route) : [];
1191
+ return createDocsStandardsResponse({
1192
+ request,
1193
+ apiCatalogEnabled,
1194
+ apiRoute: resolvedApiRoute,
1195
+ preferredSkillDocument,
1196
+ fallbackSkillDocument,
1197
+ publishedSkills,
1198
+ agentCard,
1199
+ lastModified,
1200
+ apiCatalog: apiCatalogEnabled ? buildDocsApiCatalog({
1201
+ origin: catalogOrigin,
1202
+ docsRoute: normalizedDocsPath ? `/${normalizedDocsPath}` : "/",
1203
+ apiRoute: resolvedApiRoute,
1204
+ configRoute: `${resolvedApiRoute}?format=config`,
1205
+ diagnosticsRoute: `${resolvedApiRoute}?format=diagnostics`,
1206
+ agentManifestRoute: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
1207
+ agentSkillsIndexRoute: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
1208
+ legacySkillsIndexRoute: DEFAULT_LEGACY_SKILLS_INDEX_ROUTE,
1209
+ agentCardRoute: agentCard ? DEFAULT_A2A_AGENT_CARD_ROUTE : null,
1210
+ agentsRoute: DEFAULT_AGENTS_MD_ROUTE,
1211
+ skillRoute: DEFAULT_SKILL_MD_ROUTE,
1212
+ markdownRootRoute: `/${normalizedEntry}.md`,
1213
+ llmsRoutes,
1214
+ sitemapRoutes,
1215
+ robotsRoute: robotsEnabled ? DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE : null,
1216
+ mcpRoute: mcp.enabled ? mcp.route : null,
1217
+ protectedResourceMetadataRoutes,
1218
+ feedbackRoutes: feedback?.enabled ? [feedbackRoute, feedbackSchemaRoute] : [],
1219
+ openapiDefinitions: openapiConfig.enabled && openapiConfig.catalogTargets?.length ? (openapiConfig.versions?.length ? openapiConfig.versions : [{
1220
+ id: "default",
1221
+ label: "Product API",
1222
+ default: true,
1223
+ url: openapiUrl ?? `${resolvedApiRoute}?format=openapi`,
1224
+ apiReferencePath: openapiConfig.apiReferencePath ?? "/api-reference",
1225
+ operationCount: openapiConfig.operationCount
1226
+ }]).map((version) => ({
1227
+ route: resolveDocsOpenApiVersionUrl(openapiConfig, version, resolvedApiRoute),
1228
+ title: `${openapiConfig.versions?.length ? `OpenAPI schema – ${version.label}` : "OpenAPI schema"}${formatOpenApiOperationCount(version.operationCount) ? ` — ${formatOpenApiOperationCount(version.operationCount)}` : ""}`,
1229
+ targets: openapiConfig.catalogTargets.map((route) => ({
1230
+ route,
1231
+ title: "Product API"
1232
+ }))
1233
+ })) : [],
1234
+ apiReferenceRoute: openapiConfig.enabled && openapiConfig.apiReferencePath ? openapiConfig.apiReferencePath : null
1235
+ }) : void 0
1236
+ });
1237
+ }
1238
+ function isDocsAgentDiscoveryRequest(url, options = {}) {
1239
+ const pathname = normalizeDocsUrlPath(url.pathname);
1240
+ if (pathname === resolveDocsDiscoveryApiRoute(options.apiRoute) && url.searchParams.get("agent")?.trim() === "spec") return true;
1241
+ return pathname === DEFAULT_AGENT_SPEC_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE;
1242
+ }
1243
+ function buildDocsMcpEndpointCandidates(baseUrl, routes = [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE], options = {}) {
1244
+ const includeOriginFallback = options.includeOriginFallback !== false;
1245
+ const includeRootDomainFallback = options.includeRootDomainFallback !== false;
1246
+ const includeMcpSubdomainFallback = options.includeMcpSubdomainFallback !== false;
1247
+ const base = new URL(baseUrl);
1248
+ const primaryOrigin = base.origin;
1249
+ const seen = /* @__PURE__ */ new Set();
1250
+ const candidates = [];
1251
+ const addCandidate = (candidateBaseUrl, route) => {
1252
+ const resolved = resolveDocsMcpCandidateUrl(candidateBaseUrl, route);
1253
+ if (seen.has(resolved.url)) return;
1254
+ seen.add(resolved.url);
1255
+ candidates.push({
1256
+ ...resolved,
1257
+ label: formatDocsMcpCandidateLabel(resolved.url, primaryOrigin)
1258
+ });
1259
+ };
1260
+ for (const route of routes) addCandidate(baseUrl, route);
1261
+ const originBaseUrl = primaryOrigin;
1262
+ if (includeOriginFallback && originBaseUrl !== baseUrl.replace(/\/+$/, "")) for (const route of routes) addCandidate(originBaseUrl, route);
1263
+ if (includeRootDomainFallback) for (const rootDomainBaseUrl of toDocsRootDomainBaseUrls(base)) for (const route of routes) addCandidate(rootDomainBaseUrl, route);
1264
+ if (includeMcpSubdomainFallback) for (const mcpBaseUrl of toDocsMcpSubdomainBaseUrls(base)) {
1265
+ addCandidate(mcpBaseUrl, DEFAULT_MCP_PUBLIC_ROUTE);
1266
+ addCandidate(mcpBaseUrl, "/");
1267
+ }
1268
+ return candidates;
1269
+ }
1270
+ function resolveDocsMcpCandidateUrl(baseUrl, route) {
1271
+ if (/^https?:\/\//i.test(route)) {
1272
+ const parsed = new URL(route);
1273
+ const path = `${parsed.pathname || "/"}${parsed.search}`;
1274
+ return {
1275
+ baseUrl: parsed.origin,
1276
+ route: path,
1277
+ url: parsed.toString()
1278
+ };
1279
+ }
1280
+ const base = new URL(baseUrl);
1281
+ const basePath = base.pathname.replace(/\/+$/, "");
1282
+ const routePath = route.startsWith("/") ? route : `/${route}`;
1283
+ const parsed = new URL(`${basePath}${routePath}`, base.origin);
1284
+ return {
1285
+ baseUrl: parsed.origin,
1286
+ route: `${parsed.pathname}${parsed.search}`,
1287
+ url: parsed.toString()
1288
+ };
1289
+ }
1290
+ function formatDocsMcpCandidateLabel(url, primaryOrigin) {
1291
+ const parsed = new URL(url);
1292
+ const path = `${parsed.pathname}${parsed.search}`;
1293
+ return parsed.origin === primaryOrigin ? path : `${parsed.origin}${path}`;
1294
+ }
1295
+ function toDocsMcpSubdomainBaseUrls(base) {
1296
+ return getDocsMcpRootDomainCandidates(base.hostname).map((rootDomain) => `${base.protocol}//mcp.${rootDomain}${base.port ? `:${base.port}` : ""}`);
1297
+ }
1298
+ function toDocsRootDomainBaseUrls(base) {
1299
+ return getDocsMcpRootDomainCandidates(base.hostname).map((rootDomain) => `${base.protocol}//${rootDomain}${base.port ? `:${base.port}` : ""}`);
1300
+ }
1301
+ function getDocsMcpRootDomainCandidates(hostname) {
1302
+ const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
1303
+ if (!normalized || !normalized.includes(".") || isDocsIpHostname(normalized)) return [];
1304
+ const labels = normalized.split(".").filter(Boolean);
1305
+ if (labels.length < 2) return [];
1306
+ const candidates = [];
1307
+ const addCandidate = (candidateLabels) => {
1308
+ if (candidateLabels.length < 2) return;
1309
+ const candidate = candidateLabels.join(".");
1310
+ if (!candidates.includes(candidate)) candidates.push(candidate);
1311
+ };
1312
+ if (labels.length >= 3 && DOCS_MCP_SERVICE_SUBDOMAIN_LABELS.has(labels[0] ?? "")) addCandidate(labels.slice(1));
1313
+ const tld = labels.at(-1) ?? "";
1314
+ const secondLevel = labels.at(-2) ?? "";
1315
+ if (labels.length >= 3 && tld.length === 2 && COMMON_SECOND_LEVEL_PUBLIC_SUFFIX_LABELS.has(secondLevel)) addCandidate(labels.slice(-3));
1316
+ else addCandidate(labels.slice(-2));
1317
+ return candidates;
1318
+ }
1319
+ function isDocsIpHostname(hostname) {
1320
+ return /^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || hostname.includes(":");
1321
+ }
1322
+ function isDocsSkillRequest(url, options = {}) {
1323
+ const pathname = normalizeDocsUrlPath(url.pathname);
1324
+ if (pathname === DEFAULT_SKILL_MD_ROUTE || pathname === DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE) return true;
1325
+ return pathname === resolveDocsDiscoveryApiRoute(options.apiRoute) && resolveDocsSkillFormat(url) === "skill";
1326
+ }
1327
+ function resolveDocsSkillFormat(url) {
1328
+ return url.searchParams.get("format")?.trim() === "skill" ? "skill" : null;
1329
+ }
1330
+ function isDocsAgentsPath(pathname) {
1331
+ return pathname === DEFAULT_AGENTS_MD_ROUTE || pathname === DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE || pathname === DEFAULT_AGENT_MD_ROUTE || pathname === DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE;
1332
+ }
1333
+ function isDocsAgentsRequest(url, options = {}) {
1334
+ const pathname = normalizeDocsUrlPath(url.pathname);
1335
+ if (isDocsAgentsPath(pathname)) return true;
1336
+ return pathname === resolveDocsDiscoveryApiRoute(options.apiRoute) && resolveDocsAgentsFormat(url) === "agents";
1337
+ }
1338
+ function resolveDocsAgentsFormat(url) {
1339
+ return url.searchParams.get("format")?.trim() === "agents" ? "agents" : null;
1340
+ }
1341
+ function isDocsPublicGetRequest(entry, url, request, options = {}) {
1342
+ const pathname = normalizeDocsUrlPath(url.pathname);
1343
+ const apiRoute = resolveDocsDiscoveryApiRoute(options.apiRoute);
1344
+ if (pathname === apiRoute || pathname === DEFAULT_MCP_ROUTE) return false;
1345
+ return isDocsStandardsDiscoveryRequest(url, { apiRoute }) || isDocsAgentDiscoveryRequest(url, { apiRoute }) || isDocsAgentsRequest(url, { apiRoute }) || isDocsSkillRequest(url, { apiRoute }) || pathname === DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE && isRobotsDiscoveryEnabled(options.robots) || resolveDocsLlmsTxtRequest(url, options.llms, entry, { apiRoute }) !== null || resolveDocsSitemapRequest(url, options.sitemap, { apiRoute }) !== null || resolveDocsMarkdownRequest(entry, url, request, { apiRoute }) !== null;
1346
+ }
1347
+ function isDocsLlmsTxtPublicRequest(url, llms, basePath, options = {}) {
1348
+ const pathname = normalizeDocsUrlPath(url.pathname);
1349
+ const apiRoute = resolveDocsDiscoveryApiRoute(options.apiRoute);
1350
+ return pathname !== apiRoute && resolveDocsLlmsTxtRequest(url, llms, basePath, { apiRoute }) !== null;
1351
+ }
1352
+ function resolveDocsLlmsTxtFormat(url, basePath, options = {}) {
1353
+ return resolveDocsLlmsTxtRequest(url, void 0, basePath, options)?.format ?? null;
1354
+ }
1355
+ function resolveDocsMarkdownRequest(entry, url, request, options = {}) {
1356
+ const pathname = normalizeDocsUrlPath(url.pathname);
1357
+ const format = url.searchParams.get("format")?.trim();
1358
+ if (pathname === resolveDocsDiscoveryApiRoute(options.apiRoute) && format === "markdown") return { requestedPath: url.searchParams.get("path")?.trim() ?? "" };
1359
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
1360
+ if (pathname === `${normalizedEntry}.md`) return { requestedPath: "" };
1361
+ const slugPrefix = `${normalizedEntry}/`;
1362
+ if (pathname.startsWith(slugPrefix) && pathname.endsWith(".md")) return { requestedPath: pathname.slice(slugPrefix.length, -3) };
1363
+ if (acceptsDocsMarkdown(request) || hasDocsMarkdownSignatureAgent(request) || detectDocsMarkdownAgentRequest(request).detected) {
1364
+ if (pathname === normalizedEntry) return { requestedPath: "" };
1365
+ if (pathname.startsWith(slugPrefix)) return { requestedPath: pathname.slice(slugPrefix.length) };
1366
+ }
1367
+ return null;
1368
+ }
1369
+ function hasDocsMarkdownSignatureAgent(request) {
1370
+ return Boolean(request.headers.get(DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER)?.trim());
1371
+ }
1372
+ function detectDocsMarkdownAgentRequest(request) {
1373
+ if (hasDocsMarkdownSignatureAgent(request)) return {
1374
+ detected: true,
1375
+ method: "signature_agent"
1376
+ };
1377
+ const userAgent = request.headers.get("user-agent")?.trim().toLowerCase() ?? "";
1378
+ if (!userAgent) return {
1379
+ detected: false,
1380
+ method: null
1381
+ };
1382
+ if (DOCS_AI_AGENT_USER_AGENT_PATTERNS.some((pattern) => userAgent.includes(pattern))) return {
1383
+ detected: true,
1384
+ method: "user_agent"
1385
+ };
1386
+ if (!request.headers.get("sec-fetch-mode") && DOCS_BOT_LIKE_USER_AGENT_PATTERN.test(userAgent)) {
1387
+ if (!DOCS_TRADITIONAL_BOT_USER_AGENT_PATTERNS.some((pattern) => userAgent.includes(pattern))) return {
1388
+ detected: true,
1389
+ method: "heuristic"
1390
+ };
1391
+ }
1392
+ return {
1393
+ detected: false,
1394
+ method: null
1395
+ };
1396
+ }
1397
+ function getDocsMarkdownVaryHeader(request) {
1398
+ const values = /* @__PURE__ */ new Set();
1399
+ if (acceptsDocsMarkdown(request)) values.add("Accept");
1400
+ if (hasDocsMarkdownSignatureAgent(request)) {
1401
+ values.add("Accept");
1402
+ values.add(DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER);
1403
+ }
1404
+ const agentDetection = detectDocsMarkdownAgentRequest(request);
1405
+ if (agentDetection.detected && agentDetection.method !== "signature_agent") {
1406
+ values.add("User-Agent");
1407
+ if (agentDetection.method === "heuristic") values.add("Sec-Fetch-Mode");
1408
+ }
1409
+ return values.size > 0 ? Array.from(values).join(", ") : null;
1410
+ }
1411
+ const DOCS_MARKDOWN_RECOVERY_MATCH_LIMIT = 5;
1412
+ const DOCS_MARKDOWN_RECOVERY_REDIRECT_CONFIDENCE = .99;
1413
+ const DOCS_MARKDOWN_RECOVERY_SLUG_MAX_LENGTH = 256;
1414
+ function normalizeDocsRecoveryText(value) {
1415
+ return value.toLowerCase().replace(/\.md$/i, "").replace(/[#?].*$/g, "").replace(/['"`]/g, "").replace(/[^a-z0-9]+/g, " ").trim();
1416
+ }
1417
+ function normalizeDocsRecoverySlug(entry, value) {
1418
+ const withoutHash = value.split("#", 1)[0] ?? value;
1419
+ const withoutMarkdown = (withoutHash.split("?", 1)[0] ?? withoutHash).replace(/\.md$/i, "");
1420
+ const normalizedPath = normalizeDocsUrlPath(withoutMarkdown.startsWith("/") ? withoutMarkdown : `/${withoutMarkdown}`);
1421
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
1422
+ if (normalizedPath === normalizedEntry) return "";
1423
+ if (normalizedPath.startsWith(`${normalizedEntry}/`)) return normalizeDocsPathSegment(normalizedPath.slice(normalizedEntry.length + 1));
1424
+ return normalizeDocsPathSegment(normalizedPath);
1425
+ }
1426
+ function limitDocsRecoverySlug(value) {
1427
+ return value.slice(0, DOCS_MARKDOWN_RECOVERY_SLUG_MAX_LENGTH);
1428
+ }
1429
+ function tokenizeDocsRecoveryText(value) {
1430
+ return normalizeDocsRecoveryText(value).split(/\s+/).filter((token) => token.length > 1);
1431
+ }
1432
+ function levenshteinDistance(a, b) {
1433
+ if (a === b) return 0;
1434
+ if (!a) return b.length;
1435
+ if (!b) return a.length;
1436
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
1437
+ let current = new Array(b.length + 1);
1438
+ for (let i = 1; i <= a.length; i += 1) {
1439
+ current[0] = i;
1440
+ for (let j = 1; j <= b.length; j += 1) {
1441
+ const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
1442
+ current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, substitution);
1443
+ }
1444
+ [previous, current] = [current, previous];
1445
+ }
1446
+ return previous[b.length] ?? Math.max(a.length, b.length);
1447
+ }
1448
+ function normalizedEditSimilarity(a, b) {
1449
+ if (!a || !b) return 0;
1450
+ if (a === b) return 1;
1451
+ const maxLength = Math.max(a.length, b.length);
1452
+ if (maxLength === 0) return 1;
1453
+ return Math.max(0, 1 - levenshteinDistance(a, b) / maxLength);
1454
+ }
1455
+ function tokenOverlapScore(requestedTokens, candidateTokens) {
1456
+ if (requestedTokens.length === 0 || candidateTokens.length === 0) return 0;
1457
+ const candidates = new Set(candidateTokens);
1458
+ return requestedTokens.filter((token) => candidates.has(token)).length / requestedTokens.length;
1459
+ }
1460
+ function sameDocsRecoveryParent(a, b) {
1461
+ const aParts = a.split("/").filter(Boolean);
1462
+ const bParts = b.split("/").filter(Boolean);
1463
+ if (aParts.length !== bParts.length || aParts.length === 0) return false;
1464
+ return aParts.slice(0, -1).join("/") === bParts.slice(0, -1).join("/");
1465
+ }
1466
+ function scoreDocsMarkdownRecoveryMatch({ entry, requestedPath, page }) {
1467
+ const requestedSlug = limitDocsRecoverySlug(normalizeDocsRecoverySlug(entry, requestedPath));
1468
+ const pageSlug = limitDocsRecoverySlug(normalizeDocsRecoverySlug(entry, page.url));
1469
+ const requestedLast = requestedSlug.split("/").filter(Boolean).at(-1) ?? requestedSlug;
1470
+ const pageLast = pageSlug.split("/").filter(Boolean).at(-1) ?? pageSlug;
1471
+ const title = normalizeDocsRecoveryText(page.title);
1472
+ const requestedText = normalizeDocsRecoveryText(requestedSlug.replace(/\//g, " "));
1473
+ const requestedTokens = tokenizeDocsRecoveryText(requestedText);
1474
+ const candidateTokens = tokenizeDocsRecoveryText([
1475
+ pageSlug.replace(/\//g, " "),
1476
+ page.title,
1477
+ page.description ?? ""
1478
+ ].join(" "));
1479
+ const pathDistance = levenshteinDistance(requestedSlug, pageSlug);
1480
+ const segmentDistance = levenshteinDistance(requestedLast, pageLast);
1481
+ if (requestedSlug && pageSlug && (sameDocsRecoveryParent(requestedSlug, pageSlug) && segmentDistance <= 1 || pathDistance <= 1)) return .995;
1482
+ const pathSimilarity = normalizedEditSimilarity(requestedSlug, pageSlug);
1483
+ const segmentSimilarity = normalizedEditSimilarity(requestedLast, pageLast);
1484
+ const titleSimilarity = normalizedEditSimilarity(requestedText, title);
1485
+ const overlap = tokenOverlapScore(requestedTokens, candidateTokens);
1486
+ const substringBoost = requestedText && (pageSlug.includes(requestedSlug) || title.includes(requestedText) || requestedText.includes(title)) ? .12 : 0;
1487
+ return Math.min(.98, Math.max(pathSimilarity * .86, segmentSimilarity * .8, titleSimilarity * .72, overlap * .75) + substringBoost);
1488
+ }
1489
+ function resolveDocsMarkdownRecovery({ entry = "docs", requestedPath, pages = [] }) {
1490
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
1491
+ const matches = pages.map((page) => ({
1492
+ page,
1493
+ confidence: scoreDocsMarkdownRecoveryMatch({
1494
+ entry: normalizedEntry,
1495
+ requestedPath,
1496
+ page
1497
+ })
1498
+ })).filter((item) => item.confidence >= .35).sort((a, b) => {
1499
+ if (b.confidence !== a.confidence) return b.confidence - a.confidence;
1500
+ return a.page.url.localeCompare(b.page.url);
1501
+ }).slice(0, DOCS_MARKDOWN_RECOVERY_MATCH_LIMIT).map(({ page, confidence }) => ({
1502
+ title: page.title,
1503
+ url: normalizeDocsUrlPath(page.url),
1504
+ markdownUrl: toDocsMarkdownUrl(page.url),
1505
+ description: page.description,
1506
+ confidence
1507
+ }));
1508
+ return {
1509
+ matches,
1510
+ redirect: matches[0] && matches[0].confidence >= DOCS_MARKDOWN_RECOVERY_REDIRECT_CONFIDENCE ? matches[0] : void 0
1511
+ };
1512
+ }
1513
+ function renderDocsMarkdownSitemapFooter(sitemap) {
1514
+ const sitemapConfig = resolveDocsSitemapConfig(sitemap);
1515
+ const lines = ["## Sitemap", ""];
1516
+ if (sitemapConfig.enabled && sitemapConfig.markdown.enabled) {
1517
+ lines.push(`See the full [sitemap](${sitemapConfig.markdown.route}) for all pages.`);
1518
+ if (sitemapConfig.markdown.docsRoute) lines.push(`Docs-scoped sitemap: [${sitemapConfig.markdown.docsRoute}](${sitemapConfig.markdown.docsRoute}).`);
1519
+ lines.push(`Well-known sitemap: [${sitemapConfig.markdown.wellKnownRoute}](${sitemapConfig.markdown.wellKnownRoute}).`);
1520
+ } else if (sitemapConfig.enabled && sitemapConfig.xml.enabled) lines.push(`See the XML [sitemap](${sitemapConfig.xml.route}) for all pages.`);
1521
+ else lines.push("Sitemap discovery is not enabled for this deployment.");
1522
+ return lines.join("\n");
1523
+ }
1524
+ function appendDocsMarkdownSitemapFooter(markdown, sitemap) {
1525
+ if (/^##\s+Sitemap\s*$/im.test(markdown)) return markdown;
1526
+ return `${markdown.replace(/\s+$/g, "")}\n\n${renderDocsMarkdownSitemapFooter(sitemap)}\n`;
1527
+ }
1528
+ function normalizeDocsMarkdownLastUpdated(value) {
1529
+ if (!value) return void 0;
1530
+ const trimmed = value.trim();
1531
+ const dateOnly = trimmed.match(/^\d{4}-\d{2}-\d{2}/)?.[0];
1532
+ if (dateOnly) return dateOnly;
1533
+ const date = new Date(trimmed);
1534
+ if (Number.isNaN(date.getTime())) return void 0;
1535
+ return date.toISOString().slice(0, 10);
1536
+ }
1537
+ function resolveDocsMarkdownMetadataUrl(value, origin) {
1538
+ if (!origin) return value;
1539
+ try {
1540
+ return new URL(value, origin).toString();
1541
+ } catch {
1542
+ return value;
1543
+ }
1544
+ }
1545
+ function renderDocsMarkdownFrontmatter({ title, description, canonicalUrl, markdownUrl, lastUpdated, trust, agent, generatedPreamble }) {
1546
+ return [
1547
+ "---",
1548
+ `title: ${toYamlString(title)}`,
1549
+ ...description ? [`description: ${toYamlString(description)}`] : [],
1550
+ `canonical_url: ${toYamlString(canonicalUrl)}`,
1551
+ `markdown_url: ${toYamlString(markdownUrl)}`,
1552
+ ...lastUpdated ? [`last_updated: ${toYamlString(lastUpdated)}`] : [],
1553
+ ...trust ? [`okf: ${JSON.stringify(trust)}`] : [],
1554
+ ...generatedPreamble ? [`${DOCS_MARKDOWN_GENERATED_PREAMBLE_FIELD}: true`] : [],
1555
+ ...renderPageAgentFrontmatterYamlLines(agent),
1556
+ "---"
1557
+ ].join("\n");
1558
+ }
1559
+ function hasDocsMarkdownFrontmatter(markdown) {
1560
+ return /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.test(markdown);
1561
+ }
1562
+ function prependDocsMarkdownFrontmatter(markdown, metadata) {
1563
+ if (hasDocsMarkdownFrontmatter(markdown)) return markdown;
1564
+ return `${renderDocsMarkdownFrontmatter(metadata)}\n\n${markdown.replace(/^\r?\n+/, "")}`;
1565
+ }
1566
+ function stripDocsMarkdownFrontmatter(markdown) {
1567
+ const match = markdown.match(/^(---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$))([\s\S]*)$/);
1568
+ if (!match) return {
1569
+ frontmatter: "",
1570
+ body: markdown
1571
+ };
1572
+ return {
1573
+ frontmatter: match[1] ?? "",
1574
+ body: match[2] ?? ""
1575
+ };
1576
+ }
1577
+ function resolveDocsMarkdownSectionBody(document) {
1578
+ const stripped = stripDocsMarkdownFrontmatter(document);
1579
+ const hasGeneratedPreamble = stripped.frontmatter.split(/\r?\n/u).some((line) => line.trim() === `${DOCS_MARKDOWN_GENERATED_PREAMBLE_FIELD}: true`);
1580
+ const lines = stripped.body.split(/\r?\n/u);
1581
+ let generatedPreambleStart = 0;
1582
+ while (generatedPreambleStart < lines.length && (lines[generatedPreambleStart] ?? "").trim() === "") generatedPreambleStart += 1;
1583
+ if (hasGeneratedPreamble && /^#(?:\s+|$)/u.test(lines[generatedPreambleStart] ?? "") && /^URL:\s+\S/u.test(lines[generatedPreambleStart + 1] ?? "")) {
1584
+ lines[generatedPreambleStart] = "";
1585
+ let metadataEnd = generatedPreambleStart + 2;
1586
+ while (/^(?:LLM index|Description|Related):\s*/u.test(lines[metadataEnd] ?? "")) metadataEnd += 1;
1587
+ for (let index = generatedPreambleStart + 1; index < metadataEnd; index += 1) lines[index] = "";
1588
+ }
1589
+ return {
1590
+ body: lines.join("\n").replace(/\n{2,}## Sitemap\n\n(?:(?:See the (?:full|XML) \[sitemap\]\()|Sitemap discovery is not enabled)[\s\S]*$/u, ""),
1591
+ lineOffset: 0
1592
+ };
1593
+ }
1594
+ function estimateDocsMarkdownTokens(value) {
1595
+ if (!value) return 0;
1596
+ return Math.max(1, Math.ceil(docsMarkdownUtf8Bytes(value) / 4));
1597
+ }
1598
+ function docsMarkdownUtf8Bytes(value) {
1599
+ return new TextEncoder().encode(value).byteLength;
1600
+ }
1601
+ function trimDocsMarkdownToByteBudget(value, byteBudget) {
1602
+ if (docsMarkdownUtf8Bytes(value) <= byteBudget) return value;
1603
+ let output = "";
1604
+ let bytes = 0;
1605
+ const encoder = new TextEncoder();
1606
+ for (const character of value) {
1607
+ const characterBytes = encoder.encode(character).byteLength;
1608
+ if (bytes + characterBytes > byteBudget) break;
1609
+ output += character;
1610
+ bytes += characterBytes;
1611
+ }
1612
+ return output.trimEnd();
1613
+ }
1614
+ function truncateDocsMarkdownSectionContent(content, request) {
1615
+ const budgets = [typeof request.byteBudget === "number" ? request.byteBudget : void 0, typeof request.tokenBudget === "number" ? request.tokenBudget * 4 : void 0].filter((budget) => budget !== void 0 && Number.isSafeInteger(budget) && budget > 0);
1616
+ const byteBudget = budgets.length > 0 ? Math.min(...budgets) : void 0;
1617
+ if (!byteBudget || docsMarkdownUtf8Bytes(content) <= byteBudget) return {
1618
+ document: content,
1619
+ truncated: false
1620
+ };
1621
+ const fullMarker = "\n\n<!-- section truncated: increase tokenBudget or byteBudget for more. -->";
1622
+ const marker = docsMarkdownUtf8Bytes(fullMarker) <= byteBudget ? fullMarker : "\n\n<!-- section truncated -->";
1623
+ if (docsMarkdownUtf8Bytes(marker) > byteBudget) return {
1624
+ document: trimDocsMarkdownToByteBudget(marker.trimStart(), byteBudget),
1625
+ truncated: true
1626
+ };
1627
+ let truncated = trimDocsMarkdownToByteBudget(content, Math.max(0, byteBudget - docsMarkdownUtf8Bytes(marker)));
1628
+ const paragraphBoundary = truncated.lastIndexOf("\n\n");
1629
+ if (paragraphBoundary > 0 && paragraphBoundary > truncated.length * .45) truncated = truncated.slice(0, paragraphBoundary).trimEnd();
1630
+ return {
1631
+ document: `${truncated}${marker}`,
1632
+ truncated: true
1633
+ };
1634
+ }
1635
+ function parsePositiveDocsMarkdownBudget(value) {
1636
+ if (!value) return null;
1637
+ const parsed = Number.parseInt(value.replace(/_/g, ""), 10);
1638
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) return null;
1639
+ return Math.min(parsed, DOCS_MARKDOWN_SECTION_MAX_BUDGET);
1640
+ }
1641
+ function resolveDocsMarkdownSectionRequest(url) {
1642
+ const section = url.searchParams.get("section")?.trim() || null;
1643
+ const tokenBudget = parsePositiveDocsMarkdownBudget(url.searchParams.get("tokenBudget")) ?? parsePositiveDocsMarkdownBudget(url.searchParams.get("tokens"));
1644
+ const byteBudget = parsePositiveDocsMarkdownBudget(url.searchParams.get("byteBudget")) ?? parsePositiveDocsMarkdownBudget(url.searchParams.get("bytes"));
1645
+ if (!section && tokenBudget === null && byteBudget === null) return null;
1646
+ return {
1647
+ section,
1648
+ tokenBudget,
1649
+ byteBudget
1650
+ };
1651
+ }
1652
+ function isDocsMarkdownSectionIndexRequest(url) {
1653
+ if (!url.searchParams.has("sections")) return false;
1654
+ const value = url.searchParams.get("sections")?.trim().toLowerCase();
1655
+ return value === void 0 || ![
1656
+ "0",
1657
+ "false",
1658
+ "no",
1659
+ "off"
1660
+ ].includes(value);
1661
+ }
1662
+ function collectDocsMarkdownSections(document) {
1663
+ const { body, lineOffset } = resolveDocsMarkdownSectionBody(document);
1664
+ return parseDocsMarkdownSections(body).map((section) => ({
1665
+ id: section.anchor,
1666
+ heading: section.title,
1667
+ level: section.level,
1668
+ content: section.content,
1669
+ startLine: section.startLine + lineOffset,
1670
+ endLine: section.endLine + lineOffset
1671
+ }));
1672
+ }
1673
+ function updateDocsMarkdownUrl(value, update) {
1674
+ const isAbsolute = /^[a-z][a-z\d+.-]*:/iu.test(value);
1675
+ try {
1676
+ const url = new URL(value, "https://docs.invalid");
1677
+ update(url);
1678
+ return (isAbsolute ? url.toString() : `${url.pathname}${url.search}${url.hash}`).replace(/([?&])sections=(?=&|$)/u, "$1sections");
1679
+ } catch {
1680
+ return value;
1681
+ }
1682
+ }
1683
+ function resolveDocsMarkdownSectionCanonicalUrl(canonicalUrl, sectionId) {
1684
+ return updateDocsMarkdownUrl(canonicalUrl, (url) => {
1685
+ url.hash = encodeURIComponent(sectionId);
1686
+ });
1687
+ }
1688
+ function resolveDocsMarkdownSectionFetchUrl(markdownUrl, sectionId, request) {
1689
+ return updateDocsMarkdownUrl(markdownUrl, (url) => {
1690
+ url.searchParams.delete("sections");
1691
+ url.searchParams.delete("section");
1692
+ url.searchParams.delete("tokens");
1693
+ url.searchParams.delete("bytes");
1694
+ url.searchParams.delete("tokenBudget");
1695
+ url.searchParams.delete("byteBudget");
1696
+ url.searchParams.set("section", sectionId);
1697
+ if (request.tokenBudget) url.searchParams.set("tokenBudget", String(request.tokenBudget));
1698
+ if (request.byteBudget) url.searchParams.set("byteBudget", String(request.byteBudget));
1699
+ });
1700
+ }
1701
+ function resolveDocsMarkdownSectionIndexUrl(markdownUrl, request) {
1702
+ return updateDocsMarkdownUrl(markdownUrl, (url) => {
1703
+ url.searchParams.delete("sections");
1704
+ url.searchParams.delete("section");
1705
+ url.searchParams.delete("tokens");
1706
+ url.searchParams.delete("bytes");
1707
+ url.searchParams.delete("tokenBudget");
1708
+ url.searchParams.delete("byteBudget");
1709
+ url.searchParams.set("sections", "");
1710
+ if (request.tokenBudget) url.searchParams.set("tokenBudget", String(request.tokenBudget));
1711
+ if (request.byteBudget) url.searchParams.set("byteBudget", String(request.byteBudget));
1712
+ });
1713
+ }
1714
+ function buildDocsMarkdownSectionIndex(document, options) {
1715
+ const request = {
1716
+ tokenBudget: options.tokenBudget ?? void 0,
1717
+ byteBudget: options.byteBudget ?? void 0
1718
+ };
1719
+ const sections = collectDocsMarkdownSections(document);
1720
+ const ancestors = [];
1721
+ const metadata = sections.map((section) => {
1722
+ while (ancestors.length > 0 && (ancestors[ancestors.length - 1]?.level ?? Number.NEGATIVE_INFINITY) >= section.level) ancestors.pop();
1723
+ const parentId = ancestors.at(-1)?.id;
1724
+ const item = {
1725
+ id: section.id,
1726
+ heading: section.heading,
1727
+ level: section.level,
1728
+ ...parentId ? { parentId } : {},
1729
+ startLine: section.startLine,
1730
+ endLine: section.endLine,
1731
+ estimatedTokens: estimateDocsMarkdownTokens(section.content),
1732
+ utf8Bytes: docsMarkdownUtf8Bytes(section.content),
1733
+ canonicalUrl: resolveDocsMarkdownSectionCanonicalUrl(options.canonicalUrl, section.id),
1734
+ markdownUrl: resolveDocsMarkdownSectionFetchUrl(options.markdownUrl, section.id, request)
1735
+ };
1736
+ ancestors.push(section);
1737
+ return item;
1738
+ });
1739
+ const fetchBudget = request.tokenBudget || request.byteBudget ? {
1740
+ ...request.tokenBudget ? { tokenBudget: request.tokenBudget } : {},
1741
+ ...request.byteBudget ? { byteBudget: request.byteBudget } : {}
1742
+ } : void 0;
1743
+ return {
1744
+ schemaVersion: 2,
1745
+ format: DOCS_MARKDOWN_SECTION_INDEX_FORMAT,
1746
+ canonicalUrl: options.canonicalUrl,
1747
+ markdownUrl: options.markdownUrl,
1748
+ sectionIndexUrl: options.sectionIndexUrl ?? resolveDocsMarkdownSectionIndexUrl(options.markdownUrl, request),
1749
+ lineNumbering: "body",
1750
+ sectionCount: metadata.length,
1751
+ estimatedTokens: estimateDocsMarkdownTokens(document),
1752
+ utf8Bytes: docsMarkdownUtf8Bytes(document),
1753
+ ...fetchBudget ? { fetchBudget } : {},
1754
+ sections: metadata
1755
+ };
1756
+ }
1757
+ function normalizeRequestedDocsMarkdownSectionId(requestedSection) {
1758
+ let selector = requestedSection.trim();
1759
+ const hashIndex = selector.indexOf("#");
1760
+ if (hashIndex >= 0) selector = selector.slice(hashIndex + 1);
1761
+ try {
1762
+ selector = decodeURIComponent(selector);
1763
+ } catch {}
1764
+ return selector;
1765
+ }
1766
+ function selectDocsMarkdownSection(document, request) {
1767
+ const availableSections = collectDocsMarkdownSections(document);
1768
+ const requestedSection = request.section?.trim();
1769
+ if (!requestedSection) {
1770
+ const { document: budgetedDocument, truncated } = truncateDocsMarkdownSectionContent(document, request);
1771
+ return {
1772
+ found: true,
1773
+ document: budgetedDocument,
1774
+ truncated,
1775
+ tokenBudget: request.tokenBudget ?? void 0,
1776
+ byteBudget: request.byteBudget ?? void 0,
1777
+ estimatedTokens: estimateDocsMarkdownTokens(budgetedDocument),
1778
+ utf8Bytes: docsMarkdownUtf8Bytes(budgetedDocument),
1779
+ availableSections
1780
+ };
1781
+ }
1782
+ const rawRequestedId = requestedSection.trim();
1783
+ const rawExactSection = availableSections.find((candidate) => candidate.id === rawRequestedId);
1784
+ const requestedId = normalizeRequestedDocsMarkdownSectionId(requestedSection);
1785
+ const exactSection = rawExactSection ?? availableSections.find((candidate) => candidate.id === requestedId);
1786
+ const caseInsensitiveSections = exactSection ? [] : availableSections.filter((candidate) => candidate.id.toLowerCase() === requestedId.toLowerCase());
1787
+ const directSection = exactSection ?? (caseInsensitiveSections.length === 1 ? caseInsensitiveSections[0] : void 0);
1788
+ const { body, lineOffset } = resolveDocsMarkdownSectionBody(document);
1789
+ const selectedSharedSection = directSection ? void 0 : findDocsMarkdownSection(body, requestedSection);
1790
+ const sharedAliasSection = selectedSharedSection ? availableSections.find((candidate) => candidate.startLine === selectedSharedSection.startLine + lineOffset) : void 0;
1791
+ const section = directSection ?? sharedAliasSection;
1792
+ if (!section) {
1793
+ const lines = [
1794
+ "# Markdown Section Not Found",
1795
+ "",
1796
+ requestedSection ? `Could not find section \`${requestedSection}\`.` : "No Markdown sections were available in this page."
1797
+ ];
1798
+ if (availableSections.length > 0) lines.push("", "## Available Sections", "", ...availableSections.map((candidate) => `- ${candidate.heading} (#${candidate.id})`));
1799
+ const notFoundDocument = lines.join("\n");
1800
+ return {
1801
+ found: false,
1802
+ requestedSection,
1803
+ document: notFoundDocument,
1804
+ truncated: false,
1805
+ tokenBudget: request.tokenBudget ?? void 0,
1806
+ byteBudget: request.byteBudget ?? void 0,
1807
+ estimatedTokens: estimateDocsMarkdownTokens(notFoundDocument),
1808
+ utf8Bytes: docsMarkdownUtf8Bytes(notFoundDocument),
1809
+ availableSections
1810
+ };
1811
+ }
1812
+ const { document: selectedDocument, truncated } = truncateDocsMarkdownSectionContent(section.content, request);
1813
+ return {
1814
+ found: true,
1815
+ requestedSection,
1816
+ section,
1817
+ document: selectedDocument,
1818
+ truncated,
1819
+ tokenBudget: request.tokenBudget ?? void 0,
1820
+ byteBudget: request.byteBudget ?? void 0,
1821
+ estimatedTokens: estimateDocsMarkdownTokens(selectedDocument),
1822
+ utf8Bytes: docsMarkdownUtf8Bytes(selectedDocument),
1823
+ availableSections
1824
+ };
1825
+ }
1826
+ function resolveDocsMarkdownPageMetadata(page, options) {
1827
+ const canonicalUrl = resolveDocsMarkdownMetadataUrl(page.url, options?.origin);
1828
+ const okfEnabled = resolveDocsOkfConfig(options?.okf).enabled || page.okf !== void 0;
1829
+ return {
1830
+ title: page.title,
1831
+ description: page.description,
1832
+ canonicalUrl,
1833
+ markdownUrl: resolveDocsMarkdownMetadataUrl(page.markdownUrl ?? toDocsMarkdownUrl(page.url), options?.origin),
1834
+ lastUpdated: normalizeDocsMarkdownLastUpdated(page.lastmod ?? page.lastModified),
1835
+ agent: page.agent,
1836
+ ...okfEnabled ? { trust: resolveDocsOkfTrustMetadata({
1837
+ ...page,
1838
+ canonicalUrl
1839
+ }, options?.okf) } : {}
1840
+ };
1841
+ }
1842
+ function renderDocsMarkdownNotFound({ entry = "docs", apiRoute, requestedPath, origin, pages, sitemap }) {
1843
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
1844
+ const resolvedApiRoute = resolveDocsDiscoveryApiRoute(apiRoute);
1845
+ const agentSpecApiRoute = resolvedApiRoute === DEFAULT_DOCS_API_ROUTE ? DEFAULT_AGENT_SPEC_ROUTE : `${resolvedApiRoute}?agent=spec`;
1846
+ const normalizedRequest = normalizeRequestedMarkdownPath(normalizedEntry, requestedPath);
1847
+ const slugPrefix = `/${normalizedEntry}/`;
1848
+ const requestedSlug = normalizedRequest === `/${normalizedEntry}` ? "" : normalizedRequest.slice(slugPrefix.length);
1849
+ const encodedRequestedSlug = requestedSlug.split("/").map(encodeURIComponent).join("/");
1850
+ const requestedMarkdownRoute = toDocsMarkdownUrl(normalizedRequest);
1851
+ const requestedApiRoute = requestedSlug ? `${resolvedApiRoute}?format=markdown&path=${encodedRequestedSlug}` : `${resolvedApiRoute}?format=markdown`;
1852
+ const sitemapConfig = resolveDocsSitemapConfig(sitemap);
1853
+ const recovery = resolveDocsMarkdownRecovery({
1854
+ entry: normalizedEntry,
1855
+ requestedPath,
1856
+ pages
1857
+ });
1858
+ const lines = [
1859
+ "# Docs Page Not Found",
1860
+ "",
1861
+ `Could not find a markdown page for \`${requestedMarkdownRoute}\`.`
1862
+ ];
1863
+ if (recovery.matches.length > 0) {
1864
+ lines.push("", "## Closest Matches", "");
1865
+ for (const match of recovery.matches) {
1866
+ const confidence = `${Math.round(match.confidence * 1e3) / 10}%`;
1867
+ lines.push(`- [${match.title}](${match.markdownUrl}) (${confidence} confidence)${match.description ? ` - ${match.description}` : ""}`);
1868
+ }
1869
+ }
1870
+ lines.push("", "## Recovery", "", "- If a closest match looks right, fetch that markdown URL directly.", "- If the match is uncertain, search the docs first and then fetch the smallest page that answers the task.", "- Use the sitemap routes below to browse the full docs index before guessing another slug.", "", "## Discovery Routes", "", "Use these discovery routes to find the right page:", "", `- Agent discovery spec: \`${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}\``, `- Agent discovery fallback: \`${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE}\``, `- Agent discovery API: \`${agentSpecApiRoute}\``, `- API catalog: \`${DEFAULT_API_CATALOG_ROUTE}\``, `- Agent Skills index: \`${DEFAULT_AGENT_SKILLS_INDEX_ROUTE}\``, `- Agent instructions: \`${DEFAULT_AGENTS_MD_ROUTE}\``, `- Search scope facets: \`${resolvedApiRoute}?audience=agent&response=facets\` (discover valid \`framework\`, \`version\`, \`package\`, and \`tags\` values without page bodies; continue large fields with \`facet={field}&limit={limit}&cursor={nextCursor}\`).`, `- Structured agent search: \`${resolvedApiRoute}?query={query}&audience=agent&response=structured\` (optionally repeat \`framework\`, \`version\`, \`package\`, or \`tags\`).`, `- Docs index markdown: \`/${normalizedEntry}.md\``, `- Requested markdown API route: \`${requestedApiRoute}\``);
1871
+ if (sitemapConfig.enabled) {
1872
+ if (sitemapConfig.markdown.enabled) {
1873
+ lines.push(`- Semantic sitemap: \`${sitemapConfig.markdown.route}\``);
1874
+ if (sitemapConfig.markdown.docsRoute) lines.push(`- Docs-scoped sitemap alias: \`${sitemapConfig.markdown.docsRoute}\``);
1875
+ lines.push(`- Semantic sitemap well-known alias: \`${sitemapConfig.markdown.wellKnownRoute}\``);
1876
+ }
1877
+ if (sitemapConfig.xml.enabled) lines.push(`- XML sitemap: \`${sitemapConfig.xml.route}\``);
1878
+ } else lines.push(`- Sitemap discovery, if enabled: \`${DEFAULT_SITEMAP_MD_ROUTE}\`, \`${DEFAULT_SITEMAP_MD_DOCS_ROUTE}\`, \`${DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE}\`, or \`${DEFAULT_SITEMAP_XML_ROUTE}\``);
1879
+ lines.push("", "The agent discovery spec is the safest first step because it lists the active markdown, sitemap, robots, search, MCP, and feedback routes for this deployment.");
1880
+ return appendDocsMarkdownSitemapFooter(prependDocsMarkdownFrontmatter(lines.join("\n"), {
1881
+ title: "Docs Page Not Found",
1882
+ description: `Could not find a markdown page for ${requestedMarkdownRoute}.`,
1883
+ canonicalUrl: resolveDocsMarkdownMetadataUrl(normalizedRequest, origin),
1884
+ markdownUrl: resolveDocsMarkdownMetadataUrl(requestedMarkdownRoute, origin)
1885
+ }), sitemap);
1886
+ }
1887
+ function extractDocsMarkdownLastModified(document) {
1888
+ const frontmatter = document.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];
1889
+ if (!frontmatter) return void 0;
1890
+ const raw = frontmatter.match(/^last_updated:\s*(.+?)\s*$/m)?.[1]?.trim();
1891
+ if (!raw) return void 0;
1892
+ if (raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1);
1893
+ return raw;
1894
+ }
1895
+ function resolveDocsMarkdownContentLocation(canonicalUrl) {
1896
+ const url = new URL(canonicalUrl);
1897
+ url.pathname = toDocsMarkdownUrl(url.pathname);
1898
+ return url.toString();
1899
+ }
1900
+ function appendDocsMarkdownSectionQuery(value, sectionRequest) {
1901
+ try {
1902
+ const url = new URL(value);
1903
+ if (sectionRequest.section) url.searchParams.set("section", sectionRequest.section);
1904
+ if (sectionRequest.tokenBudget) url.searchParams.set("tokenBudget", String(sectionRequest.tokenBudget));
1905
+ if (sectionRequest.byteBudget) url.searchParams.set("byteBudget", String(sectionRequest.byteBudget));
1906
+ return url.toString();
1907
+ } catch {
1908
+ return value;
1909
+ }
1910
+ }
1911
+ /** Build one standards-aware Markdown response for every framework adapter. */
1912
+ function createDocsMarkdownResponse(options) {
1913
+ const { request, document, entry = "docs", requestedPath, origin = new URL(request.url).origin, pages: rawPages, sitemap, locale } = options;
1914
+ const canonicalUrl = options.canonicalUrl ?? resolveDocsMarkdownCanonicalUrl({
1915
+ origin,
1916
+ entry,
1917
+ requestedPath,
1918
+ locale
1919
+ });
1920
+ const contentLocation = options.contentLocation ?? resolveDocsMarkdownContentLocation(canonicalUrl);
1921
+ const varyHeader = getDocsMarkdownVaryHeader(request);
1922
+ const llmsTxtUrl = new URL(DEFAULT_LLMS_TXT_ROUTE, origin).toString();
1923
+ const discoveryLink = `<${llmsTxtUrl}>; rel="describedby"; type="text/plain"`;
1924
+ const baseSharedHeaders = {
1925
+ "X-Robots-Tag": "noindex",
1926
+ "X-Llms-Txt": llmsTxtUrl,
1927
+ ...locale ? { "Content-Language": locale } : {},
1928
+ ...varyHeader ? { Vary: varyHeader } : {}
1929
+ };
1930
+ const pages = rawPages ? filterDocsPagesByAccess(rawPages, options.principal) : void 0;
1931
+ if (!isDocsPageAccessAllowed(options.access, options.principal)) return new Response("Not Found", {
1932
+ status: 404,
1933
+ headers: {
1934
+ ...baseSharedHeaders,
1935
+ "Cache-Control": "private, no-store",
1936
+ "Content-Type": "text/plain; charset=utf-8"
1937
+ }
1938
+ });
1939
+ if (!document) {
1940
+ const recovery = resolveDocsMarkdownRecovery({
1941
+ entry,
1942
+ requestedPath,
1943
+ pages,
1944
+ sitemap
1945
+ });
1946
+ if (recovery.redirect) return new Response(null, {
1947
+ status: 307,
1948
+ headers: {
1949
+ ...baseSharedHeaders,
1950
+ "Content-Location": contentLocation,
1951
+ Link: `<${canonicalUrl}>; rel="canonical", ${discoveryLink}`,
1952
+ "Cache-Control": "no-store",
1953
+ Location: new URL(recovery.redirect.markdownUrl, request.url).toString()
1954
+ }
1955
+ });
1956
+ return new Response(renderDocsMarkdownNotFound({
1957
+ entry,
1958
+ apiRoute: options.apiRoute,
1959
+ requestedPath,
1960
+ origin,
1961
+ pages,
1962
+ sitemap
1963
+ }), {
1964
+ status: 404,
1965
+ headers: {
1966
+ ...baseSharedHeaders,
1967
+ "Content-Location": contentLocation,
1968
+ Link: `<${canonicalUrl}>; rel="canonical", ${discoveryLink}`,
1969
+ "Cache-Control": "no-store",
1970
+ "Content-Type": "text/markdown; charset=utf-8"
1971
+ }
1972
+ });
1973
+ }
1974
+ const requestUrl = new URL(request.url);
1975
+ const sectionRequest = resolveDocsMarkdownSectionRequest(requestUrl);
1976
+ if (isDocsMarkdownSectionIndexRequest(requestUrl)) {
1977
+ const sectionIndexRequest = sectionRequest ?? {};
1978
+ const sectionIndexContentLocation = resolveDocsMarkdownSectionIndexUrl(contentLocation, sectionIndexRequest);
1979
+ const sectionIndex = buildDocsMarkdownSectionIndex(document, {
1980
+ canonicalUrl,
1981
+ markdownUrl: contentLocation,
1982
+ sectionIndexUrl: sectionIndexContentLocation,
1983
+ tokenBudget: sectionIndexRequest.tokenBudget,
1984
+ byteBudget: sectionIndexRequest.byteBudget
1985
+ });
1986
+ const responseDocument = JSON.stringify(sectionIndex);
1987
+ const lastModified = resolveDocsHttpDate(options.lastModified ?? extractDocsMarkdownLastModified(document));
1988
+ return createDocsCacheableResponse({
1989
+ request,
1990
+ content: responseDocument,
1991
+ lastModified,
1992
+ headers: {
1993
+ ...baseSharedHeaders,
1994
+ "Content-Location": sectionIndexContentLocation,
1995
+ Link: `<${canonicalUrl}>; rel="canonical", <${contentLocation}>; rel="alternate"; type="text/markdown", ${discoveryLink}`,
1996
+ "Cache-Control": options.cacheControl ?? "public, max-age=0, s-maxage=3600",
1997
+ "Content-Type": "application/json; charset=utf-8",
1998
+ "X-Docs-Markdown-Section-Index": DOCS_MARKDOWN_SECTION_INDEX_FORMAT,
1999
+ "X-Docs-Markdown-Section-Count": String(sectionIndex.sectionCount),
2000
+ "X-Docs-Markdown-Source-Estimated-Tokens": String(sectionIndex.estimatedTokens),
2001
+ "X-Docs-Markdown-Source-Utf8-Bytes": String(sectionIndex.utf8Bytes),
2002
+ "X-Docs-Markdown-Estimated-Tokens": String(estimateDocsMarkdownTokens(responseDocument)),
2003
+ "X-Docs-Markdown-Utf8-Bytes": String(docsMarkdownUtf8Bytes(responseDocument)),
2004
+ ...sectionIndexRequest.tokenBudget ? { "X-Docs-Markdown-Token-Budget": String(sectionIndexRequest.tokenBudget) } : {},
2005
+ ...sectionIndexRequest.byteBudget ? { "X-Docs-Markdown-Byte-Budget": String(sectionIndexRequest.byteBudget) } : {},
2006
+ ...lastModified ? { "Last-Modified": lastModified } : {}
2007
+ }
2008
+ });
2009
+ }
2010
+ const sectionResult = sectionRequest ? selectDocsMarkdownSection(document, sectionRequest) : void 0;
2011
+ const responseDocument = sectionResult?.document ?? document;
2012
+ const sectionCanonicalUrl = sectionResult?.found && sectionResult.section ? resolveDocsMarkdownSectionCanonicalUrl(canonicalUrl, sectionResult.section.id) : canonicalUrl;
2013
+ const sectionContentLocation = sectionRequest ? appendDocsMarkdownSectionQuery(contentLocation, sectionRequest) : contentLocation;
2014
+ const sectionHeaders = sectionResult ? {
2015
+ "X-Docs-Markdown-Section": sectionResult.section ? encodeURIComponent(sectionResult.section.id) : "",
2016
+ "X-Docs-Markdown-Section-Found": sectionResult.found ? "true" : "false",
2017
+ "X-Docs-Markdown-Section-Truncated": sectionResult.truncated ? "true" : "false",
2018
+ "X-Docs-Markdown-Estimated-Tokens": String(sectionResult.estimatedTokens),
2019
+ "X-Docs-Markdown-Utf8-Bytes": String(sectionResult.utf8Bytes),
2020
+ ...sectionResult.tokenBudget ? { "X-Docs-Markdown-Token-Budget": String(sectionResult.tokenBudget) } : {},
2021
+ ...sectionResult.byteBudget ? { "X-Docs-Markdown-Byte-Budget": String(sectionResult.byteBudget) } : {}
2022
+ } : {};
2023
+ const lastModified = resolveDocsHttpDate(options.lastModified ?? extractDocsMarkdownLastModified(document));
2024
+ const responseHeaders = {
2025
+ ...baseSharedHeaders,
2026
+ "Content-Location": sectionContentLocation,
2027
+ Link: `<${sectionCanonicalUrl}>; rel="canonical", ${discoveryLink}`,
2028
+ "Cache-Control": sectionResult?.found === false ? "no-store" : options.cacheControl ?? "public, max-age=0, s-maxage=3600",
2029
+ "Content-Type": "text/markdown; charset=utf-8",
2030
+ ...sectionHeaders,
2031
+ ...lastModified ? { "Last-Modified": lastModified } : {}
2032
+ };
2033
+ return createDocsCacheableResponse({
2034
+ request,
2035
+ content: responseDocument,
2036
+ status: sectionResult?.found === false ? 404 : 200,
2037
+ lastModified,
2038
+ headers: responseHeaders
2039
+ });
2040
+ }
2041
+ function findDocsMarkdownPage(entry, pages, requestedPath) {
2042
+ const normalizedRequest = normalizeRequestedMarkdownPath(entry, requestedPath);
2043
+ for (const page of pages) if (normalizeDocsUrlPath(page.url) === normalizedRequest) return page;
2044
+ const normalizedSlug = normalizeDocsPathSegment(requestedPath.replace(/^\//, "").replace(/\.md$/i, ""));
2045
+ for (const page of pages) if (page.slug !== void 0 && normalizeDocsPathSegment(page.slug) === normalizedSlug) return page;
2046
+ return null;
2047
+ }
2048
+ function shouldRenderLlmsDirective(options) {
2049
+ if (options?.llms === false) return false;
2050
+ if (options?.llms && typeof options.llms === "object" && options.llms.enabled === false) return false;
2051
+ return true;
2052
+ }
2053
+ function resolveDocsAgentDocumentContext({ entry = "docs", apiRoute, apiCatalog: explicitApiCatalog, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi, markdown }) {
2054
+ const feedbackRoute = feedback?.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
2055
+ const resolvedApiRoute = resolveDocsDiscoveryApiRoute(apiRoute);
2056
+ const openapiConfig = resolveDocsOpenApiDiscoveryConfig(openapi);
2057
+ return {
2058
+ apiRoute: resolvedApiRoute,
2059
+ normalizedEntry: normalizeDocsPathSegment(entry) || "docs",
2060
+ siteTitle: compactSkillText(llms?.siteTitle ?? "Documentation"),
2061
+ siteDescription: llms?.siteDescription ? compactSkillText(llms.siteDescription) : void 0,
2062
+ llmsEnabled: llms?.enabled ?? true,
2063
+ searchEnabled: isSearchEnabled(search),
2064
+ contentChangesEnabled: resolveDocsContentChangesConfig(contentChanges).enabled,
2065
+ mcpEnabled: mcp.enabled,
2066
+ feedbackEnabled: feedback?.enabled ?? false,
2067
+ sitemapConfig: resolveDocsSitemapConfig(sitemap),
2068
+ robotsEnabled: isRobotsDiscoveryEnabled(robots),
2069
+ openapiConfig: {
2070
+ ...openapiConfig,
2071
+ url: resolveDocsOpenApiDiscoveryUrl(openapiConfig, resolvedApiRoute)
2072
+ },
2073
+ feedbackRoute,
2074
+ feedbackSchemaRoute: feedback?.schemaRoute ?? `${feedbackRoute}/schema`,
2075
+ llmsSections: resolveDocsLlmsTxtSections(llms),
2076
+ markdownAcceptHeader: markdown?.acceptHeader === false ? null : "text/markdown",
2077
+ markdownSignatureAgentHeader: markdown?.signatureAgentHeader === false ? null : DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER,
2078
+ markdownSectionDiscoveryEnabled: markdown?.sectionDiscovery !== false,
2079
+ apiCatalogEnabled: explicitApiCatalog ?? llms?.apiCatalog ?? true
2080
+ };
2081
+ }
2082
+ function appendDocsMarkdownNegotiationStartLines(lines, context, variant) {
2083
+ if (context.markdownAcceptHeader) lines.push(variant === "skill" ? `- You can also request ${context.markdownAcceptHeader} from normal page URLs.` : `- Normal docs pages can return markdown with Accept: ${context.markdownAcceptHeader}.`);
2084
+ if (context.markdownSignatureAgentHeader) lines.push(variant === "skill" ? `- Requests with ${context.markdownSignatureAgentHeader} on normal page URLs receive markdown automatically.` : `- Normal docs pages can also return markdown when ${context.markdownSignatureAgentHeader} is present.`);
2085
+ }
2086
+ function appendDocsSearchStartLine(lines, context, variant) {
2087
+ if (!context.searchEnabled) return;
2088
+ lines.push(`- Discover valid search filters with ${context.apiRoute}?audience=agent&response=facets before choosing framework, version, package, or tags values; continue a large field with facet={field}, limit={limit}, and its nextCursor.`, variant === "skill" ? `- Search with ${context.apiRoute}?query={query}&audience=agent&response=structured when you do not know the page; optionally repeat framework, version, package, or tags filters and add explain=true for retrieval evidence.` : `- Search with ${context.apiRoute}?query={query}&audience=agent&response=structured when the route is unknown; optionally repeat framework, version, package, or tags filters and add explain=true for retrieval evidence.`);
2089
+ }
2090
+ function appendDocsContentChangesStartLine(lines, context) {
2091
+ if (!context.contentChangesEnabled) return;
2092
+ lines.push(`- Synchronize changed documents with ${context.apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}; pass the returned indexGeneration as since on the next request and clear stale state when resetRequired is true.`);
2093
+ }
2094
+ function appendDocsOpenApiStartLine(lines, context, variant) {
2095
+ if (!context.openapiConfig.enabled || !context.openapiConfig.url) return;
2096
+ lines.push(variant === "skill" ? `- Fetch ${context.openapiConfig.url} for the machine-readable OpenAPI schema before scraping API reference pages.` : `- Fetch ${context.openapiConfig.url} before scraping API reference pages; prefer schemas over prose.`);
2097
+ }
2098
+ function appendDocsLlmsStartLines(lines, context, variant) {
2099
+ if (!context.llmsEnabled) return;
2100
+ lines.push(variant === "skill" ? `- Use ${DEFAULT_LLMS_TXT_ROUTE} for a compact docs index.` : `- Use ${DEFAULT_LLMS_TXT_ROUTE} as the compact docs map.`, variant === "skill" ? `- Use ${DEFAULT_LLMS_FULL_TXT_ROUTE} for full markdown context.` : `- Use ${DEFAULT_LLMS_FULL_TXT_ROUTE} when you need the full markdown bundle.`);
2101
+ for (const section of context.llmsSections) lines.push(variant === "skill" ? `- Use ${section.route} for the ${section.title} llms.txt section.` : `- Use ${section.route} for the ${section.title} section map.`);
2102
+ }
2103
+ function appendDocsSitemapStartLines(lines, context, variant) {
2104
+ if (!context.sitemapConfig.enabled) return;
2105
+ if (variant === "skill") {
2106
+ if (context.sitemapConfig.xml.enabled) lines.push(`- Use ${context.sitemapConfig.xml.route} to check canonical page freshness.`);
2107
+ if (context.sitemapConfig.markdown.enabled) lines.push(`- Use ${context.sitemapConfig.markdown.route} for a semantic docs map.`);
2108
+ return;
2109
+ }
2110
+ if (context.sitemapConfig.markdown.enabled) lines.push(`- Use ${context.sitemapConfig.markdown.route} for a semantic sitemap with sections.`);
2111
+ if (context.sitemapConfig.xml.enabled) lines.push(`- Use ${context.sitemapConfig.xml.route} for canonical URLs and freshness metadata.`);
2112
+ }
2113
+ function appendDocsRobotsStartLine(lines, context, variant) {
2114
+ if (!context.robotsEnabled) return;
2115
+ lines.push(variant === "skill" ? `- Check ${DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE} for crawler and AI-agent access policy.` : `- Check ${DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE} before crawling broadly.`);
2116
+ }
2117
+ function appendDocsMcpStartLine(lines, context, variant) {
2118
+ if (!context.mcpEnabled) return;
2119
+ lines.push(variant === "skill" ? `- Use ${DEFAULT_MCP_WELL_KNOWN_ROUTE} or ${DEFAULT_MCP_PUBLIC_ROUTE} for MCP tools, resources, and configured prompts.` : `- Use MCP at ${DEFAULT_MCP_PUBLIC_ROUTE} or ${DEFAULT_MCP_WELL_KNOWN_ROUTE} when your environment supports MCP capabilities.`);
2120
+ }
2121
+ function appendDocsFeedbackStartLine(lines, context, variant) {
2122
+ if (!context.feedbackEnabled) return;
2123
+ lines.push(variant === "skill" ? `- Read ${context.feedbackSchemaRoute} before posting agent feedback to ${context.feedbackRoute}.` : `- Read ${context.feedbackSchemaRoute} before posting feedback to ${context.feedbackRoute}.`);
2124
+ }
2125
+ function appendDocsAgentStartHereLines(lines, context, variant) {
2126
+ const agentSpecFallback = context.apiRoute === DEFAULT_DOCS_API_ROUTE ? DEFAULT_AGENT_SPEC_ROUTE : `${context.apiRoute}?agent=spec`;
2127
+ lines.push(variant === "skill" ? `- Fetch ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}; fall back to ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE} or ${agentSpecFallback}.` : `- Read ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE} first; fall back to ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE} or ${agentSpecFallback}.`);
2128
+ if (context.apiCatalogEnabled) lines.push(variant === "skill" ? `- Use ${DEFAULT_API_CATALOG_ROUTE} for standards-based API discovery.` : `- Use ${DEFAULT_API_CATALOG_ROUTE} for RFC 9727 API discovery.`);
2129
+ lines.push(variant === "skill" ? `- Use ${DEFAULT_AGENT_SKILLS_INDEX_ROUTE} for hashed skill discovery.` : `- Use ${DEFAULT_AGENT_SKILLS_INDEX_ROUTE} for integrity-checked skills.`, variant === "skill" ? `- Fetch /${context.normalizedEntry}.md for the root docs page.` : `- Read /${context.normalizedEntry}.md for the root docs page.`, variant === "skill" ? `- Fetch /${context.normalizedEntry}/{slug}.md for page-specific context.` : `- Read /${context.normalizedEntry}/{slug}.md for page-specific context.`);
2130
+ if (context.markdownSectionDiscoveryEnabled) lines.push(variant === "skill" ? `- Fetch /${context.normalizedEntry}/{slug}.md?sections to list section IDs and size estimates before requesting ?section={id}.` : `- Read /${context.normalizedEntry}/{slug}.md?sections before fetching ?section={id} when a full page is larger than needed.`);
2131
+ if (variant === "skill") {
2132
+ appendDocsMarkdownNegotiationStartLines(lines, context, variant);
2133
+ appendDocsSearchStartLine(lines, context, variant);
2134
+ appendDocsContentChangesStartLine(lines, context);
2135
+ appendDocsOpenApiStartLine(lines, context, variant);
2136
+ appendDocsLlmsStartLines(lines, context, variant);
2137
+ appendDocsSitemapStartLines(lines, context, variant);
2138
+ appendDocsRobotsStartLine(lines, context, variant);
2139
+ appendDocsMcpStartLine(lines, context, variant);
2140
+ appendDocsFeedbackStartLine(lines, context, variant);
2141
+ return;
2142
+ }
2143
+ appendDocsLlmsStartLines(lines, context, variant);
2144
+ appendDocsSitemapStartLines(lines, context, variant);
2145
+ appendDocsRobotsStartLine(lines, context, variant);
2146
+ appendDocsSearchStartLine(lines, context, variant);
2147
+ appendDocsContentChangesStartLine(lines, context);
2148
+ appendDocsOpenApiStartLine(lines, context, variant);
2149
+ appendDocsMcpStartLine(lines, context, variant);
2150
+ appendDocsFeedbackStartLine(lines, context, variant);
2151
+ appendDocsMarkdownNegotiationStartLines(lines, context, variant);
2152
+ }
2153
+ function appendDocsLlmsRouteLines(lines, context) {
2154
+ if (!context.llmsEnabled) return;
2155
+ lines.push(`- llms.txt: ${DEFAULT_LLMS_TXT_ROUTE}`, `- llms-full.txt: ${DEFAULT_LLMS_FULL_TXT_ROUTE}`, `- llms well-known aliases: ${DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE}, ${DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE}`);
2156
+ for (const section of context.llmsSections) {
2157
+ lines.push(`- ${section.title} llms.txt: ${section.route}`);
2158
+ lines.push(`- ${section.title} llms-full.txt: ${section.fullRoute}`);
2159
+ }
2160
+ }
2161
+ function appendDocsOpenApiRouteLines(lines, context) {
2162
+ if (!context.openapiConfig.enabled || !context.openapiConfig.url) return;
2163
+ lines.push(`- OpenAPI schema: ${context.openapiConfig.url}`);
2164
+ if (context.openapiConfig.apiReferencePath) lines.push(`- API reference: ${context.openapiConfig.apiReferencePath}`);
2165
+ for (const version of context.openapiConfig.versions ?? []) lines.push(`- OpenAPI ${version.label}${version.default ? " (default)" : ""}: ${version.url}`, `- API reference ${version.label}: ${version.apiReferencePath}`);
2166
+ }
2167
+ function appendDocsSitemapRouteLines(lines, context) {
2168
+ if (!context.sitemapConfig.enabled) return;
2169
+ if (context.sitemapConfig.xml.enabled) lines.push(`- Sitemap XML: ${context.sitemapConfig.xml.route}`);
2170
+ if (context.sitemapConfig.markdown.enabled) lines.push(`- Sitemap Markdown: ${context.sitemapConfig.markdown.route}`, ...context.sitemapConfig.markdown.docsRoute ? [`- Sitemap docs alias: ${context.sitemapConfig.markdown.docsRoute}`] : [], `- Sitemap well-known alias: ${context.sitemapConfig.markdown.wellKnownRoute}`);
2171
+ }
2172
+ function appendDocsMcpRouteLines(lines, context) {
2173
+ if (!context.mcpEnabled) return;
2174
+ lines.push(`- MCP: ${DEFAULT_MCP_PUBLIC_ROUTE}, ${DEFAULT_MCP_WELL_KNOWN_ROUTE}`);
2175
+ }
2176
+ function appendDocsAgentPublicRouteLines(lines, context, variant) {
2177
+ if (variant === "skill") {
2178
+ lines.push(`- Agent instructions: ${DEFAULT_AGENTS_MD_ROUTE}`, `- Agent instructions well-known alias: ${DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE}`, `- Agent instructions API format: ${context.apiRoute}?format=agents`, `- Skill document: ${DEFAULT_SKILL_MD_ROUTE}`, `- Skill well-known alias: ${DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE}`, `- Skill API format: ${context.apiRoute}?format=skill`, `- Agent discovery: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}`, `- Agent discovery fallback: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE}`);
2179
+ if (context.apiCatalogEnabled) lines.push(`- API catalog (RFC 9727): ${DEFAULT_API_CATALOG_ROUTE}`);
2180
+ lines.push(`- Agent Skills index: ${DEFAULT_AGENT_SKILLS_INDEX_ROUTE}`, `- Agent Skills artifacts: ${DEFAULT_AGENT_SKILLS_ROUTE_PATTERN}`, `- Markdown root: /${context.normalizedEntry}.md`, `- Markdown pages: /${context.normalizedEntry}/{slug}.md`);
2181
+ if (context.markdownSectionDiscoveryEnabled) lines.push(`- Markdown section index: /${context.normalizedEntry}/{slug}.md?sections`, `- Markdown section fetch: /${context.normalizedEntry}/{slug}.md?section={id}&tokenBudget={tokens}`);
2182
+ if (context.robotsEnabled) lines.push(`- Robots policy: ${DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE}`);
2183
+ appendDocsLlmsRouteLines(lines, context);
2184
+ appendDocsOpenApiRouteLines(lines, context);
2185
+ appendDocsSitemapRouteLines(lines, context);
2186
+ appendDocsMcpRouteLines(lines, context);
2187
+ if (context.contentChangesEnabled) lines.push(`- Content changes: ${context.apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}`);
2188
+ return;
2189
+ }
2190
+ lines.push(`- Agent instructions: ${DEFAULT_AGENTS_MD_ROUTE}`, `- Agent instructions well-known alias: ${DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE}`, `- Agent instructions API format: ${context.apiRoute}?format=agents`, `- Agent instructions aliases: ${DEFAULT_AGENT_MD_ROUTE}, ${DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE}`, `- Site skill: ${DEFAULT_SKILL_MD_ROUTE}`, `- Site skill well-known alias: ${DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE}`, `- Site skill API format: ${context.apiRoute}?format=skill`, `- Markdown root: /${context.normalizedEntry}.md`, `- Markdown pages: /${context.normalizedEntry}/{slug}.md`, `- Agent discovery: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}`, `- Agent discovery fallback: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE}`);
2191
+ if (context.markdownSectionDiscoveryEnabled) lines.push(`- Markdown section index: /${context.normalizedEntry}/{slug}.md?sections`, `- Markdown section fetch: /${context.normalizedEntry}/{slug}.md?section={id}&tokenBudget={tokens}`);
2192
+ if (context.apiCatalogEnabled) lines.push(`- API catalog (RFC 9727): ${DEFAULT_API_CATALOG_ROUTE}`);
2193
+ lines.push(`- Agent Skills index: ${DEFAULT_AGENT_SKILLS_INDEX_ROUTE}`, `- Agent Skills artifacts: ${DEFAULT_AGENT_SKILLS_ROUTE_PATTERN}`);
2194
+ appendDocsLlmsRouteLines(lines, context);
2195
+ if (context.robotsEnabled) lines.push(`- Robots policy: ${DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE}`);
2196
+ appendDocsSitemapRouteLines(lines, context);
2197
+ appendDocsOpenApiRouteLines(lines, context);
2198
+ appendDocsMcpRouteLines(lines, context);
2199
+ if (context.contentChangesEnabled) lines.push(`- Content changes: ${context.apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}`);
2200
+ }
2201
+ function renderDocsMarkdownDocument(page, options) {
2202
+ const explicitAgentContent = page.agentRawContent ?? page.agentContent;
2203
+ if (explicitAgentContent !== void 0) return appendDocsMarkdownSitemapFooter(prependDocsMarkdownFrontmatter(upsertPageAgentContractMarkdown(explicitAgentContent, page.agent), resolveDocsMarkdownPageMetadata(page, options)), options?.sitemap);
2204
+ const relatedLines = renderDocsRelatedMarkdownLines(page.related);
2205
+ const lines = [`# ${page.title}`, `URL: ${page.url}`];
2206
+ if (shouldRenderLlmsDirective(options)) lines.push(DOCS_LLMS_TXT_DIRECTIVE_LINE);
2207
+ if (page.description) lines.push(`Description: ${page.description}`);
2208
+ lines.push(...relatedLines);
2209
+ lines.push("", upsertPageAgentContractMarkdown(page.agentFallbackRawContent ?? page.agentFallbackContent ?? page.rawContent ?? page.content, page.agent));
2210
+ return appendDocsMarkdownSitemapFooter(prependDocsMarkdownFrontmatter(lines.join("\n"), {
2211
+ ...resolveDocsMarkdownPageMetadata(page, options),
2212
+ generatedPreamble: true
2213
+ }), options?.sitemap);
2214
+ }
2215
+ function renderDocsSkillDocument(options) {
2216
+ const { origin } = options;
2217
+ const context = resolveDocsAgentDocumentContext(options);
2218
+ const lines = [
2219
+ "---",
2220
+ "name: docs",
2221
+ `description: ${toYamlString(truncateSkillDescription(`Use ${context.siteTitle} through markdown routes, llms.txt, robots.txt, agent discovery, search, and MCP when available.`))}`,
2222
+ "---",
2223
+ "",
2224
+ `# ${context.siteTitle} Skill`,
2225
+ "",
2226
+ `Base URL: ${origin}`
2227
+ ];
2228
+ if (context.siteDescription) lines.push(`Description: ${context.siteDescription}`);
2229
+ lines.push("", "## When To Use", "Use this skill when you need to read or implement against this documentation site.", "", "## Start Here");
2230
+ appendDocsAgentStartHereLines(lines, context, "skill");
2231
+ lines.push("", "## Routes");
2232
+ appendDocsAgentPublicRouteLines(lines, context, "skill");
2233
+ lines.push("", "## Reusable Framework Skills", "For framework setup, CLI, page actions, Ask AI, or configuration work, install the reusable Farming Labs skills:", "", "```sh", "npx skills add farming-labs/docs", "```");
2234
+ return lines.join("\n");
2235
+ }
2236
+ function renderDocsAgentsDocument(options) {
2237
+ const { origin } = options;
2238
+ const context = resolveDocsAgentDocumentContext(options);
2239
+ const lines = [
2240
+ "# Agent Instructions",
2241
+ "",
2242
+ `Site: ${context.siteTitle}`,
2243
+ `Base URL: ${origin}`
2244
+ ];
2245
+ if (context.siteDescription) lines.push(`Description: ${context.siteDescription}`);
2246
+ lines.push("", "## Start Here");
2247
+ appendDocsAgentStartHereLines(lines, context, "agents");
2248
+ lines.push("", "## Working Rules", "- Prefer markdown routes, llms.txt, sitemap.md, OpenAPI schemas, and MCP capabilities over scraping rendered HTML.", "- Treat generated context files as discovery aids, then fetch the smallest page or section that answers the task.", "- Preserve canonical docs URLs when citing pages back to humans.", "- If a markdown route returns a recovery page, use its closest matches, sitemap, and discovery spec before guessing another slug.", "", "## Public Routes");
2249
+ appendDocsAgentPublicRouteLines(lines, context, "agents");
2250
+ lines.push("", "## Framework Maintenance", "- For @farming-labs/docs projects, keep the framework package current before debugging missing agent surfaces.", "", "```sh", "npx @farming-labs/docs@latest upgrade --latest", "```", "", "- For framework setup, configuration, CLI, Ask AI, page actions, or theme work, install the reusable Skills pack:", "", "```sh", "npx skills add farming-labs/docs", "```");
2251
+ return lines.join("\n");
2252
+ }
2253
+ /** Resolve only the task tools that the advertised MCP endpoint actually exposes. */
2254
+ function resolveDocsAgentContractMcpTools(mcp) {
2255
+ if (!mcp.enabled) return void 0;
2256
+ const tools = {};
2257
+ if (mcp.tools.listTasks !== false) tools.list = "list_tasks";
2258
+ if (mcp.tools.readTask !== false) tools.read = "read_task";
2259
+ return tools.list || tools.read ? tools : void 0;
2260
+ }
2261
+ function buildDocsAgentDiscoverySpec({ origin, entry = "docs", apiRoute, apiCatalog: explicitApiCatalog, i18n = null, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi, markdown, publishedSkills = [], agentCard }) {
2262
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
2263
+ const resolvedApiRoute = resolveDocsDiscoveryApiRoute(apiRoute);
2264
+ const apiQueryRoute = (query) => `${resolvedApiRoute}?${query}`;
2265
+ const localesEnabled = i18n !== null;
2266
+ const searchEnabled = isSearchEnabled(search);
2267
+ const contentChangesEnabled = resolveDocsContentChangesConfig(contentChanges).enabled;
2268
+ const feedbackRoute = feedback?.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
2269
+ const feedbackSchemaRoute = feedback?.schemaRoute ?? `${feedbackRoute}/schema`;
2270
+ const llmsEnabled = llms?.enabled ?? true;
2271
+ const llmsSections = resolveDocsLlmsTxtSections(llms);
2272
+ const sitemapConfig = resolveDocsSitemapConfig(sitemap, { baseUrl: llms?.baseUrl });
2273
+ const robotsEnabled = isRobotsDiscoveryEnabled(robots);
2274
+ const openapiConfig = resolveDocsOpenApiDiscoveryConfig(openapi);
2275
+ const defaultOpenapiRoute = apiQueryRoute("format=openapi");
2276
+ const openapiUrl = resolveDocsOpenApiDiscoveryUrl(openapiConfig, resolvedApiRoute);
2277
+ const agentContractMcpTools = resolveDocsAgentContractMcpTools(mcp);
2278
+ const apiCatalogEnabled = explicitApiCatalog ?? llms?.apiCatalog ?? true;
2279
+ const markdownSectionDiscoveryEnabled = markdown?.sectionDiscovery !== false;
2280
+ const protectedResource = mcp.enabled && mcp.security?.authenticate ? mcp.security.protectedResource : void 0;
2281
+ const protectedResourceMetadataRoutes = protectedResource ? getDocsMcpProtectedResourceMetadataRoutes(mcp.route) : [];
2282
+ return {
2283
+ $schema: DOCS_AGENT_MANIFEST_SCHEMA_URI,
2284
+ format: DOCS_AGENT_MANIFEST_FORMAT,
2285
+ version: DOCS_AGENT_MANIFEST_VERSION,
2286
+ name: "@farming-labs/docs",
2287
+ profile: "full",
2288
+ profiles: {
2289
+ default: "full",
2290
+ full: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
2291
+ compact: `${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}?profile=compact`
2292
+ },
2293
+ baseUrl: origin,
2294
+ site: {
2295
+ title: llms?.siteTitle ?? "Documentation",
2296
+ description: llms?.siteDescription,
2297
+ entry: normalizedEntry,
2298
+ baseUrl: llms?.baseUrl ?? origin
2299
+ },
2300
+ locales: {
2301
+ enabled: localesEnabled,
2302
+ available: i18n?.locales ?? [],
2303
+ default: i18n?.defaultLocale ?? null,
2304
+ queryParam: "lang",
2305
+ fallbackQueryParam: "locale"
2306
+ },
2307
+ capabilities: {
2308
+ markdownRoutes: true,
2309
+ markdownSectionDiscovery: markdownSectionDiscoveryEnabled,
2310
+ agentMdOverrides: true,
2311
+ agentBlocks: true,
2312
+ structuredAgentContracts: true,
2313
+ agents: true,
2314
+ llms: llmsEnabled,
2315
+ skills: true,
2316
+ apiCatalog: apiCatalogEnabled,
2317
+ agentSkillsDiscovery: true,
2318
+ mcp: mcp.enabled,
2319
+ search: searchEnabled,
2320
+ contentChanges: contentChangesEnabled,
2321
+ sitemap: sitemapConfig.enabled,
2322
+ robots: robotsEnabled,
2323
+ structuredData: true,
2324
+ apiReference: openapiConfig.enabled,
2325
+ openapi: openapiConfig.enabled,
2326
+ agentFeedback: feedback?.enabled ?? false,
2327
+ locales: localesEnabled
2328
+ },
2329
+ api: {
2330
+ docs: resolvedApiRoute,
2331
+ config: apiQueryRoute("format=config"),
2332
+ diagnostics: apiQueryRoute("format=diagnostics"),
2333
+ agentSpec: resolvedApiRoute === DEFAULT_DOCS_API_ROUTE ? DEFAULT_AGENT_SPEC_ROUTE : apiQueryRoute("agent=spec"),
2334
+ agentSpecDefault: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
2335
+ agentSpecFallback: DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
2336
+ agentSpecWellKnown: DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
2337
+ agentSpecWellKnownJson: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
2338
+ agentSpecQuery: apiQueryRoute("agent=spec"),
2339
+ agents: apiQueryRoute("format=agents"),
2340
+ ...apiCatalogEnabled ? {
2341
+ apiCatalog: DEFAULT_API_CATALOG_ROUTE,
2342
+ apiCatalogQuery: apiQueryRoute(`format=${DEFAULT_API_CATALOG_FORMAT}`)
2343
+ } : {},
2344
+ agentSkillsIndex: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
2345
+ legacySkillsIndex: DEFAULT_LEGACY_SKILLS_INDEX_ROUTE,
2346
+ ...agentCard ? { agentCard: DEFAULT_A2A_AGENT_CARD_ROUTE } : {},
2347
+ openapi: defaultOpenapiRoute,
2348
+ ...contentChangesEnabled ? { contentChanges: apiQueryRoute(`audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}`) } : {}
2349
+ },
2350
+ apiCatalog: {
2351
+ enabled: apiCatalogEnabled,
2352
+ route: apiCatalogEnabled ? DEFAULT_API_CATALOG_ROUTE : null,
2353
+ api: apiCatalogEnabled ? apiQueryRoute(`format=${DEFAULT_API_CATALOG_FORMAT}`) : null,
2354
+ mediaType: API_CATALOG_MEDIA_TYPE,
2355
+ profile: API_CATALOG_PROFILE_URI
2356
+ },
2357
+ config: {
2358
+ format: DEFAULT_DOCS_CONFIG_FORMAT,
2359
+ endpoint: apiQueryRoute("format=config")
2360
+ },
2361
+ markdown: {
2362
+ enabled: true,
2363
+ acceptHeader: markdown?.acceptHeader === false ? null : "text/markdown",
2364
+ signatureAgentHeader: markdown?.signatureAgentHeader === false ? null : DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER,
2365
+ pagePattern: `/${normalizedEntry}/{slug}.md`,
2366
+ rootPage: `/${normalizedEntry}.md`,
2367
+ apiPattern: apiQueryRoute("format=markdown&path={slug}"),
2368
+ sectionDiscovery: {
2369
+ enabled: markdownSectionDiscoveryEnabled,
2370
+ ...markdownSectionDiscoveryEnabled ? {
2371
+ format: DOCS_MARKDOWN_SECTION_INDEX_FORMAT,
2372
+ indexParam: "sections",
2373
+ sectionParam: "section",
2374
+ tokenBudgetParam: "tokenBudget",
2375
+ byteBudgetParam: "byteBudget"
2376
+ } : {}
2377
+ },
2378
+ resolutionOrder: [
2379
+ "agent.md",
2380
+ "agent audience projection",
2381
+ "shared page markdown"
2382
+ ]
2383
+ },
2384
+ agentContract: {
2385
+ enabled: true,
2386
+ schemaVersion: "page-agent-contract.v1",
2387
+ source: "page-frontmatter",
2388
+ frontmatterPath: "agent",
2389
+ markdownSection: "Agent Contract",
2390
+ mcpField: "agent",
2391
+ ...agentContractMcpTools ? { mcpTools: agentContractMcpTools } : {},
2392
+ usefulContractFields: ["task", "outcome"],
2393
+ fields: PAGE_AGENT_CONTRACT_FIELD_SCHEMA
2394
+ },
2395
+ llms: {
2396
+ enabled: llmsEnabled,
2397
+ defaultTxt: DEFAULT_LLMS_TXT_ROUTE,
2398
+ defaultFull: DEFAULT_LLMS_FULL_TXT_ROUTE,
2399
+ txt: apiQueryRoute("format=llms"),
2400
+ full: apiQueryRoute("format=llms-full"),
2401
+ publicTxt: DEFAULT_LLMS_TXT_ROUTE,
2402
+ publicFull: DEFAULT_LLMS_FULL_TXT_ROUTE,
2403
+ wellKnownTxt: DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE,
2404
+ wellKnownFull: DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE,
2405
+ ...llmsSections.length > 0 ? { sections: llmsSections.map((section) => ({
2406
+ title: section.title,
2407
+ description: section.description,
2408
+ match: section.match,
2409
+ txt: section.route,
2410
+ full: section.fullRoute
2411
+ })) } : {}
2412
+ },
2413
+ sitemap: {
2414
+ enabled: sitemapConfig.enabled,
2415
+ xml: {
2416
+ enabled: sitemapConfig.xml.enabled,
2417
+ route: sitemapConfig.xml.route,
2418
+ api: apiQueryRoute("format=sitemap-xml"),
2419
+ defaultRoute: DEFAULT_SITEMAP_XML_ROUTE
2420
+ },
2421
+ markdown: {
2422
+ enabled: sitemapConfig.markdown.enabled,
2423
+ route: sitemapConfig.markdown.route,
2424
+ docsRoute: sitemapConfig.markdown.docsRoute,
2425
+ wellKnownRoute: sitemapConfig.markdown.wellKnownRoute,
2426
+ api: apiQueryRoute("format=sitemap-md"),
2427
+ defaultRoute: DEFAULT_SITEMAP_MD_ROUTE,
2428
+ defaultDocsRoute: DEFAULT_SITEMAP_MD_DOCS_ROUTE,
2429
+ defaultWellKnownRoute: DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE
2430
+ }
2431
+ },
2432
+ robots: {
2433
+ enabled: robotsEnabled,
2434
+ route: DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE,
2435
+ defaultRoute: DEFAULT_AGENT_DISCOVERY_ROBOTS_TXT_ROUTE
2436
+ },
2437
+ structuredData: {
2438
+ enabled: true,
2439
+ format: "application/ld+json",
2440
+ schema: "https://schema.org/TechArticle",
2441
+ fields: [
2442
+ "headline",
2443
+ "description",
2444
+ "url",
2445
+ "dateModified",
2446
+ "breadcrumb",
2447
+ "mainEntity"
2448
+ ],
2449
+ canonicalUrlField: "url",
2450
+ breadcrumbType: "BreadcrumbList",
2451
+ agentContractType: "HowTo"
2452
+ },
2453
+ openapi: {
2454
+ enabled: openapiConfig.enabled,
2455
+ url: openapiUrl ?? null,
2456
+ source: openapiConfig.source ?? null,
2457
+ specUrl: openapiConfig.specUrl ?? null,
2458
+ apiReferencePath: openapiConfig.apiReferencePath ?? null,
2459
+ ...openapiConfig.operationCount !== void 0 ? { operationCount: openapiConfig.operationCount } : {},
2460
+ versions: openapiConfig.versions?.map((version) => ({
2461
+ ...version,
2462
+ url: resolveDocsOpenApiVersionUrl(openapiConfig, version, resolvedApiRoute)
2463
+ })) ?? [],
2464
+ format: "OpenAPI 3.1"
2465
+ },
2466
+ search: {
2467
+ enabled: searchEnabled,
2468
+ endpoint: apiQueryRoute("query={query}"),
2469
+ agentEndpoint: apiQueryRoute("query={query}&audience=agent"),
2470
+ structuredAgentEndpoint: apiQueryRoute("query={query}&audience=agent&response=structured"),
2471
+ explainedAgentEndpoint: apiQueryRoute("query={query}&audience=agent&response=structured&explain=true"),
2472
+ facetsEndpoint: apiQueryRoute("audience=agent&response=facets"),
2473
+ method: "GET",
2474
+ queryParam: "query",
2475
+ localeParam: "lang",
2476
+ audienceParam: "audience",
2477
+ defaultAudience: "human",
2478
+ supportedAudiences: ["human", "agent"],
2479
+ responseParam: "response",
2480
+ structuredResponseValue: "structured",
2481
+ facetsResponseValue: "facets",
2482
+ responseFormat: "docs-search.v1",
2483
+ explainParam: "explain",
2484
+ explainValue: "true",
2485
+ explanationField: "explanation",
2486
+ explanationFormat: "docs-search-explanation.v1",
2487
+ facetsResponseFormat: "docs-search-facets.v1",
2488
+ filterParams: {
2489
+ framework: "framework",
2490
+ version: "version",
2491
+ package: "package",
2492
+ tags: "tags"
2493
+ },
2494
+ repeatedFilterParams: [
2495
+ "framework",
2496
+ "version",
2497
+ "package",
2498
+ "tags"
2499
+ ],
2500
+ warningsField: "warnings",
2501
+ facetParam: "facet",
2502
+ limitParam: "limit",
2503
+ cursorParam: "cursor",
2504
+ nextCursorField: "nextCursor",
2505
+ hasMoreField: "hasMore",
2506
+ totalField: "total"
2507
+ },
2508
+ contentChanges: {
2509
+ enabled: contentChangesEnabled,
2510
+ endpoint: contentChangesEnabled ? apiQueryRoute(`audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}`) : null,
2511
+ method: "GET",
2512
+ audienceParam: "audience",
2513
+ defaultAudience: "agent",
2514
+ supportedAudiences: ["human", "agent"],
2515
+ responseParam: "response",
2516
+ responseValue: DOCS_CONTENT_CHANGES_RESPONSE_VALUE,
2517
+ sinceParam: "since",
2518
+ format: DOCS_CONTENT_CHANGES_FORMAT,
2519
+ generationField: "indexGeneration",
2520
+ resetRequiredField: "resetRequired",
2521
+ modes: [
2522
+ "snapshot",
2523
+ "delta",
2524
+ "reset"
2525
+ ],
2526
+ bodyFree: true,
2527
+ etag: true
2528
+ },
2529
+ agents: {
2530
+ enabled: true,
2531
+ file: "AGENTS.md",
2532
+ route: DEFAULT_AGENTS_MD_ROUTE,
2533
+ wellKnown: DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE,
2534
+ api: apiQueryRoute("format=agents"),
2535
+ generatedFallback: true,
2536
+ aliases: [DEFAULT_AGENT_MD_ROUTE, DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE]
2537
+ },
2538
+ skills: {
2539
+ enabled: true,
2540
+ file: "skill.md",
2541
+ route: DEFAULT_SKILL_MD_ROUTE,
2542
+ wellKnown: DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE,
2543
+ api: apiQueryRoute("format=skill"),
2544
+ generatedFallback: true,
2545
+ registry: "skills.sh",
2546
+ install: "npx skills add farming-labs/docs",
2547
+ discovery: {
2548
+ schema: AGENT_SKILLS_DISCOVERY_SCHEMA_URI,
2549
+ index: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
2550
+ artifact: DEFAULT_AGENT_SKILLS_ROUTE_PATTERN,
2551
+ archive: DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN,
2552
+ file: `${DEFAULT_AGENT_SKILLS_ROUTE_PATTERN.replace("/SKILL.md", "/{path}")}`,
2553
+ legacyIndex: DEFAULT_LEGACY_SKILLS_INDEX_ROUTE,
2554
+ apiIndex: apiQueryRoute(`format=${DEFAULT_AGENT_SKILLS_INDEX_FORMAT}`),
2555
+ apiArtifact: apiQueryRoute("format=agent-skill&name={name}"),
2556
+ apiFile: apiQueryRoute("format=agent-skill-file&name={name}&path={path}"),
2557
+ digest: "sha256"
2558
+ },
2559
+ published: publishedSkills.map((skill) => ({
2560
+ name: skill.name,
2561
+ description: skill.description,
2562
+ type: skill.type,
2563
+ url: skill.url,
2564
+ digest: skill.digest,
2565
+ files: skill.files.map((file) => ({
2566
+ path: file.path,
2567
+ url: file.url,
2568
+ digest: file.digest
2569
+ }))
2570
+ })),
2571
+ recommended: [{
2572
+ name: "getting-started",
2573
+ description: "Use for installation, init, framework setup, theme CSS, and first docs.config wiring."
2574
+ }]
2575
+ },
2576
+ mcp: {
2577
+ enabled: mcp.enabled,
2578
+ endpoint: mcp.route,
2579
+ defaultEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
2580
+ publicEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
2581
+ wellKnownEndpoint: DEFAULT_MCP_WELL_KNOWN_ROUTE,
2582
+ publicEndpoints: [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE],
2583
+ canonicalEndpoint: mcp.route,
2584
+ name: mcp.name,
2585
+ version: mcp.version,
2586
+ tools: mcp.tools,
2587
+ prompts: mcp.prompts ?? {
2588
+ enabled: false,
2589
+ contracts: false,
2590
+ goldenTasks: []
2591
+ },
2592
+ ...protectedResource ? { protectedResource: {
2593
+ metadataEndpoints: protectedResourceMetadataRoutes,
2594
+ authorizationServers: protectedResource.authorizationServers,
2595
+ scopesSupported: protectedResource.scopesSupported,
2596
+ requiredScopes: protectedResource.requiredScopes
2597
+ } } : {}
2598
+ },
2599
+ feedback: {
2600
+ enabled: feedback?.enabled ?? false,
2601
+ schema: feedbackSchemaRoute,
2602
+ submit: feedbackRoute,
2603
+ schemaQuery: apiQueryRoute("feedback=agent&schema=1"),
2604
+ submitQuery: apiQueryRoute("feedback=agent")
2605
+ },
2606
+ instructions: {
2607
+ preferMarkdownRoutes: true,
2608
+ useMcpWhenAvailable: true,
2609
+ useOpenApiWhenAvailable: true,
2610
+ readFeedbackSchemaBeforeSubmitting: true,
2611
+ doNotAssumeFeedbackPayloadShape: true
2612
+ }
2613
+ };
2614
+ }
2615
+ /** Remove first-hop file inventories while retaining every discovery route and capability. */
2616
+ function compactDocsAgentDiscoverySpec(spec) {
2617
+ return {
2618
+ ...spec,
2619
+ profile: "compact",
2620
+ skills: {
2621
+ ...spec.skills,
2622
+ published: spec.skills.published.map((skill) => ({
2623
+ ...skill,
2624
+ fileCount: skill.files.length,
2625
+ files: []
2626
+ }))
2627
+ }
2628
+ };
2629
+ }
2630
+ function acceptsDocsMarkdown(request) {
2631
+ const accept = request.headers.get("accept");
2632
+ if (!accept) return false;
2633
+ const ranges = accept.split(",").map((value) => {
2634
+ const [rawMediaType = "", ...params] = value.split(";").map((part) => part.trim().toLowerCase());
2635
+ const qualityParam = params.find((param) => param.split("=", 1)[0]?.trim() === "q");
2636
+ const parsedQuality = qualityParam ? Number.parseFloat(qualityParam.slice(qualityParam.indexOf("=") + 1).trim()) : 1;
2637
+ return {
2638
+ mediaType: rawMediaType,
2639
+ quality: Number.isFinite(parsedQuality) ? Math.min(1, Math.max(0, parsedQuality)) : 0
2640
+ };
2641
+ });
2642
+ const qualityFor = (mediaType) => {
2643
+ const matches = ranges.filter((range) => range.mediaType === mediaType);
2644
+ return matches.length > 0 ? Math.max(...matches.map((range) => range.quality)) : void 0;
2645
+ };
2646
+ const markdownQuality = qualityFor("text/markdown");
2647
+ if (!markdownQuality || markdownQuality <= 0) return false;
2648
+ const htmlQuality = qualityFor("text/html") ?? qualityFor("text/*") ?? qualityFor("*/*");
2649
+ return htmlQuality === void 0 || htmlQuality <= markdownQuality;
2650
+ }
2651
+ function normalizeRequestedMarkdownPath(entry, requestedPath) {
2652
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
2653
+ const trimmed = requestedPath.trim().replace(/\.md$/i, "");
2654
+ if (!trimmed) return normalizedEntry;
2655
+ const normalized = normalizeDocsUrlPath(trimmed.startsWith("/") ? trimmed : `/${trimmed}`);
2656
+ if (normalized === normalizedEntry || normalized.startsWith(`${normalizedEntry}/`)) return normalized;
2657
+ const slug = normalizeDocsPathSegment(trimmed);
2658
+ return slug ? normalizeDocsUrlPath(`${normalizedEntry}/${slug}`) : normalizedEntry;
2659
+ }
2660
+ function isSearchEnabled(search) {
2661
+ if (search === false) return false;
2662
+ if (search && typeof search === "object" && search.enabled === false) return false;
2663
+ return true;
2664
+ }
2665
+ function isRobotsDiscoveryEnabled(robots) {
2666
+ if (robots === false) return false;
2667
+ if (robots && typeof robots === "object" && robots.enabled === false) return false;
2668
+ return true;
2669
+ }
2670
+ function resolveDocsOpenApiDiscoveryConfig(openapi) {
2671
+ if (openapi === false || openapi === void 0) return { enabled: false };
2672
+ if (openapi === true) return {
2673
+ enabled: true,
2674
+ url: DEFAULT_OPENAPI_SCHEMA_ROUTE,
2675
+ urlSource: "default",
2676
+ source: "generated",
2677
+ catalogTargets: ["/"]
2678
+ };
2679
+ if (openapi.enabled === false) return { enabled: false };
2680
+ const specUrl = openapi.specUrl?.trim();
2681
+ const source = openapi.source ?? (specUrl ? "configured" : "generated");
2682
+ const requestRelativeSpecUrl = specUrl?.startsWith("/") && !specUrl.startsWith("//");
2683
+ const catalogTargets = openapi.catalogTargets === void 0 ? source === "generated" || requestRelativeSpecUrl ? ["/"] : void 0 : Array.from(new Set(openapi.catalogTargets.filter((target) => typeof target === "string").map((target) => target.trim()).filter(Boolean)));
2684
+ return {
2685
+ enabled: true,
2686
+ url: openapi.url ?? DEFAULT_OPENAPI_SCHEMA_ROUTE,
2687
+ urlSource: openapi.urlSource ?? (openapi.url === void 0 ? "default" : "configured"),
2688
+ source,
2689
+ specUrl: openapi.specUrl,
2690
+ apiReferencePath: openapi.apiReferencePath,
2691
+ catalogTargets,
2692
+ operationCount: openapi.operationCount,
2693
+ versions: openapi.versions?.map((version) => ({ ...version }))
2694
+ };
2695
+ }
2696
+ /** Attach operation totals from the same normalized pages used by search and Markdown. */
2697
+ function withDocsOpenApiOperationCounts(openapi, operationPages) {
2698
+ const resolved = resolveDocsOpenApiDiscoveryConfig(openapi);
2699
+ if (!resolved.enabled) return resolved;
2700
+ return {
2701
+ ...resolved,
2702
+ operationCount: operationPages.length,
2703
+ versions: resolved.versions?.map((version) => ({
2704
+ ...version,
2705
+ operationCount: operationPages.filter((page) => page.version === version.id).length
2706
+ }))
2707
+ };
2708
+ }
2709
+ function resolveDocsOpenApiDiscoveryUrl(openapi, apiRoute) {
2710
+ return openapi.urlSource === "default" && openapi.url === DEFAULT_OPENAPI_SCHEMA_ROUTE ? `${apiRoute}?format=openapi` : openapi.url;
2711
+ }
2712
+ function resolveDocsOpenApiVersionUrl(openapi, version, apiRoute) {
2713
+ if (openapi.urlSource === "default" && openapi.url === DEFAULT_OPENAPI_SCHEMA_ROUTE && version.url.startsWith(`${DEFAULT_OPENAPI_SCHEMA_ROUTE}&`)) return `${apiRoute}?format=openapi&${version.url.slice(DEFAULT_OPENAPI_SCHEMA_ROUTE.length + 1)}`;
2714
+ return version.url;
2715
+ }
2716
+ function compactSkillText(value) {
2717
+ return value.replace(/\s+/g, " ").trim();
2718
+ }
2719
+ function truncateSkillDescription(value) {
2720
+ const normalized = compactSkillText(value);
2721
+ if (normalized.length <= 1024) return normalized;
2722
+ return `${normalized.slice(0, 1021).trimEnd()}...`;
2723
+ }
2724
+ function toYamlString(value) {
2725
+ return JSON.stringify(value);
2726
+ }
2727
+
2728
+ //#endregion
2729
+ export { DEFAULT_AGENTS_MD_ROUTE, DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, DEFAULT_AGENT_FEEDBACK_PAYLOAD_SCHEMA, DEFAULT_AGENT_FEEDBACK_ROUTE, DEFAULT_AGENT_MD_ROUTE, DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, DEFAULT_AGENT_SPEC_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, DEFAULT_DOCS_API_ROUTE, DEFAULT_DOCS_CONFIG_FORMAT, DEFAULT_DOCS_CONFIG_ROUTE, DEFAULT_DOCS_DIAGNOSTICS_FORMAT, DEFAULT_DOCS_DIAGNOSTICS_ROUTE, DEFAULT_LLMS_FULL_TXT_ROUTE, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE, DEFAULT_LLMS_TXT_MAX_CHARS, DEFAULT_LLMS_TXT_ROUTE, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE, DEFAULT_OPENAPI_SCHEMA_ROUTE, DEFAULT_SKILL_MD_ROUTE, DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE, DOCS_CONFIG_MAP_TOP_LEVEL_KEYS, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER, acceptsDocsMarkdown, buildDocsAgentDiscoverySpec, buildDocsAgentFeedbackSchema, buildDocsConfigMap, buildDocsDiagnostics, buildDocsMarkdownSectionIndex, buildDocsMcpEndpointCandidates, collectDocsMarkdownSections, compactDocsAgentDiscoverySpec, createDocsMarkdownResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, findDocsMarkdownPage, getDocsLlmsTxtMaxCharsIssue, getDocsMarkdownCanonicalLinkHeader, getDocsMarkdownVaryHeader, hasDocsMarkdownSignatureAgent, isDocsAgentDiscoveryRequest, isDocsAgentsRequest, isDocsConfigRequest, isDocsDiagnosticsRequest, isDocsLlmsTxtPublicRequest, isDocsMarkdownSectionIndexRequest, isDocsPublicGetRequest, isDocsSkillRequest, matchesDocsLlmsTxtSection, normalizeDocsPathSegment, parseDocsAgentFeedbackData, renderDocsAgentsDocument, renderDocsLlmsTxt, renderDocsMarkdownDocument, renderDocsMarkdownNotFound, renderDocsSkillDocument, resolveDocsAgentContractMcpTools, resolveDocsAgentFeedbackConfig, resolveDocsAgentFeedbackRequest, resolveDocsAgentsFormat, resolveDocsLlmsTxtFormat, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsMarkdownCanonicalUrl, resolveDocsMarkdownRecovery, resolveDocsMarkdownRequest, resolveDocsMarkdownSectionRequest, resolveDocsOpenApiDiscoveryConfig, resolveDocsRequestApiRoute, resolveDocsSkillFormat, selectDocsLlmsTxtContent, selectDocsMarkdownSection, validateDocsAgentFeedbackPayload, withDocsOpenApiOperationCounts };