shrike-guard 4.0.3 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,91 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.1.0] - 2026-09-09
4
+
5
+ ### Added
6
+ - **Act-plane scanning: every channel the backend scans is now reachable from
7
+ the SDK.** The backend has scanned eight specialized content types since the
8
+ act plane shipped; this SDK exposed four. `scanCommand` — the highest-volume
9
+ surface, the one an agent hits before every shell-out — had no method at all,
10
+ so the only way to reach it was to hand-roll an HTTP call or route through the
11
+ MCP server. New on `ScanClient`:
12
+ - `scanCommand(command, cwd?)` — shell commands, screened before `exec`.
13
+ Commands are decomposed, so SQL passed to `psql -c`, `mysql -e`, or a
14
+ heredoc is scanned as SQL rather than as opaque shell text.
15
+ - `scanWebSearch(query)` — search queries that acquire attack tooling,
16
+ credentials, or evasion tradecraft.
17
+ - `scanRagContext(chunks, query?)` — retrieved context, the standard carrier
18
+ for indirect prompt injection. Accepts a string or a list of strings.
19
+ - `scanMcpSchema(name, description, inputSchema?)` — a single MCP tool
20
+ definition, for tool poisoning in a `tools/list` response. Screened once at
21
+ registration, not per call.
22
+ - **`content_origin` on `ScanResult`.** Every verdict now says where the scanned
23
+ content came from: `human_prompt`, `agent_output`, `agent_action`, or
24
+ `third_party`. This answers the question a verdict alone cannot — was that my
25
+ prompt, or the agent acting on its own — which decides who a refusal message
26
+ is addressed to. Exported alongside it: the `ContentOrigin` type and
27
+ `attributableToOperator(origin)`, true only for `human_prompt`. Unknown content
28
+ types resolve to `agent_action`, never to `human_prompt`.
29
+ - **Act-plane parity test** (`tests/unit/actplane-parity.test.ts`). Iterates the
30
+ canonical channel list and fails when a channel has no SDK method. The gap
31
+ above went unnoticed because "the backend supports it" and "a customer can
32
+ call it" were two facts with nothing comparing them. This is the comparison.
33
+
34
+ - **Per-request session identity — `sessionId`/`agentId` on `ScanClientOptions`,
35
+ and `forSession()`.** Session identity is the key the backend accumulates
36
+ multi-turn risk against, so it has to mean one unit of work: one agent run,
37
+ one conversation, one user's request. It defaults to a process-wide id, which
38
+ suits a CLI or a worker but not a server serving many end users, where every
39
+ user would share one risk score and one user's refusal would count against
40
+ the next user's action. `forSession()` returns a view that shares the parent's
41
+ rate limiter, so deriving one per request is cheap and cannot multiply your
42
+ rate budget:
43
+
44
+ ```ts
45
+ const guard = new ScanClient({ apiKey: KEY }); // once, at startup
46
+
47
+ app.post('/act', async (req, res) => { // per request
48
+ const scoped = guard.forSession(req.session.id);
49
+ const verdict = await scoped.scanCommand(req.body.command);
50
+ });
51
+ ```
52
+
53
+ The process-wide default is unchanged when nothing is supplied, so
54
+ single-agent callers keep multi-turn correlation; the SDK now warns once when
55
+ it is in force. Silence that with `SHRIKE_SUPPRESS_SESSION_WARNING=1`.
56
+
57
+ `evaluateRotation`'s caller-owned branch now applies: a caller that supplies
58
+ its own session id receives a rotation recommendation rather than a
59
+ module-owned rotation.
60
+
61
+ ### Fixed
62
+ - **`content_origin` was computed by the backend and dropped before the caller.**
63
+ The response sanitizer is an allow-list, and the field was never added to
64
+ `PRESERVED_GOVERNANCE_FIELDS`, so it was serialized by the server and stripped
65
+ one layer before the application. It now survives sanitization.
66
+ - **Session identity could not be configured.** Every scan in a process used one
67
+ generated session id, so a server scanning on behalf of more than one person
68
+ placed all of them in one session. See `sessionId` / `forSession()` above.
69
+
70
+ ## [4.0.5] - 2026-08-31
71
+
72
+ ### Added
73
+ - **Custom endpoint for the Gemini wrapper (`baseUrl`).** `ShrikeGemini` now
74
+ accepts `baseUrl` (applied as `httpOptions.baseUrl` on the underlying
75
+ `@google/genai` client) and a `geminiOptions` passthrough for advanced
76
+ constructor options — parity with the OpenAI and Anthropic wrappers, which
77
+ already forwarded a custom endpoint via `openaiOptions.baseURL` /
78
+ `anthropicOptions.baseURL`. An explicit `geminiOptions.httpOptions` takes
79
+ precedence over `baseUrl`.
80
+ - **Documented local / self-hosted LLM governance.** New README section shows
81
+ governing an OpenAI-compatible local runtime (Ollama, vLLM, LM Studio) by
82
+ forwarding `openaiOptions.baseURL`, plus the Gemini and Anthropic equivalents.
83
+ The plumbing already worked for OpenAI/Anthropic; this documents it and closes
84
+ the Gemini gap.
85
+
86
+ _(Version aligned to 4.0.5 with the Python SDK for this coordinated local-LLM
87
+ parity release; 4.0.4 was not published for the TypeScript SDK.)_
88
+
3
89
  ## [4.0.3] - 2026-07-17
4
90
 
5
91
  ### Fixed
package/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  **Shrike Guard** is a TypeScript SDK for the [Shrike](https://shrikesecurity.com) platform — AI governance for every AI interaction. It wraps OpenAI, Anthropic (Claude), and Google Gemini clients to automatically evaluate all prompts against policy before they reach the LLM. Whether you're governing a customer-facing chatbot, securing developer AI tools, or managing autonomous agent actions — the same 9-layer cognitive pipeline evaluates every interaction.
8
8
 
9
+ It scans two different things. **Prompts and responses**, which is what a guardrail library normally means. And **agent actions** — the shell command, the SQL query, the web search, the retrieved document, the MCP tool definition — screened before they run, which is where an autonomous agent actually causes harm. See [Scanning agent actions](#scanning-agent-actions-shell-commands-sql-web-search-rag-mcp-tools).
10
+
9
11
  ## Features
10
12
 
11
13
  - **Drop-in replacement** for OpenAI, Anthropic, and Gemini clients
@@ -15,6 +17,8 @@
15
17
  - Jailbreak attempts
16
18
  - SQL injection
17
19
  - Path traversal
20
+ - **Pre-execution scanning for agent actions**: shell commands, SQL, file writes, web searches, RAG context, agent-to-agent messages, agent cards, and MCP tool schemas
21
+ - **Content provenance** (`content_origin`): every verdict says whether a human typed it, the model wrote it, the agent is about to do it, or it arrived from outside
18
22
  - **Fail-safe modes**: Defaults to fail-closed (Zero Trust posture); opt into fail-open explicitly when availability outranks enforcement
19
23
  - **CJS + ESM**: Dual build via tsup, works everywhere
20
24
  - **Subpath imports**: `shrike-guard/openai`, `shrike-guard/anthropic`, `shrike-guard/gemini`
@@ -167,31 +171,216 @@ const client = new ShrikeOpenAI({
167
171
  });
168
172
  ```
169
173
 
170
- ## SQL and File Scanning
174
+ ### Sessions: one per unit of work, not one per process
175
+
176
+ Shrike correlates risk across a session. After a refusal, later actions in the
177
+ same session are held until the session recovers. That is the multi-turn
178
+ defence, and it means the session id has to mean one unit of work: one agent
179
+ run, one conversation, one user's request.
180
+
181
+ By default the SDK scans under one id for the whole process. That suits a CLI,
182
+ a worker, or a single agent. It does not suit a server that scans on behalf of
183
+ many end users, because every user then shares one risk score, and one user's
184
+ refusal counts against the next user's action.
185
+
186
+ Build one client at startup and derive a per-request view from it. The view
187
+ shares the rate limiter, so it costs nothing to make one per request and it
188
+ cannot multiply your rate budget:
189
+
190
+ ```typescript
191
+ import { ScanClient } from 'shrike-guard';
192
+
193
+ const guard = new ScanClient({ apiKey: 'shrike-...' }); // once, at startup
194
+
195
+ app.post('/act', async (req, res) => { // per request
196
+ const scoped = guard.forSession(req.session.id);
197
+ const verdict = await scoped.scanCommand(req.body.command);
198
+ if (verdict.refuse_tier !== 'allow') {
199
+ ...
200
+ }
201
+ });
202
+ ```
171
203
 
172
- The OpenAI client also provides standalone scanning for SQL queries and file paths:
204
+ Or pin the identity at construction when one client serves one unit of work:
205
+
206
+ ```typescript
207
+ const client = new ScanClient({ apiKey: 'shrike-...', sessionId: 'job-42', agentId: 'ingest' });
208
+ ```
209
+
210
+ `agentId` is separate on purpose: it names *which agent* a scope is enforced
211
+ against and who an incident is attributed to. Set it when one process drives
212
+ several distinct agents.
213
+
214
+ The SDK warns once per process when it is scanning under the shared default.
215
+ Set `SHRIKE_SUPPRESS_SESSION_WARNING=1` to silence it once you have decided
216
+ the default is what you want.
217
+
218
+ ### Local and self-hosted LLMs
219
+
220
+ Shrike governs the model you point it at — it does not have to be a hosted
221
+ frontier API. Local runtimes like [Ollama](https://ollama.com),
222
+ [vLLM](https://docs.vllm.ai), and LM Studio expose an OpenAI-compatible
223
+ endpoint, so `ShrikeOpenAI` guards them by forwarding a `baseURL` through
224
+ `openaiOptions`:
173
225
 
174
226
  ```typescript
175
227
  import { ShrikeOpenAI } from 'shrike-guard/openai';
176
228
 
229
+ // Ollama serving llama3 locally, governed by Shrike before every call
177
230
  const client = new ShrikeOpenAI({
178
- apiKey: 'sk-...',
231
+ apiKey: 'ollama', // local servers ignore the value
232
+ shrikeApiKey: 'shrike-...', // governance still runs server-side
233
+ openaiOptions: { baseURL: 'http://localhost:11434/v1' },
234
+ });
235
+
236
+ const response = await client.chat.completions.create({
237
+ model: 'llama3',
238
+ messages: [{ role: 'user', content: 'Summarize this ticket…' }],
239
+ });
240
+ ```
241
+
242
+ The same pattern works for Anthropic-compatible gateways via
243
+ `anthropicOptions.baseURL`. For Gemini, use the `baseUrl` option (added in
244
+ 4.0.5), applied as `httpOptions.baseUrl` on the `@google/genai` client:
245
+
246
+ ```typescript
247
+ import { ShrikeGemini } from 'shrike-guard/gemini';
248
+
249
+ const client = new ShrikeGemini({
250
+ apiKey: '…',
179
251
  shrikeApiKey: 'shrike-...',
252
+ baseUrl: 'https://your-gemini-gateway.example',
180
253
  });
254
+ ```
255
+
256
+ The prompt still leaves your process to reach the Shrike backend for scanning;
257
+ the *model call* stays on your local/self-hosted endpoint.
258
+
259
+ ## Scanning agent actions (shell commands, SQL, web search, RAG, MCP tools)
260
+
261
+ Scanning the prompt protects the model. It does not protect the shell. An agent
262
+ that was never told anything malicious can still be talked into running
263
+ `curl … | sh` by a poisoned README, and the prompt scan has no view of that.
181
264
 
182
- // Scan SQL queries for injection attacks
183
- const sqlResult = await client.scanSql('SELECT * FROM users WHERE id = 1');
184
- if (!sqlResult.safe) {
185
- console.log(`SQL threat: ${sqlResult.reason}`);
265
+ `ScanClient` exposes a method per action channel. Call the one that matches
266
+ what the agent is about to do, before it does it:
267
+
268
+ | Channel | Method | Screens for |
269
+ |---|---|---|
270
+ | Shell command | `scanCommand(command, cwd?)` | destructive commands, data exfiltration, credential dumps, embedded SQL injection |
271
+ | SQL query | `scanSql(query, database?, allowDestructive?)` | SQL injection, unauthorized destructive statements |
272
+ | File path | `scanFile(path)` | path traversal, writes outside the working tree |
273
+ | File content | `scanFile(path, content)` | secrets, credentials, PII before they land on disk |
274
+ | Web search | `scanWebSearch(query)` | searches that acquire attack tooling, credentials, or evasion tradecraft |
275
+ | RAG context | `scanRagContext(chunks, query?)` | indirect prompt injection in retrieved documents |
276
+ | Agent message | `scanA2AMessage(message, options?)` | instructions smuggled between agents |
277
+ | Agent card | `scanAgentCard(card, verifySignature?)` | capability misrepresentation in A2A discovery |
278
+ | MCP tool schema | `scanMcpSchema(name, description, inputSchema?)` | tool poisoning in `tools/list` responses |
279
+
280
+ ```typescript
281
+ import { ScanClient } from 'shrike-guard';
282
+
283
+ const scanner = new ScanClient({ apiKey: 'shrike-...' });
284
+
285
+ // Before shelling out
286
+ const cmd = await scanner.scanCommand('psql -c "SELECT * FROM users"', '/srv/app');
287
+ if (!cmd.safe) throw new Error(`Refused: ${cmd.reason}`);
288
+
289
+ // Before querying
290
+ const sql = await scanner.scanSql('SELECT * FROM users WHERE id = $1', 'postgres');
291
+
292
+ // Before writing
293
+ const write = await scanner.scanFile('/tmp/config.py', 'api_key = "sk-..."');
294
+
295
+ // Before searching the web
296
+ const search = await scanner.scanWebSearch('sql injection prevention owasp');
297
+ ```
298
+
299
+ A shell command is not one thing. `scanCommand` decomposes it, so SQL passed to
300
+ `psql -c`, `mysql -e`, or a heredoc is scanned as SQL rather than as an opaque
301
+ string of shell text.
302
+
303
+ ### Indirect prompt injection in RAG pipelines
304
+
305
+ Retrieved chunks are text somebody else wrote. They are the standard carrier for
306
+ indirect prompt injection: the user asks nothing unusual, the document tells the
307
+ model what to do, and the model complies. Scan on the way in, not after the
308
+ model has acted:
309
+
310
+ ```typescript
311
+ const chunks = await vectorStore.similaritySearch(userQuery, 5);
312
+
313
+ const verdict = await scanner.scanRagContext(
314
+ chunks.map((c) => c.pageContent),
315
+ userQuery,
316
+ );
317
+
318
+ if (!verdict.safe) {
319
+ // The retrieved context is hostile, not the user's question.
320
+ console.warn(`Poisoned context: ${verdict.reason}`);
186
321
  }
322
+ ```
323
+
324
+ ### MCP tool poisoning
187
325
 
188
- // Scan file paths for path traversal
189
- const fileResult = await client.scanFile('/app/data/output.csv');
326
+ An MCP tool description is read by the model as guidance. A hostile server can
327
+ put instructions in the `description` field of a tool that never executes, and
328
+ the agent will act on them at registration time. Screen every entry of a
329
+ `tools/list` response from a server you do not control:
190
330
 
191
- // Scan file content for secrets
192
- const contentResult = await client.scanFile('/tmp/config.py', 'api_key = "sk-..."');
331
+ ```typescript
332
+ const { tools } = await mcpClient.listTools();
333
+
334
+ for (const tool of tools) {
335
+ const verdict = await scanner.scanMcpSchema(
336
+ tool.name,
337
+ tool.description ?? '',
338
+ tool.inputSchema,
339
+ );
340
+ if (!verdict.safe) {
341
+ console.warn(`Not registering ${tool.name}: ${verdict.reason}`);
342
+ continue;
343
+ }
344
+ register(tool);
345
+ }
193
346
  ```
194
347
 
348
+ Screening happens once per tool at registration, not on every call.
349
+
350
+ ## Who is answerable: `content_origin`
351
+
352
+ Every verdict carries `content_origin`, which says where the scanned content
353
+ came from. It answers the question a verdict alone cannot: *was that my prompt,
354
+ or the agent acting on its own?*
355
+
356
+ | Value | Meaning |
357
+ |---|---|
358
+ | `human_prompt` | the operator typed it |
359
+ | `agent_output` | the model generated it |
360
+ | `agent_action` | the agent is about to do it (every act-plane channel) |
361
+ | `third_party` | it arrived from outside: a tool result, a retrieved document, a peer agent |
362
+
363
+ ```typescript
364
+ import { attributableToOperator } from 'shrike-guard';
365
+
366
+ const verdict = await scanner.scanRagContext(chunks);
367
+
368
+ if (!verdict.safe) {
369
+ if (attributableToOperator(verdict.content_origin)) {
370
+ showUser(`Your request was blocked: ${verdict.reason}`);
371
+ } else {
372
+ // The agent poisoned its own context. Telling the user "your request
373
+ // was blocked" would be both wrong and unhelpful.
374
+ log.warn('agent-side refusal', verdict.reason);
375
+ retryWithCleanContext();
376
+ }
377
+ }
378
+ ```
379
+
380
+ Unknown content types resolve to `agent_action`, never to `human_prompt`:
381
+ attributing an unattributable action to the operator is the one error that is
382
+ never safe to make by default.
383
+
195
384
  ## Error Handling
196
385
 
197
386
  ```typescript
@@ -257,6 +446,8 @@ export OPENAI_API_KEY="sk-..."
257
446
  export ANTHROPIC_API_KEY="sk-ant-..."
258
447
  export SHRIKE_API_KEY="shrike-..."
259
448
  export SHRIKE_ENDPOINT="https://your-shrike-instance.com"
449
+ export SHRIKE_AGENT_ID="ingest" # names this process's agent; see Sessions
450
+ export SHRIKE_SUPPRESS_SESSION_WARNING=1 # once you have decided the shared session is right
260
451
  ```
261
452
 
262
453
  ## Scope and Limitations
@@ -267,13 +458,23 @@ export SHRIKE_ENDPOINT="https://your-shrike-instance.com"
267
458
  | Multi-modal text content | Image/audio content |
268
459
  | SQL queries | Non-chat API calls |
269
460
  | File paths and content | |
461
+ | Shell commands | |
462
+ | Web search queries | |
463
+ | Retrieved RAG context | |
464
+ | Agent-to-agent messages and agent cards | |
465
+ | MCP tool schemas | |
270
466
 
271
- ### Why Input-Only Scanning?
467
+ ### Why Pre-Execution Scanning?
272
468
 
273
- Shrike Guard focuses on **pre-flight protection** — blocking malicious prompts BEFORE they reach the LLM. This:
469
+ Shrike Guard focuses on **pre-flight protection** — evaluating a prompt before it
470
+ reaches the LLM, and an action before it runs. This:
274
471
  - Prevents prompt injection attacks at the source
275
472
  - Has zero latency impact on LLM responses
276
- - Catches the vast majority of threats at the input layer
473
+ - Puts the decision point before the side effect, where refusing still costs nothing
474
+
475
+ An action already taken cannot be un-taken by detecting it afterwards. That is the
476
+ distinction between this and after-the-fact monitoring: the verdict arrives while
477
+ refusing is still free.
277
478
 
278
479
  ## Other Integration Surfaces
279
480
 
@@ -291,22 +492,39 @@ Shrike Guard is one of several ways to integrate with the Shrike platform:
291
492
  | Scenario | How Shrike Guard Helps |
292
493
  |---|---|
293
494
  | **Customer chatbot** | Wrap your OpenAI/Anthropic client. Every user message scanned for injection before it reaches the model. |
294
- | **Internal RAG pipeline** | Scan retrieved context + user queries for PII leakage and injection attempts. |
295
- | **AI coding assistant** | Scan prompts for proprietary code patterns before they leave your environment. |
296
- | **Agent orchestration** | Scan every tool call and LLM request in your LangChain/CrewAI/AutoGen pipeline. |
297
-
298
- ## Alternatives
299
-
300
- Looking for an AI security SDK? Here's how Shrike Guard compares:
301
-
302
- | Feature | Shrike Guard | Lakera | Prompt Armor |
303
- |---|---|---|---|
304
- | Drop-in OpenAI/Anthropic/Gemini wrapper | Yes | No | No |
305
- | 9-layer cognitive pipeline | Yes | Limited | Limited |
306
- | PII detection + redaction | Yes | Partial | No |
307
- | Session correlation | Yes (Pro+) | No | No |
308
- | Free tier (no API key) | Yes | No | No |
309
- | Open source client | Yes (Apache 2.0) | No | No |
495
+ | **Coding agents that shell out** | `scanCommand` before every `exec`. Destructive commands, exfiltration, and SQL smuggled through `psql -c` are caught before the process starts. |
496
+ | **Internal RAG pipeline** | `scanRagContext` on retrieved chunks for indirect prompt injection, plus PII leakage on the query. |
497
+ | **MCP clients** | `scanMcpSchema` on every `tools/list` entry from a server you do not control, to catch tool poisoning at registration. |
498
+ | **Multi-agent systems** | `scanA2AMessage` and `scanAgentCard` for instructions smuggled between agents. |
499
+ | **Agent orchestration** | Scan every tool call and LLM request in your LangChain/LangGraph/CrewAI/AutoGen pipeline. |
500
+
501
+ ## How This Differs From a Prompt Scanner
502
+
503
+ If you are evaluating TypeScript or JavaScript AI security SDKs, this is the
504
+ distinction worth testing against your own workload.
505
+
506
+ Most guardrail libraries answer one question: **is this text hostile?** They read
507
+ the prompt, and sometimes the response. That is necessary, and Shrike Guard does
508
+ it through a 9-layer cascade with PII redaction and multi-turn session
509
+ correlation.
510
+
511
+ But an autonomous agent does not cause harm by saying something. It causes harm
512
+ by *doing* something: running a command, writing a file, querying a database,
513
+ calling a tool. Shrike Guard scans those too, before they execute:
514
+
515
+ - **A verdict per action channel** — shell commands, SQL, file writes, web
516
+ searches, RAG context, agent-to-agent messages, agent cards, MCP tool schemas.
517
+ See [Scanning agent actions](#scanning-agent-actions-shell-commands-sql-web-search-rag-mcp-tools).
518
+ - **Pre-execution, not after the fact.** The verdict arrives while refusing is
519
+ still free. An action already taken cannot be un-taken by detecting it.
520
+ - **Provenance on every verdict** (`content_origin`) — whether a human typed it,
521
+ the model wrote it, the agent is about to do it, or it arrived from outside.
522
+ - **A governance contract, not just a boolean** — `refuse_tier`
523
+ (allow / warn / require_approval / block), a `recovery` block telling the agent
524
+ how to proceed legitimately, and `session_state`. Present on safe verdicts too.
525
+ - **Drop-in wrappers** for OpenAI, Anthropic, and Gemini, so the prompt-scanning
526
+ half needs no code changes.
527
+ - **Free tier with no API key**, and an Apache 2.0 client you can read.
310
528
 
311
529
  ## License
312
530
 
@@ -1,6 +1,6 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
2
  import { MessageCreateParams, Message } from '@anthropic-ai/sdk/resources/messages';
3
- import { F as FailMode, S as ScanResult } from '../scanner-cxA3LnQR.mjs';
3
+ import { F as FailMode, S as ScanResult } from '../scanner-z9r8BJvO.mjs';
4
4
 
5
5
  /**
6
6
  * ShrikeAnthropic - Drop-in replacement for Anthropic client with security scanning.
@@ -78,8 +78,8 @@ declare class ShrikeAnthropic {
78
78
  * Scan user messages for security threats via backend API.
79
79
  *
80
80
  * Always calls backend - backend handles tier-based scanning:
81
- * - Community tier (no API key): L1-L4 (regex, unicode, encoding, token normalization)
82
- * - Pro tier: L1-L8 (full scan including LLM)
81
+ * - Community tier (no API key): L1-L5 (regex, unicode, malformed, encoding, token/semantic)
82
+ * - Pro tier and above: L1-L9 (adds visual, LLM semantic, response intel, session correlation)
83
83
  */
84
84
  _scanMessages(messages: AnthropicMessage[]): Promise<ScanResult>;
85
85
  /**
@@ -1,6 +1,6 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
2
  import { MessageCreateParams, Message } from '@anthropic-ai/sdk/resources/messages';
3
- import { F as FailMode, S as ScanResult } from '../scanner-cxA3LnQR.js';
3
+ import { F as FailMode, S as ScanResult } from '../scanner-z9r8BJvO.js';
4
4
 
5
5
  /**
6
6
  * ShrikeAnthropic - Drop-in replacement for Anthropic client with security scanning.
@@ -78,8 +78,8 @@ declare class ShrikeAnthropic {
78
78
  * Scan user messages for security threats via backend API.
79
79
  *
80
80
  * Always calls backend - backend handles tier-based scanning:
81
- * - Community tier (no API key): L1-L4 (regex, unicode, encoding, token normalization)
82
- * - Pro tier: L1-L8 (full scan including LLM)
81
+ * - Community tier (no API key): L1-L5 (regex, unicode, malformed, encoding, token/semantic)
82
+ * - Pro tier and above: L1-L9 (adds visual, LLM semantic, response intel, session correlation)
83
83
  */
84
84
  _scanMessages(messages: AnthropicMessage[]): Promise<ScanResult>;
85
85
  /**
@@ -13,12 +13,12 @@ var DEFAULT_FAIL_MODE = "closed" /* CLOSED */;
13
13
  var DEFAULT_ENDPOINT = "https://api.shrikesecurity.com/agent";
14
14
  var SDK_NAME = "typescript";
15
15
  var SESSION_ID = crypto.randomUUID();
16
- var AGENT_ID = process.env.SHRIKE_AGENT_ID || `sdk-ts-${crypto.randomUUID().slice(0, 8)}`;
16
+ var AGENT_ID = `sdk-ts-${crypto.randomUUID().slice(0, 8)}`;
17
17
  function getSessionId() {
18
18
  return SESSION_ID;
19
19
  }
20
20
  function getAgentId() {
21
- return AGENT_ID;
21
+ return process.env.SHRIKE_AGENT_ID || AGENT_ID;
22
22
  }
23
23
 
24
24
  // src/errors.ts
@@ -266,6 +266,13 @@ var PRESERVED_GOVERNANCE_FIELDS = [
266
266
  "recovery",
267
267
  "session_state",
268
268
  "content_type",
269
+ // Provenance: human_prompt / agent_output / agent_action / third_party.
270
+ // An OUTCOME field, not detection attribution — it says who is answerable
271
+ // for the scanned content, which the caller needs in order to know who a
272
+ // refusal is even addressed to. It shipped on the backend in 2cfaa5f and was
273
+ // silently dropped here, because this is an allow-list and nobody added it:
274
+ // a new backend field reaches no caller until it appears in this array.
275
+ "content_origin",
269
276
  "approval_info",
270
277
  "client_session_rotation"
271
278
  ];
@@ -330,7 +337,7 @@ function sanitizeScanResponse(raw) {
330
337
  }
331
338
 
332
339
  // package.json
333
- var version = "4.0.3";
340
+ var version = "4.1.0";
334
341
 
335
342
  // src/version.ts
336
343
  var VERSION = version;
@@ -419,8 +426,8 @@ var ShrikeAnthropic = class {
419
426
  * Scan user messages for security threats via backend API.
420
427
  *
421
428
  * Always calls backend - backend handles tier-based scanning:
422
- * - Community tier (no API key): L1-L4 (regex, unicode, encoding, token normalization)
423
- * - Pro tier: L1-L8 (full scan including LLM)
429
+ * - Community tier (no API key): L1-L5 (regex, unicode, malformed, encoding, token/semantic)
430
+ * - Pro tier and above: L1-L9 (adds visual, LLM semantic, response intel, session correlation)
424
431
  */
425
432
  async _scanMessages(messages) {
426
433
  const userContent = this._extractUserContent(messages);