@fenglimg/fabric-shared 2.3.0-rc.8 → 2.3.0

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,7 @@ import {
7
7
  BOOTSTRAP_REGEX,
8
8
  matchBootstrapCanonicalLocale,
9
9
  resolveBootstrapCanonical
10
- } from "./chunk-KFFBQRL5.js";
10
+ } from "./chunk-V7IUN5U2.js";
11
11
  import "./chunk-LXNCAKJZ.js";
12
12
  import {
13
13
  PROTECTED_TOKENS,
@@ -16,7 +16,7 @@ import {
16
16
  enMessages,
17
17
  resolveFabricLocale,
18
18
  zhCNMessages
19
- } from "./chunk-P6INRP3A.js";
19
+ } from "./chunk-WXVFJBL5.js";
20
20
  import {
21
21
  GLOBAL_BINDINGS_DIR,
22
22
  GLOBAL_STATE_DIR,
@@ -40,6 +40,7 @@ import {
40
40
  resolveGlobalLocale,
41
41
  resolveGlobalRoot,
42
42
  saveGlobalConfig,
43
+ saveGlobalConfigAsync,
43
44
  storeAliasSchema,
44
45
  storeIdentitySchema,
45
46
  storeKnowledgeTypeDir,
@@ -51,7 +52,7 @@ import {
51
52
  storeRelativePath,
52
53
  storeRelativePathForMount,
53
54
  storeUuidSchema
54
- } from "./chunk-ASS2KBB7.js";
55
+ } from "./chunk-OX6DWUK4.js";
55
56
  import {
56
57
  atomicWriteJson,
57
58
  withFileLock
@@ -116,7 +117,7 @@ import {
116
117
  scopeCoordinateSchema,
117
118
  scopeRoot,
118
119
  structuredWarningSchema
119
- } from "./chunk-CVNJCAC7.js";
120
+ } from "./chunk-5M3HB554.js";
120
121
 
121
122
  // src/schemas/agents-meta.ts
122
123
  import { z } from "zod";
@@ -655,6 +656,11 @@ var fabricConfigSchema = z5.object({
655
656
  // relieve; until then they ship as inert-safe opt-outs.
656
657
  cite_policy_enabled: z5.boolean().optional().default(true),
657
658
  self_archive_policy_enabled: z5.boolean().optional().default(true),
659
+ // Peer micro-transfer P0-2: when true, fab_propose hard-refuses dump-shaped
660
+ // session_context / bodies (body altitude). Default false = warn-and-still-write
661
+ // so corpus recovery stays non-blocking. Env FABRIC_ALTITUDE_PROPOSE_GATE=1
662
+ // overrides this to true for CI/dogfood. Power-user JSON only (not on config TUI).
663
+ altitude_propose_gate: z5.boolean().optional().default(false),
658
664
  // v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the narrow PreToolUse hint
659
665
  // emitted by knowledge-hint-narrow.cjs. After filtering to entries whose
660
666
  // `relevance_scope === "narrow"` (rc.27 TASK-005 audit §2.5 fix), the hook
@@ -677,10 +683,13 @@ var fabricConfigSchema = z5.object({
677
683
  // fabric-hint Stop hook — knowledge-hint-broad re-fires on every
678
684
  // SessionStart by default (compact / clear / new-window), which on long
679
685
  // sessions becomes redundant noise. Setting to 1 means "emit the broad
680
- // menu at most once per hour"; 0 means "no cooldown, current behavior."
686
+ // menu at most once per hour"; 0 means "no cooldown, re-fire every open."
687
+ // ISS-20260713-033 shipped a non-zero quiet DEFAULT of 24 (at most once per
688
+ // day) so repeat session-opens stay quiet; set 0 for verbose/debug.
681
689
  // Range 0..168 (one week). Stored alongside fabric-hint's cooldown cache
682
690
  // under a distinct knowledge-hint-broad key.
683
- hint_broad_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
691
+ hint_broad_cooldown_hours: z5.number().int().min(0).max(168).optional().default(24),
692
+ // ISS-20260713-033
684
693
  // v2.0.0-rc.33 W2-5 (P1-8): cooldown for the narrow PreToolUse hint.
685
694
  // Same shape as hint_broad_cooldown_hours but applies to per-Edit hint
686
695
  // re-emission across the cooldown window — independent of E3 session-
@@ -837,6 +846,7 @@ function makePositiveIntField(key, defaultValue) {
837
846
  return {
838
847
  key,
839
848
  group: "B_hint_threshold",
849
+ type: "number",
840
850
  widget: "text",
841
851
  label_i18n_key: `cli.config.fields.${key}.label`,
842
852
  description_i18n_key: `cli.config.fields.${key}.description`,
@@ -866,6 +876,7 @@ function makeEnumField(key, group, enumValues, defaultValue) {
866
876
  return {
867
877
  key,
868
878
  group,
879
+ type: "string",
869
880
  widget: "select",
870
881
  label_i18n_key: `cli.config.fields.${key}.label`,
871
882
  description_i18n_key: `cli.config.fields.${key}.description`,
@@ -892,6 +903,7 @@ function makeBooleanField(key, defaultValue) {
892
903
  return {
893
904
  key,
894
905
  group: "D_behavior",
906
+ type: "boolean",
895
907
  widget: "select",
896
908
  label_i18n_key: `cli.config.fields.${key}.label`,
897
909
  description_i18n_key: `cli.config.fields.${key}.description`,
@@ -1450,7 +1462,7 @@ function hasScriptExtension(name) {
1450
1462
  return dot !== -1 && SCRIPT_EXTENSIONS.has(name.slice(dot).toLowerCase());
1451
1463
  }
1452
1464
  var STORE_SCAN_MAX_DEPTH = 32;
1453
- var STORE_SCAN_MAX_ENTRIES = 1e5;
1465
+ var STORE_SCAN_MAX_ENTRIES = 5e4;
1454
1466
  function findStoreExecutableViolations(absDir, options = {}) {
1455
1467
  const maxDepth = options.maxDepth ?? STORE_SCAN_MAX_DEPTH;
1456
1468
  const maxEntries = options.maxEntries ?? STORE_SCAN_MAX_ENTRIES;
@@ -1646,12 +1658,11 @@ async function initStore(absDir, identity, options = {}) {
1646
1658
  await mkdir(join2(absDir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR), { recursive: true });
1647
1659
  await mkdir(join2(absDir, STORE_LAYOUT.bindingsDir), { recursive: true });
1648
1660
  await mkdir(join2(absDir, STORE_LAYOUT.stateDir), { recursive: true });
1649
- await writeFile(identityFile, `${JSON.stringify(parsed, null, 2)}
1650
- `, "utf8");
1651
1661
  await writeFile(join2(absDir, ".gitignore"), STORE_GITIGNORE, "utf8");
1652
1662
  if (options.git !== false) {
1653
1663
  await git(absDir, ["init", "-b", "main"]);
1654
1664
  }
1665
+ await atomicWriteJson(identityFile, parsed, { indent: 2 });
1655
1666
  const readBack = await readStoreIdentityAsync(absDir);
1656
1667
  if (readBack === null) {
1657
1668
  throw new Error(`store init wrote an unrecognizable store.json at ${absDir}`);
@@ -1699,46 +1710,94 @@ async function readKnowledgeAcrossStores(stores) {
1699
1710
  function storeProjectsPath(storeDir) {
1700
1711
  return join2(storeDir, STORE_LAYOUT.projectsFile);
1701
1712
  }
1713
+ function storeProjectsLockPath(storeDir) {
1714
+ return `${storeProjectsPath(storeDir)}.lock`;
1715
+ }
1702
1716
  async function readStoreProjects(storeDir) {
1703
1717
  const path = storeProjectsPath(storeDir);
1718
+ let text;
1719
+ try {
1720
+ text = await readFile2(path, "utf8");
1721
+ } catch (err) {
1722
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
1723
+ return [];
1724
+ }
1725
+ throw err;
1726
+ }
1704
1727
  let raw;
1705
1728
  try {
1706
- raw = JSON.parse(await readFile2(path, "utf8"));
1729
+ raw = JSON.parse(text);
1707
1730
  } catch {
1708
- return [];
1731
+ throw new Error(
1732
+ `projects.json at ${path} is not valid JSON \u2014 refusing to treat as empty (would risk overwrite on next add)`
1733
+ );
1709
1734
  }
1710
1735
  const parsed = storeProjectsFileSchema.safeParse(raw);
1711
- return parsed.success ? parsed.data.projects : [];
1736
+ if (!parsed.success) {
1737
+ throw new Error(
1738
+ `projects.json at ${path} failed schema validation \u2014 refusing to treat as empty (would risk overwrite on next add): ${parsed.error.message}`
1739
+ );
1740
+ }
1741
+ return parsed.data.projects;
1712
1742
  }
1713
1743
  async function storeHasProject(storeDir, id) {
1714
1744
  return (await readStoreProjects(storeDir)).some((p) => p.id === id);
1715
1745
  }
1716
1746
  async function addStoreProject(storeDir, project) {
1717
1747
  const parsed = storeProjectSchema.parse(project);
1718
- const existing = await readStoreProjects(storeDir);
1719
- if (existing.some((p) => p.id === parsed.id)) {
1720
- throw new Error(`project '${parsed.id}' already exists in store at ${storeDir}`);
1721
- }
1722
- const next = [...existing, parsed];
1723
- const validated = storeProjectsFileSchema.parse({ projects: next });
1724
- await writeFile(storeProjectsPath(storeDir), `${JSON.stringify(validated, null, 2)}
1725
- `, "utf8");
1726
- return validated.projects;
1748
+ return withFileLock(storeProjectsLockPath(storeDir), async () => {
1749
+ const existing = await readStoreProjects(storeDir);
1750
+ if (existing.some((p) => p.id === parsed.id)) {
1751
+ throw new Error(`project '${parsed.id}' already exists in store at ${storeDir}`);
1752
+ }
1753
+ const next = [...existing, parsed];
1754
+ const validated = storeProjectsFileSchema.parse({ projects: next });
1755
+ await atomicWriteJson(storeProjectsPath(storeDir), validated, { indent: 2 });
1756
+ return validated.projects;
1757
+ });
1727
1758
  }
1728
1759
  async function aggregatePendingAcrossStores(stores) {
1729
- const lists = await Promise.all(stores.map(
1730
- async (store) => (await listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR))).map((file) => ({
1731
- store_uuid: store.store_uuid,
1732
- alias: store.alias,
1733
- type: STORE_PENDING_DIR,
1734
- file
1735
- }))
1736
- ));
1760
+ const lists = await Promise.all(
1761
+ stores.map(async (store) => {
1762
+ const pendingRoot = join2(store.dir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR);
1763
+ const refs = [];
1764
+ for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
1765
+ for (const file of await listMarkdown(join2(pendingRoot, type))) {
1766
+ refs.push({
1767
+ store_uuid: store.store_uuid,
1768
+ alias: store.alias,
1769
+ type: STORE_PENDING_DIR,
1770
+ file
1771
+ });
1772
+ }
1773
+ }
1774
+ for (const file of await listMarkdown(pendingRoot)) {
1775
+ refs.push({
1776
+ store_uuid: store.store_uuid,
1777
+ alias: store.alias,
1778
+ type: STORE_PENDING_DIR,
1779
+ file
1780
+ });
1781
+ }
1782
+ return refs;
1783
+ })
1784
+ );
1737
1785
  return lists.flat();
1738
1786
  }
1739
1787
 
1740
1788
  // src/store/store-counters.ts
1741
- import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync } from "fs";
1789
+ import {
1790
+ closeSync,
1791
+ existsSync as existsSync2,
1792
+ openSync,
1793
+ readFileSync as readFileSync2,
1794
+ readdirSync as readdirSync2,
1795
+ renameSync,
1796
+ rmSync,
1797
+ statSync,
1798
+ writeFileSync
1799
+ } from "fs";
1800
+ import { randomUUID } from "crypto";
1742
1801
  import { join as join3 } from "path";
1743
1802
  function storeCountersPath(storeDir) {
1744
1803
  return join3(storeDir, STORE_LAYOUT.countersFile);
@@ -1812,6 +1871,57 @@ function readEntryId(file) {
1812
1871
  const idPart = stem.split("--")[0];
1813
1872
  return idPart.length > 0 ? idPart : null;
1814
1873
  }
1874
+ function withCountersFileLockSync(lockPath, fn) {
1875
+ const token = `${process.pid}.${randomUUID()}`;
1876
+ const start = Date.now();
1877
+ const maxWaitMs = 1e4;
1878
+ const staleMs = 1e4;
1879
+ for (; ; ) {
1880
+ let fd;
1881
+ try {
1882
+ fd = openSync(lockPath, "wx");
1883
+ writeFileSync(lockPath, token, "utf8");
1884
+ try {
1885
+ closeSync(fd);
1886
+ } catch {
1887
+ }
1888
+ try {
1889
+ return fn();
1890
+ } finally {
1891
+ try {
1892
+ if (readFileSync2(lockPath, "utf8") === token) rmSync(lockPath, { force: true });
1893
+ } catch {
1894
+ }
1895
+ }
1896
+ } catch (err) {
1897
+ const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
1898
+ if (code !== "EEXIST") throw err;
1899
+ try {
1900
+ const st = statSync(lockPath);
1901
+ if (Date.now() - st.mtimeMs > staleMs) {
1902
+ const stale = readFileSync2(lockPath, "utf8");
1903
+ if (readFileSync2(lockPath, "utf8") === stale) rmSync(lockPath, { force: true });
1904
+ continue;
1905
+ }
1906
+ } catch {
1907
+ continue;
1908
+ }
1909
+ if (Date.now() - start > maxWaitMs) {
1910
+ throw new Error(`withCountersFileLockSync: timeout on ${lockPath}`);
1911
+ }
1912
+ const end = Date.now() + 20;
1913
+ while (Date.now() < end) {
1914
+ }
1915
+ }
1916
+ }
1917
+ }
1918
+ function atomicWriteCountersSync(path, counters) {
1919
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
1920
+ const body = `${JSON.stringify(counters, null, 2)}
1921
+ `;
1922
+ writeFileSync(tmp, body, "utf8");
1923
+ renameSync(tmp, path);
1924
+ }
1815
1925
  function reconcileStoreCounters(storeDir) {
1816
1926
  const current = readStoreCounters(storeDir);
1817
1927
  const next = {
@@ -1836,13 +1946,20 @@ function reconcileStoreCounters(storeDir) {
1836
1946
  next[layerKey][typeCode] = Math.max(next[layerKey][typeCode], parsed.counter);
1837
1947
  }
1838
1948
  }
1839
- writeFileSync(storeCountersPath(storeDir), `${JSON.stringify(next, null, 2)}
1840
- `, "utf8");
1949
+ withCountersFileLockSync(`${storeCountersPath(storeDir)}.lock`, () => {
1950
+ const latest = readStoreCounters(storeDir);
1951
+ for (const layer of ["KP", "KT"]) {
1952
+ for (const code of ["MOD", "DEC", "GLD", "PIT", "PRO"]) {
1953
+ next[layer][code] = Math.max(next[layer][code], latest[layer][code]);
1954
+ }
1955
+ }
1956
+ atomicWriteCountersSync(storeCountersPath(storeDir), next);
1957
+ });
1841
1958
  return next;
1842
1959
  }
1843
1960
 
1844
1961
  // src/store/project-config-io.ts
1845
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1962
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
1846
1963
  import { join as join4 } from "path";
1847
1964
  function projectConfigPath(projectRoot) {
1848
1965
  return join4(projectRoot, ".fabric", "fabric-config.json");
@@ -1862,8 +1979,20 @@ function loadProjectConfig(projectRoot) {
1862
1979
  function saveProjectConfig(config, projectRoot) {
1863
1980
  const validated = fabricConfigSchema.parse(config);
1864
1981
  mkdirSync(join4(projectRoot, ".fabric"), { recursive: true });
1865
- writeFileSync2(projectConfigPath(projectRoot), `${JSON.stringify(validated, null, 2)}
1866
- `, "utf8");
1982
+ const target = projectConfigPath(projectRoot);
1983
+ const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
1984
+ const body = `${JSON.stringify(validated, null, 2)}
1985
+ `;
1986
+ try {
1987
+ writeFileSync2(tmp, body, "utf8");
1988
+ renameSync2(tmp, target);
1989
+ } catch (err) {
1990
+ try {
1991
+ unlinkSync(tmp);
1992
+ } catch {
1993
+ }
1994
+ throw err;
1995
+ }
1867
1996
  }
1868
1997
 
1869
1998
  // src/store/resolve-input.ts
@@ -1976,6 +2105,31 @@ function scrubRemoteUrl(remote) {
1976
2105
  );
1977
2106
  }
1978
2107
 
2108
+ // src/store/git-remote-allowlist.ts
2109
+ var ALLOWED_REMOTE_RE = /^(?:https?:\/\/|ssh:\/\/|git:\/\/|git@[^:\s]+:)/i;
2110
+ var BLOCKED_REMOTE_RE = /^(?:ext::|file:\/\/|fd::|http\+\w+:\/\/|https\+\w+:\/\/)/i;
2111
+ var ABSOLUTE_LOCAL_PATH_RE = /^(?:\/|[A-Za-z]:[\\/])/;
2112
+ function isTestRuntime() {
2113
+ return process.env.VITEST !== void 0 || process.env.VITEST_WORKER_ID !== void 0;
2114
+ }
2115
+ function isAllowedGitRemote(remote) {
2116
+ const trimmed = remote.trim();
2117
+ if (trimmed.length === 0) return false;
2118
+ if (BLOCKED_REMOTE_RE.test(trimmed)) return false;
2119
+ if (trimmed.startsWith("-")) return false;
2120
+ if (ALLOWED_REMOTE_RE.test(trimmed)) return true;
2121
+ return isTestRuntime() && ABSOLUTE_LOCAL_PATH_RE.test(trimmed);
2122
+ }
2123
+ function assertAllowedGitRemote(remote) {
2124
+ const trimmed = remote.trim();
2125
+ if (!isAllowedGitRemote(trimmed)) {
2126
+ throw new Error(
2127
+ `git remote not allowlisted: ${remote} (allowed: https://, http://, ssh://, git://, git@host:path; blocked: ext::, file://, option-like)`
2128
+ );
2129
+ }
2130
+ return trimmed;
2131
+ }
2132
+
1979
2133
  // src/scanner/scan-recommendations.ts
1980
2134
  function buildScanRecommendations(input, t) {
1981
2135
  const recs = [];
@@ -2101,7 +2255,8 @@ var MCP_STORE_AWARE_TOOLS = [
2101
2255
  "fab_get_knowledge_sections",
2102
2256
  "fab_archive_scan",
2103
2257
  "fab_propose",
2104
- "fab_review"
2258
+ "fab_review",
2259
+ "fab_pending"
2105
2260
  ];
2106
2261
  var storeAwareEntrySchema = z11.object({
2107
2262
  stable_id: z11.string(),
@@ -2130,7 +2285,10 @@ var MCP_STORE_AWARE_CONTRACTS = {
2130
2285
  surfacesEntries: false,
2131
2286
  echoesWrittenStore: true
2132
2287
  },
2133
- fab_review: { tool: "fab_review", surfacesEntries: true, echoesWrittenStore: true }
2288
+ fab_review: { tool: "fab_review", surfacesEntries: true, echoesWrittenStore: true },
2289
+ // W3-K K2: read-only list/search lifted from fab_review — surfaces pending/
2290
+ // canonical entries with store provenance, does not write.
2291
+ fab_pending: { tool: "fab_pending", surfacesEntries: true, echoesWrittenStore: false }
2134
2292
  };
2135
2293
 
2136
2294
  // src/schemas/bindings-snapshot.ts
@@ -2177,9 +2335,9 @@ import {
2177
2335
  mkdirSync as mkdirSync2,
2178
2336
  readdirSync as readdirSync3,
2179
2337
  readFileSync as readFileSync4,
2180
- renameSync,
2181
- statSync,
2182
- unlinkSync,
2338
+ renameSync as renameSync3,
2339
+ statSync as statSync2,
2340
+ unlinkSync as unlinkSync2,
2183
2341
  writeFileSync as writeFileSync3
2184
2342
  } from "fs";
2185
2343
  import { join as join5, resolve, sep } from "path";
@@ -2230,7 +2388,7 @@ function countMarkdownFiles(dir) {
2230
2388
  }
2231
2389
  let mtimeMs;
2232
2390
  try {
2233
- mtimeMs = statSync(fullPath).mtimeMs;
2391
+ mtimeMs = statSync2(fullPath).mtimeMs;
2234
2392
  } catch {
2235
2393
  continue;
2236
2394
  }
@@ -2276,10 +2434,10 @@ function atomicWriteJsonSync(path, value) {
2276
2434
  try {
2277
2435
  writeFileSync3(tmpPath, `${JSON.stringify(value, null, 2)}
2278
2436
  `, "utf8");
2279
- renameSync(tmpPath, path);
2437
+ renameSync3(tmpPath, path);
2280
2438
  } catch (error) {
2281
2439
  try {
2282
- unlinkSync(tmpPath);
2440
+ unlinkSync2(tmpPath);
2283
2441
  } catch {
2284
2442
  }
2285
2443
  throw error;
@@ -3180,6 +3338,54 @@ var fileMutatedEventSchema = z16.object({
3180
3338
  source_event_id: z16.string().optional(),
3181
3339
  store_id: z16.string().optional()
3182
3340
  });
3341
+ var storeMountedEventSchema = z16.object({
3342
+ ...eventLedgerEnvelopeSchema,
3343
+ event_type: z16.literal("store_mounted"),
3344
+ alias: z16.string(),
3345
+ store_uuid: z16.string(),
3346
+ personal: z16.boolean().optional(),
3347
+ source: z16.string().optional()
3348
+ });
3349
+ var storeDetachedEventSchema = z16.object({
3350
+ ...eventLedgerEnvelopeSchema,
3351
+ event_type: z16.literal("store_detached"),
3352
+ alias: z16.string(),
3353
+ store_uuid: z16.string().optional(),
3354
+ source: z16.string().optional()
3355
+ });
3356
+ var storeBoundEventSchema = z16.object({
3357
+ ...eventLedgerEnvelopeSchema,
3358
+ event_type: z16.literal("store_bound"),
3359
+ alias: z16.string(),
3360
+ store_uuid: z16.string().optional(),
3361
+ project: z16.string().optional(),
3362
+ source: z16.string().optional()
3363
+ });
3364
+ var writeStoreSwitchedEventSchema = z16.object({
3365
+ ...eventLedgerEnvelopeSchema,
3366
+ event_type: z16.literal("write_store_switched"),
3367
+ alias: z16.string(),
3368
+ previous_alias: z16.string().optional(),
3369
+ // Named switch_kind (not `kind`) so it never shadows the envelope
3370
+ // kind: "fabric-event" discriminator required by every ledger row.
3371
+ switch_kind: z16.enum(["project_write", "personal"]).optional(),
3372
+ source: z16.string().optional()
3373
+ });
3374
+ var writeRouteChangedEventSchema = z16.object({
3375
+ ...eventLedgerEnvelopeSchema,
3376
+ event_type: z16.literal("write_route_changed"),
3377
+ scope: z16.string(),
3378
+ alias: z16.string(),
3379
+ previous_alias: z16.string().optional(),
3380
+ source: z16.string().optional()
3381
+ });
3382
+ var narrowHintFailedEventSchema = z16.object({
3383
+ ...eventLedgerEnvelopeSchema,
3384
+ event_type: z16.literal("narrow_hint_failed"),
3385
+ hook_name: z16.string(),
3386
+ reason: z16.string(),
3387
+ bin: z16.string().optional()
3388
+ });
3183
3389
  var knowledgeBodyReadEventSchema = z16.object({
3184
3390
  ...eventLedgerEnvelopeSchema,
3185
3391
  event_type: z16.literal("knowledge_body_read"),
@@ -3292,6 +3498,14 @@ var eventLedgerEventSchema = z16.discriminatedUnion("event_type", [
3292
3498
  // lifecycle-refactor Wave 2 — dormant-hook activation markers.
3293
3499
  sessionEndedEventSchema,
3294
3500
  fileMutatedEventSchema,
3501
+ // ISS-20260711-127: store topology admin events
3502
+ storeMountedEventSchema,
3503
+ storeDetachedEventSchema,
3504
+ storeBoundEventSchema,
3505
+ writeStoreSwitchedEventSchema,
3506
+ writeRouteChangedEventSchema,
3507
+ // ISS-20260711-215: narrow_hint_failed — CLI failure observability for narrow hook
3508
+ narrowHintFailedEventSchema,
3295
3509
  // KT-DEC-0030: knowledge_body_read — PostToolUse native-Read consumption marker
3296
3510
  // (replaces knowledge_consumed as the funnel's "body opened" signal).
3297
3511
  knowledgeBodyReadEventSchema,
@@ -3299,6 +3513,49 @@ var eventLedgerEventSchema = z16.discriminatedUnion("event_type", [
3299
3513
  graphEdgeCandidateRequestedEventSchema
3300
3514
  ]);
3301
3515
 
3516
+ // src/high-value-predicate.ts
3517
+ var HIGH_VALUE_ARCHIVE_EVENT_TYPES = /* @__PURE__ */ new Set([
3518
+ "knowledge_context_planned",
3519
+ "edit_paths_recorded",
3520
+ "edit_intent_checked"
3521
+ // the real high-freq edit signal (rc.37 NEW-14/B3)
3522
+ ]);
3523
+ var NORMATIVE_KEYWORDS = [
3524
+ "\u4EE5\u540E",
3525
+ "always",
3526
+ "never",
3527
+ "from now on",
3528
+ "\u4E0B\u6B21",
3529
+ "\u8BB0\u4E00\u4E0B",
3530
+ "\u6C38\u8FDC\u4E0D\u8981"
3531
+ ];
3532
+ function isHighValueArchiveCandidate(events, sessionId, watermarkTs) {
3533
+ if (!Array.isArray(events)) return false;
3534
+ if (typeof sessionId !== "string" || sessionId.length === 0) return false;
3535
+ const wm = typeof watermarkTs === "number" ? watermarkTs : 0;
3536
+ let latestTurn = null;
3537
+ for (const raw of events) {
3538
+ const e = raw;
3539
+ if (!e || typeof e.ts !== "number" || e.ts <= wm) continue;
3540
+ if (e.session_id !== sessionId) continue;
3541
+ if (typeof e.event_type === "string" && HIGH_VALUE_ARCHIVE_EVENT_TYPES.has(e.event_type)) {
3542
+ return true;
3543
+ }
3544
+ if (e.event_type === "assistant_turn_observed") {
3545
+ if (latestTurn === null || typeof latestTurn.ts === "number" && e.ts > latestTurn.ts) {
3546
+ latestTurn = e;
3547
+ }
3548
+ }
3549
+ }
3550
+ if (latestTurn !== null) {
3551
+ const haystack = JSON.stringify(latestTurn).toLowerCase();
3552
+ for (const kw of NORMATIVE_KEYWORDS) {
3553
+ if (haystack.includes(kw.toLowerCase())) return true;
3554
+ }
3555
+ }
3556
+ return false;
3557
+ }
3558
+
3302
3559
  // src/text-tokenize.ts
3303
3560
  var CJK_CLASS = "\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\u3040-\\u30ff\\uac00-\\ud7af";
3304
3561
  var RUN_RE = new RegExp(`[a-z0-9]+|[${CJK_CLASS}]+`, "gu");
@@ -3433,6 +3690,7 @@ export {
3433
3690
  archiveScanAnnotations,
3434
3691
  archiveScanInputSchema,
3435
3692
  archiveScanOutputSchema,
3693
+ assertAllowedGitRemote,
3436
3694
  assistantTurnObservedEventSchema,
3437
3695
  auditModeSchema,
3438
3696
  bindRequiredStore,
@@ -3521,6 +3779,8 @@ export {
3521
3779
  initScanCompletedEventSchema,
3522
3780
  initStore,
3523
3781
  installDiffAppliedEventSchema,
3782
+ isAllowedGitRemote,
3783
+ isHighValueArchiveCandidate,
3524
3784
  isKnowledgeStableId,
3525
3785
  isNonPersonalRequiredStore,
3526
3786
  isPersonalLeakIntoSharedStore,
@@ -3577,6 +3837,7 @@ export {
3577
3837
  metaUpdatedEventSchema,
3578
3838
  migrateRequiredStores,
3579
3839
  mountedStoreSchema,
3840
+ narrowHintFailedEventSchema,
3580
3841
  normalizeCiteTag,
3581
3842
  normalizeLocale,
3582
3843
  nudgeModeSchema,
@@ -3635,6 +3896,7 @@ export {
3635
3896
  ruleDescriptionIndexItemSchema,
3636
3897
  ruleDescriptionSchema,
3637
3898
  saveGlobalConfig,
3899
+ saveGlobalConfigAsync,
3638
3900
  saveProjectConfig,
3639
3901
  scanForSecrets,
3640
3902
  scopeCoordinateSchema,
@@ -3650,14 +3912,17 @@ export {
3650
3912
  skillTriggerCandidateEventSchema,
3651
3913
  storeAliasSchema,
3652
3914
  storeAwareEntrySchema,
3915
+ storeBoundEventSchema,
3653
3916
  storeCountersPath,
3654
3917
  storeCountersSchema,
3918
+ storeDetachedEventSchema,
3655
3919
  storeHasProject,
3656
3920
  storeIdentitySchema,
3657
3921
  storeKnowledgeTypeDir,
3658
3922
  storeMountGroup,
3659
3923
  storeMountNameSchema,
3660
3924
  storeMountSubPath,
3925
+ storeMountedEventSchema,
3661
3926
  storeProjectSchema,
3662
3927
  storeProjectsFileSchema,
3663
3928
  storeReadSetSchema,
@@ -3672,7 +3937,9 @@ export {
3672
3937
  uidSchema,
3673
3938
  withDerivedAgentsMetaNodeDefaults,
3674
3939
  writeBindingsSnapshot,
3940
+ writeRouteChangedEventSchema,
3675
3941
  writeRouteSchema,
3942
+ writeStoreSwitchedEventSchema,
3676
3943
  writeTargetSchema,
3677
3944
  writtenToStoreSchema,
3678
3945
  zhCNMessages