@darkiceinteractive/mcp-conductor 2.0.0-alpha.1 → 3.0.0-beta.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/README.md +35 -5
- package/dist/bin/cli.d.ts +20 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +260 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bridge/http-server.d.ts +35 -0
- package/dist/bridge/http-server.d.ts.map +1 -1
- package/dist/bridge/http-server.js +51 -2
- package/dist/bridge/http-server.js.map +1 -1
- package/dist/bridge/index.d.ts +1 -0
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/index.js +1 -0
- package/dist/bridge/index.js.map +1 -1
- package/dist/bridge/pool.d.ts +95 -0
- package/dist/bridge/pool.d.ts.map +1 -0
- package/dist/bridge/pool.js +384 -0
- package/dist/bridge/pool.js.map +1 -0
- package/dist/cache/cache.d.ts +64 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +209 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/delta.d.ts +32 -0
- package/dist/cache/delta.d.ts.map +1 -0
- package/dist/cache/delta.js +131 -0
- package/dist/cache/delta.js.map +1 -0
- package/dist/cache/disk.d.ts +65 -0
- package/dist/cache/disk.d.ts.map +1 -0
- package/dist/cache/disk.js +238 -0
- package/dist/cache/disk.js.map +1 -0
- package/dist/cache/index.d.ts +53 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +12 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/key.d.ts +44 -0
- package/dist/cache/key.d.ts.map +1 -0
- package/dist/cache/key.js +83 -0
- package/dist/cache/key.js.map +1 -0
- package/dist/cache/lru.d.ts +57 -0
- package/dist/cache/lru.d.ts.map +1 -0
- package/dist/cache/lru.js +112 -0
- package/dist/cache/lru.js.map +1 -0
- package/dist/cache/policy.d.ts +34 -0
- package/dist/cache/policy.d.ts.map +1 -0
- package/dist/cache/policy.js +95 -0
- package/dist/cache/policy.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +33 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +135 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export-servers.d.ts +22 -0
- package/dist/cli/commands/export-servers.d.ts.map +1 -0
- package/dist/cli/commands/export-servers.js +45 -0
- package/dist/cli/commands/export-servers.js.map +1 -0
- package/dist/cli/commands/import-servers.d.ts +57 -0
- package/dist/cli/commands/import-servers.d.ts.map +1 -0
- package/dist/cli/commands/import-servers.js +137 -0
- package/dist/cli/commands/import-servers.js.map +1 -0
- package/dist/cli/commands/routing.d.ts +34 -0
- package/dist/cli/commands/routing.d.ts.map +1 -0
- package/dist/cli/commands/routing.js +60 -0
- package/dist/cli/commands/routing.js.map +1 -0
- package/dist/cli/commands/test-server.d.ts +34 -0
- package/dist/cli/commands/test-server.d.ts.map +1 -0
- package/dist/cli/commands/test-server.js +86 -0
- package/dist/cli/commands/test-server.js.map +1 -0
- package/dist/cli/daemon.d.ts +60 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +244 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/replay.d.ts +16 -0
- package/dist/cli/replay.d.ts.map +1 -0
- package/dist/cli/replay.js +89 -0
- package/dist/cli/replay.js.map +1 -0
- package/dist/cli/wizard/setup.d.ts +12 -0
- package/dist/cli/wizard/setup.d.ts.map +1 -0
- package/dist/cli/wizard/setup.js +71 -0
- package/dist/cli/wizard/setup.js.map +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +4 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/daemon/client.d.ts +99 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +292 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/discovery.d.ts +50 -0
- package/dist/daemon/discovery.d.ts.map +1 -0
- package/dist/daemon/discovery.js +104 -0
- package/dist/daemon/discovery.js.map +1 -0
- package/dist/daemon/index.d.ts +16 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +11 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/sandbox-api.d.ts +45 -0
- package/dist/daemon/sandbox-api.d.ts.map +1 -0
- package/dist/daemon/sandbox-api.js +74 -0
- package/dist/daemon/sandbox-api.js.map +1 -0
- package/dist/daemon/server.d.ts +65 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +373 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/shared-kv.d.ts +81 -0
- package/dist/daemon/shared-kv.d.ts.map +1 -0
- package/dist/daemon/shared-kv.js +215 -0
- package/dist/daemon/shared-kv.js.map +1 -0
- package/dist/daemon/shared-lock.d.ts +71 -0
- package/dist/daemon/shared-lock.d.ts.map +1 -0
- package/dist/daemon/shared-lock.js +119 -0
- package/dist/daemon/shared-lock.js.map +1 -0
- package/dist/hub/mcp-hub.d.ts +23 -0
- package/dist/hub/mcp-hub.d.ts.map +1 -1
- package/dist/hub/mcp-hub.js +34 -1
- package/dist/hub/mcp-hub.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/observability/anomaly.d.ts +67 -0
- package/dist/observability/anomaly.d.ts.map +1 -0
- package/dist/observability/anomaly.js +141 -0
- package/dist/observability/anomaly.js.map +1 -0
- package/dist/observability/cost-predictor.d.ts +49 -0
- package/dist/observability/cost-predictor.d.ts.map +1 -0
- package/dist/observability/cost-predictor.js +145 -0
- package/dist/observability/cost-predictor.js.map +1 -0
- package/dist/observability/hot-path.d.ts +49 -0
- package/dist/observability/hot-path.d.ts.map +1 -0
- package/dist/observability/hot-path.js +125 -0
- package/dist/observability/hot-path.js.map +1 -0
- package/dist/observability/index.d.ts +10 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +10 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/replay.d.ts +104 -0
- package/dist/observability/replay.d.ts.map +1 -0
- package/dist/observability/replay.js +239 -0
- package/dist/observability/replay.js.map +1 -0
- package/dist/registry/built-in-recommendations.d.ts +54 -0
- package/dist/registry/built-in-recommendations.d.ts.map +1 -0
- package/dist/registry/built-in-recommendations.js +65 -0
- package/dist/registry/built-in-recommendations.js.map +1 -0
- package/dist/registry/events.d.ts +26 -0
- package/dist/registry/events.d.ts.map +1 -0
- package/dist/registry/events.js +22 -0
- package/dist/registry/events.js.map +1 -0
- package/dist/registry/index.d.ts +159 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +12 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/registry.d.ts +87 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +294 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry/snapshot.d.ts +42 -0
- package/dist/registry/snapshot.d.ts.map +1 -0
- package/dist/registry/snapshot.js +71 -0
- package/dist/registry/snapshot.js.map +1 -0
- package/dist/registry/typegen.d.ts +48 -0
- package/dist/registry/typegen.d.ts.map +1 -0
- package/dist/registry/typegen.js +200 -0
- package/dist/registry/typegen.js.map +1 -0
- package/dist/registry/validator.d.ts +23 -0
- package/dist/registry/validator.d.ts.map +1 -0
- package/dist/registry/validator.js +50 -0
- package/dist/registry/validator.js.map +1 -0
- package/dist/reliability/breaker.d.ts +57 -0
- package/dist/reliability/breaker.d.ts.map +1 -0
- package/dist/reliability/breaker.js +130 -0
- package/dist/reliability/breaker.js.map +1 -0
- package/dist/reliability/errors.d.ts +78 -0
- package/dist/reliability/errors.d.ts.map +1 -0
- package/dist/reliability/errors.js +160 -0
- package/dist/reliability/errors.js.map +1 -0
- package/dist/reliability/gateway.d.ts +88 -0
- package/dist/reliability/gateway.d.ts.map +1 -0
- package/dist/reliability/gateway.js +180 -0
- package/dist/reliability/gateway.js.map +1 -0
- package/dist/reliability/index.d.ts +20 -0
- package/dist/reliability/index.d.ts.map +1 -0
- package/dist/reliability/index.js +16 -0
- package/dist/reliability/index.js.map +1 -0
- package/dist/reliability/profile.d.ts +49 -0
- package/dist/reliability/profile.d.ts.map +1 -0
- package/dist/reliability/profile.js +58 -0
- package/dist/reliability/profile.js.map +1 -0
- package/dist/reliability/retry.d.ts +39 -0
- package/dist/reliability/retry.d.ts.map +1 -0
- package/dist/reliability/retry.js +51 -0
- package/dist/reliability/retry.js.map +1 -0
- package/dist/reliability/timeout.d.ts +34 -0
- package/dist/reliability/timeout.d.ts.map +1 -0
- package/dist/reliability/timeout.js +53 -0
- package/dist/reliability/timeout.js.map +1 -0
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +122 -14
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/findtool/embed.d.ts +28 -0
- package/dist/runtime/findtool/embed.d.ts.map +1 -0
- package/dist/runtime/findtool/embed.js +85 -0
- package/dist/runtime/findtool/embed.js.map +1 -0
- package/dist/runtime/findtool/index.d.ts +52 -0
- package/dist/runtime/findtool/index.d.ts.map +1 -0
- package/dist/runtime/findtool/index.js +78 -0
- package/dist/runtime/findtool/index.js.map +1 -0
- package/dist/runtime/findtool/vector-index.d.ts +53 -0
- package/dist/runtime/findtool/vector-index.d.ts.map +1 -0
- package/dist/runtime/findtool/vector-index.js +71 -0
- package/dist/runtime/findtool/vector-index.js.map +1 -0
- package/dist/runtime/helpers/budget.d.ts +27 -0
- package/dist/runtime/helpers/budget.d.ts.map +1 -0
- package/dist/runtime/helpers/budget.js +103 -0
- package/dist/runtime/helpers/budget.js.map +1 -0
- package/dist/runtime/helpers/compact.d.ts +32 -0
- package/dist/runtime/helpers/compact.d.ts.map +1 -0
- package/dist/runtime/helpers/compact.js +93 -0
- package/dist/runtime/helpers/compact.js.map +1 -0
- package/dist/runtime/helpers/delta.d.ts +45 -0
- package/dist/runtime/helpers/delta.d.ts.map +1 -0
- package/dist/runtime/helpers/delta.js +116 -0
- package/dist/runtime/helpers/delta.js.map +1 -0
- package/dist/runtime/helpers/index.d.ts +16 -0
- package/dist/runtime/helpers/index.d.ts.map +1 -0
- package/dist/runtime/helpers/index.js +13 -0
- package/dist/runtime/helpers/index.js.map +1 -0
- package/dist/runtime/helpers/summarize.d.ts +24 -0
- package/dist/runtime/helpers/summarize.d.ts.map +1 -0
- package/dist/runtime/helpers/summarize.js +124 -0
- package/dist/runtime/helpers/summarize.js.map +1 -0
- package/dist/runtime/helpers/worker-preload.d.ts +25 -0
- package/dist/runtime/helpers/worker-preload.d.ts.map +1 -0
- package/dist/runtime/helpers/worker-preload.js +223 -0
- package/dist/runtime/helpers/worker-preload.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/pool/index.d.ts +11 -0
- package/dist/runtime/pool/index.d.ts.map +1 -0
- package/dist/runtime/pool/index.js +8 -0
- package/dist/runtime/pool/index.js.map +1 -0
- package/dist/runtime/pool/recycle.d.ts +44 -0
- package/dist/runtime/pool/recycle.d.ts.map +1 -0
- package/dist/runtime/pool/recycle.js +50 -0
- package/dist/runtime/pool/recycle.js.map +1 -0
- package/dist/runtime/pool/worker-pool.d.ts +77 -0
- package/dist/runtime/pool/worker-pool.d.ts.map +1 -0
- package/dist/runtime/pool/worker-pool.js +216 -0
- package/dist/runtime/pool/worker-pool.js.map +1 -0
- package/dist/runtime/pool/worker.d.ts +80 -0
- package/dist/runtime/pool/worker.d.ts.map +1 -0
- package/dist/runtime/pool/worker.js +324 -0
- package/dist/runtime/pool/worker.js.map +1 -0
- package/dist/server/mcp-server.d.ts +3 -0
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +457 -3
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/server/passthrough-registrar.d.ts +123 -0
- package/dist/server/passthrough-registrar.d.ts.map +1 -0
- package/dist/server/passthrough-registrar.js +199 -0
- package/dist/server/passthrough-registrar.js.map +1 -0
- package/dist/skills/skills-engine.d.ts +9 -1
- package/dist/skills/skills-engine.d.ts.map +1 -1
- package/dist/skills/skills-engine.js +20 -3
- package/dist/skills/skills-engine.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/tokenize.d.ts +55 -0
- package/dist/utils/tokenize.d.ts.map +1 -0
- package/dist/utils/tokenize.js +205 -0
- package/dist/utils/tokenize.js.map +1 -0
- package/dist/version.d.ts +3 -3
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +3 -3
- package/dist/version.js.map +1 -1
- package/package.json +13 -3
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CacheLayer — three-tier cache composition (memory LRU + disk CBOR + delta).
|
|
3
|
+
*
|
|
4
|
+
* Call flow:
|
|
5
|
+
* get(server, tool, args)
|
|
6
|
+
* → memory LRU hit? → return CacheHit (source: 'memory')
|
|
7
|
+
* → disk hit? → promote to memory, return CacheHit (source: 'disk')
|
|
8
|
+
* → miss → return null (caller must fetch from backend)
|
|
9
|
+
*
|
|
10
|
+
* Stale-while-revalidate (SWR):
|
|
11
|
+
* All staleness decisions are made here, not in DiskCache, so that
|
|
12
|
+
* vi.useFakeTimers() in tests correctly affects TTL checks at all tiers.
|
|
13
|
+
*
|
|
14
|
+
* Bridge wiring order (per spec):
|
|
15
|
+
* cache check → cache miss → reliability gateway (Agent C) → backend
|
|
16
|
+
*
|
|
17
|
+
* @module cache/cache
|
|
18
|
+
*/
|
|
19
|
+
import { MemoryLru } from './lru.js';
|
|
20
|
+
import { DiskCache } from './disk.js';
|
|
21
|
+
import { buildCacheKey, cacheKeyToString } from './key.js';
|
|
22
|
+
import { resolveTtl, isCacheable } from './policy.js';
|
|
23
|
+
import { computeDelta } from './delta.js';
|
|
24
|
+
import { logger } from '../utils/index.js';
|
|
25
|
+
export class CacheLayer {
|
|
26
|
+
lru;
|
|
27
|
+
disk;
|
|
28
|
+
registry;
|
|
29
|
+
options;
|
|
30
|
+
unsubscribe = null;
|
|
31
|
+
diskMisses = 0;
|
|
32
|
+
ttlMap = new Map();
|
|
33
|
+
/** Keys currently being background-revalidated; prevents SWR thundering herd. */
|
|
34
|
+
revalidating = new Set();
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.options = {
|
|
37
|
+
diskDir: `${process.env['HOME'] ?? '/tmp'}/.mcp-conductor/cache`,
|
|
38
|
+
maxMemoryBytes: 100 * 1024 * 1024,
|
|
39
|
+
maxDiskBytes: 2 * 1024 * 1024 * 1024,
|
|
40
|
+
staleWhileRevalidate: true,
|
|
41
|
+
policies: {},
|
|
42
|
+
...options,
|
|
43
|
+
};
|
|
44
|
+
this.registry = options.registry;
|
|
45
|
+
this.lru = new MemoryLru({ maxMemoryBytes: this.options.maxMemoryBytes });
|
|
46
|
+
this.disk = new DiskCache({
|
|
47
|
+
diskDir: this.options.diskDir,
|
|
48
|
+
maxDiskBytes: this.options.maxDiskBytes,
|
|
49
|
+
});
|
|
50
|
+
const sub = this.registry.watch((event) => {
|
|
51
|
+
if (event.type === 'tool-updated') {
|
|
52
|
+
const prefix = `${event.server}:${event.tool}:`;
|
|
53
|
+
this.lru.invalidateByPrefix(prefix);
|
|
54
|
+
this.disk.invalidateByPrefix(prefix).catch((err) => logger.warn('CacheLayer: disk invalidation error', { error: String(err) }));
|
|
55
|
+
logger.debug('CacheLayer: invalidated on tool-updated', {
|
|
56
|
+
server: event.server,
|
|
57
|
+
tool: event.tool,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
this.unsubscribe = sub.unsubscribe;
|
|
62
|
+
}
|
|
63
|
+
async get(server, tool, args) {
|
|
64
|
+
const cacheKey = buildCacheKey(server, tool, args);
|
|
65
|
+
const keyStr = cacheKeyToString(cacheKey);
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
// 1. Memory LRU — MemoryLru.get() enforces TTL via manual storedAt check
|
|
68
|
+
const memHit = this.lru.get(keyStr);
|
|
69
|
+
if (memHit) {
|
|
70
|
+
const ttlMs = this.ttlMap.get(keyStr) ?? 0;
|
|
71
|
+
const isStale = ttlMs > 0 && now - memHit.storedAt > ttlMs;
|
|
72
|
+
if (!isStale) {
|
|
73
|
+
return { ...memHit, staleness: now - memHit.storedAt };
|
|
74
|
+
}
|
|
75
|
+
if (this.options.staleWhileRevalidate) {
|
|
76
|
+
// Suppress duplicate SWR triggers: if already revalidating, return stale without the flag
|
|
77
|
+
const needsRevalidation = !this.revalidating.has(keyStr);
|
|
78
|
+
return { ...memHit, staleness: now - memHit.storedAt, needsRevalidation };
|
|
79
|
+
}
|
|
80
|
+
this.lru.delete(keyStr);
|
|
81
|
+
// fall through to disk
|
|
82
|
+
}
|
|
83
|
+
// 2. Disk — DiskCache.get() does NOT enforce TTL; we decide staleness here
|
|
84
|
+
const diskHit = await this.disk.get(cacheKey.argsHash);
|
|
85
|
+
if (diskHit) {
|
|
86
|
+
const entryTtl = diskHit.ttlMs > 0 ? diskHit.ttlMs : (this.ttlMap.get(keyStr) ?? 0);
|
|
87
|
+
const staleness = now - diskHit.storedAt;
|
|
88
|
+
const isStale = entryTtl > 0 && staleness > entryTtl;
|
|
89
|
+
if (!isStale) {
|
|
90
|
+
const remaining = entryTtl > 0 ? Math.max(1, entryTtl - staleness) : 0;
|
|
91
|
+
this.lru.set(keyStr, diskHit.value, remaining);
|
|
92
|
+
this.ttlMap.set(keyStr, entryTtl);
|
|
93
|
+
return { value: diskHit.value, storedAt: diskHit.storedAt, source: 'disk', staleness };
|
|
94
|
+
}
|
|
95
|
+
if (this.options.staleWhileRevalidate) {
|
|
96
|
+
const remaining = Math.max(1, entryTtl);
|
|
97
|
+
this.lru.set(keyStr, diskHit.value, remaining);
|
|
98
|
+
this.ttlMap.set(keyStr, entryTtl);
|
|
99
|
+
// Suppress duplicate SWR triggers: if already revalidating, return stale without the flag
|
|
100
|
+
const needsRevalidation = !this.revalidating.has(keyStr);
|
|
101
|
+
return {
|
|
102
|
+
value: diskHit.value,
|
|
103
|
+
storedAt: diskHit.storedAt,
|
|
104
|
+
source: 'disk',
|
|
105
|
+
staleness,
|
|
106
|
+
needsRevalidation,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
await this.disk.delete(cacheKey.argsHash);
|
|
110
|
+
}
|
|
111
|
+
this.diskMisses++;
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
async set(server, tool, args, result, options) {
|
|
115
|
+
const toolDef = this.registry.getTool(server, tool);
|
|
116
|
+
const ttlMs = options?.ttl ?? resolveTtl(toolDef, this.options.policies);
|
|
117
|
+
if (ttlMs === 0)
|
|
118
|
+
return;
|
|
119
|
+
const cacheKey = buildCacheKey(server, tool, args);
|
|
120
|
+
const keyStr = cacheKeyToString(cacheKey);
|
|
121
|
+
this.lru.set(keyStr, result, ttlMs);
|
|
122
|
+
this.ttlMap.set(keyStr, ttlMs);
|
|
123
|
+
await this.disk.set(cacheKey.argsHash, {
|
|
124
|
+
value: result,
|
|
125
|
+
storedAt: Date.now(),
|
|
126
|
+
ttlMs,
|
|
127
|
+
server,
|
|
128
|
+
tool,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async invalidate(server, pattern) {
|
|
132
|
+
const prefix = pattern ? `${server}:${pattern}` : `${server}:`;
|
|
133
|
+
const memCount = this.lru.invalidateByPrefix(prefix);
|
|
134
|
+
const diskCount = await this.disk.invalidateByPrefix(prefix);
|
|
135
|
+
return memCount + diskCount;
|
|
136
|
+
}
|
|
137
|
+
async delta(server, tool, args, current) {
|
|
138
|
+
const hit = await this.get(server, tool, args);
|
|
139
|
+
if (!hit)
|
|
140
|
+
return { unchanged: false, full: current };
|
|
141
|
+
return computeDelta(hit.value, current);
|
|
142
|
+
}
|
|
143
|
+
stats() {
|
|
144
|
+
const lruCounters = this.lru.getCounters();
|
|
145
|
+
const diskCounters = this.disk.getCounters();
|
|
146
|
+
return {
|
|
147
|
+
memoryHits: lruCounters.hits,
|
|
148
|
+
diskHits: diskCounters.hits,
|
|
149
|
+
misses: this.diskMisses,
|
|
150
|
+
evictions: lruCounters.evictions,
|
|
151
|
+
bytesInMemory: this.lru.bytesUsed,
|
|
152
|
+
bytesOnDisk: this.disk.approximateBytesOnDisk,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async clear() {
|
|
156
|
+
this.lru.clear();
|
|
157
|
+
this.lru.resetCounters();
|
|
158
|
+
this.ttlMap.clear();
|
|
159
|
+
this.diskMisses = 0;
|
|
160
|
+
await this.disk.clear();
|
|
161
|
+
}
|
|
162
|
+
wouldCache(server, tool) {
|
|
163
|
+
const toolDef = this.registry.getTool(server, tool);
|
|
164
|
+
return isCacheable(toolDef, this.options.policies);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Deduplicated background revalidation helper for stale-while-revalidate.
|
|
168
|
+
*
|
|
169
|
+
* Marks the cache key as being revalidated so subsequent concurrent `get()`
|
|
170
|
+
* calls that would also see `needsRevalidation: true` get `false` instead,
|
|
171
|
+
* preventing a thundering herd of parallel refreshes.
|
|
172
|
+
*
|
|
173
|
+
* The key is removed from the revalidating set on completion (success or
|
|
174
|
+
* failure), allowing a future `get()` to schedule a fresh revalidation if
|
|
175
|
+
* the entry is still stale at that point.
|
|
176
|
+
*
|
|
177
|
+
* @param server MCP server name.
|
|
178
|
+
* @param tool Tool name.
|
|
179
|
+
* @param args Original call arguments (used to build the cache key).
|
|
180
|
+
* @param fetchFn Async function that calls the backend and returns the fresh result.
|
|
181
|
+
* @returns A promise that resolves when the background refresh completes.
|
|
182
|
+
* Callers should `.catch()` the returned promise to handle errors.
|
|
183
|
+
*/
|
|
184
|
+
refreshInBackground(server, tool, args, fetchFn) {
|
|
185
|
+
const cacheKey = buildCacheKey(server, tool, args);
|
|
186
|
+
const keyStr = cacheKeyToString(cacheKey);
|
|
187
|
+
// Already in-flight — skip
|
|
188
|
+
if (this.revalidating.has(keyStr)) {
|
|
189
|
+
return Promise.resolve();
|
|
190
|
+
}
|
|
191
|
+
this.revalidating.add(keyStr);
|
|
192
|
+
return (async () => {
|
|
193
|
+
try {
|
|
194
|
+
const freshResult = await fetchFn();
|
|
195
|
+
await this.set(server, tool, args, freshResult);
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
this.revalidating.delete(keyStr);
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
}
|
|
202
|
+
destroy() {
|
|
203
|
+
if (this.unsubscribe) {
|
|
204
|
+
this.unsubscribe();
|
|
205
|
+
this.unsubscribe = null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delta encoding for repeated tool-call results.
|
|
3
|
+
*
|
|
4
|
+
* When a caller wants to know "what changed since the last call", we compute
|
|
5
|
+
* a structural diff and return only the delta. This is significantly smaller
|
|
6
|
+
* than the full result for incremental work (e.g. polling a list of issues).
|
|
7
|
+
*
|
|
8
|
+
* Strategy:
|
|
9
|
+
* - Arrays: compare items by identity (JSON equality). Report added/removed.
|
|
10
|
+
* Items that appear in both but at different positions count as unchanged
|
|
11
|
+
* (we do not track moves for simplicity).
|
|
12
|
+
* - Objects at the top level: compare property values; modified = { before, after }.
|
|
13
|
+
* - Scalars or mixed types: return full result (no useful delta possible).
|
|
14
|
+
* - When the delta payload is larger than the full result, return full result.
|
|
15
|
+
*
|
|
16
|
+
* @module cache/delta
|
|
17
|
+
*/
|
|
18
|
+
import type { DeltaResult } from './index.js';
|
|
19
|
+
/**
|
|
20
|
+
* Compute the delta between a previous result and the current result.
|
|
21
|
+
*
|
|
22
|
+
* Always returns a DeltaResult. When results are identical, returns
|
|
23
|
+
* `{ unchanged: true }`. When results differ but the full payload is
|
|
24
|
+
* smaller than the delta, `full` is set and the delta fields are absent.
|
|
25
|
+
*/
|
|
26
|
+
export declare function computeDelta(previous: unknown, current: unknown): DeltaResult;
|
|
27
|
+
/**
|
|
28
|
+
* Deep equality via JSON serialisation. Fast enough for typical MCP result
|
|
29
|
+
* sizes; avoids pulling in a heavy comparison library.
|
|
30
|
+
*/
|
|
31
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
32
|
+
//# sourceMappingURL=delta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.d.ts","sourceRoot":"","sources":["../../src/cache/delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,WAAW,CAqB7E;AAiFD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAOzD"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delta encoding for repeated tool-call results.
|
|
3
|
+
*
|
|
4
|
+
* When a caller wants to know "what changed since the last call", we compute
|
|
5
|
+
* a structural diff and return only the delta. This is significantly smaller
|
|
6
|
+
* than the full result for incremental work (e.g. polling a list of issues).
|
|
7
|
+
*
|
|
8
|
+
* Strategy:
|
|
9
|
+
* - Arrays: compare items by identity (JSON equality). Report added/removed.
|
|
10
|
+
* Items that appear in both but at different positions count as unchanged
|
|
11
|
+
* (we do not track moves for simplicity).
|
|
12
|
+
* - Objects at the top level: compare property values; modified = { before, after }.
|
|
13
|
+
* - Scalars or mixed types: return full result (no useful delta possible).
|
|
14
|
+
* - When the delta payload is larger than the full result, return full result.
|
|
15
|
+
*
|
|
16
|
+
* @module cache/delta
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Compute the delta between a previous result and the current result.
|
|
20
|
+
*
|
|
21
|
+
* Always returns a DeltaResult. When results are identical, returns
|
|
22
|
+
* `{ unchanged: true }`. When results differ but the full payload is
|
|
23
|
+
* smaller than the delta, `full` is set and the delta fields are absent.
|
|
24
|
+
*/
|
|
25
|
+
export function computeDelta(previous, current) {
|
|
26
|
+
// Identical check (fast path)
|
|
27
|
+
if (deepEqual(previous, current)) {
|
|
28
|
+
return { unchanged: true };
|
|
29
|
+
}
|
|
30
|
+
// Array diff
|
|
31
|
+
if (Array.isArray(previous) && Array.isArray(current)) {
|
|
32
|
+
return arrayDelta(previous, current);
|
|
33
|
+
}
|
|
34
|
+
// Object diff
|
|
35
|
+
if (isPlainObject(previous) && isPlainObject(current)) {
|
|
36
|
+
return objectDelta(previous, current);
|
|
37
|
+
}
|
|
38
|
+
// Scalar or mixed-type change — return full
|
|
39
|
+
return { unchanged: false, full: current };
|
|
40
|
+
}
|
|
41
|
+
// ── Array diff ────────────────────────────────────────────────────────────────
|
|
42
|
+
function arrayDelta(prev, curr) {
|
|
43
|
+
const added = [];
|
|
44
|
+
const removed = [];
|
|
45
|
+
// Items in curr but not in prev
|
|
46
|
+
for (const item of curr) {
|
|
47
|
+
if (!prev.some((p) => deepEqual(p, item))) {
|
|
48
|
+
added.push(item);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Items in prev but not in curr
|
|
52
|
+
for (const item of prev) {
|
|
53
|
+
if (!curr.some((c) => deepEqual(c, item))) {
|
|
54
|
+
removed.push(item);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const delta = {
|
|
58
|
+
unchanged: false,
|
|
59
|
+
added: added.length > 0 ? added : undefined,
|
|
60
|
+
removed: removed.length > 0 ? removed : undefined,
|
|
61
|
+
};
|
|
62
|
+
// Only prefer full when the delta is genuinely larger than the full result
|
|
63
|
+
if (shouldReturnFull(delta, curr)) {
|
|
64
|
+
return { unchanged: false, full: curr };
|
|
65
|
+
}
|
|
66
|
+
return delta;
|
|
67
|
+
}
|
|
68
|
+
// ── Object diff ───────────────────────────────────────────────────────────────
|
|
69
|
+
function objectDelta(prev, curr) {
|
|
70
|
+
const added = [];
|
|
71
|
+
const removed = [];
|
|
72
|
+
const modified = [];
|
|
73
|
+
const allKeys = new Set([...Object.keys(prev), ...Object.keys(curr)]);
|
|
74
|
+
for (const key of allKeys) {
|
|
75
|
+
const inPrev = key in prev;
|
|
76
|
+
const inCurr = key in curr;
|
|
77
|
+
if (!inPrev && inCurr) {
|
|
78
|
+
added.push({ [key]: curr[key] });
|
|
79
|
+
}
|
|
80
|
+
else if (inPrev && !inCurr) {
|
|
81
|
+
removed.push({ [key]: prev[key] });
|
|
82
|
+
}
|
|
83
|
+
else if (!deepEqual(prev[key], curr[key])) {
|
|
84
|
+
modified.push({ before: { [key]: prev[key] }, after: { [key]: curr[key] } });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const delta = {
|
|
88
|
+
unchanged: false,
|
|
89
|
+
added: added.length > 0 ? added : undefined,
|
|
90
|
+
removed: removed.length > 0 ? removed : undefined,
|
|
91
|
+
modified: modified.length > 0 ? modified : undefined,
|
|
92
|
+
};
|
|
93
|
+
if (shouldReturnFull(delta, curr)) {
|
|
94
|
+
return { unchanged: false, full: curr };
|
|
95
|
+
}
|
|
96
|
+
return delta;
|
|
97
|
+
}
|
|
98
|
+
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
99
|
+
function isPlainObject(v) {
|
|
100
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Deep equality via JSON serialisation. Fast enough for typical MCP result
|
|
104
|
+
* sizes; avoids pulling in a heavy comparison library.
|
|
105
|
+
*/
|
|
106
|
+
export function deepEqual(a, b) {
|
|
107
|
+
if (a === b)
|
|
108
|
+
return true;
|
|
109
|
+
try {
|
|
110
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Returns true when the full current value is strictly smaller (in serialised
|
|
118
|
+
* bytes) than the delta payload. We use a strict greater-than comparison so
|
|
119
|
+
* that delta is preferred when sizes are equal.
|
|
120
|
+
*/
|
|
121
|
+
function shouldReturnFull(delta, current) {
|
|
122
|
+
try {
|
|
123
|
+
const deltaSize = Buffer.byteLength(JSON.stringify(delta), 'utf8');
|
|
124
|
+
const fullSize = Buffer.byteLength(JSON.stringify(current), 'utf8');
|
|
125
|
+
return deltaSize > fullSize;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return true; // If we can't measure, default to full
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=delta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.js","sourceRoot":"","sources":["../../src/cache/delta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent disk cache tier using CBOR encoding (cbor-x).
|
|
3
|
+
*
|
|
4
|
+
* Layout on disk:
|
|
5
|
+
* <diskDir>/<sha256-prefix-2>/<full-argsHash>.cbor
|
|
6
|
+
*
|
|
7
|
+
* TTL enforcement is the responsibility of the caller (CacheLayer). DiskCache
|
|
8
|
+
* returns entries regardless of TTL so that stale-while-revalidate can be
|
|
9
|
+
* handled at the CacheLayer level.
|
|
10
|
+
*
|
|
11
|
+
* Parallel writes are safe: each key maps to a unique path; writes are
|
|
12
|
+
* atomic via a temp-file rename pattern.
|
|
13
|
+
*
|
|
14
|
+
* @module cache/disk
|
|
15
|
+
*/
|
|
16
|
+
import type { CacheHit } from './index.js';
|
|
17
|
+
export interface DiskEntry {
|
|
18
|
+
value: unknown;
|
|
19
|
+
storedAt: number;
|
|
20
|
+
ttlMs: number;
|
|
21
|
+
server: string;
|
|
22
|
+
tool: string;
|
|
23
|
+
}
|
|
24
|
+
/** Extended CacheHit that includes the stored TTL so CacheLayer can apply its own expiry policy. */
|
|
25
|
+
export interface DiskCacheHit extends CacheHit {
|
|
26
|
+
ttlMs: number;
|
|
27
|
+
}
|
|
28
|
+
export interface DiskCacheOptions {
|
|
29
|
+
diskDir: string;
|
|
30
|
+
maxDiskBytes?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class DiskCache {
|
|
33
|
+
private diskDir;
|
|
34
|
+
private maxDiskBytes;
|
|
35
|
+
private hits;
|
|
36
|
+
private misses;
|
|
37
|
+
private bytesOnDisk;
|
|
38
|
+
private ready;
|
|
39
|
+
constructor(options: DiskCacheOptions);
|
|
40
|
+
private ensureReady;
|
|
41
|
+
private entryPath;
|
|
42
|
+
/**
|
|
43
|
+
* Read an entry from disk.
|
|
44
|
+
* Returns null only on genuine miss or read errors — TTL is NOT enforced here.
|
|
45
|
+
* CacheLayer is responsible for staleness decisions (enabling SWR to work).
|
|
46
|
+
*/
|
|
47
|
+
get(argsHash: string): Promise<DiskCacheHit | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Write an entry to disk atomically (write to .tmp then rename).
|
|
50
|
+
* Triggers rotation when over maxDiskBytes.
|
|
51
|
+
*/
|
|
52
|
+
set(argsHash: string, entry: DiskEntry): Promise<void>;
|
|
53
|
+
delete(argsHash: string): Promise<boolean>;
|
|
54
|
+
invalidateByPrefix(serverToolPrefix: string): Promise<number>;
|
|
55
|
+
invalidateServer(server: string): Promise<number>;
|
|
56
|
+
clear(): Promise<void>;
|
|
57
|
+
get approximateBytesOnDisk(): number;
|
|
58
|
+
getCounters(): {
|
|
59
|
+
hits: number;
|
|
60
|
+
misses: number;
|
|
61
|
+
};
|
|
62
|
+
private scanAndDelete;
|
|
63
|
+
private rotate;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=disk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../src/cache/disk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oGAAoG;AACpG,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAgB;gBAEjB,OAAO,EAAE,gBAAgB;YAMvB,WAAW;IAIzB,OAAO,CAAC,SAAS;IAKjB;;;;OAIG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAuBzD;;;OAGG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBtD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa1C,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB7D,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,IAAI,sBAAsB,IAAI,MAAM,CAA6B;IACjE,WAAW,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;YAEjC,aAAa;YA2Bb,MAAM;CAqCrB"}
|