@intutic/mcp-governance-proxy 1.9.0 → 1.10.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.
Files changed (123) hide show
  1. package/README.md +28 -0
  2. package/dist/anomaly/detectors.d.ts +129 -0
  3. package/dist/anomaly/detectors.d.ts.map +1 -0
  4. package/dist/anomaly/detectors.js +368 -0
  5. package/dist/anomaly/detectors.js.map +1 -0
  6. package/dist/anomaly/index.d.ts +52 -0
  7. package/dist/anomaly/index.d.ts.map +1 -0
  8. package/dist/anomaly/index.js +71 -0
  9. package/dist/anomaly/index.js.map +1 -0
  10. package/dist/config.d.ts +107 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +129 -6
  13. package/dist/config.js.map +1 -1
  14. package/dist/daemon/healthMonitor.d.ts.map +1 -1
  15. package/dist/daemon/healthMonitor.js +45 -46
  16. package/dist/daemon/healthMonitor.js.map +1 -1
  17. package/dist/daemon/index.d.ts +15 -1
  18. package/dist/daemon/index.d.ts.map +1 -1
  19. package/dist/daemon/index.js +78 -9
  20. package/dist/daemon/index.js.map +1 -1
  21. package/dist/daemon/instrumentation.d.ts +20 -0
  22. package/dist/daemon/instrumentation.d.ts.map +1 -0
  23. package/dist/daemon/instrumentation.js +61 -0
  24. package/dist/daemon/instrumentation.js.map +1 -0
  25. package/dist/daemon/policyCache.d.ts +83 -0
  26. package/dist/daemon/policyCache.d.ts.map +1 -1
  27. package/dist/daemon/policyCache.js +280 -11
  28. package/dist/daemon/policyCache.js.map +1 -1
  29. package/dist/daemon/statusReporter.d.ts +12 -0
  30. package/dist/daemon/statusReporter.d.ts.map +1 -0
  31. package/dist/daemon/statusReporter.js +130 -0
  32. package/dist/daemon/statusReporter.js.map +1 -0
  33. package/dist/daemon/telemetryBatcher.d.ts +3 -1
  34. package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
  35. package/dist/daemon/telemetryBatcher.js +56 -13
  36. package/dist/daemon/telemetryBatcher.js.map +1 -1
  37. package/dist/daemonClient.d.ts +1 -1
  38. package/dist/daemonClient.d.ts.map +1 -1
  39. package/dist/daemonClient.js +0 -2
  40. package/dist/daemonClient.js.map +1 -1
  41. package/dist/dlp.d.ts +19 -0
  42. package/dist/dlp.d.ts.map +1 -1
  43. package/dist/dlp.js +95 -18
  44. package/dist/dlp.js.map +1 -1
  45. package/dist/emitter.d.ts +40 -2
  46. package/dist/emitter.d.ts.map +1 -1
  47. package/dist/emitter.js +27 -5
  48. package/dist/emitter.js.map +1 -1
  49. package/dist/index.js +13 -2
  50. package/dist/index.js.map +1 -1
  51. package/dist/injection.d.ts +46 -0
  52. package/dist/injection.d.ts.map +1 -0
  53. package/dist/injection.js +147 -0
  54. package/dist/injection.js.map +1 -0
  55. package/dist/interceptor.d.ts +131 -1
  56. package/dist/interceptor.d.ts.map +1 -1
  57. package/dist/interceptor.js +301 -5
  58. package/dist/interceptor.js.map +1 -1
  59. package/dist/policy.d.ts +110 -2
  60. package/dist/policy.d.ts.map +1 -1
  61. package/dist/policy.js +206 -5
  62. package/dist/policy.js.map +1 -1
  63. package/dist/proxy.d.ts +205 -0
  64. package/dist/proxy.d.ts.map +1 -1
  65. package/dist/proxy.js +572 -65
  66. package/dist/proxy.js.map +1 -1
  67. package/dist/remoteBridge.d.ts +51 -0
  68. package/dist/remoteBridge.d.ts.map +1 -0
  69. package/dist/remoteBridge.js +182 -0
  70. package/dist/remoteBridge.js.map +1 -0
  71. package/dist/session.d.ts +158 -0
  72. package/dist/session.d.ts.map +1 -0
  73. package/dist/session.js +206 -0
  74. package/dist/session.js.map +1 -0
  75. package/dist/sessionScope.d.ts +51 -0
  76. package/dist/sessionScope.d.ts.map +1 -0
  77. package/dist/sessionScope.js +94 -0
  78. package/dist/sessionScope.js.map +1 -0
  79. package/dist/sessionStore.d.ts +69 -0
  80. package/dist/sessionStore.d.ts.map +1 -0
  81. package/dist/sessionStore.js +143 -0
  82. package/dist/sessionStore.js.map +1 -0
  83. package/dist/tofu.d.ts +112 -0
  84. package/dist/tofu.d.ts.map +1 -0
  85. package/dist/tofu.js +180 -0
  86. package/dist/tofu.js.map +1 -0
  87. package/dist/toolPoison.d.ts +32 -0
  88. package/dist/toolPoison.d.ts.map +1 -0
  89. package/dist/toolPoison.js +81 -0
  90. package/dist/toolPoison.js.map +1 -0
  91. package/dist/valkeyErrors.d.ts +17 -0
  92. package/dist/valkeyErrors.d.ts.map +1 -0
  93. package/dist/valkeyErrors.js +24 -0
  94. package/dist/valkeyErrors.js.map +1 -0
  95. package/dist/wasm/context.d.ts +57 -0
  96. package/dist/wasm/context.d.ts.map +1 -0
  97. package/dist/wasm/context.js +59 -0
  98. package/dist/wasm/context.js.map +1 -0
  99. package/dist/wasm/hostImports.d.ts +43 -0
  100. package/dist/wasm/hostImports.d.ts.map +1 -0
  101. package/dist/wasm/hostImports.js +158 -0
  102. package/dist/wasm/hostImports.js.map +1 -0
  103. package/dist/wasm/loader.d.ts +95 -0
  104. package/dist/wasm/loader.d.ts.map +1 -0
  105. package/dist/wasm/loader.js +198 -0
  106. package/dist/wasm/loader.js.map +1 -0
  107. package/dist/wasm/referencedFiles.d.ts +115 -0
  108. package/dist/wasm/referencedFiles.d.ts.map +1 -0
  109. package/dist/wasm/referencedFiles.js +366 -0
  110. package/dist/wasm/referencedFiles.js.map +1 -0
  111. package/dist/wasm/runner.d.ts +91 -0
  112. package/dist/wasm/runner.d.ts.map +1 -0
  113. package/dist/wasm/runner.js +308 -0
  114. package/dist/wasm/runner.js.map +1 -0
  115. package/dist/wasm/watch.d.ts +5 -0
  116. package/dist/wasm/watch.d.ts.map +1 -0
  117. package/dist/wasm/watch.js +54 -0
  118. package/dist/wasm/watch.js.map +1 -0
  119. package/dist/wasm/worker.d.ts +39 -0
  120. package/dist/wasm/worker.d.ts.map +1 -0
  121. package/dist/wasm/worker.js +245 -0
  122. package/dist/wasm/worker.js.map +1 -0
  123. package/package.json +14 -4
@@ -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 ~/.intutic directory exists with correct permissions
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
- // Write PID file
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
- process.on('SIGTERM', () => { void shutdown('SIGTERM'); });
74
- process.on('SIGINT', () => { void shutdown('SIGINT'); });
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
- void main();
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
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":"AA+BA,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,QAAQ,EAAO,MAAM,CAAA;CACtB;AA0CD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA+CvF;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"}
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"}