@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/{chunk-JEXTOQVV.js → chunk-355LUDLW.js} +92 -8
- package/dist/chunk-4N6DMOOW.js +128 -0
- package/dist/{chunk-TX2XZ7AW.js → chunk-AFT7DB4P.js} +6 -5
- package/dist/{chunk-7TZ2PMVH.js → chunk-KUYCTRFI.js} +87 -3
- package/dist/{chunk-3SZRB42B.js → chunk-VDSM73PK.js} +8 -0
- package/dist/errors/index.d.ts +7 -1
- package/dist/errors/index.js +7 -3
- package/dist/i18n/index.js +1 -1
- package/dist/{index-J3Xn5h2J.d.ts → index-BqA89S9q.d.ts} +28 -3
- package/dist/index.d.ts +348 -10
- package/dist/index.js +367 -24
- package/dist/node/atomic-write.js +6 -106
- package/dist/node/mcp-payload-guard.js +1 -1
- package/dist/schemas/api-contracts.d.ts +140 -18
- package/dist/schemas/api-contracts.js +1 -1
- package/dist/templates/bootstrap-canonical.d.ts +1 -1
- package/dist/templates/bootstrap-canonical.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
1723
|
-
import { join as
|
|
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
|
-
|
|
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
|
-
|
|
1740
|
-
|
|
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 (!
|
|
2030
|
+
if (!existsSync6(path)) {
|
|
1747
2031
|
return null;
|
|
1748
2032
|
}
|
|
1749
2033
|
try {
|
|
1750
|
-
const parsed = resolvedBindingsSnapshotSchema.safeParse(JSON.parse(
|
|
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,
|