@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 +30 -0
- package/README.md +101 -8
- package/dist/adapters/deepagents.cjs +251 -0
- package/dist/adapters/deepagents.cjs.map +1 -0
- package/dist/adapters/deepagents.d.cts +184 -0
- package/dist/adapters/deepagents.d.ts +184 -0
- package/dist/adapters/deepagents.js +221 -0
- package/dist/adapters/deepagents.js.map +1 -0
- package/dist/adapters/langchain.cjs +10 -3
- package/dist/adapters/langchain.cjs.map +1 -1
- package/dist/adapters/langchain.d.cts +1 -1
- package/dist/adapters/langchain.d.ts +1 -1
- package/dist/adapters/langchain.js +10 -3
- package/dist/adapters/langchain.js.map +1 -1
- package/dist/adapters/vercel.cjs +3 -1
- package/dist/adapters/vercel.cjs.map +1 -1
- package/dist/adapters/vercel.d.cts +1 -1
- package/dist/adapters/vercel.d.ts +1 -1
- package/dist/adapters/vercel.js +3 -1
- package/dist/adapters/vercel.js.map +1 -1
- package/dist/{firewall-Cdnk_UWY.d.cts → firewall-CRNFqgMa.d.cts} +21 -0
- package/dist/{firewall-Cdnk_UWY.d.ts → firewall-CRNFqgMa.d.ts} +21 -0
- package/dist/index.cjs +115 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +115 -25
- package/dist/index.js.map +1 -1
- package/package.json +34 -7
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.
|
|
40
|
+
npm install @silmaril-security/sdk@0.7.0
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
Requires Node
|
|
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`,
|
|
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.
|
|
443
|
-
|
|
444
|
-
|
|
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":[]}
|