@productbrain/mcp 0.0.1-beta.1414 → 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-K5LBOKWM.js → chunk-54VAG6CG.js} +95 -23
- 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-K5LBOKWM.js.map +0 -1
- package/dist/chunk-RJ6V5N5E.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;
|
|
789
|
+
}
|
|
790
|
+
function invalidateHubCacheForScope() {
|
|
791
|
+
hubCache.delete(cacheScope());
|
|
726
792
|
}
|
|
727
|
-
function computeLinkConfidence(candidate, sourceName, sourceDescription, sourceCollection, candidateCollection) {
|
|
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: [{
|
|
@@ -14412,4 +14484,4 @@ export {
|
|
|
14412
14484
|
createProductBrainServer,
|
|
14413
14485
|
initFeatureFlags
|
|
14414
14486
|
};
|
|
14415
|
-
//# sourceMappingURL=chunk-
|
|
14487
|
+
//# sourceMappingURL=chunk-54VAG6CG.js.map
|