@silmaril-security/sdk 0.6.2 → 0.7.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
@@ -2,6 +2,36 @@
2
2
 
3
3
  All notable changes to the Silmaril Firewall TypeScript SDK are documented here.
4
4
 
5
+ ## 0.7.0 - 2026-09-30
6
+
7
+ - Add optional Deep Agents middleware and protected constructors for root, general-purpose,
8
+ declarative, and explicitly protected compiled subagents. The compiled graph factory
9
+ records middleware installation and constructors reject unverified graphs.
10
+ - Enforce input, tool-call, tool-result, and non-streamed model-output decisions in
11
+ Block mode with safe continuation and a repeated-denial cap. Shadow and Warn report
12
+ decisions without changing content.
13
+ - Enable LangChain tool start/end callbacks by default and send the LangChain run ID
14
+ as correlation metadata alongside a distinct classification request ID per event.
15
+
16
+ ## 0.6.3 - 2026-09-24
17
+
18
+ - Add an optional `signal` option to `classify()` and `classifyBatch()`.
19
+ Aborting stops the in-flight request or the 429 retry wait, rejects with the
20
+ signal's reason, and leaves sibling calls on the same client unaffected. An
21
+ already-aborted signal rejects without sending a request.
22
+ - Combine the caller signal with the existing per-attempt `timeoutMs` without
23
+ changing timeout behavior, and release the abort listener and timer once an
24
+ attempt finishes.
25
+ - Discard a retried 429 response body before backing off so a retry does not
26
+ leak the connection.
27
+ - Serialize each request payload once before retries, so mutating the objects
28
+ passed to a call can no longer change an in-flight logical event.
29
+ - Reject a `timeoutMs` above 2147483647 ms in the constructor instead of
30
+ letting a timer silently collapse it to an immediate abort.
31
+ - Document that `Firewall` state is immutable and one client is reusable for
32
+ concurrent promises within a runtime, while each worker thread owns its own
33
+ client.
34
+
5
35
  ## 0.6.2 - 2026-08-24
6
36
 
7
37
  - Align the runtime `sdk_version` metadata with the published package version.
package/README.md CHANGED
@@ -37,10 +37,10 @@ npm install @silmaril-security/sdk
37
37
  For reproducible installs, pin a tagged release:
38
38
 
39
39
  ```sh
40
- npm install @silmaril-security/sdk@0.6.2
40
+ npm install @silmaril-security/sdk@0.7.0
41
41
  ```
42
42
 
43
- Requires Node 18 or later.
43
+ Requires Node 20 or later.
44
44
 
45
45
  The package name and SDK import path are both `@silmaril-security/sdk`, so call
46
46
  sites use `Firewall`, `HookLabel`, and `FirewallBlockedException` from that
@@ -63,6 +63,12 @@ Optional LangChain.js support:
63
63
  npm install @langchain/core @langchain/openai
64
64
  ```
65
65
 
66
+ Optional Deep Agents support:
67
+
68
+ ```sh
69
+ npm install deepagents langchain @langchain/core @langchain/langgraph
70
+ ```
71
+
66
72
  ## Configuration
67
73
 
68
74
  Every `Firewall` client needs two required options:
@@ -117,6 +123,47 @@ internally applied threshold. Direct calls do not throw on malicious verdicts.
117
123
  The Vercel AI SDK and LangChain.js adapters use `result.threshold` and throw
118
124
  `FirewallBlockedException` when enforcement is enabled.
119
125
 
126
+ ## Concurrency and Cancellation
127
+
128
+ A `Firewall` instance holds no per-request state, so one client can serve any
129
+ number of concurrent calls in a runtime. Responses are matched to their own
130
+ promise regardless of completion order:
131
+
132
+ ```ts
133
+ const [userResult, toolResult] = await Promise.all([
134
+ fw.classify(userInput, { hook: HookLabel.USER_INPUT }),
135
+ fw.classify(toolOutput, { hook: HookLabel.TOOL_RESPONSE, toolName: "read_file" }),
136
+ ]);
137
+ ```
138
+
139
+ Pass a `signal` to cancel one call without touching its siblings:
140
+
141
+ ```ts
142
+ const controller = new AbortController();
143
+ request.on("close", () => controller.abort());
144
+
145
+ try {
146
+ const result = await fw.classify(userInput, {
147
+ hook: HookLabel.USER_INPUT,
148
+ signal: controller.signal,
149
+ });
150
+ return result;
151
+ } catch (err) {
152
+ if (controller.signal.aborted) {
153
+ return; // caller went away
154
+ }
155
+ throw err;
156
+ }
157
+ ```
158
+
159
+ Aborting stops the in-flight request or the 429 backoff wait and rejects with
160
+ the signal's reason. An already-aborted signal rejects without sending a
161
+ request. `signal` composes with `timeoutMs`, which still applies per attempt.
162
+ `classifyBatch()` accepts the same option.
163
+
164
+ Each worker thread owns its own client: construct a `Firewall` inside the
165
+ thread instead of sharing one instance across `worker_threads` boundaries.
166
+
120
167
  ## Handle Outcomes
121
168
 
122
169
  Direct calls expose typed outcome labels so applications can choose different
@@ -183,14 +230,15 @@ Outcome taxonomy:
183
230
  interface FirewallOptions {
184
231
  apiKey: string; // required
185
232
  apiUrl: string; // required
186
- timeoutMs?: number; // default: 10000 ms
233
+ timeoutMs?: number; // default: 10000 ms, max 2147483647
187
234
  mode?: "shadow" | "warn" | "block"; // omitted uses backend configuration
188
235
  shadowMode?: boolean; // deprecated legacy mapping
189
236
  }
190
237
  ```
191
238
 
192
- The SDK uses native `fetch`, `AbortSignal.timeout`, and JSON request bodies with
193
- `x-api-key` and `content-type` headers.
239
+ The SDK uses native `fetch`, an abort signal per request attempt, and JSON
240
+ request bodies with `x-api-key` and `content-type` headers. `timeoutMs` applies
241
+ to each attempt and is combined with any caller-supplied `signal`.
194
242
 
195
243
  ## Backend Thresholding
196
244
 
@@ -311,6 +359,12 @@ preserved as the backend sequence identity. No aliases are inspected. If
311
359
  callers provide `metadata.silmaril`, it must be an object and SDK-reserved keys
312
360
  are overwritten by the SDK.
313
361
 
362
+ The `governance` option on `classify()` and per-item `governance` array on
363
+ `classifyBatch()` send typed agent/resource context. `BlockResult.governance`
364
+ returns the server action, policy version, and optional rule ID. A malicious
365
+ prediction or explicit governance block is blocked in Block mode; legacy
366
+ responses without governance remain valid.
367
+
314
368
  ## Errors
315
369
 
316
370
  - `SilmarilApiError`: thrown when the firewall API responds with a non-2xx or redirect status. Carries `status`, `statusText`, a 64 KiB-capped `body`, and any parsed malformed-input diagnostics. The default error message omits the body to keep logs clean.
@@ -422,6 +476,12 @@ and the LLM call proceeds. Set `failOpen: false` to make API errors bubble up.
422
476
  Blocking decisions still throw `FirewallBlockedException` unless shadow mode is
423
477
  enabled. `PromptBlockedException` continues to work as a deprecated alias for
424
478
  one release.
479
+ Model-start, tool-start, and tool-end hooks are enabled by default; retriever
480
+ hooks remain opt-in. The LangChain run ID is sent as
481
+ `metadata.langgraph.run_id`; every classification gets a distinct
482
+ `metadata.silmaril.request_id`. Supply `conversationId` to the handler for
483
+ sequence identity; it sends `metadata.conversationId`. Callback blocks throw
484
+ and end that graph execution.
425
485
 
426
486
  `asLangChainHandler()` is async because it lazy-loads `@langchain/core` so core
427
487
  users do not pay for it. The root package intentionally exposes a structural
@@ -436,12 +496,45 @@ import { createLangChainHandler } from "@silmaril-security/sdk/adapters/langchai
436
496
  const handler = await createLangChainHandler(fw);
437
497
  ```
438
498
 
499
+ ## Deep Agents
500
+
501
+ ```ts
502
+ import { createProtectedDeepAgent } from "@silmaril-security/sdk/adapters/deepagents";
503
+
504
+ const agent = createProtectedDeepAgent(fw, {
505
+ model, tools,
506
+ subagents: [{ name: "research", description: "Research safely" }],
507
+ silmaril: { conversationId },
508
+ });
509
+ ```
510
+
511
+ The constructor installs checks on the root, general-purpose, and declarative
512
+ subagents. For a compiled subagent, call
513
+ `createProtectedCompiledSubagent(fw, { name: "review", description: "Review",
514
+ model, tools })` and pass its returned spec through
515
+ `protectedCompiledSubagents`. That factory installs middleware before
516
+ compilation. The parent constructor verifies the exact graph and Firewall
517
+ client; it rejects arbitrary compiled runnables. Root custom middleware does
518
+ not automatically reach every subagent.
519
+
520
+ The middleware checks input before model use, tool calls before execution,
521
+ tool results before the next model call, and non-streamed model output before
522
+ the graph consumes it. In Block mode, denied tool interactions become a fixed
523
+ safe `ToolMessage` with the original call ID. The agent can choose an allowed
524
+ alternative; repeated denials end with a fixed safe response. Denied model
525
+ output is replaced. Shadow and Warn report decisions through `onClassify`
526
+ without replacing content. Classification errors allow model and tool execution
527
+ by default; set `silmaril: { failOpen: false }` to require a successful
528
+ classification. Already emitted streaming text cannot be recalled.
529
+
439
530
  ## Retries
440
531
 
441
532
  HTTP 429 responses are retried with exponential backoff capped at 30s, up to 5
442
- times. Redirects are rejected rather than followed. Other non-2xx responses are
443
- surfaced as `SilmarilApiError`, and transport or timeout failures are surfaced
444
- unchanged.
533
+ times. The retried response body is discarded before the wait, and each request
534
+ payload is serialized once so every attempt sends the same logical event.
535
+ Redirects are rejected rather than followed. Other non-2xx responses are
536
+ surfaced as `SilmarilApiError`, and transport, timeout, or cancellation failures
537
+ are surfaced unchanged.
445
538
 
446
539
  ## Development
447
540
 
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/deepagents.ts
21
+ var deepagents_exports = {};
22
+ __export(deepagents_exports, {
23
+ SAFE_FINAL_MESSAGE: () => SAFE_FINAL_MESSAGE,
24
+ SAFE_OUTPUT_MESSAGE: () => SAFE_OUTPUT_MESSAGE,
25
+ SAFE_TOOL_MESSAGE: () => SAFE_TOOL_MESSAGE,
26
+ createDeepAgentsMiddleware: () => createDeepAgentsMiddleware,
27
+ createProtectedCompiledSubagent: () => createProtectedCompiledSubagent,
28
+ createProtectedDeepAgent: () => createProtectedDeepAgent
29
+ });
30
+ module.exports = __toCommonJS(deepagents_exports);
31
+ var import_messages = require("@langchain/core/messages");
32
+ var import_langgraph = require("@langchain/langgraph");
33
+ var import_deepagents = require("deepagents");
34
+ var import_langchain = require("langchain");
35
+
36
+ // src/hooks.ts
37
+ var HookLabel = {
38
+ USER_INPUT: "user_input",
39
+ SYSTEM_PROMPT: "system_prompt",
40
+ TOOL_CALL: "tool_call",
41
+ TOOL_RESPONSE: "tool_response",
42
+ LLM_OUTPUT: "llm_output",
43
+ UNKNOWN: "unknown"
44
+ };
45
+ var FirewallHook = {
46
+ LLM_START: "on_llm_start",
47
+ CHAT_MODEL_START: "on_chat_model_start",
48
+ TOOL_START: "on_tool_start",
49
+ RETRIEVER_START: "on_retriever_start",
50
+ LLM_END: "on_llm_end",
51
+ TOOL_END: "on_tool_end",
52
+ RETRIEVER_END: "on_retriever_end"
53
+ };
54
+ var DEFAULT_HOOKS = /* @__PURE__ */ new Set([
55
+ FirewallHook.LLM_START,
56
+ FirewallHook.CHAT_MODEL_START,
57
+ FirewallHook.TOOL_START,
58
+ FirewallHook.TOOL_END
59
+ ]);
60
+ var INPUT_HOOKS = /* @__PURE__ */ new Set([
61
+ FirewallHook.LLM_START,
62
+ FirewallHook.CHAT_MODEL_START,
63
+ FirewallHook.TOOL_START,
64
+ FirewallHook.RETRIEVER_START
65
+ ]);
66
+ var OUTPUT_HOOKS = /* @__PURE__ */ new Set([
67
+ FirewallHook.LLM_END,
68
+ FirewallHook.TOOL_END,
69
+ FirewallHook.RETRIEVER_END
70
+ ]);
71
+ var ALL_HOOKS = /* @__PURE__ */ new Set([
72
+ ...INPUT_HOOKS,
73
+ ...OUTPUT_HOOKS
74
+ ]);
75
+ var FIREWALL_HOOK_TO_LABEL = {
76
+ [FirewallHook.CHAT_MODEL_START]: HookLabel.USER_INPUT,
77
+ [FirewallHook.LLM_START]: HookLabel.USER_INPUT,
78
+ [FirewallHook.TOOL_START]: HookLabel.TOOL_CALL,
79
+ [FirewallHook.TOOL_END]: HookLabel.TOOL_RESPONSE,
80
+ [FirewallHook.RETRIEVER_START]: HookLabel.TOOL_CALL,
81
+ [FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,
82
+ [FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT
83
+ };
84
+ var FIREWALL_HOOK_VALUES = new Set(Object.values(FirewallHook));
85
+
86
+ // src/adapters/deepagents.ts
87
+ var SAFE_TOOL_MESSAGE = "Silmaril Firewall blocked this tool interaction. Choose a different safe action.";
88
+ var SAFE_FINAL_MESSAGE = "Silmaril Firewall stopped this request after repeated unsafe actions.";
89
+ var SAFE_OUTPUT_MESSAGE = "Silmaril Firewall blocked this response.";
90
+ var BLOCKED_TOOL_MARKER = "silmaril-firewall:v1:blocked-tool";
91
+ var issuedBlockedMessages = /* @__PURE__ */ new WeakSet();
92
+ var protectedCompiledGraphs = /* @__PURE__ */ new WeakMap();
93
+ function textOf(message) {
94
+ return typeof message.content === "string" ? message.content : JSON.stringify(message.content);
95
+ }
96
+ function textOfStructuredValue(value, seen = /* @__PURE__ */ new WeakSet()) {
97
+ if (value == null) return "";
98
+ if (typeof value !== "object") return String(value);
99
+ if (seen.has(value)) return "[circular]";
100
+ seen.add(value);
101
+ if (Array.isArray(value)) return value.map((item) => textOfStructuredValue(item, seen)).join("\n");
102
+ return Object.entries(value).map(([key, item]) => `${key}: ${textOfStructuredValue(item, seen)}`).join("\n");
103
+ }
104
+ function stripAllowedMarker(result) {
105
+ const strip = (message) => {
106
+ if (message.additional_kwargs?.silmarilBlocked !== BLOCKED_TOOL_MARKER) return message;
107
+ if (issuedBlockedMessages.has(message)) return message;
108
+ const { silmarilBlocked: _marker, ...additional_kwargs } = message.additional_kwargs;
109
+ return new import_messages.ToolMessage({ ...message, additional_kwargs });
110
+ };
111
+ if (import_messages.ToolMessage.isInstance(result)) return strip(result);
112
+ if (result instanceof import_langgraph.Command) {
113
+ const update = result.update;
114
+ if (update && !Array.isArray(update) && "messages" in update && Array.isArray(update.messages)) {
115
+ const originalMessages = update.messages;
116
+ const messages = originalMessages.map((message) => import_messages.ToolMessage.isInstance(message) ? strip(message) : message);
117
+ if (messages.some((message, index) => message !== originalMessages[index])) {
118
+ return new import_langgraph.Command({ ...result, update: { ...update, messages } });
119
+ }
120
+ }
121
+ }
122
+ return result;
123
+ }
124
+ function createDeepAgentsMiddleware(firewall, options = {}) {
125
+ const maxBlockedAttempts = options.maxBlockedAttempts ?? 3;
126
+ if (!Number.isInteger(maxBlockedAttempts) || maxBlockedAttempts < 1) {
127
+ throw new Error("maxBlockedAttempts must be a positive integer");
128
+ }
129
+ const effectiveMode = options.mode ?? firewall.mode;
130
+ const classify = async (text, hook, toolName) => {
131
+ if (!text.trim()) return { enforce: false, mode: effectiveMode };
132
+ try {
133
+ const result = await firewall.classify(text, {
134
+ hook,
135
+ ...toolName === void 0 ? {} : { toolName },
136
+ ...effectiveMode === void 0 ? {} : { mode: effectiveMode },
137
+ ...options.conversationId === void 0 ? {} : { metadata: { conversationId: options.conversationId } }
138
+ });
139
+ const mode = effectiveMode ?? result.mode ?? "block";
140
+ const blocked = result.prediction === "MALICIOUS" || result.governance?.action === "block";
141
+ if (options.onClassify) {
142
+ try {
143
+ options.onClassify({ hook, ...toolName === void 0 ? {} : { toolName }, text, result, blocked, mode, shadowMode: mode === "shadow" });
144
+ } catch {
145
+ }
146
+ }
147
+ return { enforce: blocked && mode === "block", mode };
148
+ } catch (error) {
149
+ if (options.failOpen === false) throw error;
150
+ return { enforce: false, mode: effectiveMode };
151
+ }
152
+ };
153
+ return (0, import_langchain.createMiddleware)({
154
+ name: "SilmarilDeepAgentsMiddleware",
155
+ wrapModelCall: async (request, handler) => {
156
+ const history = request.state.messages;
157
+ let lastUserIndex = -1;
158
+ for (let i = history.length - 1; i >= 0; i--) {
159
+ if (import_messages.HumanMessage.isInstance(history[i])) {
160
+ lastUserIndex = i;
161
+ break;
162
+ }
163
+ }
164
+ const blockedCount = history.slice(lastUserIndex + 1).filter(
165
+ (message) => import_messages.ToolMessage.isInstance(message) && message.content === SAFE_TOOL_MESSAGE && message.additional_kwargs?.silmarilBlocked === BLOCKED_TOOL_MARKER
166
+ ).length;
167
+ const latestUser = [...request.messages].reverse().find(import_messages.HumanMessage.isInstance);
168
+ const inputDecision = latestUser ? await classify(textOf(latestUser), HookLabel.USER_INPUT) : void 0;
169
+ if (inputDecision?.enforce) return new import_messages.AIMessage(SAFE_OUTPUT_MESSAGE);
170
+ if (blockedCount >= maxBlockedAttempts) {
171
+ return new import_messages.AIMessage(SAFE_FINAL_MESSAGE);
172
+ }
173
+ const response = await handler(request);
174
+ if (response instanceof import_langgraph.Command && (await classify(textOfStructuredValue(response.update), HookLabel.LLM_OUTPUT)).enforce) {
175
+ return new import_messages.AIMessage(SAFE_OUTPUT_MESSAGE);
176
+ }
177
+ if (import_messages.AIMessage.isInstance(response) && (await classify(textOf(response), HookLabel.LLM_OUTPUT)).enforce) {
178
+ return new import_messages.AIMessage(SAFE_OUTPUT_MESSAGE);
179
+ }
180
+ return response;
181
+ },
182
+ wrapToolCall: async (request, handler) => {
183
+ const safe = () => {
184
+ const message = new import_messages.ToolMessage({
185
+ content: SAFE_TOOL_MESSAGE,
186
+ tool_call_id: request.toolCall.id ?? "",
187
+ name: request.toolCall.name,
188
+ additional_kwargs: { silmarilBlocked: BLOCKED_TOOL_MARKER }
189
+ });
190
+ issuedBlockedMessages.add(message);
191
+ return message;
192
+ };
193
+ if ((await classify(JSON.stringify(request.toolCall.args ?? {}), HookLabel.TOOL_CALL, request.toolCall.name)).enforce) {
194
+ return safe();
195
+ }
196
+ const result = await handler(request);
197
+ let resultText;
198
+ try {
199
+ resultText = import_messages.ToolMessage.isInstance(result) ? textOf(result) : result instanceof import_langgraph.Command ? textOfStructuredValue(result.update) : String(result);
200
+ } catch {
201
+ const decision = await classify("[uninspectable tool result]", HookLabel.TOOL_RESPONSE, request.toolCall.name);
202
+ return decision.mode === "block" ? safe() : stripAllowedMarker(result);
203
+ }
204
+ if ((await classify(resultText, HookLabel.TOOL_RESPONSE, request.toolCall.name)).enforce) {
205
+ return safe();
206
+ }
207
+ return stripAllowedMarker(result);
208
+ }
209
+ });
210
+ }
211
+ function createProtectedCompiledSubagent(firewall, options) {
212
+ const { name, description, silmaril, middleware = [], ...agentOptions } = options;
213
+ const runnable = (0, import_langchain.createAgent)({
214
+ ...agentOptions,
215
+ middleware: [...middleware, createDeepAgentsMiddleware(firewall, silmaril)]
216
+ });
217
+ protectedCompiledGraphs.set(runnable, firewall);
218
+ return { name, description, runnable };
219
+ }
220
+ function createProtectedDeepAgent(firewall, options = {}) {
221
+ const { silmaril, subagents = [], protectedCompiledSubagents = [], middleware = [], ...agentOptions } = options;
222
+ const protectedSubagents = subagents.map((subagent) => {
223
+ if ("runnable" in subagent || "graphId" in subagent) {
224
+ throw new Error("Compiled or remote subagents must install Silmaril middleware inside their own graph");
225
+ }
226
+ return { ...subagent, middleware: [...subagent.middleware ?? [], createDeepAgentsMiddleware(firewall, silmaril)] };
227
+ });
228
+ if (!protectedSubagents.some((subagent) => subagent.name === import_deepagents.GENERAL_PURPOSE_SUBAGENT.name)) {
229
+ protectedSubagents.unshift({ ...import_deepagents.GENERAL_PURPOSE_SUBAGENT, middleware: [createDeepAgentsMiddleware(firewall, silmaril)] });
230
+ }
231
+ for (const spec of protectedCompiledSubagents) {
232
+ if (!spec.runnable || protectedCompiledGraphs.get(spec.runnable) !== firewall) {
233
+ throw new Error("Compiled subagents must be built with createProtectedCompiledSubagent for this Firewall client");
234
+ }
235
+ }
236
+ return (0, import_deepagents.createDeepAgent)({
237
+ ...agentOptions,
238
+ middleware: [...middleware, createDeepAgentsMiddleware(firewall, silmaril)],
239
+ subagents: [...protectedSubagents, ...protectedCompiledSubagents]
240
+ });
241
+ }
242
+ // Annotate the CommonJS export names for ESM import in node:
243
+ 0 && (module.exports = {
244
+ SAFE_FINAL_MESSAGE,
245
+ SAFE_OUTPUT_MESSAGE,
246
+ SAFE_TOOL_MESSAGE,
247
+ createDeepAgentsMiddleware,
248
+ createProtectedCompiledSubagent,
249
+ createProtectedDeepAgent
250
+ });
251
+ //# sourceMappingURL=deepagents.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/deepagents.ts","../../src/hooks.ts"],"sourcesContent":["// Copyright (c) 2024-2026 Silmaril Security Inc. All rights reserved.\n\n/** Optional Deep Agents integration. Import from @silmaril-security/sdk/adapters/deepagents. */\nimport { AIMessage, HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport { Command } from \"@langchain/langgraph\";\nimport { createDeepAgent, GENERAL_PURPOSE_SUBAGENT, type AnySubAgent, type CompiledSubAgent } from \"deepagents\";\nimport { createAgent, createMiddleware, type CreateAgentParams } from \"langchain\";\n\nimport type { Firewall } from \"../firewall.js\";\nimport { HookLabel } from \"../hooks.js\";\nimport type { ClassifyEvent, FirewallMode } from \"../types.js\";\n\nexport const SAFE_TOOL_MESSAGE = \"Silmaril Firewall blocked this tool interaction. Choose a different safe action.\";\nexport const SAFE_FINAL_MESSAGE = \"Silmaril Firewall stopped this request after repeated unsafe actions.\";\nexport const SAFE_OUTPUT_MESSAGE = \"Silmaril Firewall blocked this response.\";\nconst BLOCKED_TOOL_MARKER = \"silmaril-firewall:v1:blocked-tool\";\nconst issuedBlockedMessages = new WeakSet<ToolMessage>();\n\n// A protected graph is identified by the exact runnable returned by our factory.\nconst protectedCompiledGraphs = new WeakMap<object, Firewall>();\n\nexport interface DeepAgentsMiddlewareOptions {\n mode?: FirewallMode;\n failOpen?: boolean;\n maxBlockedAttempts?: number;\n /** A caller-supplied sequence identity; never derived from a LangChain run ID. */\n conversationId?: string;\n onClassify?: (event: ClassifyEvent) => void;\n}\n\nfunction textOf(message: { content: unknown }): string {\n return typeof message.content === \"string\" ? message.content : JSON.stringify(message.content);\n}\n\nfunction textOfStructuredValue(value: unknown, seen = new WeakSet<object>()): string {\n if (value == null) return \"\";\n if (typeof value !== \"object\") return String(value);\n if (seen.has(value)) return \"[circular]\";\n seen.add(value);\n if (Array.isArray(value)) return value.map((item) => textOfStructuredValue(item, seen)).join(\"\\n\");\n return Object.entries(value).map(([key, item]) => `${key}: ${textOfStructuredValue(item, seen)}`).join(\"\\n\");\n}\n\nfunction stripAllowedMarker(result: ToolMessage | Command): ToolMessage | Command {\n const strip = (message: ToolMessage): ToolMessage => {\n if (message.additional_kwargs?.silmarilBlocked !== BLOCKED_TOOL_MARKER) return message;\n if (issuedBlockedMessages.has(message)) return message;\n const { silmarilBlocked: _marker, ...additional_kwargs } = message.additional_kwargs;\n // Tool providers may return frozen messages or metadata. Copy rather\n // than mutating a result owned by the caller.\n return new ToolMessage({ ...message, additional_kwargs });\n };\n if (ToolMessage.isInstance(result)) return strip(result);\n if (result instanceof Command) {\n const update = result.update;\n if (update && !Array.isArray(update) && \"messages\" in update && Array.isArray(update.messages)) {\n const originalMessages = update.messages;\n const messages = originalMessages.map((message) => ToolMessage.isInstance(message) ? strip(message) : message);\n if (messages.some((message, index) => message !== originalMessages[index])) {\n return new Command({ ...result, update: { ...update, messages } });\n }\n }\n }\n return result;\n}\n\nexport function createDeepAgentsMiddleware(firewall: Firewall, options: DeepAgentsMiddlewareOptions = {}) {\n const maxBlockedAttempts = options.maxBlockedAttempts ?? 3;\n if (!Number.isInteger(maxBlockedAttempts) || maxBlockedAttempts < 1) {\n throw new Error(\"maxBlockedAttempts must be a positive integer\");\n }\n const effectiveMode = options.mode ?? firewall.mode;\n const classify = async (text: string, hook: HookLabel, toolName?: string): Promise<{ enforce: boolean; mode: FirewallMode | undefined }> => {\n if (!text.trim()) return { enforce: false, mode: effectiveMode };\n try {\n const result = await firewall.classify(text, {\n hook,\n ...(toolName === undefined ? {} : { toolName }),\n ...(effectiveMode === undefined ? {} : { mode: effectiveMode }),\n ...(options.conversationId === undefined ? {} : { metadata: { conversationId: options.conversationId } }),\n });\n const mode = effectiveMode ?? result.mode ?? \"block\";\n const blocked = result.prediction === \"MALICIOUS\" || result.governance?.action === \"block\";\n if (options.onClassify) {\n try {\n options.onClassify({ hook, ...(toolName === undefined ? {} : { toolName }), text, result, blocked, mode, shadowMode: mode === \"shadow\" });\n } catch {\n // Observation callbacks do not change enforcement.\n }\n }\n return { enforce: blocked && mode === \"block\", mode };\n } catch (error) {\n if (options.failOpen === false) throw error;\n return { enforce: false, mode: effectiveMode };\n }\n };\n\n return createMiddleware({\n name: \"SilmarilDeepAgentsMiddleware\",\n wrapModelCall: async (request, handler) => {\n const history = request.state.messages;\n let lastUserIndex = -1;\n for (let i = history.length - 1; i >= 0; i--) {\n if (HumanMessage.isInstance(history[i])) {\n lastUserIndex = i;\n break;\n }\n }\n const blockedCount = history.slice(lastUserIndex + 1).filter(\n (message) => ToolMessage.isInstance(message) && message.content === SAFE_TOOL_MESSAGE\n && message.additional_kwargs?.silmarilBlocked === BLOCKED_TOOL_MARKER,\n ).length;\n const latestUser = [...request.messages].reverse().find(HumanMessage.isInstance);\n const inputDecision = latestUser\n ? await classify(textOf(latestUser), HookLabel.USER_INPUT)\n : undefined;\n if (inputDecision?.enforce) return new AIMessage(SAFE_OUTPUT_MESSAGE);\n // The marker is added only after a Block decision, so neither an\n // unrelated input mode nor tool output that quotes the safe text affects the cap.\n if (blockedCount >= maxBlockedAttempts) {\n return new AIMessage(SAFE_FINAL_MESSAGE);\n }\n const response = await handler(request);\n if (response instanceof Command && (await classify(textOfStructuredValue(response.update), HookLabel.LLM_OUTPUT)).enforce) {\n return new AIMessage(SAFE_OUTPUT_MESSAGE);\n }\n if (AIMessage.isInstance(response) && (await classify(textOf(response), HookLabel.LLM_OUTPUT)).enforce) {\n return new AIMessage(SAFE_OUTPUT_MESSAGE);\n }\n return response;\n },\n wrapToolCall: async (request, handler) => {\n const safe = () => {\n const message = new ToolMessage({\n content: SAFE_TOOL_MESSAGE,\n tool_call_id: request.toolCall.id ?? \"\",\n name: request.toolCall.name,\n additional_kwargs: { silmarilBlocked: BLOCKED_TOOL_MARKER },\n });\n issuedBlockedMessages.add(message);\n return message;\n };\n if ((await classify(JSON.stringify(request.toolCall.args ?? {}), HookLabel.TOOL_CALL, request.toolCall.name)).enforce) {\n return safe();\n }\n const result = await handler(request);\n let resultText: string;\n try {\n resultText = ToolMessage.isInstance(result) ? textOf(result)\n : result instanceof Command ? textOfStructuredValue(result.update) : String(result);\n } catch {\n // Content cannot be inspected, but the backend still resolves the\n // enforcement mode. Observation modes retain the original result.\n const decision = await classify(\"[uninspectable tool result]\", HookLabel.TOOL_RESPONSE, request.toolCall.name);\n return decision.mode === \"block\" ? safe() : stripAllowedMarker(result);\n }\n if ((await classify(resultText, HookLabel.TOOL_RESPONSE, request.toolCall.name)).enforce) {\n return safe();\n }\n return stripAllowedMarker(result);\n },\n });\n}\n\nexport interface ProtectedCompiledSubagentOptions extends Omit<CreateAgentParams, \"middleware\"> {\n name: string;\n description: string;\n middleware?: CreateAgentParams[\"middleware\"];\n silmaril?: DeepAgentsMiddlewareOptions;\n}\n\n/** Compile a subagent with Silmaril middleware and register that exact graph. */\nexport function createProtectedCompiledSubagent(firewall: Firewall, options: ProtectedCompiledSubagentOptions): CompiledSubAgent {\n const { name, description, silmaril, middleware = [], ...agentOptions } = options;\n const runnable = createAgent({\n ...agentOptions,\n middleware: [...middleware, createDeepAgentsMiddleware(firewall, silmaril)] as const,\n });\n protectedCompiledGraphs.set(runnable, firewall);\n return { name, description, runnable };\n}\n\nexport interface ProtectedDeepAgentOptions extends Omit<NonNullable<Parameters<typeof createDeepAgent>[0]>, \"subagents\" | \"middleware\"> {\n subagents?: readonly AnySubAgent[];\n /** Only pass specs returned by createProtectedCompiledSubagent for this client. */\n protectedCompiledSubagents?: readonly CompiledSubAgent[];\n middleware?: NonNullable<Parameters<typeof createDeepAgent>[0]>[\"middleware\"];\n silmaril?: DeepAgentsMiddlewareOptions;\n}\n\n/** Protect root, default general-purpose, and declarative subagents. */\nexport function createProtectedDeepAgent(firewall: Firewall, options: ProtectedDeepAgentOptions = {}) {\n const { silmaril, subagents = [], protectedCompiledSubagents = [], middleware = [], ...agentOptions } = options;\n const protectedSubagents = subagents.map((subagent) => {\n if (\"runnable\" in subagent || \"graphId\" in subagent) {\n throw new Error(\"Compiled or remote subagents must install Silmaril middleware inside their own graph\");\n }\n return { ...subagent, middleware: [...(subagent.middleware ?? []), createDeepAgentsMiddleware(firewall, silmaril)] };\n });\n if (!protectedSubagents.some((subagent) => subagent.name === GENERAL_PURPOSE_SUBAGENT.name)) {\n protectedSubagents.unshift({ ...GENERAL_PURPOSE_SUBAGENT, middleware: [createDeepAgentsMiddleware(firewall, silmaril)] });\n }\n for (const spec of protectedCompiledSubagents) {\n if (!spec.runnable || protectedCompiledGraphs.get(spec.runnable) !== firewall) {\n throw new Error(\"Compiled subagents must be built with createProtectedCompiledSubagent for this Firewall client\");\n }\n }\n return createDeepAgent({\n ...agentOptions,\n middleware: [...middleware, createDeepAgentsMiddleware(firewall, silmaril)],\n subagents: [...protectedSubagents, ...protectedCompiledSubagents],\n });\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport const HookLabel = {\n USER_INPUT: \"user_input\",\n SYSTEM_PROMPT: \"system_prompt\",\n TOOL_CALL: \"tool_call\",\n TOOL_RESPONSE: \"tool_response\",\n LLM_OUTPUT: \"llm_output\",\n UNKNOWN: \"unknown\",\n} as const;\n\nexport type HookLabel = (typeof HookLabel)[keyof typeof HookLabel];\n\nexport const FirewallHook = {\n LLM_START: \"on_llm_start\",\n CHAT_MODEL_START: \"on_chat_model_start\",\n TOOL_START: \"on_tool_start\",\n RETRIEVER_START: \"on_retriever_start\",\n LLM_END: \"on_llm_end\",\n TOOL_END: \"on_tool_end\",\n RETRIEVER_END: \"on_retriever_end\",\n} as const;\n\nexport type FirewallHook = (typeof FirewallHook)[keyof typeof FirewallHook];\n\nexport const DEFAULT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n FirewallHook.TOOL_START,\n FirewallHook.TOOL_END,\n]);\n\nexport const INPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n FirewallHook.TOOL_START,\n FirewallHook.RETRIEVER_START,\n]);\n\nexport const OUTPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_END,\n FirewallHook.TOOL_END,\n FirewallHook.RETRIEVER_END,\n]);\n\nexport const ALL_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n ...INPUT_HOOKS,\n ...OUTPUT_HOOKS,\n]);\n\nexport const FIREWALL_HOOK_TO_LABEL: Readonly<Record<FirewallHook, HookLabel>> = {\n [FirewallHook.CHAT_MODEL_START]: HookLabel.USER_INPUT,\n [FirewallHook.LLM_START]: HookLabel.USER_INPUT,\n [FirewallHook.TOOL_START]: HookLabel.TOOL_CALL,\n [FirewallHook.TOOL_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.RETRIEVER_START]: HookLabel.TOOL_CALL,\n [FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT,\n};\n\nconst FIREWALL_HOOK_VALUES: ReadonlySet<string> = new Set(Object.values(FirewallHook));\n\nexport function resolveHooks(\n hooks: Iterable<FirewallHook | string> | undefined,\n): ReadonlySet<FirewallHook> {\n if (hooks === undefined) {\n return DEFAULT_HOOKS;\n }\n const resolved = new Set<FirewallHook>();\n for (const h of hooks) {\n if (!FIREWALL_HOOK_VALUES.has(h)) {\n throw new Error(`Invalid FirewallHook value: ${String(h)}`);\n }\n resolved.add(h as FirewallHook);\n }\n return resolved;\n}\n\nexport function prependHook(text: string, hook: HookLabel | string | undefined | null): string {\n if (hook === undefined || hook === null) {\n return text;\n }\n if (hook === HookLabel.UNKNOWN) {\n return text;\n }\n return `[HOOK:${hook}] ${text}`;\n}\n\nexport function prependToolName(text: string, toolName: string | undefined | null): string {\n if (toolName === undefined || toolName === null) {\n return text;\n }\n return `[TOOL:${toolName}] ${text}`;\n}\n"],"mappings":"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","names":[]}