@fenglimg/fabric-server 2.3.0-rc.6 → 2.3.0-rc.8
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 +3 -1
- package/dist/index.js +115 -11
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -988,10 +988,12 @@ type RecallInput = PlanContextInput & {
|
|
|
988
988
|
*/
|
|
989
989
|
include_related?: boolean;
|
|
990
990
|
};
|
|
991
|
+
type FullRuleDescription = PlanContextResult["candidates"][number]["description"];
|
|
992
|
+
type RecallEntryDescription = Pick<FullRuleDescription, "summary" | "must_read_if" | "intent_clues"> & Partial<Pick<FullRuleDescription, "knowledge_type">>;
|
|
991
993
|
type RecallEntry = {
|
|
992
994
|
stable_id: string;
|
|
993
995
|
rank: number;
|
|
994
|
-
description:
|
|
996
|
+
description: RecallEntryDescription;
|
|
995
997
|
read_path?: string;
|
|
996
998
|
store?: {
|
|
997
999
|
alias: string;
|
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;
|
|
@@ -11513,7 +11617,7 @@ function createFabricServer(tracker) {
|
|
|
11513
11617
|
const server = new McpServer(
|
|
11514
11618
|
{
|
|
11515
11619
|
name: "fabric-knowledge-server",
|
|
11516
|
-
version: "2.3.0-rc.
|
|
11620
|
+
version: "2.3.0-rc.8"
|
|
11517
11621
|
},
|
|
11518
11622
|
{
|
|
11519
11623
|
instructions: FABRIC_SERVER_INSTRUCTIONS
|
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.8",
|
|
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.8"
|
|
41
41
|
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
43
|
"fastembed": "^2.0.0"
|