@intutic/mcp-governance-proxy 1.10.0 → 2.0.0
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 +28 -0
- package/dist/anomaly/detectors.d.ts +129 -0
- package/dist/anomaly/detectors.d.ts.map +1 -0
- package/dist/anomaly/detectors.js +368 -0
- package/dist/anomaly/detectors.js.map +1 -0
- package/dist/anomaly/index.d.ts +52 -0
- package/dist/anomaly/index.d.ts.map +1 -0
- package/dist/anomaly/index.js +71 -0
- package/dist/anomaly/index.js.map +1 -0
- package/dist/config.d.ts +107 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +129 -6
- package/dist/config.js.map +1 -1
- package/dist/daemon/healthMonitor.d.ts.map +1 -1
- package/dist/daemon/healthMonitor.js +45 -46
- package/dist/daemon/healthMonitor.js.map +1 -1
- package/dist/daemon/index.d.ts +15 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +78 -9
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/instrumentation.d.ts +20 -0
- package/dist/daemon/instrumentation.d.ts.map +1 -0
- package/dist/daemon/instrumentation.js +61 -0
- package/dist/daemon/instrumentation.js.map +1 -0
- package/dist/daemon/policyCache.d.ts +83 -0
- package/dist/daemon/policyCache.d.ts.map +1 -1
- package/dist/daemon/policyCache.js +280 -11
- package/dist/daemon/policyCache.js.map +1 -1
- package/dist/daemon/statusReporter.d.ts +12 -0
- package/dist/daemon/statusReporter.d.ts.map +1 -0
- package/dist/daemon/statusReporter.js +130 -0
- package/dist/daemon/statusReporter.js.map +1 -0
- package/dist/daemon/telemetryBatcher.d.ts +3 -1
- package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
- package/dist/daemon/telemetryBatcher.js +56 -13
- package/dist/daemon/telemetryBatcher.js.map +1 -1
- package/dist/daemonClient.d.ts +1 -1
- package/dist/daemonClient.d.ts.map +1 -1
- package/dist/daemonClient.js +0 -2
- package/dist/daemonClient.js.map +1 -1
- package/dist/dlp.d.ts +19 -0
- package/dist/dlp.d.ts.map +1 -1
- package/dist/dlp.js +95 -18
- package/dist/dlp.js.map +1 -1
- package/dist/emitter.d.ts +40 -2
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +27 -5
- package/dist/emitter.js.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/injection.d.ts +46 -0
- package/dist/injection.d.ts.map +1 -0
- package/dist/injection.js +147 -0
- package/dist/injection.js.map +1 -0
- package/dist/interceptor.d.ts +131 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +301 -5
- package/dist/interceptor.js.map +1 -1
- package/dist/policy.d.ts +110 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +206 -5
- package/dist/policy.js.map +1 -1
- package/dist/proxy.d.ts +205 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +572 -65
- package/dist/proxy.js.map +1 -1
- package/dist/remoteBridge.d.ts +51 -0
- package/dist/remoteBridge.d.ts.map +1 -0
- package/dist/remoteBridge.js +182 -0
- package/dist/remoteBridge.js.map +1 -0
- package/dist/session.d.ts +158 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +206 -0
- package/dist/session.js.map +1 -0
- package/dist/sessionScope.d.ts +51 -0
- package/dist/sessionScope.d.ts.map +1 -0
- package/dist/sessionScope.js +94 -0
- package/dist/sessionScope.js.map +1 -0
- package/dist/sessionStore.d.ts +69 -0
- package/dist/sessionStore.d.ts.map +1 -0
- package/dist/sessionStore.js +143 -0
- package/dist/sessionStore.js.map +1 -0
- package/dist/tofu.d.ts +112 -0
- package/dist/tofu.d.ts.map +1 -0
- package/dist/tofu.js +180 -0
- package/dist/tofu.js.map +1 -0
- package/dist/toolPoison.d.ts +32 -0
- package/dist/toolPoison.d.ts.map +1 -0
- package/dist/toolPoison.js +81 -0
- package/dist/toolPoison.js.map +1 -0
- package/dist/valkeyErrors.d.ts +17 -0
- package/dist/valkeyErrors.d.ts.map +1 -0
- package/dist/valkeyErrors.js +24 -0
- package/dist/valkeyErrors.js.map +1 -0
- package/dist/wasm/context.d.ts +57 -0
- package/dist/wasm/context.d.ts.map +1 -0
- package/dist/wasm/context.js +59 -0
- package/dist/wasm/context.js.map +1 -0
- package/dist/wasm/hostImports.d.ts +43 -0
- package/dist/wasm/hostImports.d.ts.map +1 -0
- package/dist/wasm/hostImports.js +158 -0
- package/dist/wasm/hostImports.js.map +1 -0
- package/dist/wasm/loader.d.ts +95 -0
- package/dist/wasm/loader.d.ts.map +1 -0
- package/dist/wasm/loader.js +198 -0
- package/dist/wasm/loader.js.map +1 -0
- package/dist/wasm/referencedFiles.d.ts +115 -0
- package/dist/wasm/referencedFiles.d.ts.map +1 -0
- package/dist/wasm/referencedFiles.js +366 -0
- package/dist/wasm/referencedFiles.js.map +1 -0
- package/dist/wasm/runner.d.ts +91 -0
- package/dist/wasm/runner.d.ts.map +1 -0
- package/dist/wasm/runner.js +308 -0
- package/dist/wasm/runner.js.map +1 -0
- package/dist/wasm/watch.d.ts +5 -0
- package/dist/wasm/watch.d.ts.map +1 -0
- package/dist/wasm/watch.js +54 -0
- package/dist/wasm/watch.js.map +1 -0
- package/dist/wasm/worker.d.ts +39 -0
- package/dist/wasm/worker.d.ts.map +1 -0
- package/dist/wasm/worker.js +245 -0
- package/dist/wasm/worker.js.map +1 -0
- package/package.json +14 -4
package/dist/daemon/index.js
CHANGED
|
@@ -12,12 +12,19 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @module
|
|
14
14
|
*/
|
|
15
|
+
// Must be first: OTel instrumentation needs to monkey-patch before other
|
|
16
|
+
// modules import the libraries it instruments. No-op unless
|
|
17
|
+
// OTEL_EXPORTER_OTLP_ENDPOINT is set. Deliberately not imported by
|
|
18
|
+
// src/index.ts (the stdio JSON-RPC proxy) -- see instrumentation.ts's
|
|
19
|
+
// own doc comment for why.
|
|
20
|
+
import './instrumentation.js';
|
|
15
21
|
import fs from 'node:fs';
|
|
16
22
|
import path from 'node:path';
|
|
17
23
|
import os from 'node:os';
|
|
18
24
|
import { createLogger } from '@intutic/logger';
|
|
19
25
|
import { startBatcher, stopBatcher } from './telemetryBatcher.js';
|
|
20
26
|
import { startHealthMonitor, stopHealthMonitor } from './healthMonitor.js';
|
|
27
|
+
import { startStatusReporter, stopStatusReporter } from './statusReporter.js';
|
|
21
28
|
import { createSocketServer, getSocketPath } from './socketServer.js';
|
|
22
29
|
const logger = createLogger('intutic-mcp-daemon');
|
|
23
30
|
const PID_FILE = path.join(os.homedir(), '.intutic', 'mcp-daemon.pid');
|
|
@@ -27,25 +34,58 @@ function ensureDir(dir) {
|
|
|
27
34
|
async function main() {
|
|
28
35
|
const socketPath = getSocketPath();
|
|
29
36
|
logger.info({ pid: process.pid, socketPath }, 'mcp_daemon.starting');
|
|
30
|
-
// Ensure
|
|
37
|
+
// Ensure the socket's own directory exists with correct permissions. This is
|
|
38
|
+
// ~/.intutic only by default: MCP_DAEMON_SOCKET (honoured by getSocketPath())
|
|
39
|
+
// can put the socket anywhere, so this call says nothing about where the PID
|
|
40
|
+
// file lives. The PID file's directory is ensured separately below.
|
|
31
41
|
ensureDir(path.dirname(socketPath));
|
|
32
42
|
// Remove stale socket file
|
|
33
43
|
try {
|
|
34
44
|
fs.unlinkSync(socketPath);
|
|
35
45
|
}
|
|
36
|
-
catch {
|
|
37
|
-
|
|
46
|
+
catch {
|
|
47
|
+
// ENOENT — no socket left by a previous run. That is the normal clean
|
|
48
|
+
// start, so there is nothing to report. A removal failure for any other
|
|
49
|
+
// reason is not actually swallowed: listen() below then fails with
|
|
50
|
+
// EADDRINUSE and the server 'error' handler exits the process.
|
|
51
|
+
}
|
|
52
|
+
// Write PID file. PID_FILE always lives under ~/.intutic, which is a
|
|
53
|
+
// different directory from the socket's whenever MCP_DAEMON_SOCKET is set —
|
|
54
|
+
// so it needs its own ensureDir, or this write is an ENOENT out of main().
|
|
55
|
+
ensureDir(path.dirname(PID_FILE));
|
|
38
56
|
fs.writeFileSync(PID_FILE, String(process.pid), { mode: 0o600 });
|
|
57
|
+
// Seed the policy cache from the local snapshot before accepting connections.
|
|
58
|
+
//
|
|
59
|
+
// Without it the first tool call after every daemon restart pays a blocking
|
|
60
|
+
// HTTP GET with a 5s socket timeout, on the agent's critical path. The sync
|
|
61
|
+
// daemon has already written the same policy locally.
|
|
62
|
+
//
|
|
63
|
+
// Awaited rather than fired-and-forgotten so the seed is in place before the
|
|
64
|
+
// socket accepts anything — a race here would just reintroduce the cold fetch
|
|
65
|
+
// it exists to remove. It never throws and never blocks on the network; a
|
|
66
|
+
// missing snapshot returns null and the cold path stands.
|
|
67
|
+
const { seedFromSnapshot } = await import('./policyCache.js');
|
|
68
|
+
const seeded = await seedFromSnapshot();
|
|
69
|
+
if (seeded)
|
|
70
|
+
logger.info({ workspaceId: seeded }, 'mcp_daemon.policy_seeded');
|
|
39
71
|
// Start subsystems
|
|
40
72
|
startBatcher();
|
|
41
73
|
startHealthMonitor();
|
|
74
|
+
startStatusReporter();
|
|
42
75
|
const server = createSocketServer();
|
|
43
76
|
server.listen(socketPath, () => {
|
|
44
77
|
// Restrict socket to owner only
|
|
45
78
|
try {
|
|
46
79
|
fs.chmodSync(socketPath, 0o600);
|
|
47
80
|
}
|
|
48
|
-
catch {
|
|
81
|
+
catch (err) {
|
|
82
|
+
// Platforms without unix-socket file permissions (Windows named pipes)
|
|
83
|
+
// fail here, so this must not be fatal. It is still worth surfacing:
|
|
84
|
+
// ensureDir() only applies mode 0o700 when it creates ~/.intutic, so on
|
|
85
|
+
// a pre-existing looser directory this chmod is the only thing keeping
|
|
86
|
+
// the control socket owner-only.
|
|
87
|
+
logger.warn({ err, socketPath }, 'mcp_daemon.socket_chmod_failed');
|
|
88
|
+
}
|
|
49
89
|
logger.info({ socketPath, pid: process.pid }, 'mcp_daemon.running');
|
|
50
90
|
process.stdout.write(JSON.stringify({ event: 'mcp_daemon.started', pid: process.pid }) + '\n');
|
|
51
91
|
});
|
|
@@ -58,20 +98,49 @@ async function main() {
|
|
|
58
98
|
logger.info({ signal }, 'mcp_daemon.stopping');
|
|
59
99
|
server.close();
|
|
60
100
|
stopHealthMonitor();
|
|
101
|
+
await stopStatusReporter();
|
|
61
102
|
await stopBatcher();
|
|
103
|
+
// Best-effort cleanup: both files are routinely already gone (ENOENT) when
|
|
104
|
+
// an operator or a service manager removed them, or when listen() never
|
|
105
|
+
// got far enough to create the socket. The process is exiting either way,
|
|
106
|
+
// and a leftover file is handled by the stale-socket unlink on next start.
|
|
62
107
|
try {
|
|
63
108
|
fs.unlinkSync(socketPath);
|
|
64
109
|
}
|
|
65
|
-
catch {
|
|
110
|
+
catch {
|
|
111
|
+
// socket already removed or never created — nothing to clean up
|
|
112
|
+
}
|
|
66
113
|
try {
|
|
67
114
|
fs.unlinkSync(PID_FILE);
|
|
68
115
|
}
|
|
69
|
-
catch {
|
|
116
|
+
catch {
|
|
117
|
+
// pid file already removed or never written — nothing to clean up
|
|
118
|
+
}
|
|
70
119
|
logger.info('mcp_daemon.stopped');
|
|
71
120
|
process.exit(0);
|
|
72
121
|
};
|
|
73
|
-
|
|
74
|
-
|
|
122
|
+
// Same reasoning as the main().catch below: shutdown() ends in process.exit,
|
|
123
|
+
// so a rejection on the way there leaves the daemon alive and unresponsive
|
|
124
|
+
// until the service manager escalates to SIGKILL. Exit non-zero instead.
|
|
125
|
+
const onSignal = (signal) => {
|
|
126
|
+
void shutdown(signal).catch((err) => {
|
|
127
|
+
logger.error({ err, signal }, 'mcp_daemon.shutdown_failed');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
process.on('SIGTERM', () => { onSignal('SIGTERM'); });
|
|
132
|
+
process.on('SIGINT', () => { onSignal('SIGINT'); });
|
|
75
133
|
}
|
|
76
|
-
|
|
134
|
+
// main() is the whole of startup, and everything it does before listen() —
|
|
135
|
+
// ensureDir, the PID write, subsystem start — can throw. Without a handler
|
|
136
|
+
// those were an unhandled rejection, which under Node's default mode prints a
|
|
137
|
+
// raw V8 stack to stderr and exits 1: the exit status is fine, but nothing goes
|
|
138
|
+
// through the structured logger, so the failure carries no mcp_daemon.* event
|
|
139
|
+
// for a log pipeline to alert on and no socketPath to point at the cause. Under
|
|
140
|
+
// --unhandled-rejections=warn it is worse — the process stays up as a daemon
|
|
141
|
+
// that will never listen. Both become one logged event and a definite exit.
|
|
142
|
+
main().catch((err) => {
|
|
143
|
+
logger.error({ err }, 'mcp_daemon.startup_failed');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
});
|
|
77
146
|
//# sourceMappingURL=index.js.map
|
package/dist/daemon/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,MAAQ,SAAS,CAAA;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAQ,SAAS,CAAA;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAErE,MAAM,MAAM,GAAG,YAAY,CAAC,oBAAoB,CAAC,CAAA;AACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAA;AAEtE,SAAS,SAAS,CAAC,GAAW;IAC5B,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;AACrD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAClC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,qBAAqB,CAAC,CAAA;IAEpE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry Code-Level Instrumentation — mcp-daemon only.
|
|
3
|
+
*
|
|
4
|
+
* Same pattern as gateway-daemon/src/instrumentation.ts and
|
|
5
|
+
* services/control-plane/src/instrumentation.ts. Deliberately NOT imported
|
|
6
|
+
* by src/index.ts (the `intutic-mcp-proxy` stdio JSON-RPC proxy) -- that
|
|
7
|
+
* process reserves stdout exclusively for protocol frames, and
|
|
8
|
+
* auto-instrumentations-node's console/process patching is not a risk worth
|
|
9
|
+
* taking on that channel. This file only instruments `intutic-mcp-daemon`
|
|
10
|
+
* (daemon/index.ts), the long-lived companion process that already owns its
|
|
11
|
+
* own telemetryBatcher.ts and talks to the control plane over a Unix socket,
|
|
12
|
+
* not stdio.
|
|
13
|
+
*
|
|
14
|
+
* Opt-in only -- off unless OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
15
|
+
*
|
|
16
|
+
* Must be imported at the very top of daemon/index.ts, before other
|
|
17
|
+
* modules, to allow monkey-patching.
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=instrumentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/daemon/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry Code-Level Instrumentation — mcp-daemon only.
|
|
3
|
+
*
|
|
4
|
+
* Same pattern as gateway-daemon/src/instrumentation.ts and
|
|
5
|
+
* services/control-plane/src/instrumentation.ts. Deliberately NOT imported
|
|
6
|
+
* by src/index.ts (the `intutic-mcp-proxy` stdio JSON-RPC proxy) -- that
|
|
7
|
+
* process reserves stdout exclusively for protocol frames, and
|
|
8
|
+
* auto-instrumentations-node's console/process patching is not a risk worth
|
|
9
|
+
* taking on that channel. This file only instruments `intutic-mcp-daemon`
|
|
10
|
+
* (daemon/index.ts), the long-lived companion process that already owns its
|
|
11
|
+
* own telemetryBatcher.ts and talks to the control plane over a Unix socket,
|
|
12
|
+
* not stdio.
|
|
13
|
+
*
|
|
14
|
+
* Opt-in only -- off unless OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
15
|
+
*
|
|
16
|
+
* Must be imported at the very top of daemon/index.ts, before other
|
|
17
|
+
* modules, to allow monkey-patching.
|
|
18
|
+
*/
|
|
19
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
20
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
21
|
+
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
|
|
22
|
+
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
|
|
23
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
24
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
25
|
+
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
26
|
+
import { createLogger } from '@intutic/logger';
|
|
27
|
+
const log = createLogger('instrumentation');
|
|
28
|
+
const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4318/v1/traces';
|
|
29
|
+
const serviceName = process.env.OTEL_SERVICE_NAME ?? 'mcp-daemon';
|
|
30
|
+
// The env var may name either the collector base or the full traces path;
|
|
31
|
+
// derive the base once so traces and metrics land on their own signal paths.
|
|
32
|
+
const base = endpoint.replace(/\/v1\/traces$/, '');
|
|
33
|
+
const traceExporter = new OTLPTraceExporter({
|
|
34
|
+
url: `${base}/v1/traces`,
|
|
35
|
+
});
|
|
36
|
+
const sdk = new NodeSDK({
|
|
37
|
+
resource: resourceFromAttributes({
|
|
38
|
+
[ATTR_SERVICE_NAME]: serviceName,
|
|
39
|
+
}),
|
|
40
|
+
traceExporter,
|
|
41
|
+
metricReader: new PeriodicExportingMetricReader({
|
|
42
|
+
exporter: new OTLPMetricExporter({ url: `${base}/v1/metrics` }),
|
|
43
|
+
}),
|
|
44
|
+
instrumentations: [
|
|
45
|
+
getNodeAutoInstrumentations({
|
|
46
|
+
'@opentelemetry/instrumentation-fs': {
|
|
47
|
+
enabled: false,
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
53
|
+
sdk.start();
|
|
54
|
+
log.info({ action: 'otel_init', serviceName, endpoint }, 'Tracing SDK initialized');
|
|
55
|
+
}
|
|
56
|
+
process.on('SIGTERM', () => {
|
|
57
|
+
sdk.shutdown()
|
|
58
|
+
.then(() => log.info({ action: 'otel_shutdown' }, 'Tracing SDK terminated gracefully'))
|
|
59
|
+
.catch((err) => log.error({ err, action: 'otel_shutdown_failed' }, 'Error terminating Tracing SDK'));
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/daemon/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAA;AAC9E,OAAO,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAA;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAA;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,GAAG,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAA;AAE3C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,iCAAiC,CAAA;AAC7F,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,YAAY,CAAA;AAEjE,0EAA0E;AAC1E,6EAA6E;AAC7E,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAA;AAElD,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC;IAC1C,GAAG,EAAE,GAAG,IAAI,YAAY;CACzB,CAAC,CAAA;AAEF,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC;IACtB,QAAQ,EAAE,sBAAsB,CAAC;QAC/B,CAAC,iBAAiB,CAAC,EAAE,WAAW;KACjC,CAAC;IACF,aAAa;IACb,YAAY,EAAE,IAAI,6BAA6B,CAAC;QAC9C,QAAQ,EAAE,IAAI,kBAAkB,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,aAAa,EAAE,CAAC;KAChE,CAAC;IACF,gBAAgB,EAAE;QAChB,2BAA2B,CAAC;YAC1B,mCAAmC,EAAE;gBACnC,OAAO,EAAE,KAAK;aACf;SACF,CAAC;KACH;CACF,CAAC,CAAA;AAEF,IAAI,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,CAAC;IAC5C,GAAG,CAAC,KAAK,EAAE,CAAA;IACX,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,yBAAyB,CAAC,CAAA;AACrF,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACzB,GAAG,CAAC,QAAQ,EAAE;SACX,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,mCAAmC,CAAC,CAAC;SACtF,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,sBAAsB,EAAE,EAAE,+BAA+B,CAAC,CAAC,CAAA;AACjH,CAAC,CAAC,CAAA"}
|
|
@@ -3,8 +3,91 @@ export interface ResolvedPolicy {
|
|
|
3
3
|
sopRules: Record<string, unknown>[];
|
|
4
4
|
dlpPatterns: string[];
|
|
5
5
|
interventionMode: string;
|
|
6
|
+
/**
|
|
7
|
+
* Additive MCP tool allowlist; empty means unrestricted. Was silently
|
|
8
|
+
* absent from `GET /api/v1/policy/resolve` (the daemon-mode source of this
|
|
9
|
+
* type) until `lib/mcpCuration.ts` on the control plane started serving it
|
|
10
|
+
* from the same place `GET /api/v1/sop/rules` (non-daemon mode) always
|
|
11
|
+
* has. Always normalized to `[]` rather than left `undefined` here — a
|
|
12
|
+
* stale Valkey/LRU entry or snapshot written before this field existed
|
|
13
|
+
* must not read as "curation absent" one layer up.
|
|
14
|
+
*/
|
|
15
|
+
allowedTools: string[];
|
|
16
|
+
/** Operator-curated tool descriptions, applied to tools/list responses. */
|
|
17
|
+
toolDescriptionOverrides: Record<string, string>;
|
|
18
|
+
/** Additive MCP server allowlist; empty means unrestricted. Same source
|
|
19
|
+
* and same backward-compat treatment as `allowedTools` above. */
|
|
20
|
+
allowedServers: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Prompt-injection disposition override (`packages/mcp-proxy/src/injection.ts`).
|
|
23
|
+
* `undefined` when the control plane has not sent this field yet — read the
|
|
24
|
+
* same way as every other optional curation field here: absent must mean
|
|
25
|
+
* "no override, use the package's own env-derived default"
|
|
26
|
+
* (`PolicyClient.getInjectionAction`, `../policy.ts`), never "warn" by
|
|
27
|
+
* silent default at this layer, so an old snapshot/cache entry from before
|
|
28
|
+
* this field existed reads correctly as absent rather than as an explicit
|
|
29
|
+
* value.
|
|
30
|
+
*/
|
|
31
|
+
mcpInjectionAction?: 'warn' | 'block';
|
|
32
|
+
/** Workspace injection-pattern sources (TD-436); normalized to `[]` like `allowedTools`. */
|
|
33
|
+
mcpInjectionPatterns: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Anomaly-detection mode override (Phase 2, `packages/mcp-proxy/src/anomaly/`).
|
|
36
|
+
* `undefined` means absent — same convention as `mcpInjectionAction` above.
|
|
37
|
+
*/
|
|
38
|
+
mcpAnomalyMode?: 'enforce' | 'warn' | 'off';
|
|
39
|
+
/**
|
|
40
|
+
* Per-detector disposition overrides. Always an object, never `undefined`
|
|
41
|
+
* — an absent field normalizes to `{}` (no overrides) the same way
|
|
42
|
+
* `toolDescriptionOverrides` above does, since "absent" and "explicitly
|
|
43
|
+
* empty" mean the same thing for an override map.
|
|
44
|
+
*/
|
|
45
|
+
mcpAnomalyOverrides: Record<string, 'steer' | 'reask' | 'kill' | 'off'>;
|
|
6
46
|
cachedAt: number;
|
|
47
|
+
/**
|
|
48
|
+
* The workspace's `v2:sync:config_version` at fetch time (TD-474 item 5).
|
|
49
|
+
* A guardrail promote/retire bumps it; a fresh LRU hit whose version no
|
|
50
|
+
* longer matches refetches instead of waiting out the TTL. Absent when
|
|
51
|
+
* Valkey could not be read — then the TTL is the floor, as before.
|
|
52
|
+
*/
|
|
53
|
+
configVersion?: number;
|
|
7
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Seeds the LRU from the policy snapshot the sync daemon writes locally.
|
|
57
|
+
*
|
|
58
|
+
* # What this removes
|
|
59
|
+
*
|
|
60
|
+
* `resolvePolicy`'s cold path is a blocking HTTP GET with a 5s socket timeout,
|
|
61
|
+
* taken on the daemon's first request for a workspace after every restart. The
|
|
62
|
+
* sync daemon already writes the same policy to
|
|
63
|
+
* `~/.intutic/hooks/policy-snapshot.json` every cycle, so on the machine's own
|
|
64
|
+
* workspace that round trip is avoidable.
|
|
65
|
+
*
|
|
66
|
+
* # Two things that make the obvious version wrong
|
|
67
|
+
*
|
|
68
|
+
* **It reads `sopRules`, not `rules`.** The snapshot carries both. `rules` is the
|
|
69
|
+
* *gate* projection — patterns rewritten as space-padded EREs, `{source,
|
|
70
|
+
* severity, subject}`, `warn` rules already dropped. Those pass this module's
|
|
71
|
+
* `parsePolicyResponse` (it only checks that entries are objects) and are then
|
|
72
|
+
* rejected wholesale by `isSopRule` in `../policy.ts`, which requires
|
|
73
|
+
* `toolPattern` and `action`. The result would be a cache that reports entries,
|
|
74
|
+
* logs nothing above `debug`, and enforces **nothing** — strictly worse than the
|
|
75
|
+
* blocking fetch, and invisible.
|
|
76
|
+
*
|
|
77
|
+
* **`cachedAt` comes from the snapshot, not from now.** Stamping `Date.now()` on
|
|
78
|
+
* a file written days ago (the sync daemon may be stopped — the snapshot writer
|
|
79
|
+
* anticipates exactly that) would present stale policy as fresh and suppress the
|
|
80
|
+
* refresh for a full TTL. Carrying the real write time means a stale snapshot
|
|
81
|
+
* seeds the cache *and* is immediately recognised as stale, so the first request
|
|
82
|
+
* returns instantly and triggers a background refresh — which is the actual goal.
|
|
83
|
+
*
|
|
84
|
+
* Never throws. This runs inside the daemon's `main()`, whose rejections become
|
|
85
|
+
* `process.exit(1)`, and the daemon is a KeepAlive LaunchAgent — so a throw here
|
|
86
|
+
* would be a restart loop rather than an error.
|
|
87
|
+
*
|
|
88
|
+
* @returns the workspaceId seeded, or null if nothing usable was found.
|
|
89
|
+
*/
|
|
90
|
+
export declare function seedFromSnapshot(snapshotPath?: string): Promise<string | null>;
|
|
8
91
|
/**
|
|
9
92
|
* Resolves policy for a workspace.
|
|
10
93
|
* Order: LRU (fresh) → LRU (stale, triggers async refresh) → Valkey → HTTP fetch
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policyCache.d.ts","sourceRoot":"","sources":["../../src/daemon/policyCache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policyCache.d.ts","sourceRoot":"","sources":["../../src/daemon/policyCache.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAI,MAAM,CAAA;IACrB,QAAQ,EAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACxC,WAAW,EAAI,MAAM,EAAE,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,2EAA2E;IAC3E,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChD;sEACkE;IAClE,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACrC,4FAA4F;IAC5F,oBAAoB,EAAE,MAAM,EAAE,CAAA;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,KAAK,CAAA;IAC3C;;;;;OAKG;IACH,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC,CAAA;IACvE,QAAQ,EAAO,MAAM,CAAA;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAqMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgEpF;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmGvF;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED,gCAAgC;AAChC,wBAAgB,aAAa,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAEpE"}
|