@darkiceinteractive/mcp-conductor 1.1.0 → 3.0.0-beta.1
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 +65 -1
- package/dist/bridge/http-server.d.ts.map +1 -1
- package/dist/bridge/http-server.js +192 -7
- 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/bridge/session-registry.d.ts +64 -0
- package/dist/bridge/session-registry.d.ts.map +1 -0
- package/dist/bridge/session-registry.js +124 -0
- package/dist/bridge/session-registry.js.map +1 -0
- package/dist/cache/cache.d.ts +43 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +167 -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 +10 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +14 -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 +97 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +279 -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 +351 -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 +19 -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 +12 -0
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +148 -16
- 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 +6 -0
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +610 -45
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/server/passthrough-registrar.d.ts +73 -0
- package/dist/server/passthrough-registrar.d.ts.map +1 -0
- package/dist/server/passthrough-registrar.js +110 -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 +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +5 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/orphan-watch.d.ts +34 -0
- package/dist/utils/orphan-watch.d.ts.map +1 -0
- package/dist/utils/orphan-watch.js +54 -0
- package/dist/utils/orphan-watch.js.map +1 -0
- package/dist/utils/redact.d.ts +15 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +48 -0
- package/dist/utils/redact.js.map +1 -0
- 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,167 @@
|
|
|
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
|
+
constructor(options) {
|
|
34
|
+
this.options = {
|
|
35
|
+
diskDir: `${process.env['HOME'] ?? '/tmp'}/.mcp-conductor/cache`,
|
|
36
|
+
maxMemoryBytes: 100 * 1024 * 1024,
|
|
37
|
+
maxDiskBytes: 2 * 1024 * 1024 * 1024,
|
|
38
|
+
staleWhileRevalidate: true,
|
|
39
|
+
policies: {},
|
|
40
|
+
...options,
|
|
41
|
+
};
|
|
42
|
+
this.registry = options.registry;
|
|
43
|
+
this.lru = new MemoryLru({ maxMemoryBytes: this.options.maxMemoryBytes });
|
|
44
|
+
this.disk = new DiskCache({
|
|
45
|
+
diskDir: this.options.diskDir,
|
|
46
|
+
maxDiskBytes: this.options.maxDiskBytes,
|
|
47
|
+
});
|
|
48
|
+
const sub = this.registry.watch((event) => {
|
|
49
|
+
if (event.type === 'tool-updated') {
|
|
50
|
+
const prefix = `${event.server}:${event.tool}:`;
|
|
51
|
+
this.lru.invalidateByPrefix(prefix);
|
|
52
|
+
this.disk.invalidateByPrefix(prefix).catch((err) => logger.warn('CacheLayer: disk invalidation error', { error: String(err) }));
|
|
53
|
+
logger.debug('CacheLayer: invalidated on tool-updated', {
|
|
54
|
+
server: event.server,
|
|
55
|
+
tool: event.tool,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
this.unsubscribe = sub.unsubscribe;
|
|
60
|
+
}
|
|
61
|
+
async get(server, tool, args) {
|
|
62
|
+
const cacheKey = buildCacheKey(server, tool, args);
|
|
63
|
+
const keyStr = cacheKeyToString(cacheKey);
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
// 1. Memory LRU — MemoryLru.get() enforces TTL via manual storedAt check
|
|
66
|
+
const memHit = this.lru.get(keyStr);
|
|
67
|
+
if (memHit) {
|
|
68
|
+
const ttlMs = this.ttlMap.get(keyStr) ?? 0;
|
|
69
|
+
const isStale = ttlMs > 0 && now - memHit.storedAt > ttlMs;
|
|
70
|
+
if (!isStale) {
|
|
71
|
+
return { ...memHit, staleness: now - memHit.storedAt };
|
|
72
|
+
}
|
|
73
|
+
if (this.options.staleWhileRevalidate) {
|
|
74
|
+
return { ...memHit, staleness: now - memHit.storedAt, needsRevalidation: true };
|
|
75
|
+
}
|
|
76
|
+
this.lru.delete(keyStr);
|
|
77
|
+
// fall through to disk
|
|
78
|
+
}
|
|
79
|
+
// 2. Disk — DiskCache.get() does NOT enforce TTL; we decide staleness here
|
|
80
|
+
const diskHit = await this.disk.get(cacheKey.argsHash);
|
|
81
|
+
if (diskHit) {
|
|
82
|
+
const entryTtl = diskHit.ttlMs > 0 ? diskHit.ttlMs : (this.ttlMap.get(keyStr) ?? 0);
|
|
83
|
+
const staleness = now - diskHit.storedAt;
|
|
84
|
+
const isStale = entryTtl > 0 && staleness > entryTtl;
|
|
85
|
+
if (!isStale) {
|
|
86
|
+
const remaining = entryTtl > 0 ? Math.max(1, entryTtl - staleness) : 0;
|
|
87
|
+
this.lru.set(keyStr, diskHit.value, remaining);
|
|
88
|
+
this.ttlMap.set(keyStr, entryTtl);
|
|
89
|
+
return { value: diskHit.value, storedAt: diskHit.storedAt, source: 'disk', staleness };
|
|
90
|
+
}
|
|
91
|
+
if (this.options.staleWhileRevalidate) {
|
|
92
|
+
const remaining = Math.max(1, entryTtl);
|
|
93
|
+
this.lru.set(keyStr, diskHit.value, remaining);
|
|
94
|
+
this.ttlMap.set(keyStr, entryTtl);
|
|
95
|
+
return {
|
|
96
|
+
value: diskHit.value,
|
|
97
|
+
storedAt: diskHit.storedAt,
|
|
98
|
+
source: 'disk',
|
|
99
|
+
staleness,
|
|
100
|
+
needsRevalidation: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
await this.disk.delete(cacheKey.argsHash);
|
|
104
|
+
}
|
|
105
|
+
this.diskMisses++;
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
async set(server, tool, args, result, options) {
|
|
109
|
+
const toolDef = this.registry.getTool(server, tool);
|
|
110
|
+
const ttlMs = options?.ttl ?? resolveTtl(toolDef, this.options.policies);
|
|
111
|
+
if (ttlMs === 0)
|
|
112
|
+
return;
|
|
113
|
+
const cacheKey = buildCacheKey(server, tool, args);
|
|
114
|
+
const keyStr = cacheKeyToString(cacheKey);
|
|
115
|
+
this.lru.set(keyStr, result, ttlMs);
|
|
116
|
+
this.ttlMap.set(keyStr, ttlMs);
|
|
117
|
+
await this.disk.set(cacheKey.argsHash, {
|
|
118
|
+
value: result,
|
|
119
|
+
storedAt: Date.now(),
|
|
120
|
+
ttlMs,
|
|
121
|
+
server,
|
|
122
|
+
tool,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async invalidate(server, pattern) {
|
|
126
|
+
const prefix = pattern ? `${server}:${pattern}` : `${server}:`;
|
|
127
|
+
const memCount = this.lru.invalidateByPrefix(prefix);
|
|
128
|
+
const diskCount = await this.disk.invalidateByPrefix(prefix);
|
|
129
|
+
return memCount + diskCount;
|
|
130
|
+
}
|
|
131
|
+
async delta(server, tool, args, current) {
|
|
132
|
+
const hit = await this.get(server, tool, args);
|
|
133
|
+
if (!hit)
|
|
134
|
+
return { unchanged: false, full: current };
|
|
135
|
+
return computeDelta(hit.value, current);
|
|
136
|
+
}
|
|
137
|
+
stats() {
|
|
138
|
+
const lruCounters = this.lru.getCounters();
|
|
139
|
+
const diskCounters = this.disk.getCounters();
|
|
140
|
+
return {
|
|
141
|
+
memoryHits: lruCounters.hits,
|
|
142
|
+
diskHits: diskCounters.hits,
|
|
143
|
+
misses: this.diskMisses,
|
|
144
|
+
evictions: lruCounters.evictions,
|
|
145
|
+
bytesInMemory: this.lru.bytesUsed,
|
|
146
|
+
bytesOnDisk: this.disk.approximateBytesOnDisk,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async clear() {
|
|
150
|
+
this.lru.clear();
|
|
151
|
+
this.lru.resetCounters();
|
|
152
|
+
this.ttlMap.clear();
|
|
153
|
+
this.diskMisses = 0;
|
|
154
|
+
await this.disk.clear();
|
|
155
|
+
}
|
|
156
|
+
wouldCache(server, tool) {
|
|
157
|
+
const toolDef = this.registry.getTool(server, tool);
|
|
158
|
+
return isCacheable(toolDef, this.options.policies);
|
|
159
|
+
}
|
|
160
|
+
destroy() {
|
|
161
|
+
if (this.unsubscribe) {
|
|
162
|
+
this.unsubscribe();
|
|
163
|
+
this.unsubscribe = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAS3C,MAAM,OAAO,UAAU;IACb,GAAG,CAAY;IACf,IAAI,CAAY;IAChB,QAAQ,CAAe;IACvB,OAAO,CAA8B;IACrC,WAAW,GAAwB,IAAI,CAAC;IACxC,UAAU,GAAG,CAAC,CAAC;IACf,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,YAAY,OAA0B;QACpC,IAAI,CAAC,OAAO,GAAG;YACb,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,uBAAuB;YAChE,cAAc,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI;YACjC,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;YACpC,oBAAoB,EAAE,IAAI;YAC1B,QAAQ,EAAE,EAAE;YACZ,GAAG,OAAO;SACX,CAAC;QACF,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,IAAI,SAAS,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,IAAI,SAAS,CAAC;YACxB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;YAC7B,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;SACxC,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAoB,EAAE,EAAE;YACvD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAClC,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC;gBAChD,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACjD,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAC3E,CAAC;gBACF,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE;oBACtD,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,IAAY,EAAE,IAAa;QACnD,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;YAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YACzD,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;gBACtC,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,MAAM,CAAC,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;YAClF,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACxB,uBAAuB;QACzB,CAAC;QAED,2EAA2E;QAC3E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACpF,MAAM,SAAS,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;YACzC,MAAM,OAAO,GAAG,QAAQ,GAAG,CAAC,IAAI,SAAS,GAAG,QAAQ,CAAC;YAErD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAClC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YACzF,CAAC;YAED,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;gBACtC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAClC,OAAO;oBACL,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,MAAM,EAAE,MAAM;oBACd,SAAS;oBACT,iBAAiB,EAAE,IAAI;iBACxB,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,GAAG,CACP,MAAc,EACd,IAAY,EACZ,IAAa,EACb,MAAe,EACf,OAA0B;QAE1B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,EAAE,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzE,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO;QAExB,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC/B,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE;YACrC,KAAK,EAAE,MAAM;YACb,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;YACpB,KAAK;YACL,MAAM;YACN,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,OAAgB;QAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC;QAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC7D,OAAO,QAAQ,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAAc,EAAE,IAAY,EAAE,IAAa,EAAE,OAAgB;QACvE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACrD,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC7C,OAAO;YACL,UAAU,EAAE,WAAW,CAAC,IAAI;YAC5B,QAAQ,EAAE,YAAY,CAAC,IAAI;YAC3B,MAAM,EAAE,IAAI,CAAC,UAAU;YACvB,SAAS,EAAE,WAAW,CAAC,SAAS;YAChC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS;YACjC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,sBAAsB;SAC9C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED,UAAU,CAAC,MAAc,EAAE,IAAY;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpD,OAAO,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,238 @@
|
|
|
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 { readFile, writeFile, mkdir, readdir, stat, unlink, rename } from 'node:fs/promises';
|
|
17
|
+
import { join, dirname } from 'node:path';
|
|
18
|
+
import { encode as cborEncode, decode as cborDecode } from 'cbor-x';
|
|
19
|
+
import { logger } from '../utils/index.js';
|
|
20
|
+
const ROTATION_TARGET_RATIO = 0.8;
|
|
21
|
+
export class DiskCache {
|
|
22
|
+
diskDir;
|
|
23
|
+
maxDiskBytes;
|
|
24
|
+
hits = 0;
|
|
25
|
+
misses = 0;
|
|
26
|
+
bytesOnDisk = 0;
|
|
27
|
+
ready;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.diskDir = options.diskDir;
|
|
30
|
+
this.maxDiskBytes = options.maxDiskBytes ?? 2 * 1024 * 1024 * 1024;
|
|
31
|
+
this.ready = mkdir(this.diskDir, { recursive: true }).then(() => undefined);
|
|
32
|
+
}
|
|
33
|
+
async ensureReady() {
|
|
34
|
+
await this.ready;
|
|
35
|
+
}
|
|
36
|
+
entryPath(argsHash) {
|
|
37
|
+
const prefix = argsHash.substring(0, 2);
|
|
38
|
+
return join(this.diskDir, prefix, `${argsHash}.cbor`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Read an entry from disk.
|
|
42
|
+
* Returns null only on genuine miss or read errors — TTL is NOT enforced here.
|
|
43
|
+
* CacheLayer is responsible for staleness decisions (enabling SWR to work).
|
|
44
|
+
*/
|
|
45
|
+
async get(argsHash) {
|
|
46
|
+
await this.ensureReady();
|
|
47
|
+
const path = this.entryPath(argsHash);
|
|
48
|
+
try {
|
|
49
|
+
const buf = await readFile(path);
|
|
50
|
+
const entry = cborDecode(buf);
|
|
51
|
+
this.hits++;
|
|
52
|
+
return {
|
|
53
|
+
value: entry.value,
|
|
54
|
+
storedAt: entry.storedAt,
|
|
55
|
+
source: 'disk',
|
|
56
|
+
staleness: Date.now() - entry.storedAt,
|
|
57
|
+
ttlMs: entry.ttlMs,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err.code !== 'ENOENT') {
|
|
62
|
+
logger.debug('DiskCache: read error', { path, error: String(err) });
|
|
63
|
+
}
|
|
64
|
+
this.misses++;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Write an entry to disk atomically (write to .tmp then rename).
|
|
70
|
+
* Triggers rotation when over maxDiskBytes.
|
|
71
|
+
*/
|
|
72
|
+
async set(argsHash, entry) {
|
|
73
|
+
await this.ensureReady();
|
|
74
|
+
const path = this.entryPath(argsHash);
|
|
75
|
+
const tmpPath = `${path}.tmp.${process.pid}`;
|
|
76
|
+
await mkdir(dirname(path), { recursive: true });
|
|
77
|
+
const buf = cborEncode(entry);
|
|
78
|
+
await writeFile(tmpPath, buf);
|
|
79
|
+
await rename(tmpPath, path);
|
|
80
|
+
this.bytesOnDisk += buf.byteLength;
|
|
81
|
+
if (this.bytesOnDisk > this.maxDiskBytes) {
|
|
82
|
+
this.rotate().catch((err) => logger.warn('DiskCache: rotation error', { error: String(err) }));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async delete(argsHash) {
|
|
86
|
+
await this.ensureReady();
|
|
87
|
+
const path = this.entryPath(argsHash);
|
|
88
|
+
try {
|
|
89
|
+
const s = await stat(path);
|
|
90
|
+
await unlink(path);
|
|
91
|
+
this.bytesOnDisk = Math.max(0, this.bytesOnDisk - s.size);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async invalidateByPrefix(serverToolPrefix) {
|
|
99
|
+
await this.ensureReady();
|
|
100
|
+
return this.scanAndDelete(async (entry, filePath) => {
|
|
101
|
+
const entryKey = `${entry.server}:${entry.tool}`;
|
|
102
|
+
const normalizedPrefix = serverToolPrefix.endsWith(':')
|
|
103
|
+
? serverToolPrefix.slice(0, -1)
|
|
104
|
+
: serverToolPrefix;
|
|
105
|
+
// Match: entryKey starts with prefix (handles server-only and server:tool prefixes)
|
|
106
|
+
if (entryKey.startsWith(normalizedPrefix)) {
|
|
107
|
+
try {
|
|
108
|
+
const s = await stat(filePath);
|
|
109
|
+
await unlink(filePath);
|
|
110
|
+
this.bytesOnDisk = Math.max(0, this.bytesOnDisk - s.size);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async invalidateServer(server) {
|
|
121
|
+
await this.ensureReady();
|
|
122
|
+
return this.scanAndDelete(async (entry, filePath) => {
|
|
123
|
+
if (entry.server === server) {
|
|
124
|
+
try {
|
|
125
|
+
const s = await stat(filePath);
|
|
126
|
+
await unlink(filePath);
|
|
127
|
+
this.bytesOnDisk = Math.max(0, this.bytesOnDisk - s.size);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async clear() {
|
|
138
|
+
await this.ensureReady();
|
|
139
|
+
await this.scanAndDelete(async (_entry, filePath) => {
|
|
140
|
+
try {
|
|
141
|
+
await unlink(filePath);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
this.bytesOnDisk = 0;
|
|
149
|
+
}
|
|
150
|
+
get approximateBytesOnDisk() { return this.bytesOnDisk; }
|
|
151
|
+
getCounters() { return { hits: this.hits, misses: this.misses }; }
|
|
152
|
+
async scanAndDelete(predicate) {
|
|
153
|
+
let count = 0;
|
|
154
|
+
let prefixDirs;
|
|
155
|
+
try {
|
|
156
|
+
prefixDirs = await readdir(this.diskDir);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
await Promise.all(prefixDirs.map(async (prefix) => {
|
|
162
|
+
const prefixPath = join(this.diskDir, prefix);
|
|
163
|
+
let files;
|
|
164
|
+
try {
|
|
165
|
+
const s = await stat(prefixPath);
|
|
166
|
+
if (!s.isDirectory())
|
|
167
|
+
return;
|
|
168
|
+
files = await readdir(prefixPath);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
await Promise.all(files.filter((f) => f.endsWith('.cbor')).map(async (f) => {
|
|
174
|
+
const filePath = join(prefixPath, f);
|
|
175
|
+
try {
|
|
176
|
+
const buf = await readFile(filePath);
|
|
177
|
+
const entry = cborDecode(buf);
|
|
178
|
+
const deleted = await predicate(entry, filePath);
|
|
179
|
+
if (deleted)
|
|
180
|
+
count++;
|
|
181
|
+
}
|
|
182
|
+
catch { /* corrupt — skip */ }
|
|
183
|
+
}));
|
|
184
|
+
}));
|
|
185
|
+
return count;
|
|
186
|
+
}
|
|
187
|
+
async rotate() {
|
|
188
|
+
const target = Math.floor(this.maxDiskBytes * ROTATION_TARGET_RATIO);
|
|
189
|
+
if (this.bytesOnDisk <= target)
|
|
190
|
+
return;
|
|
191
|
+
const entries = [];
|
|
192
|
+
let prefixDirs;
|
|
193
|
+
try {
|
|
194
|
+
prefixDirs = await readdir(this.diskDir);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
await Promise.all(prefixDirs.map(async (prefix) => {
|
|
200
|
+
const prefixPath = join(this.diskDir, prefix);
|
|
201
|
+
let files;
|
|
202
|
+
try {
|
|
203
|
+
const s = await stat(prefixPath);
|
|
204
|
+
if (!s.isDirectory())
|
|
205
|
+
return;
|
|
206
|
+
files = await readdir(prefixPath);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
await Promise.all(files.filter((f) => f.endsWith('.cbor')).map(async (f) => {
|
|
212
|
+
const filePath = join(prefixPath, f);
|
|
213
|
+
try {
|
|
214
|
+
const s = await stat(filePath);
|
|
215
|
+
const buf = await readFile(filePath);
|
|
216
|
+
const entry = cborDecode(buf);
|
|
217
|
+
entries.push({ filePath, storedAt: entry.storedAt, size: s.size });
|
|
218
|
+
}
|
|
219
|
+
catch { /* skip */ }
|
|
220
|
+
}));
|
|
221
|
+
}));
|
|
222
|
+
entries.sort((a, b) => a.storedAt - b.storedAt);
|
|
223
|
+
let freed = 0;
|
|
224
|
+
const toFree = this.bytesOnDisk - target;
|
|
225
|
+
for (const e of entries) {
|
|
226
|
+
if (freed >= toFree)
|
|
227
|
+
break;
|
|
228
|
+
try {
|
|
229
|
+
await unlink(e.filePath);
|
|
230
|
+
freed += e.size;
|
|
231
|
+
this.bytesOnDisk = Math.max(0, this.bytesOnDisk - e.size);
|
|
232
|
+
}
|
|
233
|
+
catch { /* concurrent delete — ok */ }
|
|
234
|
+
}
|
|
235
|
+
logger.debug('DiskCache: rotated', { freedBytes: freed });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=disk.js.map
|