promptguard-sdk 1.8.0 → 1.10.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.
Files changed (67) hide show
  1. package/README.md +116 -27
  2. package/dist/auto.d.ts +59 -1
  3. package/dist/auto.d.ts.map +1 -1
  4. package/dist/auto.js +84 -6
  5. package/dist/auto.js.map +1 -1
  6. package/dist/client.d.ts +120 -36
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +209 -26
  9. package/dist/client.js.map +1 -1
  10. package/dist/generated/api-types.d.ts +78 -0
  11. package/dist/generated/api-types.d.ts.map +1 -1
  12. package/dist/guard.d.ts +80 -0
  13. package/dist/guard.d.ts.map +1 -1
  14. package/dist/guard.js +147 -28
  15. package/dist/guard.js.map +1 -1
  16. package/dist/index.d.ts +7 -5
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +8 -7
  19. package/dist/index.js.map +1 -1
  20. package/dist/integrations/langchain.d.ts +46 -1
  21. package/dist/integrations/langchain.d.ts.map +1 -1
  22. package/dist/integrations/langchain.js +97 -10
  23. package/dist/integrations/langchain.js.map +1 -1
  24. package/dist/integrations/vercel-ai.d.ts +19 -1
  25. package/dist/integrations/vercel-ai.d.ts.map +1 -1
  26. package/dist/integrations/vercel-ai.js +158 -29
  27. package/dist/integrations/vercel-ai.js.map +1 -1
  28. package/dist/logger.d.ts +31 -0
  29. package/dist/logger.d.ts.map +1 -0
  30. package/dist/logger.js +63 -0
  31. package/dist/logger.js.map +1 -0
  32. package/dist/patches/anthropic.d.ts +16 -0
  33. package/dist/patches/anthropic.d.ts.map +1 -1
  34. package/dist/patches/anthropic.js +69 -32
  35. package/dist/patches/anthropic.js.map +1 -1
  36. package/dist/patches/base.d.ts +17 -1
  37. package/dist/patches/base.d.ts.map +1 -1
  38. package/dist/patches/base.js +98 -7
  39. package/dist/patches/base.js.map +1 -1
  40. package/dist/patches/bedrock.d.ts +35 -2
  41. package/dist/patches/bedrock.d.ts.map +1 -1
  42. package/dist/patches/bedrock.js +195 -23
  43. package/dist/patches/bedrock.js.map +1 -1
  44. package/dist/patches/cohere.d.ts +11 -0
  45. package/dist/patches/cohere.d.ts.map +1 -1
  46. package/dist/patches/cohere.js +76 -2
  47. package/dist/patches/cohere.js.map +1 -1
  48. package/dist/patches/google.d.ts +8 -0
  49. package/dist/patches/google.d.ts.map +1 -1
  50. package/dist/patches/google.js +54 -0
  51. package/dist/patches/google.js.map +1 -1
  52. package/dist/patches/openai.d.ts +26 -5
  53. package/dist/patches/openai.d.ts.map +1 -1
  54. package/dist/patches/openai.js +225 -21
  55. package/dist/patches/openai.js.map +1 -1
  56. package/dist/resolve.d.ts.map +1 -1
  57. package/dist/resolve.js +6 -1
  58. package/dist/resolve.js.map +1 -1
  59. package/dist/retry.d.ts +54 -0
  60. package/dist/retry.d.ts.map +1 -0
  61. package/dist/retry.js +99 -0
  62. package/dist/retry.js.map +1 -0
  63. package/dist/version.d.ts +1 -1
  64. package/dist/version.d.ts.map +1 -1
  65. package/dist/version.js +2 -1
  66. package/dist/version.js.map +1 -1
  67. package/package.json +33 -6
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![npm version](https://img.shields.io/npm/v/promptguard)](https://www.npmjs.com/package/promptguard)
1
+ [![npm version](https://img.shields.io/npm/v/promptguard-sdk)](https://www.npmjs.com/package/promptguard-sdk)
2
2
  [![CI](https://github.com/acebot712/promptguard-node/actions/workflows/ci.yml/badge.svg)](https://github.com/acebot712/promptguard-node/actions/workflows/ci.yml)
3
3
  [![License](https://img.shields.io/github/license/acebot712/promptguard-node)](https://github.com/acebot712/promptguard-node/blob/main/LICENSE)
4
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
@@ -13,18 +13,36 @@ Drop-in security for AI applications. Secure any GenAI app - regardless of frame
13
13
  npm install promptguard-sdk
14
14
  ```
15
15
 
16
+ > The npm package and the import specifier are both **`promptguard-sdk`** — no surprises:
17
+ > ```typescript
18
+ > import { init, PromptGuard } from 'promptguard-sdk';
19
+ > ```
20
+
21
+ Get a free API key at [app.promptguard.co](https://app.promptguard.co).
22
+
23
+ > **The SDK reads `PROMPTGUARD_API_KEY` from the environment; it does not auto-load `.env`.** Use [dotenv](https://www.npmjs.com/package/dotenv) (call `import 'dotenv/config'` first) if you keep secrets in a `.env` file.
24
+
25
+ > **PromptGuard fails open by default** — if the Guard API is unavailable, calls proceed *unscanned* so your app stays up. Set `failOpen: false` to block (fail closed) on a Guard outage instead.
26
+
27
+ > **Module format:** the package currently ships **CommonJS** (`require`) builds. It works in ESM projects via Node's CJS interop (`import { init } from 'promptguard-sdk'` transpiles to a `require`), and in plain CommonJS via `const { init } = require('promptguard-sdk')`.
28
+ >
29
+ > **Running a native-ESM app?** Auto-instrumentation (`init()`) may not cover your LLM calls — see [Limitations: ESM apps](#limitations) before relying on enforce mode.
30
+
16
31
  ## Option 1: Auto-Instrumentation (Recommended)
17
32
 
18
33
  One line secures **every** LLM call in your application - no matter which framework you use.
19
34
 
20
35
  ```typescript
36
+ // All imports first — ES module imports are hoisted and always run before
37
+ // any other statement, regardless of their position in the file.
21
38
  import { init } from 'promptguard-sdk';
39
+ import OpenAI from 'openai';
22
40
 
23
- // Call once at startup - before any LLM SDK usage.
24
- init({ apiKey: 'pg_xxx' });
41
+ // init() runs as the first executed statement and patches the SDK prototypes.
42
+ // Patching works regardless of import order, so you don't need to worry about
43
+ // importing the LLM SDK "after" calling init().
44
+ init({ apiKey: 'pg_live_xxx' });
25
45
 
26
- // Everything below is now secured transparently.
27
- import OpenAI from 'openai';
28
46
  const client = new OpenAI();
29
47
 
30
48
  // This call is automatically scanned by PromptGuard.
@@ -34,35 +52,51 @@ const response = await client.chat.completions.create({
34
52
  });
35
53
  ```
36
54
 
55
+ > `init()` is intentionally quiet on success (SDK logging defaults to `warn`). To confirm which provider SDKs are actually being protected, read `getAppliedPatches()` — the source of truth for what got patched:
56
+ > ```typescript
57
+ > import { init, getAppliedPatches } from 'promptguard-sdk';
58
+ > init({ apiKey: 'pg_live_xxx' });
59
+ > console.log('PromptGuard protecting:', getAppliedPatches()); // e.g. ['openai']
60
+ > ```
61
+ > If this list is empty, nothing is being scanned (see [Limitations: ESM apps](#limitations)). Set `logLevel: 'info'` on `init()` to also emit a one-line confirmation banner.
62
+
37
63
  ### Supported SDKs
38
64
 
39
65
  Auto-instrumentation patches the `create` / `generateContent` / `chat` / `send` methods on:
40
66
 
41
67
  | SDK | npm Package | What Gets Patched |
42
68
  |-----|------------|-------------------|
43
- | OpenAI | `openai` | `chat.completions.create` |
69
+ | OpenAI | `openai` | `chat.completions.create`, `responses.create` (string and message-item `input` forms) |
44
70
  | Anthropic | `@anthropic-ai/sdk` | `messages.create` |
45
71
  | Google Generative AI | `@google/generative-ai` | `generateContent` |
46
72
  | Cohere | `cohere-ai` | `Client.chat` / `ClientV2.chat` |
47
- | AWS Bedrock | `@aws-sdk/client-bedrock-runtime` | `BedrockRuntimeClient.send` (InvokeModel, Converse) |
73
+ | AWS Bedrock | `@aws-sdk/client-bedrock-runtime` | `BedrockRuntimeClient.send` (InvokeModel, InvokeModelWithResponseStream, Converse, ConverseStream) |
48
74
 
49
75
  Any framework built on these SDKs is automatically covered: **LangChain.js**, **Vercel AI SDK**, **AutoGen**, **Semantic Kernel**, and more.
50
76
 
77
+ > Patches attach to the modules resolved via CommonJS `require()`. If `init()` finds **no** patchable SDK it logs a warning (nothing would be scanned). You can also verify at runtime with `getAppliedPatches()`:
78
+ > ```typescript
79
+ > import { init, getAppliedPatches } from 'promptguard-sdk';
80
+ > init({ apiKey: 'pg_live_xxx' });
81
+ > console.log(getAppliedPatches()); // e.g. ['openai', 'anthropic']
82
+ > ```
83
+ > Native-ESM apps: see [Limitations: ESM apps](#limitations).
84
+
51
85
  ### Modes
52
86
 
53
87
  ```typescript
54
88
  // Enforce mode (default) - blocks policy violations.
55
- init({ apiKey: 'pg_xxx', mode: 'enforce' });
89
+ init({ apiKey: 'pg_live_xxx', mode: 'enforce' });
56
90
 
57
91
  // Monitor mode - logs threats but never blocks. Good for shadow deployment.
58
- init({ apiKey: 'pg_xxx', mode: 'monitor' });
92
+ init({ apiKey: 'pg_live_xxx', mode: 'monitor' });
59
93
  ```
60
94
 
61
95
  ### Options
62
96
 
63
97
  ```typescript
64
98
  init({
65
- apiKey: 'pg_xxx', // or set PROMPTGUARD_API_KEY env var
99
+ apiKey: 'pg_live_xxx', // or set PROMPTGUARD_API_KEY env var
66
100
  baseUrl: 'https://...', // or set PROMPTGUARD_BASE_URL env var
67
101
  mode: 'enforce', // 'enforce' | 'monitor'
68
102
  failOpen: true, // allow calls when Guard API is unreachable
@@ -87,7 +121,7 @@ Route LLM traffic through PromptGuard. Just swap your base URL.
87
121
  ```typescript
88
122
  import { PromptGuard } from 'promptguard-sdk';
89
123
 
90
- const pg = new PromptGuard({ apiKey: 'pg_xxx' });
124
+ const pg = new PromptGuard({ apiKey: 'pg_live_xxx' });
91
125
 
92
126
  // Use exactly like the OpenAI client.
93
127
  const response = await pg.chat.completions.create({
@@ -123,7 +157,7 @@ import { PromptGuardCallbackHandler } from 'promptguard-sdk/integrations/langcha
123
157
  import { ChatOpenAI } from '@langchain/openai';
124
158
 
125
159
  const handler = new PromptGuardCallbackHandler({
126
- apiKey: 'pg_xxx',
160
+ apiKey: 'pg_live_xxx',
127
161
  mode: 'enforce',
128
162
  scanResponses: true,
129
163
  });
@@ -143,6 +177,10 @@ const result = await chain.invoke(
143
177
 
144
178
  The callback handler provides rich context to PromptGuard - chain names, tool calls, agent steps - for more precise threat detection.
145
179
 
180
+ > **Redact decisions block in enforce mode:** LangChain callbacks observe calls but cannot rewrite the inputs of the in-flight LLM call, so a `redact` decision cannot be honored — in enforce mode it is escalated to a block (`PromptGuardBlockedError`) rather than silently sending the content the Guard API asked to redact. Use auto-instrumentation or explicit `GuardClient.scan()` calls if you need actual redaction.
181
+ >
182
+ > `scanResponses` defaults to `false` (consistent with `init()` and the Vercel AI middleware) — pass `scanResponses: true` to opt in to output scanning.
183
+
146
184
  ### Vercel AI SDK
147
185
 
148
186
  ```typescript
@@ -153,7 +191,7 @@ import { promptGuardMiddleware } from 'promptguard-sdk/integrations/vercel-ai';
153
191
  const model = wrapLanguageModel({
154
192
  model: openai('gpt-5-nano'),
155
193
  middleware: promptGuardMiddleware({
156
- apiKey: 'pg_xxx',
194
+ apiKey: 'pg_live_xxx',
157
195
  mode: 'enforce',
158
196
  scanResponses: true,
159
197
  }),
@@ -172,13 +210,12 @@ Use the Guard client directly for maximum control:
172
210
  ```typescript
173
211
  import { GuardClient } from 'promptguard-sdk';
174
212
 
175
- const guard = new GuardClient({ apiKey: 'pg_xxx' });
213
+ const guard = new GuardClient({ apiKey: 'pg_live_xxx' });
176
214
 
177
- // Scan before sending to LLM
215
+ // Scan before sending to LLM (options-object form, preferred)
178
216
  const decision = await guard.scan(
179
217
  [{ role: 'user', content: userInput }],
180
- 'input',
181
- 'gpt-5-nano',
218
+ { direction: 'input', model: 'gpt-5-nano' },
182
219
  );
183
220
 
184
221
  if (decision.blocked) {
@@ -191,23 +228,44 @@ if (decision.blocked) {
191
228
  // Scan LLM response
192
229
  const outputDecision = await guard.scan(
193
230
  [{ role: 'assistant', content: llmOutput }],
194
- 'output',
231
+ { direction: 'output' },
195
232
  );
233
+
234
+ // The positional form still works for back-compat:
235
+ // await guard.scan(messages, 'input', 'gpt-5-nano');
196
236
  ```
197
237
 
198
238
  ## Retry Logic
199
239
 
200
- Both `PromptGuard` and `GuardClient` support configurable retry behavior for transient failures:
240
+ Both the proxy client (`PromptGuard`) and the Guard client (`GuardClient`) support configurable retry behavior for transient failures:
201
241
 
202
242
  ```typescript
243
+ // Proxy client
203
244
  const pg = new PromptGuard({
204
- apiKey: 'pg_xxx',
205
- maxRetries: 3, // Number of retry attempts (default: 2)
206
- retryDelay: 500, // Base delay in ms between retries (default: 250)
245
+ apiKey: 'pg_live_xxx',
246
+ maxRetries: 3, // Number of retry attempts (default: 3)
247
+ retryDelay: 500, // Base delay in ms between retries (default: 1000)
207
248
  });
249
+
250
+ // Guard client (standalone scanning)
251
+ const guard = new GuardClient({
252
+ apiKey: 'pg_live_xxx',
253
+ maxRetries: 3, // default: 3
254
+ retryDelay: 500, // default: 1000
255
+ });
256
+ ```
257
+
258
+ Because auto-instrumentation (`init()`) and the framework integrations all scan through `GuardClient`, they retry transient Guard API failures too — pass `maxRetries` / `retryDelay` to `init()`, `PromptGuardCallbackHandler`, or `promptGuardMiddleware`:
259
+
260
+ ```typescript
261
+ init({ apiKey: 'pg_live_xxx', maxRetries: 3, retryDelay: 500 });
208
262
  ```
209
263
 
210
- Retries use exponential backoff starting from `retryDelay`. Only transient errors (network timeouts, 5xx responses) are retried; client errors (4xx) fail immediately.
264
+ Retries use exponential backoff starting from `retryDelay`, with jitter so concurrent clients don't retry in lockstep. A server-provided `Retry-After` header is honored but clamped to 60 seconds. Only transient errors (network timeouts, 429/5xx responses) are retried; client errors (4xx) fail immediately.
265
+
266
+ > **Enforcement is unchanged by retries.** Retrying only affects *transient* Guard failures (network errors, 429/5xx). A real `block` / `redact` decision is terminal and is never retried, and once retries are exhausted a `GuardApiError` is raised so your existing `failOpen` policy governs — exactly as it would with `maxRetries: 0`. Retries never turn a would-be error into an allow.
267
+
268
+ > **Idempotency caveat:** all requests — including `POST`s — are retried on transient failure. If a request reached the server but the response was lost, the retry re-submits it. Chat/completion/scan calls are safe to re-submit, but each attempt may bill separately; set `maxRetries: 0` if you need strict at-most-once semantics.
211
269
 
212
270
  ## Embeddings
213
271
 
@@ -250,18 +308,18 @@ if (!validation.allowed) {
250
308
  ## Red Team Testing
251
309
 
252
310
  ```typescript
253
- const pg = new PromptGuard({ apiKey: 'pg_xxx' });
311
+ const pg = new PromptGuard({ apiKey: 'pg_live_xxx' });
254
312
 
255
313
  // Run the autonomous red team agent (LLM-powered mutation)
256
314
  const report = await pg.redteam.runAutonomous({
257
315
  budget: 200,
258
- target_preset: 'support_bot:strict',
316
+ targetPreset: 'support_bot:strict', // snake_case `target_preset` also accepted
259
317
  });
260
- console.log(`Grade: ${report.grade}, Bypass rate: ${(report.bypass_rate * 100).toFixed(0)}%`);
318
+ console.log(`Grade: ${report.grade}, Bypass rate: ${(report.bypassRate * 100).toFixed(0)}%`);
261
319
 
262
320
  // Get Attack Intelligence stats
263
321
  const stats = await pg.redteam.intelligenceStats();
264
- console.log(`Total patterns: ${stats.total_patterns}`);
322
+ console.log(`Total patterns: ${stats.totalPatterns}`);
265
323
  ```
266
324
 
267
325
  ## Configuration
@@ -274,6 +332,35 @@ console.log(`Total patterns: ${stats.total_patterns}`);
274
332
  | `failOpen` | - | `true` | Allow calls when Guard API is unreachable |
275
333
  | `scanResponses` | - | `false` | Also scan LLM responses |
276
334
  | `timeout` | - | `10000` | HTTP timeout in milliseconds |
335
+ | `logLevel` | - | `"warn"` | SDK log verbosity: `"debug"`, `"info"`, `"warn"`, `"error"`, `"silent"` |
336
+ | `silent` | - | `false` | Shorthand for `logLevel: "silent"` |
337
+
338
+ > The proxy client (`PromptGuard`) talks to the `/api/v1/proxy` endpoints. If you set `baseUrl` / `PROMPTGUARD_BASE_URL` to `.../api/v1` (without `/proxy`), the SDK appends the `/proxy` suffix for you, so requests still land on the proxy.
339
+ >
340
+ > **Security:** the SDK sends your API key (and, in proxy mode, your prompt content) to whatever `PROMPTGUARD_BASE_URL` points at. Self-hosting is supported, so only point it at a host you trust.
341
+ >
342
+ > **Logging is process-global:** `logLevel` / `silent` set a single shared log level for the whole SDK. If several integrations or `init()` calls pass different values, the most recently constructed one wins. Use `setLogLevel()` directly for fine-grained control.
343
+
344
+ ## Limitations
345
+
346
+ ### ESM apps (auto-instrumentation)
347
+
348
+ Auto-instrumentation (`init()`) patches the provider modules that Node resolves via **CommonJS `require()`**. If your application runs as **native ESM** (`"type": "module"` in package.json, or `.mjs` files) and a provider ships separate ESM builds, the module instances your code `import`s can be *different objects* from the ones the SDK patched — the **dual-package hazard**. In that case your LLM calls bypass the patches entirely and **enforce mode silently protects nothing**.
349
+
350
+ What to do:
351
+
352
+ - **Verify at runtime** with `getAppliedPatches()` after `init()` — and note that a patch being listed proves the CJS build was patched, not that your ESM imports go through it. `init()` also logs a warning when it applies zero patches.
353
+ - **Prefer the ESM-safe APIs**, which don't rely on module patching:
354
+ - LangChain: `PromptGuardCallbackHandler` (`promptguard-sdk/integrations/langchain`)
355
+ - Vercel AI SDK: `promptGuardMiddleware` (`promptguard-sdk/integrations/vercel-ai`)
356
+ - Any framework: explicit `GuardClient.scan()` calls around your LLM invocations
357
+ - Transpiled-to-CJS TypeScript apps (the common `tsc`/`ts-node` default) are **not** affected — their `import`s compile to `require()` and hit the patched modules.
358
+
359
+ ### Other limitations
360
+
361
+ - **Streaming responses are not output-scanned.** With auto-instrumentation and `scanResponses: true`, streaming calls (`stream: true`, Bedrock `ConverseStreamCommand`, etc.) skip the output scan — the stream is consumed incrementally by your code and cannot be buffered without breaking stream semantics. Input scanning still applies. A `debug`-level log is emitted when the output scan is skipped. The same applies to the Vercel AI SDK middleware: `streamText()` outputs are not scanned (`wrapStream` logs the skip); `generateText()` outputs are.
362
+ - **OpenAI `APIPromise` helpers are not preserved by auto-instrumentation.** Patched methods return a plain `Promise`, so `.withResponse()` / `.asResponse()` on `client.chat.completions.create(...)` are unavailable while `init()` is active. `await` the call and use the plain result instead.
363
+ - **Proxy client streaming:** `pg.chat.completions.create({ stream: true })` is rejected with a clear error — streaming is not yet supported by the proxy client.
277
364
 
278
365
  ## Error Handling
279
366
 
@@ -314,6 +401,8 @@ import type {
314
401
  } from 'promptguard-sdk';
315
402
  ```
316
403
 
404
+ > **Response fields are camelCase — with one deliberate exception.** The SDK's normalized response objects (the `security`, `redteam`, `agent`, `scrape`, and `guard` namespaces) use camelCase field names (e.g. `report.bypassRate`, `stats.totalPatterns`, `validation.riskScore`, `result.threatsDetected`) regardless of the snake_case wire format — consistent with `GuardDecision` and `SecurityScanResult`. The **OpenAI-compatible** responses (`chat.completions`, `completions`, `embeddings`, i.e. `ChatCompletionResponse`) are the exception: they intentionally preserve OpenAI's snake_case shape (`choices[].finish_reason`, `usage.prompt_tokens`, …) so they stay drop-in compatible with the `openai` client. Request options are camelCase throughout (`maxTokens`, `targetPreset`), including `GuardContext` (`chainName`, `agentId`, `sessionId`, `toolCalls`).
405
+
317
406
  ## Links
318
407
 
319
408
  - [Documentation](https://docs.promptguard.co)
package/dist/auto.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * ```ts
10
10
  * import { init } from 'promptguard-sdk/auto';
11
11
  *
12
- * init({ apiKey: 'pg_xxx' });
12
+ * init({ apiKey: 'pg_live_xxx' });
13
13
  *
14
14
  * // Everything below is now secured transparently.
15
15
  * import OpenAI from 'openai';
@@ -18,6 +18,7 @@
18
18
  * ```
19
19
  */
20
20
  import { GuardClient } from "./guard";
21
+ import { type LogLevel } from "./logger";
21
22
  export interface InitOptions {
22
23
  apiKey?: string;
23
24
  baseUrl?: string;
@@ -29,11 +30,68 @@ export interface InitOptions {
29
30
  scanResponses?: boolean;
30
31
  /** HTTP timeout in ms for Guard API calls (default: `10000`). */
31
32
  timeout?: number;
33
+ /**
34
+ * Retry attempts for transient Guard API failures (network errors and
35
+ * 429/5xx responses) before the `failOpen` policy governs. Default: `3`.
36
+ * Retries never change the eventual decision — see {@link GuardClientConfig}.
37
+ */
38
+ maxRetries?: number;
39
+ /** Base delay in ms between Guard API retries (exponential backoff). Default: `1000`. */
40
+ retryDelay?: number;
41
+ /**
42
+ * SDK log verbosity (default: `"warn"`). Set to `"silent"` to suppress all
43
+ * SDK logging, or `"info"`/`"debug"` for the init banner and more detail.
44
+ * NOTE: this sets the **process-global** SDK log level (shared with the
45
+ * framework integrations); the most recent setter wins.
46
+ */
47
+ logLevel?: LogLevel;
48
+ /** Convenience shorthand for `logLevel: "silent"`. */
49
+ silent?: boolean;
32
50
  }
51
+ /**
52
+ * Initialise PromptGuard auto-instrumentation.
53
+ *
54
+ * Patches the provider SDKs (`openai`, `@anthropic-ai/sdk`,
55
+ * `@google/generative-ai`, `cohere-ai`, `@aws-sdk/client-bedrock-runtime`)
56
+ * so every LLM call they make is scanned by the Guard API.
57
+ *
58
+ * **Limitation — ESM apps (dual-package hazard):** patches are applied to the
59
+ * module instances resolved via CommonJS `require()`. If your application runs
60
+ * as native ESM (`"type": "module"` / `.mjs`) and the provider ships separate
61
+ * ESM builds, your `import`ed module instances can be *different objects* from
62
+ * the patched CJS ones — in that case auto-instrumentation silently protects
63
+ * nothing. Use {@link getAppliedPatches} as a runtime canary to verify the
64
+ * providers you use were actually patched, and prefer the ESM-safe
65
+ * alternatives: the LangChain callback handler
66
+ * (`promptguard-sdk/integrations/langchain`), the Vercel AI SDK middleware
67
+ * (`promptguard-sdk/integrations/vercel-ai`), or explicit `GuardClient.scan()`
68
+ * calls. See README "Limitations: ESM apps".
69
+ *
70
+ * When zero patches could be applied, a warning is logged so a
71
+ * misconfiguration never fails silently.
72
+ */
33
73
  export declare function init(options?: InitOptions): void;
34
74
  export declare function shutdown(): void;
35
75
  export declare function getGuardClient(): GuardClient | null;
36
76
  export declare function getMode(): "enforce" | "monitor";
37
77
  export declare function isFailOpen(): boolean;
38
78
  export declare function shouldScanResponses(): boolean;
79
+ /**
80
+ * Runtime canary: the names of the auto-instrumentation patches that were
81
+ * actually applied by {@link init} (e.g. `["openai", "anthropic"]`).
82
+ *
83
+ * Because patches attach to the CJS-resolved provider modules, an ESM app can
84
+ * call `init()` successfully and still be unprotected (see the dual-package
85
+ * hazard note on {@link init}). Assert on this at startup when you rely on
86
+ * enforce mode:
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * init({ apiKey: "pg_live_xxx" });
91
+ * if (!getAppliedPatches().includes("openai")) {
92
+ * throw new Error("PromptGuard did not patch the openai SDK");
93
+ * }
94
+ * ```
95
+ */
96
+ export declare function getAppliedPatches(): string[];
39
97
  //# sourceMappingURL=auto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAkBrC,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kDAAkD;IAClD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,IAAI,CAuBpD;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAU/B;AAMD,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED,wBAAgB,OAAO,IAAI,SAAS,GAAG,SAAS,CAE/C;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C"}
1
+ {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,UAAU,CAAA;AAkB7D,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kDAAkD;IAClD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,IAAI,CAqCpD;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAU/B;AAMD,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED,wBAAgB,OAAO,IAAI,SAAS,GAAG,SAAS,CAE/C;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C"}
package/dist/auto.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * ```ts
11
11
  * import { init } from 'promptguard-sdk/auto';
12
12
  *
13
- * init({ apiKey: 'pg_xxx' });
13
+ * init({ apiKey: 'pg_live_xxx' });
14
14
  *
15
15
  * // Everything below is now secured transparently.
16
16
  * import OpenAI from 'openai';
@@ -25,7 +25,9 @@ exports.getGuardClient = getGuardClient;
25
25
  exports.getMode = getMode;
26
26
  exports.isFailOpen = isFailOpen;
27
27
  exports.shouldScanResponses = shouldScanResponses;
28
+ exports.getAppliedPatches = getAppliedPatches;
28
29
  const guard_1 = require("./guard");
30
+ const logger_1 = require("./logger");
29
31
  const resolve_1 = require("./resolve");
30
32
  // ---------------------------------------------------------------------------
31
33
  // Global state
@@ -35,22 +37,58 @@ let mode = "enforce";
35
37
  let failOpen = true;
36
38
  let scanResponses = false;
37
39
  const appliedPatches = [];
40
+ /**
41
+ * Initialise PromptGuard auto-instrumentation.
42
+ *
43
+ * Patches the provider SDKs (`openai`, `@anthropic-ai/sdk`,
44
+ * `@google/generative-ai`, `cohere-ai`, `@aws-sdk/client-bedrock-runtime`)
45
+ * so every LLM call they make is scanned by the Guard API.
46
+ *
47
+ * **Limitation — ESM apps (dual-package hazard):** patches are applied to the
48
+ * module instances resolved via CommonJS `require()`. If your application runs
49
+ * as native ESM (`"type": "module"` / `.mjs`) and the provider ships separate
50
+ * ESM builds, your `import`ed module instances can be *different objects* from
51
+ * the patched CJS ones — in that case auto-instrumentation silently protects
52
+ * nothing. Use {@link getAppliedPatches} as a runtime canary to verify the
53
+ * providers you use were actually patched, and prefer the ESM-safe
54
+ * alternatives: the LangChain callback handler
55
+ * (`promptguard-sdk/integrations/langchain`), the Vercel AI SDK middleware
56
+ * (`promptguard-sdk/integrations/vercel-ai`), or explicit `GuardClient.scan()`
57
+ * calls. See README "Limitations: ESM apps".
58
+ *
59
+ * When zero patches could be applied, a warning is logged so a
60
+ * misconfiguration never fails silently.
61
+ */
38
62
  function init(options = {}) {
63
+ if (options.silent) {
64
+ (0, logger_1.setLogLevel)("silent");
65
+ }
66
+ else if (options.logLevel) {
67
+ (0, logger_1.setLogLevel)(options.logLevel);
68
+ }
39
69
  const { apiKey, baseUrl } = (0, resolve_1.resolveCredentials)(options.apiKey, options.baseUrl);
40
70
  const resolvedMode = options.mode ?? "enforce";
41
71
  if (resolvedMode !== "enforce" && resolvedMode !== "monitor") {
42
- throw new Error("mode must be 'enforce' or 'monitor'");
72
+ throw new Error(`mode must be 'enforce' or 'monitor', got '${resolvedMode}'`);
43
73
  }
44
74
  guardClient = new guard_1.GuardClient({
45
75
  apiKey,
46
76
  baseUrl,
47
77
  timeout: options.timeout ?? 10000,
78
+ maxRetries: options.maxRetries,
79
+ retryDelay: options.retryDelay,
48
80
  });
49
81
  mode = resolvedMode;
50
82
  failOpen = options.failOpen ?? true;
51
83
  scanResponses = options.scanResponses ?? false;
52
84
  applyPatches();
53
- console.log(`[promptguard] auto-instrumentation initialised (mode=${mode}, fail_open=${failOpen})`);
85
+ // One-line confirmation of which provider SDKs are actually protected.
86
+ // Suppressed by default (info level); set logLevel: "info" to see it, or
87
+ // read the same list programmatically via getAppliedPatches(). When ZERO
88
+ // patches applied, applyPatches() already logged a warn (visible by default).
89
+ const protecting = getAppliedPatches();
90
+ logger_1.logger.info(`auto-instrumentation initialised (mode=${mode}, fail_open=${failOpen}) — ` +
91
+ `protecting: ${protecting.length ? protecting.join(", ") : "none"}`);
54
92
  }
55
93
  function shutdown() {
56
94
  for (const patch of appliedPatches) {
@@ -79,6 +117,26 @@ function isFailOpen() {
79
117
  function shouldScanResponses() {
80
118
  return scanResponses;
81
119
  }
120
+ /**
121
+ * Runtime canary: the names of the auto-instrumentation patches that were
122
+ * actually applied by {@link init} (e.g. `["openai", "anthropic"]`).
123
+ *
124
+ * Because patches attach to the CJS-resolved provider modules, an ESM app can
125
+ * call `init()` successfully and still be unprotected (see the dual-package
126
+ * hazard note on {@link init}). Assert on this at startup when you rely on
127
+ * enforce mode:
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * init({ apiKey: "pg_live_xxx" });
132
+ * if (!getAppliedPatches().includes("openai")) {
133
+ * throw new Error("PromptGuard did not patch the openai SDK");
134
+ * }
135
+ * ```
136
+ */
137
+ function getAppliedPatches() {
138
+ return appliedPatches.map((p) => p.name);
139
+ }
82
140
  // ---------------------------------------------------------------------------
83
141
  // Patch orchestration
84
142
  // ---------------------------------------------------------------------------
@@ -94,12 +152,32 @@ function applyPatches() {
94
152
  try {
95
153
  const patchModule = require(mod.path);
96
154
  if (patchModule.apply()) {
97
- appliedPatches.push({ name: mod.name, revert: patchModule.revert });
155
+ // apply() is idempotent per patch module, so a second init() without
156
+ // shutdown() reports "already patched" success. Skip the push when
157
+ // the patch is already tracked — a duplicate entry would make the
158
+ // getAppliedPatches() canary lie and call revert() twice on
159
+ // shutdown().
160
+ if (!appliedPatches.some((p) => p.name === mod.name)) {
161
+ appliedPatches.push({ name: mod.name, revert: patchModule.revert });
162
+ }
163
+ }
164
+ else {
165
+ logger_1.logger.debug(`${mod.name} patch not applied: SDK not resolvable via require() ` +
166
+ `(not installed, or only reachable as ESM)`);
98
167
  }
99
168
  }
100
- catch {
101
- // SDK not installed or incompatible - skip silently
169
+ catch (err) {
170
+ logger_1.logger.debug(`${mod.name} patch not applied: ${err instanceof Error ? err.message : String(err)}`);
102
171
  }
103
172
  }
173
+ if (appliedPatches.length === 0) {
174
+ logger_1.logger.warn("init() applied ZERO auto-instrumentation patches — no supported LLM SDK was " +
175
+ "resolvable via CommonJS require(), so NO calls are being scanned. If your app " +
176
+ 'runs as native ESM ("type": "module" / .mjs), auto-instrumentation cannot see ' +
177
+ "the module instances your code imports (dual-package hazard). Use the ESM-safe " +
178
+ "integrations instead: the LangChain callback handler, the Vercel AI SDK " +
179
+ "middleware, or explicit GuardClient.scan() calls. " +
180
+ 'See README "Limitations: ESM apps". Set logLevel: "debug" for per-SDK reasons.');
181
+ }
104
182
  }
105
183
  //# sourceMappingURL=auto.js.map
package/dist/auto.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"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"}