@princeverma130104/agentguard 0.0.0-stage → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AgentGuard Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,334 @@
1
- # Temporary Holding Version
1
+ # 🛡️ AgentGuard
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > **Privacy & Context Redaction Middleware for AI Agents**
4
+ > Detect, tokenise, and restore PII/credentials in real time — sensitive values are kept in your configured Redis vault and are not sent to the LLM.
5
+
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org)
9
+
10
+ ---
11
+
12
+ ## The Problem
13
+
14
+ When you build AI agents that handle user data — resumes, emails, SSNs, API keys — that data flows raw into your LLM provider's servers. This violates GDPR, SOC2, and HIPAA, and exposes your users to data breaches.
15
+
16
+ ## The Solution
17
+
18
+ AgentGuard acts as an invisible middleware layer between your agent framework and the LLM:
19
+
20
+ ```
21
+ ┌──────────────────────┐
22
+ │ Agent Framework │ (1) Raw Prompt: "Register John at john@x.com"
23
+ │ (Mastra / LangChain)│
24
+ └──────────┬───────────┘
25
+ │
26
+ ▼
27
+ ┌──────────────────────┐
28
+ │ AgentGuard SDK │ (2) Detect PII with Regex/NLP patterns
29
+ │ (Local Middleware) │ (3) Store in Redis: {[EMAIL_1]: john@x.com}
30
+ └──────────┬───────────┘
31
+ │ (4) Sanitized: "Register [NAME_1] at [EMAIL_1]"
32
+ ▼
33
+ ┌──────────────────────┐
34
+ │ LLM Provider │ ← Never sees real PII
35
+ │ (OpenAI / Claude) │
36
+ └──────────┬───────────┘
37
+ │ (5) Response: "Fill form with [NAME_1], [EMAIL_1]"
38
+ ▼
39
+ ┌──────────────────────┐
40
+ │ AgentGuard SDK │ (6) Restore: "Fill form with John, john@x.com"
41
+ └──────────┬───────────┘
42
+ │
43
+ ▼
44
+ ┌──────────────────────┐
45
+ │ Tool / Browser │ ← Gets real values for execution
46
+ └──────────────────────┘
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Features
52
+
53
+ | Feature | Status |
54
+ |---|---|
55
+ | 📧 Email, 📱 Phone, 🔑 API Key detection | ✅ |
56
+ | 🇺🇸 SSN, 🇮🇳 Aadhaar, 💳 Credit Card | ✅ |
57
+ | ☁️ AWS Access Keys, Bearer Tokens | ✅ |
58
+ | 🔒 URL with embedded credentials | ✅ |
59
+ | 🧠 Multi-turn session deduplication | ✅ |
60
+ | ⚡ Redis-backed ephemeral vault (TTL) | ✅ |
61
+ | 🔄 Mastra framework wrapper | ✅ |
62
+ | 🦜 LangChain.js callback handler | ✅ |
63
+ | 🌐 Express HTTP middleware | ✅ |
64
+ | 📊 Audit log (metadata only, no PII) | ✅ |
65
+ | 🐳 Docker Compose ready | ✅ |
66
+ | 🧪 Vitest test suite (80% coverage) | ✅ |
67
+
68
+ ---
69
+
70
+ ## Quick Start
71
+
72
+ ### 1. Install
73
+
74
+ ```bash
75
+ npm install @princeverma130104/agentguard
76
+ ```
77
+
78
+ ### 2. Start Redis
79
+
80
+ ```bash
81
+ docker-compose up -d
82
+ ```
83
+
84
+ ### 3. Use
85
+
86
+ ```typescript
87
+ import { AgentGuard } from '@princeverma130104/agentguard';
88
+
89
+ const guard = new AgentGuard({
90
+ redisUrl: 'redis://localhost:6379',
91
+ ttlSeconds: 900,
92
+ });
93
+
94
+ // Anonymize before sending to LLM
95
+ const { sanitizedText, sessionId } = await guard.anonymize(
96
+ 'Please register John Smith at john@acme.com, SSN 123-45-6789'
97
+ );
98
+ // sanitizedText → "Please register [NAME_1] at [EMAIL_1], SSN [SSN_1]"
99
+
100
+ // ... send sanitizedText to your LLM ...
101
+
102
+ // Deanonymize before tool execution
103
+ const toolInput = await guard.deanonymize(
104
+ 'Fill form: [NAME_1] <[EMAIL_1]>',
105
+ sessionId
106
+ );
107
+ // toolInput → "Fill form: John Smith <john@acme.com>"
108
+
109
+ await guard.destroySession(sessionId);
110
+ await guard.close();
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Mastra Integration
116
+
117
+ ```typescript
118
+ import { createMastraGuard } from '@princeverma130104/agentguard/mastra';
119
+
120
+ const { wrapStep } = createMastraGuard({
121
+ redisUrl: process.env.REDIS_URL,
122
+ });
123
+
124
+ // Wrap any Mastra step — anonymization/deanonymization is automatic
125
+ const myStep = {
126
+ execute: wrapStep(async ({ context }) => {
127
+ // context.prompt is already sanitized here
128
+ const response = await callLLM(context.prompt);
129
+ return { text: response }; // response tokens are auto-restored
130
+ }),
131
+ };
132
+ ```
133
+
134
+ ---
135
+
136
+ ## LangChain.js Integration
137
+
138
+ ```typescript
139
+ import { createLangChainGuard } from '@princeverma130104/agentguard/langchain';
140
+ import { ChatOpenAI } from '@langchain/openai';
141
+
142
+ const { handler, restore } = createLangChainGuard({
143
+ redisUrl: process.env.REDIS_URL,
144
+ });
145
+
146
+ // Just pass the handler as a callback — no other changes needed
147
+ const llm = new ChatOpenAI({ callbacks: [handler] });
148
+ const result = await llm.invoke('My SSN is 123-45-6789');
149
+
150
+ // Restore real values in the output before using in tools
151
+ const clean = await restore(result.content, handler.sessionId);
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Express Middleware
157
+
158
+ ```typescript
159
+ import express from 'express';
160
+ import { createGuardMiddleware } from '@princeverma130104/agentguard/middleware';
161
+
162
+ const app = express();
163
+ app.use(express.json());
164
+ app.use(...createGuardMiddleware({ redisUrl: process.env.REDIS_URL }));
165
+
166
+ // All request body strings are automatically anonymized
167
+ // All response body strings are automatically deanonymized
168
+ app.post('/chat', async (req, res) => {
169
+ const response = await callLLM(req.body.prompt); // already sanitized
170
+ res.json({ text: response }); // auto-deanonymized before sending
171
+ });
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Detected Entity Types
177
+
178
+ | Token Pattern | Detects |
179
+ |---|---|
180
+ | `[EMAIL_N]` | Email addresses |
181
+ | `[PHONE_N]` | US phone numbers |
182
+ | `[SSN_N]` | US Social Security Numbers |
183
+ | `[AADHAAR_N]` | Indian Aadhaar numbers |
184
+ | `[CREDIT_CARD_N]` | Visa, Mastercard, Amex, Discover |
185
+ | `[API_KEY_N]` | Stripe, OpenAI-style secret keys |
186
+ | `[BEARER_TOKEN_N]` | HTTP Bearer tokens |
187
+ | `[AWS_KEY_N]` | AWS Access Key IDs |
188
+ | `[PRIVATE_KEY_N]` | PEM private keys |
189
+ | `[URL_WITH_CREDS_N]` | `user:pass@host` style URLs |
190
+ | `[IP_ADDRESS_N]` | IPv4 addresses |
191
+ | `[NAME_N]` | Named entity heuristic |
192
+
193
+ ---
194
+
195
+ ## Configuration
196
+
197
+ ```typescript
198
+ const guard = new AgentGuard({
199
+ redisUrl: 'redis://localhost:6379', // default
200
+ ttlSeconds: 900, // 15 minutes default
201
+ verbose: false, // console logging (no PII)
202
+ });
203
+ ```
204
+
205
+ | Option | Type | Default | Description |
206
+ |---|---|---|---|
207
+ | `redisUrl` | `string` | `redis://localhost:6379` | Redis connection URL |
208
+ | `ttlSeconds` | `number` | `900` | Session TTL in seconds |
209
+ | `verbose` | `boolean` | `false` | Log operation metadata (never PII) |
210
+
211
+ ---
212
+
213
+ ## Running Tests
214
+
215
+ ```bash
216
+ # Unit tests only (no Redis required)
217
+ npm run test:unit
218
+
219
+ # All tests (requires Redis via docker-compose up -d)
220
+ npm test
221
+
222
+ # With coverage report
223
+ npm run test:coverage
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Deploying AgentGuard
229
+
230
+ AgentGuard is a Node.js library, not a standalone web application. Deploy it by
231
+ publishing the package to npm and using it from your agent/API service. The Redis
232
+ vault must be reachable from that service.
233
+
234
+ ### Prepare a release
235
+
236
+ 1. The published package name is `@princeverma130104/agentguard`. To publish
237
+ under a different npm account, change `name` in `package.json` and the
238
+ lockfile to a scope that account owns.
239
+ 2. Replace the repository, bugs, homepage, and author placeholders in
240
+ `package.json` with your project details.
241
+ 3. Run the production checks and inspect the package contents:
242
+
243
+ ```bash
244
+ npm ci
245
+ npm run build
246
+ npm test
247
+ npm pack --dry-run
248
+ ```
249
+
250
+ 4. Sign in to npm and publish:
251
+
252
+ ```bash
253
+ npm login
254
+ npm publish
255
+ ```
256
+
257
+ For a scoped package intended for public use, publish with
258
+ `npm publish --access public`. `prepublishOnly` rebuilds the package and runs
259
+ the test suite before publication.
260
+
261
+ ### Deploy the consuming service
262
+
263
+ 1. Provision a Redis instance reachable only by your application. Use a managed
264
+ Redis service with TLS for production; do not expose the local Docker Redis
265
+ port to the public internet.
266
+ 2. Set `REDIS_URL` in the application host's secret/environment settings. Use
267
+ the provider's TLS URL (`rediss://...`) when offered, and never commit
268
+ credentials in `.env` or source control.
269
+ 3. Set `AGENTGUARD_TTL_SECONDS` if the default 900 seconds is not appropriate.
270
+ Configure your app to pass `REDIS_URL` and the desired TTL to `AgentGuard`.
271
+ 4. Install the published package in the application, deploy the service using
272
+ its normal Node.js deployment process, and verify Redis connectivity with
273
+ `await guard.healthCheck()` before accepting traffic.
274
+ 5. Ensure your Redis provider's persistence, backups, access controls, and
275
+ retention settings meet your privacy requirements. AgentGuard cannot disable
276
+ persistence on an externally managed Redis service.
277
+
278
+ The included `docker-compose.yml` is for local development only. It binds Redis
279
+ to localhost and disables RDB snapshots and AOF persistence; it is not a
280
+ production Redis deployment. Your service host and Redis must be networked
281
+ together, and the service must receive the Redis URL through its secret manager.
282
+
283
+ ---
284
+
285
+ ## Project Structure
286
+
287
+ ```
288
+ agentguard/
289
+ ├── src/
290
+ │ ├── index.ts # Public API barrel exports
291
+ │ ├── guard.ts # Core AgentGuard class
292
+ │ ├── vault.ts # Redis token vault
293
+ │ ├── scanner.ts # PII detection engine
294
+ │ ├── patterns.ts # Regex pattern registry
295
+ │ ├── middleware.ts # Express middleware
296
+ │ ├── mastra.ts # Mastra framework wrapper
297
+ │ └── langchain.ts # LangChain.js callback handler
298
+ ├── tests/
299
+ │ ├── scanner.test.ts # Scanner unit tests
300
+ │ ├── vault.test.ts # Vault unit tests
301
+ │ └── guard.test.ts # Integration tests
302
+ ├── examples/
303
+ │ ├── basic-usage.ts # Standalone example
304
+ │ └── mastra-quickstart.ts
305
+ ├── docker-compose.yml
306
+ ├── .env.example
307
+ ├── tsconfig.json
308
+ └── vitest.config.ts
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Security & Compliance
314
+
315
+ - **Ephemeral vault**: Raw PII is stored in Redis and is not sent to the LLM; configure Redis persistence and access controls to match your privacy requirements
316
+ - **TTL enforcement**: All session data expires automatically (default 15 min)
317
+ - **Session isolation**: Each session has its own Redis key namespace — no cross-session leakage
318
+ - **Audit log**: All operations are logged with timestamps and entity types only — never raw PII values
319
+ - **Pipeline batching**: Vault reads/writes use Redis pipelines to minimise latency overhead
320
+
321
+ ### NFR Compliance
322
+
323
+ | Requirement | Target | Implementation |
324
+ |---|---|---|
325
+ | Latency overhead | < 15ms | Redis pipeline batching |
326
+ | Zero persistence | No disk writes | Local Compose disables RDB/AOF; production Redis persistence is provider-configured |
327
+ | Session isolation | Per session | Namespaced Redis keys per session UUID |
328
+ | Scalability | Horizontal | Stateless middleware + shared Redis |
329
+
330
+ ---
331
+
332
+ ## License
333
+
334
+ MIT © AgentGuard Contributors
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @file guard.ts
3
+ * @description Core AgentGuard class — the main public API.
4
+ * Orchestrates scanning, tokenization, vault storage, and deanonymization.
5
+ */
6
+ import { VaultOptions } from './vault';
7
+ import { PatternKey } from './patterns';
8
+ export interface GuardOptions extends VaultOptions {
9
+ /**
10
+ * Custom pattern overrides or additions. Keys are entity type labels,
11
+ * values are RegExp factory functions (must return a /g-flagged RegExp).
12
+ */
13
+ customPatterns?: Partial<Record<PatternKey, () => RegExp>>;
14
+ /**
15
+ * If true, logs redaction summaries to console (no raw PII ever logged).
16
+ * Defaults to false.
17
+ */
18
+ verbose?: boolean;
19
+ }
20
+ export interface AnonymizeResult {
21
+ /** The sanitized text with all PII replaced by placeholder tokens */
22
+ sanitizedText: string;
23
+ /** The session ID that maps tokens back to their raw values in the vault */
24
+ sessionId: string;
25
+ /** Summary of what was redacted (types and counts only — no raw values) */
26
+ redactionSummary: Record<string, number>;
27
+ }
28
+ export interface AuditEntry {
29
+ timestamp: string;
30
+ sessionId: string;
31
+ operation: 'anonymize' | 'deanonymize' | 'session_destroy';
32
+ entityTypesRedacted?: string[];
33
+ totalTokensRestored?: number;
34
+ }
35
+ export declare class AgentGuard {
36
+ private readonly options;
37
+ private readonly vault;
38
+ private readonly verbose;
39
+ private readonly auditLog;
40
+ constructor(options?: GuardOptions);
41
+ /**
42
+ * Scans input text, replaces all detected PII with deterministic tokens,
43
+ * and stores the (token → rawValue) mapping in the ephemeral Redis vault.
44
+ *
45
+ * If a `sessionId` is provided, tokens are added to the existing session.
46
+ * Otherwise, a new session UUID is created automatically.
47
+ *
48
+ * @param text Raw text potentially containing PII/credentials
49
+ * @param sessionId Optional existing session to extend
50
+ * @returns Sanitized text, session ID, and a redaction summary
51
+ */
52
+ anonymize(text: string, sessionId?: string): Promise<AnonymizeResult>;
53
+ /**
54
+ * Re-injects raw PII values into tokenized text before tool execution.
55
+ * Looks up all tokens from the session vault and replaces them with
56
+ * their original values.
57
+ *
58
+ * @param tokenizedText Text containing AgentGuard placeholder tokens
59
+ * @param sessionId The session ID returned from `anonymize()`
60
+ * @returns Text with all tokens replaced by original values
61
+ */
62
+ deanonymize(tokenizedText: string, sessionId: string): Promise<string>;
63
+ /**
64
+ * Wraps an async function, automatically anonymizing its string input
65
+ * and deanonymizing the string output. Useful for wrapping LLM calls.
66
+ *
67
+ * @example
68
+ * const safeCall = guard.wrapCall(openai.chat.completions.create.bind(openai));
69
+ */
70
+ wrapCall<T>(fn: (sanitizedInput: string) => Promise<T>, rawInput: string, sessionId?: string, outputExtractor?: (result: T) => string, outputInjector?: (result: T, restored: string) => T): Promise<{
71
+ result: T;
72
+ sessionId: string;
73
+ }>;
74
+ /**
75
+ * Explicitly destroys a session and all its vault data from Redis.
76
+ * Call this when a user session completes to proactively free memory.
77
+ */
78
+ destroySession(sessionId: string): Promise<void>;
79
+ /**
80
+ * Returns the in-memory audit log (no PII, only metadata).
81
+ */
82
+ getAuditLog(): ReadonlyArray<AuditEntry>;
83
+ /**
84
+ * Checks Redis connectivity. Use in health-check endpoints.
85
+ */
86
+ healthCheck(): Promise<{
87
+ status: 'ok' | 'degraded';
88
+ redis: boolean;
89
+ }>;
90
+ /**
91
+ * Gracefully shuts down the vault connection.
92
+ */
93
+ close(): Promise<void>;
94
+ private recordAudit;
95
+ }
96
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAc,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC;IAC3D;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,WAAW,GAAG,aAAa,GAAG,iBAAiB,CAAC;IAC3D,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,qBAAa,UAAU;IAKT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;gBAEhB,OAAO,GAAE,YAAiB;IAQvD;;;;;;;;;;OAUG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAsE3E;;;;;;;;OAQG;IACG,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4B5E;;;;;;OAMG;IACG,QAAQ,CAAC,CAAC,EACd,EAAE,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,EACvC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC,GAClD,OAAO,CAAC;QAAE,MAAM,EAAE,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAa5C;;;OAGG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD;;OAEG;IACH,WAAW,IAAI,aAAa,CAAC,UAAU,CAAC;IAIxC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,IAAI,GAAG,UAAU,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAK3E;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,CAAC,WAAW;CAOpB"}
package/dist/guard.js ADDED
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * @file guard.ts
4
+ * @description Core AgentGuard class — the main public API.
5
+ * Orchestrates scanning, tokenization, vault storage, and deanonymization.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.AgentGuard = void 0;
9
+ const uuid_1 = require("uuid");
10
+ const scanner_1 = require("./scanner");
11
+ const vault_1 = require("./vault");
12
+ class AgentGuard {
13
+ constructor(options = {}) {
14
+ this.options = options;
15
+ this.auditLog = [];
16
+ this.vault = new vault_1.TokenVault({
17
+ redisUrl: options.redisUrl,
18
+ ttlSeconds: options.ttlSeconds,
19
+ });
20
+ this.verbose = options.verbose ?? false;
21
+ }
22
+ /**
23
+ * Scans input text, replaces all detected PII with deterministic tokens,
24
+ * and stores the (token → rawValue) mapping in the ephemeral Redis vault.
25
+ *
26
+ * If a `sessionId` is provided, tokens are added to the existing session.
27
+ * Otherwise, a new session UUID is created automatically.
28
+ *
29
+ * @param text Raw text potentially containing PII/credentials
30
+ * @param sessionId Optional existing session to extend
31
+ * @returns Sanitized text, session ID, and a redaction summary
32
+ */
33
+ async anonymize(text, sessionId) {
34
+ const activeSession = sessionId ?? (0, uuid_1.v4)();
35
+ const scan = (0, scanner_1.scanText)(text, this.options.customPatterns);
36
+ if (!scan.hasSensitiveData) {
37
+ return {
38
+ sanitizedText: text,
39
+ sessionId: activeSession,
40
+ redactionSummary: {},
41
+ };
42
+ }
43
+ const uniqueValues = (0, scanner_1.extractUniqueValues)(scan);
44
+ const typeCounters = {};
45
+ const redactedCounts = {};
46
+ const existingVault = await this.vault.getVault(activeSession);
47
+ for (const token of Object.keys(existingVault)) {
48
+ const match = /^\[([A-Z_]+)_(\d+)\]$/.exec(token);
49
+ if (match) {
50
+ typeCounters[match[1]] = Math.max(typeCounters[match[1]] ?? 0, Number(match[2]));
51
+ }
52
+ }
53
+ let sanitizedText = text;
54
+ for (const [rawValue, entityType] of uniqueValues.entries()) {
55
+ // Check if this value already has a token in the session (deduplication)
56
+ const existingToken = await this.vault.lookupToken(activeSession, rawValue);
57
+ let token;
58
+ if (existingToken) {
59
+ token = existingToken;
60
+ }
61
+ else {
62
+ typeCounters[entityType] = (typeCounters[entityType] ?? 0) + 1;
63
+ token = `[${entityType}_${typeCounters[entityType]}]`;
64
+ while (Object.prototype.hasOwnProperty.call(existingVault, token)) {
65
+ typeCounters[entityType]++;
66
+ token = `[${entityType}_${typeCounters[entityType]}]`;
67
+ }
68
+ await this.vault.store(activeSession, token, rawValue);
69
+ existingVault[token] = rawValue;
70
+ redactedCounts[entityType] = (redactedCounts[entityType] ?? 0) + 1;
71
+ }
72
+ // Replace ALL occurrences of rawValue in the text
73
+ sanitizedText = sanitizedText.split(rawValue).join(token);
74
+ }
75
+ const redactionSummary = redactedCounts;
76
+ this.recordAudit({
77
+ timestamp: new Date().toISOString(),
78
+ sessionId: activeSession,
79
+ operation: 'anonymize',
80
+ entityTypesRedacted: Object.keys(redactionSummary),
81
+ });
82
+ if (this.verbose) {
83
+ console.log(`[AgentGuard] Anonymized | session=${activeSession} | types=${JSON.stringify(redactionSummary)}`);
84
+ }
85
+ return { sanitizedText, sessionId: activeSession, redactionSummary };
86
+ }
87
+ /**
88
+ * Re-injects raw PII values into tokenized text before tool execution.
89
+ * Looks up all tokens from the session vault and replaces them with
90
+ * their original values.
91
+ *
92
+ * @param tokenizedText Text containing AgentGuard placeholder tokens
93
+ * @param sessionId The session ID returned from `anonymize()`
94
+ * @returns Text with all tokens replaced by original values
95
+ */
96
+ async deanonymize(tokenizedText, sessionId) {
97
+ const vault = await this.vault.getVault(sessionId);
98
+ let restoredText = tokenizedText;
99
+ let restoredCount = 0;
100
+ for (const [token, rawValue] of Object.entries(vault)) {
101
+ if (restoredText.includes(token)) {
102
+ restoredText = restoredText.split(token).join(rawValue);
103
+ restoredCount++;
104
+ }
105
+ }
106
+ this.recordAudit({
107
+ timestamp: new Date().toISOString(),
108
+ sessionId,
109
+ operation: 'deanonymize',
110
+ totalTokensRestored: restoredCount,
111
+ });
112
+ if (this.verbose) {
113
+ console.log(`[AgentGuard] Deanonymized | session=${sessionId} | tokensRestored=${restoredCount}`);
114
+ }
115
+ return restoredText;
116
+ }
117
+ /**
118
+ * Wraps an async function, automatically anonymizing its string input
119
+ * and deanonymizing the string output. Useful for wrapping LLM calls.
120
+ *
121
+ * @example
122
+ * const safeCall = guard.wrapCall(openai.chat.completions.create.bind(openai));
123
+ */
124
+ async wrapCall(fn, rawInput, sessionId, outputExtractor, outputInjector) {
125
+ const { sanitizedText, sessionId: sid } = await this.anonymize(rawInput, sessionId);
126
+ const result = await fn(sanitizedText);
127
+ if (outputExtractor && outputInjector) {
128
+ const rawOutput = outputExtractor(result);
129
+ const restoredOutput = await this.deanonymize(rawOutput, sid);
130
+ return { result: outputInjector(result, restoredOutput), sessionId: sid };
131
+ }
132
+ return { result, sessionId: sid };
133
+ }
134
+ /**
135
+ * Explicitly destroys a session and all its vault data from Redis.
136
+ * Call this when a user session completes to proactively free memory.
137
+ */
138
+ async destroySession(sessionId) {
139
+ await this.vault.destroySession(sessionId);
140
+ this.recordAudit({
141
+ timestamp: new Date().toISOString(),
142
+ sessionId,
143
+ operation: 'session_destroy',
144
+ });
145
+ if (this.verbose) {
146
+ console.log(`[AgentGuard] Session destroyed | session=${sessionId}`);
147
+ }
148
+ }
149
+ /**
150
+ * Returns the in-memory audit log (no PII, only metadata).
151
+ */
152
+ getAuditLog() {
153
+ return this.auditLog;
154
+ }
155
+ /**
156
+ * Checks Redis connectivity. Use in health-check endpoints.
157
+ */
158
+ async healthCheck() {
159
+ const redis = await this.vault.ping();
160
+ return { status: redis ? 'ok' : 'degraded', redis };
161
+ }
162
+ /**
163
+ * Gracefully shuts down the vault connection.
164
+ */
165
+ async close() {
166
+ await this.vault.close();
167
+ }
168
+ recordAudit(entry) {
169
+ this.auditLog.push(entry);
170
+ // Keep audit log bounded to last 1000 entries
171
+ if (this.auditLog.length > 1000) {
172
+ this.auditLog.shift();
173
+ }
174
+ }
175
+ }
176
+ exports.AgentGuard = AgentGuard;
177
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description Public API surface for the AgentGuard npm package.
4
+ * Import from 'agentguard' for core functionality.
5
+ * Import from 'agentguard/mastra' for Mastra-specific wrappers.
6
+ * Import from 'agentguard/langchain' for LangChain.js integration.
7
+ */
8
+ export { AgentGuard } from './guard';
9
+ export type { GuardOptions, AnonymizeResult, AuditEntry } from './guard';
10
+ export { TokenVault } from './vault';
11
+ export type { VaultOptions } from './vault';
12
+ export { scanText, extractUniqueValues } from './scanner';
13
+ export type { DetectedEntity, ScanResult } from './scanner';
14
+ export { PATTERN_REGISTRY, PATTERN_ORDER } from './patterns';
15
+ export type { PatternKey } from './patterns';
16
+ export { createRequestMiddleware, createResponseMiddleware, createGuardMiddleware, } from './middleware';
17
+ export type { GuardRequest, GuardResponse } from './middleware';
18
+ export { createMastraGuard } from './mastra';
19
+ export type { MastraGuard, MastraStepContext, MastraStepResult } from './mastra';
20
+ export { createLangChainGuard } from './langchain';
21
+ export type { LangChainGuard, LangChainGuardHandler } from './langchain';
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGzE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}