@fenglimg/fabric-shared 2.2.0-rc.1 → 2.2.0-rc.4

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
@@ -1,4 +1,12 @@
1
- import "./chunk-LXNCAKJZ.js";
1
+ import {
2
+ BOOTSTRAP_CANONICAL,
3
+ BOOTSTRAP_MARKER_BEGIN,
4
+ BOOTSTRAP_MARKER_END,
5
+ BOOTSTRAP_REGEX,
6
+ LEGACY_KB_MARKER_BEGIN,
7
+ LEGACY_KB_MARKER_END,
8
+ LEGACY_KB_REGEX
9
+ } from "./chunk-AFT7DB4P.js";
2
10
  import {
3
11
  PROTECTED_TOKENS,
4
12
  createTranslator,
@@ -8,7 +16,11 @@ import {
8
16
  normalizeLocale,
9
17
  resolveFabricLocale,
10
18
  zhCNMessages
11
- } from "./chunk-7TZ2PMVH.js";
19
+ } from "./chunk-KUYCTRFI.js";
20
+ import {
21
+ atomicWriteJson,
22
+ withFileLock
23
+ } from "./chunk-4N6DMOOW.js";
12
24
  import {
13
25
  FabExtractKnowledgeInputSchema,
14
26
  FabExtractKnowledgeInputShape,
@@ -56,16 +68,8 @@ import {
56
68
  recallInputSchema,
57
69
  recallOutputSchema,
58
70
  structuredWarningSchema
59
- } from "./chunk-JEXTOQVV.js";
60
- import {
61
- BOOTSTRAP_CANONICAL,
62
- BOOTSTRAP_MARKER_BEGIN,
63
- BOOTSTRAP_MARKER_END,
64
- BOOTSTRAP_REGEX,
65
- LEGACY_KB_MARKER_BEGIN,
66
- LEGACY_KB_MARKER_END,
67
- LEGACY_KB_REGEX
68
- } from "./chunk-TX2XZ7AW.js";
71
+ } from "./chunk-355LUDLW.js";
72
+ import "./chunk-LXNCAKJZ.js";
69
73
 
70
74
  // src/schemas/agents-meta.ts
71
75
  import { z } from "zod";
@@ -358,6 +362,18 @@ var storeIdentitySchema = z5.object({
358
362
  // time in P2). Open coordinate strings — see schemas/scope.ts.
359
363
  allowed_scopes: z5.array(z5.string()).optional()
360
364
  }).strict();
365
+ var STORE_PROJECT_ID_PATTERN = /^[a-z0-9_-]+$/u;
366
+ var storeProjectSchema = z5.object({
367
+ // Single scope segment forming the `project:<id>` coordinate. Immutable.
368
+ id: z5.string().regex(STORE_PROJECT_ID_PATTERN, "project id must be a single lowercase [a-z0-9_-] segment"),
369
+ // Optional human-facing label surfaced in `store project list`.
370
+ name: z5.string().optional(),
371
+ // ISO-8601. When the project was first registered in this store.
372
+ created_at: z5.string()
373
+ }).strict();
374
+ var storeProjectsFileSchema = z5.object({
375
+ projects: z5.array(storeProjectSchema).default([])
376
+ }).strict();
361
377
  var requiredStoreEntrySchema = z5.object({
362
378
  id: z5.string().min(1),
363
379
  suggested_remote: z5.union([z5.string().min(1), z5.literal(PERSONAL_STORE_SENTINEL)]).optional()
@@ -371,6 +387,16 @@ var STORE_KNOWLEDGE_TYPE_DIRS = [
371
387
  ];
372
388
  var STORE_LAYOUT = {
373
389
  identityFile: "store.json",
390
+ // Store-internal project registry (W1/A2). Committed parallel to store.json.
391
+ projectsFile: "projects.json",
392
+ // v2.2 W4 (agents.meta decolo) — per-store monotonic stable_id counters.
393
+ // COMMITTED parallel to store.json/projects.json (NOT gitignored like the
394
+ // derived agents.meta) because the counter ledger is non-derivable state that
395
+ // must travel with the store on clone: a fresh clone rebuilding from disk-max
396
+ // would re-mint a deleted entry's id and corrupt cite history (KT-DEC-0004
397
+ // monotonic invariant). Replaces the retired co-location
398
+ // <projectRoot>/.fabric/agents.meta.json#counters.
399
+ countersFile: "counters.json",
374
400
  knowledgeDir: "knowledge",
375
401
  bindingsDir: "bindings",
376
402
  stateDir: "state"
@@ -455,6 +481,15 @@ var fabricConfigSchema = z6.object({
455
481
  // activeWriteAlias. Absent → no active write store yet. Personal-scope
456
482
  // writes always target the implicit personal store regardless (R5#3).
457
483
  active_write_store: z6.string().optional(),
484
+ // v2.1 global-refactor (W1/A2 — store project registry): the project this repo
485
+ // currently participates in, as the SINGLE scope segment forming the
486
+ // `project:<id>` coordinate (schemas/scope.ts). Set by `store bind --project
487
+ // <id>` (validated against the bound store's projects.json). Drives:
488
+ // - write: project-scoped writes get `semantic_scope: project:<active_project>`.
489
+ // - recall: keep `project:<active_project>` + non-project coords, drop other
490
+ // `project:*` entries (G-FILTER).
491
+ // Absent → the repo has no project binding; recall does not project-filter.
492
+ active_project: z6.string().optional(),
458
493
  // rc.17 (R-cut): the dev/test fixture-path config field was removed
459
494
  // end-to-end. The `EXTERNAL_FIXTURE_PATH` env var is now the sole source
460
495
  // consumed by `resolveDevMode()`. No z.preprocess alias — pre-rc.17
@@ -601,6 +636,30 @@ var fabricConfigSchema = z6.object({
601
636
  // high-contract-criticality projects. Other strategies (time-based,
602
637
  // token-budget) deferred to rc.35 per plan locked-decisions 2026-05-26.
603
638
  cite_evict_interval: z6.number().int().min(0).optional().default(0),
639
+ // v2.1 ⑤ cite-redesign (P5): recall-based cite-accounting hook config. The
640
+ // rc.34 cite_evict_interval turn-counter above is superseded by the
641
+ // PreToolUse(Edit/Write) recall-aware nudge in cite-policy-evict.cjs; the old
642
+ // key is retained for back-compat (inert now that the hook moved off
643
+ // UserPromptSubmit). `cite_recall_nudge` is the master switch (default true =
644
+ // ON); set false to silence the "改前先 fab_recall" nudge entirely (mirrors
645
+ // the cite_evict_interval=0 opt-out convention). `cite_recall_window_minutes`
646
+ // bounds how far back an in-session fab_recall counts as "informing" the edit
647
+ // (default 30; 0 = unbounded).
648
+ cite_recall_nudge: z6.boolean().optional().default(true),
649
+ cite_recall_window_minutes: z6.number().int().min(0).optional().default(30),
650
+ // F2: glob exemptions for the cite nudge (cite-policy-evict.cjs). Edit paths
651
+ // matching any glob skip the "改前先 fab_recall" nudge — meta/orchestration
652
+ // files (e.g. `.workflow/` scratchpads) are not source the cite policy
653
+ // governs. MERGED with the hook's built-in [".workflow/**"] default; an
654
+ // omitted/empty value keeps just that default. `*` = within a path segment,
655
+ // `**` = across segments.
656
+ cite_nudge_ignore_globs: z6.array(z6.string()).optional(),
657
+ // v2.1 ④ conflict-detection (P4): bm25 content-similarity threshold (0..1)
658
+ // for the knowledge-conflict lint (`fabric doctor --lint-conflicts`). A
659
+ // same-(type,layer) pair whose normalized bm25 similarity reaches this floor
660
+ // is surfaced as a candidate (possible duplicate OR conflict). Conservative
661
+ // default 0.5 — raise to reduce noise, lower to catch looser pairs.
662
+ conflict_lint_similarity_threshold: z6.number().min(0).max(1).optional().default(0.5),
604
663
  // v2.0.0-rc.22 Scope A T3: sliding-window retention (in days) for the
605
664
  // event ledger rotation primitive (`rotateEventLedgerIfNeeded`). Lines
606
665
  // whose `ts` is older than `now - fabric_event_retention_days * 86_400_000`
@@ -703,13 +762,25 @@ var fabricConfigSchema = z6.object({
703
762
  // 0..168 (one week).
704
763
  hint_narrow_cooldown_hours: z6.number().int().min(0).max(168).optional().default(0),
705
764
  // v2.0.0-rc.33 W4-B3 (T5 P2): per-maturity inactivity thresholds (days)
706
- // driving orphan_demote. Hardcoded at stable=90/endorsed=30/draft=14 in
765
+ // driving orphan_demote. Hardcoded at proven=90/verified=30/draft=14 in
707
766
  // rc.32; chatty workspaces want them tighter, slow ones want them looser.
708
767
  // Each field optional; absent → defaults inside doctor.ts apply. Ranges
709
768
  // chosen so a typo can't accidentally disable the lint (min 1).
769
+ //
770
+ // v2.2 W3-T5 (F-MATURITY-ENDORSED): the canonical maturity enum is
771
+ // draft/verified/proven (KT-DEC-0005), but these threshold keys historically
772
+ // used the legacy stable/endorsed vocabulary — a config authored with the
773
+ // canonical names could never tune the proven/verified tiers. The canonical
774
+ // keys below are the preferred form; the legacy keys are retained for
775
+ // backward-compat (a config written before this fix keeps working). The
776
+ // loader maps stable→proven / endorsed→verified, canonical taking precedence.
777
+ orphan_demote_proven_days: z6.number().int().min(1).max(3650).optional(),
778
+ orphan_demote_verified_days: z6.number().int().min(1).max(3650).optional(),
779
+ orphan_demote_draft_days: z6.number().int().min(1).max(3650).optional(),
780
+ // Legacy aliases (deprecated; map to proven/verified). Kept so existing
781
+ // configs do not silently lose their tuning.
710
782
  orphan_demote_stable_days: z6.number().int().min(1).max(3650).optional(),
711
783
  orphan_demote_endorsed_days: z6.number().int().min(1).max(3650).optional(),
712
- orphan_demote_draft_days: z6.number().int().min(1).max(3650).optional(),
713
784
  // v2.0.0-rc.33 W4-A3 (T4 P2): per-entry summary truncation length used by
714
785
  // knowledge-hint-{broad,narrow}.cjs. Hard-coded at 80 chars in rc.32 — too
715
786
  // short for entries with parameterized summaries (e.g. "Use bcrypt with
@@ -766,7 +837,25 @@ var fabricConfigSchema = z6.object({
766
837
  // ENFORCES the "vectors supplement, never override lexical relevance"
767
838
  // invariant in the schema rather than leaving it to a comment (W2-REVIEW codex
768
839
  // MED-4). Range 0..49; default 30.
769
- embed_weight: z6.number().int().min(0).max(49).optional().default(30)
840
+ embed_weight: z6.number().int().min(0).max(49).optional().default(30),
841
+ // v2.1 ③ vector-chinese-model (P3): which fastembed model to load. The prior
842
+ // code pinned fastembed's English default (bge-small-en-v1.5) — wrong for the
843
+ // Chinese-heavy zh-CN-hybrid KB. Values are the fastembed@2.x EmbeddingModel
844
+ // enum strings. Default `fast-bge-small-zh-v1.5` (BGESmallZH): light, fast,
845
+ // Chinese-capable (bm25 already covers English/code tokens; the vector term
846
+ // supplements Chinese semantics). `fast-multilingual-e5-large` (MLE5Large) is
847
+ // available for full multilingual recall at a ~1GB download + slower CPU cost.
848
+ // (V1 research: fastembed@2.1.0 has NO multilingual-e5-SMALL — the originally
849
+ // planned pin — so bge-small-zh is the light Chinese choice.)
850
+ embed_model: z6.enum([
851
+ "fast-bge-small-zh-v1.5",
852
+ "fast-multilingual-e5-large",
853
+ "fast-bge-small-en-v1.5",
854
+ "fast-bge-small-en",
855
+ "fast-bge-base-en-v1.5",
856
+ "fast-bge-base-en",
857
+ "fast-all-MiniLM-L6-v2"
858
+ ]).optional().default("fast-bge-small-zh-v1.5")
770
859
  });
771
860
 
772
861
  // src/schemas/fabric-config-introspect.ts
@@ -1437,7 +1526,7 @@ function resolveCandidates(candidates, options = {}) {
1437
1526
 
1438
1527
  // src/store/core.ts
1439
1528
  import { execFileSync } from "child_process";
1440
- import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, writeFileSync } from "fs";
1529
+ import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1441
1530
  import { join as join2 } from "path";
1442
1531
  var STORE_PENDING_DIR = "pending";
1443
1532
  var STORE_GITIGNORE = [
@@ -1492,6 +1581,38 @@ function listStoreKnowledge(store) {
1492
1581
  function readKnowledgeAcrossStores(stores) {
1493
1582
  return stores.flatMap((store) => listStoreKnowledge(store));
1494
1583
  }
1584
+ function storeProjectsPath(storeDir) {
1585
+ return join2(storeDir, STORE_LAYOUT.projectsFile);
1586
+ }
1587
+ function readStoreProjects(storeDir) {
1588
+ const path = storeProjectsPath(storeDir);
1589
+ if (!existsSync2(path)) {
1590
+ return [];
1591
+ }
1592
+ let raw;
1593
+ try {
1594
+ raw = JSON.parse(readFileSync2(path, "utf8"));
1595
+ } catch {
1596
+ return [];
1597
+ }
1598
+ const parsed = storeProjectsFileSchema.safeParse(raw);
1599
+ return parsed.success ? parsed.data.projects : [];
1600
+ }
1601
+ function storeHasProject(storeDir, id) {
1602
+ return readStoreProjects(storeDir).some((p) => p.id === id);
1603
+ }
1604
+ function addStoreProject(storeDir, project) {
1605
+ const parsed = storeProjectSchema.parse(project);
1606
+ const existing = readStoreProjects(storeDir);
1607
+ if (existing.some((p) => p.id === parsed.id)) {
1608
+ throw new Error(`project '${parsed.id}' already exists in store at ${storeDir}`);
1609
+ }
1610
+ const next = [...existing, parsed];
1611
+ const validated = storeProjectsFileSchema.parse({ projects: next });
1612
+ writeFileSync(storeProjectsPath(storeDir), `${JSON.stringify(validated, null, 2)}
1613
+ `, "utf8");
1614
+ return validated.projects;
1615
+ }
1495
1616
  function aggregatePendingAcrossStores(stores) {
1496
1617
  return stores.flatMap(
1497
1618
  (store) => listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR)).map((file) => ({
@@ -1503,6 +1624,151 @@ function aggregatePendingAcrossStores(stores) {
1503
1624
  );
1504
1625
  }
1505
1626
 
1627
+ // src/store/store-counters.ts
1628
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync2 } from "fs";
1629
+ import { join as join3 } from "path";
1630
+ function storeCountersPath(storeDir) {
1631
+ return join3(storeDir, STORE_LAYOUT.countersFile);
1632
+ }
1633
+ function readStoreCounters(storeDir) {
1634
+ let raw;
1635
+ try {
1636
+ raw = readFileSync3(storeCountersPath(storeDir), "utf8");
1637
+ } catch {
1638
+ return defaultAgentsMetaCounters();
1639
+ }
1640
+ let parsed;
1641
+ try {
1642
+ parsed = JSON.parse(raw);
1643
+ } catch {
1644
+ return defaultAgentsMetaCounters();
1645
+ }
1646
+ const result = AgentsMetaCountersSchema.safeParse(parsed);
1647
+ return result.success ? result.data : defaultAgentsMetaCounters();
1648
+ }
1649
+ async function allocateStoreKnowledgeId(layer, type, storeDir) {
1650
+ const countersPath = storeCountersPath(storeDir);
1651
+ return withFileLock(`${countersPath}.lock`, async () => {
1652
+ const counters = readStoreCounters(storeDir);
1653
+ const { id, nextCounters } = allocateKnowledgeId(layer, type, counters);
1654
+ await atomicWriteJson(countersPath, nextCounters, { indent: 2 });
1655
+ return id;
1656
+ });
1657
+ }
1658
+ function readEntryId(file) {
1659
+ let content;
1660
+ try {
1661
+ content = readFileSync3(file, "utf8");
1662
+ } catch {
1663
+ return null;
1664
+ }
1665
+ const match = content.match(/^id:\s*(\S+)\s*$/mu);
1666
+ if (match) {
1667
+ return match[1] ?? null;
1668
+ }
1669
+ const stem = file.slice(file.lastIndexOf("/") + 1).replace(/\.md$/u, "");
1670
+ const idPart = stem.split("--")[0];
1671
+ return idPart.length > 0 ? idPart : null;
1672
+ }
1673
+ function reconcileStoreCounters(storeDir) {
1674
+ const current = readStoreCounters(storeDir);
1675
+ const next = {
1676
+ KP: { ...current.KP },
1677
+ KT: { ...current.KT }
1678
+ };
1679
+ for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
1680
+ const dir = join3(storeDir, STORE_LAYOUT.knowledgeDir, type);
1681
+ if (!existsSync3(dir)) {
1682
+ continue;
1683
+ }
1684
+ for (const name of readdirSync3(dir)) {
1685
+ if (!name.endsWith(".md")) {
1686
+ continue;
1687
+ }
1688
+ const parsed = parseKnowledgeId(readEntryId(join3(dir, name)) ?? "");
1689
+ if (parsed === null) {
1690
+ continue;
1691
+ }
1692
+ const layerKey = parsed.layer === "personal" ? "KP" : "KT";
1693
+ const typeCode = KNOWLEDGE_TYPE_CODES[parsed.type];
1694
+ next[layerKey][typeCode] = Math.max(next[layerKey][typeCode], parsed.counter);
1695
+ }
1696
+ }
1697
+ writeFileSync2(storeCountersPath(storeDir), `${JSON.stringify(next, null, 2)}
1698
+ `, "utf8");
1699
+ return next;
1700
+ }
1701
+
1702
+ // src/store/global-config-io.ts
1703
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1704
+ import { homedir } from "os";
1705
+ import { join as join4 } from "path";
1706
+ function resolveGlobalRoot() {
1707
+ return join4(process.env.FABRIC_HOME ?? homedir(), ".fabric");
1708
+ }
1709
+ function globalConfigPath(globalRoot = resolveGlobalRoot()) {
1710
+ return join4(globalRoot, "fabric-global.json");
1711
+ }
1712
+ function loadGlobalConfig(globalRoot = resolveGlobalRoot()) {
1713
+ const path = globalConfigPath(globalRoot);
1714
+ if (!existsSync4(path)) {
1715
+ return null;
1716
+ }
1717
+ return globalConfigSchema.parse(JSON.parse(readFileSync4(path, "utf8")));
1718
+ }
1719
+ function saveGlobalConfig(config, globalRoot = resolveGlobalRoot()) {
1720
+ const validated = globalConfigSchema.parse(config);
1721
+ mkdirSync2(globalRoot, { recursive: true });
1722
+ writeFileSync3(globalConfigPath(globalRoot), `${JSON.stringify(validated, null, 2)}
1723
+ `, "utf8");
1724
+ }
1725
+
1726
+ // src/store/project-config-io.ts
1727
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
1728
+ import { join as join5 } from "path";
1729
+ function projectConfigPath(projectRoot) {
1730
+ return join5(projectRoot, ".fabric", "fabric-config.json");
1731
+ }
1732
+ function loadProjectConfig(projectRoot) {
1733
+ const path = projectConfigPath(projectRoot);
1734
+ if (!existsSync5(path)) {
1735
+ return null;
1736
+ }
1737
+ return fabricConfigSchema.parse(JSON.parse(readFileSync5(path, "utf8")));
1738
+ }
1739
+ function saveProjectConfig(config, projectRoot) {
1740
+ const validated = fabricConfigSchema.parse(config);
1741
+ mkdirSync3(join5(projectRoot, ".fabric"), { recursive: true });
1742
+ writeFileSync4(projectConfigPath(projectRoot), `${JSON.stringify(validated, null, 2)}
1743
+ `, "utf8");
1744
+ }
1745
+
1746
+ // src/store/resolve-input.ts
1747
+ function buildStoreResolveInput(projectRoot, globalRoot = resolveGlobalRoot()) {
1748
+ const global = loadGlobalConfig(globalRoot);
1749
+ if (global === null) {
1750
+ return null;
1751
+ }
1752
+ const project = loadProjectConfig(projectRoot);
1753
+ return {
1754
+ uid: global.uid,
1755
+ mountedStores: global.stores.map((s) => ({
1756
+ store_uuid: s.store_uuid,
1757
+ alias: s.alias,
1758
+ ...s.remote === void 0 ? {} : { remote: s.remote },
1759
+ writable: s.writable ?? true,
1760
+ personal: s.personal ?? false
1761
+ })),
1762
+ requiredStores: (project?.required_stores ?? []).map(
1763
+ (r) => ({
1764
+ id: r.id,
1765
+ ...r.suggested_remote === void 0 ? {} : { suggested_remote: r.suggested_remote }
1766
+ })
1767
+ ),
1768
+ ...project?.active_write_store === void 0 ? {} : { activeWriteAlias: project.active_write_store }
1769
+ };
1770
+ }
1771
+
1506
1772
  // src/store/secret-scan.ts
1507
1773
  var SECRET_RULES = [
1508
1774
  { rule: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/ },
@@ -1540,6 +1806,10 @@ function redactSecrets(content) {
1540
1806
  return out;
1541
1807
  }
1542
1808
  function scrubRemoteUrl(remote) {
1809
+ const httpStripped = remote.replace(/^(https?:\/\/)[^/@]+@/i, "$1");
1810
+ if (httpStripped !== remote) {
1811
+ return httpStripped;
1812
+ }
1543
1813
  return remote.replace(
1544
1814
  /^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/@]*:[^/@]*@/,
1545
1815
  "$1"
@@ -1719,10 +1989,24 @@ var resolvedBindingsSnapshotSchema = z14.object({
1719
1989
  }).strict();
1720
1990
 
1721
1991
  // src/store/bindings.ts
1722
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1723
- import { join as join3 } from "path";
1992
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1993
+ import { join as join6, resolve, sep } from "path";
1994
+ var SAFE_PROJECT_ID = /^[A-Za-z0-9._-]+$/;
1995
+ function assertSafeProjectId(projectId) {
1996
+ if (!SAFE_PROJECT_ID.test(projectId) || projectId.includes("..")) {
1997
+ throw new Error(
1998
+ `bindingsSnapshotPath: refusing unsafe project_id ${JSON.stringify(projectId)} (must match ${SAFE_PROJECT_ID} and contain no "..")`
1999
+ );
2000
+ }
2001
+ }
1724
2002
  function bindingsSnapshotPath(globalRoot, projectId) {
1725
- return join3(globalRoot, GLOBAL_STATE_DIR, GLOBAL_BINDINGS_DIR, `${projectId}_resolved.json`);
2003
+ assertSafeProjectId(projectId);
2004
+ const bindingsDir = resolve(join6(globalRoot, GLOBAL_STATE_DIR, GLOBAL_BINDINGS_DIR));
2005
+ const path = resolve(join6(bindingsDir, `${projectId}_resolved.json`));
2006
+ if (path !== bindingsDir && !path.startsWith(bindingsDir + sep)) {
2007
+ throw new Error(`bindingsSnapshotPath: resolved path escapes bindings dir for ${JSON.stringify(projectId)}`);
2008
+ }
2009
+ return path;
1726
2010
  }
1727
2011
  function writeBindingsSnapshot(options) {
1728
2012
  const resolver = createStoreResolver();
@@ -1736,18 +2020,18 @@ function writeBindingsSnapshot(options) {
1736
2020
  write_target: target
1737
2021
  });
1738
2022
  const path = bindingsSnapshotPath(options.globalRoot, options.projectId);
1739
- mkdirSync2(join3(path, ".."), { recursive: true });
1740
- writeFileSync2(path, `${JSON.stringify(snapshot, null, 2)}
2023
+ mkdirSync4(join6(path, ".."), { recursive: true });
2024
+ writeFileSync5(path, `${JSON.stringify(snapshot, null, 2)}
1741
2025
  `, "utf8");
1742
2026
  return snapshot;
1743
2027
  }
1744
2028
  function readBindingsSnapshot(globalRoot, projectId) {
1745
2029
  const path = bindingsSnapshotPath(globalRoot, projectId);
1746
- if (!existsSync3(path)) {
2030
+ if (!existsSync6(path)) {
1747
2031
  return null;
1748
2032
  }
1749
2033
  try {
1750
- const parsed = resolvedBindingsSnapshotSchema.safeParse(JSON.parse(readFileSync2(path, "utf8")));
2034
+ const parsed = resolvedBindingsSnapshotSchema.safeParse(JSON.parse(readFileSync6(path, "utf8")));
1751
2035
  return parsed.success ? parsed.data : null;
1752
2036
  } catch {
1753
2037
  return null;
@@ -2438,6 +2722,15 @@ var assistantTurnObservedEventSchema = z18.object({
2438
2722
  skip_reason: z18.string().nullable()
2439
2723
  })
2440
2724
  ).default([]),
2725
+ // lifecycle-refactor W3-T4 (§2 store 轴 / store-qualified 观测): per-cite store
2726
+ // qualifier, index-aligned with cite_ids. Mirrors the cite-line-parser's
2727
+ // `cite_stores` output (`<alias-or-uuid>:<id>` → the qualifier; a bare id →
2728
+ // null). Persists the store provenance the parser already extracts so
2729
+ // doctor --cite-coverage can break compliance down per store WITHOUT joining
2730
+ // against the store registry. Additive `.optional()` (NOT `.default([])`) so
2731
+ // existing inline event constructors stay valid without supplying it — pre-W3-T4
2732
+ // events parse with the field absent and bucket under the project-local default.
2733
+ cite_stores: z18.array(z18.string().nullable()).optional(),
2441
2734
  client: z18.enum(["cc", "codex", "cursor"]).optional(),
2442
2735
  turn_id: z18.string(),
2443
2736
  envelope_index: z18.number().int().nonnegative().optional(),
@@ -2580,6 +2873,29 @@ var clientCapabilitySnapshotEventSchema = z18.object({
2580
2873
  capabilities: z18.array(z18.string()),
2581
2874
  version: z18.string()
2582
2875
  });
2876
+ var sessionEndedEventSchema = z18.object({
2877
+ ...eventLedgerEnvelopeSchema,
2878
+ event_type: z18.literal("session_ended")
2879
+ });
2880
+ var fileMutatedEventSchema = z18.object({
2881
+ ...eventLedgerEnvelopeSchema,
2882
+ event_type: z18.literal("file_mutated"),
2883
+ path: z18.string(),
2884
+ tool_call_id: z18.string(),
2885
+ tool_name: z18.string().optional(),
2886
+ source_event_id: z18.string().optional(),
2887
+ store_id: z18.string().optional()
2888
+ });
2889
+ var precompactObservedEventSchema = z18.object({
2890
+ ...eventLedgerEnvelopeSchema,
2891
+ event_type: z18.literal("precompact_observed")
2892
+ });
2893
+ var graphEdgeCandidateRequestedEventSchema = z18.object({
2894
+ ...eventLedgerEnvelopeSchema,
2895
+ event_type: z18.literal("graph_edge_candidate_requested"),
2896
+ stable_id: z18.string(),
2897
+ store: z18.string().optional()
2898
+ });
2583
2899
  var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
2584
2900
  knowledgeContextPlannedEventSchema,
2585
2901
  knowledgeSelectionEventSchema,
@@ -2672,7 +2988,12 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
2672
2988
  skillPhaseTransitionEventSchema,
2673
2989
  skillTriggerCandidateEventSchema,
2674
2990
  llmJudgeRunEventSchema,
2675
- clientCapabilitySnapshotEventSchema
2991
+ clientCapabilitySnapshotEventSchema,
2992
+ // lifecycle-refactor Wave 2 — dormant-hook activation markers.
2993
+ sessionEndedEventSchema,
2994
+ fileMutatedEventSchema,
2995
+ precompactObservedEventSchema,
2996
+ graphEdgeCandidateRequestedEventSchema
2676
2997
  ]);
2677
2998
 
2678
2999
  // src/text-tokenize.ts
@@ -2788,11 +3109,13 @@ export {
2788
3109
  STORE_KNOWLEDGE_TYPE_DIRS,
2789
3110
  STORE_LAYOUT,
2790
3111
  STORE_PENDING_DIR,
3112
+ STORE_PROJECT_ID_PATTERN,
2791
3113
  STORE_RESOLVER_WARNING_CODES,
2792
3114
  STORE_UUID_PATTERN,
2793
3115
  StableIdSchema,
2794
3116
  UID_SEGMENT_PATTERN,
2795
3117
  addMountedStore,
3118
+ addStoreProject,
2796
3119
  agentsIdentitySourceSchema,
2797
3120
  agentsLayerSchema,
2798
3121
  agentsMetaNodeSchema,
@@ -2801,6 +3124,7 @@ export {
2801
3124
  aggregatePendingAcrossStores,
2802
3125
  aiLedgerEntrySchema,
2803
3126
  allocateKnowledgeId,
3127
+ allocateStoreKnowledgeId,
2804
3128
  annotateIntentRequestSchema,
2805
3129
  archiveScanAnnotations,
2806
3130
  archiveScanInputSchema,
@@ -2813,6 +3137,7 @@ export {
2813
3137
  buildDebugBundle,
2814
3138
  buildFailureTrace,
2815
3139
  buildScanRecommendations,
3140
+ buildStoreResolveInput,
2816
3141
  candidateFileEntrySchema,
2817
3142
  citeContractMetricsSchema,
2818
3143
  citeContractPolicyActivatedEventSchema,
@@ -2850,6 +3175,7 @@ export {
2850
3175
  fabricConfigSchema,
2851
3176
  fabricEventSchema,
2852
3177
  fabricLanguageSchema,
3178
+ fileMutatedEventSchema,
2853
3179
  findMountedStore,
2854
3180
  findStoreExecutableViolations,
2855
3181
  forensicAssertionCoverageSchema,
@@ -2866,8 +3192,10 @@ export {
2866
3192
  formatKnowledgeId,
2867
3193
  getPanelFieldByKey,
2868
3194
  getPanelFields,
3195
+ globalConfigPath,
2869
3196
  globalConfigSchema,
2870
3197
  globalRefSchema,
3198
+ graphEdgeCandidateRequestedEventSchema,
2871
3199
  hasSecrets,
2872
3200
  historyStateQuerySchema,
2873
3201
  hookSignalEmittedEventSchema,
@@ -2926,6 +3254,8 @@ export {
2926
3254
  lintCrossStoreReferences,
2927
3255
  listStoreKnowledge,
2928
3256
  llmJudgeRunEventSchema,
3257
+ loadGlobalConfig,
3258
+ loadProjectConfig,
2929
3259
  localKnowledgeIdSchema,
2930
3260
  lockApprovedEventSchema,
2931
3261
  lockDriftEventSchema,
@@ -2956,6 +3286,8 @@ export {
2956
3286
  planContextInputSchema,
2957
3287
  planContextOutputSchema,
2958
3288
  planContextTopKSchema,
3289
+ precompactObservedEventSchema,
3290
+ projectConfigPath,
2959
3291
  projectRootGoldenCaseSchema,
2960
3292
  projectRootGoldenFileSchema,
2961
3293
  projectRootResolutionSchema,
@@ -2966,23 +3298,29 @@ export {
2966
3298
  readSetEntrySchema,
2967
3299
  readSetGoldenCaseSchema,
2968
3300
  readSetGoldenFileSchema,
3301
+ readStoreCounters,
2969
3302
  readStoreIdentity,
3303
+ readStoreProjects,
2970
3304
  reapplyCompletedEventSchema,
2971
3305
  recallAnnotations,
2972
3306
  recallInputSchema,
2973
3307
  recallOutputSchema,
2974
3308
  recognizeStoreDir,
3309
+ reconcileStoreCounters,
2975
3310
  redactSecrets,
2976
3311
  relevanceMigrationRunEventSchema,
2977
3312
  requiredStoreEntrySchema,
2978
3313
  resolveCandidates,
2979
3314
  resolveFabricLocale,
3315
+ resolveGlobalRoot,
2980
3316
  resolveRetrievalBudget,
2981
3317
  resolveStoreQualifiedId,
2982
3318
  resolvedBindingsSnapshotSchema,
2983
3319
  retrievalBudgetProfile,
2984
3320
  ruleDescriptionIndexItemSchema,
2985
3321
  ruleDescriptionSchema,
3322
+ saveGlobalConfig,
3323
+ saveProjectConfig,
2986
3324
  scanForSecrets,
2987
3325
  scopeCoordinateSchema,
2988
3326
  scopeRoot,
@@ -2990,14 +3328,19 @@ export {
2990
3328
  selectionTokenTtlMsSchema,
2991
3329
  serveLockClearedEventSchema,
2992
3330
  sessionArchiveAttemptedEventSchema,
3331
+ sessionEndedEventSchema,
2993
3332
  skillInvocationCompletedEventSchema,
2994
3333
  skillInvocationStartedEventSchema,
2995
3334
  skillPhaseTransitionEventSchema,
2996
3335
  skillTriggerCandidateEventSchema,
2997
3336
  storeAwareEntrySchema,
3337
+ storeCountersPath,
2998
3338
  storeCountersSchema,
3339
+ storeHasProject,
2999
3340
  storeIdentitySchema,
3000
3341
  storeKnowledgeTypeDir,
3342
+ storeProjectSchema,
3343
+ storeProjectsFileSchema,
3001
3344
  storeReadSetSchema,
3002
3345
  storeRelativePath,
3003
3346
  storeResolveInputSchema,