@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.
- package/README.md +8 -0
- package/dist/access.d.mts +13 -0
- package/dist/access.mjs +56 -0
- package/dist/agent-conformance.d.mts +60 -0
- package/dist/agent-conformance.mjs +538 -0
- package/dist/agent-contract.d.mts +47 -0
- package/dist/agent-contract.mjs +645 -0
- package/dist/agent-detection.d.mts +6 -0
- package/dist/agent-detection.mjs +83 -0
- package/dist/agent-evals.d.mts +224 -0
- package/dist/{agent-evals-BLNLvXbG.mjs → agent-evals.mjs} +7 -565
- package/dist/agent-feedback-evaluations-node.mjs +38 -0
- package/dist/agent-feedback-evaluations.d.mts +91 -0
- package/dist/{agent-feedback-evaluations-CcMGlgsO.mjs → agent-feedback-evaluations.mjs} +2 -2
- package/dist/agent-feedback-loop.d.mts +45 -0
- package/dist/{agent-feedback-loop-CcZL_sb1.mjs → agent-feedback-loop.mjs} +1 -1
- package/dist/agent-maintenance.d.mts +71 -0
- package/dist/{agent-maintenance-CuZ7iAKq.mjs → agent-maintenance.mjs} +1 -1
- package/dist/agent-provenance.d.mts +23 -0
- package/dist/agent-provenance.mjs +77 -0
- package/dist/agent-scope.mjs +321 -0
- package/dist/agent-skills-archive.mjs +86 -0
- package/dist/agent-skills-bundle.d.mts +1 -1
- package/dist/{agent-skills-progressive-disclosure-PjPtTYL-.mjs → agent-skills-progressive-disclosure.mjs} +2 -2
- package/dist/{agent-skills-server-D5IuvwM7.d.mts → agent-skills-server.d.mts} +3 -3
- package/dist/{agent-skills-server-BC7md56k.mjs → agent-skills-server.mjs} +2 -2
- package/dist/agent-skills-vite.d.mts +3 -3
- package/dist/agent-skills-vite.mjs +1 -2
- package/dist/agent-surface-drift.mjs +236 -0
- package/dist/{golden-evaluations-CLUViycJ.mjs → agent-usefulness.mjs} +5 -290
- package/dist/{content-change-hydration-wqXIZHp6.d.mts → agent.d.mts} +10 -411
- package/dist/agent.mjs +2729 -0
- package/dist/{analytics-w3FDOksr.d.mts → analytics.d.mts} +2 -2
- package/dist/{analytics-p8Vhhu2P.mjs → analytics.mjs} +2 -2
- package/dist/{sitemap-server-BUdPBcGs.d.mts → api-reference.d.mts} +2 -6
- package/dist/api-reference.mjs +1481 -0
- package/dist/{audience-exposure-DBrFWULA.d.mts → audience-exposure.d.mts} +1 -1
- package/dist/audience-exposure.mjs +8 -0
- package/dist/audience.d.mts +44 -0
- package/dist/audience.mjs +1654 -0
- package/dist/authoring-mcp.d.mts +44 -0
- package/dist/{authoring-mcp-CiCUQe3o.mjs → authoring-mcp.mjs} +3 -3
- package/dist/browser.d.mts +6 -4
- package/dist/browser.mjs +6 -7
- package/dist/build.d.mts +8 -0
- package/dist/build.mjs +8 -0
- package/dist/changelog.d.mts +20 -0
- package/dist/changelog.mjs +39 -0
- package/dist/{agent-export-CA6IUQ7T.mjs → cli/agent-export.mjs} +19 -45
- package/dist/{agent-CYk2DxY7.mjs → cli/agent.mjs} +6 -35
- package/dist/{agents-BUiVmj8i.mjs → cli/agents.mjs} +5 -18
- package/dist/cli/api-doctor.mjs +206 -0
- package/dist/{authoring-C2ymvYWl.mjs → cli/authoring.mjs} +2 -8
- package/dist/{cloud-6eKrlmKd.mjs → cli/cloud.mjs} +3 -3
- package/dist/{codeblocks-CHpgAwY-.mjs → cli/codeblocks.mjs} +2 -2
- package/dist/cli/config.d.mts +11 -0
- package/dist/{config-BZnKAj_Y.mjs → cli/config.mjs} +1 -1
- package/dist/cli/dev-banner.mjs +58 -0
- package/dist/{dev-Bnn-NUNa.mjs → cli/dev.mjs} +3 -57
- package/dist/{doctor-Bfkrkzad.mjs → cli/doctor.mjs} +41 -226
- package/dist/{downgrade-CHMta85e.mjs → cli/downgrade.mjs} +2 -2
- package/dist/cli/errors.d.mts +4 -0
- package/dist/{errors-BtnVLhNF.mjs → cli/errors.mjs} +1 -1
- package/dist/{feedback-evals-DMilg9pY.mjs → cli/feedback-evals.mjs} +3 -36
- package/dist/{feedback-DL4T5oIO.mjs → cli/feedback.mjs} +1 -1
- package/dist/cli/git-dates.mjs +35 -0
- package/dist/cli/golden-evaluations.mjs +52 -0
- package/dist/cli/index.d.mts +2 -3
- package/dist/cli/index.mjs +38 -38
- package/dist/{init-K0LmXDZ5.mjs → cli/init.mjs} +3 -3
- package/dist/{mcp-iLCmnPJI.mjs → cli/mcp.mjs} +7 -19
- package/dist/{openapi-diff-HcpUNjA-.mjs → cli/openapi-diff.mjs} +2 -7
- package/dist/{openapi-sdk-BQKC6H-_.mjs → cli/openapi-sdk.mjs} +2 -7
- package/dist/{openapi-source-DK6ts_Bk.mjs → cli/openapi-source.mjs} +2 -2
- package/dist/{package-version-HJhYeq4M.mjs → cli/package-version.mjs} +2 -2
- package/dist/{propose-JmUv_BTt.mjs → cli/propose.mjs} +1 -1
- package/dist/{review-C6EmbV0l.mjs → cli/review.mjs} +16 -15
- package/dist/{robots-DteZB8vl.mjs → cli/robots.mjs} +3 -7
- package/dist/{search-Bhuv_YsL.mjs → cli/search.mjs} +7 -20
- package/dist/{sitemap-DW5LDKDa.mjs → cli/sitemap.mjs} +7 -42
- package/dist/{skills-BAPQLSCm.mjs → cli/skills.mjs} +5 -19
- package/dist/{templates-BuQ8YsGv.mjs → cli/templates.mjs} +1 -1
- package/dist/{upgrade-DlQMOFV1.mjs → cli/upgrade.mjs} +2 -2
- package/dist/{utils-C7KDrPpA.mjs → cli/utils.mjs} +1 -1
- package/dist/client/react.d.mts +2 -18
- package/dist/client/react.mjs +1 -157
- package/dist/{cloud-analytics-D14HplI7.mjs → cloud-analytics.mjs} +1 -1
- package/dist/{cloud-ask-ai-BrsTXQf6.d.mts → cloud-ask-ai.d.mts} +2 -2
- package/dist/{cloud-ask-ai-BqKhjpS5.mjs → cloud-ask-ai.mjs} +3 -3
- package/dist/code-blocks.d.mts +51 -0
- package/dist/{code-blocks-GYdvS3BH.mjs → code-blocks.mjs} +2 -2
- package/dist/code-group-mdx.d.mts +11 -0
- package/dist/code-group-mdx.mjs +57 -0
- package/dist/content-change-hydration.d.mts +87 -0
- package/dist/{openapi-mcp-BIOTLiA7.mjs → content-change-hydration.mjs} +5 -210
- package/dist/content-changes.d.mts +47 -0
- package/dist/content-changes.mjs +216 -0
- package/dist/create-theme.d.mts +48 -0
- package/dist/create-theme.mjs +57 -0
- package/dist/date.d.mts +5 -0
- package/dist/date.mjs +17 -0
- package/dist/define-docs.d.mts +9 -0
- package/dist/define-docs.mjs +46 -0
- package/dist/docs-cloud-client.d.mts +18 -0
- package/dist/docs-cloud-client.mjs +161 -0
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/docs-cloud-server.mjs +2 -3
- package/dist/heading-toc.d.mts +10 -0
- package/dist/heading-toc.mjs +23 -0
- package/dist/http-body.d.mts +14 -0
- package/dist/{http-body-DkAPk6D5.mjs → http-body.mjs} +1 -1
- package/dist/http-cache.d.mts +25 -0
- package/dist/http-cache.mjs +140 -0
- package/dist/http-link.mjs +81 -0
- package/dist/i18n.d.mts +18 -0
- package/dist/{i18n-B1gJ9EW0.mjs → i18n.mjs} +1 -1
- package/dist/index.d.mts +42 -674
- package/dist/index.mjs +40 -14
- package/dist/markdown-rendering.mjs +1 -1
- package/dist/markdown-section-cache.mjs +49 -0
- package/dist/markdown-sections.d.mts +47 -0
- package/dist/{markdown-sections-9Oxr33b-.mjs → markdown-sections.mjs} +29 -703
- package/dist/mcp-auth.d.mts +14 -0
- package/dist/mcp-auth.mjs +99 -0
- package/dist/mcp.d.mts +5 -3
- package/dist/mcp.mjs +21 -9
- package/dist/mdx.d.mts +6 -0
- package/dist/mdx.mjs +7 -0
- package/dist/metadata.d.mts +60 -0
- package/dist/{metadata-BOHn0Y5K.mjs → metadata.mjs} +2 -2
- package/dist/native-api-reference.mjs +968 -0
- package/dist/okf.d.mts +42 -0
- package/dist/okf.mjs +145 -0
- package/dist/openapi-contract.mjs +154 -0
- package/dist/{openapi-mcp-node-C4jUpZG6.mjs → openapi-mcp-node.mjs} +1 -1
- package/dist/openapi-mcp.d.mts +26 -0
- package/dist/openapi-mcp.mjs +132 -0
- package/dist/openapi-operations.d.mts +71 -0
- package/dist/{openapi-operations-DFpaqOqA.mjs → openapi-operations.mjs} +1 -1
- package/dist/openapi-overlays.mjs +221 -0
- package/dist/openapi-references.mjs +233 -0
- package/dist/pagination.d.mts +7 -0
- package/dist/pagination.mjs +100 -0
- package/dist/prompt-references.d.mts +12 -0
- package/dist/prompt-references.mjs +132 -0
- package/dist/prompt-utils.d.mts +36 -0
- package/dist/{prompt-utils-B5g-bv7o.mjs → prompt-utils.mjs} +1 -57
- package/dist/reading-time.d.mts +23 -0
- package/dist/reading-time.mjs +58 -0
- package/dist/related.d.mts +7 -0
- package/dist/related.mjs +50 -0
- package/dist/retrieval-digest.d.mts +16 -0
- package/dist/{retrieval-digest-CT-eIEcp.mjs → retrieval-digest.mjs} +1 -1
- package/dist/review.d.mts +69 -0
- package/dist/review.mjs +562 -0
- package/dist/robots.d.mts +54 -0
- package/dist/{robots-BYJAVeI6.mjs → robots.mjs} +5 -3
- package/dist/runtime.d.mts +3 -3
- package/dist/runtime.mjs +3 -9
- package/dist/search.d.mts +210 -0
- package/dist/search.mjs +3401 -0
- package/dist/server.d.mts +22 -198
- package/dist/server.mjs +21 -19
- package/dist/sidebar.d.mts +15 -0
- package/dist/sidebar.mjs +73 -0
- package/dist/sitemap-server.d.mts +7 -0
- package/dist/{sitemap-server-Lg3YhUx4.mjs → sitemap-server.mjs} +2 -2
- package/dist/{sitemap-Cl8vrixd.d.mts → sitemap.d.mts} +3 -3
- package/dist/sitemap.mjs +254 -0
- package/dist/{standards-discovery-BHPc7YIX.d.mts → standards-discovery.d.mts} +2 -2
- package/dist/{standards-discovery-sSEojU80.mjs → standards-discovery.mjs} +3 -139
- package/dist/telemetry-runtime.d.mts +42 -0
- package/dist/telemetry-runtime.mjs +238 -0
- package/dist/telemetry.d.mts +15 -0
- package/dist/telemetry.mjs +117 -0
- package/dist/{types-BJFeMXWZ.d.mts → types.d.mts} +3 -2
- package/dist/urls.d.mts +7 -0
- package/dist/urls.mjs +21 -0
- package/dist/utils.d.mts +8 -0
- package/dist/utils.mjs +22 -0
- package/package.json +21 -3
- package/dist/agent-DkAidqtV.mjs +0 -9026
- package/dist/api-reference-BYee4zMo.mjs +0 -3046
- package/dist/prompt-references-DMgbu_dz.mjs +0 -1082
- package/dist/retrieval-digest-AOx2-LbC.d.mts +0 -367
- package/dist/sidebar-CFHBj29f.mjs +0 -421
- package/dist/sidebar-CMPbVfGP.d.mts +0 -63
- /package/dist/{openapi-CfCzX_a1.mjs → cli/openapi.mjs} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//#region src/agent-skills-archive.ts
|
|
2
|
+
const TAR_BLOCK_BYTES = 512;
|
|
3
|
+
const TAR_NAME_BYTES = 100;
|
|
4
|
+
const TAR_PREFIX_OFFSET = 345;
|
|
5
|
+
const TAR_PREFIX_BYTES = 155;
|
|
6
|
+
const AGENT_SKILL_ARCHIVE_MAX_UNCOMPRESSED_BYTES = 10 * 1024 * 1024;
|
|
7
|
+
const AGENT_SKILL_DOCUMENT_MAX_BYTES = 1024 * 1024;
|
|
8
|
+
function isZeroBlock(block) {
|
|
9
|
+
return block.every((byte) => byte === 0);
|
|
10
|
+
}
|
|
11
|
+
function readTarText(header, offset, length, field) {
|
|
12
|
+
const bytes = header.subarray(offset, offset + length);
|
|
13
|
+
const terminator = bytes.indexOf(0);
|
|
14
|
+
const value = terminator < 0 ? bytes : bytes.subarray(0, terminator);
|
|
15
|
+
try {
|
|
16
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
17
|
+
} catch {
|
|
18
|
+
throw new Error(`Agent Skill archive has invalid UTF-8 in its ${field}.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function readTarOctal(header, offset, length, field) {
|
|
22
|
+
const value = readTarText(header, offset, length, field).trim();
|
|
23
|
+
if (!/^[0-7]+$/u.test(value)) throw new Error(`Agent Skill archive has an invalid ${field}.`);
|
|
24
|
+
const parsed = Number.parseInt(value, 8);
|
|
25
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) throw new Error(`Agent Skill archive has an unsafe ${field}.`);
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
function validateTarChecksum(header) {
|
|
29
|
+
const expected = readTarOctal(header, 148, 8, "header checksum");
|
|
30
|
+
if (header.reduce((sum, byte, index) => {
|
|
31
|
+
return sum + (index >= 148 && index < 156 ? 32 : byte);
|
|
32
|
+
}, 0) !== expected) throw new Error("Agent Skill archive has an invalid tar header checksum.");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract the root SKILL.md from an uncompressed Agent Skill tar archive.
|
|
36
|
+
*
|
|
37
|
+
* Only the regular-file payload is returned; duplicate, oversized, malformed,
|
|
38
|
+
* or non-UTF-8 documents are rejected before frontmatter validation.
|
|
39
|
+
*/
|
|
40
|
+
function readAgentSkillDocumentFromTar(archive) {
|
|
41
|
+
if (archive.byteLength > AGENT_SKILL_ARCHIVE_MAX_UNCOMPRESSED_BYTES) throw new Error(`Agent Skill archive exceeds ${AGENT_SKILL_ARCHIVE_MAX_UNCOMPRESSED_BYTES} uncompressed bytes.`);
|
|
42
|
+
let skillDocument;
|
|
43
|
+
let offset = 0;
|
|
44
|
+
let foundEndOfArchive = false;
|
|
45
|
+
while (offset < archive.byteLength) {
|
|
46
|
+
if (offset + TAR_BLOCK_BYTES > archive.byteLength) throw new Error("Agent Skill archive contains a truncated tar header.");
|
|
47
|
+
const header = archive.subarray(offset, offset + TAR_BLOCK_BYTES);
|
|
48
|
+
if (isZeroBlock(header)) {
|
|
49
|
+
const secondZeroBlockStart = offset + TAR_BLOCK_BYTES;
|
|
50
|
+
const secondZeroBlockEnd = secondZeroBlockStart + TAR_BLOCK_BYTES;
|
|
51
|
+
if (secondZeroBlockEnd > archive.byteLength) throw new Error("Agent Skill archive contains a truncated end-of-archive marker.");
|
|
52
|
+
if (!isZeroBlock(archive.subarray(secondZeroBlockStart, secondZeroBlockEnd))) throw new Error("Agent Skill archive is missing the second end-of-archive block.");
|
|
53
|
+
if (!archive.subarray(secondZeroBlockEnd).every((byte) => byte === 0)) throw new Error("Agent Skill archive contains non-zero bytes after its end marker.");
|
|
54
|
+
foundEndOfArchive = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
validateTarChecksum(header);
|
|
58
|
+
const name = readTarText(header, 0, TAR_NAME_BYTES, "file name");
|
|
59
|
+
const prefix = readTarText(header, TAR_PREFIX_OFFSET, TAR_PREFIX_BYTES, "file prefix");
|
|
60
|
+
const path = prefix ? `${prefix}/${name}` : name;
|
|
61
|
+
const size = readTarOctal(header, 124, 12, "file size");
|
|
62
|
+
const contentStart = offset + TAR_BLOCK_BYTES;
|
|
63
|
+
const contentEnd = contentStart + size;
|
|
64
|
+
if (contentEnd > archive.byteLength) throw new Error(`Agent Skill archive entry ${JSON.stringify(path)} is truncated.`);
|
|
65
|
+
const typeFlag = header[156];
|
|
66
|
+
if (path === "SKILL.md") {
|
|
67
|
+
if (typeFlag !== 0 && typeFlag !== "0".charCodeAt(0)) throw new Error("Agent Skill archive root SKILL.md must be a regular file.");
|
|
68
|
+
if (skillDocument) throw new Error("Agent Skill archive contains more than one root SKILL.md.");
|
|
69
|
+
if (size > AGENT_SKILL_DOCUMENT_MAX_BYTES) throw new Error(`Agent Skill archive SKILL.md exceeds ${AGENT_SKILL_DOCUMENT_MAX_BYTES} bytes.`);
|
|
70
|
+
skillDocument = archive.subarray(contentStart, contentEnd);
|
|
71
|
+
}
|
|
72
|
+
const nextOffset = contentStart + Math.ceil(size / TAR_BLOCK_BYTES) * TAR_BLOCK_BYTES;
|
|
73
|
+
if (nextOffset > archive.byteLength) throw new Error(`Agent Skill archive entry ${JSON.stringify(path)} has truncated padding.`);
|
|
74
|
+
offset = nextOffset;
|
|
75
|
+
}
|
|
76
|
+
if (!foundEndOfArchive) throw new Error("Agent Skill archive is missing its end-of-archive marker.");
|
|
77
|
+
if (!skillDocument) throw new Error("Agent Skill archive does not contain a root SKILL.md.");
|
|
78
|
+
try {
|
|
79
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(skillDocument);
|
|
80
|
+
} catch {
|
|
81
|
+
throw new Error("Agent Skill archive SKILL.md is not valid UTF-8.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { AGENT_SKILL_ARCHIVE_MAX_UNCOMPRESSED_BYTES, readAgentSkillDocumentFromTar };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { validateDocsAgentSkillFrontmatter } from "./agent-skills-spec.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveConfiguredAgentSkillPathsSync } from "./agent-skills-server.mjs";
|
|
3
3
|
import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
@@ -305,4 +305,4 @@ function analyzeConfiguredAgentSkillsProgressiveDisclosure(input, options = {})
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
//#endregion
|
|
308
|
-
export { estimateAgentSkillInstructionTokens
|
|
308
|
+
export { analyzeConfiguredAgentSkillsProgressiveDisclosure, estimateAgentSkillInstructionTokens, resolveDocsAgentSkillsProgressiveDisclosureConfig };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DocsAgentSkillsInput } from "./types.mjs";
|
|
2
|
+
import { DocsPublishedAgentSkill } from "./standards-discovery.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/agent-skills-server.d.ts
|
|
5
5
|
interface ResolveConfiguredAgentSkillsOptions {
|
|
@@ -11,4 +11,4 @@ declare function resolveConfiguredAgentSkillsSync(input: DocsAgentSkillsInput |
|
|
|
11
11
|
/** Resolve and package configured skills without blocking runtime filesystem or compression work. */
|
|
12
12
|
declare function resolveConfiguredAgentSkills(input: DocsAgentSkillsInput | undefined, options?: ResolveConfiguredAgentSkillsOptions): Promise<DocsPublishedAgentSkill[]>;
|
|
13
13
|
//#endregion
|
|
14
|
-
export { resolveConfiguredAgentSkills
|
|
14
|
+
export { ResolveConfiguredAgentSkillsOptions, resolveConfiguredAgentSkills, resolveConfiguredAgentSkillsSync };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, buildDocsPublishedAgentSkill } from "./standards-discovery.mjs";
|
|
2
2
|
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
3
3
|
import { access, lstat, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
@@ -603,4 +603,4 @@ async function resolveConfiguredAgentSkills(input, options = {}) {
|
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
//#endregion
|
|
606
|
-
export {
|
|
606
|
+
export { getDocsAgentSkillsConfiguredPaths, resolveConfiguredAgentSkillPathsSync, resolveConfiguredAgentSkills, resolveConfiguredAgentSkillsSync };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { DocsAgentSkillsInput } from "./types.mjs";
|
|
2
|
+
import { DocsPublishedAgentSkill } from "./standards-discovery.mjs";
|
|
3
|
+
import { ResolveConfiguredAgentSkillsOptions } from "./agent-skills-server.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/agent-skills-vite.d.ts
|
|
6
6
|
declare const DOCS_AGENT_SKILLS_BUNDLE_MODULE = "@farming-labs/docs/agent-skills-bundle";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import { r as resolveConfiguredAgentSkills } from "./agent-skills-server-BC7md56k.mjs";
|
|
1
|
+
import { resolveConfiguredAgentSkills } from "./agent-skills-server.mjs";
|
|
3
2
|
|
|
4
3
|
//#region src/agent-skills-vite.ts
|
|
5
4
|
const DOCS_AGENT_SKILLS_BUNDLE_MODULE = "@farming-labs/docs/agent-skills-bundle";
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
//#region src/agent-surface-drift.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function normalizeDotPath(value) {
|
|
6
|
+
const segments = value.trim().replace(/\[\d+\]/gu, "[]").replace(/^\/+|\/+$/g, "").replaceAll("/", ".").split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
7
|
+
const normalized = [];
|
|
8
|
+
for (const segment of segments) {
|
|
9
|
+
if (/^\d+$/u.test(segment) && normalized.length > 0) {
|
|
10
|
+
normalized[normalized.length - 1] = `${normalized.at(-1)}[]`;
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
normalized.push(segment);
|
|
14
|
+
}
|
|
15
|
+
return normalized.join(".");
|
|
16
|
+
}
|
|
17
|
+
function collectSchemaPaths(options) {
|
|
18
|
+
const paths = /* @__PURE__ */ new Set();
|
|
19
|
+
const visited = /* @__PURE__ */ new Set();
|
|
20
|
+
const visit = (option) => {
|
|
21
|
+
if (visited.has(option)) return;
|
|
22
|
+
visited.add(option);
|
|
23
|
+
const optionPath = normalizeDotPath(option.path);
|
|
24
|
+
if (optionPath) paths.add(optionPath);
|
|
25
|
+
for (const child of option.children ?? []) visit(child);
|
|
26
|
+
};
|
|
27
|
+
for (const option of options) visit(option);
|
|
28
|
+
return paths;
|
|
29
|
+
}
|
|
30
|
+
function schemaCoversConfigPath(schemaPaths, configPath) {
|
|
31
|
+
if (schemaPaths.has(configPath)) return true;
|
|
32
|
+
if (configPath.endsWith("[]") && schemaPaths.has(configPath.slice(0, -2))) return true;
|
|
33
|
+
for (const schemaPath of schemaPaths) {
|
|
34
|
+
if (!schemaPath.endsWith(".*")) continue;
|
|
35
|
+
const prefix = schemaPath.slice(0, -1);
|
|
36
|
+
if (configPath.startsWith(prefix) && configPath.length > prefix.length) return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
function readPath(root, dotPath) {
|
|
41
|
+
const normalized = normalizeDotPath(dotPath);
|
|
42
|
+
if (!normalized) return {
|
|
43
|
+
present: true,
|
|
44
|
+
value: root
|
|
45
|
+
};
|
|
46
|
+
let current = root;
|
|
47
|
+
for (const segment of normalized.split(".")) {
|
|
48
|
+
if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, segment)) return {
|
|
49
|
+
present: false,
|
|
50
|
+
value: void 0
|
|
51
|
+
};
|
|
52
|
+
current = current[segment];
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
present: true,
|
|
56
|
+
value: current
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function stableJsonValue(value) {
|
|
60
|
+
if (Array.isArray(value)) return value.map(stableJsonValue);
|
|
61
|
+
if (!isRecord(value)) return value;
|
|
62
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableJsonValue(value[key])]));
|
|
63
|
+
}
|
|
64
|
+
function displayValue(value, present = true) {
|
|
65
|
+
if (!present || value === void 0) return "<missing>";
|
|
66
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
67
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
|
|
68
|
+
try {
|
|
69
|
+
return JSON.stringify(stableJsonValue(value));
|
|
70
|
+
} catch {
|
|
71
|
+
return "<unserializable>";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function valuesMatch(actual, expected) {
|
|
75
|
+
if (!actual.present) return false;
|
|
76
|
+
if (Object.is(actual.value, expected)) return true;
|
|
77
|
+
if ((Array.isArray(actual.value) || isRecord(actual.value)) && (Array.isArray(expected) || isRecord(expected))) return displayValue(actual.value) === displayValue(expected);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
function mismatchIssue(code, path, expected, actual, label) {
|
|
81
|
+
const expectedDisplay = displayValue(expected);
|
|
82
|
+
const actualDisplay = displayValue(actual.value, actual.present);
|
|
83
|
+
return {
|
|
84
|
+
code,
|
|
85
|
+
path,
|
|
86
|
+
expected: expectedDisplay,
|
|
87
|
+
actual: actualDisplay,
|
|
88
|
+
message: `${label} is ${actualDisplay}; expected ${expectedDisplay}.`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function compareExpectedValue(issues, discovery, code, path, expected, label) {
|
|
92
|
+
const actual = readPath(discovery, path);
|
|
93
|
+
if (!valuesMatch(actual, expected)) issues.push(mismatchIssue(code, path, expected, actual, label));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Compare independently resolved docs values with the public agent discovery and
|
|
97
|
+
* config-schema surfaces. The helper is intentionally pure so doctor, review,
|
|
98
|
+
* and golden evaluations can use the same deterministic drift rules.
|
|
99
|
+
*/
|
|
100
|
+
function analyzeAgentSurfaceDrift(options) {
|
|
101
|
+
const issues = [];
|
|
102
|
+
const schemaPaths = collectSchemaPaths(options.schemaOptions);
|
|
103
|
+
const configPaths = Array.from(new Set(options.configOptionPaths.map(normalizeDotPath).filter(Boolean))).sort();
|
|
104
|
+
for (const configPath of configPaths) {
|
|
105
|
+
if (schemaCoversConfigPath(schemaPaths, configPath)) continue;
|
|
106
|
+
issues.push({
|
|
107
|
+
code: "config-schema-omission",
|
|
108
|
+
path: configPath,
|
|
109
|
+
expected: "documented schema option",
|
|
110
|
+
actual: "<missing>",
|
|
111
|
+
message: `Config option ${JSON.stringify(configPath)} is present but missing from the published config schema.`
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const canonicalFields = Array.from(new Set(options.agentContractFields.map((field) => field.trim()).filter(Boolean))).sort();
|
|
115
|
+
const discoveredFieldsValue = readPath(options.discovery, "agentContract.fields");
|
|
116
|
+
const discoveredFields = isRecord(discoveredFieldsValue.value) ? Object.keys(discoveredFieldsValue.value).sort() : [];
|
|
117
|
+
const canonicalFieldSet = new Set(canonicalFields);
|
|
118
|
+
const discoveredFieldSet = new Set(discoveredFields);
|
|
119
|
+
for (const field of canonicalFields) {
|
|
120
|
+
if (discoveredFieldSet.has(field)) continue;
|
|
121
|
+
const issuePath = `agentContract.fields.${field}`;
|
|
122
|
+
issues.push({
|
|
123
|
+
code: "agent-contract-field-missing",
|
|
124
|
+
path: issuePath,
|
|
125
|
+
expected: "canonical field",
|
|
126
|
+
actual: "<missing>",
|
|
127
|
+
message: `Discovery agent contract is missing canonical field ${JSON.stringify(field)}.`
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
for (const field of discoveredFields) {
|
|
131
|
+
if (canonicalFieldSet.has(field)) continue;
|
|
132
|
+
const issuePath = `agentContract.fields.${field}`;
|
|
133
|
+
issues.push({
|
|
134
|
+
code: "agent-contract-field-unexpected",
|
|
135
|
+
path: issuePath,
|
|
136
|
+
expected: "<absent>",
|
|
137
|
+
actual: "declared field",
|
|
138
|
+
message: `Discovery agent contract advertises non-canonical field ${JSON.stringify(field)}.`
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
compareExpectedValue(issues, options.discovery, "entry-mismatch", "site.entry", options.expected.entry, "Discovery site.entry");
|
|
142
|
+
compareExpectedValue(issues, options.discovery, "search-enabled-mismatch", "search.enabled", options.expected.search.enabled, "Discovery search.enabled");
|
|
143
|
+
compareExpectedValue(issues, options.discovery, "search-capability-mismatch", "capabilities.search", options.expected.search.enabled, "Discovery capabilities.search");
|
|
144
|
+
compareExpectedValue(issues, options.discovery, "search-route-mismatch", "search.endpoint", options.expected.search.endpoint, "Discovery search.endpoint");
|
|
145
|
+
const expectedAgentSearchEndpoint = options.expected.search.endpoint === null ? null : `${options.expected.search.endpoint}${options.expected.search.endpoint.includes("?") ? "&" : "?"}audience=agent`;
|
|
146
|
+
compareExpectedValue(issues, options.discovery, "search-audience-mismatch", "search.agentEndpoint", expectedAgentSearchEndpoint, "Discovery search.agentEndpoint");
|
|
147
|
+
compareExpectedValue(issues, options.discovery, "search-audience-mismatch", "search.audienceParam", "audience", "Discovery search.audienceParam");
|
|
148
|
+
compareExpectedValue(issues, options.discovery, "search-audience-mismatch", "search.defaultAudience", "human", "Discovery search.defaultAudience");
|
|
149
|
+
compareExpectedValue(issues, options.discovery, "search-audience-mismatch", "search.supportedAudiences", ["human", "agent"], "Discovery search.supportedAudiences");
|
|
150
|
+
const expectedStructuredAgentSearchEndpoint = expectedAgentSearchEndpoint === null ? null : `${expectedAgentSearchEndpoint}&response=structured`;
|
|
151
|
+
const expectedSearchFacetsEndpoint = expectedAgentSearchEndpoint === null ? null : expectedAgentSearchEndpoint.replace("query={query}&audience=agent", "audience=agent&response=facets");
|
|
152
|
+
const expectedExplainedAgentSearchEndpoint = expectedStructuredAgentSearchEndpoint === null ? null : `${expectedStructuredAgentSearchEndpoint}&explain=true`;
|
|
153
|
+
for (const [path, expected] of [
|
|
154
|
+
["search.structuredAgentEndpoint", expectedStructuredAgentSearchEndpoint],
|
|
155
|
+
["search.explainedAgentEndpoint", expectedExplainedAgentSearchEndpoint],
|
|
156
|
+
["search.facetsEndpoint", expectedSearchFacetsEndpoint],
|
|
157
|
+
["search.responseParam", "response"],
|
|
158
|
+
["search.structuredResponseValue", "structured"],
|
|
159
|
+
["search.facetsResponseValue", "facets"],
|
|
160
|
+
["search.responseFormat", "docs-search.v1"],
|
|
161
|
+
["search.explainParam", "explain"],
|
|
162
|
+
["search.explainValue", "true"],
|
|
163
|
+
["search.explanationField", "explanation"],
|
|
164
|
+
["search.explanationFormat", "docs-search-explanation.v1"],
|
|
165
|
+
["search.facetsResponseFormat", "docs-search-facets.v1"],
|
|
166
|
+
["search.filterParams", {
|
|
167
|
+
framework: "framework",
|
|
168
|
+
version: "version",
|
|
169
|
+
package: "package",
|
|
170
|
+
tags: "tags"
|
|
171
|
+
}],
|
|
172
|
+
["search.repeatedFilterParams", [
|
|
173
|
+
"framework",
|
|
174
|
+
"version",
|
|
175
|
+
"package",
|
|
176
|
+
"tags"
|
|
177
|
+
]],
|
|
178
|
+
["search.warningsField", "warnings"],
|
|
179
|
+
["search.facetParam", "facet"],
|
|
180
|
+
["search.limitParam", "limit"],
|
|
181
|
+
["search.cursorParam", "cursor"],
|
|
182
|
+
["search.nextCursorField", "nextCursor"],
|
|
183
|
+
["search.hasMoreField", "hasMore"],
|
|
184
|
+
["search.totalField", "total"]
|
|
185
|
+
]) compareExpectedValue(issues, options.discovery, "search-scope-mismatch", path, expected, `Discovery ${path}`);
|
|
186
|
+
compareExpectedValue(issues, options.discovery, "mcp-enabled-mismatch", "mcp.enabled", options.expected.mcp.enabled, "Discovery mcp.enabled");
|
|
187
|
+
compareExpectedValue(issues, options.discovery, "mcp-capability-mismatch", "capabilities.mcp", options.expected.mcp.enabled, "Discovery capabilities.mcp");
|
|
188
|
+
compareExpectedValue(issues, options.discovery, "mcp-route-mismatch", "mcp.endpoint", options.expected.mcp.endpoint, "Discovery mcp.endpoint");
|
|
189
|
+
if (options.expected.mcp.protectedResource !== void 0) {
|
|
190
|
+
const expectedProtectedResource = options.expected.mcp.protectedResource;
|
|
191
|
+
const discoveredProtectedResource = readPath(options.discovery, "mcp.protectedResource");
|
|
192
|
+
if (expectedProtectedResource === null) {
|
|
193
|
+
if (discoveredProtectedResource.present) issues.push(mismatchIssue("mcp-protected-resource-mismatch", "mcp.protectedResource", void 0, discoveredProtectedResource, "Discovery mcp.protectedResource"));
|
|
194
|
+
} else for (const field of [
|
|
195
|
+
"metadataEndpoints",
|
|
196
|
+
"authorizationServers",
|
|
197
|
+
"scopesSupported",
|
|
198
|
+
"requiredScopes"
|
|
199
|
+
]) compareExpectedValue(issues, options.discovery, "mcp-protected-resource-mismatch", `mcp.protectedResource.${field}`, expectedProtectedResource[field], `Discovery mcp.protectedResource.${field}`);
|
|
200
|
+
}
|
|
201
|
+
const expectedToolNames = Object.keys(options.expected.mcp.tools).sort();
|
|
202
|
+
const expectedToolSet = new Set(expectedToolNames);
|
|
203
|
+
const discoveredToolsValue = readPath(options.discovery, "mcp.tools");
|
|
204
|
+
const discoveredTools = isRecord(discoveredToolsValue.value) ? discoveredToolsValue.value : {};
|
|
205
|
+
for (const toolName of expectedToolNames) {
|
|
206
|
+
const expected = options.expected.mcp.tools[toolName];
|
|
207
|
+
const path = `mcp.tools.${toolName}`;
|
|
208
|
+
const actual = Object.prototype.hasOwnProperty.call(discoveredTools, toolName) ? {
|
|
209
|
+
present: true,
|
|
210
|
+
value: discoveredTools[toolName]
|
|
211
|
+
} : {
|
|
212
|
+
present: false,
|
|
213
|
+
value: void 0
|
|
214
|
+
};
|
|
215
|
+
if (!valuesMatch(actual, expected)) issues.push(mismatchIssue("mcp-tool-mismatch", path, expected, actual, `Discovery ${path}`));
|
|
216
|
+
}
|
|
217
|
+
for (const toolName of Object.keys(discoveredTools).sort()) {
|
|
218
|
+
if (expectedToolSet.has(toolName)) continue;
|
|
219
|
+
const path = `mcp.tools.${toolName}`;
|
|
220
|
+
issues.push({
|
|
221
|
+
code: "mcp-tool-unexpected",
|
|
222
|
+
path,
|
|
223
|
+
expected: "<absent>",
|
|
224
|
+
actual: displayValue(discoveredTools[toolName]),
|
|
225
|
+
message: `Discovery MCP tools advertise unexpected tool flag ${JSON.stringify(toolName)}.`
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
for (const routePath of Object.keys(options.expected.routes).sort()) {
|
|
229
|
+
const expected = options.expected.routes[routePath];
|
|
230
|
+
compareExpectedValue(issues, options.discovery, "route-mismatch", routePath, expected, `Discovery ${routePath}`);
|
|
231
|
+
}
|
|
232
|
+
return issues.sort((left, right) => left.path.localeCompare(right.path) || left.code.localeCompare(right.code) || left.message.localeCompare(right.message));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
//#endregion
|
|
236
|
+
export { analyzeAgentSurfaceDrift };
|