@fenglimg/fabric-server 2.3.0-rc.6 → 2.3.0-rc.7
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/index.d.ts +41 -2
- package/dist/index.js +197 -12
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -745,6 +745,36 @@ interface RelatedGraphNode {
|
|
|
745
745
|
* id index carries both forms for every node.
|
|
746
746
|
*/
|
|
747
747
|
declare function buildRelatedGraph(nodes: RelatedGraphNode[]): RelatedGraphInspection;
|
|
748
|
+
/** A richer node carrying the text/metadata signals the suggestion heuristic reads. */
|
|
749
|
+
interface RelatedGraphNodeRich {
|
|
750
|
+
/** Store-qualified id (`<alias>:<stableId>`). */
|
|
751
|
+
qualifiedId: string;
|
|
752
|
+
summary: string;
|
|
753
|
+
/** RuleDescription.intent_clues (NOT `keywords` — RuleDescription has no such field). */
|
|
754
|
+
intentClues: string[];
|
|
755
|
+
tags: string[];
|
|
756
|
+
relevancePaths: string[];
|
|
757
|
+
/** Existing declared `related` edges — a pair already connected here is never re-proposed. */
|
|
758
|
+
related: string[];
|
|
759
|
+
}
|
|
760
|
+
/** A proposed related edge: an ordered id pair, a confidence, and the signals that fired. */
|
|
761
|
+
interface SuggestedRelatedEdge {
|
|
762
|
+
source: string;
|
|
763
|
+
target: string;
|
|
764
|
+
confidence: number;
|
|
765
|
+
provenance: string[];
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Propose related edges the corpus is missing. For every unordered pair NOT already
|
|
769
|
+
* connected via existing `related`, score:
|
|
770
|
+
* - token Jaccard over tokenize(summary + intent_clues) — the DOMINANT signal
|
|
771
|
+
* - tag-set intersection — an INDEPENDENT boolean bonus (never folded into tokens)
|
|
772
|
+
* - shared relevance_paths — an INDEPENDENT boolean bonus
|
|
773
|
+
* confidence = jaccard + bonuses, clamped [0,1]. Only pairs >= 0.6 are returned, each
|
|
774
|
+
* with a non-empty provenance[] naming the firing signals. Pure + deterministic
|
|
775
|
+
* (same input → same output; sorted confidence desc, then source, then target).
|
|
776
|
+
*/
|
|
777
|
+
declare function suggestRelatedEdges(nodes: RelatedGraphNodeRich[]): SuggestedRelatedEdge[];
|
|
748
778
|
/**
|
|
749
779
|
* Walk the store canonical corpus and build the related graph. Never throws —
|
|
750
780
|
* collectStoreCanonicalEntries degrades to [] when no store is in the read-set.
|
|
@@ -756,6 +786,13 @@ declare function buildRelatedGraph(nodes: RelatedGraphNode[]): RelatedGraphInspe
|
|
|
756
786
|
* read `description.related` directly (the single source the recall path uses).
|
|
757
787
|
*/
|
|
758
788
|
declare function inspectRelatedGraph(projectRoot: string): Promise<RelatedGraphInspection>;
|
|
789
|
+
/**
|
|
790
|
+
* Walk the store canonical corpus and PROPOSE the top-N missing related edges via the
|
|
791
|
+
* pure suggestRelatedEdges heuristic. Never throws — collectStoreCanonicalEntries
|
|
792
|
+
* degrades to [] when no store is in the read-set. READ-ONLY: it computes suggestions
|
|
793
|
+
* and writes nothing; edge creation stays on the fabric-review modify path.
|
|
794
|
+
*/
|
|
795
|
+
declare function inspectSuggestedRelatedEdges(projectRoot: string): Promise<SuggestedRelatedEdge[]>;
|
|
759
796
|
|
|
760
797
|
interface StoreReachability {
|
|
761
798
|
uuid: string;
|
|
@@ -988,10 +1025,12 @@ type RecallInput = PlanContextInput & {
|
|
|
988
1025
|
*/
|
|
989
1026
|
include_related?: boolean;
|
|
990
1027
|
};
|
|
1028
|
+
type FullRuleDescription = PlanContextResult["candidates"][number]["description"];
|
|
1029
|
+
type RecallEntryDescription = Pick<FullRuleDescription, "summary" | "must_read_if" | "intent_clues"> & Partial<Pick<FullRuleDescription, "knowledge_type">>;
|
|
991
1030
|
type RecallEntry = {
|
|
992
1031
|
stable_id: string;
|
|
993
1032
|
rank: number;
|
|
994
|
-
description:
|
|
1033
|
+
description: RecallEntryDescription;
|
|
995
1034
|
read_path?: string;
|
|
996
1035
|
store?: {
|
|
997
1036
|
alias: string;
|
|
@@ -1147,4 +1186,4 @@ interface ShutdownHandlerDeps {
|
|
|
1147
1186
|
*/
|
|
1148
1187
|
declare function createShutdownHandler(deps: ShutdownHandlerDeps): () => void;
|
|
1149
1188
|
|
|
1150
|
-
export { AGENTS_MD_RESOURCE_URI, type AlwaysActiveBody, type ArchiveHistoryEntry, type ArchiveHistoryReport, COLD_EVAL_RUBRIC, type CiteCoverageReport, type ColdEvalBatch, type ColdEvalCandidate, type ColdEvalVerdict, type ConflictEntry, type ConflictJudge, type ConflictLintReport, type ConflictPair, type ConflictVerdict, type ConsumptionEntry, type ConsumptionInspection, type ConsumptionLintConfig, DEFAULT_CONFLICT_SIMILARITY_THRESHOLD, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, FABRIC_SERVER_INSTRUCTIONS, type HistoryAllReport, type HistoryDayRow, type InFlightTracker, type KnowledgeCensus, LEDGER_PATH, LEGACY_LEDGER_PATH, METRICS_LEDGER_PATH, METRIC_COUNTER_NAMES, type MetricCounterName, type MetricsRow, OPTIONAL_EMBED_PACKAGE, type PlanContextInput, type PlanContextResult, type PrecheckResult, RETIRED_TOKENS, type RecallInput, type RecallResult, type RelatedBrokenLink, type RelatedGraphInspection, type RelatedGraphNode, type RelatedHubEntry, type RequirementProfile, type RetiredReferenceHit, type RetiredReferenceInspection, type RetiredToken, type SelectionTokenState, type ShutdownHandlerDeps, type StoreCanonicalEntry, type StoreReachability, type SurfaceVerdict, type UnboundProjectViolation, type WhyNotSurfacedResult, aggregateConsumption, appendEventLedgerEvent, buildAlwaysActiveBodies, buildColdEvalBatch, buildKnowledgeCensus, buildRelatedGraph, bumpCounter, clearPrecheckCache, collectStoreCanonicalEntries, contextCache, createFabricServer, createInFlightTracker, createShutdownHandler, defaultEmbedCacheDir, detectUnboundProject, drainCounters, enrichDescriptions, evaluateStoreDir, explainWhyNotSurfaced, extractKnowledge, findConflictCandidates, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, inspectConsumption, inspectRelatedGraph, inspectRetiredReferences, isEmbedderResolvable, lintConflicts, loadConflictEntries, loadEmbedder, pairSimilarity, planContext, precheckStoreReachability, readEmbedConfig, readEventLedger, readFusion, readLedger, readMetrics, readSelectionToken, recall, rehydrateAgentsMetaAt, resolveLedgerPaths, reviewKnowledge, reviewPending, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorConflictLint, runDoctorFix, runDoctorHistoryAll, runDoctorReport, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick };
|
|
1189
|
+
export { AGENTS_MD_RESOURCE_URI, type AlwaysActiveBody, type ArchiveHistoryEntry, type ArchiveHistoryReport, COLD_EVAL_RUBRIC, type CiteCoverageReport, type ColdEvalBatch, type ColdEvalCandidate, type ColdEvalVerdict, type ConflictEntry, type ConflictJudge, type ConflictLintReport, type ConflictPair, type ConflictVerdict, type ConsumptionEntry, type ConsumptionInspection, type ConsumptionLintConfig, DEFAULT_CONFLICT_SIMILARITY_THRESHOLD, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, FABRIC_SERVER_INSTRUCTIONS, type HistoryAllReport, type HistoryDayRow, type InFlightTracker, type KnowledgeCensus, LEDGER_PATH, LEGACY_LEDGER_PATH, METRICS_LEDGER_PATH, METRIC_COUNTER_NAMES, type MetricCounterName, type MetricsRow, OPTIONAL_EMBED_PACKAGE, type PlanContextInput, type PlanContextResult, type PrecheckResult, RETIRED_TOKENS, type RecallInput, type RecallResult, type RelatedBrokenLink, type RelatedGraphInspection, type RelatedGraphNode, type RelatedGraphNodeRich, type RelatedHubEntry, type RequirementProfile, type RetiredReferenceHit, type RetiredReferenceInspection, type RetiredToken, type SelectionTokenState, type ShutdownHandlerDeps, type StoreCanonicalEntry, type StoreReachability, type SuggestedRelatedEdge, type SurfaceVerdict, type UnboundProjectViolation, type WhyNotSurfacedResult, aggregateConsumption, appendEventLedgerEvent, buildAlwaysActiveBodies, buildColdEvalBatch, buildKnowledgeCensus, buildRelatedGraph, bumpCounter, clearPrecheckCache, collectStoreCanonicalEntries, contextCache, createFabricServer, createInFlightTracker, createShutdownHandler, defaultEmbedCacheDir, detectUnboundProject, drainCounters, enrichDescriptions, evaluateStoreDir, explainWhyNotSurfaced, extractKnowledge, findConflictCandidates, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, inspectConsumption, inspectRelatedGraph, inspectRetiredReferences, inspectSuggestedRelatedEdges, isEmbedderResolvable, lintConflicts, loadConflictEntries, loadEmbedder, pairSimilarity, planContext, precheckStoreReachability, readEmbedConfig, readEventLedger, readFusion, readLedger, readMetrics, readSelectionToken, recall, rehydrateAgentsMetaAt, resolveLedgerPaths, reviewKnowledge, reviewPending, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorConflictLint, runDoctorFix, runDoctorHistoryAll, runDoctorReport, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick, suggestRelatedEdges };
|
package/dist/index.js
CHANGED
|
@@ -817,6 +817,64 @@ function readOrphanDemoteThresholdDays(projectRoot) {
|
|
|
817
817
|
return {};
|
|
818
818
|
}
|
|
819
819
|
}
|
|
820
|
+
function readCredibilityHalfLives(projectRoot) {
|
|
821
|
+
const defaults = {
|
|
822
|
+
decisions: 180,
|
|
823
|
+
guidelines: 150,
|
|
824
|
+
models: 150,
|
|
825
|
+
pitfalls: 120,
|
|
826
|
+
processes: 120
|
|
827
|
+
};
|
|
828
|
+
try {
|
|
829
|
+
const cfg = readFabricConfig(projectRoot);
|
|
830
|
+
const validate = (v) => {
|
|
831
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 1 || v > 3650 || !Number.isInteger(v)) {
|
|
832
|
+
return void 0;
|
|
833
|
+
}
|
|
834
|
+
return v;
|
|
835
|
+
};
|
|
836
|
+
const out = { ...defaults };
|
|
837
|
+
const dec = validate(cfg.credibility_half_life_decisions_days);
|
|
838
|
+
if (dec !== void 0) out.decisions = dec;
|
|
839
|
+
const gui = validate(cfg.credibility_half_life_guidelines_days);
|
|
840
|
+
if (gui !== void 0) out.guidelines = gui;
|
|
841
|
+
const mod = validate(cfg.credibility_half_life_models_days);
|
|
842
|
+
if (mod !== void 0) out.models = mod;
|
|
843
|
+
const pit = validate(cfg.credibility_half_life_pitfalls_days);
|
|
844
|
+
if (pit !== void 0) out.pitfalls = pit;
|
|
845
|
+
const pro = validate(cfg.credibility_half_life_processes_days);
|
|
846
|
+
if (pro !== void 0) out.processes = pro;
|
|
847
|
+
return out;
|
|
848
|
+
} catch {
|
|
849
|
+
return { ...defaults };
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function readCredibilityFloors(projectRoot) {
|
|
853
|
+
const defaults = {
|
|
854
|
+
draft: 0.4,
|
|
855
|
+
verified: 0.55,
|
|
856
|
+
proven: 0.7
|
|
857
|
+
};
|
|
858
|
+
try {
|
|
859
|
+
const cfg = readFabricConfig(projectRoot);
|
|
860
|
+
const validate = (v) => {
|
|
861
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 0 || v > 1) {
|
|
862
|
+
return void 0;
|
|
863
|
+
}
|
|
864
|
+
return v;
|
|
865
|
+
};
|
|
866
|
+
const out = { ...defaults };
|
|
867
|
+
const draft = validate(cfg.credibility_floor_draft);
|
|
868
|
+
if (draft !== void 0) out.draft = draft;
|
|
869
|
+
const verified = validate(cfg.credibility_floor_verified);
|
|
870
|
+
if (verified !== void 0) out.verified = verified;
|
|
871
|
+
const proven = validate(cfg.credibility_floor_proven);
|
|
872
|
+
if (proven !== void 0) out.proven = proven;
|
|
873
|
+
return out;
|
|
874
|
+
} catch {
|
|
875
|
+
return { ...defaults };
|
|
876
|
+
}
|
|
877
|
+
}
|
|
820
878
|
function readFusion(projectRoot) {
|
|
821
879
|
try {
|
|
822
880
|
const raw = readFabricConfig(projectRoot).fusion;
|
|
@@ -1109,7 +1167,9 @@ function extractRuleDescription(source) {
|
|
|
1109
1167
|
relevance_scope: knowledge?.relevance_scope ?? "broad",
|
|
1110
1168
|
relevance_paths: knowledge?.relevance_paths ?? [],
|
|
1111
1169
|
// v2.2 H2-related (W1-T7): graph edges, undefined when absent.
|
|
1112
|
-
related: knowledge?.related
|
|
1170
|
+
related: knowledge?.related,
|
|
1171
|
+
// v2.2 glossary aliases FIELD (C-002): synonym terms, undefined when absent.
|
|
1172
|
+
aliases: knowledge?.aliases
|
|
1113
1173
|
};
|
|
1114
1174
|
}
|
|
1115
1175
|
function extractDescriptionFromFrontmatter(frontmatter) {
|
|
@@ -1133,7 +1193,9 @@ function extractDescriptionFromFrontmatter(frontmatter) {
|
|
|
1133
1193
|
relevance_scope: knowledge.relevance_scope,
|
|
1134
1194
|
relevance_paths: knowledge.relevance_paths,
|
|
1135
1195
|
// v2.2 H2-related (W1-T7): graph edges parsed from frontmatter.
|
|
1136
|
-
related: knowledge.related
|
|
1196
|
+
related: knowledge.related,
|
|
1197
|
+
// v2.2 glossary aliases FIELD (C-002): synonym terms for the BM25 body.
|
|
1198
|
+
aliases: knowledge.aliases
|
|
1137
1199
|
};
|
|
1138
1200
|
}
|
|
1139
1201
|
function isForbiddenCrossLayerEdge(sourceLayer, targetId) {
|
|
@@ -1222,6 +1284,7 @@ function extractKnowledgeFieldsFromFrontmatter(frontmatter) {
|
|
|
1222
1284
|
}
|
|
1223
1285
|
return true;
|
|
1224
1286
|
});
|
|
1287
|
+
const aliases = extractInlineArray(frontmatter, "aliases");
|
|
1225
1288
|
return {
|
|
1226
1289
|
id,
|
|
1227
1290
|
knowledge_type,
|
|
@@ -1230,7 +1293,8 @@ function extractKnowledgeFieldsFromFrontmatter(frontmatter) {
|
|
|
1230
1293
|
tags: tags.length > 0 ? tags : void 0,
|
|
1231
1294
|
relevance_scope,
|
|
1232
1295
|
relevance_paths,
|
|
1233
|
-
related: related.length > 0 ? related : void 0
|
|
1296
|
+
related: related.length > 0 ? related : void 0,
|
|
1297
|
+
aliases: aliases.length > 0 ? aliases : void 0
|
|
1234
1298
|
};
|
|
1235
1299
|
}
|
|
1236
1300
|
function extractScalar(frontmatter, key) {
|
|
@@ -3491,7 +3555,11 @@ async function buildScoringContext(projectRoot, revision, rawItems, opts) {
|
|
|
3491
3555
|
const scoringContext = {
|
|
3492
3556
|
nowMs: Date.now(),
|
|
3493
3557
|
targetPaths: opts.targetPaths,
|
|
3494
|
-
queryTerms: buildQueryTerms(opts.queryText)
|
|
3558
|
+
queryTerms: buildQueryTerms(opts.queryText),
|
|
3559
|
+
// PLN-004 F1: resolve the credibility half-lives + floors ONCE here (never per
|
|
3560
|
+
// candidate) so credibilityFactor is a pure lookup on the ranking hot path.
|
|
3561
|
+
credibilityHalfLives: readCredibilityHalfLives(projectRoot),
|
|
3562
|
+
credibilityFloors: readCredibilityFloors(projectRoot)
|
|
3495
3563
|
};
|
|
3496
3564
|
const docTexts = /* @__PURE__ */ new Map();
|
|
3497
3565
|
for (const item of rawItems) {
|
|
@@ -3620,14 +3688,21 @@ function documentTextForItem(description) {
|
|
|
3620
3688
|
...description.intent_clues,
|
|
3621
3689
|
...description.tech_stack,
|
|
3622
3690
|
...description.impact,
|
|
3623
|
-
...description.tags ?? []
|
|
3691
|
+
...description.tags ?? [],
|
|
3692
|
+
// v2.2 glossary aliases (C-002): long-tail synonym terms feed the flat
|
|
3693
|
+
// vector-embedding document verbatim, same as the BM25F summary slot below.
|
|
3694
|
+
...description.aliases ?? []
|
|
3624
3695
|
].join(" ");
|
|
3625
3696
|
}
|
|
3626
3697
|
function documentFieldsForItem(description) {
|
|
3627
3698
|
return {
|
|
3628
3699
|
title: tokenize2(description.summary),
|
|
3629
3700
|
tags: tokenize2([...description.tags ?? [], ...description.tech_stack].join(" ")),
|
|
3630
|
-
summary: tokenize2(
|
|
3701
|
+
summary: tokenize2(
|
|
3702
|
+
[description.must_read_if, ...description.intent_clues, ...description.aliases ?? []].join(
|
|
3703
|
+
" "
|
|
3704
|
+
)
|
|
3705
|
+
),
|
|
3631
3706
|
body: tokenize2(description.impact.join(" "))
|
|
3632
3707
|
};
|
|
3633
3708
|
}
|
|
@@ -3716,11 +3791,28 @@ function localityBoost(item, context) {
|
|
|
3716
3791
|
function structuralScaleFor(context) {
|
|
3717
3792
|
return context.fusion === "rrf" && context.queryTerms.length > 0 ? RRF_STRUCTURAL_SCALE : 1;
|
|
3718
3793
|
}
|
|
3794
|
+
function credibilityFactor(item, context) {
|
|
3795
|
+
const halfLives = context.credibilityHalfLives;
|
|
3796
|
+
const floors = context.credibilityFloors;
|
|
3797
|
+
if (halfLives === void 0 || floors === void 0) return 1;
|
|
3798
|
+
const createdAtRaw = item.description?.created_at;
|
|
3799
|
+
if (typeof createdAtRaw !== "string" || createdAtRaw.length === 0) return 1;
|
|
3800
|
+
const createdMs = Date.parse(createdAtRaw);
|
|
3801
|
+
if (!Number.isFinite(createdMs)) return 1;
|
|
3802
|
+
const ageDays = (context.nowMs - createdMs) / (24 * 60 * 60 * 1e3);
|
|
3803
|
+
if (ageDays <= 0) return 1;
|
|
3804
|
+
const type = item.description?.knowledge_type;
|
|
3805
|
+
const halfLife = type !== void 0 ? halfLives[type] : halfLives.decisions;
|
|
3806
|
+
const factor = Math.pow(2, -ageDays / halfLife);
|
|
3807
|
+
const maturity = item.description?.maturity;
|
|
3808
|
+
const floor = maturity !== void 0 ? floors[maturity] : floors.draft;
|
|
3809
|
+
return Math.max(floor, Math.min(1, factor));
|
|
3810
|
+
}
|
|
3719
3811
|
function scoreDescriptionItem(item, context) {
|
|
3720
3812
|
const content = contentScore(item, context);
|
|
3721
3813
|
const structural = salienceScore(item) + recencyBoost(item, context) + localityBoost(item, context);
|
|
3722
3814
|
const proximity = proximityBoost(item, context, content);
|
|
3723
|
-
return content + structuralScaleFor(context) * structural + proximity;
|
|
3815
|
+
return (content + structuralScaleFor(context) * structural + proximity) * credibilityFactor(item, context);
|
|
3724
3816
|
}
|
|
3725
3817
|
function scoreBreakdownForItem(item, context) {
|
|
3726
3818
|
const hasQuery = context.queryTerms.length > 0;
|
|
@@ -3742,7 +3834,9 @@ function scoreBreakdownForItem(item, context) {
|
|
|
3742
3834
|
const salience = salienceScore(item) * scale;
|
|
3743
3835
|
const recency = recencyBoost(item, context) * scale;
|
|
3744
3836
|
const locality = localityBoost(item, context) * scale;
|
|
3745
|
-
const
|
|
3837
|
+
const proximity = proximityBoost(item, context, bm25 + vector);
|
|
3838
|
+
const credibility = credibilityFactor(item, context);
|
|
3839
|
+
const final = (bm25 + vector + salience + recency + locality + proximity) * credibility;
|
|
3746
3840
|
return {
|
|
3747
3841
|
final,
|
|
3748
3842
|
...bm25 !== 0 ? { bm25 } : {},
|
|
@@ -3751,7 +3845,9 @@ function scoreBreakdownForItem(item, context) {
|
|
|
3751
3845
|
...vectorRank !== void 0 ? { vector_rank: vectorRank } : {},
|
|
3752
3846
|
salience,
|
|
3753
3847
|
recency,
|
|
3754
|
-
locality
|
|
3848
|
+
locality,
|
|
3849
|
+
proximity,
|
|
3850
|
+
credibility
|
|
3755
3851
|
};
|
|
3756
3852
|
}
|
|
3757
3853
|
function localityTier(relevancePath, targetPath) {
|
|
@@ -3848,7 +3944,7 @@ async function recall(projectRoot, input) {
|
|
|
3848
3944
|
return {
|
|
3849
3945
|
stable_id: c.stable_id,
|
|
3850
3946
|
rank: index + 1,
|
|
3851
|
-
description: c.description,
|
|
3947
|
+
description: slimDescription(c.description),
|
|
3852
3948
|
...readPath ? { read_path: readPath.path } : {},
|
|
3853
3949
|
...readPath?.store ? { store: readPath.store } : {},
|
|
3854
3950
|
...isAlwaysActive(c) ? { body_in_context: true } : {},
|
|
@@ -3868,6 +3964,14 @@ function isAlwaysActive(candidate) {
|
|
|
3868
3964
|
const { relevance_scope, knowledge_type } = candidate.description;
|
|
3869
3965
|
return (relevance_scope ?? "broad") !== "narrow" && ALWAYS_ACTIVE_TYPES2.has(knowledge_type ?? "");
|
|
3870
3966
|
}
|
|
3967
|
+
function slimDescription(d) {
|
|
3968
|
+
return {
|
|
3969
|
+
summary: d.summary,
|
|
3970
|
+
must_read_if: d.must_read_if,
|
|
3971
|
+
intent_clues: d.intent_clues,
|
|
3972
|
+
...d.knowledge_type !== void 0 ? { knowledge_type: d.knowledge_type } : {}
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3871
3975
|
function buildNextSteps(planResult, paths, candidateById, candidateLookup) {
|
|
3872
3976
|
const nextSteps = [];
|
|
3873
3977
|
const omitted = (planResult.dropped ?? []).filter((d) => d.reason === "retrieval_budget").length;
|
|
@@ -10983,6 +11087,7 @@ function buildColdEvalBatch(candidates) {
|
|
|
10983
11087
|
}
|
|
10984
11088
|
|
|
10985
11089
|
// src/services/doctor-related-graph.ts
|
|
11090
|
+
import { tokenize as tokenize3 } from "@fenglimg/fabric-shared";
|
|
10986
11091
|
function buildRelatedGraph(nodes) {
|
|
10987
11092
|
const brokenLinks = [];
|
|
10988
11093
|
const inDegree = /* @__PURE__ */ new Map();
|
|
@@ -11023,6 +11128,66 @@ function extractBareStableId(qualifiedId) {
|
|
|
11023
11128
|
}
|
|
11024
11129
|
return null;
|
|
11025
11130
|
}
|
|
11131
|
+
var SUGGEST_CONFIDENCE_THRESHOLD = 0.6;
|
|
11132
|
+
var TAG_OVERLAP_BONUS = 0.15;
|
|
11133
|
+
var PATH_OVERLAP_BONUS = 0.15;
|
|
11134
|
+
function suggestRelatedEdges(nodes) {
|
|
11135
|
+
const pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
11136
|
+
const prepared = nodes.map((node) => ({
|
|
11137
|
+
qualifiedId: node.qualifiedId,
|
|
11138
|
+
bareId: extractBareStableId(node.qualifiedId) ?? node.qualifiedId,
|
|
11139
|
+
tokens: new Set(tokenize3(`${node.summary} ${node.intentClues.join(" ")}`)),
|
|
11140
|
+
tags: new Set(node.tags),
|
|
11141
|
+
paths: new Set(node.relevancePaths)
|
|
11142
|
+
}));
|
|
11143
|
+
const existing = /* @__PURE__ */ new Set();
|
|
11144
|
+
for (const node of nodes) {
|
|
11145
|
+
const aBare = extractBareStableId(node.qualifiedId) ?? node.qualifiedId;
|
|
11146
|
+
for (const rel of node.related) {
|
|
11147
|
+
existing.add(pairKey(aBare, extractBareStableId(rel) ?? rel));
|
|
11148
|
+
}
|
|
11149
|
+
}
|
|
11150
|
+
const out = [];
|
|
11151
|
+
for (let i = 0; i < prepared.length; i++) {
|
|
11152
|
+
for (let j = i + 1; j < prepared.length; j++) {
|
|
11153
|
+
const a = prepared[i];
|
|
11154
|
+
const b = prepared[j];
|
|
11155
|
+
if (a.bareId === b.bareId) continue;
|
|
11156
|
+
if (existing.has(pairKey(a.bareId, b.bareId))) continue;
|
|
11157
|
+
let intersection = 0;
|
|
11158
|
+
for (const t of a.tokens) if (b.tokens.has(t)) intersection++;
|
|
11159
|
+
const union = a.tokens.size + b.tokens.size - intersection;
|
|
11160
|
+
const jaccard = union === 0 ? 0 : intersection / union;
|
|
11161
|
+
let tagOverlap = false;
|
|
11162
|
+
for (const t of a.tags)
|
|
11163
|
+
if (b.tags.has(t)) {
|
|
11164
|
+
tagOverlap = true;
|
|
11165
|
+
break;
|
|
11166
|
+
}
|
|
11167
|
+
let pathOverlap = false;
|
|
11168
|
+
for (const p of a.paths)
|
|
11169
|
+
if (b.paths.has(p)) {
|
|
11170
|
+
pathOverlap = true;
|
|
11171
|
+
break;
|
|
11172
|
+
}
|
|
11173
|
+
let confidence = jaccard;
|
|
11174
|
+
if (tagOverlap) confidence += TAG_OVERLAP_BONUS;
|
|
11175
|
+
if (pathOverlap) confidence += PATH_OVERLAP_BONUS;
|
|
11176
|
+
confidence = Math.min(1, confidence);
|
|
11177
|
+
if (confidence < SUGGEST_CONFIDENCE_THRESHOLD) continue;
|
|
11178
|
+
const provenance = [];
|
|
11179
|
+
if (jaccard > 0) provenance.push("token-jaccard");
|
|
11180
|
+
if (tagOverlap) provenance.push("tag-overlap");
|
|
11181
|
+
if (pathOverlap) provenance.push("shared-path");
|
|
11182
|
+
const [source, target] = a.qualifiedId < b.qualifiedId ? [a.qualifiedId, b.qualifiedId] : [b.qualifiedId, a.qualifiedId];
|
|
11183
|
+
out.push({ source, target, confidence, provenance });
|
|
11184
|
+
}
|
|
11185
|
+
}
|
|
11186
|
+
out.sort(
|
|
11187
|
+
(x, y) => y.confidence - x.confidence || x.source.localeCompare(y.source) || x.target.localeCompare(y.target)
|
|
11188
|
+
);
|
|
11189
|
+
return out;
|
|
11190
|
+
}
|
|
11026
11191
|
async function inspectRelatedGraph(projectRoot) {
|
|
11027
11192
|
const entries = await collectStoreCanonicalEntries(projectRoot);
|
|
11028
11193
|
return buildRelatedGraph(
|
|
@@ -11032,6 +11197,24 @@ async function inspectRelatedGraph(projectRoot) {
|
|
|
11032
11197
|
}))
|
|
11033
11198
|
);
|
|
11034
11199
|
}
|
|
11200
|
+
var SUGGESTED_EDGES_TOP_N = 20;
|
|
11201
|
+
async function inspectSuggestedRelatedEdges(projectRoot) {
|
|
11202
|
+
let entries;
|
|
11203
|
+
try {
|
|
11204
|
+
entries = await collectStoreCanonicalEntries(projectRoot);
|
|
11205
|
+
} catch {
|
|
11206
|
+
return [];
|
|
11207
|
+
}
|
|
11208
|
+
const nodes = entries.map((entry) => ({
|
|
11209
|
+
qualifiedId: entry.qualifiedId,
|
|
11210
|
+
summary: entry.description.summary ?? "",
|
|
11211
|
+
intentClues: entry.description.intent_clues ?? [],
|
|
11212
|
+
tags: entry.description.tags ?? [],
|
|
11213
|
+
relevancePaths: entry.description.relevance_paths ?? [],
|
|
11214
|
+
related: entry.description.related ?? []
|
|
11215
|
+
}));
|
|
11216
|
+
return suggestRelatedEdges(nodes).slice(0, SUGGESTED_EDGES_TOP_N);
|
|
11217
|
+
}
|
|
11035
11218
|
|
|
11036
11219
|
// src/services/store-precheck.ts
|
|
11037
11220
|
import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
|
|
@@ -11513,7 +11696,7 @@ function createFabricServer(tracker) {
|
|
|
11513
11696
|
const server = new McpServer(
|
|
11514
11697
|
{
|
|
11515
11698
|
name: "fabric-knowledge-server",
|
|
11516
|
-
version: "2.3.0-rc.
|
|
11699
|
+
version: "2.3.0-rc.7"
|
|
11517
11700
|
},
|
|
11518
11701
|
{
|
|
11519
11702
|
instructions: FABRIC_SERVER_INSTRUCTIONS
|
|
@@ -11667,6 +11850,7 @@ export {
|
|
|
11667
11850
|
inspectConsumption,
|
|
11668
11851
|
inspectRelatedGraph,
|
|
11669
11852
|
inspectRetiredReferences,
|
|
11853
|
+
inspectSuggestedRelatedEdges,
|
|
11670
11854
|
isEmbedderResolvable,
|
|
11671
11855
|
lintConflicts,
|
|
11672
11856
|
loadConflictEntries,
|
|
@@ -11696,5 +11880,6 @@ export {
|
|
|
11696
11880
|
startRotationTick,
|
|
11697
11881
|
startStdioServer,
|
|
11698
11882
|
stopMetricsFlush,
|
|
11699
|
-
stopRotationTick
|
|
11883
|
+
stopRotationTick,
|
|
11884
|
+
suggestRelatedEdges
|
|
11700
11885
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-server",
|
|
3
|
-
"version": "2.3.0-rc.
|
|
3
|
+
"version": "2.3.0-rc.7",
|
|
4
4
|
"description": "Fabric MCP knowledge server — stdio transport for Claude Code / Codex CLI, manages .fabric/ knowledge base + agents.meta.json + event ledger.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wangzhichao <fenglimg90@gmail.com>",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
38
|
"minimatch": "^10.0.1",
|
|
39
39
|
"zod": "^3.25.0",
|
|
40
|
-
"@fenglimg/fabric-shared": "2.3.0-rc.
|
|
40
|
+
"@fenglimg/fabric-shared": "2.3.0-rc.7"
|
|
41
41
|
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
43
|
"fastembed": "^2.0.0"
|