@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/README.md +3 -4
- package/dist/index.d.ts +549 -323
- package/dist/index.js +8233 -9278
- package/package.json +3 -4
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
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
summary
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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" | "
|
|
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" | "
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
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
|
|
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
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
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
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
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
|
-
*
|
|
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
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
|
396
|
-
*
|
|
397
|
-
* index
|
|
398
|
-
*
|
|
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
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
572
|
-
|
|
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
|
-
*
|
|
773
|
+
* Invalidate cache slots based on what changed.
|
|
575
774
|
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
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 };
|