klypix-mcp 1.67.0 → 1.67.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klypix-mcp",
3
- "version": "1.67.0",
3
+ "version": "1.67.2",
4
4
  "description": "Shared project brain and MCP coordination server for multi-agent coding.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -83,7 +83,7 @@
83
83
  "bench": "node bin/klypix-mcp.mjs bench",
84
84
  "test:bench": "node test/bench.mjs",
85
85
  "pretest": "node test/publish-workflow.mjs",
86
- "test": "node test/publish-verdict.mjs && node test/remote-client.mjs && node test/project-graph.mjs && node test/project-map-cli.mjs && node test/mcp-auto-update.mjs && node test/mcp-supervisor.mjs && node test/runtime-inspector.mjs && node test/codex-hooks.mjs && node test/request-identity.mjs && node test/session-identity-core.mjs && node test/agent-presence.mjs && node test/message-delivery-v3.mjs && node test/claude-message-delivery-v3.mjs && node test/result-reconcile.mjs && node test/evidence-publication-gate.mjs && node test/release-evidence-cli.mjs && node test/intent-guard.mjs && node test/git-capture-install.mjs && node test/brain-history.mjs && node test/brain-graveyard.mjs && node test/archived-visibility.mjs && node test/finding-routing.mjs && node test/finding-routing-hook.mjs && node test/presence-relay.mjs && node test/install-version.mjs && node test/context-gateway.mjs && node test/conformance.mjs && node test/brain-doctor.mjs && node test/version-currency.mjs && node test/ship-capture.mjs && node test/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/brief-and-recall.mjs && node test/layout-cluster.mjs && node test/brain-ask.mjs && node test/field-report-2026-07-04.mjs && node test/autoprop.mjs && node test/overlay-recency-2026-07-12.mjs && node test/brain-challenge.mjs && node test/brain-lens.mjs && node test/brain-kind.mjs && node test/rule-drafts.mjs && node test/claim-engine.mjs && node test/skill-staleness.mjs && node test/canvas-view.mjs && node test/status-completeness.mjs && node test/semantic-gate.mjs && node test/memory-runtime.mjs && node test/semantic-cache.mjs && node test/decay-status.mjs && node test/decay-hook.mjs && node test/evidence-anchors.mjs && node test/presence-visibility.mjs && node test/merge-brains.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/a2a-smoke.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/git-tools.mjs && node test/uninstall.mjs",
86
+ "test": "node test/publish-verdict.mjs && node test/remote-client.mjs && node test/project-graph.mjs && node test/project-map-cli.mjs && node test/mcp-auto-update.mjs && node test/mcp-supervisor.mjs && node test/runtime-inspector.mjs && node test/codex-hooks.mjs && node test/request-identity.mjs && node test/session-identity-core.mjs && node test/agent-presence.mjs && node test/message-delivery-v3.mjs && node test/claude-message-delivery-v3.mjs && node test/result-reconcile.mjs && node test/evidence-publication-gate.mjs && node test/release-evidence-cli.mjs && node test/intent-guard.mjs && node test/git-capture-install.mjs && node test/brain-history.mjs && node test/brain-graveyard.mjs && node test/archived-visibility.mjs && node test/finding-routing.mjs && node test/finding-routing-hook.mjs && node test/presence-relay.mjs && node test/install-version.mjs && node test/context-gateway.mjs && node test/conformance.mjs && node test/brain-doctor.mjs && node test/version-currency.mjs && node test/ship-capture.mjs && node test/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/brief-and-recall.mjs && node test/layout-cluster.mjs && node test/brain-ask.mjs && node test/field-report-2026-07-04.mjs && node test/autoprop.mjs && node test/overlay-recency-2026-07-12.mjs && node test/brain-challenge.mjs && node test/brain-lens.mjs && node test/brain-kind.mjs && node test/rule-drafts.mjs && node test/claim-engine.mjs && node test/skill-staleness.mjs && node test/canvas-view.mjs && node test/status-completeness.mjs && node test/semantic-security.mjs && node test/semantic-gate.mjs && node test/memory-runtime.mjs && node test/semantic-cache.mjs && node test/decay-status.mjs && node test/decay-hook.mjs && node test/evidence-anchors.mjs && node test/presence-visibility.mjs && node test/merge-brains.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/a2a-smoke.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/git-tools.mjs && node test/uninstall.mjs",
87
87
  "test:memory": "node test/memory-runtime.mjs",
88
88
  "test:memory:soak": "node --expose-gc test/memory-soak.mjs",
89
89
  "runtime": "node bin/klypix-runtime.mjs"
@@ -18,6 +18,7 @@ import fs from 'fs';
18
18
  import path from 'path';
19
19
  import os from 'os';
20
20
  import crypto from 'crypto';
21
+ import { createRequire } from 'module';
21
22
 
22
23
  const PB_DIR = path.join(os.homedir(), '.claude', 'project-brain');
23
24
  const EMB_DIR = path.join(PB_DIR, 'embeddings');
@@ -29,6 +30,49 @@ export const HOOK_EMBEDDING_MODEL_ID = 'Xenova/bge-small-en-v1.5';
29
30
  export const HOOK_EMBEDDING_QUERY_PREFIX = 'Represent this sentence for searching relevant passages: ';
30
31
  export const HOOK_EMBEDDING_POOLING = 'cls';
31
32
  export const HOOK_EMBEDDING_CACHE_KEY = `${HOOK_EMBEDDING_MODEL_ID}|q8|${HOOK_EMBEDDING_POOLING}|query-instruction-v1`;
33
+ const MIN_SAFE_SHARP_VERSION = [0, 35, 3];
34
+
35
+ function findPackageManifest(entryPath, expectedName) {
36
+ let dir = path.dirname(entryPath);
37
+ while (dir !== path.dirname(dir)) {
38
+ try {
39
+ const manifestPath = path.join(dir, 'package.json');
40
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
41
+ if (manifest?.name === expectedName) return { manifestPath, manifest };
42
+ } catch { /* keep walking */ }
43
+ dir = path.dirname(dir);
44
+ }
45
+ return null;
46
+ }
47
+
48
+ function isSafeSharpVersion(version) {
49
+ const parts = String(version || '').split('.').slice(0, 3).map(Number);
50
+ if (parts.length !== 3 || parts.some(part => !Number.isInteger(part) || part < 0)) return false;
51
+ for (let i = 0; i < MIN_SAFE_SHARP_VERSION.length; i++) {
52
+ if (parts[i] !== MIN_SAFE_SHARP_VERSION[i]) return parts[i] > MIN_SAFE_SHARP_VERSION[i];
53
+ }
54
+ return true;
55
+ }
56
+
57
+ export function semanticRuntimeEntryIfSafe(transformersEntry) {
58
+ try {
59
+ const transformers = findPackageManifest(transformersEntry, '@huggingface/transformers');
60
+ if (!transformers) return null;
61
+ // Resolve from the exact executable bundle entry. Resolving from the
62
+ // package manifest misses a Sharp installation shadowed under dist/
63
+ // even though that is the copy the ESM bundle itself will import.
64
+ const sharpEntry = createRequire(transformersEntry).resolve('sharp');
65
+ const sharp = findPackageManifest(sharpEntry, 'sharp');
66
+ return sharp && isSafeSharpVersion(sharp.manifest.version) ? transformersEntry : null;
67
+ } catch { return null; }
68
+ }
69
+
70
+ function ambientTransformersEntryIfSafe() {
71
+ try {
72
+ const entry = createRequire(import.meta.url).resolve('@huggingface/transformers');
73
+ return semanticRuntimeEntryIfSafe(entry);
74
+ } catch { return null; }
75
+ }
32
76
 
33
77
  // Memoized within THIS process only (a one-shot hook run) — across prompts it
34
78
  // reloads cold, which is exactly why we only pay it on a lexical miss.
@@ -37,15 +81,20 @@ function getEmbedder() {
37
81
  if (_embedder !== undefined) return _embedder;
38
82
  _embedder = (async () => {
39
83
  let t;
40
- try { t = await import('@huggingface/transformers'); }
41
- catch {
84
+ const ambientEntry = ambientTransformersEntryIfSafe();
85
+ if (ambientEntry) {
86
+ try { t = await import('@huggingface/transformers'); } catch { /* try owned runtime */ }
87
+ }
88
+ if (!t) {
42
89
  const base = path.join(PB_DIR, 'semantic', 'node_modules', '@huggingface', 'transformers', 'dist');
43
90
  let last;
44
91
  for (const f of ['transformers.node.mjs', 'transformers.mjs']) {
45
- try { t = await import(new URL('file:///' + path.join(base, f).replace(/\\/g, '/')).href); last = null; break; }
92
+ const entry = path.join(base, f);
93
+ if (!semanticRuntimeEntryIfSafe(entry)) continue;
94
+ try { t = await import(new URL('file:///' + entry.replace(/\\/g, '/')).href); last = null; break; }
46
95
  catch (e) { last = e; }
47
96
  }
48
- if (!t) throw last;
97
+ if (!t) throw last || new Error('semantic runtime rejected: sharp must be >=0.35.3');
49
98
  }
50
99
  t.env.cacheDir = path.join(PB_DIR, 'hf-cache');
51
100
  return await t.pipeline('feature-extraction', HOOK_EMBEDDING_MODEL_ID, { dtype: 'q8' });
@@ -13,6 +13,7 @@ import { createRequire } from 'module';
13
13
  const PB_DIR = path.join(os.homedir(), '.claude', 'project-brain');
14
14
  const EMB_DIR = path.join(PB_DIR, 'embeddings');
15
15
  const sha1 = (s) => crypto.createHash('sha1').update(String(s)).digest('hex');
16
+ const MIN_SAFE_SHARP_VERSION = [0, 35, 3];
16
17
  export const EMBEDDING_MODEL_ID = 'Xenova/bge-small-en-v1.5';
17
18
  export const EMBEDDING_QUERY_PREFIX = 'Represent this sentence for searching relevant passages: ';
18
19
  export const EMBEDDING_POOLING = 'cls';
@@ -236,16 +237,62 @@ function disposeTensorTree(value, seen = new Set()) {
236
237
  for (const item of Object.values(value)) disposeTensorTree(item, seen);
237
238
  }
238
239
 
240
+ function findPackageManifest(entryPath, expectedName) {
241
+ let dir = path.dirname(entryPath);
242
+ while (dir !== path.dirname(dir)) {
243
+ try {
244
+ const manifestPath = path.join(dir, 'package.json');
245
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
246
+ if (manifest?.name === expectedName) return { manifestPath, manifest };
247
+ } catch { /* keep walking */ }
248
+ dir = path.dirname(dir);
249
+ }
250
+ return null;
251
+ }
252
+
253
+ function isSafeSharpVersion(version) {
254
+ const parts = String(version || '').split('.').slice(0, 3).map(Number);
255
+ if (parts.length !== 3 || parts.some(part => !Number.isInteger(part) || part < 0)) return false;
256
+ for (let i = 0; i < MIN_SAFE_SHARP_VERSION.length; i++) {
257
+ if (parts[i] !== MIN_SAFE_SHARP_VERSION[i]) return parts[i] > MIN_SAFE_SHARP_VERSION[i];
258
+ }
259
+ return true;
260
+ }
261
+
262
+ export function semanticRuntimeEntryIfSafe(transformersEntry) {
263
+ try {
264
+ const transformers = findPackageManifest(transformersEntry, '@huggingface/transformers');
265
+ if (!transformers) return null;
266
+ // Resolve from the exact executable bundle entry. Resolving from the
267
+ // package manifest misses a Sharp installation shadowed under dist/
268
+ // even though that is the copy the ESM bundle itself will import.
269
+ const sharpEntry = createRequire(transformersEntry).resolve('sharp');
270
+ const sharp = findPackageManifest(sharpEntry, 'sharp');
271
+ return sharp && isSafeSharpVersion(sharp.manifest.version) ? transformersEntry : null;
272
+ } catch { return null; }
273
+ }
274
+
275
+ function ambientTransformersEntryIfSafe() {
276
+ try {
277
+ const entry = createRequire(import.meta.url).resolve('@huggingface/transformers');
278
+ return semanticRuntimeEntryIfSafe(entry);
279
+ } catch { return null; }
280
+ }
281
+
239
282
  async function loadTransformers() {
240
- try { return await import('@huggingface/transformers'); }
241
- catch {
283
+ if (ambientTransformersEntryIfSafe()) {
284
+ try { return await import('@huggingface/transformers'); } catch { /* try owned runtime */ }
285
+ }
286
+ {
242
287
  const base = path.join(PB_DIR, 'semantic', 'node_modules', '@huggingface', 'transformers', 'dist');
243
288
  let lastErr;
244
289
  for (const f of ['transformers.node.mjs', 'transformers.mjs']) {
245
- try { return await import(new URL('file:///' + path.join(base, f).replace(/\\/g, '/')).href); }
290
+ const entry = path.join(base, f);
291
+ if (!semanticRuntimeEntryIfSafe(entry)) continue;
292
+ try { return await import(new URL('file:///' + entry.replace(/\\/g, '/')).href); }
246
293
  catch (error) { lastErr = error; }
247
294
  }
248
- throw lastErr;
295
+ throw lastErr || new Error('semantic runtime rejected: sharp must be >=0.35.3');
249
296
  }
250
297
  }
251
298
 
@@ -267,13 +314,15 @@ export function semanticRuntimeInstalled() {
267
314
  if (runtimeInstalledCache !== null) return runtimeInstalledCache;
268
315
  let found = false;
269
316
  try {
270
- createRequire(import.meta.url).resolve('@huggingface/transformers');
271
- found = true;
317
+ const entry = createRequire(import.meta.url).resolve('@huggingface/transformers');
318
+ found = Boolean(semanticRuntimeEntryIfSafe(entry));
272
319
  } catch {
320
+ // Fall through to the owned runtime below.
321
+ }
322
+ if (!found) {
273
323
  const base = path.join(PB_DIR, 'semantic', 'node_modules', '@huggingface', 'transformers', 'dist');
274
- found = ['transformers.node.mjs', 'transformers.mjs'].some(f => {
275
- try { return fs.existsSync(path.join(base, f)); } catch { return false; }
276
- });
324
+ found = ['transformers.node.mjs', 'transformers.mjs']
325
+ .some(f => Boolean(semanticRuntimeEntryIfSafe(path.join(base, f))));
277
326
  }
278
327
  runtimeInstalledCache = found;
279
328
  return found;