@fenglimg/fabric-shared 2.1.0-rc.2 → 2.2.0-rc.1
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-R2J7DAED.js → chunk-7TZ2PMVH.js} +78 -2
- package/dist/{chunk-WVPDH4BF.js → chunk-JEXTOQVV.js} +31 -2
- package/dist/{chunk-MDWTGOAY.js → chunk-TX2XZ7AW.js} +1 -0
- package/dist/i18n/index.js +1 -1
- package/dist/{index-GQpaWTm-.d.ts → index-J3Xn5h2J.d.ts} +24 -1
- package/dist/index.d.ts +1144 -230
- package/dist/index.js +320 -20
- package/dist/node/atomic-write.d.ts +26 -1
- package/dist/node/atomic-write.js +45 -2
- package/dist/node/mcp-payload-guard.d.ts +32 -1
- package/dist/node/mcp-payload-guard.js +15 -1
- package/dist/schemas/api-contracts.d.ts +47 -0
- 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,12 +1,4 @@
|
|
|
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-MDWTGOAY.js";
|
|
1
|
+
import "./chunk-LXNCAKJZ.js";
|
|
10
2
|
import {
|
|
11
3
|
PROTECTED_TOKENS,
|
|
12
4
|
createTranslator,
|
|
@@ -16,7 +8,7 @@ import {
|
|
|
16
8
|
normalizeLocale,
|
|
17
9
|
resolveFabricLocale,
|
|
18
10
|
zhCNMessages
|
|
19
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7TZ2PMVH.js";
|
|
20
12
|
import {
|
|
21
13
|
FabExtractKnowledgeInputSchema,
|
|
22
14
|
FabExtractKnowledgeInputShape,
|
|
@@ -64,8 +56,16 @@ import {
|
|
|
64
56
|
recallInputSchema,
|
|
65
57
|
recallOutputSchema,
|
|
66
58
|
structuredWarningSchema
|
|
67
|
-
} from "./chunk-
|
|
68
|
-
import
|
|
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";
|
|
69
69
|
|
|
70
70
|
// src/schemas/agents-meta.ts
|
|
71
71
|
import { z } from "zod";
|
|
@@ -112,7 +112,15 @@ var ruleDescriptionSchema = z.object({
|
|
|
112
112
|
// relevance_scope → 'broad' (always-surface, safe default)
|
|
113
113
|
// relevance_paths → [] (no path anchors)
|
|
114
114
|
relevance_scope: z.enum(["narrow", "broad"]).default("broad"),
|
|
115
|
-
relevance_paths: z.array(z.string()).default([])
|
|
115
|
+
relevance_paths: z.array(z.string()).default([]),
|
|
116
|
+
// v2.2 H2-related (W1-T7): explicit graph edges to related KB entries by
|
|
117
|
+
// stable_id. Authored in frontmatter (`related: [KT-DEC-0001, KT-PIT-0002]`)
|
|
118
|
+
// or written by the fabric-connect skill (SK2); read by fab_recall's
|
|
119
|
+
// include_related packaging (MC1). Optional + default [] so the field is a
|
|
120
|
+
// pure additive — every pre-v2.2 entry parses unchanged. The schema is
|
|
121
|
+
// .strict(), so this MUST be declared or `related:` frontmatter would be
|
|
122
|
+
// rejected at parse time.
|
|
123
|
+
related: z.array(z.string()).default([]).optional()
|
|
116
124
|
}).strict();
|
|
117
125
|
var ruleDescriptionIndexItemSchema = z.object({
|
|
118
126
|
stable_id: z.string(),
|
|
@@ -417,6 +425,7 @@ var mcpPayloadLimitsSchema = z6.object({
|
|
|
417
425
|
hardBytes: z6.number().int().positive().optional()
|
|
418
426
|
}).optional();
|
|
419
427
|
var selectionTokenTtlMsSchema = z6.number().int().min(3e4).max(36e5);
|
|
428
|
+
var planContextTopKSchema = z6.number().int().min(1).max(200);
|
|
420
429
|
var fabricLanguageSchema = z6.enum([
|
|
421
430
|
"match-existing",
|
|
422
431
|
"zh-CN",
|
|
@@ -634,12 +643,32 @@ var fabricConfigSchema = z6.object({
|
|
|
634
643
|
// TRUNCATION_THRESHOLD=12 grouped-render kicks in. Mirrors the rc.7 T7 +
|
|
635
644
|
// archive_max_* pattern of externalizing previously-hardcoded thresholds.
|
|
636
645
|
hint_broad_top_k: z6.number().int().min(1).max(50).optional().default(8),
|
|
646
|
+
// v2.2 HK2-degrade (W2-T2): char budget for the rendered SessionStart broad-menu
|
|
647
|
+
// body — the final rung of the degradation ladder after the hint_broad_top_k
|
|
648
|
+
// count slice. Once the rendered entry/group lines exceed this, the tail
|
|
649
|
+
// collapses to a single "N more omitted" marker so a large corpus cannot blow
|
|
650
|
+
// the agent's working memory. Default 2000 (~one screenful); 0 disables the
|
|
651
|
+
// budget. Read by knowledge-hint-broad.cjs via readConfigNumber. Range 0..20000.
|
|
652
|
+
hint_broad_budget_chars: z6.number().int().min(0).max(2e4).optional().default(2e3),
|
|
637
653
|
// v2.0.0-rc.37 NEW-16: durable per-signal dismiss for the fabric-hint Stop
|
|
638
654
|
// hook nudges. Any signal type listed here is suppressed at emit time across
|
|
639
655
|
// all sessions (the session-scoped sibling lives in a .fabric/.cache sidecar
|
|
640
656
|
// written on request). Mirrors the cite_evict_interval=0 opt-out convention —
|
|
641
657
|
// a knob for an existing surface, not a new feature. Unknown types ignored.
|
|
642
658
|
hint_dismiss_signals: z6.array(z6.enum(["archive", "review", "import", "maintenance"])).optional(),
|
|
659
|
+
// v2.1 ADJ-NEWN-4: user-override escape hatches for the two strong behavioral
|
|
660
|
+
// policies (cite-before-edit + self-archive). The strong policies can make an
|
|
661
|
+
// agent feel like a "stubborn parrot" (D2 user-in-control red line); these
|
|
662
|
+
// flags let a user durably turn either off via fabric-config.json (or the
|
|
663
|
+
// `fabric config` panel) without editing bootstrap/AGENTS.md. Default true
|
|
664
|
+
// preserves rc.x behavior (policies ON); set false to opt a project out.
|
|
665
|
+
// The bootstrap behavior layer references these so the AGENTS.md rules degrade
|
|
666
|
+
// from "MUST" to "optional" when disabled — a config knob for an existing
|
|
667
|
+
// surface, mirroring the cite_evict_interval=0 / hint_dismiss_signals opt-out
|
|
668
|
+
// convention, NOT a new feature. Wave3 J32 will quantify the friction these
|
|
669
|
+
// relieve; until then they ship as inert-safe opt-outs.
|
|
670
|
+
cite_policy_enabled: z6.boolean().optional().default(true),
|
|
671
|
+
self_archive_policy_enabled: z6.boolean().optional().default(true),
|
|
643
672
|
// v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the narrow PreToolUse hint
|
|
644
673
|
// emitted by knowledge-hint-narrow.cjs. After filtering to entries whose
|
|
645
674
|
// `relevance_scope === "narrow"` (rc.27 TASK-005 audit §2.5 fix), the hook
|
|
@@ -713,7 +742,31 @@ var fabricConfigSchema = z6.object({
|
|
|
713
742
|
// so the server-side per-field reader can validate without re-running the
|
|
714
743
|
// whole fabricConfigSchema on every plan_context call — that lets a corrupt
|
|
715
744
|
// unrelated field stay isolated from the hot read path.
|
|
716
|
-
selection_token_ttl_ms: selectionTokenTtlMsSchema.optional()
|
|
745
|
+
selection_token_ttl_ms: selectionTokenTtlMsSchema.optional(),
|
|
746
|
+
// v2.2 A-INFRA-3 (W1-T3-TOPK): bound on `fab_plan_context` candidate count,
|
|
747
|
+
// applied after BM25 ranking. Absent → library default (24). See
|
|
748
|
+
// planContextTopKSchema for the range/calibration rationale.
|
|
749
|
+
plan_context_top_k: planContextTopKSchema.optional(),
|
|
750
|
+
// v2.2 C5-budget (W2-T3): layered retrieval budget profile. A single coherent
|
|
751
|
+
// strategy across the injection + MCP layers — `balanced` (default) reproduces
|
|
752
|
+
// the historical per-knob defaults exactly, `conservative` / `generous` scale
|
|
753
|
+
// the whole truncation chain (top_k + payload bytes + injection chars) down /
|
|
754
|
+
// up together. Per-field knobs (plan_context_top_k, mcpPayloadLimits.*,
|
|
755
|
+
// hint_broad_budget_chars) still override the profile when set. See
|
|
756
|
+
// retrieval-budget.ts (resolveRetrievalBudget) for the resolution order.
|
|
757
|
+
retrieval_budget_profile: z6.enum(["conservative", "balanced", "generous"]).optional(),
|
|
758
|
+
// v2.2 C2-vector (W2-T7): OPTIONAL dense-embedding semantic retrieval, layered
|
|
759
|
+
// as a recall supplement after BM25. Default OFF (`--no-embed` is the baseline);
|
|
760
|
+
// requires the operator to install the optional `fastembed` package — absent →
|
|
761
|
+
// text-only fallback. Never grows the default install footprint.
|
|
762
|
+
embed_enabled: z6.boolean().optional().default(false),
|
|
763
|
+
// Weight applied to the 0..1 cosine similarity before it joins the additive
|
|
764
|
+
// score. Capped at 49 — strictly BELOW BM25_WEIGHT (50) — so a perfect vector
|
|
765
|
+
// match (weight × 1) can never outscore a single strong BM25 term match. This
|
|
766
|
+
// ENFORCES the "vectors supplement, never override lexical relevance"
|
|
767
|
+
// invariant in the schema rather than leaving it to a comment (W2-REVIEW codex
|
|
768
|
+
// MED-4). Range 0..49; default 30.
|
|
769
|
+
embed_weight: z6.number().int().min(0).max(49).optional().default(30)
|
|
717
770
|
});
|
|
718
771
|
|
|
719
772
|
// src/schemas/fabric-config-introspect.ts
|
|
@@ -1226,9 +1279,23 @@ function hasScriptExtension(name) {
|
|
|
1226
1279
|
const dot = name.lastIndexOf(".");
|
|
1227
1280
|
return dot !== -1 && SCRIPT_EXTENSIONS.has(name.slice(dot).toLowerCase());
|
|
1228
1281
|
}
|
|
1229
|
-
|
|
1282
|
+
var STORE_SCAN_MAX_DEPTH = 32;
|
|
1283
|
+
var STORE_SCAN_MAX_ENTRIES = 1e5;
|
|
1284
|
+
function findStoreExecutableViolations(absDir, options = {}) {
|
|
1285
|
+
const maxDepth = options.maxDepth ?? STORE_SCAN_MAX_DEPTH;
|
|
1286
|
+
const maxEntries = options.maxEntries ?? STORE_SCAN_MAX_ENTRIES;
|
|
1230
1287
|
const violations = [];
|
|
1231
|
-
|
|
1288
|
+
let entriesScanned = 0;
|
|
1289
|
+
let bounded = false;
|
|
1290
|
+
const walk = (dir, rel, depth) => {
|
|
1291
|
+
if (bounded) {
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
if (depth > maxDepth) {
|
|
1295
|
+
violations.push(`<scan-bounded: depth > ${maxDepth} at ${rel === "" ? "." : rel}>`);
|
|
1296
|
+
bounded = true;
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1232
1299
|
let entries;
|
|
1233
1300
|
try {
|
|
1234
1301
|
entries = readdirSync(dir);
|
|
@@ -1236,9 +1303,18 @@ function findStoreExecutableViolations(absDir) {
|
|
|
1236
1303
|
return;
|
|
1237
1304
|
}
|
|
1238
1305
|
for (const entry of entries) {
|
|
1306
|
+
if (bounded) {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1239
1309
|
if (rel === "" && entry === ".git") {
|
|
1240
1310
|
continue;
|
|
1241
1311
|
}
|
|
1312
|
+
entriesScanned += 1;
|
|
1313
|
+
if (entriesScanned > maxEntries) {
|
|
1314
|
+
violations.push(`<scan-bounded: entries > ${maxEntries}>`);
|
|
1315
|
+
bounded = true;
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1242
1318
|
const abs = join(dir, entry);
|
|
1243
1319
|
const relPath = rel === "" ? entry : `${rel}/${entry}`;
|
|
1244
1320
|
let stat;
|
|
@@ -1248,7 +1324,7 @@ function findStoreExecutableViolations(absDir) {
|
|
|
1248
1324
|
continue;
|
|
1249
1325
|
}
|
|
1250
1326
|
if (stat.isDirectory()) {
|
|
1251
|
-
walk(abs, relPath);
|
|
1327
|
+
walk(abs, relPath, depth + 1);
|
|
1252
1328
|
continue;
|
|
1253
1329
|
}
|
|
1254
1330
|
if ((stat.mode & 73) !== 0 || hasScriptExtension(entry)) {
|
|
@@ -1256,7 +1332,7 @@ function findStoreExecutableViolations(absDir) {
|
|
|
1256
1332
|
}
|
|
1257
1333
|
}
|
|
1258
1334
|
};
|
|
1259
|
-
walk(absDir, "");
|
|
1335
|
+
walk(absDir, "", 0);
|
|
1260
1336
|
return violations;
|
|
1261
1337
|
}
|
|
1262
1338
|
|
|
@@ -1463,6 +1539,47 @@ function redactSecrets(content) {
|
|
|
1463
1539
|
}
|
|
1464
1540
|
return out;
|
|
1465
1541
|
}
|
|
1542
|
+
function scrubRemoteUrl(remote) {
|
|
1543
|
+
return remote.replace(
|
|
1544
|
+
/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/@]*:[^/@]*@/,
|
|
1545
|
+
"$1"
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// src/scanner/scan-recommendations.ts
|
|
1550
|
+
function buildScanRecommendations(input, t) {
|
|
1551
|
+
const recs = [];
|
|
1552
|
+
if (input.hasExistingFabric === false) {
|
|
1553
|
+
recs.push(t("scan.rec.install"));
|
|
1554
|
+
}
|
|
1555
|
+
if (input.readmeOk === false) {
|
|
1556
|
+
recs.push(t("scan.rec.readme"));
|
|
1557
|
+
}
|
|
1558
|
+
if (input.hasContributing === false) {
|
|
1559
|
+
recs.push(t("scan.rec.contributing"));
|
|
1560
|
+
}
|
|
1561
|
+
switch (input.frameworkKind) {
|
|
1562
|
+
case "cocos-creator":
|
|
1563
|
+
recs.push(t("scan.rec.cocos.lifecycle"));
|
|
1564
|
+
recs.push(t("scan.rec.cocos.human-protect"));
|
|
1565
|
+
if (input.hasMeta === true) {
|
|
1566
|
+
recs.push(t("scan.rec.cocos.meta-lock"));
|
|
1567
|
+
}
|
|
1568
|
+
break;
|
|
1569
|
+
case "next":
|
|
1570
|
+
recs.push(t("scan.rec.next"));
|
|
1571
|
+
break;
|
|
1572
|
+
case "vite":
|
|
1573
|
+
recs.push(t("scan.rec.vite"));
|
|
1574
|
+
break;
|
|
1575
|
+
case "unknown":
|
|
1576
|
+
recs.push(t("scan.rec.unknown"));
|
|
1577
|
+
break;
|
|
1578
|
+
default:
|
|
1579
|
+
recs.push(t("scan.rec.generic", { kind: input.frameworkKind }));
|
|
1580
|
+
}
|
|
1581
|
+
return recs;
|
|
1582
|
+
}
|
|
1466
1583
|
|
|
1467
1584
|
// src/store/cross-store-lint.ts
|
|
1468
1585
|
function lintCrossStoreReferences(input) {
|
|
@@ -1650,6 +1767,8 @@ function addMountedStore(config, store) {
|
|
|
1650
1767
|
`alias '${store.alias}' already mounts store ${aliasClash.store_uuid}; choose another alias`
|
|
1651
1768
|
);
|
|
1652
1769
|
}
|
|
1770
|
+
const sanitized = store.remote === void 0 ? store : { ...store, remote: scrubRemoteUrl(store.remote) };
|
|
1771
|
+
store = sanitized;
|
|
1653
1772
|
const existing = config.stores.find((s) => s.store_uuid === store.store_uuid);
|
|
1654
1773
|
const stores = existing === void 0 ? [...config.stores, store] : config.stores.map((s) => s.store_uuid === store.store_uuid ? store : s);
|
|
1655
1774
|
return { ...config, stores };
|
|
@@ -1665,7 +1784,8 @@ function detachMountedStore(config, alias) {
|
|
|
1665
1784
|
};
|
|
1666
1785
|
}
|
|
1667
1786
|
function bindRequiredStore(required, entry) {
|
|
1668
|
-
|
|
1787
|
+
const safeEntry = entry.suggested_remote === void 0 ? entry : { ...entry, suggested_remote: scrubRemoteUrl(entry.suggested_remote) };
|
|
1788
|
+
return required.some((r) => r.id === safeEntry.id) ? required.map((r) => r.id === safeEntry.id ? safeEntry : r) : [...required, safeEntry];
|
|
1669
1789
|
}
|
|
1670
1790
|
function explainStore(config, alias) {
|
|
1671
1791
|
const store = findMountedStore(config, alias);
|
|
@@ -2372,6 +2492,94 @@ var sessionArchiveAttemptedEventSchema = z18.object({
|
|
|
2372
2492
|
candidates_proposed: z18.number().int().nonnegative().default(0),
|
|
2373
2493
|
knowledge_proposed_ids: z18.array(z18.string()).default([])
|
|
2374
2494
|
});
|
|
2495
|
+
var hookSurfaceEmittedEventSchema = z18.object({
|
|
2496
|
+
...eventLedgerEnvelopeSchema,
|
|
2497
|
+
event_type: z18.literal("hook_surface_emitted"),
|
|
2498
|
+
hook_name: z18.string(),
|
|
2499
|
+
client: z18.enum(["cc", "codex", "cursor"]),
|
|
2500
|
+
target_channel: z18.string(),
|
|
2501
|
+
rendered_ids: z18.array(z18.string()),
|
|
2502
|
+
delivery_status: z18.enum(["delivered", "suppressed", "error"]),
|
|
2503
|
+
suppression_reason: z18.string().optional()
|
|
2504
|
+
});
|
|
2505
|
+
var hookSignalEmittedEventSchema = z18.object({
|
|
2506
|
+
...eventLedgerEnvelopeSchema,
|
|
2507
|
+
event_type: z18.literal("hook_signal_emitted"),
|
|
2508
|
+
signal_type: z18.enum(["archive", "review", "maintenance", "other"]),
|
|
2509
|
+
threshold: z18.number(),
|
|
2510
|
+
actual_value: z18.number(),
|
|
2511
|
+
fired: z18.boolean()
|
|
2512
|
+
});
|
|
2513
|
+
var mcpStdioTraceEventSchema = z18.object({
|
|
2514
|
+
...eventLedgerEnvelopeSchema,
|
|
2515
|
+
event_type: z18.literal("mcp_stdio_trace"),
|
|
2516
|
+
tool_name: z18.string(),
|
|
2517
|
+
request_id: z18.string(),
|
|
2518
|
+
duration_ms: z18.number().nonnegative(),
|
|
2519
|
+
status: z18.enum(["ok", "error"]),
|
|
2520
|
+
payload_bytes_in: z18.number().int().nonnegative(),
|
|
2521
|
+
payload_bytes_out: z18.number().int().nonnegative(),
|
|
2522
|
+
error_code: z18.string().optional()
|
|
2523
|
+
});
|
|
2524
|
+
var payloadGuardObservedEventSchema = z18.object({
|
|
2525
|
+
...eventLedgerEnvelopeSchema,
|
|
2526
|
+
event_type: z18.literal("payload_guard_observed"),
|
|
2527
|
+
tool_name: z18.string(),
|
|
2528
|
+
path_count: z18.number().int().nonnegative(),
|
|
2529
|
+
tokens_estimated: z18.number().int().nonnegative(),
|
|
2530
|
+
truncated: z18.boolean(),
|
|
2531
|
+
cap: z18.number().int().positive()
|
|
2532
|
+
});
|
|
2533
|
+
var skillInvocationStartedEventSchema = z18.object({
|
|
2534
|
+
...eventLedgerEnvelopeSchema,
|
|
2535
|
+
event_type: z18.literal("skill_invocation_started"),
|
|
2536
|
+
skill_name: z18.string(),
|
|
2537
|
+
trigger_source: z18.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
2538
|
+
entry_point: z18.string()
|
|
2539
|
+
});
|
|
2540
|
+
var skillInvocationCompletedEventSchema = z18.object({
|
|
2541
|
+
...eventLedgerEnvelopeSchema,
|
|
2542
|
+
event_type: z18.literal("skill_invocation_completed"),
|
|
2543
|
+
skill_name: z18.string(),
|
|
2544
|
+
trigger_source: z18.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
2545
|
+
entry_point: z18.string(),
|
|
2546
|
+
outcome: z18.enum(["completed", "aborted", "error", "no_op"]),
|
|
2547
|
+
elapsed_ms: z18.number().nonnegative().optional()
|
|
2548
|
+
});
|
|
2549
|
+
var skillPhaseTransitionEventSchema = z18.object({
|
|
2550
|
+
...eventLedgerEnvelopeSchema,
|
|
2551
|
+
event_type: z18.literal("skill_phase_transition"),
|
|
2552
|
+
skill_name: z18.string(),
|
|
2553
|
+
phase: z18.string(),
|
|
2554
|
+
status: z18.enum(["entered", "completed", "skipped", "failed"]),
|
|
2555
|
+
checkpoint: z18.string().optional(),
|
|
2556
|
+
elapsed_ms: z18.number().nonnegative().optional()
|
|
2557
|
+
});
|
|
2558
|
+
var skillTriggerCandidateEventSchema = z18.object({
|
|
2559
|
+
...eventLedgerEnvelopeSchema,
|
|
2560
|
+
event_type: z18.literal("skill_trigger_candidate"),
|
|
2561
|
+
skill_name: z18.string(),
|
|
2562
|
+
trigger_source: z18.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
2563
|
+
signal: z18.string(),
|
|
2564
|
+
invoked: z18.boolean()
|
|
2565
|
+
});
|
|
2566
|
+
var llmJudgeRunEventSchema = z18.object({
|
|
2567
|
+
...eventLedgerEnvelopeSchema,
|
|
2568
|
+
event_type: z18.literal("llm_judge_run"),
|
|
2569
|
+
prompt: z18.string(),
|
|
2570
|
+
version: z18.string(),
|
|
2571
|
+
model: z18.string(),
|
|
2572
|
+
input_trace_id: z18.string(),
|
|
2573
|
+
score: z18.number(),
|
|
2574
|
+
rationale: z18.string()
|
|
2575
|
+
});
|
|
2576
|
+
var clientCapabilitySnapshotEventSchema = z18.object({
|
|
2577
|
+
...eventLedgerEnvelopeSchema,
|
|
2578
|
+
event_type: z18.literal("client_capability_snapshot"),
|
|
2579
|
+
client: z18.enum(["cc", "codex", "cursor"]),
|
|
2580
|
+
capabilities: z18.array(z18.string()),
|
|
2581
|
+
version: z18.string()
|
|
2582
|
+
});
|
|
2375
2583
|
var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
2376
2584
|
knowledgeContextPlannedEventSchema,
|
|
2377
2585
|
knowledgeSelectionEventSchema,
|
|
@@ -2453,8 +2661,83 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2453
2661
|
// fabric-archive skill at the end of every invocation. Drives Phase 0.0
|
|
2454
2662
|
// cross-session digest, outcome-based rescan filter (skips user_dismissed),
|
|
2455
2663
|
// covered_through_ts watermark, and `fabric doctor --archive-history`.
|
|
2456
|
-
sessionArchiveAttemptedEventSchema
|
|
2664
|
+
sessionArchiveAttemptedEventSchema,
|
|
2665
|
+
// v2.1 GATE-INSTR (NEW-N-3): 9 interaction-axis instrumentation events.
|
|
2666
|
+
hookSurfaceEmittedEventSchema,
|
|
2667
|
+
hookSignalEmittedEventSchema,
|
|
2668
|
+
mcpStdioTraceEventSchema,
|
|
2669
|
+
payloadGuardObservedEventSchema,
|
|
2670
|
+
skillInvocationStartedEventSchema,
|
|
2671
|
+
skillInvocationCompletedEventSchema,
|
|
2672
|
+
skillPhaseTransitionEventSchema,
|
|
2673
|
+
skillTriggerCandidateEventSchema,
|
|
2674
|
+
llmJudgeRunEventSchema,
|
|
2675
|
+
clientCapabilitySnapshotEventSchema
|
|
2457
2676
|
]);
|
|
2677
|
+
|
|
2678
|
+
// src/text-tokenize.ts
|
|
2679
|
+
var CJK_CLASS = "\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\u3040-\\u30ff\\uac00-\\ud7af";
|
|
2680
|
+
var RUN_RE = new RegExp(`[a-z0-9]+|[${CJK_CLASS}]+`, "gu");
|
|
2681
|
+
var CJK_FIRST_RE = new RegExp(`[${CJK_CLASS}]`, "u");
|
|
2682
|
+
function tokenize(text) {
|
|
2683
|
+
if (text.length === 0) {
|
|
2684
|
+
return [];
|
|
2685
|
+
}
|
|
2686
|
+
const tokens = [];
|
|
2687
|
+
const lowered = text.toLowerCase();
|
|
2688
|
+
RUN_RE.lastIndex = 0;
|
|
2689
|
+
let match;
|
|
2690
|
+
while ((match = RUN_RE.exec(lowered)) !== null) {
|
|
2691
|
+
const run = match[0];
|
|
2692
|
+
if (CJK_FIRST_RE.test(run[0])) {
|
|
2693
|
+
if (run.length === 1) {
|
|
2694
|
+
tokens.push(run);
|
|
2695
|
+
} else {
|
|
2696
|
+
for (let i = 0; i < run.length - 1; i += 1) {
|
|
2697
|
+
tokens.push(run.slice(i, i + 2));
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
} else {
|
|
2701
|
+
tokens.push(run);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
return tokens;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
// src/retrieval-budget.ts
|
|
2708
|
+
var PROFILES = {
|
|
2709
|
+
conservative: {
|
|
2710
|
+
topK: 12,
|
|
2711
|
+
payloadWarnBytes: 8192,
|
|
2712
|
+
payloadHardBytes: 32768,
|
|
2713
|
+
injectionChars: 1e3
|
|
2714
|
+
},
|
|
2715
|
+
balanced: {
|
|
2716
|
+
topK: 24,
|
|
2717
|
+
payloadWarnBytes: 16384,
|
|
2718
|
+
payloadHardBytes: 65536,
|
|
2719
|
+
injectionChars: 2e3
|
|
2720
|
+
},
|
|
2721
|
+
generous: {
|
|
2722
|
+
topK: 48,
|
|
2723
|
+
payloadWarnBytes: 32768,
|
|
2724
|
+
payloadHardBytes: 131072,
|
|
2725
|
+
injectionChars: 4e3
|
|
2726
|
+
}
|
|
2727
|
+
};
|
|
2728
|
+
var DEFAULT_RETRIEVAL_BUDGET_PROFILE = "balanced";
|
|
2729
|
+
function resolveRetrievalBudget(overrides) {
|
|
2730
|
+
const base = PROFILES[overrides?.profile ?? DEFAULT_RETRIEVAL_BUDGET_PROFILE];
|
|
2731
|
+
return {
|
|
2732
|
+
topK: overrides?.topK ?? base.topK,
|
|
2733
|
+
payloadWarnBytes: overrides?.payloadWarnBytes ?? base.payloadWarnBytes,
|
|
2734
|
+
payloadHardBytes: overrides?.payloadHardBytes ?? base.payloadHardBytes,
|
|
2735
|
+
injectionChars: overrides?.injectionChars ?? base.injectionChars
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
function retrievalBudgetProfile(profile) {
|
|
2739
|
+
return PROFILES[profile];
|
|
2740
|
+
}
|
|
2458
2741
|
export {
|
|
2459
2742
|
AGENTS_META_IDENTITY_SOURCES,
|
|
2460
2743
|
AGENTS_META_LAYERS,
|
|
@@ -2464,6 +2747,7 @@ export {
|
|
|
2464
2747
|
BOOTSTRAP_MARKER_BEGIN,
|
|
2465
2748
|
BOOTSTRAP_MARKER_END,
|
|
2466
2749
|
BOOTSTRAP_REGEX,
|
|
2750
|
+
DEFAULT_RETRIEVAL_BUDGET_PROFILE,
|
|
2467
2751
|
FabExtractKnowledgeInputSchema,
|
|
2468
2752
|
FabExtractKnowledgeInputShape,
|
|
2469
2753
|
FabExtractKnowledgeOutputSchema,
|
|
@@ -2528,6 +2812,7 @@ export {
|
|
|
2528
2812
|
bootstrapMarkerMigratedEventSchema,
|
|
2529
2813
|
buildDebugBundle,
|
|
2530
2814
|
buildFailureTrace,
|
|
2815
|
+
buildScanRecommendations,
|
|
2531
2816
|
candidateFileEntrySchema,
|
|
2532
2817
|
citeContractMetricsSchema,
|
|
2533
2818
|
citeContractPolicyActivatedEventSchema,
|
|
@@ -2536,6 +2821,7 @@ export {
|
|
|
2536
2821
|
citePolicyActivatedEventSchema,
|
|
2537
2822
|
claudeHookPathMigratedEventSchema,
|
|
2538
2823
|
claudeSkillPathMigratedEventSchema,
|
|
2824
|
+
clientCapabilitySnapshotEventSchema,
|
|
2539
2825
|
clientPathsSchema,
|
|
2540
2826
|
codexSkillPathMigratedEventSchema,
|
|
2541
2827
|
createProjectRootResolver,
|
|
@@ -2584,6 +2870,8 @@ export {
|
|
|
2584
2870
|
globalRefSchema,
|
|
2585
2871
|
hasSecrets,
|
|
2586
2872
|
historyStateQuerySchema,
|
|
2873
|
+
hookSignalEmittedEventSchema,
|
|
2874
|
+
hookSurfaceEmittedEventSchema,
|
|
2587
2875
|
humanLedgerEntrySchema,
|
|
2588
2876
|
humanLockApproveRequestSchema,
|
|
2589
2877
|
humanLockEntrySchema,
|
|
@@ -2637,12 +2925,14 @@ export {
|
|
|
2637
2925
|
ledgerSourceSchema,
|
|
2638
2926
|
lintCrossStoreReferences,
|
|
2639
2927
|
listStoreKnowledge,
|
|
2928
|
+
llmJudgeRunEventSchema,
|
|
2640
2929
|
localKnowledgeIdSchema,
|
|
2641
2930
|
lockApprovedEventSchema,
|
|
2642
2931
|
lockDriftEventSchema,
|
|
2643
2932
|
mcpConfigMigratedEventSchema,
|
|
2644
2933
|
mcpEventLedgerEventSchema,
|
|
2645
2934
|
mcpPayloadLimitsSchema,
|
|
2935
|
+
mcpStdioTraceEventSchema,
|
|
2646
2936
|
metaReconciledEventSchema,
|
|
2647
2937
|
metaReconciledOnStartupEventSchema,
|
|
2648
2938
|
metaUpdatedEventSchema,
|
|
@@ -2658,12 +2948,14 @@ export {
|
|
|
2658
2948
|
parseCiteLine,
|
|
2659
2949
|
parseGlobalRef,
|
|
2660
2950
|
parseKnowledgeId,
|
|
2951
|
+
payloadGuardObservedEventSchema,
|
|
2661
2952
|
pendingAutoArchivedEventSchema,
|
|
2662
2953
|
planContextAnnotations,
|
|
2663
2954
|
planContextHintNarrowEntrySchema,
|
|
2664
2955
|
planContextHintOutputSchema,
|
|
2665
2956
|
planContextInputSchema,
|
|
2666
2957
|
planContextOutputSchema,
|
|
2958
|
+
planContextTopKSchema,
|
|
2667
2959
|
projectRootGoldenCaseSchema,
|
|
2668
2960
|
projectRootGoldenFileSchema,
|
|
2669
2961
|
projectRootResolutionSchema,
|
|
@@ -2685,16 +2977,23 @@ export {
|
|
|
2685
2977
|
requiredStoreEntrySchema,
|
|
2686
2978
|
resolveCandidates,
|
|
2687
2979
|
resolveFabricLocale,
|
|
2980
|
+
resolveRetrievalBudget,
|
|
2688
2981
|
resolveStoreQualifiedId,
|
|
2689
2982
|
resolvedBindingsSnapshotSchema,
|
|
2983
|
+
retrievalBudgetProfile,
|
|
2690
2984
|
ruleDescriptionIndexItemSchema,
|
|
2691
2985
|
ruleDescriptionSchema,
|
|
2692
2986
|
scanForSecrets,
|
|
2693
2987
|
scopeCoordinateSchema,
|
|
2694
2988
|
scopeRoot,
|
|
2989
|
+
scrubRemoteUrl,
|
|
2695
2990
|
selectionTokenTtlMsSchema,
|
|
2696
2991
|
serveLockClearedEventSchema,
|
|
2697
2992
|
sessionArchiveAttemptedEventSchema,
|
|
2993
|
+
skillInvocationCompletedEventSchema,
|
|
2994
|
+
skillInvocationStartedEventSchema,
|
|
2995
|
+
skillPhaseTransitionEventSchema,
|
|
2996
|
+
skillTriggerCandidateEventSchema,
|
|
2698
2997
|
storeAwareEntrySchema,
|
|
2699
2998
|
storeCountersSchema,
|
|
2700
2999
|
storeIdentitySchema,
|
|
@@ -2706,6 +3005,7 @@ export {
|
|
|
2706
3005
|
storeResolverWarningSchema,
|
|
2707
3006
|
storeUuidSchema,
|
|
2708
3007
|
structuredWarningSchema,
|
|
3008
|
+
tokenize,
|
|
2709
3009
|
uidSchema,
|
|
2710
3010
|
withDerivedAgentsMetaNodeDefaults,
|
|
2711
3011
|
writeBindingsSnapshot,
|
|
@@ -6,6 +6,31 @@ interface AtomicWriteJsonOptions extends AtomicWriteOptions {
|
|
|
6
6
|
}
|
|
7
7
|
declare function atomicWriteText(path: string, content: string, opts?: AtomicWriteOptions): Promise<void>;
|
|
8
8
|
declare function atomicWriteJson(path: string, value: unknown, opts?: AtomicWriteJsonOptions): Promise<void>;
|
|
9
|
+
interface FileLockOptions {
|
|
10
|
+
/** A held lock older than this (ms, by lock-file mtime) is presumed stale —
|
|
11
|
+
* left by a crashed holder — and reclaimed. Default 10s. */
|
|
12
|
+
staleMs?: number;
|
|
13
|
+
/** Poll interval (ms) between acquire attempts while contended. Default 20ms. */
|
|
14
|
+
retryDelayMs?: number;
|
|
15
|
+
/** Give up acquiring after this long (ms) and throw. Default 10s. */
|
|
16
|
+
maxWaitMs?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run `fn` while holding a cross-process advisory lock at `lockPath`.
|
|
20
|
+
*
|
|
21
|
+
* Unlike the hook-side `appendLockedLine` (which DROPS on contention, fine for
|
|
22
|
+
* best-effort telemetry), this WAITS for the lock — the critical section it
|
|
23
|
+
* guards (e.g. a read-modify-write of a shared counter file) must not be
|
|
24
|
+
* skipped. The lock is a `wx` (O_CREAT|O_EXCL) lock file, so acquisition is
|
|
25
|
+
* atomic across processes; a crashed holder leaves the file behind, so any
|
|
26
|
+
* holder older than `staleMs` is reclaimed. The lock is always released in a
|
|
27
|
+
* `finally`, even if `fn` throws.
|
|
28
|
+
*
|
|
29
|
+
* Scope: cross-process AND in-process. Two concurrent callers on the same
|
|
30
|
+
* `lockPath` (same process or not) serialize, because both race the same
|
|
31
|
+
* O_EXCL create.
|
|
32
|
+
*/
|
|
33
|
+
declare function withFileLock<T>(lockPath: string, fn: () => Promise<T>, opts?: FileLockOptions): Promise<T>;
|
|
9
34
|
interface LedgerWriteQueue {
|
|
10
35
|
append(path: string, line: string): Promise<void>;
|
|
11
36
|
/**
|
|
@@ -27,4 +52,4 @@ interface LedgerWriteQueue {
|
|
|
27
52
|
}
|
|
28
53
|
declare function createLedgerWriteQueue(): LedgerWriteQueue;
|
|
29
54
|
|
|
30
|
-
export { type AtomicWriteJsonOptions, type AtomicWriteOptions, type LedgerWriteQueue, atomicWriteJson, atomicWriteText, createLedgerWriteQueue };
|
|
55
|
+
export { type AtomicWriteJsonOptions, type AtomicWriteOptions, type FileLockOptions, type LedgerWriteQueue, atomicWriteJson, atomicWriteText, createLedgerWriteQueue, withFileLock };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/node/atomic-write.ts
|
|
2
|
-
import { appendFile, open, rename, unlink, writeFile } from "fs/promises";
|
|
2
|
+
import { appendFile, mkdir, open, rename, stat, unlink, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
3
4
|
function makeTmpSuffix() {
|
|
4
5
|
const rand = Math.floor(Math.random() * 65535).toString(16).padStart(4, "0");
|
|
5
6
|
return `.${process.pid}.${Date.now()}.${rand}.tmp`;
|
|
@@ -32,6 +33,47 @@ async function atomicWriteJson(path, value, opts) {
|
|
|
32
33
|
const content = JSON.stringify(value, null, indent) + "\n";
|
|
33
34
|
await atomicWriteText(path, content, { fsync: opts?.fsync });
|
|
34
35
|
}
|
|
36
|
+
function isErrnoException(err) {
|
|
37
|
+
return err instanceof Error && typeof err.code === "string";
|
|
38
|
+
}
|
|
39
|
+
function sleep(ms) {
|
|
40
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
|
+
}
|
|
42
|
+
async function withFileLock(lockPath, fn, opts = {}) {
|
|
43
|
+
const staleMs = opts.staleMs ?? 1e4;
|
|
44
|
+
const retryDelayMs = opts.retryDelayMs ?? 20;
|
|
45
|
+
const maxWaitMs = opts.maxWaitMs ?? 1e4;
|
|
46
|
+
await mkdir(dirname(lockPath), { recursive: true });
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
for (; ; ) {
|
|
49
|
+
let handle;
|
|
50
|
+
try {
|
|
51
|
+
handle = await open(lockPath, "wx");
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (!isErrnoException(err) || err.code !== "EEXIST") throw err;
|
|
54
|
+
try {
|
|
55
|
+
const st = await stat(lockPath);
|
|
56
|
+
if (Date.now() - st.mtimeMs > staleMs) {
|
|
57
|
+
await unlink(lockPath).catch(() => void 0);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (Date.now() - start > maxWaitMs) {
|
|
64
|
+
throw new Error(`withFileLock: timed out acquiring ${lockPath} after ${maxWaitMs}ms`);
|
|
65
|
+
}
|
|
66
|
+
await sleep(retryDelayMs);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
await handle.close();
|
|
71
|
+
return await fn();
|
|
72
|
+
} finally {
|
|
73
|
+
await unlink(lockPath).catch(() => void 0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
35
77
|
function createLedgerWriteQueue() {
|
|
36
78
|
const chains = /* @__PURE__ */ new Map();
|
|
37
79
|
async function doAppend(path, line) {
|
|
@@ -65,5 +107,6 @@ function createLedgerWriteQueue() {
|
|
|
65
107
|
export {
|
|
66
108
|
atomicWriteJson,
|
|
67
109
|
atomicWriteText,
|
|
68
|
-
createLedgerWriteQueue
|
|
110
|
+
createLedgerWriteQueue,
|
|
111
|
+
withFileLock
|
|
69
112
|
};
|
|
@@ -14,5 +14,36 @@ interface PayloadGuardResult {
|
|
|
14
14
|
declare const PAYLOAD_LIMIT_DEFAULT_WARN_BYTES = 16384;
|
|
15
15
|
declare const PAYLOAD_LIMIT_DEFAULT_HARD_BYTES = 65536;
|
|
16
16
|
declare function enforcePayloadLimit(serializedPayload: string, opts?: PayloadGuardOptions): PayloadGuardResult;
|
|
17
|
+
interface PayloadBudgetTrimResult<T> {
|
|
18
|
+
/** The retained head of `items` (the ranked tail was dropped to fit). */
|
|
19
|
+
items: T[];
|
|
20
|
+
/** How many trailing items were dropped to fit the hard budget. */
|
|
21
|
+
dropped: number;
|
|
22
|
+
/** Serialized byte size of the envelope built from the retained items. */
|
|
23
|
+
bytes: number;
|
|
24
|
+
/**
|
|
25
|
+
* True when even `minKeep` items still overflow the hard budget — the caller
|
|
26
|
+
* must surface this (a single oversized entry) rather than assume it fit.
|
|
27
|
+
*/
|
|
28
|
+
overBudget: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* v2.2 MC4-payload-budget (W1-T4): the byte-budget tail of the unified
|
|
32
|
+
* truncation chain (CJK → BM25 → top_k → payload). Rather than hard-throwing
|
|
33
|
+
* when a response overflows the hard limit, callers trim the LEAST-relevant
|
|
34
|
+
* items off the tail of an already-ranked list until the serialized envelope
|
|
35
|
+
* fits — turning a 413 crash into graceful degradation.
|
|
36
|
+
*
|
|
37
|
+
* `serialize` builds the FULL response envelope from a candidate slice (so the
|
|
38
|
+
* byte count includes warnings / metadata, not just the list). Trimming drops
|
|
39
|
+
* from the END, which is correct ONLY when the list is pre-ranked best-first
|
|
40
|
+
* (plan_context sorts by BM25 before calling this). `minKeep` (default 1)
|
|
41
|
+
* guarantees a non-empty result even under a pathological oversized head; in
|
|
42
|
+
* that case `overBudget` is returned true so the caller can warn instead of
|
|
43
|
+
* silently shipping an over-limit payload.
|
|
44
|
+
*/
|
|
45
|
+
declare function trimToPayloadBudget<T>(items: T[], serialize: (items: T[]) => string, opts?: PayloadGuardOptions & {
|
|
46
|
+
minKeep?: number;
|
|
47
|
+
}): PayloadBudgetTrimResult<T>;
|
|
17
48
|
|
|
18
|
-
export { PAYLOAD_LIMIT_DEFAULT_HARD_BYTES, PAYLOAD_LIMIT_DEFAULT_WARN_BYTES, type PayloadGuardOptions, type PayloadGuardResult, enforcePayloadLimit };
|
|
49
|
+
export { PAYLOAD_LIMIT_DEFAULT_HARD_BYTES, PAYLOAD_LIMIT_DEFAULT_WARN_BYTES, type PayloadBudgetTrimResult, type PayloadGuardOptions, type PayloadGuardResult, enforcePayloadLimit, trimToPayloadBudget };
|