@farming-labs/docs 0.2.70 → 0.2.72
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/dist/{agent-MOuft73V.mjs → agent-DbDfIqhe.mjs} +3 -3
- package/dist/{agent-evals-BTh28hl-.mjs → agent-evals-CjYzCQMZ.mjs} +90 -10
- package/dist/{agent-export-43XUqLf5.mjs → agent-export-CpQhD8eA.mjs} +13 -10
- package/dist/{agent-CXkn2ihX.mjs → agent-pS2nNQS3.mjs} +540 -14
- package/dist/agent-skills-bundle.d.mts +1 -1
- package/dist/{agent-skills-server-DTwblsLu.d.mts → agent-skills-server-DZnNB4_S.d.mts} +2 -2
- package/dist/agent-skills-vite.d.mts +3 -3
- package/dist/agent-skills-vite.mjs +1 -1
- package/dist/{agents-C16sSxHU.mjs → agents-B-1h9Jlm.mjs} +8 -8
- package/dist/cli/index.mjs +29 -29
- package/dist/client/react.d.mts +1 -1
- package/dist/{cloud-pdNC-tyj.mjs → cloud-CyysfDny.mjs} +3 -3
- package/dist/{cloud-ask-ai-BfnX18rI.d.mts → cloud-ask-ai-D9gIN4Vr.d.mts} +1 -1
- package/dist/{codeblocks-CFuurVIH.mjs → codeblocks-DAj7EAwt.mjs} +2 -2
- package/dist/{dev-Cc1cFFj-.mjs → dev-qE7MYEh9.mjs} +2 -2
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/docs-cloud-server.mjs +1 -1
- package/dist/{doctor-WjSas47Z.mjs → doctor-CBy8-tPu.mjs} +13 -13
- package/dist/{downgrade-w7e6Se0L.mjs → downgrade-DCxezPZI.mjs} +2 -2
- package/dist/{golden-evaluations-Cg0braJV.mjs → golden-evaluations-DGg_eHIY.mjs} +23 -3
- package/dist/index.d.mts +104 -6
- package/dist/index.mjs +6 -6
- package/dist/{init-DAkI0Lkf.mjs → init-p1MHf06G.mjs} +3 -3
- package/dist/{mcp-BDPfedoh.mjs → mcp-BNK45dzD.mjs} +7 -7
- package/dist/mcp.d.mts +7 -2
- package/dist/mcp.mjs +122 -18
- package/dist/{metadata-BMBvGASC.mjs → metadata-D6qlWDu-.mjs} +1 -1
- package/dist/{package-version-n5AFur8a.mjs → package-version-BXHcIm1T.mjs} +1 -1
- package/dist/{reading-time-D0s9Osqw.mjs → reading-time-DvTuW2Iy.mjs} +2 -2
- package/dist/{review-_0G2gAtx.mjs → review-Cace7A2c.mjs} +8 -8
- package/dist/{robots-DOnLGE0l.mjs → robots-C2SindI5.mjs} +1 -1
- package/dist/{robots-DLl9Zizg.mjs → robots-DmE_wXvG.mjs} +4 -4
- package/dist/{search-CozHzW7v.d.mts → search-1aftosa5.d.mts} +24 -9
- package/dist/{search-Boqkxa3T.mjs → search-BbArHODP.mjs} +672 -147
- package/dist/{search-B9tkri5i.mjs → search-DB3svkNQ.mjs} +7 -7
- package/dist/server.d.mts +13 -5
- package/dist/server.mjs +8 -8
- package/dist/{sitemap-FgXlxHEO.mjs → sitemap-DPTLh_26.mjs} +8 -8
- package/dist/{sitemap-server-Bynukz-j.mjs → sitemap-server-PUoOhplO.mjs} +1 -1
- package/dist/{standards-discovery-C0CIC4QI.d.mts → standards-discovery-DRJ10g7B.d.mts} +1 -1
- package/dist/{types-BQC05SQm.d.mts → types-B9DNNAdT.d.mts} +49 -1
- package/dist/{upgrade-oz-GChgt.mjs → upgrade-DDJQ5l6n.mjs} +2 -2
- package/package.json +1 -1
- package/dist/agent-scope-C_U--OZ7.mjs +0 -283
- /package/dist/{agent-skills-server-B4u3qy-1.mjs → agent-skills-server-BPVv4gMg.mjs} +0 -0
- /package/dist/{cloud-ask-ai-sbpjOR2K.mjs → cloud-ask-ai-hnJfj8-X.mjs} +0 -0
- /package/dist/{code-blocks-DnNVNK2M.mjs → code-blocks-qe0T8-xe.mjs} +0 -0
- /package/dist/{config-Wcdj-D0a.mjs → config-z7_R0f86.mjs} +0 -0
- /package/dist/{errors-DbOhkE1h.mjs → errors-Cc_qplZU.mjs} +0 -0
- /package/dist/{templates-C6avM0U3.mjs → templates-BJnf6B8l.mjs} +0 -0
- /package/dist/{utils-DpiIioYb.mjs → utils-C4RC8Zq_.mjs} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as resolveDocsAudienceMdxContent, G as isDocsAgentDiscoveryRequest, Gt as findDocsMarkdownSection, K as isDocsAgentsRequest, Kt as parseDocsMarkdownSections, Q as isDocsSkillRequest, Qt as PAGE_AGENT_CONTRACT_START_MARKER, Yt as PAGE_AGENT_CONTRACT_END_MARKER, _t as resolveDocsMarkdownRequest, dt as resolveDocsAgentsFormat, lt as resolveDocsAgentFeedbackConfig, on as upsertPageAgentContractMarkdown, pt as resolveDocsLlmsTxtRequest, rn as renderPageAgentContractMarkdown, ut as resolveDocsAgentFeedbackRequest, xt as resolveDocsSkillFormat } from "./agent-pS2nNQS3.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/telemetry.ts
|
|
4
4
|
const DOCS_PACKAGE_NAME = "@farming-labs/docs";
|
|
@@ -458,143 +458,323 @@ function serializeGeneratedAgentDocument(content, provenance) {
|
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
//#endregion
|
|
461
|
-
//#region src/
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
461
|
+
//#region src/agent-scope.ts
|
|
462
|
+
/** Normalize framework aliases used by page metadata, agent contracts, MCP, and evaluations. */
|
|
463
|
+
function normalizeAgentFramework(value) {
|
|
464
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]/gu, "");
|
|
465
|
+
if ([
|
|
466
|
+
"next",
|
|
467
|
+
"nextjs",
|
|
468
|
+
"nextjsapp",
|
|
469
|
+
"reactnext"
|
|
470
|
+
].includes(normalized)) return "nextjs";
|
|
471
|
+
if ([
|
|
472
|
+
"tanstack",
|
|
473
|
+
"tanstackstart",
|
|
474
|
+
"start"
|
|
475
|
+
].includes(normalized)) return "tanstackstart";
|
|
476
|
+
if (["svelte", "sveltekit"].includes(normalized)) return "sveltekit";
|
|
477
|
+
if (["nuxt", "nuxtjs"].includes(normalized)) return "nuxt";
|
|
478
|
+
return normalized;
|
|
479
|
+
}
|
|
480
|
+
function normalizeAgentLocale(value) {
|
|
481
|
+
return value.trim().toLowerCase().replace(/_/gu, "-");
|
|
482
|
+
}
|
|
483
|
+
function normalizeAgentVersion(value) {
|
|
484
|
+
return value.trim().toLowerCase().replace(/^v(?=\d)/u, "");
|
|
485
|
+
}
|
|
486
|
+
function normalizeAgentScopeValues(value) {
|
|
487
|
+
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
488
|
+
return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean)));
|
|
489
|
+
}
|
|
490
|
+
function compareVersions(left, right) {
|
|
491
|
+
for (let index = 0; index < 3; index += 1) if (left[index] !== right[index]) return left[index] > right[index] ? 1 : -1;
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
function parseExactVersion(value) {
|
|
495
|
+
const match = normalizeAgentVersion(value).match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-[0-9a-z.-]+)?(?:\+[0-9a-z.-]+)?$/iu);
|
|
496
|
+
if (!match) return void 0;
|
|
497
|
+
return [
|
|
498
|
+
Number(match[1]),
|
|
499
|
+
Number(match[2] ?? 0),
|
|
500
|
+
Number(match[3] ?? 0)
|
|
501
|
+
];
|
|
491
502
|
}
|
|
492
|
-
function
|
|
493
|
-
const
|
|
503
|
+
function parseVersionOperand(value) {
|
|
504
|
+
const normalized = normalizeAgentVersion(value);
|
|
505
|
+
if (normalized === "*" || normalized === "x") return {
|
|
506
|
+
version: [
|
|
507
|
+
0,
|
|
508
|
+
0,
|
|
509
|
+
0
|
|
510
|
+
],
|
|
511
|
+
components: 0,
|
|
512
|
+
wildcard: true
|
|
513
|
+
};
|
|
514
|
+
const match = normalized.match(/^(\d+)(?:\.(\d+|x|\*))?(?:\.(\d+|x|\*))?$/iu);
|
|
494
515
|
if (!match) return void 0;
|
|
516
|
+
const raw = [
|
|
517
|
+
match[1],
|
|
518
|
+
match[2],
|
|
519
|
+
match[3]
|
|
520
|
+
];
|
|
521
|
+
const wildcardIndex = raw.findIndex((part, index) => index > 0 && (part?.toLowerCase() === "x" || part === "*"));
|
|
522
|
+
if (wildcardIndex >= 0 && raw.slice(wildcardIndex + 1).some((part) => part && part.toLowerCase() !== "x" && part !== "*")) return;
|
|
523
|
+
const components = wildcardIndex >= 0 ? wildcardIndex : raw.filter(Boolean).length;
|
|
495
524
|
return {
|
|
496
|
-
|
|
497
|
-
|
|
525
|
+
version: [
|
|
526
|
+
Number(raw[0]),
|
|
527
|
+
Number(raw[1] ?? 0) || 0,
|
|
528
|
+
Number(raw[2] ?? 0) || 0
|
|
529
|
+
],
|
|
530
|
+
components,
|
|
531
|
+
wildcard: wildcardIndex >= 0
|
|
498
532
|
};
|
|
499
533
|
}
|
|
500
|
-
function
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
534
|
+
function nextVersionBoundary(operand) {
|
|
535
|
+
const [major, minor] = operand.version;
|
|
536
|
+
if (operand.components <= 0) return void 0;
|
|
537
|
+
if (operand.components === 1) return [
|
|
538
|
+
major + 1,
|
|
539
|
+
0,
|
|
540
|
+
0
|
|
541
|
+
];
|
|
542
|
+
return [
|
|
543
|
+
major,
|
|
544
|
+
minor + 1,
|
|
545
|
+
0
|
|
546
|
+
];
|
|
504
547
|
}
|
|
505
|
-
function
|
|
506
|
-
|
|
507
|
-
|
|
548
|
+
function operandRange(operand) {
|
|
549
|
+
if (operand.components === 0) return {
|
|
550
|
+
minimumInclusive: true,
|
|
551
|
+
maximumInclusive: false
|
|
552
|
+
};
|
|
553
|
+
if (operand.components < 3 || operand.wildcard) return {
|
|
554
|
+
minimum: operand.version,
|
|
555
|
+
minimumInclusive: true,
|
|
556
|
+
maximum: nextVersionBoundary(operand),
|
|
557
|
+
maximumInclusive: false
|
|
558
|
+
};
|
|
508
559
|
return {
|
|
509
|
-
|
|
510
|
-
|
|
560
|
+
minimum: operand.version,
|
|
561
|
+
minimumInclusive: true,
|
|
562
|
+
maximum: operand.version,
|
|
563
|
+
maximumInclusive: true
|
|
511
564
|
};
|
|
512
565
|
}
|
|
513
|
-
function
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
566
|
+
function caretRange(operand) {
|
|
567
|
+
const [major, minor, patch] = operand.version;
|
|
568
|
+
let maximum;
|
|
569
|
+
if (major > 0) maximum = [
|
|
570
|
+
major + 1,
|
|
571
|
+
0,
|
|
572
|
+
0
|
|
573
|
+
];
|
|
574
|
+
else if (operand.components <= 1) maximum = [
|
|
575
|
+
1,
|
|
576
|
+
0,
|
|
577
|
+
0
|
|
578
|
+
];
|
|
579
|
+
else if (minor > 0 || operand.components === 2) maximum = [
|
|
580
|
+
0,
|
|
581
|
+
minor + 1,
|
|
582
|
+
0
|
|
583
|
+
];
|
|
584
|
+
else maximum = [
|
|
585
|
+
0,
|
|
586
|
+
0,
|
|
587
|
+
patch + 1
|
|
588
|
+
];
|
|
589
|
+
return {
|
|
590
|
+
minimum: operand.version,
|
|
591
|
+
minimumInclusive: true,
|
|
592
|
+
maximum,
|
|
593
|
+
maximumInclusive: false
|
|
594
|
+
};
|
|
517
595
|
}
|
|
518
|
-
function
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
596
|
+
function tildeRange(operand) {
|
|
597
|
+
const [major, minor] = operand.version;
|
|
598
|
+
const maximum = operand.components <= 1 ? [
|
|
599
|
+
major + 1,
|
|
600
|
+
0,
|
|
601
|
+
0
|
|
602
|
+
] : [
|
|
603
|
+
major,
|
|
604
|
+
minor + 1,
|
|
605
|
+
0
|
|
606
|
+
];
|
|
607
|
+
return {
|
|
608
|
+
minimum: operand.version,
|
|
609
|
+
minimumInclusive: true,
|
|
610
|
+
maximum,
|
|
611
|
+
maximumInclusive: false
|
|
612
|
+
};
|
|
526
613
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
614
|
+
function intersectRanges(left, right) {
|
|
615
|
+
let minimum = left.minimum;
|
|
616
|
+
let minimumInclusive = left.minimumInclusive;
|
|
617
|
+
if (!minimum || right.minimum && compareVersions(right.minimum, minimum) > 0) {
|
|
618
|
+
minimum = right.minimum;
|
|
619
|
+
minimumInclusive = right.minimumInclusive;
|
|
620
|
+
} else if (right.minimum && compareVersions(right.minimum, minimum) === 0) minimumInclusive = minimumInclusive && right.minimumInclusive;
|
|
621
|
+
let maximum = left.maximum;
|
|
622
|
+
let maximumInclusive = left.maximumInclusive;
|
|
623
|
+
if (!maximum || right.maximum && compareVersions(right.maximum, maximum) < 0) {
|
|
624
|
+
maximum = right.maximum;
|
|
625
|
+
maximumInclusive = right.maximumInclusive;
|
|
626
|
+
} else if (right.maximum && compareVersions(right.maximum, maximum) === 0) maximumInclusive = maximumInclusive && right.maximumInclusive;
|
|
627
|
+
if (minimum && maximum) {
|
|
628
|
+
const compared = compareVersions(minimum, maximum);
|
|
629
|
+
if (compared > 0 || compared === 0 && !(minimumInclusive && maximumInclusive)) return void 0;
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
minimum,
|
|
633
|
+
minimumInclusive,
|
|
634
|
+
maximum,
|
|
635
|
+
maximumInclusive
|
|
548
636
|
};
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
if (setextLevel && setextCandidate?.index === index - 1) {
|
|
564
|
-
const title = cleanDocsMarkdownHeadingLabel(setextCandidate.value.trim());
|
|
565
|
-
if (title) pushHeading(title, setextLevel, setextCandidate.index);
|
|
566
|
-
setextCandidate = void 0;
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
const atxHeading = readAtxHeading(line);
|
|
570
|
-
if (atxHeading) {
|
|
571
|
-
pushHeading(atxHeading.title, atxHeading.level, index);
|
|
572
|
-
setextCandidate = void 0;
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
if (!line.trim() || /^ {4}|^\t/u.test(line)) {
|
|
576
|
-
setextCandidate = void 0;
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
setextCandidate = {
|
|
580
|
-
index,
|
|
581
|
-
value: line
|
|
637
|
+
}
|
|
638
|
+
function comparatorRange(operator, operand) {
|
|
639
|
+
if (!operator || operator === "=") return operandRange(operand);
|
|
640
|
+
if (operator === ">=") return {
|
|
641
|
+
minimum: operand.version,
|
|
642
|
+
minimumInclusive: true,
|
|
643
|
+
maximumInclusive: false
|
|
644
|
+
};
|
|
645
|
+
if (operator === ">") {
|
|
646
|
+
const partialBoundary = operand.components < 3 ? nextVersionBoundary(operand) : void 0;
|
|
647
|
+
return {
|
|
648
|
+
minimum: partialBoundary ?? operand.version,
|
|
649
|
+
minimumInclusive: Boolean(partialBoundary),
|
|
650
|
+
maximumInclusive: false
|
|
582
651
|
};
|
|
583
652
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
653
|
+
if (operator === "<") return {
|
|
654
|
+
minimumInclusive: true,
|
|
655
|
+
maximum: operand.version,
|
|
656
|
+
maximumInclusive: false
|
|
657
|
+
};
|
|
658
|
+
const partialBoundary = operand.components < 3 ? nextVersionBoundary(operand) : void 0;
|
|
659
|
+
return {
|
|
660
|
+
minimumInclusive: true,
|
|
661
|
+
maximum: partialBoundary ?? operand.version,
|
|
662
|
+
maximumInclusive: !partialBoundary
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function parseVersionRangeBranch(value) {
|
|
666
|
+
const branch = normalizeAgentVersion(value).trim();
|
|
667
|
+
if (!branch) return void 0;
|
|
668
|
+
const hyphen = branch.match(/^(.+?)\s+-\s+(.+)$/u);
|
|
669
|
+
if (hyphen) {
|
|
670
|
+
const minimumOperand = parseVersionOperand(hyphen[1]);
|
|
671
|
+
const maximumOperand = parseVersionOperand(hyphen[2]);
|
|
672
|
+
if (!minimumOperand || !maximumOperand) return void 0;
|
|
673
|
+
const maximumBoundary = maximumOperand.components < 3 ? nextVersionBoundary(maximumOperand) : maximumOperand.version;
|
|
674
|
+
return intersectRanges({
|
|
675
|
+
minimum: minimumOperand.version,
|
|
676
|
+
minimumInclusive: true,
|
|
677
|
+
maximumInclusive: false
|
|
678
|
+
}, {
|
|
679
|
+
minimumInclusive: true,
|
|
680
|
+
maximum: maximumBoundary,
|
|
681
|
+
maximumInclusive: maximumOperand.components === 3
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
const special = branch.match(/^(\^|~)\s*(.+)$/u);
|
|
685
|
+
if (special) {
|
|
686
|
+
const operand = parseVersionOperand(special[2]);
|
|
687
|
+
if (!operand || operand.components === 0) return void 0;
|
|
688
|
+
return special[1] === "^" ? caretRange(operand) : tildeRange(operand);
|
|
689
|
+
}
|
|
690
|
+
const comparators = Array.from(branch.matchAll(/(>=|<=|>|<|=)\s*([^\s]+)/gu));
|
|
691
|
+
if (comparators.length > 0) {
|
|
692
|
+
if (comparators.map((match) => match[0]).join(" ").replace(/\s+/gu, " ") !== branch.replace(/\s+/gu, " ")) return void 0;
|
|
693
|
+
let range = {
|
|
694
|
+
minimumInclusive: true,
|
|
695
|
+
maximumInclusive: false
|
|
591
696
|
};
|
|
592
|
-
|
|
697
|
+
for (const comparator of comparators) {
|
|
698
|
+
const operand = parseVersionOperand(comparator[2]);
|
|
699
|
+
if (!operand) return void 0;
|
|
700
|
+
const next = intersectRanges(range, comparatorRange(comparator[1], operand));
|
|
701
|
+
if (!next) return void 0;
|
|
702
|
+
range = next;
|
|
703
|
+
}
|
|
704
|
+
return range;
|
|
705
|
+
}
|
|
706
|
+
const operand = parseVersionOperand(branch);
|
|
707
|
+
return operand ? operandRange(operand) : void 0;
|
|
708
|
+
}
|
|
709
|
+
function parseVersionRanges(value) {
|
|
710
|
+
return normalizeAgentVersion(value).split("||").map((branch) => parseVersionRangeBranch(branch)).filter((range) => Boolean(range));
|
|
711
|
+
}
|
|
712
|
+
function parseVersionConstraintBranches(value) {
|
|
713
|
+
const ranges = parseVersionRanges(value);
|
|
714
|
+
if (ranges.length > 0) return ranges.map((range) => ({
|
|
715
|
+
kind: "range",
|
|
716
|
+
range
|
|
717
|
+
}));
|
|
718
|
+
const normalized = normalizeAgentVersion(value);
|
|
719
|
+
return normalized ? [{
|
|
720
|
+
kind: "opaque",
|
|
721
|
+
value: normalized
|
|
722
|
+
}] : [];
|
|
723
|
+
}
|
|
724
|
+
function intersectVersionConstraintBranches(left, right) {
|
|
725
|
+
if (left.kind === "opaque" || right.kind === "opaque") return left.kind === "opaque" && right.kind === "opaque" && left.value === right.value ? left : void 0;
|
|
726
|
+
const range = intersectRanges(left.range, right.range);
|
|
727
|
+
return range ? {
|
|
728
|
+
kind: "range",
|
|
729
|
+
range
|
|
730
|
+
} : void 0;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Return true when choosing one alternative from every group can select one shared version.
|
|
734
|
+
*
|
|
735
|
+
* Each inner group is an OR list while the groups themselves are intersected. Computing the
|
|
736
|
+
* accumulated range prevents pairwise matches from accepting three mutually incompatible scopes.
|
|
737
|
+
*/
|
|
738
|
+
function agentVersionConstraintGroupsOverlap(groups) {
|
|
739
|
+
if (groups.length === 0) return false;
|
|
740
|
+
let intersections = groups[0].flatMap(parseVersionConstraintBranches);
|
|
741
|
+
if (intersections.length === 0) return false;
|
|
742
|
+
for (const group of groups.slice(1)) {
|
|
743
|
+
const branches = group.flatMap(parseVersionConstraintBranches);
|
|
744
|
+
if (branches.length === 0) return false;
|
|
745
|
+
intersections = intersections.flatMap((intersection) => branches.map((branch) => intersectVersionConstraintBranches(intersection, branch)).filter((value) => Boolean(value)));
|
|
746
|
+
if (intersections.length === 0) return false;
|
|
747
|
+
}
|
|
748
|
+
return true;
|
|
593
749
|
}
|
|
594
|
-
function
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
750
|
+
function rangeContains(range, version) {
|
|
751
|
+
if (range.minimum) {
|
|
752
|
+
const compared = compareVersions(version, range.minimum);
|
|
753
|
+
if (compared < 0 || compared === 0 && !range.minimumInclusive) return false;
|
|
754
|
+
}
|
|
755
|
+
if (range.maximum) {
|
|
756
|
+
const compared = compareVersions(version, range.maximum);
|
|
757
|
+
if (compared > 0 || compared === 0 && !range.maximumInclusive) return false;
|
|
758
|
+
}
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
function rangesOverlap(left, right) {
|
|
762
|
+
return Boolean(intersectRanges(left, right));
|
|
763
|
+
}
|
|
764
|
+
/** Match an exact requested version against an exact or range-like documented constraint. */
|
|
765
|
+
function agentVersionConstraintMatches(requested, constraint) {
|
|
766
|
+
if (normalizeAgentVersion(requested) === normalizeAgentVersion(constraint)) return Boolean(normalizeAgentVersion(requested));
|
|
767
|
+
const wanted = parseExactVersion(requested);
|
|
768
|
+
if (!wanted) return false;
|
|
769
|
+
return parseVersionRanges(constraint).some((range) => rangeContains(range, wanted));
|
|
770
|
+
}
|
|
771
|
+
/** Return true when two exact or range-like documented version constraints can select one version. */
|
|
772
|
+
function agentVersionConstraintsOverlap(left, right) {
|
|
773
|
+
const normalizedLeft = normalizeAgentVersion(left);
|
|
774
|
+
if (normalizedLeft === normalizeAgentVersion(right)) return Boolean(normalizedLeft);
|
|
775
|
+
const leftRanges = parseVersionRanges(left);
|
|
776
|
+
const rightRanges = parseVersionRanges(right);
|
|
777
|
+
return leftRanges.some((leftRange) => rightRanges.some((rightRange) => rangesOverlap(leftRange, rightRange)));
|
|
598
778
|
}
|
|
599
779
|
|
|
600
780
|
//#endregion
|
|
@@ -607,6 +787,26 @@ const syncedIndexes = /* @__PURE__ */ new Set();
|
|
|
607
787
|
const ALGOLIA_MAX_RECORD_BYTES = 9500;
|
|
608
788
|
const DEFAULT_ASK_AI_CONTEXT_CHARS = 24e3;
|
|
609
789
|
const DEFAULT_ASK_AI_RESULT_CHARS = 6e3;
|
|
790
|
+
const MAX_SEARCH_FILTER_VALUES = 16;
|
|
791
|
+
const MAX_SEARCH_FILTER_VALUE_CHARS = 128;
|
|
792
|
+
const MAX_SEARCH_FILTER_RAW_CHARS = 4096;
|
|
793
|
+
const MAX_SEARCH_FILTER_SEGMENTS = 64;
|
|
794
|
+
const MAX_PROVIDER_SCOPE_FILTER_IDS = 1e3;
|
|
795
|
+
const MAX_PROVIDER_SCOPE_FILTER_CHARS = 16e3;
|
|
796
|
+
const MAX_SEARCH_WARNINGS = 16;
|
|
797
|
+
const MAX_SEARCH_WARNING_VALUES = 16;
|
|
798
|
+
const MAX_SEARCH_WARNING_PAGE_URLS = 8;
|
|
799
|
+
const SEARCH_FILTER_FIELDS = [
|
|
800
|
+
"framework",
|
|
801
|
+
"version",
|
|
802
|
+
"package",
|
|
803
|
+
"tags"
|
|
804
|
+
];
|
|
805
|
+
const SEARCH_AMBIGUITY_FIELDS = [
|
|
806
|
+
"framework",
|
|
807
|
+
"version",
|
|
808
|
+
"package"
|
|
809
|
+
];
|
|
610
810
|
const SEARCH_STOP_WORDS = new Set([
|
|
611
811
|
"a",
|
|
612
812
|
"an",
|
|
@@ -638,6 +838,143 @@ const SEARCH_STOP_WORDS = new Set([
|
|
|
638
838
|
"which",
|
|
639
839
|
"with"
|
|
640
840
|
]);
|
|
841
|
+
function normalizeDocsSearchFilterValue(field, value) {
|
|
842
|
+
const bounded = value.trim().slice(0, MAX_SEARCH_FILTER_VALUE_CHARS).trim();
|
|
843
|
+
if (!bounded) return "";
|
|
844
|
+
if (field === "framework") return normalizeAgentFramework(bounded);
|
|
845
|
+
if (field === "version") return normalizeAgentVersion(bounded);
|
|
846
|
+
return bounded.toLowerCase();
|
|
847
|
+
}
|
|
848
|
+
function normalizeDocsSearchFilterValues(field, input) {
|
|
849
|
+
const values = typeof input === "string" ? [input] : input ?? [];
|
|
850
|
+
const normalized = [];
|
|
851
|
+
const seen = /* @__PURE__ */ new Set();
|
|
852
|
+
let remainingChars = MAX_SEARCH_FILTER_RAW_CHARS;
|
|
853
|
+
let remainingSegments = MAX_SEARCH_FILTER_SEGMENTS;
|
|
854
|
+
for (const item of values) {
|
|
855
|
+
if (remainingChars <= 0 || remainingSegments <= 0) break;
|
|
856
|
+
const boundedItem = item.slice(0, remainingChars);
|
|
857
|
+
remainingChars -= boundedItem.length;
|
|
858
|
+
for (const part of boundedItem.split(",")) {
|
|
859
|
+
if (remainingSegments <= 0) break;
|
|
860
|
+
remainingSegments -= 1;
|
|
861
|
+
const value = normalizeDocsSearchFilterValue(field, part);
|
|
862
|
+
if (!value || seen.has(value)) continue;
|
|
863
|
+
seen.add(value);
|
|
864
|
+
normalized.push(value);
|
|
865
|
+
if (normalized.length >= MAX_SEARCH_FILTER_VALUES) return normalized;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return normalized;
|
|
869
|
+
}
|
|
870
|
+
/** Normalize scalar or array-valued scope filters for programmatic, HTTP, and MCP callers. */
|
|
871
|
+
function normalizeDocsSearchFilters(input = {}) {
|
|
872
|
+
const filters = {};
|
|
873
|
+
for (const field of SEARCH_FILTER_FIELDS) {
|
|
874
|
+
const values = normalizeDocsSearchFilterValues(field, input[field]);
|
|
875
|
+
if (values.length > 0) filters[field] = values;
|
|
876
|
+
}
|
|
877
|
+
return filters;
|
|
878
|
+
}
|
|
879
|
+
/** Parse the public search scope parameters from a URL query string. */
|
|
880
|
+
function resolveDocsSearchFilters(searchParams) {
|
|
881
|
+
return normalizeDocsSearchFilters({
|
|
882
|
+
framework: searchParams.getAll("framework"),
|
|
883
|
+
version: searchParams.getAll("version"),
|
|
884
|
+
package: searchParams.getAll("package"),
|
|
885
|
+
tags: searchParams.getAll("tags")
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
/** Resolve search filters and the backwards-compatible structured response opt-in. */
|
|
889
|
+
function resolveDocsSearchRequest(searchParams) {
|
|
890
|
+
return {
|
|
891
|
+
filters: resolveDocsSearchFilters(searchParams),
|
|
892
|
+
structured: searchParams.get("response") === "structured"
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
function hasDocsSearchFilters(filters) {
|
|
896
|
+
return SEARCH_FILTER_FIELDS.some((field) => (filters[field]?.length ?? 0) > 0);
|
|
897
|
+
}
|
|
898
|
+
function resolveProviderScopeDocumentIds(query, context) {
|
|
899
|
+
if (!hasDocsSearchFilters(query.filters ?? {})) return void 0;
|
|
900
|
+
const ids = Array.from(new Set(context.documents.map((document) => document.id)));
|
|
901
|
+
return ids.length <= MAX_PROVIDER_SCOPE_FILTER_IDS ? ids : [];
|
|
902
|
+
}
|
|
903
|
+
function docsSearchFiltersMatch(expected, actual) {
|
|
904
|
+
return SEARCH_FILTER_FIELDS.every((field) => {
|
|
905
|
+
const expectedValues = expected[field] ?? [];
|
|
906
|
+
const actualValues = actual[field] ?? [];
|
|
907
|
+
return expectedValues.length === actualValues.length && expectedValues.every((value) => actualValues.includes(value));
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
function parseVerifiedMcpSearchFilters(value) {
|
|
911
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
912
|
+
const record = value;
|
|
913
|
+
const filterFields = SEARCH_FILTER_FIELDS;
|
|
914
|
+
if (Object.keys(record).some((field) => !filterFields.includes(field))) return;
|
|
915
|
+
const input = {};
|
|
916
|
+
for (const field of SEARCH_FILTER_FIELDS) {
|
|
917
|
+
const values = record[field];
|
|
918
|
+
if (values === void 0) continue;
|
|
919
|
+
if (!Array.isArray(values) || values.length > MAX_SEARCH_FILTER_VALUES || values.some((item) => typeof item !== "string" || item.length > MAX_SEARCH_FILTER_VALUE_CHARS)) return;
|
|
920
|
+
input[field] = values;
|
|
921
|
+
}
|
|
922
|
+
const normalized = normalizeDocsSearchFilters(input);
|
|
923
|
+
for (const field of SEARCH_FILTER_FIELDS) {
|
|
924
|
+
const values = record[field];
|
|
925
|
+
if (values === void 0) continue;
|
|
926
|
+
if (!Array.isArray(values) || values.length !== (normalized[field]?.length ?? 0) || values.some((value, index) => value !== normalized[field]?.[index])) return;
|
|
927
|
+
}
|
|
928
|
+
return normalized;
|
|
929
|
+
}
|
|
930
|
+
function normalizeDocsSearchMetadataValues(field, value) {
|
|
931
|
+
const values = normalizeAgentScopeValues(value);
|
|
932
|
+
return Array.from(new Set(values.map((item) => normalizeDocsSearchFilterValue(field, item)).filter((item) => Boolean(item))));
|
|
933
|
+
}
|
|
934
|
+
function valuesOverlap(left, right, matches) {
|
|
935
|
+
return left.some((leftValue) => right.some((rightValue) => matches(leftValue, rightValue)));
|
|
936
|
+
}
|
|
937
|
+
function resolveDocsSearchPageScope(page) {
|
|
938
|
+
const topFramework = normalizeDocsSearchMetadataValues("framework", page.framework);
|
|
939
|
+
const contractFramework = normalizeDocsSearchMetadataValues("framework", page.agent?.appliesTo?.framework);
|
|
940
|
+
const topVersion = normalizeDocsSearchMetadataValues("version", page.version);
|
|
941
|
+
const contractVersion = normalizeDocsSearchMetadataValues("version", page.agent?.appliesTo?.version);
|
|
942
|
+
const packageValues = normalizeDocsSearchMetadataValues("package", page.agent?.appliesTo?.package);
|
|
943
|
+
const tags = normalizeDocsSearchMetadataValues("tags", page.tags);
|
|
944
|
+
const conflicts = [];
|
|
945
|
+
if (topFramework.length > 0 && contractFramework.length > 0 && !valuesOverlap(topFramework, contractFramework, (left, right) => left === right)) conflicts.push("framework");
|
|
946
|
+
if (topVersion.length > 0 && contractVersion.length > 0 && !valuesOverlap(topVersion, contractVersion, agentVersionConstraintsOverlap)) conflicts.push("version");
|
|
947
|
+
return {
|
|
948
|
+
framework: topFramework.length > 0 && contractFramework.length > 0 ? topFramework.filter((value) => contractFramework.includes(value)) : Array.from(new Set([...topFramework, ...contractFramework])),
|
|
949
|
+
version: Array.from(new Set([...topVersion, ...contractVersion])),
|
|
950
|
+
package: packageValues,
|
|
951
|
+
tags,
|
|
952
|
+
declarations: {
|
|
953
|
+
framework: [topFramework, contractFramework].filter((values) => values.length > 0),
|
|
954
|
+
version: [topVersion, contractVersion].filter((values) => values.length > 0),
|
|
955
|
+
package: packageValues.length > 0 ? [packageValues] : [],
|
|
956
|
+
tags: tags.length > 0 ? [tags] : []
|
|
957
|
+
},
|
|
958
|
+
conflicts
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function docsSearchScopeValueMatches(field, requested, candidate) {
|
|
962
|
+
return field === "version" ? agentVersionConstraintsOverlap(requested, candidate) : requested === candidate;
|
|
963
|
+
}
|
|
964
|
+
function docsSearchPageMatchesFilters(scope, filters) {
|
|
965
|
+
if (scope.conflicts.length > 0) return false;
|
|
966
|
+
return SEARCH_FILTER_FIELDS.every((field) => {
|
|
967
|
+
const requested = filters[field];
|
|
968
|
+
if (!requested || requested.length === 0) return true;
|
|
969
|
+
return docsSearchScopeFieldMatches(scope, field, requested);
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
function docsSearchScopeFieldMatches(scope, field, requested) {
|
|
973
|
+
const declarations = scope.declarations[field];
|
|
974
|
+
if (declarations.length === 0) return false;
|
|
975
|
+
if (field === "version") return agentVersionConstraintGroupsOverlap([requested, ...declarations]);
|
|
976
|
+
return requested.some((value) => declarations.every((candidates) => candidates.some((candidate) => docsSearchScopeValueMatches(field, value, candidate))));
|
|
977
|
+
}
|
|
641
978
|
function stripMarkdownText(content) {
|
|
642
979
|
return removeMdxModuleLinesOutsideFences(content).replace(/```[^\n]*\n([\s\S]*?)```/g, "$1").replace(/```([\s\S]*?)```/g, "$1").replace(/~~~[^\n]*\n([\s\S]*?)~~~/g, "$1").replace(/~~~([\s\S]*?)~~~/g, "$1").replace(/<[^>]+\/>/g, "").replace(/<\/?[A-Z][^>]*>/g, "").replace(/<\/?[a-z][^>]*>/g, "").replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^\|?[\s:-]+(\|[\s:-]+)+\|?\s*$/gm, "").replace(/\|/g, " ").replace(/^[-*+]\s+/gm, "").replace(/(\*{1,3}|_{1,3})(.*?)\1/g, "$2").replace(/`{3,}[^\n]*$/gm, "").replace(/`([^`]+)`/g, "$1").replace(/`+/g, "").replace(/^>\s+/gm, "").replace(/^[-*_]{3,}\s*$/gm, "").replace(/\n{3,}/g, "\n\n").replace(/\s{2,}/g, " ").trim();
|
|
643
980
|
}
|
|
@@ -970,6 +1307,7 @@ function hasOppositeAudienceEvidence(options) {
|
|
|
970
1307
|
return oppositeOnlyMatches > selectedOnlyMatches;
|
|
971
1308
|
}
|
|
972
1309
|
function pageToSearchDocument(page, audience = "human") {
|
|
1310
|
+
const scope = resolveDocsSearchPageScope(page);
|
|
973
1311
|
return {
|
|
974
1312
|
id: makeDocumentId(page.url, "page"),
|
|
975
1313
|
url: page.url,
|
|
@@ -978,9 +1316,10 @@ function pageToSearchDocument(page, audience = "human") {
|
|
|
978
1316
|
description: page.description,
|
|
979
1317
|
type: "page",
|
|
980
1318
|
locale: page.locale,
|
|
981
|
-
framework:
|
|
982
|
-
version:
|
|
983
|
-
|
|
1319
|
+
framework: scope.framework[0],
|
|
1320
|
+
version: scope.version[0],
|
|
1321
|
+
package: scope.package.length > 0 ? scope.package : void 0,
|
|
1322
|
+
tags: scope.tags.length > 0 ? scope.tags : void 0
|
|
984
1323
|
};
|
|
985
1324
|
}
|
|
986
1325
|
function buildExactPageSearchResults(query, pages, audience = "human") {
|
|
@@ -1043,7 +1382,9 @@ function makeDocumentId(url, suffix) {
|
|
|
1043
1382
|
return `${url}#${suffix}`;
|
|
1044
1383
|
}
|
|
1045
1384
|
function splitPageIntoSections(page, audience = "human") {
|
|
1046
|
-
|
|
1385
|
+
const raw = getPageAudienceRawContent(page, audience);
|
|
1386
|
+
const scope = resolveDocsSearchPageScope(page);
|
|
1387
|
+
return parseDocsMarkdownSections(raw).flatMap((section, index) => {
|
|
1047
1388
|
const content = normalizeWhitespace(stripMarkdownText(section.content));
|
|
1048
1389
|
if (!content) return [];
|
|
1049
1390
|
return [{
|
|
@@ -1055,9 +1396,10 @@ function splitPageIntoSections(page, audience = "human") {
|
|
|
1055
1396
|
description: page.description,
|
|
1056
1397
|
type: "heading",
|
|
1057
1398
|
locale: page.locale,
|
|
1058
|
-
framework:
|
|
1059
|
-
version:
|
|
1060
|
-
|
|
1399
|
+
framework: scope.framework[0],
|
|
1400
|
+
version: scope.version[0],
|
|
1401
|
+
package: scope.package.length > 0 ? scope.package : void 0,
|
|
1402
|
+
tags: scope.tags.length > 0 ? scope.tags : void 0
|
|
1061
1403
|
}];
|
|
1062
1404
|
});
|
|
1063
1405
|
}
|
|
@@ -1195,7 +1537,12 @@ function buildAlgoliaRecord(document) {
|
|
|
1195
1537
|
section: document.section,
|
|
1196
1538
|
content: document.content,
|
|
1197
1539
|
description: document.description,
|
|
1198
|
-
type: document.type
|
|
1540
|
+
type: document.type,
|
|
1541
|
+
locale: document.locale,
|
|
1542
|
+
framework: document.framework,
|
|
1543
|
+
version: document.version,
|
|
1544
|
+
package: document.package,
|
|
1545
|
+
tags: document.tags
|
|
1199
1546
|
};
|
|
1200
1547
|
const encoder = new TextEncoder();
|
|
1201
1548
|
const sizeOf = (value) => encoder.encode(JSON.stringify(value)).length;
|
|
@@ -1300,6 +1647,14 @@ function mapMcpSearchResult(value) {
|
|
|
1300
1647
|
section
|
|
1301
1648
|
};
|
|
1302
1649
|
}
|
|
1650
|
+
function readMcpSearchToolPayload(payload) {
|
|
1651
|
+
if (!payload || typeof payload !== "object" || !("result" in payload) || !payload.result || typeof payload.result !== "object") return null;
|
|
1652
|
+
const result = payload.result;
|
|
1653
|
+
if (result.structuredContent && typeof result.structuredContent === "object" && !Array.isArray(result.structuredContent)) return result.structuredContent;
|
|
1654
|
+
const content = Array.isArray(result.content) ? result.content : [];
|
|
1655
|
+
const resultText = content.length > 0 && content[0] && typeof content[0] === "object" && "text" in content[0] && typeof content[0].text === "string" ? content[0].text : null;
|
|
1656
|
+
return resultText ? JSON.parse(resultText) : null;
|
|
1657
|
+
}
|
|
1303
1658
|
async function createOllamaEmbedding(text, config, signal) {
|
|
1304
1659
|
const response = await fetch(`${(config.baseUrl ?? "http://127.0.0.1:11434").replace(/\/$/, "")}/api/embed`, {
|
|
1305
1660
|
method: "POST",
|
|
@@ -1319,6 +1674,9 @@ async function createOllamaEmbedding(text, config, signal) {
|
|
|
1319
1674
|
function getTypesenseSearchBase(config) {
|
|
1320
1675
|
return config.baseUrl.replace(/\/$/, "");
|
|
1321
1676
|
}
|
|
1677
|
+
function quoteTypesenseFilterValue(value) {
|
|
1678
|
+
return `\`${value.replace(/\\/gu, "\\\\").replace(/`/gu, "\\`")}\``;
|
|
1679
|
+
}
|
|
1322
1680
|
async function ensureTypesenseCollection(config, dimensions, signal) {
|
|
1323
1681
|
const baseUrl = getTypesenseSearchBase(config);
|
|
1324
1682
|
const headers = {
|
|
@@ -1377,6 +1735,11 @@ async function ensureTypesenseCollection(config, dimensions, signal) {
|
|
|
1377
1735
|
type: "string",
|
|
1378
1736
|
optional: true
|
|
1379
1737
|
},
|
|
1738
|
+
{
|
|
1739
|
+
name: "package",
|
|
1740
|
+
type: "string[]",
|
|
1741
|
+
optional: true
|
|
1742
|
+
},
|
|
1380
1743
|
{
|
|
1381
1744
|
name: "tags",
|
|
1382
1745
|
type: "string[]",
|
|
@@ -1416,6 +1779,7 @@ function createTypesenseSearchAdapter(config) {
|
|
|
1416
1779
|
locale: document.locale,
|
|
1417
1780
|
framework: document.framework,
|
|
1418
1781
|
version: document.version,
|
|
1782
|
+
package: document.package,
|
|
1419
1783
|
tags: document.tags
|
|
1420
1784
|
};
|
|
1421
1785
|
if (config.mode === "hybrid" && config.embeddings) next.embedding = await createOllamaEmbedding(`${document.title}\n${document.section ?? ""}\n${document.content}`.trim(), config.embeddings, context.signal);
|
|
@@ -1434,6 +1798,8 @@ function createTypesenseSearchAdapter(config) {
|
|
|
1434
1798
|
}), "Failed to sync documents to Typesense");
|
|
1435
1799
|
},
|
|
1436
1800
|
async search(query, context) {
|
|
1801
|
+
const scopedDocumentIds = resolveProviderScopeDocumentIds(query, context);
|
|
1802
|
+
if (scopedDocumentIds?.length === 0) return [];
|
|
1437
1803
|
const params = new URLSearchParams({
|
|
1438
1804
|
q: query.query,
|
|
1439
1805
|
query_by: (config.queryBy ?? [
|
|
@@ -1451,12 +1817,27 @@ function createTypesenseSearchAdapter(config) {
|
|
|
1451
1817
|
const vector = await createOllamaEmbedding(query.query, config.embeddings, context.signal);
|
|
1452
1818
|
params.set("vector_query", `embedding:([${vector.join(",")}],k:${Math.max((query.limit ?? 10) * 4, 20)})`);
|
|
1453
1819
|
}
|
|
1454
|
-
const
|
|
1820
|
+
const filterBy = scopedDocumentIds ? `id:=[${scopedDocumentIds.map(quoteTypesenseFilterValue).join(",")}]` : void 0;
|
|
1821
|
+
if (filterBy && filterBy.length > MAX_PROVIDER_SCOPE_FILTER_CHARS) return [];
|
|
1822
|
+
const response = filterBy ? await fetch(`${getTypesenseSearchBase(config)}/multi_search`, {
|
|
1823
|
+
method: "POST",
|
|
1824
|
+
headers: {
|
|
1825
|
+
"Content-Type": "application/json",
|
|
1826
|
+
"X-TYPESENSE-API-KEY": config.apiKey
|
|
1827
|
+
},
|
|
1828
|
+
body: JSON.stringify({ searches: [{
|
|
1829
|
+
collection: config.collection,
|
|
1830
|
+
...Object.fromEntries(params),
|
|
1831
|
+
filter_by: filterBy
|
|
1832
|
+
}] }),
|
|
1833
|
+
signal: context.signal
|
|
1834
|
+
}) : await fetch(`${getTypesenseSearchBase(config)}/collections/${encodeURIComponent(config.collection)}/documents/search?${params.toString()}`, {
|
|
1455
1835
|
headers: { "X-TYPESENSE-API-KEY": config.apiKey },
|
|
1456
1836
|
signal: context.signal
|
|
1457
1837
|
});
|
|
1458
1838
|
ensureOk(response, "Typesense search failed");
|
|
1459
|
-
|
|
1839
|
+
const rawPayload = await readResponseJson(response);
|
|
1840
|
+
return ((filterBy ? rawPayload.results?.[0] ?? {} : rawPayload).hits ?? []).map((hit) => {
|
|
1460
1841
|
const document = hit.document ?? {};
|
|
1461
1842
|
const section = typeof document.section === "string" ? document.section : void 0;
|
|
1462
1843
|
const content = typeof document.title === "string" ? section ? `${document.title} — ${section}` : document.title : typeof document.content === "string" ? document.content : "Untitled result";
|
|
@@ -1567,7 +1948,11 @@ function createMcpSearchAdapter(config) {
|
|
|
1567
1948
|
query: query.query,
|
|
1568
1949
|
limit: query.limit ?? config.maxResults ?? DEFAULT_SEARCH_LIMIT,
|
|
1569
1950
|
locale: query.locale,
|
|
1570
|
-
...forwardAudience ? { audience } : {}
|
|
1951
|
+
...forwardAudience ? { audience } : {},
|
|
1952
|
+
...query.filters?.framework ? { framework: query.filters.framework } : {},
|
|
1953
|
+
...query.filters?.version ? { version: query.filters.version } : {},
|
|
1954
|
+
...query.filters?.package ? { package: query.filters.package } : {},
|
|
1955
|
+
...query.filters?.tags ? { tags: query.filters.tags } : {}
|
|
1571
1956
|
}
|
|
1572
1957
|
}
|
|
1573
1958
|
}),
|
|
@@ -1576,9 +1961,13 @@ function createMcpSearchAdapter(config) {
|
|
|
1576
1961
|
const payload = await readMcpResponsePayload(searchResponse);
|
|
1577
1962
|
ensureOk(searchResponse, "MCP search request failed");
|
|
1578
1963
|
ensureJsonRpcOk(payload, "MCP search request failed");
|
|
1579
|
-
const
|
|
1580
|
-
if (!
|
|
1581
|
-
|
|
1964
|
+
const parsed = readMcpSearchToolPayload(payload);
|
|
1965
|
+
if (!parsed) return [];
|
|
1966
|
+
if (hasDocsSearchFilters(query.filters ?? {})) {
|
|
1967
|
+
if (Array.isArray(parsed) || parsed.format !== "docs-search.v1") return [];
|
|
1968
|
+
const echoedFilters = parseVerifiedMcpSearchFilters(parsed.filters);
|
|
1969
|
+
if (!echoedFilters || !docsSearchFiltersMatch(query.filters ?? {}, echoedFilters)) return [];
|
|
1970
|
+
}
|
|
1582
1971
|
return (Array.isArray(parsed) ? parsed : Array.isArray(parsed.results) ? parsed.results : Array.isArray(parsed.pages) ? parsed.pages : []).map(mapMcpSearchResult).filter((result) => Boolean(result));
|
|
1583
1972
|
} finally {
|
|
1584
1973
|
if (sessionId) {
|
|
@@ -1605,6 +1994,9 @@ function createMcpSearchAdapter(config) {
|
|
|
1605
1994
|
function getAlgoliaBase(config) {
|
|
1606
1995
|
return `https://${config.appId}-dsn.algolia.net`;
|
|
1607
1996
|
}
|
|
1997
|
+
function quoteAlgoliaFilterValue(value) {
|
|
1998
|
+
return `"${value.replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"")}"`;
|
|
1999
|
+
}
|
|
1608
2000
|
function createAlgoliaSearchAdapter(config) {
|
|
1609
2001
|
return {
|
|
1610
2002
|
name: "algolia",
|
|
@@ -1625,6 +2017,10 @@ function createAlgoliaSearchAdapter(config) {
|
|
|
1625
2017
|
}), "Failed to sync documents to Algolia");
|
|
1626
2018
|
},
|
|
1627
2019
|
async search(query, context) {
|
|
2020
|
+
const scopedDocumentIds = resolveProviderScopeDocumentIds(query, context);
|
|
2021
|
+
if (scopedDocumentIds?.length === 0) return [];
|
|
2022
|
+
const filters = scopedDocumentIds?.map((id) => `objectID:${quoteAlgoliaFilterValue(id)}`).join(" OR ");
|
|
2023
|
+
if (filters && filters.length > MAX_PROVIDER_SCOPE_FILTER_CHARS) return [];
|
|
1628
2024
|
const response = await fetch(`${getAlgoliaBase(config)}/1/indexes/${encodeURIComponent(config.indexName)}/query`, {
|
|
1629
2025
|
method: "POST",
|
|
1630
2026
|
headers: {
|
|
@@ -1635,7 +2031,8 @@ function createAlgoliaSearchAdapter(config) {
|
|
|
1635
2031
|
body: JSON.stringify({
|
|
1636
2032
|
query: query.query,
|
|
1637
2033
|
hitsPerPage: query.limit ?? config.maxResults ?? DEFAULT_SEARCH_LIMIT,
|
|
1638
|
-
attributesToSnippet: ["content:20"]
|
|
2034
|
+
attributesToSnippet: ["content:20"],
|
|
2035
|
+
...filters ? { filters } : {}
|
|
1639
2036
|
}),
|
|
1640
2037
|
signal: context.signal
|
|
1641
2038
|
});
|
|
@@ -1692,9 +2089,12 @@ async function performDocsSearch(options) {
|
|
|
1692
2089
|
const search = normalizeDocsSearchConfig(options.search);
|
|
1693
2090
|
if (!search.enabled) return [];
|
|
1694
2091
|
const audience = resolveDocsSearchAudience(options.audience);
|
|
1695
|
-
const
|
|
2092
|
+
const filters = normalizeDocsSearchFilters(options.filters);
|
|
2093
|
+
const hasFilters = hasDocsSearchFilters(filters);
|
|
2094
|
+
const scopedPages = hasFilters ? options.pages.filter((page) => docsSearchPageMatchesFilters(resolveDocsSearchPageScope(page), filters)) : options.pages;
|
|
2095
|
+
const documents = buildDocsSearchDocuments(scopedPages, search.chunking, audience);
|
|
1696
2096
|
const context = {
|
|
1697
|
-
pages:
|
|
2097
|
+
pages: scopedPages,
|
|
1698
2098
|
documents,
|
|
1699
2099
|
audience,
|
|
1700
2100
|
locale: options.locale,
|
|
@@ -1707,27 +2107,30 @@ async function performDocsSearch(options) {
|
|
|
1707
2107
|
limit: options.limit ?? search.maxResults,
|
|
1708
2108
|
locale: options.locale,
|
|
1709
2109
|
pathname: options.pathname,
|
|
1710
|
-
audience
|
|
2110
|
+
audience,
|
|
2111
|
+
...hasFilters ? { filters } : {}
|
|
1711
2112
|
};
|
|
1712
2113
|
try {
|
|
1713
2114
|
const adapter = await resolveSearchAdapter(search, context);
|
|
1714
|
-
await maybeSyncSearchIndex(adapter, search, audience === "agent" ? {
|
|
2115
|
+
await maybeSyncSearchIndex(adapter, search, audience === "agent" || hasFilters ? {
|
|
1715
2116
|
...context,
|
|
2117
|
+
pages: options.pages,
|
|
1716
2118
|
documents: buildDocsSearchDocuments(options.pages, search.chunking, "human"),
|
|
1717
2119
|
audience: "human"
|
|
1718
2120
|
} : context);
|
|
1719
2121
|
const results = await adapter.search(query, context);
|
|
1720
2122
|
if (search.provider === "simple") return results;
|
|
1721
2123
|
const localAudienceProjectionResults = buildAudienceProjectionSearchResults(documents, options.query);
|
|
1722
|
-
const localPagePaths = new Set(
|
|
1723
|
-
const
|
|
2124
|
+
const localPagePaths = new Set(scopedPages.map((page) => normalizeUrlPathname(page.url)));
|
|
2125
|
+
const allLocalPagePaths = hasFilters && search.provider === "mcp" ? new Set(options.pages.map((page) => normalizeUrlPathname(page.url))) : localPagePaths;
|
|
2126
|
+
const preserveUnmatched = !hasFilters && audience === "human" || search.provider === "mcp" ? (result) => shouldPreserveUnmatchedExternalResult({
|
|
1724
2127
|
result,
|
|
1725
|
-
localPagePaths,
|
|
2128
|
+
localPagePaths: allLocalPagePaths,
|
|
1726
2129
|
baseUrl: options.baseUrl
|
|
1727
2130
|
}) : void 0;
|
|
1728
2131
|
const safeAdapterResults = sanitizeExternalAudienceSearchResults(results.filter((result) => !hasOppositeAudienceEvidence({
|
|
1729
2132
|
result,
|
|
1730
|
-
pages:
|
|
2133
|
+
pages: scopedPages,
|
|
1731
2134
|
query: options.query,
|
|
1732
2135
|
audience,
|
|
1733
2136
|
baseUrl: options.baseUrl,
|
|
@@ -1736,7 +2139,7 @@ async function performDocsSearch(options) {
|
|
|
1736
2139
|
if (options.supplementExternalResults === false) return safeAdapterResults.slice(0, query.limit ?? search.maxResults ?? DEFAULT_SEARCH_LIMIT);
|
|
1737
2140
|
const simpleAudienceResults = audience === "agent" || results.length === 0 || safeAdapterResults.length < results.length ? await createSimpleSearchAdapter().search(query, context) : [];
|
|
1738
2141
|
const combinedResults = mergeSearchResults([
|
|
1739
|
-
buildExactPageSearchResults(options.query,
|
|
2142
|
+
buildExactPageSearchResults(options.query, scopedPages, audience),
|
|
1740
2143
|
safeAdapterResults,
|
|
1741
2144
|
simpleAudienceResults
|
|
1742
2145
|
], audience === "agent" ? (result) => getAskAIResultKey(result, options.baseUrl, options.strictExternalOrigins) : void 0);
|
|
@@ -1746,6 +2149,127 @@ async function performDocsSearch(options) {
|
|
|
1746
2149
|
return createSimpleSearchAdapter().search(query, context);
|
|
1747
2150
|
}
|
|
1748
2151
|
}
|
|
2152
|
+
function compareSearchMetadataValues(left, right) {
|
|
2153
|
+
if (left === right) return 0;
|
|
2154
|
+
return left < right ? -1 : 1;
|
|
2155
|
+
}
|
|
2156
|
+
function boundedSearchWarningValues(values) {
|
|
2157
|
+
const bounded = Array.from(new Set(values)).sort(compareSearchMetadataValues).slice(0, MAX_SEARCH_WARNING_VALUES);
|
|
2158
|
+
return bounded.length > 0 ? bounded : void 0;
|
|
2159
|
+
}
|
|
2160
|
+
function boundedSearchWarningPageUrls(pages) {
|
|
2161
|
+
const bounded = Array.from(new Set(pages.map((page) => page.url))).sort(compareSearchMetadataValues).slice(0, MAX_SEARCH_WARNING_PAGE_URLS);
|
|
2162
|
+
return bounded.length > 0 ? bounded : void 0;
|
|
2163
|
+
}
|
|
2164
|
+
function findSearchResultPages(pages, results, baseUrl) {
|
|
2165
|
+
const pagesByPath = new Map(pages.map((page) => [normalizeUrlPathname(page.url), page]));
|
|
2166
|
+
const found = /* @__PURE__ */ new Map();
|
|
2167
|
+
for (const result of results) {
|
|
2168
|
+
if (!isLocalProviderResult(result, baseUrl, true)) continue;
|
|
2169
|
+
const page = pagesByPath.get(normalizeUrlPathname(result.url));
|
|
2170
|
+
if (page) found.set(page.url, page);
|
|
2171
|
+
}
|
|
2172
|
+
return [...found.values()].sort((left, right) => compareSearchMetadataValues(left.url, right.url));
|
|
2173
|
+
}
|
|
2174
|
+
function searchScopeIsAmbiguous(field, values) {
|
|
2175
|
+
if (values.length < 2) return false;
|
|
2176
|
+
if (field !== "version") return true;
|
|
2177
|
+
return values.some((left, index) => values.slice(index + 1).some((right) => !agentVersionConstraintsOverlap(left, right)));
|
|
2178
|
+
}
|
|
2179
|
+
function buildDocsSearchWarnings(options) {
|
|
2180
|
+
const pageScopes = options.pages.map((page) => ({
|
|
2181
|
+
page,
|
|
2182
|
+
scope: resolveDocsSearchPageScope(page)
|
|
2183
|
+
}));
|
|
2184
|
+
const relevantPageScopes = pageScopes.filter(({ page }) => scoreDocument(options.query, pageToSearchDocument(page, options.audience)) > 0);
|
|
2185
|
+
const warnings = [];
|
|
2186
|
+
for (const field of SEARCH_FILTER_FIELDS) {
|
|
2187
|
+
const conflicting = relevantPageScopes.filter(({ scope }) => scope.conflicts.includes(field));
|
|
2188
|
+
if (conflicting.length > 0) warnings.push({
|
|
2189
|
+
code: "conflicting_scope_metadata",
|
|
2190
|
+
field,
|
|
2191
|
+
message: `${conflicting.length} page${conflicting.length === 1 ? "" : "s"} declare conflicting ${field} metadata and cannot be selected safely by scoped search.`,
|
|
2192
|
+
pageUrls: boundedSearchWarningPageUrls(conflicting.map(({ page }) => page)),
|
|
2193
|
+
count: conflicting.length
|
|
2194
|
+
});
|
|
2195
|
+
const requested = options.filters[field];
|
|
2196
|
+
if (!requested || requested.length === 0) continue;
|
|
2197
|
+
const missing = relevantPageScopes.filter(({ scope }) => scope.declarations[field].length === 0);
|
|
2198
|
+
if (missing.length > 0) warnings.push({
|
|
2199
|
+
code: "missing_scope_metadata",
|
|
2200
|
+
field,
|
|
2201
|
+
message: `${missing.length} page${missing.length === 1 ? "" : "s"} lack ${field} metadata and were excluded by the strict scope filter.`,
|
|
2202
|
+
pageUrls: boundedSearchWarningPageUrls(missing.map(({ page }) => page)),
|
|
2203
|
+
count: missing.length
|
|
2204
|
+
});
|
|
2205
|
+
const unknown = requested.filter((value) => !pageScopes.some(({ scope }) => !scope.conflicts.includes(field) && docsSearchScopeFieldMatches(scope, field, [value])));
|
|
2206
|
+
if (unknown.length > 0) warnings.push({
|
|
2207
|
+
code: "unknown_filter_value",
|
|
2208
|
+
field,
|
|
2209
|
+
message: `No page metadata matches ${field} filter value${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}.`,
|
|
2210
|
+
values: boundedSearchWarningValues(unknown),
|
|
2211
|
+
count: unknown.length
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
const resultPages = findSearchResultPages(options.pages, options.results, options.baseUrl);
|
|
2215
|
+
for (const field of SEARCH_AMBIGUITY_FIELDS) {
|
|
2216
|
+
if ((options.filters[field]?.length ?? 0) > 0) continue;
|
|
2217
|
+
const values = resultPages.flatMap((page) => {
|
|
2218
|
+
const scope = resolveDocsSearchPageScope(page);
|
|
2219
|
+
return scope.conflicts.includes(field) ? [] : scope[field];
|
|
2220
|
+
});
|
|
2221
|
+
const unique = Array.from(new Set(values)).sort(compareSearchMetadataValues);
|
|
2222
|
+
if (!searchScopeIsAmbiguous(field, unique)) continue;
|
|
2223
|
+
const contributingPages = resultPages.filter((page) => {
|
|
2224
|
+
const scope = resolveDocsSearchPageScope(page);
|
|
2225
|
+
return !scope.conflicts.includes(field) && scope[field].length > 0;
|
|
2226
|
+
});
|
|
2227
|
+
warnings.push({
|
|
2228
|
+
code: "ambiguous_scope",
|
|
2229
|
+
field,
|
|
2230
|
+
message: `Search results span multiple ${field} scopes; add a ${field} filter before acting on scope-specific guidance.`,
|
|
2231
|
+
values: boundedSearchWarningValues(unique),
|
|
2232
|
+
pageUrls: boundedSearchWarningPageUrls(contributingPages),
|
|
2233
|
+
count: unique.length
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
const codeOrder = {
|
|
2237
|
+
ambiguous_scope: 0,
|
|
2238
|
+
unknown_filter_value: 1,
|
|
2239
|
+
missing_scope_metadata: 2,
|
|
2240
|
+
conflicting_scope_metadata: 3
|
|
2241
|
+
};
|
|
2242
|
+
return warnings.sort((left, right) => {
|
|
2243
|
+
const codeDelta = codeOrder[left.code] - codeOrder[right.code];
|
|
2244
|
+
if (codeDelta !== 0) return codeDelta;
|
|
2245
|
+
return SEARCH_FILTER_FIELDS.indexOf(left.field) - SEARCH_FILTER_FIELDS.indexOf(right.field);
|
|
2246
|
+
}).slice(0, MAX_SEARCH_WARNINGS);
|
|
2247
|
+
}
|
|
2248
|
+
async function performDocsSearchWithMetadata(options) {
|
|
2249
|
+
const audience = resolveDocsSearchAudience(options.audience);
|
|
2250
|
+
const filters = normalizeDocsSearchFilters(options.filters);
|
|
2251
|
+
const results = await performDocsSearch({
|
|
2252
|
+
...options,
|
|
2253
|
+
audience,
|
|
2254
|
+
filters
|
|
2255
|
+
});
|
|
2256
|
+
return {
|
|
2257
|
+
format: "docs-search.v1",
|
|
2258
|
+
query: options.query,
|
|
2259
|
+
audience,
|
|
2260
|
+
filters,
|
|
2261
|
+
resultCount: results.length,
|
|
2262
|
+
results,
|
|
2263
|
+
warnings: buildDocsSearchWarnings({
|
|
2264
|
+
pages: options.pages,
|
|
2265
|
+
results,
|
|
2266
|
+
filters,
|
|
2267
|
+
query: options.query,
|
|
2268
|
+
audience,
|
|
2269
|
+
baseUrl: options.baseUrl
|
|
2270
|
+
})
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
1749
2273
|
async function buildDocsAskAIContext(options) {
|
|
1750
2274
|
const limit = options.limit ?? 5;
|
|
1751
2275
|
const searchLimit = Math.max(limit * 2, limit);
|
|
@@ -1761,6 +2285,7 @@ async function buildDocsAskAIContext(options) {
|
|
|
1761
2285
|
siteTitle: options.siteTitle,
|
|
1762
2286
|
baseUrl: options.baseUrl,
|
|
1763
2287
|
limit: searchLimit,
|
|
2288
|
+
filters: options.filters,
|
|
1764
2289
|
failureMode: options.searchFailureMode,
|
|
1765
2290
|
strictExternalOrigins: options.strictExternalOrigins,
|
|
1766
2291
|
signal: options.signal
|
|
@@ -1823,4 +2348,4 @@ function createCustomSearchAdapter(adapter) {
|
|
|
1823
2348
|
}
|
|
1824
2349
|
|
|
1825
2350
|
//#endregion
|
|
1826
|
-
export {
|
|
2351
|
+
export { stripGeneratedAgentProvenance as A, inferDocsTelemetryAgentSurface as B, normalizeAgentVersion as C, normalizeGeneratedAgentContent as D, hashGeneratedAgentContent as E, emitDocsTelemetryAgentSurfaceEvent as F, normalizeDocsTelemetryOrigin as H, emitDocsTelemetryEvent as I, emitDocsTelemetryMcpToolEvent as L, resolvePageSidebarFolderIndexBehavior as M, resolveSidebarFolderIndexBehavior as N, parseGeneratedAgentDocument as O, resolveSidebarFolderIndexBehaviorForPath as P, emitDocsTelemetryProjectEvent as R, normalizeAgentScopeValues as S, GENERATED_AGENT_PROVENANCE_VERSION as T, resolveDocsTelemetryConfig as U, isLocalDocsTelemetryOrigin as V, resolveSearchRequestConfig as _, createMcpSearchAdapter as a, normalizeAgentFramework as b, formatDocsAskAIPackageHints as c, performDocsSearch as d, performDocsSearchWithMetadata as f, resolveDocsSearchRequest as g, resolveDocsSearchFilters as h, createCustomSearchAdapter as i, applySidebarFolderIndexBehavior as j, serializeGeneratedAgentDocument as k, inferDocsAskAIPackageHints as l, resolveDocsSearchAudience as m, buildDocsSearchDocuments as n, createSimpleSearchAdapter as o, resolveAskAISearchRequestConfig as p, createAlgoliaSearchAdapter as r, createTypesenseSearchAdapter as s, buildDocsAskAIContext as t, normalizeDocsSearchFilters as u, agentVersionConstraintMatches as v, GENERATED_AGENT_PROVENANCE_MARKER as w, normalizeAgentLocale as x, agentVersionConstraintsOverlap as y, getDocsTelemetryFeatures as z };
|