@fenglimg/fabric-shared 2.2.0 → 2.3.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,7 +7,8 @@ import {
7
7
  BOOTSTRAP_REGEX,
8
8
  matchBootstrapCanonicalLocale,
9
9
  resolveBootstrapCanonical
10
- } from "./chunk-BDJQIOQO.js";
10
+ } from "./chunk-OAYQHN6J.js";
11
+ import "./chunk-LXNCAKJZ.js";
11
12
  import {
12
13
  PROTECTED_TOKENS,
13
14
  createTranslator,
@@ -15,7 +16,7 @@ import {
15
16
  enMessages,
16
17
  resolveFabricLocale,
17
18
  zhCNMessages
18
- } from "./chunk-AQMDXC6J.js";
19
+ } from "./chunk-ZYBWITH7.js";
19
20
  import {
20
21
  GLOBAL_BINDINGS_DIR,
21
22
  GLOBAL_STATE_DIR,
@@ -50,7 +51,7 @@ import {
50
51
  storeRelativePath,
51
52
  storeRelativePathForMount,
52
53
  storeUuidSchema
53
- } from "./chunk-2GLIAZ5M.js";
54
+ } from "./chunk-ANUDBQBK.js";
54
55
  import {
55
56
  atomicWriteJson,
56
57
  withFileLock
@@ -59,6 +60,10 @@ import {
59
60
  FabExtractKnowledgeInputSchema,
60
61
  FabExtractKnowledgeInputShape,
61
62
  FabExtractKnowledgeOutputSchema,
63
+ FabPendingInputSchema,
64
+ FabPendingInputShape,
65
+ FabPendingOutputSchema,
66
+ FabPendingOutputShape,
62
67
  FabReviewInputSchema,
63
68
  FabReviewInputShape,
64
69
  FabReviewOutputSchema,
@@ -74,6 +79,7 @@ import {
74
79
  PERSONAL_SCOPE,
75
80
  PROPOSED_REASON_DESCRIPTIONS_BY_LOCALE,
76
81
  ProposedReasonSchema,
82
+ SCOPE_COORDINATE_HINT,
77
83
  SCOPE_COORDINATE_PATTERN,
78
84
  StableIdSchema,
79
85
  annotateIntentRequestSchema,
@@ -85,6 +91,7 @@ import {
85
91
  citeLayerTypeBreakdownSchema,
86
92
  entryScopeMetadataSchema,
87
93
  fabExtractKnowledgeAnnotations,
94
+ fabPendingAnnotations,
88
95
  fabReviewAnnotations,
89
96
  formatKnowledgeId,
90
97
  historyStateQuerySchema,
@@ -109,8 +116,7 @@ import {
109
116
  scopeCoordinateSchema,
110
117
  scopeRoot,
111
118
  structuredWarningSchema
112
- } from "./chunk-5AKCRBKJ.js";
113
- import "./chunk-LXNCAKJZ.js";
119
+ } from "./chunk-7PS7LB5T.js";
114
120
 
115
121
  // src/schemas/agents-meta.ts
116
122
  import { z } from "zod";
@@ -349,6 +355,32 @@ var humanLockFileSchema = z4.object({
349
355
 
350
356
  // src/schemas/fabric-config.ts
351
357
  import { z as z5 } from "zod";
358
+ function isNonPersonalRequiredStore(entry) {
359
+ return entry.id !== PERSONAL_STORE_SENTINEL;
360
+ }
361
+ function refineMaxOneTeamStore(entries, ctx) {
362
+ const teamCount = entries.filter(isNonPersonalRequiredStore).length;
363
+ if (teamCount > 1) {
364
+ ctx.addIssue({
365
+ code: z5.ZodIssueCode.custom,
366
+ message: `a project may bind at most one team store (found ${teamCount}); run \`fabric install\` to migrate to the single team slot`
367
+ });
368
+ }
369
+ }
370
+ function migrateRequiredStores(config) {
371
+ const declared = config.required_stores;
372
+ if (declared === void 0) {
373
+ return config;
374
+ }
375
+ const team = declared.filter(isNonPersonalRequiredStore);
376
+ if (team.length <= 1) {
377
+ return config;
378
+ }
379
+ const personal = declared.filter((entry) => !isNonPersonalRequiredStore(entry));
380
+ const active = config.active_write_store;
381
+ const kept = (active !== void 0 ? team.find((entry) => entry.id === active) : void 0) ?? team[0];
382
+ return { ...config, required_stores: [...personal, kept] };
383
+ }
352
384
  var auditModeSchema = z5.enum(["strict", "warn", "off"]);
353
385
  var clientPathsSchema = z5.object({
354
386
  claudeCodeCLI: z5.string().optional(),
@@ -395,7 +427,9 @@ var fabricConfigSchema = z5.object({
395
427
  // `$personal` sentinel). Drives the read-set (required_stores ∪ implicit
396
428
  // personal, S11/S54) and `clone`'s missing-store onboarding (S51). Optional
397
429
  // + absent → read-set is just the implicit personal store.
398
- required_stores: z5.array(requiredStoreEntrySchema).optional(),
430
+ // W2 dual-slot (TASK-002 / R6): at most ONE non-personal (team-type) store —
431
+ // the two-slot model. >1 fails parse and is migrated by `migrateRequiredStores`.
432
+ required_stores: z5.array(requiredStoreEntrySchema).superRefine(refineMaxOneTeamStore).optional(),
399
433
  // v2.1.0-rc.1 P3 (S60 / `store switch-write`): alias of the store that
400
434
  // non-personal-scope writes land in for this project. Set by
401
435
  // `fabric store switch-write <alias>`; consumed as the resolver's
@@ -494,92 +528,28 @@ var fabricConfigSchema = z5.object({
494
528
  // worst — pairing 14d trigger + 7d cooldown means at most ~2 reminders
495
529
  // per month for a workspace that ignores them.
496
530
  maintenance_hint_cooldown_days: z5.number().int().positive().optional().default(7),
497
- // rc.9+ (skill-contract-fix B1): first-run import window in months. The
498
- // `fabric-import` skill scans this many months of git history on the very
499
- // first invocation (when no prior `import_run_completed` event exists).
500
- // Default 60 (~5 years) captures the bulk of a mature repo's signal in
501
- // one pass; small / fresh repos can lower to 12-24 with no loss.
502
- import_window_first_run_months: z5.number().int().min(1).optional().default(60),
503
- // rc.9+ (skill-contract-fix B1): rerun import window in months. After
504
- // the first successful import, subsequent runs only scan this many
505
- // recent months — assumed everything older has already been crystallized
506
- // into pending or canonical knowledge. Default 2 keeps incremental cost
507
- // low; raise to 6 if the workspace pauses fabric-import for long stretches.
508
- import_window_rerun_months: z5.number().int().min(1).optional().default(2),
509
- // rc.9+ (skill-contract-fix B1): hard cap on pending entries produced
510
- // per fabric-import invocation. Prevents one run from dumping hundreds
511
- // of proposals when a backfill window is wide open. Default 10 matches
512
- // the rule-of-thumb "human can triage ~10 pending entries in one
513
- // review pass." Range 1-50.
514
- import_max_pending_per_run: z5.number().int().min(1).max(50).optional().default(10),
515
- // rc.9+ (skill-contract-fix B1): hard cap on commits scanned per
516
- // fabric-import invocation. Bounds runtime on monorepos with high
517
- // commit velocity. Default 500 covers ~2 months of typical churn;
518
- // range 50-2000. Hitting the cap mid-window is logged but non-fatal.
519
- import_max_commits_scan: z5.number().int().min(50).max(2e3).optional().default(500),
520
- // rc.9+ (skill-contract-fix B1): canonical-node count above which
521
- // fabric-import's pre-flight should warn / suggest review instead of
522
- // proceeding. A workspace with 50+ canonical entries usually benefits
523
- // more from `fabric-review` to consolidate than from importing more.
524
- // Default 50; raise to 100+ for large polyglot repos.
525
- import_skip_canonical_threshold: z5.number().int().positive().optional().default(50),
526
- // rc.9+ (skill-contract-fix B1): max candidate entries surfaced per
527
- // fabric-archive batch (one invocation of the skill). Pagination knob
528
- // for the archive UI flow. Default 8 keeps each batch reviewable in
529
- // one sitting; raise for large repos with high archive throughput.
530
- archive_max_candidates_per_batch: z5.number().int().positive().optional().default(8),
531
- // rc.9+ (skill-contract-fix B1): max recently-touched paths included
532
- // in fabric-archive's "relevant context" lookup. Limits the size of
533
- // the path-relevance digest the skill emits when ranking candidates.
534
- // Default 20; large repos with deep directory fan-out can raise to
535
- // 50+ if archive candidates feel under-contextualized.
536
- archive_max_recent_paths: z5.number().int().positive().optional().default(20),
537
- // rc.9+ (skill-contract-fix B1): max prior fabric-archive sessions
538
- // summarised in the digest the skill loads on start. Prevents the
539
- // digest from ballooning past the model context budget on workspaces
540
- // that have archived repeatedly. Default 10; lower if context pressure
541
- // bites, raise if you want longer-range archive trend visibility.
542
- archive_digest_max_sessions: z5.number().int().positive().optional().default(10),
543
- // rc.9+ (skill-contract-fix B1): max review results returned per
544
- // topic when `fabric-review` clusters pending entries. Pagination
545
- // knob analogous to archive_max_candidates_per_batch but scoped to
546
- // each topic cluster. Default 8; raise to 15-20 for large repos
547
- // where each topic legitimately groups many pending entries.
548
- review_topic_result_cap: z5.number().int().positive().optional().default(8),
531
+ // ux-w2-3: import_*/archive_max_*/review_topic_result_cap skill thresholds
532
+ // were hardcoded (✂ per census Table 1) never tuned, pure skill-internal
533
+ // pagination/window caps. Removed from the schema; the skills/services read
534
+ // raw config with a built-in default, so an absent key falls to that default
535
+ // (60/2/10/500/50/8/20/10/8 respectively). Lenient parser drops any stale
536
+ // on-disk value.
549
537
  // rc.9+ (skill-contract-fix B1): age threshold (in days) above which
550
538
  // a pending entry is considered "stale" by fabric-review and surfaced
551
539
  // for explicit resolve-or-drop decision. Default 14; tighter than the
552
540
  // 7d Signal-B trigger because review specifically targets the long
553
541
  // tail. Large repos with slower cadence can raise to 30.
554
542
  review_stale_pending_days: z5.number().int().positive().optional().default(14),
555
- // v2.0.0-rc.34 TASK-05: reverse-unarchive opt-in. When true, callers of the
556
- // `unarchiveKnowledge` primitive (and any future doctor auto-detect lint built
557
- // on top) will execute the file move + ledger emit. When false (default),
558
- // the same callers MUST short-circuit before any mutation — the primitive is
559
- // shipped but inert until explicitly enabled. Opt-in posture mirrors the
560
- // archive-flow precedent: destructive-ish file moves stay behind a flag.
561
- reverse_unarchive_enabled: z5.boolean().optional().default(false),
562
- // v2.0.0-rc.34 TASK-05: forces `unarchiveKnowledge` into dry-run mode even
563
- // when called with `options.dryRun=false`. Lets operators preview a
564
- // restoration pass before flipping `reverse_unarchive_enabled` to true.
565
- reverse_unarchive_dry_run: z5.boolean().optional().default(false),
566
- // v2.0.0-rc.34 TASK-06: long-session cite-policy evict window in user-prompt
567
- // turns. UserPromptSubmit hook (Claude Code only) maintains a per-session
568
- // counter and re-injects the cite contract reminder via
569
- // hookSpecificOutput.additionalContext when `turn_count % interval === 0`.
570
- // Default 0 = OFF (opt-in). Recommend 10-20 for active sessions; 5 for
571
- // high-contract-criticality projects. Other strategies (time-based,
572
- // token-budget) deferred to rc.35 per plan locked-decisions 2026-05-26.
573
- cite_evict_interval: z5.number().int().min(0).optional().default(0),
574
543
  // v2.1 ⑤ cite-redesign (P5): recall-based cite-accounting hook config. The
575
- // rc.34 cite_evict_interval turn-counter above is superseded by the
576
- // PreToolUse(Edit/Write) recall-aware nudge in cite-policy-evict.cjs; the old
577
- // key is retained for back-compat (inert now that the hook moved off
578
- // UserPromptSubmit). `cite_recall_nudge` is the master switch (default true =
579
- // ON); set false to silence the "改前先 fab_recall" nudge entirely (mirrors
580
- // the cite_evict_interval=0 opt-out convention). `cite_recall_window_minutes`
581
- // bounds how far back an in-session fab_recall counts as "informing" the edit
582
- // (default 30; 0 = unbounded).
544
+ // PreToolUse(Edit/Write) recall-aware nudge in cite-policy-evict.cjs replaced
545
+ // the retired rc.34 `cite_evict_interval` turn-counter. ux-w1-5: that inert
546
+ // key plus the never-wired `reverse_unarchive_enabled`/`reverse_unarchive_dry_run`
547
+ // opt-in flags (the unarchiveKnowledge primitive takes dryRun from its caller,
548
+ // not from config) were deleted; the lenient root parser drops any stale
549
+ // value left in an on-disk config. `cite_recall_nudge` is the master switch
550
+ // (default true = ON); set false to silence the "改前先 fab_recall" nudge
551
+ // entirely. `cite_recall_window_minutes` bounds how far back an in-session
552
+ // fab_recall counts as "informing" the edit (default 30; 0 = unbounded).
583
553
  cite_recall_nudge: z5.boolean().optional().default(true),
584
554
  cite_recall_window_minutes: z5.number().int().min(0).optional().default(30),
585
555
  // F2: glob exemptions for the cite nudge (cite-policy-evict.cjs). Edit paths
@@ -625,18 +595,11 @@ var fabricConfigSchema = z5.object({
625
595
  // Default `[]` keeps the field optional on existing configs — fresh
626
596
  // installs land with no opt-outs.
627
597
  onboard_slots_opted_out: z5.array(z5.string()).optional().default([]),
628
- // v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the broad SessionStart hint
629
- // banner emitted by knowledge-hint-broad.cjs. After plan-context-hint returns
630
- // its full broad-scoped index, the hook slices the entries to this many
631
- // before grouping/truncation rendering keeps the banner from scrolling off
632
- // screen on well-seeded repos (Werewolf-class projects routinely surface 40+
633
- // broad entries which buried the actually-relevant top hits). Default 8 is
634
- // calibrated against the rc.32 eval baseline (cite-coverage 3.1%): the
635
- // banner needs to fit in ~1 screenful so the agent actually reads it.
636
- // Range 1..50; values above 20 effectively disable the cap because the
637
- // TRUNCATION_THRESHOLD=12 grouped-render kicks in. Mirrors the rc.7 T7 +
638
- // archive_max_* pattern of externalizing previously-hardcoded thresholds.
639
- hint_broad_top_k: z5.number().int().min(1).max(50).optional().default(8),
598
+ // ux-w3-j (W3-J): `hint_broad_top_k` was deleted. W2-1 (KT-DEC-0028) retired its
599
+ // hard-cap function the SessionStart broad banner now shows EVERY broad entry
600
+ // and `broad_index_backstop` (below) is the sole scale guard; the field had been
601
+ // inert ever since (its only remaining refs were retirement comments). The lenient
602
+ // root parser drops any stale on-disk value (zero migration).
640
603
  // KT-DEC-0036: the SessionStart broad-menu is now index-only (title + summary
641
604
  // per always-active entry, no eager body), so the former `hint_broad_budget_chars`
642
605
  // body char-budget knob was retired — there is no rendered body left to bound.
@@ -714,6 +677,15 @@ var fabricConfigSchema = z5.object({
714
677
  orphan_demote_proven_days: z5.number().int().min(1).max(3650).optional(),
715
678
  orphan_demote_verified_days: z5.number().int().min(1).max(3650).optional(),
716
679
  orphan_demote_draft_days: z5.number().int().min(1).max(3650).optional(),
680
+ // v2.2 C1 (processes/maturity-promotion-rubric-v1): days a `broad` entry may
681
+ // go without a fab-review re-confirmation before doctor surfaces a RECHECK
682
+ // nudge. `broad` is EXEMPT from usage-age decay (it is SessionStart-pushed,
683
+ // never pull-recalled → usage-blind, KT-DEC; see doctor-knowledge-age.ts), so
684
+ // its continued validity is instead checked against the review-confirmation
685
+ // clock (`last_review_confirmed_at`, stamped at approve/modify). This is a
686
+ // non-blocking INFO nudge ("re-confirm"), NEVER an auto-demote. Absent → the
687
+ // config-loader default (180d) applies. Range 1..3650 mirrors orphan_demote.
688
+ broad_review_recheck_days: z5.number().int().min(1).max(3650).optional(),
717
689
  // v2.0.0-rc.33 W4-A3 (T4 P2): per-entry summary truncation length used by
718
690
  // knowledge-hint-{broad,narrow}.cjs. Hard-coded at 80 chars in rc.32 — too
719
691
  // short for entries with parameterized summaries (e.g. "Use bcrypt with
@@ -762,10 +734,13 @@ var fabricConfigSchema = z5.object({
762
734
  // sole retrieval knob (plan_context_top_k above); payload limits pass through
763
735
  // explicit `mcpPayloadLimits`, else the fixed PAYLOAD_LIMIT_DEFAULT_* guardrail.
764
736
  // v2.2 C2-vector (W2-T7): OPTIONAL dense-embedding semantic retrieval, layered
765
- // as a recall supplement after BM25. Default OFF (`--no-embed` is the baseline);
766
- // requires the operator to install the optional `fastembed` package absent →
767
- // text-only fallback. Never grows the default install footprint.
768
- embed_enabled: z5.boolean().optional().default(false),
737
+ // as a recall supplement after BM25. P1 recall-engine-refactor (TASK-004):
738
+ // Default ON `fastembed` is now an optionalDependency (auto-installed; absent →
739
+ // degrade-safe text-only fallback), so CJK semantic recall is on out of the box.
740
+ // OFF only when set explicitly to false. This default mirrors the runtime read in
741
+ // config-loader.ts (`embed_enabled !== false`); keep the two in sync so config
742
+ // introspection never reports a default that contradicts runtime behavior.
743
+ embed_enabled: z5.boolean().optional().default(true),
769
744
  // Weight applied to the 0..1 cosine similarity before it joins the additive
770
745
  // score. Capped at 49 — strictly BELOW BM25_WEIGHT (50) — so a perfect vector
771
746
  // match (weight × 1) can never outscore a single strong BM25 term match. This
@@ -790,8 +765,22 @@ var fabricConfigSchema = z5.object({
790
765
  "fast-bge-base-en-v1.5",
791
766
  "fast-bge-base-en",
792
767
  "fast-all-MiniLM-L6-v2"
793
- ]).optional().default("fast-bge-small-zh-v1.5")
794
- });
768
+ ]).optional().default("fast-bge-small-zh-v1.5"),
769
+ // P1 recall-engine-refactor (TASK-003): content-channel fusion strategy.
770
+ // 'additive' (DEFAULT) = the historical weighted-sum path (BM25_WEIGHT·bm25 +
771
+ // vectorWeight·vector + structural). 'rrf' = Reciprocal Rank Fusion over the
772
+ // two CONTENT channels (bm25_rank, vector_rank) plus the unchanged structural
773
+ // boost. RRF is gated behind this flag and ships OFF — flipping the default to
774
+ // 'rrf' is a separate human decision gated on a one-off shadow run against the
775
+ // developer's real bound team store. no-query ranking is byte-identical under
776
+ // both values (the content channels contribute nothing without query terms).
777
+ fusion: z5.enum(["additive", "rrf"]).optional().default("additive")
778
+ });
779
+ var fabricConfigLoadSchema = fabricConfigSchema.merge(
780
+ z5.object({
781
+ required_stores: z5.array(requiredStoreEntrySchema).optional()
782
+ })
783
+ );
795
784
 
796
785
  // src/schemas/fabric-config-introspect.ts
797
786
  import { z as z6 } from "zod";
@@ -956,9 +945,11 @@ var PANEL_FIELDS = [
956
945
  // nudge_mode — the master switch for the human-visible nudge experience
957
946
  // (the most user-facing runtime knob, previously JSON-only). embed_enabled —
958
947
  // vector semantic recall, panel-editable now that config lives in `.fabric`
959
- // (A1); enabling also needs the host-side `fabric install --enable-embed`.
948
+ // (A1). TASK-004: default ON (fastembed is an optionalDependency, degrade-safe
949
+ // when absent) — this introspection default mirrors the runtime read in
950
+ // config-loader.ts so the panel never shows a default that contradicts behavior.
960
951
  makeEnumField("nudge_mode", "D_behavior", nudgeModeSchema.options, "normal"),
961
- makeBooleanField("embed_enabled", false)
952
+ makeBooleanField("embed_enabled", true)
962
953
  ];
963
954
 
964
955
  // src/schemas/store-stable-id.ts
@@ -1125,6 +1116,13 @@ var storeResolveInputSchema = z9.object({
1125
1116
  ),
1126
1117
  // Alias selected as the active write store for non-personal scopes, if any.
1127
1118
  activeWriteAlias: z9.string().min(1).optional(),
1119
+ // Alias/UUID of the ACTIVE personal store among possibly-many mounted
1120
+ // `personal:true` stores (语义 A: singleton-at-a-time). Drives the SINGLE
1121
+ // personal choke point (findPersonal) → both read-set inclusion and the
1122
+ // personal-scope write-target. Absent or dangling ⇒ the resolver falls back
1123
+ // to the first mounted personal, so legacy single-personal configs are
1124
+ // unchanged. Sourced from `~/.fabric/fabric-global.json` → active_personal_store.
1125
+ activePersonalAlias: z9.string().min(1).optional(),
1128
1126
  // Scope-aware write routes. Exact scope wins first, then longest prefix route.
1129
1127
  writeRoutes: z9.array(
1130
1128
  z9.object({
@@ -1187,7 +1185,16 @@ function createProjectRootResolver() {
1187
1185
 
1188
1186
  // src/resolver/store-resolver.ts
1189
1187
  function findPersonal(input) {
1190
- return input.mountedStores.find((s) => s.personal);
1188
+ const actives = input.mountedStores.filter((s) => s.personal);
1189
+ if (input.activePersonalAlias !== void 0) {
1190
+ const picked = actives.find(
1191
+ (s) => s.alias === input.activePersonalAlias || s.store_uuid === input.activePersonalAlias
1192
+ );
1193
+ if (picked !== void 0) {
1194
+ return picked;
1195
+ }
1196
+ }
1197
+ return actives[0];
1191
1198
  }
1192
1199
  function personalEntry(input) {
1193
1200
  const p = findPersonal(input);
@@ -1254,7 +1261,7 @@ function createStoreResolver() {
1254
1261
  warnings.push({
1255
1262
  code: "missing_store",
1256
1263
  ref: req.id,
1257
- message: `required store '${req.id}' is not mounted; run \`fabric store add\` (suggested remote: $personal)`
1264
+ message: `required store '${req.id}' is not mounted; run \`fabric store mount\` (suggested remote: $personal)`
1258
1265
  });
1259
1266
  }
1260
1267
  continue;
@@ -1267,7 +1274,7 @@ function createStoreResolver() {
1267
1274
  warnings.push({
1268
1275
  code: "missing_store",
1269
1276
  ref: req.id,
1270
- message: `required store '${req.id}' is not mounted; run \`fabric store add\`${suffix}`
1277
+ message: `required store '${req.id}' is not mounted; run \`fabric store mount\`${suffix}`
1271
1278
  });
1272
1279
  continue;
1273
1280
  }
@@ -1313,7 +1320,7 @@ function createStoreResolver() {
1313
1320
  {
1314
1321
  code: "missing_write_route",
1315
1322
  ref: scope,
1316
- message: `scope '${scope}' has no explicit write route; set \`fabric store route-write ${scope} <alias>\``
1323
+ message: `scope '${scope}' has no explicit write route; set \`fabric store switch-write <alias> --scope ${scope}\``
1317
1324
  }
1318
1325
  ]
1319
1326
  };
@@ -1776,7 +1783,12 @@ function loadProjectConfig(projectRoot) {
1776
1783
  if (!existsSync3(path)) {
1777
1784
  return null;
1778
1785
  }
1779
- return fabricConfigSchema.parse(JSON.parse(readFileSync3(path, "utf8")));
1786
+ const raw = JSON.parse(readFileSync3(path, "utf8"));
1787
+ const parsed = fabricConfigSchema.safeParse(raw);
1788
+ if (parsed.success) {
1789
+ return parsed.data;
1790
+ }
1791
+ return fabricConfigLoadSchema.parse(raw);
1780
1792
  }
1781
1793
  function saveProjectConfig(config, projectRoot) {
1782
1794
  const validated = fabricConfigSchema.parse(config);
@@ -1809,6 +1821,7 @@ function buildStoreResolveInput(projectRoot, globalRoot = resolveGlobalRoot()) {
1809
1821
  })
1810
1822
  ),
1811
1823
  ...project?.active_write_store === void 0 ? {} : { activeWriteAlias: project.active_write_store },
1824
+ ...global.active_personal_store === void 0 ? {} : { activePersonalAlias: global.active_personal_store },
1812
1825
  writeRoutes: project?.write_routes ?? [],
1813
1826
  ...project?.default_write_store === void 0 ? {} : { defaultWriteAlias: project.default_write_store }
1814
1827
  };
@@ -2018,7 +2031,7 @@ var MCP_STORE_AWARE_TOOLS = [
2018
2031
  "fab_plan_context",
2019
2032
  "fab_get_knowledge_sections",
2020
2033
  "fab_archive_scan",
2021
- "fab_extract_knowledge",
2034
+ "fab_propose",
2022
2035
  "fab_review"
2023
2036
  ];
2024
2037
  var storeAwareEntrySchema = z11.object({
@@ -2043,8 +2056,8 @@ var MCP_STORE_AWARE_CONTRACTS = {
2043
2056
  surfacesEntries: false,
2044
2057
  echoesWrittenStore: false
2045
2058
  },
2046
- fab_extract_knowledge: {
2047
- tool: "fab_extract_knowledge",
2059
+ fab_propose: {
2060
+ tool: "fab_propose",
2048
2061
  surfacesEntries: false,
2049
2062
  echoesWrittenStore: true
2050
2063
  },
@@ -3221,6 +3234,34 @@ var eventLedgerEventSchema = z16.discriminatedUnion("event_type", [
3221
3234
  var CJK_CLASS = "\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\u3040-\\u30ff\\uac00-\\ud7af";
3222
3235
  var RUN_RE = new RegExp(`[a-z0-9]+|[${CJK_CLASS}]+`, "gu");
3223
3236
  var CJK_FIRST_RE = new RegExp(`[${CJK_CLASS}]`, "u");
3237
+ var STOP_WORDS = /* @__PURE__ */ new Set([
3238
+ "the",
3239
+ "a",
3240
+ "an",
3241
+ "and",
3242
+ "or",
3243
+ "of",
3244
+ "to",
3245
+ "in",
3246
+ "on",
3247
+ "for",
3248
+ "is",
3249
+ "it",
3250
+ "with",
3251
+ "as",
3252
+ "at",
3253
+ "by",
3254
+ "be",
3255
+ "are",
3256
+ "was",
3257
+ "were",
3258
+ "this",
3259
+ "that",
3260
+ "from",
3261
+ "but",
3262
+ "not"
3263
+ ]);
3264
+ var MAX_CJK_NGRAM = 3;
3224
3265
  function tokenize(text) {
3225
3266
  if (text.length === 0) {
3226
3267
  return [];
@@ -3234,12 +3275,17 @@ function tokenize(text) {
3234
3275
  if (CJK_FIRST_RE.test(run[0])) {
3235
3276
  if (run.length === 1) {
3236
3277
  tokens.push(run);
3237
- } else {
3238
- for (let i = 0; i < run.length - 1; i += 1) {
3239
- tokens.push(run.slice(i, i + 2));
3278
+ continue;
3279
+ }
3280
+ for (let n = 2; n <= MAX_CJK_NGRAM; n += 1) {
3281
+ if (run.length < n) {
3282
+ break;
3283
+ }
3284
+ for (let i = 0; i + n <= run.length; i += 1) {
3285
+ tokens.push(run.slice(i, i + n));
3240
3286
  }
3241
3287
  }
3242
- } else {
3288
+ } else if (run.length >= 2 && !STOP_WORDS.has(run)) {
3243
3289
  tokens.push(run);
3244
3290
  }
3245
3291
  }
@@ -3258,6 +3304,10 @@ export {
3258
3304
  FabExtractKnowledgeInputSchema,
3259
3305
  FabExtractKnowledgeInputShape,
3260
3306
  FabExtractKnowledgeOutputSchema,
3307
+ FabPendingInputSchema,
3308
+ FabPendingInputShape,
3309
+ FabPendingOutputSchema,
3310
+ FabPendingOutputShape,
3261
3311
  FabReviewInputSchema,
3262
3312
  FabReviewInputShape,
3263
3313
  FabReviewOutputSchema,
@@ -3285,6 +3335,7 @@ export {
3285
3335
  PROTECTED_TOKENS,
3286
3336
  ProposedReasonSchema,
3287
3337
  REDACTION_PLACEHOLDER_PREFIX,
3338
+ SCOPE_COORDINATE_HINT,
3288
3339
  SCOPE_COORDINATE_PATTERN,
3289
3340
  STORES_ROOT_DIR,
3290
3341
  STORE_ALIAS_PATTERN,
@@ -3355,7 +3406,9 @@ export {
3355
3406
  eventsRotatedEventSchema,
3356
3407
  explainStore,
3357
3408
  fabExtractKnowledgeAnnotations,
3409
+ fabPendingAnnotations,
3358
3410
  fabReviewAnnotations,
3411
+ fabricConfigLoadSchema,
3359
3412
  fabricConfigSchema,
3360
3413
  fabricEventSchema,
3361
3414
  fabricLanguageSchema,
@@ -3400,6 +3453,7 @@ export {
3400
3453
  initStore,
3401
3454
  installDiffAppliedEventSchema,
3402
3455
  isKnowledgeStableId,
3456
+ isNonPersonalRequiredStore,
3403
3457
  isPersonalLeakIntoSharedStore,
3404
3458
  isPersonalScope,
3405
3459
  knowledgeArchiveAttemptedEventSchema,
@@ -3452,6 +3506,7 @@ export {
3452
3506
  metaReconciledEventSchema,
3453
3507
  metaReconciledOnStartupEventSchema,
3454
3508
  metaUpdatedEventSchema,
3509
+ migrateRequiredStores,
3455
3510
  mountedStoreSchema,
3456
3511
  normalizeCiteTag,
3457
3512
  normalizeLocale,