@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,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hot-Path Profiler
|
|
3
|
+
*
|
|
4
|
+
* Wraps every backend call in a latency tracker and aggregates per (server, tool).
|
|
5
|
+
* Uses a rolling window so old samples expire automatically. Surfaces top-K
|
|
6
|
+
* paths by total time or by p99 latency.
|
|
7
|
+
*/
|
|
8
|
+
import { logger } from '../utils/index.js';
|
|
9
|
+
export const DEFAULT_HOT_PATH_CONFIG = {
|
|
10
|
+
enabled: true,
|
|
11
|
+
windowMs: 3_600_000,
|
|
12
|
+
maxSamplesPerBucket: 1_000,
|
|
13
|
+
};
|
|
14
|
+
function p99(sorted) {
|
|
15
|
+
if (sorted.length === 0)
|
|
16
|
+
return 0;
|
|
17
|
+
const idx = Math.ceil(sorted.length * 0.99) - 1;
|
|
18
|
+
return sorted[Math.max(0, idx)] ?? 0;
|
|
19
|
+
}
|
|
20
|
+
export class HotPathProfiler {
|
|
21
|
+
config;
|
|
22
|
+
buckets = new Map();
|
|
23
|
+
constructor(config = {}) {
|
|
24
|
+
this.config = { ...DEFAULT_HOT_PATH_CONFIG, ...config };
|
|
25
|
+
}
|
|
26
|
+
bucketKey(server, tool) {
|
|
27
|
+
return `${server}::${tool}`;
|
|
28
|
+
}
|
|
29
|
+
/** Record a completed call with its measured latency. */
|
|
30
|
+
record(server, tool, latencyMs) {
|
|
31
|
+
if (!this.config.enabled)
|
|
32
|
+
return;
|
|
33
|
+
const key = this.bucketKey(server, tool);
|
|
34
|
+
let bucket = this.buckets.get(key);
|
|
35
|
+
if (!bucket) {
|
|
36
|
+
bucket = { server, tool, samples: [] };
|
|
37
|
+
this.buckets.set(key, bucket);
|
|
38
|
+
}
|
|
39
|
+
bucket.samples.push({ latencyMs, timestamp: Date.now() });
|
|
40
|
+
// Trim to max window size
|
|
41
|
+
if (bucket.samples.length > this.config.maxSamplesPerBucket) {
|
|
42
|
+
bucket.samples.splice(0, bucket.samples.length - this.config.maxSamplesPerBucket);
|
|
43
|
+
}
|
|
44
|
+
logger.debug('HotPathProfiler: recorded', { server, tool, latencyMs });
|
|
45
|
+
}
|
|
46
|
+
/** Evict samples older than windowMs from all buckets. */
|
|
47
|
+
evictExpired() {
|
|
48
|
+
const cutoff = Date.now() - this.config.windowMs;
|
|
49
|
+
for (const [key, bucket] of this.buckets.entries()) {
|
|
50
|
+
bucket.samples = bucket.samples.filter((s) => s.timestamp >= cutoff);
|
|
51
|
+
if (bucket.samples.length === 0) {
|
|
52
|
+
this.buckets.delete(key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
computeEntry(bucket, liveSamples) {
|
|
57
|
+
const latencies = liveSamples.map((s) => s.latencyMs).sort((a, b) => a - b);
|
|
58
|
+
const total = latencies.reduce((s, v) => s + v, 0);
|
|
59
|
+
const n = latencies.length;
|
|
60
|
+
return {
|
|
61
|
+
server: bucket.server,
|
|
62
|
+
tool: bucket.tool,
|
|
63
|
+
callCount: n,
|
|
64
|
+
totalLatencyMs: total,
|
|
65
|
+
meanLatencyMs: n > 0 ? Math.round(total / n) : 0,
|
|
66
|
+
p99LatencyMs: p99(latencies),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Return top-K paths. sortBy controls ranking:
|
|
71
|
+
* 'totalLatency' — highest cumulative cost first (default)
|
|
72
|
+
* 'p99' — highest tail latency first
|
|
73
|
+
* 'callCount' — most called first
|
|
74
|
+
*/
|
|
75
|
+
getHotPaths(options = {}) {
|
|
76
|
+
this.evictExpired();
|
|
77
|
+
const { sinceMs, topK = 10, sortBy = 'totalLatency' } = options;
|
|
78
|
+
const cutoff = sinceMs !== undefined ? Date.now() - sinceMs : 0;
|
|
79
|
+
const entries = [];
|
|
80
|
+
for (const bucket of this.buckets.values()) {
|
|
81
|
+
const live = sinceMs !== undefined
|
|
82
|
+
? bucket.samples.filter((s) => s.timestamp >= cutoff)
|
|
83
|
+
: bucket.samples;
|
|
84
|
+
if (live.length === 0)
|
|
85
|
+
continue;
|
|
86
|
+
entries.push(this.computeEntry(bucket, live));
|
|
87
|
+
}
|
|
88
|
+
// Deterministic sort: primary key, then server+tool as tiebreaker
|
|
89
|
+
entries.sort((a, b) => {
|
|
90
|
+
let diff = 0;
|
|
91
|
+
switch (sortBy) {
|
|
92
|
+
case 'p99':
|
|
93
|
+
diff = b.p99LatencyMs - a.p99LatencyMs;
|
|
94
|
+
break;
|
|
95
|
+
case 'callCount':
|
|
96
|
+
diff = b.callCount - a.callCount;
|
|
97
|
+
break;
|
|
98
|
+
default: diff = b.totalLatencyMs - a.totalLatencyMs;
|
|
99
|
+
}
|
|
100
|
+
if (diff !== 0)
|
|
101
|
+
return diff;
|
|
102
|
+
// Tiebreak by server then tool for stable ordering
|
|
103
|
+
const sk = a.server.localeCompare(b.server);
|
|
104
|
+
if (sk !== 0)
|
|
105
|
+
return sk;
|
|
106
|
+
return a.tool.localeCompare(b.tool);
|
|
107
|
+
});
|
|
108
|
+
return entries.slice(0, topK);
|
|
109
|
+
}
|
|
110
|
+
reset() {
|
|
111
|
+
this.buckets.clear();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Module-level singleton
|
|
115
|
+
let _instance = null;
|
|
116
|
+
export function getHotPathProfiler(config) {
|
|
117
|
+
if (!_instance) {
|
|
118
|
+
_instance = new HotPathProfiler(config);
|
|
119
|
+
}
|
|
120
|
+
return _instance;
|
|
121
|
+
}
|
|
122
|
+
export function shutdownHotPathProfiler() {
|
|
123
|
+
_instance = null;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=hot-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hot-path.js","sourceRoot":"","sources":["../../src/observability/hot-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability module barrel
|
|
3
|
+
*
|
|
4
|
+
* Cost prediction, hot-path profiling, anomaly detection, and replay.
|
|
5
|
+
*/
|
|
6
|
+
export * from './cost-predictor.js';
|
|
7
|
+
export * from './hot-path.js';
|
|
8
|
+
export * from './anomaly.js';
|
|
9
|
+
export * from './replay.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability module barrel
|
|
3
|
+
*
|
|
4
|
+
* Cost prediction, hot-path profiling, anomaly detection, and replay.
|
|
5
|
+
*/
|
|
6
|
+
export * from './cost-predictor.js';
|
|
7
|
+
export * from './hot-path.js';
|
|
8
|
+
export * from './anomaly.js';
|
|
9
|
+
export * from './replay.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay Recorder
|
|
3
|
+
*
|
|
4
|
+
* Records execute_code sessions to JSONL journals under
|
|
5
|
+
* ~/.mcp-conductor/recordings/. Supports deterministic replay with optional
|
|
6
|
+
* per-event modifications (replace or skip). Detects divergence when the
|
|
7
|
+
* replayed result differs from the recorded result.
|
|
8
|
+
*
|
|
9
|
+
* Journal line format (one JSON object per line):
|
|
10
|
+
* { "seq": <number>, "ts": <epoch-ms>, "type": "tool_call"|"tool_result"|"code_result",
|
|
11
|
+
* "server": <string>, "tool": <string>, "args": <object>, "result": <unknown> }
|
|
12
|
+
*
|
|
13
|
+
* Rotation: when total bytes across all recordings exceed maxTotalBytes
|
|
14
|
+
* (default 1 GB), the oldest file is removed.
|
|
15
|
+
*/
|
|
16
|
+
export interface ReplayConfig {
|
|
17
|
+
/** Directory for recording files */
|
|
18
|
+
recordingsDir: string;
|
|
19
|
+
/** Maximum total bytes across all recordings before rotation (default 1 GB) */
|
|
20
|
+
maxTotalBytes: number;
|
|
21
|
+
}
|
|
22
|
+
export declare const DEFAULT_REPLAY_CONFIG: ReplayConfig;
|
|
23
|
+
export type JournalEventType = 'tool_call' | 'tool_result' | 'code_result';
|
|
24
|
+
export interface JournalEvent {
|
|
25
|
+
seq: number;
|
|
26
|
+
ts: number;
|
|
27
|
+
type: JournalEventType;
|
|
28
|
+
server?: string;
|
|
29
|
+
tool?: string;
|
|
30
|
+
args?: Record<string, unknown>;
|
|
31
|
+
result?: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface ReplayModification {
|
|
34
|
+
/** Zero-based sequence index to target */
|
|
35
|
+
at: number;
|
|
36
|
+
op: 'replace' | 'skip';
|
|
37
|
+
/** Replacement result value (only used when op === 'replace') */
|
|
38
|
+
with?: unknown;
|
|
39
|
+
}
|
|
40
|
+
export declare class ReplayRecorder {
|
|
41
|
+
private config;
|
|
42
|
+
private activeSessions;
|
|
43
|
+
constructor(config?: Partial<ReplayConfig>);
|
|
44
|
+
private ensureDir;
|
|
45
|
+
private rotate;
|
|
46
|
+
private appendEvent;
|
|
47
|
+
/** Start a new recording session. Returns the sessionId and recording path. */
|
|
48
|
+
startRecording(sessionId?: string): {
|
|
49
|
+
sessionId: string;
|
|
50
|
+
recordingPath: string;
|
|
51
|
+
};
|
|
52
|
+
/** Stop an active recording session. Returns the path and event count. */
|
|
53
|
+
stopRecording(sessionId: string): {
|
|
54
|
+
recordingPath: string;
|
|
55
|
+
eventCount: number;
|
|
56
|
+
};
|
|
57
|
+
/** Record a tool call event into an active session. */
|
|
58
|
+
recordToolCall(sessionId: string, server: string, tool: string, args: Record<string, unknown>): void;
|
|
59
|
+
/** Record a tool result event into an active session. */
|
|
60
|
+
recordToolResult(sessionId: string, server: string, tool: string, result: unknown): void;
|
|
61
|
+
/** Record the final code execution result. */
|
|
62
|
+
recordCodeResult(sessionId: string, result: unknown): void;
|
|
63
|
+
/** Return whether a session is currently active. */
|
|
64
|
+
isRecording(sessionId: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Load and parse a recording file. Returns the ordered events.
|
|
67
|
+
*/
|
|
68
|
+
loadRecording(recordingPath: string): JournalEvent[];
|
|
69
|
+
/**
|
|
70
|
+
* Replay a recorded session.
|
|
71
|
+
*
|
|
72
|
+
* For each tool_call/tool_result pair in the journal the recorded result is
|
|
73
|
+
* returned unless a modification targets that sequence number:
|
|
74
|
+
* - op:'skip' → the tool call is omitted from the replayed sequence
|
|
75
|
+
* - op:'replace' → the recorded result is replaced with modification.with
|
|
76
|
+
*
|
|
77
|
+
* The final code_result is compared against the replayed sequence to detect
|
|
78
|
+
* divergence.
|
|
79
|
+
*
|
|
80
|
+
* This implementation operates fully in-memory against the journal — it does
|
|
81
|
+
* NOT re-execute Deno code (that would require a live executor). It reconstructs
|
|
82
|
+
* the recorded call sequence and applies modifications, returning what the
|
|
83
|
+
* sandbox would have seen.
|
|
84
|
+
*/
|
|
85
|
+
replay(recordingPath: string, modifications?: ReplayModification[]): {
|
|
86
|
+
result: unknown;
|
|
87
|
+
events: JournalEvent[];
|
|
88
|
+
divergence?: {
|
|
89
|
+
at: number;
|
|
90
|
+
expected: unknown;
|
|
91
|
+
actual: unknown;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/** List all recording files sorted by modification time (newest first). */
|
|
95
|
+
listRecordings(): Array<{
|
|
96
|
+
sessionId: string;
|
|
97
|
+
path: string;
|
|
98
|
+
sizeBytes: number;
|
|
99
|
+
createdAt: number;
|
|
100
|
+
}>;
|
|
101
|
+
}
|
|
102
|
+
export declare function getReplayRecorder(config?: Partial<ReplayConfig>): ReplayRecorder;
|
|
103
|
+
export declare function shutdownReplayRecorder(): void;
|
|
104
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/observability/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiBH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,qBAAqB,EAAE,YAGnC,CAAC;AAMF,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAE3E,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAMD,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,SAAS,GAAG,MAAM,CAAC;IACvB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA4CD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,cAAc,CAAyC;gBAEnD,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM;IAI9C,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,WAAW;IAcnB,+EAA+E;IAC/E,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAuBhF,0EAA0E;IAC1E,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAa/E,uDAAuD;IACvD,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI;IAMP,yDAAyD;IACzD,gBAAgB,CACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,GACd,IAAI;IAMP,8CAA8C;IAC9C,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAM1D,oDAAoD;IACpD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAQvC;;OAEG;IACH,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,YAAY,EAAE;IAUpD;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CACJ,aAAa,EAAE,MAAM,EACrB,aAAa,GAAE,kBAAkB,EAAO,GACvC;QACD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,YAAY,EAAE,CAAC;QACvB,UAAU,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAC;YAAC,MAAM,EAAE,OAAO,CAAA;SAAE,CAAC;KACjE;IA4CD,2EAA2E;IAC3E,cAAc,IAAI,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAgBnG;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,cAAc,CAKhF;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay Recorder
|
|
3
|
+
*
|
|
4
|
+
* Records execute_code sessions to JSONL journals under
|
|
5
|
+
* ~/.mcp-conductor/recordings/. Supports deterministic replay with optional
|
|
6
|
+
* per-event modifications (replace or skip). Detects divergence when the
|
|
7
|
+
* replayed result differs from the recorded result.
|
|
8
|
+
*
|
|
9
|
+
* Journal line format (one JSON object per line):
|
|
10
|
+
* { "seq": <number>, "ts": <epoch-ms>, "type": "tool_call"|"tool_result"|"code_result",
|
|
11
|
+
* "server": <string>, "tool": <string>, "args": <object>, "result": <unknown> }
|
|
12
|
+
*
|
|
13
|
+
* Rotation: when total bytes across all recordings exceed maxTotalBytes
|
|
14
|
+
* (default 1 GB), the oldest file is removed.
|
|
15
|
+
*/
|
|
16
|
+
import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync, unlinkSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { join, basename } from 'node:path';
|
|
19
|
+
import { randomUUID } from 'node:crypto';
|
|
20
|
+
import { logger } from '../utils/index.js';
|
|
21
|
+
export const DEFAULT_REPLAY_CONFIG = {
|
|
22
|
+
recordingsDir: join(homedir(), '.mcp-conductor', 'recordings'),
|
|
23
|
+
maxTotalBytes: 1_073_741_824, // 1 GB
|
|
24
|
+
};
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Helper: directory total bytes
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
function dirTotalBytes(dir) {
|
|
29
|
+
if (!existsSync(dir))
|
|
30
|
+
return 0;
|
|
31
|
+
return readdirSync(dir)
|
|
32
|
+
.filter((f) => f.endsWith('.jsonl'))
|
|
33
|
+
.reduce((total, f) => {
|
|
34
|
+
try {
|
|
35
|
+
return total + statSync(join(dir, f)).size;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return total;
|
|
39
|
+
}
|
|
40
|
+
}, 0);
|
|
41
|
+
}
|
|
42
|
+
function oldestFile(dir) {
|
|
43
|
+
if (!existsSync(dir))
|
|
44
|
+
return null;
|
|
45
|
+
const files = readdirSync(dir)
|
|
46
|
+
.filter((f) => f.endsWith('.jsonl'))
|
|
47
|
+
.map((f) => ({ name: f, mtime: statSync(join(dir, f)).mtimeMs }))
|
|
48
|
+
.sort((a, b) => a.mtime - b.mtime);
|
|
49
|
+
const first = files[0];
|
|
50
|
+
return first ? join(dir, first.name) : null;
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// ReplayRecorder
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
export class ReplayRecorder {
|
|
56
|
+
config;
|
|
57
|
+
activeSessions = new Map();
|
|
58
|
+
constructor(config = {}) {
|
|
59
|
+
this.config = { ...DEFAULT_REPLAY_CONFIG, ...config };
|
|
60
|
+
}
|
|
61
|
+
ensureDir() {
|
|
62
|
+
if (!existsSync(this.config.recordingsDir)) {
|
|
63
|
+
mkdirSync(this.config.recordingsDir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
rotate() {
|
|
67
|
+
while (dirTotalBytes(this.config.recordingsDir) > this.config.maxTotalBytes) {
|
|
68
|
+
const oldest = oldestFile(this.config.recordingsDir);
|
|
69
|
+
if (!oldest)
|
|
70
|
+
break;
|
|
71
|
+
logger.info('ReplayRecorder: rotating old recording', { file: basename(oldest) });
|
|
72
|
+
unlinkSync(oldest);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
appendEvent(session, event) {
|
|
76
|
+
const fullEvent = {
|
|
77
|
+
seq: session.seq++,
|
|
78
|
+
ts: Date.now(),
|
|
79
|
+
...event,
|
|
80
|
+
};
|
|
81
|
+
appendFileSync(session.recordingPath, JSON.stringify(fullEvent) + '\n', 'utf8');
|
|
82
|
+
session.eventCount++;
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Public API
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/** Start a new recording session. Returns the sessionId and recording path. */
|
|
88
|
+
startRecording(sessionId) {
|
|
89
|
+
this.ensureDir();
|
|
90
|
+
this.rotate();
|
|
91
|
+
const id = sessionId ?? randomUUID();
|
|
92
|
+
const filename = `${id}.jsonl`;
|
|
93
|
+
const recordingPath = join(this.config.recordingsDir, filename);
|
|
94
|
+
// Create an empty file to claim the name
|
|
95
|
+
writeFileSync(recordingPath, '', 'utf8');
|
|
96
|
+
const session = {
|
|
97
|
+
sessionId: id,
|
|
98
|
+
recordingPath,
|
|
99
|
+
seq: 0,
|
|
100
|
+
eventCount: 0,
|
|
101
|
+
};
|
|
102
|
+
this.activeSessions.set(id, session);
|
|
103
|
+
logger.info('ReplayRecorder: started recording', { sessionId: id, recordingPath });
|
|
104
|
+
return { sessionId: id, recordingPath };
|
|
105
|
+
}
|
|
106
|
+
/** Stop an active recording session. Returns the path and event count. */
|
|
107
|
+
stopRecording(sessionId) {
|
|
108
|
+
const session = this.activeSessions.get(sessionId);
|
|
109
|
+
if (!session) {
|
|
110
|
+
throw new Error(`No active recording session: ${sessionId}`);
|
|
111
|
+
}
|
|
112
|
+
this.activeSessions.delete(sessionId);
|
|
113
|
+
logger.info('ReplayRecorder: stopped recording', {
|
|
114
|
+
sessionId,
|
|
115
|
+
eventCount: session.eventCount,
|
|
116
|
+
});
|
|
117
|
+
return { recordingPath: session.recordingPath, eventCount: session.eventCount };
|
|
118
|
+
}
|
|
119
|
+
/** Record a tool call event into an active session. */
|
|
120
|
+
recordToolCall(sessionId, server, tool, args) {
|
|
121
|
+
const session = this.activeSessions.get(sessionId);
|
|
122
|
+
if (!session)
|
|
123
|
+
return;
|
|
124
|
+
this.appendEvent(session, { type: 'tool_call', server, tool, args });
|
|
125
|
+
}
|
|
126
|
+
/** Record a tool result event into an active session. */
|
|
127
|
+
recordToolResult(sessionId, server, tool, result) {
|
|
128
|
+
const session = this.activeSessions.get(sessionId);
|
|
129
|
+
if (!session)
|
|
130
|
+
return;
|
|
131
|
+
this.appendEvent(session, { type: 'tool_result', server, tool, result });
|
|
132
|
+
}
|
|
133
|
+
/** Record the final code execution result. */
|
|
134
|
+
recordCodeResult(sessionId, result) {
|
|
135
|
+
const session = this.activeSessions.get(sessionId);
|
|
136
|
+
if (!session)
|
|
137
|
+
return;
|
|
138
|
+
this.appendEvent(session, { type: 'code_result', result });
|
|
139
|
+
}
|
|
140
|
+
/** Return whether a session is currently active. */
|
|
141
|
+
isRecording(sessionId) {
|
|
142
|
+
return this.activeSessions.has(sessionId);
|
|
143
|
+
}
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Replay
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
/**
|
|
148
|
+
* Load and parse a recording file. Returns the ordered events.
|
|
149
|
+
*/
|
|
150
|
+
loadRecording(recordingPath) {
|
|
151
|
+
if (!existsSync(recordingPath)) {
|
|
152
|
+
throw new Error(`Recording not found: ${recordingPath}`);
|
|
153
|
+
}
|
|
154
|
+
const lines = readFileSync(recordingPath, 'utf8')
|
|
155
|
+
.split('\n')
|
|
156
|
+
.filter((l) => l.trim().length > 0);
|
|
157
|
+
return lines.map((l) => JSON.parse(l));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Replay a recorded session.
|
|
161
|
+
*
|
|
162
|
+
* For each tool_call/tool_result pair in the journal the recorded result is
|
|
163
|
+
* returned unless a modification targets that sequence number:
|
|
164
|
+
* - op:'skip' → the tool call is omitted from the replayed sequence
|
|
165
|
+
* - op:'replace' → the recorded result is replaced with modification.with
|
|
166
|
+
*
|
|
167
|
+
* The final code_result is compared against the replayed sequence to detect
|
|
168
|
+
* divergence.
|
|
169
|
+
*
|
|
170
|
+
* This implementation operates fully in-memory against the journal — it does
|
|
171
|
+
* NOT re-execute Deno code (that would require a live executor). It reconstructs
|
|
172
|
+
* the recorded call sequence and applies modifications, returning what the
|
|
173
|
+
* sandbox would have seen.
|
|
174
|
+
*/
|
|
175
|
+
replay(recordingPath, modifications = []) {
|
|
176
|
+
const events = this.loadRecording(recordingPath);
|
|
177
|
+
const modMap = new Map(modifications.map((m) => [m.at, m]));
|
|
178
|
+
const replayed = [];
|
|
179
|
+
let replayedResult = undefined;
|
|
180
|
+
let divergence;
|
|
181
|
+
let replaySeq = 0;
|
|
182
|
+
for (const event of events) {
|
|
183
|
+
const mod = modMap.get(event.seq);
|
|
184
|
+
if (mod?.op === 'skip') {
|
|
185
|
+
// Skip this event — do not include in replayed sequence
|
|
186
|
+
logger.debug('ReplayRecorder: skipping event', { seq: event.seq });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (event.type === 'code_result') {
|
|
190
|
+
// Compare recorded result with what we've accumulated
|
|
191
|
+
const expected = event.result;
|
|
192
|
+
// In no-modification replay the result is identical to recorded
|
|
193
|
+
const actual = mod?.op === 'replace' ? mod.with : event.result;
|
|
194
|
+
replayedResult = actual;
|
|
195
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
196
|
+
divergence = { at: event.seq, expected, actual };
|
|
197
|
+
}
|
|
198
|
+
replayed.push({ ...event, seq: replaySeq++, result: actual });
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (mod?.op === 'replace' && event.type === 'tool_result') {
|
|
202
|
+
replayed.push({ ...event, seq: replaySeq++, result: mod.with });
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
replayed.push({ ...event, seq: replaySeq++ });
|
|
206
|
+
}
|
|
207
|
+
return { result: replayedResult, events: replayed, divergence };
|
|
208
|
+
}
|
|
209
|
+
/** List all recording files sorted by modification time (newest first). */
|
|
210
|
+
listRecordings() {
|
|
211
|
+
if (!existsSync(this.config.recordingsDir))
|
|
212
|
+
return [];
|
|
213
|
+
return readdirSync(this.config.recordingsDir)
|
|
214
|
+
.filter((f) => f.endsWith('.jsonl'))
|
|
215
|
+
.map((f) => {
|
|
216
|
+
const p = join(this.config.recordingsDir, f);
|
|
217
|
+
const stat = statSync(p);
|
|
218
|
+
return {
|
|
219
|
+
sessionId: f.replace('.jsonl', ''),
|
|
220
|
+
path: p,
|
|
221
|
+
sizeBytes: stat.size,
|
|
222
|
+
createdAt: stat.mtimeMs,
|
|
223
|
+
};
|
|
224
|
+
})
|
|
225
|
+
.sort((a, b) => b.createdAt - a.createdAt);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Module-level singleton
|
|
229
|
+
let _instance = null;
|
|
230
|
+
export function getReplayRecorder(config) {
|
|
231
|
+
if (!_instance) {
|
|
232
|
+
_instance = new ReplayRecorder(config);
|
|
233
|
+
}
|
|
234
|
+
return _instance;
|
|
235
|
+
}
|
|
236
|
+
export function shutdownReplayRecorder() {
|
|
237
|
+
_instance = null;
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/observability/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in routing recommendations for well-known MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* These defaults apply when a server is discovered but has no user-provided
|
|
5
|
+
* routing annotation. User config always takes precedence — these are applied
|
|
6
|
+
* only when `routing` is absent from a tool's existing registry entry.
|
|
7
|
+
*
|
|
8
|
+
* Rationale for each entry:
|
|
9
|
+
* - github: read-only identity/listing tools are safe direct passthrough;
|
|
10
|
+
* mutation tools stay execute_code to preserve sandbox audit trail.
|
|
11
|
+
* - filesystem: read-only traversal is passthrough; writes/deletes stay in
|
|
12
|
+
* execute_code so the sandbox can enforce path policies.
|
|
13
|
+
* - brave-search: single query → result, no side effects, tiny response.
|
|
14
|
+
*
|
|
15
|
+
* @module registry/built-in-recommendations
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Per-server routing recommendations.
|
|
19
|
+
*
|
|
20
|
+
* Each entry maps a server name to:
|
|
21
|
+
* - `passthrough`: tools that should be exposed as first-class MCP tools.
|
|
22
|
+
*
|
|
23
|
+
* Every tool not listed in `passthrough` defaults to `execute_code`.
|
|
24
|
+
*/
|
|
25
|
+
export interface ServerRoutingRecommendation {
|
|
26
|
+
/** Tool names that should be routed as passthrough. */
|
|
27
|
+
passthrough: string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Default routing table for known public MCP servers.
|
|
31
|
+
*
|
|
32
|
+
* Apply with `applyBuiltInRecommendations()` after `registry.refresh()`.
|
|
33
|
+
* User config overrides (any existing `routing` annotation) are preserved.
|
|
34
|
+
*/
|
|
35
|
+
export declare const BUILT_IN_ROUTING: Record<string, ServerRoutingRecommendation>;
|
|
36
|
+
/**
|
|
37
|
+
* Apply built-in routing recommendations to registry-discovered tools.
|
|
38
|
+
*
|
|
39
|
+
* Only sets `routing` when none is already present — user annotations and
|
|
40
|
+
* config-file overrides are never overwritten.
|
|
41
|
+
*
|
|
42
|
+
* Returns the number of tools that were annotated.
|
|
43
|
+
*
|
|
44
|
+
* @param tools All tools returned by `registry.getAllTools()`.
|
|
45
|
+
* @param annotate The `registry.annotate()` method bound to the ToolRegistry.
|
|
46
|
+
*/
|
|
47
|
+
export declare function applyBuiltInRecommendations(tools: Array<{
|
|
48
|
+
server: string;
|
|
49
|
+
name: string;
|
|
50
|
+
routing?: string;
|
|
51
|
+
}>, annotate: (server: string, name: string, meta: {
|
|
52
|
+
routing: 'passthrough' | 'execute_code';
|
|
53
|
+
}) => void): number;
|
|
54
|
+
//# sourceMappingURL=built-in-recommendations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"built-in-recommendations.d.ts","sourceRoot":"","sources":["../../src/registry/built-in-recommendations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAUxE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAChE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,OAAO,EAAE,aAAa,GAAG,cAAc,CAAA;CAAE,KAAK,IAAI,GAClG,MAAM,CAwBR"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in routing recommendations for well-known MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* These defaults apply when a server is discovered but has no user-provided
|
|
5
|
+
* routing annotation. User config always takes precedence — these are applied
|
|
6
|
+
* only when `routing` is absent from a tool's existing registry entry.
|
|
7
|
+
*
|
|
8
|
+
* Rationale for each entry:
|
|
9
|
+
* - github: read-only identity/listing tools are safe direct passthrough;
|
|
10
|
+
* mutation tools stay execute_code to preserve sandbox audit trail.
|
|
11
|
+
* - filesystem: read-only traversal is passthrough; writes/deletes stay in
|
|
12
|
+
* execute_code so the sandbox can enforce path policies.
|
|
13
|
+
* - brave-search: single query → result, no side effects, tiny response.
|
|
14
|
+
*
|
|
15
|
+
* @module registry/built-in-recommendations
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Default routing table for known public MCP servers.
|
|
19
|
+
*
|
|
20
|
+
* Apply with `applyBuiltInRecommendations()` after `registry.refresh()`.
|
|
21
|
+
* User config overrides (any existing `routing` annotation) are preserved.
|
|
22
|
+
*/
|
|
23
|
+
export const BUILT_IN_ROUTING = {
|
|
24
|
+
github: {
|
|
25
|
+
passthrough: ['get_me', 'list_repositories'],
|
|
26
|
+
},
|
|
27
|
+
filesystem: {
|
|
28
|
+
passthrough: ['read_file', 'list_directory'],
|
|
29
|
+
},
|
|
30
|
+
'brave-search': {
|
|
31
|
+
passthrough: ['brave_web_search'],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Apply built-in routing recommendations to registry-discovered tools.
|
|
36
|
+
*
|
|
37
|
+
* Only sets `routing` when none is already present — user annotations and
|
|
38
|
+
* config-file overrides are never overwritten.
|
|
39
|
+
*
|
|
40
|
+
* Returns the number of tools that were annotated.
|
|
41
|
+
*
|
|
42
|
+
* @param tools All tools returned by `registry.getAllTools()`.
|
|
43
|
+
* @param annotate The `registry.annotate()` method bound to the ToolRegistry.
|
|
44
|
+
*/
|
|
45
|
+
export function applyBuiltInRecommendations(tools, annotate) {
|
|
46
|
+
let annotated = 0;
|
|
47
|
+
for (const tool of tools) {
|
|
48
|
+
// Never override a user-configured routing value.
|
|
49
|
+
if (tool.routing !== undefined) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const rec = BUILT_IN_ROUTING[tool.server];
|
|
53
|
+
if (!rec) {
|
|
54
|
+
// Unknown server — default remains execute_code (no annotation needed).
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const routing = rec.passthrough.includes(tool.name)
|
|
58
|
+
? 'passthrough'
|
|
59
|
+
: 'execute_code';
|
|
60
|
+
annotate(tool.server, tool.name, { routing });
|
|
61
|
+
annotated++;
|
|
62
|
+
}
|
|
63
|
+
return annotated;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=built-in-recommendations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"built-in-recommendations.js","sourceRoot":"","sources":["../../src/registry/built-in-recommendations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAeH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAgD;IAC3E,MAAM,EAAE;QACN,WAAW,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC;KAC7C;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,WAAW,EAAE,gBAAgB,CAAC;KAC7C;IACD,cAAc,EAAE;QACd,WAAW,EAAE,CAAC,kBAAkB,CAAC;KAClC;CACF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAAgE,EAChE,QAAmG;IAEnG,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,kDAAkD;QAClD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC/B,SAAS;QACX,CAAC;QAED,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,wEAAwE;YACxE,SAAS;QACX,CAAC;QAED,MAAM,OAAO,GAAmC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YACjF,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,cAAc,CAAC;QAEnB,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9C,SAAS,EAAE,CAAC;IACd,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|