@fenglimg/fabric-server 2.0.1 → 2.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +38 -41
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1829,6 +1829,7 @@ import { join as join7, relative as relative2 } from "path";
1829
1829
  import {
1830
1830
  PROPOSED_REASON_DESCRIPTIONS
1831
1831
  } from "@fenglimg/fabric-shared/schemas/api-contracts";
1832
+ import { hasSecrets } from "@fenglimg/fabric-shared";
1832
1833
 
1833
1834
  // src/services/load-active-meta.ts
1834
1835
  async function loadActiveMeta(projectRoot, opts = {}) {
@@ -2074,6 +2075,25 @@ async function extractKnowledge(projectRoot, input) {
2074
2075
  idempotency_key: idempotencyKey
2075
2076
  };
2076
2077
  }
2078
+ const secretScanTarget = [
2079
+ input.user_messages_summary ?? "",
2080
+ input.session_context ?? "",
2081
+ input.must_read_if ?? "",
2082
+ ...input.intent_clues ?? []
2083
+ ].join("\n");
2084
+ if (hasSecrets(secretScanTarget)) {
2085
+ await emitEventBestEffort(projectRoot, {
2086
+ event_type: "knowledge_archive_attempted",
2087
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2088
+ correlation_id: primarySession,
2089
+ session_id: primarySession,
2090
+ reason: `extract_knowledge:${sanitizedSlug || input.slug}:secret_detected`
2091
+ });
2092
+ return {
2093
+ pending_path: "",
2094
+ idempotency_key: idempotencyKey
2095
+ };
2096
+ }
2077
2097
  const layer = input.layer ?? "team";
2078
2098
  let relevanceScope = input.relevance_scope;
2079
2099
  let relevancePaths = input.relevance_paths;
@@ -5905,58 +5925,37 @@ async function inspectCiteGoodhart(projectRoot) {
5905
5925
  if (turns.length === 0) {
5906
5926
  return { status: "ok", fired: [] };
5907
5927
  }
5908
- const APPLIED_LIKE = /* @__PURE__ */ new Set(["recalled", "applied"]);
5909
- const recalledCount = /* @__PURE__ */ new Map();
5928
+ const appliedCount = /* @__PURE__ */ new Map();
5910
5929
  for (const turn of turns) {
5911
5930
  for (let i = 0; i < turn.cite_ids.length; i += 1) {
5912
- if (APPLIED_LIKE.has(turn.cite_tags[i])) {
5931
+ if (turn.cite_tags[i] === "applied") {
5913
5932
  const key = turn.cite_ids[i];
5914
- recalledCount.set(key, (recalledCount.get(key) ?? 0) + 1);
5933
+ appliedCount.set(key, (appliedCount.get(key) ?? 0) + 1);
5915
5934
  }
5916
5935
  }
5917
5936
  }
5918
- for (const [id, n] of recalledCount.entries()) {
5937
+ for (const [id, n] of appliedCount.entries()) {
5919
5938
  if (n > RITUAL_REPEAT_THRESHOLD) {
5920
- fired.push({ pattern: "G1", detail: `${id} repeated as [applied|recalled] ${n}x in 7d` });
5939
+ fired.push({ pattern: "G1", detail: `${id} repeated as [applied] ${n}x in 7d` });
5921
5940
  break;
5922
5941
  }
5923
5942
  }
5924
- let recalledTotal = 0;
5925
- let recalledWithSkip = 0;
5943
+ let appliedTotal = 0;
5944
+ let appliedWithSkip = 0;
5926
5945
  for (const turn of turns) {
5927
5946
  for (let i = 0; i < turn.cite_ids.length; i += 1) {
5928
- if (!APPLIED_LIKE.has(turn.cite_tags[i])) continue;
5929
- recalledTotal += 1;
5947
+ if (turn.cite_tags[i] !== "applied") continue;
5948
+ appliedTotal += 1;
5930
5949
  const commitment = turn.cite_commitments[i];
5931
5950
  if (commitment && typeof commitment.skip_reason === "string" && commitment.skip_reason.length > 0) {
5932
- recalledWithSkip += 1;
5951
+ appliedWithSkip += 1;
5933
5952
  }
5934
5953
  }
5935
5954
  }
5936
- if (recalledTotal >= 5 && recalledWithSkip / recalledTotal > DISMISSAL_ABUSE_RATIO) {
5955
+ if (appliedTotal >= 5 && appliedWithSkip / appliedTotal > DISMISSAL_ABUSE_RATIO) {
5937
5956
  fired.push({
5938
5957
  pattern: "G2",
5939
- detail: `${recalledWithSkip}/${recalledTotal} recalled cites used skip:<reason> (> ${Math.round(DISMISSAL_ABUSE_RATIO * 100)}%)`
5940
- });
5941
- }
5942
- let chainedFromMisuse = 0;
5943
- for (const turn of turns) {
5944
- for (let i = 0; i < turn.cite_ids.length; i += 1) {
5945
- if (turn.cite_tags[i] !== "chained-from") continue;
5946
- const commitment = turn.cite_commitments[i];
5947
- if (!commitment) {
5948
- chainedFromMisuse += 1;
5949
- continue;
5950
- }
5951
- const hasOps = Array.isArray(commitment.operators) && commitment.operators.length > 0;
5952
- const hasSkip = typeof commitment.skip_reason === "string" && commitment.skip_reason.length > 0;
5953
- if (!hasOps && !hasSkip) chainedFromMisuse += 1;
5954
- }
5955
- }
5956
- if (chainedFromMisuse > RITUAL_REPEAT_THRESHOLD) {
5957
- fired.push({
5958
- pattern: "G3",
5959
- detail: `${chainedFromMisuse} chained-from cites with no commitment (operators=[] + skip_reason=null) in 7d`
5958
+ detail: `${appliedWithSkip}/${appliedTotal} applied cites used skip:<reason> (> ${Math.round(DISMISSAL_ABUSE_RATIO * 100)}%)`
5960
5959
  });
5961
5960
  }
5962
5961
  let placeholderCount = 0;
@@ -9868,7 +9867,7 @@ function parseNoneSentinel(kbLineRaw) {
9868
9867
  return "unspecified";
9869
9868
  }
9870
9869
  function categorizeCiteTag(tag) {
9871
- if (tag === "planned" || tag === "recalled" || tag === "chained-from" || tag === "none") {
9870
+ if (tag === "applied" || tag === "none") {
9872
9871
  return { category: tag };
9873
9872
  }
9874
9873
  if (tag === "dismissed") {
@@ -10160,18 +10159,16 @@ async function runDoctorCiteCoverage(projectRoot, options) {
10160
10159
  }
10161
10160
  sessionCitedKbs.set(sid, set);
10162
10161
  }
10163
- let turnHadRecalled = false;
10162
+ let turnHadApplied = false;
10164
10163
  for (const tag of turn.cite_tags) {
10165
10164
  const { category, reason } = categorizeCiteTag(tag);
10166
10165
  switch (category) {
10167
- case "planned":
10168
- case "recalled":
10169
- case "chained-from":
10166
+ case "applied":
10170
10167
  qualifyingCites += 1;
10171
10168
  bumpClient(turn.client, (m) => {
10172
10169
  m.qualifying_cites += 1;
10173
10170
  });
10174
- if (category === "recalled") turnHadRecalled = true;
10171
+ turnHadApplied = true;
10175
10172
  break;
10176
10173
  case "dismissed": {
10177
10174
  const key = reason ?? "unspecified";
@@ -10187,7 +10184,7 @@ async function runDoctorCiteCoverage(projectRoot, options) {
10187
10184
  break;
10188
10185
  }
10189
10186
  }
10190
- if (turnHadRecalled && !isRecallVerified(turn)) {
10187
+ if (turnHadApplied && !isRecallVerified(turn)) {
10191
10188
  recalledUnverified += 1;
10192
10189
  bumpClient(turn.client, (m) => {
10193
10190
  m.recalled_unverified += 1;
@@ -10825,7 +10822,7 @@ function formatPreexistingRootMessage(projectRoot) {
10825
10822
  function createFabricServer(tracker) {
10826
10823
  const server = new McpServer({
10827
10824
  name: "fabric-knowledge-server",
10828
- version: "2.0.1"
10825
+ version: "2.1.0-rc.2"
10829
10826
  });
10830
10827
  registerPlanContext(server, tracker);
10831
10828
  registerKnowledgeSections(server, tracker);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-server",
3
- "version": "2.0.1",
3
+ "version": "2.1.0-rc.2",
4
4
  "description": "Fabric MCP knowledge server — stdio transport for Claude Code / Cursor / Codex CLI, manages .fabric/ knowledge base + agents.meta.json + event ledger.",
5
5
  "license": "MIT",
6
6
  "author": "wangzhichao <fenglimg90@gmail.com>",
@@ -38,7 +38,7 @@
38
38
  "@modelcontextprotocol/sdk": "^1.29.0",
39
39
  "minimatch": "^10.0.1",
40
40
  "zod": "^3.25.0",
41
- "@fenglimg/fabric-shared": "2.0.1"
41
+ "@fenglimg/fabric-shared": "2.1.0-rc.2"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.15.0",