@fenglimg/fabric-server 2.1.0-rc.2 → 2.2.0-rc.10

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { AgentsMeta, KnowledgeTestIndex, AgentsLayer, AgentsTopologyType, KnowledgeType, Layer, StableId, AgentsMetaCounters, EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem } from '@fenglimg/fabric-shared';
3
2
  import { FabExtractKnowledgeInput, FabExtractKnowledgeOutput, FabReviewInput, FabReviewOutput } from '@fenglimg/fabric-shared/schemas/api-contracts';
3
+ import { EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem, LedgerEntry, AgentsMeta } from '@fenglimg/fabric-shared';
4
+ import { PayloadGuardOptions } from '@fenglimg/fabric-shared/node/mcp-payload-guard';
4
5
 
5
6
  interface InFlightTracker {
6
7
  enter(requestId: string): void;
@@ -22,90 +23,147 @@ declare function getLegacyLedgerPath(projectRoot: string): string;
22
23
  declare function getEventLedgerPath(projectRoot: string): string;
23
24
  declare function getMetricsLedgerPath(projectRoot: string): string;
24
25
 
25
- type DoctorStatus = "ok" | "warn" | "error";
26
- type DoctorIssueKind = "fixable_error" | "manual_error" | "warning" | "info";
27
- type DoctorCheck = {
28
- name: string;
29
- status: DoctorStatus;
30
- message: string;
31
- kind?: DoctorIssueKind;
32
- code?: string;
33
- fixable?: boolean;
34
- actionHint?: string;
35
- audience?: "user" | "maintainer";
36
- };
37
- type DoctorIssue = {
38
- code: string;
39
- name: string;
40
- message: string;
41
- path?: string;
42
- actionHint?: string;
43
- audience?: "user" | "maintainer";
44
- };
45
- type DoctorPayloadLimits = {
46
- warn_bytes: number;
47
- hard_bytes: number;
48
- source: "default" | "config";
49
- };
50
- type DoctorSummary = {
51
- target: string;
52
- framework: {
53
- kind: string;
54
- version: string;
55
- subkind: string;
26
+ type DoctorHealth = {
27
+ score: number;
28
+ grade: "A" | "B" | "C" | "D" | "F";
29
+ penalties: {
30
+ manual_errors: number;
31
+ fixable_errors: number;
32
+ warnings: number;
56
33
  };
57
- entryPoints: Array<{
58
- path: string;
59
- reason: string;
60
- }>;
61
- metaRevision: string | null;
62
- computedMetaRevision: string | null;
63
- ruleCount: number;
64
- eventLedgerPath: string;
65
- fixableErrorCount: number;
66
- manualErrorCount: number;
67
- warningCount: number;
68
- infoCount: number;
69
- targetFiles: Record<string, boolean>;
70
- payload_limits: DoctorPayloadLimits;
71
34
  };
72
- type DoctorReport = {
73
- status: DoctorStatus;
74
- checks: DoctorCheck[];
75
- fixable_errors: DoctorIssue[];
76
- manual_errors: DoctorIssue[];
77
- warnings: DoctorIssue[];
78
- infos: DoctorIssue[];
79
- summary: DoctorSummary;
80
- };
81
- type DoctorFixReport = {
82
- changed: boolean;
83
- fixed: DoctorIssue[];
84
- remaining_manual_errors: DoctorIssue[];
85
- warnings: DoctorIssue[];
86
- message: string;
87
- report: DoctorReport;
88
- };
89
- type DoctorApplyLintMutationKind = "knowledge_orphan_demote_required" | "knowledge_stale_archive_required" | "knowledge_index_drift" | "knowledge_pending_auto_archive" | "knowledge_session_hints_stale_cleanup" | "knowledge_relevance_fields_missing";
90
- type DoctorApplyLintMutation = {
91
- kind: DoctorApplyLintMutationKind;
92
- path: string;
93
- detail: string;
94
- applied: boolean;
95
- error?: string;
35
+
36
+ interface AlwaysActiveBody {
37
+ /** store-qualified id (`<alias>:<stableId>`). */
38
+ stable_id: string;
39
+ /** knowledge_type — one of ALWAYS_ACTIVE_TYPES. */
40
+ type: string;
41
+ layer: "team" | "personal";
42
+ /** description.summary — the overflow-degrade fallback when the body cannot
43
+ * fit the injection char budget (the budget is enforced hook-side, D10). */
44
+ summary: string;
45
+ /** frontmatter-stripped markdown body. */
46
+ body: string;
47
+ }
48
+ /**
49
+ * Collect the always-active (guidelines + models) entries from the project's
50
+ * read-set, project-filtered identically to recall (filterByActiveProject), with
51
+ * their frontmatter-stripped bodies. The SessionStart hook injects these bodies
52
+ * into the AI context and renders category counts for the on-demand remainder.
53
+ *
54
+ * Returns [] (never throws) on any read-set resolution failure — the SessionStart
55
+ * banner must degrade gracefully, never crash session start.
56
+ */
57
+ interface KnowledgeCensus {
58
+ /** knowledge_type → count (decisions/pitfalls/guidelines/models/processes). */
59
+ by_type: Record<string, number>;
60
+ by_layer: {
61
+ team: number;
62
+ personal: number;
63
+ project: number;
64
+ };
65
+ broad_by_type: Record<string, number>;
66
+ /** count of narrow-scope kept entries (file-specific; only合计, not per-type). */
67
+ narrow_total: number;
68
+ /** entries专属 to OTHER projects that filterByActiveProject removed. */
69
+ dropped_other_project: number;
70
+ /** kept (post-filter) total. */
71
+ total: number;
72
+ }
73
+ /**
74
+ * Build the read-set census (project-filtered counts + dropped-other-project).
75
+ * Reuses the cached read-set walk, so calling alongside buildAlwaysActiveBodies
76
+ * in one SessionStart fire costs a single walk. Never throws — degrades to an
77
+ * all-zero census so the banner stays renderable.
78
+ */
79
+ declare function buildKnowledgeCensus(projectRoot: string): Promise<KnowledgeCensus>;
80
+ declare function buildAlwaysActiveBodies(projectRoot: string): Promise<AlwaysActiveBody[]>;
81
+
82
+ interface UnboundProjectViolation {
83
+ /** The store already bound as the active write target. */
84
+ alias: string;
85
+ /** Which project-scope fields are absent: `project_id` and/or `active_project`. */
86
+ missing: string[];
87
+ }
88
+ declare function detectUnboundProject(projectRoot: string): UnboundProjectViolation | null;
89
+
90
+ /**
91
+ * O(1) in-memory increment for a named counter. Safe to call from any MCP
92
+ * tool handler; no I/O happens until the next `flushMetrics()` call.
93
+ *
94
+ * `delta` defaults to 1; callers can pass a positive integer (e.g. fetched
95
+ * N stable_ids in a single sections call) to fold N bumps into one.
96
+ */
97
+ declare function bumpCounter(projectRoot: string, name: string, delta?: number): void;
98
+ /**
99
+ * Snapshot the current counter accumulator and reset it. Returned map is a
100
+ * frozen copy; the live accumulator starts fresh from zero. Exposed so
101
+ * flushMetrics + tests + a future fab_metrics manual-flush CLI hook can all
102
+ * use the same primitive without racing.
103
+ */
104
+ declare function drainCounters(projectRoot: string): Record<string, number>;
105
+ /**
106
+ * Drain the current accumulator and append one JSONL row to
107
+ * `.fabric/metrics.jsonl`. Returns the appended row (or `null` when the
108
+ * accumulator was empty — no spurious zero rows). fs failures degrade
109
+ * silently; the next flush will carry the union of the failed-write
110
+ * interval + the current one.
111
+ */
112
+ declare function flushMetrics(projectRoot: string, options?: {
113
+ windowMs?: number;
114
+ now?: Date;
115
+ }): Promise<MetricsRow | null>;
116
+ /**
117
+ * Start the background flush timer for a project root. Idempotent — calling
118
+ * twice on the same root replaces the prior interval. Returns a stop handle
119
+ * the caller can invoke at shutdown to flush + clear the timer.
120
+ *
121
+ * The flush is fire-and-forget (no await on the setInterval callback) so
122
+ * the timer cadence stays accurate even when fs is slow.
123
+ */
124
+ declare function startMetricsFlush(projectRoot: string, options?: {
125
+ intervalMs?: number;
126
+ }): () => Promise<void>;
127
+ /**
128
+ * Cancel the background flush timer for a project root if one is running.
129
+ * Does NOT drain the accumulator — callers that want a final flush should
130
+ * await flushMetrics(projectRoot) afterward.
131
+ */
132
+ declare function stopMetricsFlush(projectRoot: string): void;
133
+ /**
134
+ * Read accumulated metrics rows from `.fabric/metrics.jsonl`. Missing file
135
+ * returns []. Malformed rows are dropped silently (the sidecar is best-
136
+ * effort observability; a corrupt row never blocks a reader).
137
+ *
138
+ * Exposed for the NEW-34 `fab metrics` CLI dashboard + future doctor lints
139
+ * (e.g. cite-goodhart pattern replay) that need counter trends without
140
+ * walking events.jsonl.
141
+ */
142
+ declare function readMetrics(projectRoot: string): Promise<MetricsRow[]>;
143
+ type MetricsRow = {
144
+ timestamp: string;
145
+ window: string;
146
+ counters: Record<string, number>;
96
147
  };
97
- type DoctorApplyLintReport = {
98
- changed: boolean;
99
- mutations: DoctorApplyLintMutation[];
100
- manual_errors: DoctorIssue[];
101
- aborted: boolean;
102
- abort_reason?: string;
103
- message: string;
104
- report: DoctorReport;
148
+ /**
149
+ * Canonical metric counter names. rc.37 Wave B added a metrics.jsonl counter
150
+ * rollup for each (the forward-compatible signal), but — contrary to the
151
+ * original "these left events.jsonl" plan — every one is ALSO still appended to
152
+ * the audit ledger as a structured event, because a doctor lint consumes its
153
+ * per-id / per-path payload (see LEDGER_DUAL_WRITE_METRIC_NAMES below). The
154
+ * promised counter-only cutover (rc.38+; tracked by the G11 invariant test) has
155
+ * not happened. Centralized here so the B5 hard-gate (`metric_event_in_jsonl`)
156
+ * can grep for these exact strings; the gate only flags names NOT in the
157
+ * dual-write allowlist below.
158
+ */
159
+ declare const METRIC_COUNTER_NAMES: {
160
+ readonly knowledge_consumed: "knowledge_consumed";
161
+ readonly edit_intent_checked: "edit_intent_checked";
162
+ readonly knowledge_context_planned: "knowledge_context_planned";
163
+ readonly knowledge_sections_fetched: "knowledge_sections_fetched";
105
164
  };
106
- declare function runDoctorReport(target: string): Promise<DoctorReport>;
107
- declare function runDoctorFix(target: string): Promise<DoctorFixReport>;
108
- declare function runDoctorApplyLint(target: string): Promise<DoctorApplyLintReport>;
165
+ type MetricCounterName = (typeof METRIC_COUNTER_NAMES)[keyof typeof METRIC_COUNTER_NAMES];
166
+
109
167
  type CiteContractMetrics = {
110
168
  decisions_cited: number;
111
169
  pitfalls_cited: number;
@@ -124,7 +182,7 @@ type CiteCoverageReport = {
124
182
  marker_ts: number;
125
183
  marker_emitted_now: boolean;
126
184
  since_ts: number;
127
- client_filter: "cc" | "codex" | "cursor" | "all";
185
+ client_filter: "cc" | "codex" | "all";
128
186
  layer_filter?: "team" | "personal" | "all";
129
187
  metrics: {
130
188
  edits_touched: number;
@@ -136,6 +194,25 @@ type CiteCoverageReport = {
136
194
  compliant_cites?: number;
137
195
  noncompliant_cites?: number;
138
196
  uncorrelatable_edits?: number;
197
+ recall_backed_edits?: number;
198
+ recall_coverage_rate?: number | null;
199
+ exposed_and_mutated?: {
200
+ count: number;
201
+ ids?: string[];
202
+ };
203
+ mutations_observed?: {
204
+ count: number;
205
+ };
206
+ mutation_pool?: {
207
+ attributed: number;
208
+ unattributed_workspace_dirty: number;
209
+ };
210
+ sessions_closed?: {
211
+ count: number;
212
+ };
213
+ by_store?: Record<string, {
214
+ qualifying_cites: number;
215
+ }>;
139
216
  };
140
217
  per_client?: Record<string, Partial<CiteCoverageReport["metrics"]>>;
141
218
  dismissed_reason_histogram?: Record<string, number>;
@@ -154,9 +231,10 @@ type CiteCoverageReport = {
154
231
  };
155
232
  declare function runDoctorCiteCoverage(projectRoot: string, options: {
156
233
  since: number;
157
- client: "cc" | "codex" | "cursor" | "all";
234
+ client: "cc" | "codex" | "all";
158
235
  layer?: "team" | "personal" | "all";
159
236
  until?: number;
237
+ recallWindowMs?: number;
160
238
  }): Promise<CiteCoverageReport>;
161
239
  type ArchiveHistoryEntry = {
162
240
  session_id_short: string;
@@ -192,6 +270,94 @@ type HistoryAllReport = {
192
270
  declare function runDoctorHistoryAll(projectRoot: string, options: {
193
271
  since: number;
194
272
  }): Promise<HistoryAllReport>;
273
+
274
+ type DoctorStatus = "ok" | "warn" | "error";
275
+ type DoctorIssueKind = "fixable_error" | "manual_error" | "warning" | "info";
276
+ type DoctorCheck = {
277
+ name: string;
278
+ status: DoctorStatus;
279
+ message: string;
280
+ kind?: DoctorIssueKind;
281
+ code?: string;
282
+ fixable?: boolean;
283
+ actionHint?: string;
284
+ audience?: "user" | "maintainer";
285
+ };
286
+ type DoctorIssue = {
287
+ code: string;
288
+ name: string;
289
+ message: string;
290
+ path?: string;
291
+ actionHint?: string;
292
+ audience?: "user" | "maintainer";
293
+ };
294
+ type DoctorPayloadLimits = {
295
+ warn_bytes: number;
296
+ hard_bytes: number;
297
+ source: "default" | "config";
298
+ };
299
+ type DoctorSummary = {
300
+ target: string;
301
+ framework: {
302
+ kind: string;
303
+ version: string;
304
+ subkind: string;
305
+ };
306
+ entryPoints: Array<{
307
+ path: string;
308
+ reason: string;
309
+ }>;
310
+ metaRevision: string | null;
311
+ computedMetaRevision: string | null;
312
+ ruleCount: number;
313
+ eventLedgerPath: string;
314
+ fixableErrorCount: number;
315
+ manualErrorCount: number;
316
+ warningCount: number;
317
+ infoCount: number;
318
+ targetFiles: Record<string, boolean>;
319
+ payload_limits: DoctorPayloadLimits;
320
+ health: DoctorHealth;
321
+ };
322
+ type DoctorReport = {
323
+ status: DoctorStatus;
324
+ checks: DoctorCheck[];
325
+ fixable_errors: DoctorIssue[];
326
+ manual_errors: DoctorIssue[];
327
+ warnings: DoctorIssue[];
328
+ infos: DoctorIssue[];
329
+ summary: DoctorSummary;
330
+ };
331
+ type DoctorFixReport = {
332
+ changed: boolean;
333
+ fixed: DoctorIssue[];
334
+ remaining_manual_errors: DoctorIssue[];
335
+ warnings: DoctorIssue[];
336
+ message: string;
337
+ report: DoctorReport;
338
+ };
339
+ type DoctorApplyLintMutationKind = "knowledge_orphan_demote_required" | "knowledge_stale_archive_required" | "knowledge_index_drift" | "knowledge_pending_auto_archive" | "knowledge_session_hints_stale_cleanup" | "knowledge_relevance_fields_missing";
340
+ type DoctorApplyLintMutation = {
341
+ kind: DoctorApplyLintMutationKind;
342
+ path: string;
343
+ detail: string;
344
+ applied: boolean;
345
+ error?: string;
346
+ };
347
+ type DoctorApplyLintReport = {
348
+ changed: boolean;
349
+ mutations: DoctorApplyLintMutation[];
350
+ warnings: DoctorIssue[];
351
+ manual_errors: DoctorIssue[];
352
+ aborted: boolean;
353
+ abort_reason?: string;
354
+ message: string;
355
+ report: DoctorReport;
356
+ };
357
+ declare function runDoctorReport(target: string): Promise<DoctorReport>;
358
+ declare function runDoctorFix(target: string): Promise<DoctorFixReport>;
359
+ declare function runDoctorApplyLint(target: string): Promise<DoctorApplyLintReport>;
360
+
195
361
  type EnrichDescriptionsMode = "auto" | "preview" | "readonly" | "interactive";
196
362
  type EnrichDescriptionsCandidate = {
197
363
  path: string;
@@ -213,84 +379,131 @@ declare function enrichDescriptions(projectRoot: string, opts?: {
213
379
  dryRun?: boolean;
214
380
  }): Promise<EnrichDescriptionsReport>;
215
381
 
216
- type KnowledgeMetaBuildSource = "doctor_fix" | "sync_meta";
217
- type KnowledgeMetaBuildResult = {
218
- meta: AgentsMeta;
219
- knowledgeTestIndex: KnowledgeTestIndex;
220
- changed: boolean;
221
- };
222
- type WriteKnowledgeMetaOptions = {
223
- source: KnowledgeMetaBuildSource;
224
- };
382
+ interface Bm25Document {
383
+ id: string;
384
+ tokens: string[];
385
+ }
386
+ interface Bm25Model {
387
+ /**
388
+ * BM25 score of document `id` against the (pre-tokenized) query terms.
389
+ * Returns 0 for an unknown id, an empty document, no query terms, or no
390
+ * term overlap. Query-term duplicates are collapsed — repeating a term in
391
+ * the query does not inflate the score (term frequency is a document
392
+ * property, not a query property).
393
+ */
394
+ scoreDoc(id: string, queryTerms: string[]): number;
395
+ }
225
396
  /**
226
- * v2.0-rc.24 TASK-07: Load a Map<stable_id, knowledge_type> from the
227
- * project's `.fabric/agents.meta.json`. Consumed by the doctor cite-coverage
228
- * routing (TASK-08) to dispatch cites to the correct policy bucket
229
- * (decision/pitfall = strict contract / model = reference-only /
230
- * guideline+process = deferred to rc.25 LLM-judge). Cited ids absent from
231
- * this map fall into the `cite_id_unresolved` bucket.
232
- *
233
- * **Plural knowledge_type contract (rc.29 BUG-C1 unification):** the returned
234
- * map values are the PLURAL `KnowledgeType` enum (`"models" | "decisions" |
235
- * "guidelines" | "pitfalls" | "processes"`) — matching disk frontmatter,
236
- * filesystem layout, MCP I/O surface, and the canonical `KnowledgeTypeSchema`
237
- * exported from `@fenglimg/fabric-shared`. Legacy singular frontmatter is
238
- * normalized at parse time (see `SINGULAR_TO_PLURAL` in `parseFrontmatter`);
239
- * downstream callers (TASK-08 doctor) match against the plural enum.
240
- *
241
- * Both team (KT-*) and personal (KP-*) entries are included — they live in
242
- * the same `meta.nodes` map.
243
- *
244
- * Graceful on failure: a missing meta file, malformed JSON, or schema
245
- * validation failure all yield an empty Map (no throw). The doctor will then
246
- * surface every cite as `cite_id_unresolved`, which is the safe degraded
247
- * mode.
397
+ * Build a BM25 model over `docs`. The corpus statistics (document frequency,
398
+ * average document length) are computed once here; `scoreDoc` is then O(query
399
+ * terms) per call.
248
400
  */
249
- declare function loadKbIdTypeMap(projectRootInput: string): Promise<Map<string, KnowledgeType>>;
250
- declare function buildKnowledgeMeta(projectRootInput: string): Promise<KnowledgeMetaBuildResult>;
251
- declare function writeKnowledgeMeta(projectRootInput: string, options: WriteKnowledgeMetaOptions): Promise<KnowledgeMetaBuildResult>;
252
- declare function computeKnowledgeBasedAgentsMeta(projectRootInput: string, existingMeta?: AgentsMeta): Promise<AgentsMeta>;
253
- declare function computeKnowledgeTestIndex(projectRootInput: string, computedMeta: AgentsMeta, previousIndex?: KnowledgeTestIndex): Promise<KnowledgeTestIndex>;
254
- declare function deriveKnowledgeMetaLayer(relativePath: string): AgentsLayer;
255
- declare function deriveKnowledgeMetaTopologyType(relativePath: string): AgentsTopologyType;
256
- declare function isSameKnowledgeTestIndex(left: KnowledgeTestIndex, right: KnowledgeTestIndex): boolean;
257
- declare function stableStringify(value: unknown): string;
401
+ declare function buildBm25Model(docs: Bm25Document[]): Bm25Model;
258
402
 
403
+ interface ConflictEntry {
404
+ stable_id: string;
405
+ /** Plural knowledge_type ("decisions" | "pitfalls" | "guidelines" | "models" | "processes"). */
406
+ knowledge_type: string;
407
+ /** "team" | "personal". Conflicts are only meaningful within one layer. */
408
+ layer: string;
409
+ /** Title + body (or any text used for similarity). */
410
+ text: string;
411
+ }
412
+ type ConflictVerdict = "conflict" | "similar" | "unknown";
413
+ interface ConflictPair {
414
+ a: string;
415
+ b: string;
416
+ knowledge_type: string;
417
+ layer: string;
418
+ /** Symmetric normalized bm25 similarity in [0,1]. */
419
+ similarity: number;
420
+ /**
421
+ * "unknown" until a judge classifies it (cheap pass leaves it unknown =
422
+ * needs-human-review). "conflict" = the judge ruled a real contradiction
423
+ * (escalates to error). "similar" = judged not-a-conflict (stays a warn /
424
+ * possible duplicate).
425
+ */
426
+ verdict: ConflictVerdict;
427
+ rationale?: string;
428
+ }
429
+ type ConflictJudge = (a: ConflictEntry, b: ConflictEntry) => Promise<{
430
+ isConflict: boolean;
431
+ rationale: string;
432
+ }>;
433
+ declare const DEFAULT_CONFLICT_SIMILARITY_THRESHOLD = 0.5;
259
434
  /**
260
- * v2.0 KnowledgeIdAllocator
261
- *
262
- * Wraps the pure `allocateKnowledgeId` allocator with persistence: it reads
263
- * `agents.meta.json`, advances the counter for the requested (layer, type)
264
- * pair, and writes the updated meta atomically (write-to-tmp + rename) so
265
- * concurrent readers always see a consistent file.
435
+ * Symmetric, [0,1]-normalized bm25 similarity between two docs sharing a model.
436
+ * For each direction we measure how much of the target doc's self-relevance the
437
+ * other doc's terms recover (scoreDoc(target, otherTokens) / scoreDoc(target,
438
+ * targetTokens)), then take the MIN of both directions conservative: both
439
+ * entries must strongly overlap, so a short entry incidentally contained in a
440
+ * long one does not over-fire.
441
+ */
442
+ declare function pairSimilarity(model: ReturnType<typeof buildBm25Model>, a: {
443
+ id: string;
444
+ tokens: string[];
445
+ }, b: {
446
+ id: string;
447
+ tokens: string[];
448
+ }): number;
449
+ /**
450
+ * Cheap deterministic pass: candidate conflicting/duplicate pairs. Compares
451
+ * only entries sharing (type, layer), via bm25 similarity ≥ threshold. Returns
452
+ * pairs sorted by similarity descending (stable_id-tie-broken for determinism).
453
+ * Every returned pair has verdict "unknown" — the cheap pass cannot tell a
454
+ * conflict from a benign duplicate; that is the judge's job.
266
455
  *
267
- * Counters are MONOTONIC across the lifetime of the meta file: deleting a
268
- * knowledge entry does NOT free its counter slot, so previously-allocated
269
- * stable_ids remain unique even after their files are removed.
456
+ * O(G · n_g²) where G = groups and n_g = entries per group. Conflicts only make
457
+ * sense within one (type, layer), so the quadratic stays bounded to small
458
+ * same-bucket sets, not the whole corpus.
459
+ */
460
+ declare function findConflictCandidates(entries: ConflictEntry[], opts?: {
461
+ threshold?: number;
462
+ }): ConflictPair[];
463
+ /**
464
+ * Full lint. Always runs the cheap pass; when `judge` is supplied (deep mode)
465
+ * each candidate is classified into conflict/similar. A judge throw leaves the
466
+ * pair as "unknown" (degrade-safe — a flaky judge never crashes doctor).
270
467
  *
271
- * Key invariants:
272
- * - Counters envelope is initialized to zeros if absent (v1.x meta compat).
273
- * - Each allocate() call performs read → mutate → atomic-write in sequence.
274
- * - The returned id is guaranteed to differ from every previously-returned
275
- * id for the same meta path.
468
+ * Entry lookup for the judge is by stable_id from the same `entries` array.
276
469
  */
277
- declare class KnowledgeIdAllocator {
278
- private readonly metaPath;
279
- constructor(metaPath: string);
280
- /**
281
- * Allocate the next stable_id for the given (layer, type) pair and persist
282
- * the advanced counter to `agents.meta.json`.
283
- */
284
- allocate(layer: Layer, type: KnowledgeType): Promise<StableId>;
285
- /**
286
- * Returns the current counters envelope, defaulting to all-zero slots when
287
- * the meta file is absent or pre-v2.0 (counters key missing).
288
- */
289
- getCounters(): Promise<AgentsMetaCounters>;
290
- private readMeta;
291
- private normalizeCounters;
292
- private writeMetaAtomic;
470
+ declare function lintConflicts(entries: ConflictEntry[], opts?: {
471
+ threshold?: number;
472
+ judge?: ConflictJudge;
473
+ }): Promise<ConflictPair[]>;
474
+
475
+ interface ConflictLintReport {
476
+ status: "ok";
477
+ threshold: number;
478
+ deep: boolean;
479
+ /** Total candidate pairs (similarity threshold). */
480
+ candidate_count: number;
481
+ /** Subset judged a real contradiction (deep mode only). */
482
+ conflict_count: number;
483
+ pairs: ConflictPair[];
293
484
  }
485
+ /**
486
+ * v2.2 W5 R6 (读侧 cutover): load canonical knowledge entries with bodies from
487
+ * the read-set STORES (cross-store on-the-fly) instead of the retired
488
+ * co-location agents.meta node index. Skips pending/draft staging entries
489
+ * (conflict detection targets the curated corpus) and any entry missing a
490
+ * knowledge_type. collectStoreCanonicalEntries already reads each store entry's
491
+ * body + parsed frontmatter and degrades to [] when no store is in the read-set.
492
+ */
493
+ declare function loadConflictEntries(projectRoot: string): Promise<ConflictEntry[]>;
494
+ /**
495
+ * Run the knowledge-conflict lint. Always runs the cheap bm25 candidate pass;
496
+ * when `deep` is set AND a judge is supplied, escalates candidates to
497
+ * conflict/similar verdicts via the injected LLM judge.
498
+ *
499
+ * threshold resolution: opts.threshold → fabric-config
500
+ * conflict_lint_similarity_threshold → DEFAULT_CONFLICT_SIMILARITY_THRESHOLD.
501
+ */
502
+ declare function runDoctorConflictLint(projectRoot: string, opts?: {
503
+ threshold?: number;
504
+ deep?: boolean;
505
+ judge?: ConflictJudge;
506
+ }): Promise<ConflictLintReport>;
294
507
 
295
508
  /**
296
509
  * Append-evidence-on-collision service for fab_extract_knowledge.
@@ -328,8 +541,66 @@ declare function extractKnowledge(projectRoot: string, input: FabExtractKnowledg
328
541
  */
329
542
  declare function reviewKnowledge(projectRoot: string, input: FabReviewInput): Promise<FabReviewOutput>;
330
543
 
544
+ /** A summary to be cold-judged, keyed by its stable_id. */
545
+ interface ColdEvalCandidate {
546
+ stable_id: string;
547
+ summary: string;
548
+ }
549
+ /** The verdict the external cold-eval judge returns per candidate. */
550
+ interface ColdEvalVerdict {
551
+ stable_id: string;
552
+ /** true when the summary alone is act-on sufficient without the body. */
553
+ self_sufficient: boolean;
554
+ /** When not self-sufficient, the judge's suggested act-on rewrite. */
555
+ suggested_summary?: string;
556
+ /** Short rationale (pointer-vs-thesis) for the verdict. */
557
+ reason?: string;
558
+ }
559
+ /** The batch request handed to the external (maestro delegate) cold-eval judge. */
560
+ interface ColdEvalBatch {
561
+ rubric: string;
562
+ candidates: ColdEvalCandidate[];
563
+ }
564
+ declare const COLD_EVAL_RUBRIC: string;
565
+ /**
566
+ * Build the cold-eval batch request for the external judge. Pure + deterministic:
567
+ * drops blank summaries (nothing to judge) and pairs the candidates with the
568
+ * zero-context rubric. The fabric-review skill hands the result to
569
+ * `maestro delegate` and applies the returned {@link ColdEvalVerdict}[] via
570
+ * fab_review modify. Returns a batch with an empty candidate list when nothing is
571
+ * judgeable, so callers can short-circuit without a delegate round-trip.
572
+ */
573
+ declare function buildColdEvalBatch(candidates: ColdEvalCandidate[]): ColdEvalBatch;
574
+
331
575
  type StoredEventLedgerEvent = EventLedgerEvent;
576
+ type ReadEventLedgerOptions = {
577
+ event_type?: EventLedgerEvent["event_type"];
578
+ since?: number;
579
+ correlation_id?: string;
580
+ session_id?: string;
581
+ };
582
+ type LedgerWarning = {
583
+ kind: "partial_write_at_tail";
584
+ byte_offset: number;
585
+ byte_length: number;
586
+ snippet_first_120: string;
587
+ } | {
588
+ kind: "schema_version_unsupported";
589
+ line_index: number;
590
+ schema_version: unknown;
591
+ snippet_first_120: string;
592
+ } | {
593
+ kind: "event_type_unknown";
594
+ line_index: number;
595
+ event_type: unknown;
596
+ snippet_first_120: string;
597
+ };
598
+ type ReadEventLedgerResult = {
599
+ events: StoredEventLedgerEvent[];
600
+ warnings: LedgerWarning[];
601
+ };
332
602
  declare function appendEventLedgerEvent(projectRoot: string, event: EventLedgerEventInput): Promise<StoredEventLedgerEvent>;
603
+ declare function readEventLedger(projectRoot: string, options?: ReadEventLedgerOptions): Promise<ReadEventLedgerResult>;
333
604
  /**
334
605
  * Synchronously fsync the event ledger file to ensure OS page-cache buffers are
335
606
  * flushed to durable storage. Must be called AFTER in-flight drain but BEFORE
@@ -349,11 +620,28 @@ type PlanContextInput = {
349
620
  correlation_id?: string;
350
621
  session_id?: string;
351
622
  target_paths?: string[];
623
+ layer_filter?: "team" | "personal" | "both";
624
+ include_related?: boolean;
625
+ /**
626
+ * Internal MCP presentation budget. When supplied by the tool layer,
627
+ * candidates are byte-trimmed before the selection token is cached so the
628
+ * token cannot reference candidates omitted from the response.
629
+ */
630
+ payload_budget?: PlanContextPayloadBudget;
631
+ };
632
+ type PlanContextPayloadWarning = {
633
+ code: string;
634
+ file?: string;
635
+ action_hint?: string;
636
+ };
637
+ type PlanContextPayloadBudget = {
638
+ limits?: PayloadGuardOptions;
639
+ warnings?: PlanContextPayloadWarning[];
640
+ trim_warning?: PlanContextPayloadWarning;
352
641
  };
353
642
  type RequirementProfile = {
354
643
  target_path: string;
355
644
  known_tech: string[];
356
- user_intent: string;
357
645
  detected_entities: string[];
358
646
  };
359
647
  type PlanContextEntry = {
@@ -372,11 +660,18 @@ type PlanContextResult = {
372
660
  stale: boolean;
373
661
  selection_token: string;
374
662
  entries: PlanContextEntry[];
663
+ intent?: string;
375
664
  candidates: RuleDescriptionIndexItem[];
665
+ omitted_candidate_count?: number;
376
666
  preflight_diagnostics: PreflightDiagnostic[];
377
667
  auto_healed?: boolean;
378
668
  previous_revision_hash?: string;
379
669
  redirects?: Record<string, string>;
670
+ related_appended?: Record<string, string>;
671
+ /** Internal service→tool signal; stripped before MCP output. */
672
+ payload_trimmed?: boolean;
673
+ /** Internal service→tool signal; stripped before MCP output. */
674
+ payload_over_budget?: boolean;
380
675
  };
381
676
  type SelectionTokenState = {
382
677
  token: string;
@@ -392,102 +687,35 @@ declare function readSelectionToken(token: string, now?: number): SelectionToken
392
687
 
393
688
  type RecallInput = PlanContextInput & {
394
689
  /**
395
- * Optional explicit set of stable_ids to fetch bodies for. When omitted,
396
- * fab_recall picks up every stable_id surfaced in the shared description
397
- * index (the common case after rc.37 selectable-filter removal). When
398
- * provided, filters the fetched body set to this intersection.
690
+ * Optional explicit set of stable_ids to SCOPE the returned read paths to.
691
+ * When omitted, `paths` carries one entry per surfaced candidate. The candidate
692
+ * DESCRIPTION index is always returned in full for discovery — `ids` only narrows
693
+ * which read paths are surfaced (e.g. `recall(ids)` when the agent already knows
694
+ * which entries it wants to Read). Stale (pre layer-flip) ids are redirect-rewritten
695
+ * before the match.
399
696
  */
400
697
  ids?: string[];
698
+ /**
699
+ * When true, forwarded to planContext, which appends the one-hop `related` graph
700
+ * neighbours (H2) of the surfaced set to the candidate index (descriptions only —
701
+ * NO body). Their read paths are included in `paths` like any other candidate
702
+ * (W1-3 / KT-DEC-0031: surface the related id, do not fetch its body).
703
+ */
704
+ include_related?: boolean;
401
705
  };
402
- type RecallResult = PlanContextResult & {
403
- rules: Array<{
404
- stable_id: string;
405
- level: "L0" | "L1" | "L2";
406
- path: string;
407
- body: string;
408
- }>;
409
- selected_stable_ids: string[];
410
- diagnostics: Array<{
411
- code: "missing_knowledge_metadata";
412
- severity: "warn";
413
- stable_id: string;
414
- message: string;
415
- }>;
416
- };
417
- declare function recall(projectRoot: string, input: RecallInput): Promise<RecallResult>;
418
-
419
- /**
420
- * O(1) in-memory increment for a named counter. Safe to call from any MCP
421
- * tool handler; no I/O happens until the next `flushMetrics()` call.
422
- *
423
- * `delta` defaults to 1; callers can pass a positive integer (e.g. fetched
424
- * N stable_ids in a single sections call) to fold N bumps into one.
425
- */
426
- declare function bumpCounter(projectRoot: string, name: string, delta?: number): void;
427
- /**
428
- * Snapshot the current counter accumulator and reset it. Returned map is a
429
- * frozen copy; the live accumulator starts fresh from zero. Exposed so
430
- * flushMetrics + tests + a future fab_metrics manual-flush CLI hook can all
431
- * use the same primitive without racing.
432
- */
433
- declare function drainCounters(projectRoot: string): Record<string, number>;
434
- /**
435
- * Drain the current accumulator and append one JSONL row to
436
- * `.fabric/metrics.jsonl`. Returns the appended row (or `null` when the
437
- * accumulator was empty — no spurious zero rows). fs failures degrade
438
- * silently; the next flush will carry the union of the failed-write
439
- * interval + the current one.
440
- */
441
- declare function flushMetrics(projectRoot: string, options?: {
442
- windowMs?: number;
443
- now?: Date;
444
- }): Promise<MetricsRow | null>;
445
- /**
446
- * Start the background flush timer for a project root. Idempotent — calling
447
- * twice on the same root replaces the prior interval. Returns a stop handle
448
- * the caller can invoke at shutdown to flush + clear the timer.
449
- *
450
- * The flush is fire-and-forget (no await on the setInterval callback) so
451
- * the timer cadence stays accurate even when fs is slow.
452
- */
453
- declare function startMetricsFlush(projectRoot: string, options?: {
454
- intervalMs?: number;
455
- }): () => Promise<void>;
456
- /**
457
- * Cancel the background flush timer for a project root if one is running.
458
- * Does NOT drain the accumulator — callers that want a final flush should
459
- * await flushMetrics(projectRoot) afterward.
460
- */
461
- declare function stopMetricsFlush(projectRoot: string): void;
462
- /**
463
- * Read accumulated metrics rows from `.fabric/metrics.jsonl`. Missing file
464
- * returns []. Malformed rows are dropped silently (the sidecar is best-
465
- * effort observability; a corrupt row never blocks a reader).
466
- *
467
- * Exposed for the NEW-34 `fab metrics` CLI dashboard + future doctor lints
468
- * (e.g. cite-goodhart pattern replay) that need counter trends without
469
- * walking events.jsonl.
470
- */
471
- declare function readMetrics(projectRoot: string): Promise<MetricsRow[]>;
472
- type MetricsRow = {
473
- timestamp: string;
474
- window: string;
475
- counters: Record<string, number>;
706
+ type RecallPath = {
707
+ stable_id: string;
708
+ path: string;
709
+ store?: {
710
+ alias: string;
711
+ };
476
712
  };
477
- /**
478
- * Canonical metric counter names used by the high-frequency emitters that
479
- * left events.jsonl as part of the rc.37 Wave B clean-slate. Centralized
480
- * here so the B5 hard-gate (`metric_event_in_jsonl`) can grep for these
481
- * exact strings and fail when one accidentally re-appears in the audit
482
- * ledger emit path.
483
- */
484
- declare const METRIC_COUNTER_NAMES: {
485
- readonly knowledge_consumed: "knowledge_consumed";
486
- readonly edit_intent_checked: "edit_intent_checked";
487
- readonly knowledge_context_planned: "knowledge_context_planned";
488
- readonly knowledge_sections_fetched: "knowledge_sections_fetched";
713
+ type RecallResult = Omit<PlanContextResult, "selection_token" | "payload_trimmed" | "payload_over_budget"> & {
714
+ paths: RecallPath[];
715
+ directive: string;
716
+ next_steps?: string[];
489
717
  };
490
- type MetricCounterName = (typeof METRIC_COUNTER_NAMES)[keyof typeof METRIC_COUNTER_NAMES];
718
+ declare function recall(projectRoot: string, input: RecallInput): Promise<RecallResult>;
491
719
 
492
720
  /**
493
721
  * Start the background rotation timer for a project root. Idempotent —
@@ -511,90 +739,87 @@ declare function startRotationTick(projectRoot: string, options?: {
511
739
  declare function stopRotationTick(projectRoot: string): void;
512
740
 
513
741
  /**
514
- * Shared constants used across the server package.
515
- */
516
- /** MCP resource URI for the project's bootstrap README (L0 rules) file. */
517
- declare const AGENTS_MD_RESOURCE_URI = "fabric://bootstrap-readme";
518
-
519
- interface KnowledgeSyncOptions {
520
- mode?: "incremental" | "full";
521
- /** When true, invalid frontmatter throws RuleValidationError (default: false — collect as warning). */
522
- throwOnInvalidFrontmatter?: boolean;
523
- /**
524
- * v2.0.0-rc.29 TASK-005 (BUG-G1): when true, `ensureKnowledgeFresh`
525
- * synchronously follows a drift detection with a `reconcileKnowledge`
526
- * call to materialize the auto-heal (rewrite agents.meta.json + emit a
527
- * paired `knowledge_meta_auto_healed` event). Default false preserves
528
- * the rc.28 hot-path semantics where drift detection never blocks the
529
- * MCP read on a meta rebuild. Opt-in is intended for callers that can
530
- * tolerate ~tens-of-ms extra latency in exchange for the invariant
531
- * "every knowledge_drift_detected has a paired heal event in the same
532
- * tail window." Audit (BUG-G1) found 5/72 drifts healed on this repo
533
- * (~7%) because the hot path emitted detect-only events.
534
- */
535
- autoHealOnDrift?: boolean;
536
- }
537
- interface StructuredWarning {
538
- code: string;
539
- file: string;
540
- line?: number;
541
- action_hint: string;
542
- }
543
- /**
544
- * Granular ledger event shape for knowledge-sync operations.
545
- * These are returned in KnowledgeSyncReport and also appended to the event ledger
546
- * using the nearest available ledger event type (knowledge_drift_detected).
547
- * The shape below is what callers receive in `.events`.
548
- */
549
- interface KnowledgeSyncLedgerEvent {
550
- type: "rule_content_changed" | "rule_added" | "rule_removed";
551
- stable_id: string;
552
- path: string;
553
- prev_hash: string | null;
554
- new_hash: string | null;
555
- changed_fields: string[];
556
- source: "ensureKnowledgeFresh" | "reconcileKnowledge";
557
- }
558
- /** Alias so the public API says LedgerEvent (as documented). */
559
- type LedgerEvent = KnowledgeSyncLedgerEvent;
560
- interface KnowledgeSyncReport {
561
- status: "fresh" | "reconciled" | "errors";
562
- events: LedgerEvent[];
563
- warnings: StructuredWarning[];
564
- reconciled_files?: string[];
565
- }
566
- /**
567
- * Detects drift between disk and agents.meta.json, emits ledger events, and
568
- * invalidates the cache. Does NOT rewrite agents.meta.json. Optimised for
569
- * hot-path consumers (MCP tools).
742
+ * ContextCache unified hot-path cache for the Fabric server.
743
+ *
744
+ * Three logical slots:
745
+ * 1. "meta" — agents.meta.json content (TTL-based, default 5 s)
746
+ * 2. "context" — GetKnowledgeContext per projectRoot (TTL-based, default 5 s)
747
+ * 3. "audit" — sliding-window byte-offset cursor for audit.jsonl reads
748
+ *
749
+ * Invalidation reasons:
750
+ * - "meta_write" — eager invalidation when a write service mutates agents.meta.json
751
+ * - "file_watch" — chokidar detected an on-disk change
570
752
  */
571
- declare function ensureKnowledgeFresh(projectRoot: string, opts?: KnowledgeSyncOptions): Promise<KnowledgeSyncReport>;
572
- interface ReconcileKnowledgeOptions {
753
+ type InvalidationReason = "meta_write" | "file_watch";
754
+ type AuditCursor = {
755
+ offset: number;
756
+ remainder: string;
757
+ windowEntries: Array<{
758
+ ts: number;
759
+ }>;
760
+ };
761
+ declare class ContextCache {
762
+ private readonly defaultTtlMs;
763
+ private readonly metaSlot;
764
+ private readonly contextSlot;
765
+ private readonly auditSlot;
766
+ constructor(defaultTtlMs?: number);
767
+ get<T>(slot: "meta" | "context", key: string): T | undefined;
768
+ set<T>(slot: "meta" | "context", key: string, value: T, ttlMs?: number): void;
769
+ getAuditCursor(projectRoot: string): AuditCursor | undefined;
770
+ setAuditCursor(projectRoot: string, cursor: AuditCursor): void;
771
+ resetAuditCursor(projectRoot: string): void;
573
772
  /**
574
- * Identifies who triggered the reconcile; controls which summary ledger event is written.
773
+ * Invalidate cache slots based on what changed.
575
774
  *
576
- * v2.0.0-rc.23 TASK-005 (a-B): `auto-heal-description` added so plan_context
577
- * can drive a full reconcile when it detects nodes with `description === undefined`
578
- * (legacy meta drift the revision-hash gate cannot detect).
579
- *
580
- * v2.0.0-rc.27 TASK-001 (§2.9 root): `post-approve` / `post-modify` added so
581
- * `fab_review` approve/modify-layer-flip can drive an immediate meta rebuild
582
- * — without this the new entry's `nodes[id]` stays empty until the next
583
- * plan_context call's auto-heal, which leaves the entry undiscoverable in
584
- * the description_index window between approve and the next hint call.
775
+ * @param reason "meta_write" — only the meta slot for this projectRoot
776
+ * "file_watch" — meta + context slots (AGENTS.md may have changed)
777
+ * @param projectRoot Optional; if omitted, clears ALL keys in affected slots.
585
778
  */
586
- trigger?: "startup" | "doctor" | "manual" | "auto-heal-description" | "auto-heal-after-drift" | "post-approve" | "post-modify";
779
+ invalidate(reason: InvalidationReason, projectRoot?: string): void;
780
+ private slotStore;
587
781
  }
782
+ declare const contextCache: ContextCache;
783
+
784
+ type LedgerSourceFilter = "ai" | "human";
785
+ type StoredLedgerEntry = LedgerEntry & {
786
+ id: string;
787
+ };
788
+ type ReadLedgerOptions = {
789
+ source?: LedgerSourceFilter;
790
+ since?: number;
791
+ };
792
+ type ResolvedLedgerPaths = {
793
+ primaryPath: string;
794
+ legacyPath: string;
795
+ readPath: string;
796
+ usingLegacy: boolean;
797
+ };
798
+ declare function resolveLedgerPaths(projectRoot: string): Promise<ResolvedLedgerPaths>;
799
+ declare function readLedger(projectRoot: string, options?: ReadLedgerOptions): Promise<StoredLedgerEntry[]>;
800
+
801
+ type RehydrateTarget = {
802
+ ledgerEntryId: string;
803
+ } | {
804
+ timestamp: number;
805
+ };
806
+ type RehydratedAgentsMetaSnapshot = {
807
+ meta: AgentsMeta;
808
+ metadata: {
809
+ at_ledger_id: string;
810
+ at_commit: string | null;
811
+ replayed_count: number;
812
+ mode: "git-show" | "ledger-fallback";
813
+ };
814
+ entries: StoredLedgerEntry[];
815
+ };
816
+ declare function rehydrateAgentsMetaAt(projectRoot: string, target: RehydrateTarget): Promise<RehydratedAgentsMetaSnapshot>;
817
+
588
818
  /**
589
- * Full scan + rewrites agents.meta.json with ground-truth disk state + emits
590
- * ledger events. Used by startup (TASK-022) and doctor repair (TASK-023).
591
- * Returns reconciled_files listing all paths whose meta was updated.
592
- *
593
- * When `opts.trigger` is `'startup'`, a `meta_reconciled_on_startup` summary
594
- * ledger event is appended after per-file drift events. Other trigger values
595
- * append a `meta_reconciled` event. Omitting the trigger skips the summary.
819
+ * Shared constants used across the server package.
596
820
  */
597
- declare function reconcileKnowledge(projectRoot: string, opts?: ReconcileKnowledgeOptions): Promise<KnowledgeSyncReport>;
821
+ /** MCP resource URI for the project's bootstrap README (L0 rules) file. */
822
+ declare const AGENTS_MD_RESOURCE_URI = "fabric://bootstrap-readme";
598
823
 
599
824
  /**
600
825
  * Returns an info-level startup message when CLAUDE.md or AGENTS.md exist at
@@ -605,6 +830,7 @@ declare function reconcileKnowledge(projectRoot: string, opts?: ReconcileKnowled
605
830
  */
606
831
  declare function formatPreexistingRootMessage(projectRoot: string): string | null;
607
832
 
833
+ declare const FABRIC_SERVER_INSTRUCTIONS: string;
608
834
  declare function createFabricServer(tracker?: InFlightTracker): McpServer;
609
835
  declare function startStdioServer(): Promise<void>;
610
836
  /**
@@ -631,4 +857,4 @@ interface ShutdownHandlerDeps {
631
857
  */
632
858
  declare function createShutdownHandler(deps: ShutdownHandlerDeps): () => void;
633
859
 
634
- export { AGENTS_MD_RESOURCE_URI, type ArchiveHistoryEntry, type ArchiveHistoryReport, type CiteCoverageReport, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, type HistoryAllReport, type HistoryDayRow, type InFlightTracker, KnowledgeIdAllocator, type KnowledgeMetaBuildResult, type KnowledgeMetaBuildSource, type KnowledgeSyncLedgerEvent, type KnowledgeSyncOptions, type KnowledgeSyncReport, LEDGER_PATH, LEGACY_LEDGER_PATH, type LedgerEvent, METRICS_LEDGER_PATH, METRIC_COUNTER_NAMES, type MetricCounterName, type MetricsRow, type PlanContextInput, type PlanContextResult, type RecallInput, type RecallResult, type ReconcileKnowledgeOptions, type RequirementProfile, type SelectionTokenState, type ShutdownHandlerDeps, type StructuredWarning, type WriteKnowledgeMetaOptions, appendEventLedgerEvent, buildKnowledgeMeta, bumpCounter, computeKnowledgeBasedAgentsMeta, computeKnowledgeTestIndex, createFabricServer, createInFlightTracker, createShutdownHandler, deriveKnowledgeMetaLayer, deriveKnowledgeMetaTopologyType, drainCounters, enrichDescriptions, ensureKnowledgeFresh, extractKnowledge, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, isSameKnowledgeTestIndex, loadKbIdTypeMap, planContext, readMetrics, readSelectionToken, recall, reconcileKnowledge, reviewKnowledge, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorFix, runDoctorHistoryAll, runDoctorReport, stableStringify, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick, writeKnowledgeMeta };
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 };