netzilo 4.3.14

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 ADDED
@@ -0,0 +1,139 @@
1
+ # netzilo (Node.js / TypeScript)
2
+
3
+ Netzilo AI Detection & Response (AIDR) for Node.js / TypeScript
4
+
5
+ Provides full governance for custom AI agents written in Node.js / TypeScript
6
+
7
+ ## How it works
8
+
9
+ The Netzilo client is a native shared library loaded **in-process** via a
10
+ native FFI binding. Every LLM prompt, model response, and tool call your
11
+ agent makes can be evaluated — allowed, blocked, or redacted — against
12
+ policy pulled live from your Netzilo management server. No daemon, no
13
+ sidecar — nothing to stand up.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install netzilo
19
+ ```
20
+
21
+ Platform binaries are resolved automatically for macOS, Linux, and Windows —
22
+ no compiler or extra setup needed.
23
+
24
+ ## Core API
25
+
26
+ ```ts
27
+ import * as netzilo from "netzilo";
28
+
29
+ await netzilo.start({
30
+ server: "https://srv.netzilo.com", // management server
31
+ pat: "nzl_...", // or setupKey: "..."
32
+ agentName: "my-agent", // non-blocking
33
+ });
34
+
35
+ netzilo.isRunning(); // -> boolean: true once policy has synced (ready to govern)
36
+ netzilo.mcpGatewayPort(); // -> number: scanner gateway port in use (auto-picked)
37
+ const { allowed, reason } = netzilo.isAllowed("Bash", { command: "rm -rf /" });
38
+ netzilo.reportResult("Bash", "<stdout>"); // post-tool observability
39
+ netzilo.flush(); // force-deliver buffered events to management
40
+ netzilo.snapshot(); // force AIDR behavior-graph snapshot + delivery (else hourly/at-stop)
41
+ netzilo.stop(); // graceful stop (snapshots graph + flushes events)
42
+ ```
43
+
44
+ `isRunning()` returns `true` only after the initial policy sync — poll it
45
+ before kicking off work. Evaluation runs **inside the process** —
46
+ `evaluate()` calls the embedded engine directly, with no HTTP roundtrip and
47
+ no local port to manage.
48
+
49
+ ## Advanced governance (`enableAdvancedGovernance: true`) — Linux only
50
+
51
+ `wrapTool()` and manual `isAllowed()`/`checkPrompt()`/`checkResponse()` calls
52
+ (below) govern at the tool/LLM-call boundary you wire them into, and work on
53
+ macOS, Linux, and Windows. Advanced governance adds deep inspection of the
54
+ agent's **own** outbound traffic — covering every prompt, response, and tool
55
+ call (including from subprocesses and libraries you have no hook for), with
56
+ full content analysis and semantic classification. It is currently only
57
+ supported when your agent process runs on Linux; on macOS/Windows, leave
58
+ `enableAdvancedGovernance` unset (default `false`) and rely on `wrapTool()` /
59
+ manual gating:
60
+
61
+ ```ts
62
+ await netzilo.start({ server: "...", pat: "...", agentName: "...", enableAdvancedGovernance: true });
63
+ ```
64
+
65
+ It is fully automatic and requires **no root** and no changes to the host —
66
+ the SDK prepares the process so the agent's traffic is inspected and
67
+ continues to work normally. Inspected prompts/responses are analyzed and
68
+ classified, emitting `semantic.event` records to your dashboard alongside the
69
+ usual tool/LLM events. Default off; `wrapTool()` and manual gating work
70
+ without it.
71
+
72
+ For full HTTPS interception coverage of Node's built-in `fetch()`, also
73
+ install `undici` (`npm install undici`) — normal npm hoisting makes it
74
+ resolvable to netzilo automatically. Without it, HTTPS traffic still routes
75
+ through standard proxy environment variables (covering subprocesses and
76
+ proxy-aware libraries), but `fetch()` itself may bypass interception.
77
+ Libraries that build their own `http.Agent` without consulting proxy env
78
+ vars (e.g. `axios`/`got` without an explicit proxy agent) aren't
79
+ automatically covered either — prefer `undici`'s `fetch()`, or wire your
80
+ HTTP client's proxy option to `process.env.HTTPS_PROXY` /
81
+ `process.env.ALL_PROXY` directly.
82
+
83
+ ## Framework integration
84
+
85
+ There's no separate package or submodule to install — everything ships in
86
+ `netzilo` itself.
87
+
88
+ **One-liner, fire-and-forget** — for agents whose own LLM/egress/subprocess
89
+ traffic should be inspected in-process, regardless of which framework issues
90
+ it:
91
+
92
+ ```ts
93
+ import { govern } from "netzilo";
94
+
95
+ await govern(); // config from NETZILO_* env vars; non-blocking
96
+ ```
97
+
98
+ **Gating a specific tool call** — every Node agent framework's "tool" is
99
+ ultimately an async function the model calls with arguments. `wrapTool()`
100
+ gates at that common boundary instead of binding to one framework's specific
101
+ hook API (which drifts release to release):
102
+
103
+ ```ts
104
+ import { wrapTool } from "netzilo/wrapTool";
105
+
106
+ const getOrderStatus = wrapTool(
107
+ "get_order_status",
108
+ async (args: { orderId: string }) => fetchOrder(args.orderId)
109
+ );
110
+
111
+ // Register `getOrderStatus` with LangChain's tool(), the Vercel AI SDK's
112
+ // tool(), or any framework's tool registry exactly as you would the
113
+ // original function.
114
+ ```
115
+
116
+ A blocked call throws `NetziloBlockedError`. A tool result that trips a
117
+ redaction rule (e.g. an AWS key or PII pattern) is returned to the caller
118
+ already sanitized.
119
+
120
+ ## Configuration
121
+
122
+ `start()` / `govern()` accept an options object. Common keys:
123
+
124
+ | Key | Description |
125
+ |-----|-------------|
126
+ | `server` | Your Netzilo management server URL. |
127
+ | `pat` / `setupKey` | Credential used to enroll the agent. |
128
+ | `agentName` | Identifier this agent reports as (used for event attribution). |
129
+ | `mcpGatewayPort` | Optional; defaults to an auto-picked free port. `mcpGatewayPort: 0` disables governance. |
130
+ | `enableAdvancedGovernance` | Optional (default off). Deep inspection of the agent's own outbound traffic with semantic classification. |
131
+ | `logLevel` / `logFile` | Logging verbosity and destination. |
132
+
133
+ ## Notes
134
+
135
+ The native binary is resolved automatically for your platform via an optional
136
+ dependency selected at install time; `npm install netzilo` never downloads a
137
+ binary for a platform you're not on.
138
+
139
+ Netzilo is a commercial product. See <https://www.netzilo.com>.
@@ -0,0 +1,149 @@
1
+ export declare const DEFAULT_CONFIG_PATH: string;
2
+ export interface StartOptions {
3
+ /** Management server URL (required on first login). Legacy alias: managementUrl. */
4
+ server?: string;
5
+ managementUrl?: string;
6
+ /** Non-interactive login — one of pat/setupKey required on first login. */
7
+ pat?: string;
8
+ setupKey?: string;
9
+ /** Peer/device name + event attribution (default: hostname). */
10
+ agentName?: string;
11
+ /** Config file path (default: ~/.netzilo-sdk/config.json). */
12
+ configPath?: string;
13
+ /** Scanner gateway port (default: an auto-picked free port). Set 0 to disable governance. */
14
+ mcpGatewayPort?: number;
15
+ /**
16
+ * Deep inspection of the agent's OWN outbound traffic (beyond framework
17
+ * hooks): full prompt/response and tool-call analysis with semantic
18
+ * classification. Default off; framework adapters govern without it.
19
+ */
20
+ enableAdvancedGovernance?: boolean;
21
+ socks5Port?: number;
22
+ webServPort?: number;
23
+ logLevel?: string;
24
+ logFile?: string;
25
+ }
26
+ /**
27
+ * Start the embedded client. Non-blocking; the client runs in a background
28
+ * goroutine inside the shared library.
29
+ *
30
+ * Returns 0 on launch, 1 if already running, 2 on bad config.
31
+ */
32
+ export declare function start(opts?: StartOptions): Promise<number>;
33
+ /**
34
+ * Port the in-process scanner (MCP) gateway is listening on. Auto-picked at
35
+ * start() unless one was passed explicitly. Returns 0 if not started or
36
+ * governance is disabled (mcpGatewayPort: 0).
37
+ */
38
+ export declare function mcpGatewayPort(): number;
39
+ /** Egress port used by advanced governance. Returns 0 if not started. */
40
+ export declare function socks5Port(): number;
41
+ /**
42
+ * Gracefully stop the client (context cancel). Safe to call repeatedly.
43
+ *
44
+ * Stop() flushes any buffered governance events to management first, so a
45
+ * short-lived embedded run delivers its events before teardown.
46
+ */
47
+ export declare function stop(): number;
48
+ /**
49
+ * Force an immediate push of buffered governance events to management.
50
+ *
51
+ * Events are normally delivered in the background (near-real-time in
52
+ * embedded mode); call this to guarantee delivery on demand. No-op on older
53
+ * bundled libraries without the export.
54
+ */
55
+ export declare function flush(): void;
56
+ /**
57
+ * Force an immediate AIDR session-graph snapshot + delivery to management.
58
+ *
59
+ * The behavior graph (agents -> tools -> prompts -> redactions) is otherwise
60
+ * only uploaded hourly and at shutdown. No-op on older bundled libraries.
61
+ */
62
+ export declare function snapshot(): void;
63
+ /** Whether the embedded client is currently running. */
64
+ export declare function isRunning(): boolean;
65
+ /**
66
+ * Current agent integrity level: 0=breached, 1=low, 2=medium (default), 3=high.
67
+ *
68
+ * Reflects whatever posture rules (set_integrity_level) or host integrity
69
+ * checks have set in-process.
70
+ */
71
+ export declare function integrityLevel(): number;
72
+ export type Action = "toolCall" | "toolResult" | "promptRequest" | "promptResponse";
73
+ export interface EvaluateOptions {
74
+ args?: Record<string, unknown>;
75
+ output?: string;
76
+ prompt?: string;
77
+ response?: string;
78
+ history?: unknown[];
79
+ provider?: string;
80
+ model?: string;
81
+ durationMs?: number;
82
+ callId?: string;
83
+ source?: string;
84
+ serverName?: string;
85
+ serverUrl?: string;
86
+ callerPid?: number;
87
+ }
88
+ export interface Verdict {
89
+ action: "allow" | "block" | "require_approval";
90
+ reason?: string;
91
+ redacted?: boolean;
92
+ content?: string;
93
+ [key: string]: unknown;
94
+ }
95
+ /**
96
+ * Run one hook event through the in-process scanner + AIDR pipeline.
97
+ *
98
+ * action:
99
+ * "toolCall" pre-tool, may block (args)
100
+ * "toolResult" post-tool, fire-and-forget (output)
101
+ * "promptRequest" LLM prompt before send (prompt, history)
102
+ * "promptResponse" LLM response before use (response)
103
+ *
104
+ * Fails open ({action: "allow"}) if the scanner is not yet running.
105
+ */
106
+ export declare function evaluate(action: Action, toolName?: string, opts?: EvaluateOptions): Verdict;
107
+ /** Convenience pre-tool gate. Returns {allowed, reason}. */
108
+ export declare function isAllowed(toolName: string, args?: Record<string, unknown>, opts?: Pick<EvaluateOptions, "callId" | "source" | "serverUrl" | "callerPid">): {
109
+ allowed: boolean;
110
+ reason: string;
111
+ };
112
+ /** Convenience post-tool observability report (never blocks). */
113
+ export declare function reportResult(toolName: string, output: string, opts?: Pick<EvaluateOptions, "callId" | "source" | "callerPid">): void;
114
+ /**
115
+ * Gate an outbound LLM prompt before it is sent to the model.
116
+ *
117
+ * Returns {allowed, reason, redactedPrompt}. redactedPrompt is non-null when
118
+ * the scanner sanitized the prompt — the caller should send that text instead.
119
+ */
120
+ export declare function checkPrompt(prompt: string, opts?: Pick<EvaluateOptions, "history" | "provider" | "model" | "callId" | "source" | "callerPid">): {
121
+ allowed: boolean;
122
+ reason: string;
123
+ redactedPrompt: string | null;
124
+ };
125
+ /**
126
+ * Gate an LLM response before the agent consumes it.
127
+ *
128
+ * Returns {allowed, reason, redactedResponse}. redactedResponse is non-null
129
+ * when the scanner sanitized the response — the caller should use that text.
130
+ */
131
+ export declare function checkResponse(response: string, opts?: Pick<EvaluateOptions, "prompt" | "provider" | "model" | "durationMs" | "callId" | "source" | "callerPid">): {
132
+ allowed: boolean;
133
+ reason: string;
134
+ redactedResponse: string | null;
135
+ };
136
+ /**
137
+ * One-call, fire-and-forget governance for any Node agent framework.
138
+ *
139
+ * Unlike a framework-specific adapter (which hooks that framework's tool-call
140
+ * boundary), govern() just starts the embedded client with advanced
141
+ * governance on — the agent's own LLM traffic, egress, and subprocess
142
+ * behavior are inspected in-process regardless of which framework issues
143
+ * them. Idempotent (no-op if already running). Config comes from NETZILO_*
144
+ * env vars, overridable via opts.
145
+ *
146
+ * import { govern } from "netzilo";
147
+ * await govern();
148
+ */
149
+ export declare function govern(opts?: StartOptions): Promise<number>;
package/dist/index.js ADDED
@@ -0,0 +1,384 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_CONFIG_PATH = void 0;
37
+ exports.start = start;
38
+ exports.mcpGatewayPort = mcpGatewayPort;
39
+ exports.socks5Port = socks5Port;
40
+ exports.stop = stop;
41
+ exports.flush = flush;
42
+ exports.snapshot = snapshot;
43
+ exports.isRunning = isRunning;
44
+ exports.integrityLevel = integrityLevel;
45
+ exports.evaluate = evaluate;
46
+ exports.isAllowed = isAllowed;
47
+ exports.reportResult = reportResult;
48
+ exports.checkPrompt = checkPrompt;
49
+ exports.checkResponse = checkResponse;
50
+ exports.govern = govern;
51
+ /**
52
+ * netzilo — embed the full Netzilo client in-process for AI agent governance.
53
+ *
54
+ * The core module loads the bundled Netzilo shared library and exposes an API
55
+ * that mirrors the C exports (Start/Stop/IsRunning):
56
+ *
57
+ * import * as netzilo from "netzilo";
58
+ * netzilo.start({ server: "...", setupKey: "..." }); // non-blocking
59
+ * netzilo.stop(); // graceful stop
60
+ * netzilo.isRunning(); // boolean
61
+ * await netzilo.evaluate(...); // per-tool-call verdict, in-process (no HTTP)
62
+ * await netzilo.isAllowed(...); // convenience: {allowed, reason}
63
+ *
64
+ * Framework adapters live in their own entry points under the same package —
65
+ * no separate installs — e.g. `import { govern } from "netzilo/langchain"`.
66
+ * They build on evaluate()/isAllowed() to gate tool calls at the framework
67
+ * boundary. With enableAdvancedGovernance=true, start() additionally governs
68
+ * the agent's own outbound traffic in depth (see probe.ts / trust.ts).
69
+ */
70
+ const net = __importStar(require("net"));
71
+ const os = __importStar(require("os"));
72
+ const path = __importStar(require("path"));
73
+ const loader_1 = require("./loader");
74
+ // Default config location — callers shouldn't have to specify it.
75
+ exports.DEFAULT_CONFIG_PATH = path.join(os.homedir(), ".netzilo-sdk", "config.json");
76
+ // Ports actually used by the running client, filled in by start(). Exposed via
77
+ // mcpGatewayPort()/socks5Port() so the host can route traffic / introspect.
78
+ const resolvedPorts = { mcpGatewayPort: 0, socks5Port: 0 };
79
+ // Config directory of the running client (dirname of configPath), set by
80
+ // start(). Used by the advanced-governance setup to locate per-install state.
81
+ let configDir = "";
82
+ let exitHookRegistered = false;
83
+ async function freePorts(n) {
84
+ // Pick n distinct currently-free localhost TCP ports. Servers are held
85
+ // listening simultaneously so the returned ports can't collide with each
86
+ // other, then closed for the client to bind.
87
+ const servers = [];
88
+ try {
89
+ for (let i = 0; i < n; i++) {
90
+ const server = net.createServer();
91
+ await new Promise((resolve, reject) => {
92
+ server.once("error", reject);
93
+ server.listen(0, "127.0.0.1", () => resolve());
94
+ });
95
+ servers.push(server);
96
+ }
97
+ return servers.map((s) => s.address().port);
98
+ }
99
+ finally {
100
+ await Promise.all(servers.map((s) => new Promise((resolve) => {
101
+ s.close(() => resolve());
102
+ })));
103
+ }
104
+ }
105
+ /**
106
+ * Start the embedded client. Non-blocking; the client runs in a background
107
+ * goroutine inside the shared library.
108
+ *
109
+ * Returns 0 on launch, 1 if already running, 2 on bad config.
110
+ */
111
+ async function start(opts = {}) {
112
+ const cfg = { config_path: opts.configPath || exports.DEFAULT_CONFIG_PATH };
113
+ const server = opts.server ?? opts.managementUrl;
114
+ if (server) {
115
+ cfg.management_url = server;
116
+ }
117
+ if (opts.pat)
118
+ cfg.pat = opts.pat;
119
+ if (opts.setupKey)
120
+ cfg.setup_key = opts.setupKey;
121
+ if (opts.agentName)
122
+ cfg.agent_name = opts.agentName;
123
+ if (opts.enableAdvancedGovernance !== undefined) {
124
+ cfg.enable_advanced_governance = opts.enableAdvancedGovernance;
125
+ }
126
+ if (opts.webServPort !== undefined)
127
+ cfg.web_serv_port = opts.webServPort;
128
+ if (opts.logLevel)
129
+ cfg.log_level = opts.logLevel;
130
+ if (opts.logFile)
131
+ cfg.log_file = opts.logFile;
132
+ // Auto-pick free ports for the scanner gateway and the advanced-governance
133
+ // egress when unset, so they never collide with Netzilo.app or a second SDK
134
+ // process. Pick both at once so they can't clash with each other. Pass an
135
+ // explicit port to override, or mcpGatewayPort: 0 to disable governance.
136
+ const needMcp = opts.mcpGatewayPort === undefined;
137
+ const needSocks = opts.socks5Port === undefined;
138
+ if (needMcp || needSocks) {
139
+ const picked = await freePorts((needMcp ? 1 : 0) + (needSocks ? 1 : 0));
140
+ let i = 0;
141
+ cfg.mcp_gateway_port = needMcp ? picked[i++] : opts.mcpGatewayPort;
142
+ cfg.socks5_port = needSocks ? picked[i++] : opts.socks5Port;
143
+ }
144
+ else {
145
+ cfg.mcp_gateway_port = opts.mcpGatewayPort;
146
+ cfg.socks5_port = opts.socks5Port;
147
+ }
148
+ // Advanced governance: publish the security-probe socket path BEFORE the
149
+ // native client starts so its receiver binds the exact path the probe
150
+ // connects to (no startup race). Linux-only; no-op otherwise. Gated on
151
+ // advanced mode — with it off, the probe socket is never primed and no
152
+ // hooks are installed.
153
+ if (cfg.enable_advanced_governance) {
154
+ const { primeSocketEnv } = await Promise.resolve().then(() => __importStar(require("./probe")));
155
+ primeSocketEnv(path.dirname(cfg.config_path));
156
+ }
157
+ const lib = (0, loader_1.load)();
158
+ const rc = lib.Start(JSON.stringify(cfg));
159
+ if (rc === 0) {
160
+ resolvedPorts.mcpGatewayPort = cfg.mcp_gateway_port || 0;
161
+ resolvedPorts.socks5Port = cfg.socks5_port || 0;
162
+ configDir = path.dirname(cfg.config_path);
163
+ if (!exitHookRegistered) {
164
+ exitHookRegistered = true;
165
+ // process 'exit' handlers must be synchronous; Stop() is a synchronous
166
+ // FFI call via koffi, so a plain handler here is sufficient.
167
+ process.on("exit", () => {
168
+ stop();
169
+ });
170
+ }
171
+ if (cfg.enable_advanced_governance) {
172
+ await enableAdvancedGovernance();
173
+ }
174
+ }
175
+ return rc;
176
+ }
177
+ /**
178
+ * Internal: prepare the process for deep inspection of the agent's own
179
+ * traffic. Idempotent; never throws (must not break the agent).
180
+ */
181
+ async function enableAdvancedGovernance() {
182
+ if (!configDir) {
183
+ return;
184
+ }
185
+ try {
186
+ const { installTrust, installRouting } = await Promise.resolve().then(() => __importStar(require("./trust")));
187
+ await installTrust(configDir);
188
+ installRouting(resolvedPorts.socks5Port);
189
+ // Inject the userspace security probe (Linux only) so the agent's process
190
+ // + subprocess activity is reported to the native client's receiver,
191
+ // mirroring the macOS Endpoint Security extension. Only reached under
192
+ // advanced governance.
193
+ const { install: installProbe } = await Promise.resolve().then(() => __importStar(require("./probe")));
194
+ installProbe(configDir);
195
+ }
196
+ catch (err) {
197
+ // Advanced-governance setup must never break the agent.
198
+ // eslint-disable-next-line no-console
199
+ console.warn("netzilo: advanced governance setup failed:", err);
200
+ }
201
+ }
202
+ /**
203
+ * Port the in-process scanner (MCP) gateway is listening on. Auto-picked at
204
+ * start() unless one was passed explicitly. Returns 0 if not started or
205
+ * governance is disabled (mcpGatewayPort: 0).
206
+ */
207
+ function mcpGatewayPort() {
208
+ return resolvedPorts.mcpGatewayPort;
209
+ }
210
+ /** Egress port used by advanced governance. Returns 0 if not started. */
211
+ function socks5Port() {
212
+ return resolvedPorts.socks5Port;
213
+ }
214
+ /**
215
+ * Gracefully stop the client (context cancel). Safe to call repeatedly.
216
+ *
217
+ * Stop() flushes any buffered governance events to management first, so a
218
+ * short-lived embedded run delivers its events before teardown.
219
+ */
220
+ function stop() {
221
+ const lib = (0, loader_1.load)();
222
+ const rc = lib.Stop();
223
+ resolvedPorts.mcpGatewayPort = 0;
224
+ resolvedPorts.socks5Port = 0;
225
+ // Undo advanced-governance traffic routing so a host process that keeps
226
+ // running after stop() doesn't route to the now-closed egress port.
227
+ try {
228
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
229
+ const { uninstallRouting } = require("./trust");
230
+ uninstallRouting();
231
+ }
232
+ catch {
233
+ // stop() must never throw
234
+ }
235
+ // Stop injecting the probe into subprocesses spawned after stop().
236
+ try {
237
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
238
+ const { uninstall: uninstallProbe } = require("./probe");
239
+ uninstallProbe();
240
+ }
241
+ catch {
242
+ // stop() must never throw
243
+ }
244
+ return rc;
245
+ }
246
+ /**
247
+ * Force an immediate push of buffered governance events to management.
248
+ *
249
+ * Events are normally delivered in the background (near-real-time in
250
+ * embedded mode); call this to guarantee delivery on demand. No-op on older
251
+ * bundled libraries without the export.
252
+ */
253
+ function flush() {
254
+ (0, loader_1.load)().FlushEvents();
255
+ }
256
+ /**
257
+ * Force an immediate AIDR session-graph snapshot + delivery to management.
258
+ *
259
+ * The behavior graph (agents -> tools -> prompts -> redactions) is otherwise
260
+ * only uploaded hourly and at shutdown. No-op on older bundled libraries.
261
+ */
262
+ function snapshot() {
263
+ (0, loader_1.load)().FlushSnapshot();
264
+ }
265
+ /** Whether the embedded client is currently running. */
266
+ function isRunning() {
267
+ return (0, loader_1.load)().IsRunning() !== 0;
268
+ }
269
+ /**
270
+ * Current agent integrity level: 0=breached, 1=low, 2=medium (default), 3=high.
271
+ *
272
+ * Reflects whatever posture rules (set_integrity_level) or host integrity
273
+ * checks have set in-process.
274
+ */
275
+ function integrityLevel() {
276
+ return (0, loader_1.load)().GetIntegrityLevel();
277
+ }
278
+ /**
279
+ * Run one hook event through the in-process scanner + AIDR pipeline.
280
+ *
281
+ * action:
282
+ * "toolCall" pre-tool, may block (args)
283
+ * "toolResult" post-tool, fire-and-forget (output)
284
+ * "promptRequest" LLM prompt before send (prompt, history)
285
+ * "promptResponse" LLM response before use (response)
286
+ *
287
+ * Fails open ({action: "allow"}) if the scanner is not yet running.
288
+ */
289
+ function evaluate(action, toolName = "", opts = {}) {
290
+ const lib = (0, loader_1.load)();
291
+ const source = opts.source ?? "agentsdk";
292
+ const req = {
293
+ action,
294
+ tool_name: toolName,
295
+ server_name: opts.serverName ?? source,
296
+ call_id: opts.callId ?? "",
297
+ source,
298
+ server_url: opts.serverUrl ?? "",
299
+ caller_pid: opts.callerPid || process.pid,
300
+ };
301
+ if (opts.args !== undefined) {
302
+ req.args = opts.args;
303
+ }
304
+ if (opts.output !== undefined)
305
+ req.output = opts.output;
306
+ if (opts.prompt !== undefined)
307
+ req.prompt = opts.prompt;
308
+ if (opts.response !== undefined)
309
+ req.response = opts.response;
310
+ if (opts.history && opts.history.length) {
311
+ req.history = opts.history.map((h) => String(h));
312
+ }
313
+ if (opts.provider)
314
+ req.provider = opts.provider;
315
+ if (opts.model)
316
+ req.model = opts.model;
317
+ if (opts.durationMs)
318
+ req.duration_ms = opts.durationMs;
319
+ const raw = lib.Evaluate(JSON.stringify(req));
320
+ return raw ? JSON.parse(raw) : { action: "allow" };
321
+ }
322
+ /** Convenience pre-tool gate. Returns {allowed, reason}. */
323
+ function isAllowed(toolName, args = {}, opts = {}) {
324
+ const verdict = evaluate("toolCall", toolName, { ...opts, args });
325
+ const action = verdict.action ?? "allow";
326
+ return { allowed: action === "allow", reason: verdict.reason ?? "" };
327
+ }
328
+ /** Convenience post-tool observability report (never blocks). */
329
+ function reportResult(toolName, output, opts = {}) {
330
+ evaluate("toolResult", toolName, { ...opts, output });
331
+ }
332
+ /**
333
+ * Gate an outbound LLM prompt before it is sent to the model.
334
+ *
335
+ * Returns {allowed, reason, redactedPrompt}. redactedPrompt is non-null when
336
+ * the scanner sanitized the prompt — the caller should send that text instead.
337
+ */
338
+ function checkPrompt(prompt, opts = {}) {
339
+ const v = evaluate("promptRequest", "", { ...opts, prompt });
340
+ const allowed = (v.action ?? "allow") === "allow";
341
+ const redactedPrompt = v.redacted ? v.content ?? null : null;
342
+ return { allowed, reason: v.reason ?? "", redactedPrompt };
343
+ }
344
+ /**
345
+ * Gate an LLM response before the agent consumes it.
346
+ *
347
+ * Returns {allowed, reason, redactedResponse}. redactedResponse is non-null
348
+ * when the scanner sanitized the response — the caller should use that text.
349
+ */
350
+ function checkResponse(response, opts = {}) {
351
+ const v = evaluate("promptResponse", "", { ...opts, response });
352
+ const allowed = (v.action ?? "allow") === "allow";
353
+ const redactedResponse = v.redacted ? v.content ?? null : null;
354
+ return { allowed, reason: v.reason ?? "", redactedResponse };
355
+ }
356
+ /**
357
+ * One-call, fire-and-forget governance for any Node agent framework.
358
+ *
359
+ * Unlike a framework-specific adapter (which hooks that framework's tool-call
360
+ * boundary), govern() just starts the embedded client with advanced
361
+ * governance on — the agent's own LLM traffic, egress, and subprocess
362
+ * behavior are inspected in-process regardless of which framework issues
363
+ * them. Idempotent (no-op if already running). Config comes from NETZILO_*
364
+ * env vars, overridable via opts.
365
+ *
366
+ * import { govern } from "netzilo";
367
+ * await govern();
368
+ */
369
+ async function govern(opts = {}) {
370
+ if (isRunning()) {
371
+ return 1;
372
+ }
373
+ const cfg = {
374
+ server: process.env.NETZILO_SERVER || "https://srv.netzilo.com",
375
+ agentName: process.env.NETZILO_AGENT_NAME,
376
+ enableAdvancedGovernance: process.env.NETZILO_ADVANCED !== "0",
377
+ logLevel: process.env.NETZILO_LOG_LEVEL || "info",
378
+ logFile: "console",
379
+ ...(process.env.NETZILO_SETUP_KEY ? { setupKey: process.env.NETZILO_SETUP_KEY } : {}),
380
+ ...(process.env.NETZILO_PAT ? { pat: process.env.NETZILO_PAT } : {}),
381
+ ...opts,
382
+ };
383
+ return start(cfg);
384
+ }
@@ -0,0 +1,21 @@
1
+ export interface NetziloLib {
2
+ Start(configJSON: string): number;
3
+ Stop(): number;
4
+ IsRunning(): number;
5
+ /**
6
+ * Returns the decoded JSON string directly, or null. Evaluate's C return
7
+ * value is a heap-allocated char* that must be released with FreeString —
8
+ * bound as a koffi "disposable type" (see below), so koffi calls FreeString
9
+ * on the original pointer automatically right after decoding it to a JS
10
+ * string. Callers never see or manage the pointer.
11
+ */
12
+ Evaluate(reqJSON: string): string | null;
13
+ /** Absent on older bundled libraries; guarded with a no-op fallback. */
14
+ FlushEvents(): void;
15
+ FlushSnapshot(): void;
16
+ GetIntegrityLevel(): number;
17
+ }
18
+ export declare function load(): NetziloLib;
19
+ /** Test-only: forget the cached binding so a fresh NETZILO_LIB_PATH can be picked up. */
20
+ export declare function _resetForTests(): void;
21
+ export declare function tmpDir(): string;