@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.
@@ -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-RJ6V5N5E.js";
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 cachedCollections = null;
552
- var cachedBySlug = null;
598
+ var collectionCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
553
599
  async function getCollections() {
554
- if (cachedCollections !== null) return cachedCollections;
555
- const result = await kernelQuery("chain.listCollections");
556
- cachedCollections = result ?? [];
557
- cachedBySlug = new Map(cachedCollections.map((c) => [c.slug, c]));
558
- return cachedCollections;
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 cachedBySlug.get(slug);
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 = null;
757
+ var profileCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
707
758
  async function getProfile(slug) {
708
- if (!profileCache) profileCache = /* @__PURE__ */ new Map();
709
- const cached = profileCache.get(slug);
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
- profileCache.set(slug, profile);
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 hubCollectionCache = null;
778
+ var hubCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
720
779
  async function populateHubCache() {
721
- if (hubCollectionCache !== null) return;
780
+ const scope = cacheScope();
781
+ const hit = hubCache.get(scope);
782
+ if (hit) return hit;
722
783
  const cols = await getCollections();
723
- hubCollectionCache = new Set(
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 (hubCollectionCache?.has(candidateCollection)) {
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-K5LBOKWM.js.map
14487
+ //# sourceMappingURL=chunk-54VAG6CG.js.map