@fenglimg/fabric-shared 2.3.0-rc.9 → 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/{chunk-TV264D7E.js → chunk-5M3HB554.js} +178 -37
- package/dist/{chunk-ASS2KBB7.js → chunk-OX6DWUK4.js} +31 -2
- package/dist/{chunk-KFFBQRL5.js → chunk-V7IUN5U2.js} +9 -7
- package/dist/{chunk-P6INRP3A.js → chunk-WXVFJBL5.js} +194 -39
- package/dist/i18n/index.js +3 -2
- package/dist/{index-B2e0wKJi.d.ts → index-KvxqMZV1.d.ts} +6 -0
- package/dist/index.d.ts +474 -25
- package/dist/index.js +311 -44
- package/dist/schemas/api-contracts.d.ts +554 -151
- package/dist/schemas/api-contracts.js +1 -1
- package/dist/templates/bootstrap-canonical.d.ts +2 -2
- package/dist/templates/bootstrap-canonical.js +3 -2
- package/dist/theme.d.ts +6 -3
- package/dist/theme.js +27 -5
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import "./chunk-LXNCAKJZ.js";
|
|
2
1
|
import {
|
|
3
2
|
BOOTSTRAP_CANONICAL_BY_LOCALE,
|
|
4
3
|
BOOTSTRAP_CANONICAL_EN,
|
|
@@ -8,7 +7,8 @@ import {
|
|
|
8
7
|
BOOTSTRAP_REGEX,
|
|
9
8
|
matchBootstrapCanonicalLocale,
|
|
10
9
|
resolveBootstrapCanonical
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-V7IUN5U2.js";
|
|
11
|
+
import "./chunk-LXNCAKJZ.js";
|
|
12
12
|
import {
|
|
13
13
|
PROTECTED_TOKENS,
|
|
14
14
|
createTranslator,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
enMessages,
|
|
17
17
|
resolveFabricLocale,
|
|
18
18
|
zhCNMessages
|
|
19
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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,
|
|
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(
|
|
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 =
|
|
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(
|
|
1729
|
+
raw = JSON.parse(text);
|
|
1707
1730
|
} catch {
|
|
1708
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
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(
|
|
1730
|
-
async (store) =>
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
type
|
|
1734
|
-
|
|
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 {
|
|
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
|
-
|
|
1840
|
-
|
|
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
|
-
|
|
1866
|
-
|
|
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 =
|
|
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
|
-
|
|
2437
|
+
renameSync3(tmpPath, path);
|
|
2280
2438
|
} catch (error) {
|
|
2281
2439
|
try {
|
|
2282
|
-
|
|
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
|