@productbrain/mcp 0.0.1-beta.1409 → 0.0.1-beta.1419
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-UI4E64DX.js → chunk-54VAG6CG.js} +121 -35
- package/dist/chunk-54VAG6CG.js.map +1 -0
- package/dist/{chunk-RJ6V5N5E.js → chunk-Q5IQ5I37.js} +6 -1
- package/dist/chunk-Q5IQ5I37.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{setup-VOV2CYAX.js → setup-RZMT6ZGH.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-RJ6V5N5E.js.map +0 -1
- package/dist/chunk-UI4E64DX.js.map +0 -1
- /package/dist/{setup-VOV2CYAX.js.map → setup-RZMT6ZGH.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
cacheScope,
|
|
2
3
|
closeAgentSession,
|
|
3
4
|
getAgentSessionId,
|
|
4
5
|
getApiKeyScope,
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
trackSessionCaptureRate,
|
|
37
38
|
trackWriteBackHintServed,
|
|
38
39
|
trackZeroCaptureAuditFired
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-Q5IQ5I37.js";
|
|
40
41
|
|
|
41
42
|
// src/server.ts
|
|
42
43
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -47,6 +48,52 @@ import { z as z3 } from "zod";
|
|
|
47
48
|
// src/tools/smart-capture.ts
|
|
48
49
|
import { z as z2 } from "zod";
|
|
49
50
|
|
|
51
|
+
// src/lib/scopedCache.ts
|
|
52
|
+
var ScopedCache = class {
|
|
53
|
+
constructor(ttlMs, maxScopes = 100) {
|
|
54
|
+
this.ttlMs = ttlMs;
|
|
55
|
+
this.maxScopes = maxScopes;
|
|
56
|
+
}
|
|
57
|
+
store = /* @__PURE__ */ new Map();
|
|
58
|
+
get(scope) {
|
|
59
|
+
const e = this.store.get(scope);
|
|
60
|
+
if (!e) return void 0;
|
|
61
|
+
if (Date.now() > e.expiresAt) {
|
|
62
|
+
this.store.delete(scope);
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
return e.value;
|
|
66
|
+
}
|
|
67
|
+
set(scope, value) {
|
|
68
|
+
this.store.set(scope, { value, expiresAt: Date.now() + this.ttlMs });
|
|
69
|
+
if (this.store.size > this.maxScopes) this.evict();
|
|
70
|
+
}
|
|
71
|
+
delete(scope) {
|
|
72
|
+
this.store.delete(scope);
|
|
73
|
+
}
|
|
74
|
+
clear() {
|
|
75
|
+
this.store.clear();
|
|
76
|
+
}
|
|
77
|
+
size() {
|
|
78
|
+
return this.store.size;
|
|
79
|
+
}
|
|
80
|
+
/** Drop expired entries first; if still over budget, drop the oldest-expiring. */
|
|
81
|
+
evict() {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
for (const [k, e] of this.store) {
|
|
84
|
+
if (now > e.expiresAt) this.store.delete(k);
|
|
85
|
+
}
|
|
86
|
+
if (this.store.size > this.maxScopes) {
|
|
87
|
+
const sorted = [...this.store.entries()].sort((a, b) => a[1].expiresAt - b[1].expiresAt);
|
|
88
|
+
const overBy = sorted.length - this.maxScopes;
|
|
89
|
+
for (let i = 0; i < overBy; i++) {
|
|
90
|
+
this.store.delete(sorted[i][0]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var COLLECTION_CACHE_TTL_MS = 6e4;
|
|
96
|
+
|
|
50
97
|
// src/tools/knowledge-helpers.ts
|
|
51
98
|
var EPISTEMIC_COLLECTIONS = /* @__PURE__ */ new Set(["insights", "assumptions"]);
|
|
52
99
|
function deriveEpistemicStatus(entry) {
|
|
@@ -548,18 +595,22 @@ function inferSourceDate(...values) {
|
|
|
548
595
|
}
|
|
549
596
|
|
|
550
597
|
// src/lib/collectionCache.ts
|
|
551
|
-
var
|
|
552
|
-
var cachedBySlug = null;
|
|
598
|
+
var collectionCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
|
|
553
599
|
async function getCollections() {
|
|
554
|
-
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
600
|
+
const scope = cacheScope();
|
|
601
|
+
const hit = collectionCache.get(scope);
|
|
602
|
+
if (hit) return hit.collections;
|
|
603
|
+
const result = await kernelQuery("chain.listCollections") ?? [];
|
|
604
|
+
const bySlug = new Map(result.map((c) => [c.slug, c]));
|
|
605
|
+
collectionCache.set(scope, { collections: result, bySlug });
|
|
606
|
+
return result;
|
|
559
607
|
}
|
|
560
608
|
async function getCollectionBySlug(slug) {
|
|
561
609
|
await getCollections();
|
|
562
|
-
return
|
|
610
|
+
return collectionCache.get(cacheScope())?.bySlug.get(slug);
|
|
611
|
+
}
|
|
612
|
+
function invalidateCollectionCacheForScope() {
|
|
613
|
+
collectionCache.delete(cacheScope());
|
|
563
614
|
}
|
|
564
615
|
async function isGoverned(slug) {
|
|
565
616
|
const col = await getCollectionBySlug(slug);
|
|
@@ -703,28 +754,43 @@ async function buildRuntimeProfile(slug) {
|
|
|
703
754
|
qualityChecks
|
|
704
755
|
};
|
|
705
756
|
}
|
|
706
|
-
var profileCache =
|
|
757
|
+
var profileCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
|
|
707
758
|
async function getProfile(slug) {
|
|
708
|
-
|
|
709
|
-
|
|
759
|
+
const scope = cacheScope();
|
|
760
|
+
let map = profileCache.get(scope);
|
|
761
|
+
if (!map) {
|
|
762
|
+
map = /* @__PURE__ */ new Map();
|
|
763
|
+
profileCache.set(scope, map);
|
|
764
|
+
}
|
|
765
|
+
const cached = map.get(slug);
|
|
710
766
|
if (cached) return cached;
|
|
711
767
|
const profile = await buildRuntimeProfile(slug);
|
|
712
|
-
|
|
768
|
+
map.set(slug, profile);
|
|
713
769
|
return profile;
|
|
714
770
|
}
|
|
771
|
+
function invalidateProfileCacheForScope() {
|
|
772
|
+
profileCache.delete(cacheScope());
|
|
773
|
+
}
|
|
715
774
|
function extractSearchTerms(name, description) {
|
|
716
775
|
const text = `${name} ${description}`;
|
|
717
776
|
return text.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 3).slice(0, 8).join(" ");
|
|
718
777
|
}
|
|
719
|
-
var
|
|
778
|
+
var hubCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
|
|
720
779
|
async function populateHubCache() {
|
|
721
|
-
|
|
780
|
+
const scope = cacheScope();
|
|
781
|
+
const hit = hubCache.get(scope);
|
|
782
|
+
if (hit) return hit;
|
|
722
783
|
const cols = await getCollections();
|
|
723
|
-
|
|
784
|
+
const set = new Set(
|
|
724
785
|
cols.filter((c) => c.governanceRole === "steering" || c.thinkingLayer === "strategy").map((c) => c.slug)
|
|
725
786
|
);
|
|
787
|
+
hubCache.set(scope, set);
|
|
788
|
+
return set;
|
|
726
789
|
}
|
|
727
|
-
function
|
|
790
|
+
function invalidateHubCacheForScope() {
|
|
791
|
+
hubCache.delete(cacheScope());
|
|
792
|
+
}
|
|
793
|
+
function computeLinkConfidence(candidate, sourceName, sourceDescription, sourceCollection, candidateCollection, hubSlugs) {
|
|
728
794
|
const text = `${sourceName} ${sourceDescription}`.toLowerCase();
|
|
729
795
|
const candidateName = candidate.name.toLowerCase();
|
|
730
796
|
let score = 0;
|
|
@@ -740,7 +806,7 @@ function computeLinkConfidence(candidate, sourceName, sourceDescription, sourceC
|
|
|
740
806
|
if (matchingWords.length > 0) {
|
|
741
807
|
reasons.push(`word overlap (${matchingWords.slice(0, 3).join(", ")})`);
|
|
742
808
|
}
|
|
743
|
-
if (
|
|
809
|
+
if (hubSlugs.has(candidateCollection)) {
|
|
744
810
|
score += 15;
|
|
745
811
|
reasons.push("hub collection");
|
|
746
812
|
}
|
|
@@ -1605,7 +1671,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
1605
1671
|
const skipAutoDiscovery = links && links.length > 0;
|
|
1606
1672
|
const searchQuery = extractSearchTerms(name, description);
|
|
1607
1673
|
if (searchQuery && !skipAutoDiscovery) {
|
|
1608
|
-
await populateHubCache();
|
|
1674
|
+
const hubSlugs = await populateHubCache();
|
|
1609
1675
|
const [searchResults, allCollections] = await Promise.all([
|
|
1610
1676
|
kernelQuery("chain.searchEntries", { query: searchQuery }),
|
|
1611
1677
|
getCollections()
|
|
@@ -1613,7 +1679,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
1613
1679
|
const collMap = /* @__PURE__ */ new Map();
|
|
1614
1680
|
for (const c of allCollections) collMap.set(c._id, c.slug);
|
|
1615
1681
|
const candidates = (searchResults ?? []).filter((r) => r.entryId !== finalEntryId && r._id !== internalId).map((r) => {
|
|
1616
|
-
const conf = computeLinkConfidence(r, name, description, resolvedCollection, collMap.get(r.collectionId) ?? "unknown");
|
|
1682
|
+
const conf = computeLinkConfidence(r, name, description, resolvedCollection, collMap.get(r.collectionId) ?? "unknown", hubSlugs);
|
|
1617
1683
|
return {
|
|
1618
1684
|
...r,
|
|
1619
1685
|
collSlug: collMap.get(r.collectionId) ?? "unknown",
|
|
@@ -2145,7 +2211,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2145
2211
|
logger: "product-brain"
|
|
2146
2212
|
});
|
|
2147
2213
|
const allCollections = await getCollections();
|
|
2148
|
-
await populateHubCache();
|
|
2214
|
+
const hubSlugs = await populateHubCache();
|
|
2149
2215
|
const collCache = /* @__PURE__ */ new Map();
|
|
2150
2216
|
for (const c of allCollections) collCache.set(c.slug, c);
|
|
2151
2217
|
const collIdToSlug = /* @__PURE__ */ new Map();
|
|
@@ -2325,7 +2391,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2325
2391
|
try {
|
|
2326
2392
|
const searchResults = await kernelQuery("chain.searchEntries", { query: searchQuery });
|
|
2327
2393
|
const candidates = (searchResults ?? []).filter((r) => r.entryId !== finalEntryId).map((r) => {
|
|
2328
|
-
const conf = computeLinkConfidence(r, entry.name, entry.description, resolvedSlug, collIdToSlug.get(r.collectionId) ?? "unknown");
|
|
2394
|
+
const conf = computeLinkConfidence(r, entry.name, entry.description, resolvedSlug, collIdToSlug.get(r.collectionId) ?? "unknown", hubSlugs);
|
|
2329
2395
|
return { ...r, collSlug: collIdToSlug.get(r.collectionId) ?? "unknown", confidence: conf.score };
|
|
2330
2396
|
}).sort((a, b) => b.confidence - a.confidence);
|
|
2331
2397
|
entryOverlapCount = candidates.filter((c) => c.entryId).length;
|
|
@@ -5755,6 +5821,9 @@ async function handleCreate2(slug, name, description, purpose, icon, navGroup, f
|
|
|
5755
5821
|
...meta.classificationPriority !== void 0 && { classificationPriority: meta.classificationPriority },
|
|
5756
5822
|
createdBy: getAgentSessionId() ? `agent:${getAgentSessionId()}` : "mcp"
|
|
5757
5823
|
});
|
|
5824
|
+
invalidateCollectionCacheForScope();
|
|
5825
|
+
invalidateProfileCacheForScope();
|
|
5826
|
+
invalidateHubCacheForScope();
|
|
5758
5827
|
const fieldList = fields.map((f) => ` - \`${f.key}\` (${f.type}${f.required ? ", required" : ""}${f.searchable ? ", searchable" : ""})`).join("\n");
|
|
5759
5828
|
return {
|
|
5760
5829
|
content: [{
|
|
@@ -5819,6 +5888,9 @@ async function handleUpdate(slug, name, description, purpose, icon, navGroup, fi
|
|
|
5819
5888
|
...meta.qualityCriteria !== void 0 && { qualityCriteria: meta.qualityCriteria },
|
|
5820
5889
|
...meta.usageGuidance !== void 0 && { usageGuidance: meta.usageGuidance }
|
|
5821
5890
|
});
|
|
5891
|
+
invalidateCollectionCacheForScope();
|
|
5892
|
+
invalidateProfileCacheForScope();
|
|
5893
|
+
invalidateHubCacheForScope();
|
|
5822
5894
|
const changes = [name && "name", description && "description", purpose && "purpose", icon && "icon", navGroup && "navGroup", fields && "fields", meta.defaultCanonicalKey !== void 0 && "defaultCanonicalKey", meta.defaultWorkflowStatus !== void 0 && "defaultWorkflowStatus", meta.validWorkflowStatuses !== void 0 && "validWorkflowStatuses", meta.classificationCheck !== void 0 && "classificationCheck", meta.classificationPriority !== void 0 && "classificationPriority", meta.qualityCriteria !== void 0 && "qualityCriteria", meta.usageGuidance !== void 0 && "usageGuidance"].filter(Boolean).join(", ");
|
|
5823
5895
|
return {
|
|
5824
5896
|
content: [{
|
|
@@ -9551,8 +9623,11 @@ function registerVerifyTools(server) {
|
|
|
9551
9623
|
});
|
|
9552
9624
|
let allEntryIds;
|
|
9553
9625
|
try {
|
|
9554
|
-
const allEntries = await kernelQuery(
|
|
9555
|
-
|
|
9626
|
+
const allEntries = await kernelQuery(
|
|
9627
|
+
"chain.listEntries",
|
|
9628
|
+
{ fields: ["entryId"] }
|
|
9629
|
+
);
|
|
9630
|
+
allEntryIds = new Set((allEntries ?? []).map((e) => e.entryId).filter(Boolean));
|
|
9556
9631
|
} catch {
|
|
9557
9632
|
allEntryIds = new Set(scopedEntries.map((e) => e.entryId).filter(Boolean));
|
|
9558
9633
|
}
|
|
@@ -9781,7 +9856,9 @@ function buildPlannedWork(allEntries) {
|
|
|
9781
9856
|
}
|
|
9782
9857
|
async function queryPlannedWork() {
|
|
9783
9858
|
try {
|
|
9784
|
-
const allEntries = await kernelQuery("chain.listEntries", {
|
|
9859
|
+
const allEntries = await kernelQuery("chain.listEntries", {
|
|
9860
|
+
fields: ["_id", "entryId", "name", "status", "workflowStatus", "collectionSlug", "stratum", "canonicalKey"]
|
|
9861
|
+
});
|
|
9785
9862
|
if (!allEntries) return { uncommittedDrafts: [], inProgressEntries: [], openTensions: [] };
|
|
9786
9863
|
return buildPlannedWork(allEntries);
|
|
9787
9864
|
} catch {
|
|
@@ -10460,11 +10537,12 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
10460
10537
|
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
10461
10538
|
}
|
|
10462
10539
|
try {
|
|
10463
|
-
const allEntries = await kernelQuery(
|
|
10464
|
-
|
|
10465
|
-
|
|
10540
|
+
const allEntries = await kernelQuery(
|
|
10541
|
+
"chain.listEntries",
|
|
10542
|
+
{ fields: ["seededByPlatform", "collectionId"] }
|
|
10466
10543
|
);
|
|
10467
|
-
const
|
|
10544
|
+
const committed = (allEntries ?? []).filter((e) => !e.seededByPlatform);
|
|
10545
|
+
const committedCollections = new Set(committed.map((e) => e.collectionId));
|
|
10468
10546
|
if (committed.length >= 10 && committedCollections.size >= 3) {
|
|
10469
10547
|
lines.push("");
|
|
10470
10548
|
lines.push(`_${committed.length} entries across ${committedCollections.size} collections on the Chain._`);
|
|
@@ -12380,7 +12458,9 @@ async function _handleOrient({ mode = "full", tier, task, scope }) {
|
|
|
12380
12458
|
}
|
|
12381
12459
|
let allEntries = [];
|
|
12382
12460
|
try {
|
|
12383
|
-
allEntries = await kernelQuery("chain.listEntries", {
|
|
12461
|
+
allEntries = await kernelQuery("chain.listEntries", {
|
|
12462
|
+
fields: ["_id", "entryId", "name", "status", "workflowStatus", "collectionSlug", "stratum", "canonicalKey"]
|
|
12463
|
+
}) ?? [];
|
|
12384
12464
|
} catch {
|
|
12385
12465
|
}
|
|
12386
12466
|
const plannedWork = buildPlannedWork(allEntries);
|
|
@@ -12645,11 +12725,14 @@ function formatOrgHealthLines(orgHealth, maxFlags = 3) {
|
|
|
12645
12725
|
}
|
|
12646
12726
|
async function fetchOrganisationHealth() {
|
|
12647
12727
|
try {
|
|
12648
|
-
const allEntries = await kernelQuery(
|
|
12728
|
+
const allEntries = await kernelQuery(
|
|
12729
|
+
"chain.listEntries",
|
|
12730
|
+
{ fields: ["name", "description", "collectionSlug"] }
|
|
12731
|
+
);
|
|
12649
12732
|
if (!allEntries || allEntries.length === 0) return null;
|
|
12650
12733
|
const classifyInput = allEntries.map((e) => ({
|
|
12651
12734
|
name: e.name ?? "",
|
|
12652
|
-
description: typeof e.
|
|
12735
|
+
description: typeof e.description === "string" ? e.description : ""
|
|
12653
12736
|
}));
|
|
12654
12737
|
const classifications = await kernelQuery(
|
|
12655
12738
|
"chain.batchClassifyHeuristic",
|
|
@@ -12680,8 +12763,11 @@ async function handleHealthCheck() {
|
|
|
12680
12763
|
let totalEntries = 0;
|
|
12681
12764
|
if (collections.length > 0) {
|
|
12682
12765
|
try {
|
|
12683
|
-
const entries = await kernelQuery(
|
|
12684
|
-
|
|
12766
|
+
const entries = await kernelQuery(
|
|
12767
|
+
"chain.listEntries",
|
|
12768
|
+
{ fields: ["entryId"] }
|
|
12769
|
+
);
|
|
12770
|
+
totalEntries = (entries ?? []).length;
|
|
12685
12771
|
} catch (e) {
|
|
12686
12772
|
errors.push(`Entry count failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
12687
12773
|
}
|
|
@@ -14398,4 +14484,4 @@ export {
|
|
|
14398
14484
|
createProductBrainServer,
|
|
14399
14485
|
initFeatureFlags
|
|
14400
14486
|
};
|
|
14401
|
-
//# sourceMappingURL=chunk-
|
|
14487
|
+
//# sourceMappingURL=chunk-54VAG6CG.js.map
|