@fenglimg/fabric-server 2.2.0 → 2.3.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +113 -21
- package/dist/index.js +2483 -1612
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Fabric MCP knowledge server. Runs over stdio transport and serves Claude Code an
|
|
|
6
6
|
|
|
7
7
|
- `fab_recall` — single-step recall: returns candidate descriptions + native read paths (no body delivery over MCP; read a body on demand via a native Read of the returned path)
|
|
8
8
|
- `fab_archive_scan` — scan recent work for archive-worthy knowledge candidates
|
|
9
|
-
- `
|
|
9
|
+
- `fab_propose` — persist a pending knowledge entry
|
|
10
10
|
- `fab_review` — list / approve / reject / modify / defer pending entries
|
|
11
11
|
|
|
12
12
|
## Install
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { FabExtractKnowledgeInput, FabExtractKnowledgeOutput, FabReviewInput, FabReviewOutput } from '@fenglimg/fabric-shared/schemas/api-contracts';
|
|
3
|
-
import { EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem, LedgerEntry, AgentsMeta } from '@fenglimg/fabric-shared';
|
|
2
|
+
import { FabExtractKnowledgeInput, FabExtractKnowledgeOutput, FabReviewInput, FabReviewOutput, FabPendingInput, FabPendingOutput } from '@fenglimg/fabric-shared/schemas/api-contracts';
|
|
3
|
+
import { EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem, RecallScore, RecallScoreBreakdown, LedgerEntry, AgentsMeta } from '@fenglimg/fabric-shared';
|
|
4
4
|
import { PayloadGuardOptions } from '@fenglimg/fabric-shared/node/mcp-payload-guard';
|
|
5
5
|
|
|
6
6
|
interface InFlightTracker {
|
|
@@ -379,24 +379,49 @@ declare function enrichDescriptions(projectRoot: string, opts?: {
|
|
|
379
379
|
dryRun?: boolean;
|
|
380
380
|
}): Promise<EnrichDescriptionsReport>;
|
|
381
381
|
|
|
382
|
+
type Bm25Field = "title" | "summary" | "tags" | "body";
|
|
382
383
|
interface Bm25Document {
|
|
383
384
|
id: string;
|
|
384
|
-
|
|
385
|
+
/** Pre-tokenized terms per field. Omitted/empty fields contribute nothing. */
|
|
386
|
+
fields: Record<Bm25Field, string[]>;
|
|
385
387
|
}
|
|
386
388
|
interface Bm25Model {
|
|
387
389
|
/**
|
|
388
|
-
*
|
|
389
|
-
* Returns 0 for an unknown id,
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
390
|
+
* BM25F score of document `id` against the (pre-tokenized) query terms.
|
|
391
|
+
* Returns 0 for an unknown id, no query terms, or no term overlap in any
|
|
392
|
+
* field. Query-term duplicates are collapsed — repeating a term in the query
|
|
393
|
+
* does not inflate the score (term frequency is a document property, not a
|
|
394
|
+
* query property).
|
|
393
395
|
*/
|
|
394
396
|
scoreDoc(id: string, queryTerms: string[]): number;
|
|
397
|
+
/**
|
|
398
|
+
* P1 recall-engine-refactor (TASK-002): the plain JSON-safe snapshot of the
|
|
399
|
+
* corpus statistics this model scores against, attached so the model can be
|
|
400
|
+
* serialized to disk (`serializeBm25Model`) and a cold hook can `rehydrate`
|
|
401
|
+
* an identical scorer without re-tokenizing the corpus.
|
|
402
|
+
*/
|
|
403
|
+
readonly __serialized: SerializedBm25Model;
|
|
404
|
+
}
|
|
405
|
+
interface SerializedBm25Model {
|
|
406
|
+
/** Schema/format version — bump on any layout change so a stale on-disk cache
|
|
407
|
+
* (different shape) is detected and discarded rather than mis-rehydrated. */
|
|
408
|
+
version: 1;
|
|
409
|
+
totalDocs: number;
|
|
410
|
+
/** term → document frequency (union-of-fields df), as [term, count] pairs. */
|
|
411
|
+
documentFrequency: [string, number][];
|
|
412
|
+
/** Per-field corpus average length. */
|
|
413
|
+
avgFieldLength: Record<Bm25Field, number>;
|
|
414
|
+
/** Per-doc stats: id → { per-field [term,freq] pairs, per-field length }. */
|
|
415
|
+
perDoc: {
|
|
416
|
+
id: string;
|
|
417
|
+
fieldTermFreq: Record<Bm25Field, [string, number][]>;
|
|
418
|
+
fieldLength: Record<Bm25Field, number>;
|
|
419
|
+
}[];
|
|
395
420
|
}
|
|
396
421
|
/**
|
|
397
|
-
* Build a
|
|
398
|
-
*
|
|
399
|
-
* terms) per call.
|
|
422
|
+
* Build a BM25F model over `docs`. Corpus statistics (per-field document
|
|
423
|
+
* frequency over the union of fields, per-field average length) are computed
|
|
424
|
+
* once here; `scoreDoc` is then O(query terms × fields) per call.
|
|
400
425
|
*/
|
|
401
426
|
declare function buildBm25Model(docs: Bm25Document[]): Bm25Model;
|
|
402
427
|
|
|
@@ -505,8 +530,51 @@ declare function runDoctorConflictLint(projectRoot: string, opts?: {
|
|
|
505
530
|
judge?: ConflictJudge;
|
|
506
531
|
}): Promise<ConflictLintReport>;
|
|
507
532
|
|
|
533
|
+
interface RetiredToken {
|
|
534
|
+
/** The exact dead token as it appears in agent-facing text. */
|
|
535
|
+
token: string;
|
|
536
|
+
/** What replaced it (shown in the remediation), or null when simply removed. */
|
|
537
|
+
replacement: string | null;
|
|
538
|
+
/** Short why-retired note. */
|
|
539
|
+
reason: string;
|
|
540
|
+
}
|
|
541
|
+
declare const RETIRED_TOKENS: readonly RetiredToken[];
|
|
542
|
+
interface RetiredReferenceHit {
|
|
543
|
+
/** Project-relative POSIX path of the offending file. */
|
|
544
|
+
path: string;
|
|
545
|
+
token: string;
|
|
546
|
+
line: number;
|
|
547
|
+
replacement: string | null;
|
|
548
|
+
}
|
|
549
|
+
interface RetiredReferenceInspection {
|
|
550
|
+
status: "ok" | "warn" | "skipped";
|
|
551
|
+
scannedFiles: number;
|
|
552
|
+
hits: RetiredReferenceHit[];
|
|
553
|
+
}
|
|
554
|
+
declare function inspectRetiredReferences(projectRoot: string): Promise<RetiredReferenceInspection>;
|
|
555
|
+
|
|
556
|
+
type SurfaceVerdict = "not_found" | "store_unbound" | "project_mismatch" | "narrow_timing" | "should_surface";
|
|
557
|
+
interface WhyNotSurfacedResult {
|
|
558
|
+
/** The id exactly as queried. */
|
|
559
|
+
query: string;
|
|
560
|
+
/** Normalized LOCAL stable id (store-qualified `alias:ID` → `ID`). */
|
|
561
|
+
localId: string;
|
|
562
|
+
verdict: SurfaceVerdict;
|
|
563
|
+
/** Store the entry physically lives in, or null when not found. */
|
|
564
|
+
storeAlias: string | null;
|
|
565
|
+
/** Whether that store is in this project's read-set (null when not found). */
|
|
566
|
+
storeBound: boolean | null;
|
|
567
|
+
/** Entry's semantic_scope coordinate (audience axis), or null. */
|
|
568
|
+
semanticScope: string | null;
|
|
569
|
+
/** This repo's bound project coordinate segment, or null when unbound. */
|
|
570
|
+
activeProject: string | null;
|
|
571
|
+
/** Entry's relevance_scope (timing axis); defaults to "broad" when absent. */
|
|
572
|
+
relevanceScope: "broad" | "narrow" | null;
|
|
573
|
+
}
|
|
574
|
+
declare function explainWhyNotSurfaced(projectRoot: string, query: string): Promise<WhyNotSurfacedResult>;
|
|
575
|
+
|
|
508
576
|
/**
|
|
509
|
-
* Append-evidence-on-collision service for
|
|
577
|
+
* Append-evidence-on-collision service for fab_propose.
|
|
510
578
|
*
|
|
511
579
|
* Idempotency_key = sha256({source_session: source_sessions[0], type, slug}).
|
|
512
580
|
* The `source_session` key inside the hash payload is FROZEN for backward
|
|
@@ -523,10 +591,12 @@ declare function runDoctorConflictLint(projectRoot: string, opts?: {
|
|
|
523
591
|
declare function extractKnowledge(projectRoot: string, input: FabExtractKnowledgeInput): Promise<FabExtractKnowledgeOutput>;
|
|
524
592
|
|
|
525
593
|
/**
|
|
526
|
-
* v2.0 rc.3 fab_review service.
|
|
594
|
+
* v2.0 rc.3 fab_review service (W3-K K2: WRITE-only).
|
|
527
595
|
*
|
|
528
|
-
* Pure async dispatcher over a discriminated union of 6 actions (
|
|
529
|
-
* reject, modify,
|
|
596
|
+
* Pure async dispatcher over a discriminated union of 6 WRITE actions (approve,
|
|
597
|
+
* reject, modify, modify-content, modify-layer, defer). The two READ actions
|
|
598
|
+
* (list / search) were lifted out into `reviewPending` (the fab_pending tool) —
|
|
599
|
+
* pure relocation, ZERO behavior change.
|
|
530
600
|
*
|
|
531
601
|
* Approve performs late-bind id allocation (KP-/KT- + type-code + monotonic
|
|
532
602
|
* counter via KnowledgeIdAllocator), emits 2-phase events (knowledge_promote_started
|
|
@@ -540,6 +610,19 @@ declare function extractKnowledge(projectRoot: string, input: FabExtractKnowledg
|
|
|
540
610
|
* the file across layer roots, emits knowledge_layer_changed.
|
|
541
611
|
*/
|
|
542
612
|
declare function reviewKnowledge(projectRoot: string, input: FabReviewInput): Promise<FabReviewOutput>;
|
|
613
|
+
/**
|
|
614
|
+
* fab_pending service (W3-K K2: READ-only).
|
|
615
|
+
*
|
|
616
|
+
* Pure async dispatcher over the two READ actions (list / search) relocated
|
|
617
|
+
* from `reviewKnowledge`. list browses the store-backed pending backlog;
|
|
618
|
+
* search ranges over BOTH pending and canonical knowledge. Neither mutates
|
|
619
|
+
* state — the fab_pending tool is registered readOnlyHint:true / idempotentHint:true.
|
|
620
|
+
* P1 recall-engine-refactor (TASK-005): search now routes through `triageSearch`,
|
|
621
|
+
* which gates on the substring query then RANKS the matches via the shared
|
|
622
|
+
* rankDescriptionItems('triage') — NO top_k, NO floor, so pending review never
|
|
623
|
+
* silently drops a match. The old substring-only search machine is gone.
|
|
624
|
+
*/
|
|
625
|
+
declare function reviewPending(projectRoot: string, input: FabPendingInput): Promise<FabPendingOutput>;
|
|
543
626
|
|
|
544
627
|
/** A summary to be cold-judged, keyed by its stable_id. */
|
|
545
628
|
interface ColdEvalCandidate {
|
|
@@ -662,12 +745,16 @@ type PlanContextResult = {
|
|
|
662
745
|
entries: PlanContextEntry[];
|
|
663
746
|
intent?: string;
|
|
664
747
|
candidates: RuleDescriptionIndexItem[];
|
|
665
|
-
|
|
748
|
+
dropped?: {
|
|
749
|
+
id: string;
|
|
750
|
+
reason: "retrieval_budget" | "payload_budget";
|
|
751
|
+
}[];
|
|
666
752
|
preflight_diagnostics: PreflightDiagnostic[];
|
|
667
753
|
auto_healed?: boolean;
|
|
668
754
|
previous_revision_hash?: string;
|
|
669
755
|
redirects?: Record<string, string>;
|
|
670
756
|
related_appended?: Record<string, string>;
|
|
757
|
+
candidate_scores?: Map<string, RecallScore>;
|
|
671
758
|
/** Internal service→tool signal; stripped before MCP output. */
|
|
672
759
|
payload_trimmed?: boolean;
|
|
673
760
|
/** Internal service→tool signal; stripped before MCP output. */
|
|
@@ -703,15 +790,20 @@ type RecallInput = PlanContextInput & {
|
|
|
703
790
|
*/
|
|
704
791
|
include_related?: boolean;
|
|
705
792
|
};
|
|
706
|
-
type
|
|
793
|
+
type RecallEntry = {
|
|
707
794
|
stable_id: string;
|
|
708
|
-
|
|
795
|
+
rank: number;
|
|
796
|
+
description: PlanContextResult["candidates"][number]["description"];
|
|
797
|
+
read_path?: string;
|
|
709
798
|
store?: {
|
|
710
799
|
alias: string;
|
|
711
800
|
};
|
|
801
|
+
body_in_context?: boolean;
|
|
802
|
+
score?: number;
|
|
803
|
+
score_breakdown?: RecallScoreBreakdown;
|
|
712
804
|
};
|
|
713
|
-
type RecallResult = Omit<PlanContextResult, "selection_token" | "payload_trimmed" | "payload_over_budget"> & {
|
|
714
|
-
|
|
805
|
+
type RecallResult = Omit<PlanContextResult, "selection_token" | "payload_trimmed" | "payload_over_budget" | "entries" | "candidates" | "candidate_scores"> & {
|
|
806
|
+
entries: RecallEntry[];
|
|
715
807
|
directive: string;
|
|
716
808
|
next_steps?: string[];
|
|
717
809
|
};
|
|
@@ -857,4 +949,4 @@ interface ShutdownHandlerDeps {
|
|
|
857
949
|
*/
|
|
858
950
|
declare function createShutdownHandler(deps: ShutdownHandlerDeps): () => void;
|
|
859
951
|
|
|
860
|
-
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, 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, type PlanContextInput, type PlanContextResult, type RecallInput, type RecallResult, type RequirementProfile, type SelectionTokenState, type ShutdownHandlerDeps, type UnboundProjectViolation, appendEventLedgerEvent, buildAlwaysActiveBodies, buildColdEvalBatch, buildKnowledgeCensus, bumpCounter, contextCache, createFabricServer, createInFlightTracker, createShutdownHandler, detectUnboundProject, drainCounters, enrichDescriptions, extractKnowledge, findConflictCandidates, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, lintConflicts, loadConflictEntries, pairSimilarity, planContext, readEventLedger, readLedger, readMetrics, readSelectionToken, recall, rehydrateAgentsMetaAt, resolveLedgerPaths, reviewKnowledge, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorConflictLint, runDoctorFix, runDoctorHistoryAll, runDoctorReport, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick };
|
|
952
|
+
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, 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, type PlanContextInput, type PlanContextResult, RETIRED_TOKENS, type RecallInput, type RecallResult, type RequirementProfile, type RetiredReferenceHit, type RetiredReferenceInspection, type RetiredToken, type SelectionTokenState, type ShutdownHandlerDeps, type SurfaceVerdict, type UnboundProjectViolation, type WhyNotSurfacedResult, appendEventLedgerEvent, buildAlwaysActiveBodies, buildColdEvalBatch, buildKnowledgeCensus, bumpCounter, contextCache, createFabricServer, createInFlightTracker, createShutdownHandler, detectUnboundProject, drainCounters, enrichDescriptions, explainWhyNotSurfaced, extractKnowledge, findConflictCandidates, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, inspectRetiredReferences, lintConflicts, loadConflictEntries, pairSimilarity, planContext, readEventLedger, readLedger, readMetrics, readSelectionToken, recall, rehydrateAgentsMetaAt, resolveLedgerPaths, reviewKnowledge, reviewPending, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorConflictLint, runDoctorFix, runDoctorHistoryAll, runDoctorReport, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick };
|