@silmaril-security/sdk 0.7.4 → 0.7.6

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,7 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.6
4
+
5
+ - Send typed governance resources in the runtime's top-level `resource` and
6
+ aligned `resources` fields, preserving compatibility attribution metadata.
7
+ - Reject invalid canonical resource references before submitting a request.
8
+ Caller-supplied agent metadata remains attribution, not authenticated identity.
9
+
3
10
  All notable changes to the Silmaril Firewall TypeScript SDK are documented here.
4
11
 
12
+ ## 0.7.5 - 2026-10-12
13
+
14
+ - Correct the mode documentation for current mode-less Gateway responses and
15
+ Python/Rust wire contracts: explicit local client, call, and adapter overrides
16
+ remain authoritative. With no request or response mode, direct TypeScript
17
+ calls return decisions without local enforcement while the Vercel, LangChain,
18
+ and Deep Agents adapters retain their existing Block fallback behavior.
19
+
5
20
  ## 0.7.4 - 2026-10-09
6
21
 
7
22
  - Ship the corrected README with its reproducible install command pinned to
package/README.md CHANGED
@@ -18,8 +18,8 @@ This SDK provides the low-level TypeScript interface for that workflow:
18
18
  - Classify user input, tool calls, tool responses, model output, or system
19
19
  prompt content.
20
20
  - Preserve hook and tool-name context for more accurate decisions.
21
- - Honor backend threat and governance decisions and effective Shadow, Warn, or
22
- Block behavior in adapters.
21
+ - Honor backend threat and governance decisions while adapters apply their
22
+ effective Shadow, Warn, or Block behavior.
23
23
  - Send each complete sanitized event in one request.
24
24
  - On individual `classify()` calls, preserve exact `metadata.conversationId` as sequence identity and add one event ID.
25
25
  - Retry API rate-limit responses.
@@ -34,10 +34,10 @@ This SDK is distributed as an npm package.
34
34
  npm install @silmaril-security/sdk
35
35
  ```
36
36
 
37
- For reproducible installs, pin the current tagged release:
37
+ After publication, pin version 0.7.6 for reproducible installs:
38
38
 
39
39
  ```sh
40
- npm install @silmaril-security/sdk@0.7.4
40
+ npm install @silmaril-security/sdk@0.7.6
41
41
  ```
42
42
 
43
43
  Requires Node 20 or later.
@@ -241,7 +241,7 @@ interface FirewallOptions {
241
241
  apiKey: string; // required
242
242
  apiUrl: string; // required
243
243
  timeoutMs?: number; // default: 10000 ms, max 2147483647
244
- mode?: "shadow" | "warn" | "block"; // omitted uses backend configuration
244
+ mode?: "shadow" | "warn" | "block"; // explicit local client default
245
245
  shadowMode?: boolean; // deprecated legacy mapping
246
246
  }
247
247
  ```
@@ -265,15 +265,31 @@ threshold.
265
265
 
266
266
  ## Modes
267
267
 
268
- Use `shadow`, `warn`, or `block` only when a request needs to override the
269
- backend-configured mode. Shadow and Warn preserve the framework flow; Block
270
- throws `FirewallBlockedException` for a malicious decision. Current backends
271
- return the effective mode on every result.
272
-
273
- During a rolling upgrade, an explicit request mode remains authoritative if a
274
- legacy or mixed-version backend omits or disagrees about `mode`. When both the
275
- request and response omit it, `BlockResult.mode` remains unset; adapters retain
276
- their pre-0.6 default behavior without falsely reporting a backend Block mode.
268
+ Mode controls local SDK and adapter handling; the backend remains authoritative
269
+ for threat decisions and thresholds. Explicitly set `mode: "shadow"` or
270
+ `mode: "warn"` when you want observation without adapter enforcement. Set
271
+ `mode: "block"` when you want Block handling.
272
+
273
+ A per-call or per-adapter mode takes precedence over the client mode. Any
274
+ explicit mode is locally authoritative even when the response omits `mode` or
275
+ reports a conflicting value. If no explicit mode is requested, a future
276
+ compatible service can supply the result mode. This is forward compatibility,
277
+ not a statement that current Firewall services return mode.
278
+
279
+ Current Gateway responses omit `mode`, as do the current Python and Rust
280
+ classify wire contracts; callers cannot currently rely on backend mode
281
+ configuration. When a TypeScript request and response both omit mode, direct
282
+ `classify()` and `classifyBatch()` calls return the backend decision without
283
+ enforcing it, and `BlockResult.mode` remains unset. The Vercel middleware and
284
+ LangChain handler retain their existing Block fallback for successful mode-less
285
+ malicious or governance-block decisions. LangChain, Vercel input scanning, and
286
+ Vercel non-streamed output scanning throw `FirewallBlockedException`; Vercel
287
+ streamed-output scanning instead emits an error part carrying that exception in
288
+ the stream. Deep Agents applies its existing Block fallback to a successful
289
+ mode-less decision through its safe replacement and blocked-attempt behavior;
290
+ classification errors remain fail-open by default. In the Python and Go SDKs,
291
+ direct calls retain their existing Block enforcement when both the request and
292
+ response omit mode.
277
293
 
278
294
  ```ts
279
295
  import { wrapLanguageModel, generateText } from "ai";
@@ -621,3 +637,15 @@ npm publish --access public
621
637
 
622
638
  This SDK is distributed under the license terms in [LICENSE](LICENSE). It is not
623
639
  permissive open source.
640
+
641
+ ## Governance resource requests
642
+
643
+ Typed governance resources are sent in the classify request's top-level
644
+ `resource` field or aligned nullable `resources` array for batch calls. Resource
645
+ IDs must be non-empty; MCP tools also require their canonical server parent ID.
646
+ The compatibility metadata copy remains attribution only. Caller agent metadata
647
+ does not supply an authenticated principal.
648
+
649
+ This source prepares version 0.7.6. Its registry install command becomes
650
+ available after the authorized main-branch release workflow publishes the package.
651
+ Confirm registry publication before installing this version.
@@ -12,7 +12,7 @@ import * as _langchain_core_utils_types from '@langchain/core/utils/types';
12
12
  import * as langchain from 'langchain';
13
13
  import { CreateAgentParams } from 'langchain';
14
14
  import * as _langchain_core_tools from '@langchain/core/tools';
15
- import { h as FirewallMode, b as ClassifyEvent, e as Firewall } from '../firewall-CRNFqgMa.cjs';
15
+ import { h as FirewallMode, b as ClassifyEvent, e as Firewall } from '../firewall-CRADRBLY.cjs';
16
16
 
17
17
  declare const SAFE_TOOL_MESSAGE = "Silmaril Firewall blocked this tool interaction. Choose a different safe action.";
18
18
  declare const SAFE_FINAL_MESSAGE = "Silmaril Firewall stopped this request after repeated unsafe actions.";
@@ -12,7 +12,7 @@ import * as _langchain_core_utils_types from '@langchain/core/utils/types';
12
12
  import * as langchain from 'langchain';
13
13
  import { CreateAgentParams } from 'langchain';
14
14
  import * as _langchain_core_tools from '@langchain/core/tools';
15
- import { h as FirewallMode, b as ClassifyEvent, e as Firewall } from '../firewall-CRNFqgMa.js';
15
+ import { h as FirewallMode, b as ClassifyEvent, e as Firewall } from '../firewall-CRADRBLY.js';
16
16
 
17
17
  declare const SAFE_TOOL_MESSAGE = "Silmaril Firewall blocked this tool interaction. Choose a different safe action.";
18
18
  declare const SAFE_FINAL_MESSAGE = "Silmaril Firewall stopped this request after repeated unsafe actions.";
@@ -1,5 +1,5 @@
1
1
  import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
2
- import { e as Firewall, L as LangChainAdapterOptions } from '../firewall-CRNFqgMa.cjs';
2
+ import { e as Firewall, L as LangChainAdapterOptions } from '../firewall-CRADRBLY.cjs';
3
3
 
4
4
  declare function createLangChainHandler(firewall: Firewall, options?: LangChainAdapterOptions): Promise<BaseCallbackHandler>;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
2
- import { e as Firewall, L as LangChainAdapterOptions } from '../firewall-CRNFqgMa.js';
2
+ import { e as Firewall, L as LangChainAdapterOptions } from '../firewall-CRADRBLY.js';
3
3
 
4
4
  declare function createLangChainHandler(firewall: Firewall, options?: LangChainAdapterOptions): Promise<BaseCallbackHandler>;
5
5
 
@@ -1 +1 @@
1
- export { g as FirewallMiddleware, Q as createMiddleware } from '../firewall-CRNFqgMa.cjs';
1
+ export { g as FirewallMiddleware, Q as createMiddleware } from '../firewall-CRADRBLY.cjs';
@@ -1 +1 @@
1
- export { g as FirewallMiddleware, Q as createMiddleware } from '../firewall-CRNFqgMa.js';
1
+ export { g as FirewallMiddleware, Q as createMiddleware } from '../firewall-CRADRBLY.js';
@@ -74,11 +74,15 @@ type Prediction = "BENIGN" | "MALICIOUS";
74
74
  type FirewallMode = "shadow" | "warn" | "block";
75
75
  type GovernanceAction = "allow" | "block";
76
76
  type GovernanceResourceKind = "agent" | "tool" | "mcp_server" | "mcp_tool" | "plugin" | "skill" | "extension";
77
- interface GovernanceResource {
78
- readonly kind: GovernanceResourceKind;
79
- readonly id?: string;
80
- readonly parentId?: string;
81
- }
77
+ type GovernanceResource = {
78
+ readonly kind: "mcp_tool";
79
+ readonly id: string;
80
+ readonly parentId: string;
81
+ } | {
82
+ readonly kind: Exclude<GovernanceResourceKind, "mcp_tool">;
83
+ readonly id: string;
84
+ readonly parentId?: never;
85
+ };
82
86
  interface GovernanceContext {
83
87
  readonly agent?: string;
84
88
  readonly resource?: GovernanceResource;
@@ -92,7 +96,7 @@ interface BlockResult {
92
96
  readonly prediction: Prediction;
93
97
  readonly score: number;
94
98
  readonly threshold: number;
95
- /** Omitted only when a legacy backend did not return mode and no override was requested. */
99
+ /** Omitted when neither an explicit local override nor the response supplies a mode. */
96
100
  readonly mode?: FirewallMode;
97
101
  readonly primaryOutcome?: PrimaryOutcome;
98
102
  readonly outcomeScores?: Readonly<Partial<Record<HarmfulOutcome, number>>>;
@@ -104,12 +108,14 @@ interface FirewallOptions {
104
108
  apiKey: string;
105
109
  apiUrl: string;
106
110
  timeoutMs?: number;
111
+ /** Explicit local client default; a per-call or per-adapter mode takes precedence. */
107
112
  mode?: FirewallMode;
108
113
  /** @deprecated Use mode: "shadow" or mode: "block". */
109
114
  shadowMode?: boolean;
110
115
  }
111
116
  type ClassificationMetadata = Readonly<Record<string, unknown>>;
112
117
  interface ClassifyOptions {
118
+ /** Explicit local per-call override; authoritative over any response mode. */
113
119
  mode?: FirewallMode;
114
120
  hook?: HookLabel;
115
121
  toolName?: string;
@@ -123,6 +129,7 @@ interface ClassifyOptions {
123
129
  signal?: AbortSignal;
124
130
  }
125
131
  interface ClassifyBatchOptions {
132
+ /** Explicit local per-call override; authoritative over every response mode. */
126
133
  mode?: FirewallMode;
127
134
  hooks?: readonly HookLabel[];
128
135
  toolNames?: readonly (string | undefined)[];
@@ -143,6 +150,7 @@ interface LangChainAdapterOptions {
143
150
  includeTool?: boolean;
144
151
  failOpen?: boolean;
145
152
  logger?: (message: string, error: unknown) => void;
153
+ /** Explicit local adapter override; takes precedence over the client and response modes. */
146
154
  mode?: FirewallMode;
147
155
  /** @deprecated Use mode: "shadow" or mode: "block". */
148
156
  shadowMode?: boolean;
@@ -175,6 +183,7 @@ interface MiddlewareOptions {
175
183
  scanInput?: boolean;
176
184
  scanOutput?: boolean;
177
185
  scanToolCalls?: boolean;
186
+ /** Explicit local adapter override; takes precedence over the client and response modes. */
178
187
  mode?: FirewallMode;
179
188
  /** @deprecated Use mode: "shadow" or mode: "block". */
180
189
  shadowMode?: boolean;
@@ -74,11 +74,15 @@ type Prediction = "BENIGN" | "MALICIOUS";
74
74
  type FirewallMode = "shadow" | "warn" | "block";
75
75
  type GovernanceAction = "allow" | "block";
76
76
  type GovernanceResourceKind = "agent" | "tool" | "mcp_server" | "mcp_tool" | "plugin" | "skill" | "extension";
77
- interface GovernanceResource {
78
- readonly kind: GovernanceResourceKind;
79
- readonly id?: string;
80
- readonly parentId?: string;
81
- }
77
+ type GovernanceResource = {
78
+ readonly kind: "mcp_tool";
79
+ readonly id: string;
80
+ readonly parentId: string;
81
+ } | {
82
+ readonly kind: Exclude<GovernanceResourceKind, "mcp_tool">;
83
+ readonly id: string;
84
+ readonly parentId?: never;
85
+ };
82
86
  interface GovernanceContext {
83
87
  readonly agent?: string;
84
88
  readonly resource?: GovernanceResource;
@@ -92,7 +96,7 @@ interface BlockResult {
92
96
  readonly prediction: Prediction;
93
97
  readonly score: number;
94
98
  readonly threshold: number;
95
- /** Omitted only when a legacy backend did not return mode and no override was requested. */
99
+ /** Omitted when neither an explicit local override nor the response supplies a mode. */
96
100
  readonly mode?: FirewallMode;
97
101
  readonly primaryOutcome?: PrimaryOutcome;
98
102
  readonly outcomeScores?: Readonly<Partial<Record<HarmfulOutcome, number>>>;
@@ -104,12 +108,14 @@ interface FirewallOptions {
104
108
  apiKey: string;
105
109
  apiUrl: string;
106
110
  timeoutMs?: number;
111
+ /** Explicit local client default; a per-call or per-adapter mode takes precedence. */
107
112
  mode?: FirewallMode;
108
113
  /** @deprecated Use mode: "shadow" or mode: "block". */
109
114
  shadowMode?: boolean;
110
115
  }
111
116
  type ClassificationMetadata = Readonly<Record<string, unknown>>;
112
117
  interface ClassifyOptions {
118
+ /** Explicit local per-call override; authoritative over any response mode. */
113
119
  mode?: FirewallMode;
114
120
  hook?: HookLabel;
115
121
  toolName?: string;
@@ -123,6 +129,7 @@ interface ClassifyOptions {
123
129
  signal?: AbortSignal;
124
130
  }
125
131
  interface ClassifyBatchOptions {
132
+ /** Explicit local per-call override; authoritative over every response mode. */
126
133
  mode?: FirewallMode;
127
134
  hooks?: readonly HookLabel[];
128
135
  toolNames?: readonly (string | undefined)[];
@@ -143,6 +150,7 @@ interface LangChainAdapterOptions {
143
150
  includeTool?: boolean;
144
151
  failOpen?: boolean;
145
152
  logger?: (message: string, error: unknown) => void;
153
+ /** Explicit local adapter override; takes precedence over the client and response modes. */
146
154
  mode?: FirewallMode;
147
155
  /** @deprecated Use mode: "shadow" or mode: "block". */
148
156
  shadowMode?: boolean;
@@ -175,6 +183,7 @@ interface MiddlewareOptions {
175
183
  scanInput?: boolean;
176
184
  scanOutput?: boolean;
177
185
  scanToolCalls?: boolean;
186
+ /** Explicit local adapter override; takes precedence over the client and response modes. */
178
187
  mode?: FirewallMode;
179
188
  /** @deprecated Use mode: "shadow" or mode: "block". */
180
189
  shadowMode?: boolean;
package/dist/index.cjs CHANGED
@@ -818,7 +818,7 @@ function sanitizeText(text) {
818
818
  }
819
819
 
820
820
  // src/firewall.ts
821
- var SDK_VERSION = "0.7.4";
821
+ var SDK_VERSION = "0.7.6";
822
822
  var DEFAULT_TIMEOUT_MS = 1e4;
823
823
  var DEFAULT_MAX_RETRIES = 5;
824
824
  var MAX_BACKOFF_SECONDS = 30;
@@ -894,15 +894,31 @@ function withSdkMetadata(metadata, info) {
894
894
  };
895
895
  return payload;
896
896
  }
897
+ function governanceResourceToWire(resource) {
898
+ if (!["agent", "tool", "mcp_server", "mcp_tool", "plugin", "skill", "extension"].includes(resource.kind)) {
899
+ throw new Error("Firewall: invalid governance resource kind");
900
+ }
901
+ if (typeof resource.id !== "string" || !resource.id.trim()) {
902
+ throw new Error("Firewall: governance resource id must be a non-empty string");
903
+ }
904
+ if (resource.kind === "mcp_tool") {
905
+ if (typeof resource.parentId !== "string" || !resource.parentId.trim()) {
906
+ throw new Error("Firewall: mcp_tool resource requires a non-empty parentId");
907
+ }
908
+ } else if (resource.parentId !== void 0) {
909
+ throw new Error("Firewall: parentId is valid only for mcp_tool resources");
910
+ }
911
+ return {
912
+ kind: resource.kind,
913
+ id: resource.id,
914
+ ...resource.parentId === void 0 ? {} : { parent_id: resource.parentId }
915
+ };
916
+ }
897
917
  function governanceContextToWire(context) {
898
918
  return {
899
919
  ...context.agent === void 0 ? {} : { agent: context.agent },
900
920
  ...context.resource === void 0 ? {} : {
901
- resource: {
902
- kind: context.resource.kind,
903
- ...context.resource.id === void 0 ? {} : { id: context.resource.id },
904
- ...context.resource.parentId === void 0 ? {} : { parent_id: context.resource.parentId }
905
- }
921
+ resource: governanceResourceToWire(context.resource)
906
922
  }
907
923
  };
908
924
  }
@@ -1122,6 +1138,11 @@ var Firewall = class {
1122
1138
  ...options.governance?.[index] === void 0 ? {} : { governance: options.governance[index] }
1123
1139
  })
1124
1140
  );
1141
+ if (options.governance !== void 0) {
1142
+ payload.resources = options.governance.map(
1143
+ (context) => context?.resource === void 0 ? null : governanceResourceToWire(context.resource)
1144
+ );
1145
+ }
1125
1146
  const data = await this.postWithRetry(payload, options.signal);
1126
1147
  if (!Array.isArray(data.predictions)) {
1127
1148
  throw new Error("Firewall: response predictions must be an array");
@@ -1190,6 +1211,9 @@ var Firewall = class {
1190
1211
  if (options.toolName !== void 0) {
1191
1212
  payload.tool_name = options.toolName;
1192
1213
  }
1214
+ if (options.governance?.resource !== void 0) {
1215
+ payload.resource = governanceResourceToWire(options.governance.resource);
1216
+ }
1193
1217
  payload.metadata = withSdkMetadata(options.metadata, {
1194
1218
  ...metadataInfo,
1195
1219
  ...options.governance === void 0 ? {} : { governance: options.governance }