@neuraltrust/trustguard-sdk 0.1.4 → 0.1.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/README.md CHANGED
@@ -33,7 +33,8 @@ if (response.isBlocked) {
33
33
 
34
34
  ```typescript
35
35
  const response = await client.guard({
36
- payload: { input: "user text" },
36
+ // A model response goes in an assistant message; `{ input }` is read as the user's text.
37
+ payload: { messages: [{ role: "assistant", content: "model response" }] },
37
38
  direction: "output", // "input" (default) or "output"
38
39
  protocol: "llm", // "all" (default), "llm", "mcp", or "a2a"
39
40
  sessionId: "conversation-42", // groups multi-turn traffic
@@ -78,6 +79,55 @@ try {
78
79
  }
79
80
  ```
80
81
 
82
+ ## Vercel AI SDK
83
+
84
+ `@neuraltrust/trustguard-sdk/ai-sdk` guards an [AI SDK](https://ai-sdk.dev) agent end to end: the prompt, the response, every tool call and every tool result. It needs `ai` 7 or later, which stays an optional peer dependency: the main entry point never loads it.
85
+
86
+ ```typescript
87
+ import { streamText, wrapLanguageModel } from "ai";
88
+ import { openai } from "@ai-sdk/openai";
89
+ import { TrustGuard } from "@neuraltrust/trustguard-sdk";
90
+ import { trustguard } from "@neuraltrust/trustguard-sdk/ai-sdk";
91
+
92
+ const client = new TrustGuard({ baseUrl: process.env.TRUSTGUARD_BASE_URL!, apiKey: process.env.TRUSTGUARD_API_KEY! });
93
+
94
+ // One instance per request, so every evaluation carries the user and the conversation.
95
+ const tg = trustguard(client, { consumerId: user.id, sessionId: chatId });
96
+
97
+ const result = streamText({
98
+ model: wrapLanguageModel({ model: openai("gpt-5.2"), middleware: tg.middleware }),
99
+ tools: tg.tools(tools),
100
+ toolApproval: tg.toolApproval,
101
+ experimental_toolApprovalSecret: process.env.TOOL_APPROVAL_SECRET, // signs approval requests
102
+ messages,
103
+ });
104
+ ```
105
+
106
+ | Piece | Guards | Block | Transform | Ask |
107
+ |---|---|---|---|---|
108
+ | `middleware` | The user turn, before the model call | Throws `TrustGuardBlockedError` | Sends the masked text | Blocks, or passes with `promptAsk: "allow"` |
109
+ | `middleware` | The response | Replaces it with `blockedMessage` | Replaces it with the masked text | Not evaluated on output |
110
+ | `toolApproval` | Each tool call, before it runs | Denied, with the policy as the reason | Denied: approval cannot rewrite the arguments | `user-approval`, or denied with `toolAsk: "deny"` |
111
+ | `tools()` | Each tool result, before the model reads it | The tool fails with `TrustGuardBlockedError`, which the model sees as a tool error | Returns the masked result | Not evaluated on output |
112
+
113
+ Steps that continue a tool loop are not evaluated as prompts again: the tool results in them are covered by `tools()`. Wrap MCP tools the same way: `tg.tools(await mcpClient.tools())`.
114
+
115
+ A complete Next.js app, with approvals and a live verdict panel, is in [`examples/ai-sdk`](../examples/ai-sdk).
116
+
117
+ **Streaming.** By default a streamed response is monitored: text streams untouched and is evaluated when the response finishes, so findings reach **Activity** but nothing is enforced. `stream: "buffer"` holds each text block until it ends, evaluates it, then releases it, masked or replaced. That enforces the policy and gives up streaming.
118
+
119
+ **Failures.** With the default `failMode: "closed"`, an evaluation error throws on the prompt and the response, and denies the tool call. `failMode: "open"` lets the traffic through. `onError` sees every error either way, and `onVerdict` every verdict.
120
+
121
+ **Not evaluated.** The system prompt, which your code writes. Files in the user turn, such as images and PDFs: only their text parts are sent. Reasoning parts of the response, which pass through unchanged.
122
+
123
+ **Approvals.** The answer to an approval request comes back in the message history the browser sends. Set the AI SDK's `experimental_toolApprovalSecret` so it signs each request and a client cannot forge an approval. When the answer arrives, the AI SDK calls `toolApproval` again: a call the policy now blocks stays denied even after the user allowed it.
124
+
125
+ **Composing approvals.** `toolApproval` returns `undefined` when TrustGuard lets a call through, so your own rules can follow it:
126
+
127
+ ```typescript
128
+ toolApproval: async (options) => (await tg.toolApproval(options)) ?? myApproval(options),
129
+ ```
130
+
81
131
  ## Documentation
82
132
 
83
133
  The full guide is at [docs.neuraltrust.ai](https://docs.neuraltrust.ai/sdks/trustguard/node): setting up the collector
@@ -0,0 +1,373 @@
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/ai-sdk/index.ts
21
+ var ai_sdk_exports = {};
22
+ __export(ai_sdk_exports, {
23
+ TrustGuardBlockedError: () => TrustGuardBlockedError,
24
+ trustguard: () => trustguard
25
+ });
26
+ module.exports = __toCommonJS(ai_sdk_exports);
27
+
28
+ // src/ai-sdk/trustguard.ts
29
+ var TrustGuardBlockedError = class extends Error {
30
+ phase;
31
+ response;
32
+ constructor(phase, response, message) {
33
+ super(message);
34
+ this.name = "TrustGuardBlockedError";
35
+ this.phase = phase;
36
+ this.response = response;
37
+ }
38
+ };
39
+ var SOURCE_APPLICATION = "vercel-ai-sdk";
40
+ var DEFAULT_BLOCKED_MESSAGE = "This response was blocked by policy.";
41
+ function trustguard(client, options = {}) {
42
+ const guard = new Guard(client, options);
43
+ return {
44
+ middleware: guard.middleware(),
45
+ toolApproval: (approval) => guard.approveToolCall(approval.toolCall),
46
+ tools: (tools) => guard.wrapTools(tools)
47
+ };
48
+ }
49
+ var Guard = class {
50
+ #client;
51
+ #options;
52
+ constructor(client, options) {
53
+ this.#client = client;
54
+ this.#options = options;
55
+ }
56
+ middleware() {
57
+ return {
58
+ specificationVersion: "v4",
59
+ transformParams: async ({ params, model }) => this.#guardPrompt(params, model),
60
+ wrapGenerate: async ({ doGenerate, model }) => {
61
+ const result = await doGenerate();
62
+ return { ...result, content: await this.#guardContent(result.content, model) };
63
+ },
64
+ wrapStream: async ({ doStream, model }) => {
65
+ const result = await doStream();
66
+ const transform = this.#options.stream === "buffer" ? this.#bufferStream(model) : this.#monitorStream(model);
67
+ return { ...result, stream: result.stream.pipeThrough(transform) };
68
+ }
69
+ };
70
+ }
71
+ async approveToolCall(call) {
72
+ let response;
73
+ try {
74
+ response = await this.#evaluate("tool-call", {
75
+ payload: toolsCall(call.toolName, call.input),
76
+ direction: "input",
77
+ protocol: "mcp",
78
+ attributes: { tool: { name: call.toolName } }
79
+ });
80
+ } catch (error) {
81
+ if (this.#failOpen("tool-call", error)) return void 0;
82
+ return { type: "denied", reason: "TrustGuard could not evaluate this tool call." };
83
+ }
84
+ switch (response.status) {
85
+ case "block":
86
+ return { type: "denied", reason: reasonFor(response, "Blocked by TrustGuard") };
87
+ case "ask":
88
+ return this.#options.toolAsk === "deny" ? { type: "denied", reason: reasonFor(response, "TrustGuard requires approval") } : { type: "user-approval", reason: reasonFor(response, "TrustGuard requires approval") };
89
+ case "transform":
90
+ return {
91
+ type: "denied",
92
+ reason: reasonFor(response, "Blocked by TrustGuard because the arguments contain data the policy masks")
93
+ };
94
+ default:
95
+ return void 0;
96
+ }
97
+ }
98
+ wrapTools(tools) {
99
+ const wrapped = {};
100
+ for (const [name, tool] of Object.entries(tools)) {
101
+ const execute = tool.execute;
102
+ if (typeof execute !== "function") {
103
+ wrapped[name] = tool;
104
+ continue;
105
+ }
106
+ wrapped[name] = {
107
+ ...tool,
108
+ execute: (input, executeOptions) => {
109
+ const output = execute.call(tool, input, executeOptions);
110
+ return isAsyncIterable(output) ? this.#guardIterableResult(name, output) : Promise.resolve(output).then((value) => this.#guardResult(name, value));
111
+ }
112
+ };
113
+ }
114
+ return wrapped;
115
+ }
116
+ async #guardPrompt(params, model) {
117
+ const index = lastUserTurn(params.prompt);
118
+ if (index < 0) return params;
119
+ const message = params.prompt[index];
120
+ const text = joinText(message.content);
121
+ if (!text.trim()) return params;
122
+ let response;
123
+ try {
124
+ response = await this.#evaluate("input", {
125
+ payload: { input: text },
126
+ direction: "input",
127
+ protocol: "llm",
128
+ attributes: modelAttributes(model)
129
+ });
130
+ } catch (error) {
131
+ if (this.#failOpen("input", error)) return params;
132
+ throw error;
133
+ }
134
+ const status = response.status === "ask" && this.#options.promptAsk !== "allow" ? "block" : response.status;
135
+ if (status === "block") {
136
+ throw new TrustGuardBlockedError("input", response, reasonFor(response, "Prompt blocked by TrustGuard"));
137
+ }
138
+ if (status !== "transform") return params;
139
+ const masked = transformedText(response, sentInput);
140
+ if (masked === void 0) {
141
+ throw new TrustGuardBlockedError(
142
+ "input",
143
+ response,
144
+ "Prompt blocked by TrustGuard: the policy masked it but returned no masked text"
145
+ );
146
+ }
147
+ const prompt = [...params.prompt];
148
+ prompt[index] = { ...message, content: replaceText(message.content, masked) };
149
+ return { ...params, prompt };
150
+ }
151
+ async #guardContent(content, model) {
152
+ const text = joinText(content);
153
+ if (!text.trim()) return content;
154
+ const replacement = await this.#outputReplacement(text, model);
155
+ return replacement === void 0 ? content : replaceText(content, replacement);
156
+ }
157
+ /** The text that must replace a response, or undefined when it can go out as is. Throws when failing closed. */
158
+ async #outputReplacement(text, model) {
159
+ let response;
160
+ try {
161
+ response = await this.#evaluate("output", {
162
+ payload: assistantTurn(text),
163
+ direction: "output",
164
+ protocol: "llm",
165
+ attributes: modelAttributes(model)
166
+ });
167
+ } catch (error) {
168
+ if (this.#failOpen("output", error)) return void 0;
169
+ throw error;
170
+ }
171
+ if (response.status === "block") return this.#blockedMessage();
172
+ if (response.status === "transform") return transformedText(response, sentAssistant) ?? this.#blockedMessage();
173
+ return void 0;
174
+ }
175
+ #monitorStream(model) {
176
+ let text = "";
177
+ const evaluate = async () => {
178
+ if (!text.trim()) return;
179
+ try {
180
+ await this.#evaluate("output", {
181
+ payload: assistantTurn(text),
182
+ direction: "output",
183
+ protocol: "llm",
184
+ attributes: modelAttributes(model)
185
+ });
186
+ } catch (error) {
187
+ this.#options.onError?.({ phase: "output", error });
188
+ }
189
+ };
190
+ return new TransformStream({
191
+ transform(part, controller) {
192
+ if (part.type === "text-delta") text += part.delta;
193
+ controller.enqueue(part);
194
+ },
195
+ // The stream does not close until this resolves, so a serverless function stays alive until the evaluation is sent.
196
+ async flush() {
197
+ await evaluate();
198
+ }
199
+ });
200
+ }
201
+ #bufferStream(model) {
202
+ const open = /* @__PURE__ */ new Map();
203
+ const release = async (id, end, controller) => {
204
+ const parts = open.get(id) ?? [];
205
+ open.delete(id);
206
+ const text = parts.map((p) => p.type === "text-delta" ? p.delta : "").join("");
207
+ const replacement = text.trim() ? await this.#outputReplacement(text, model) : void 0;
208
+ if (replacement === void 0) {
209
+ for (const p of parts) controller.enqueue(p);
210
+ } else {
211
+ for (const p of parts) if (p.type !== "text-delta") controller.enqueue(p);
212
+ controller.enqueue({ type: "text-delta", id, delta: replacement });
213
+ }
214
+ if (end) controller.enqueue(end);
215
+ };
216
+ return new TransformStream({
217
+ async transform(part, controller) {
218
+ switch (part.type) {
219
+ case "text-start":
220
+ open.set(part.id, [part]);
221
+ return;
222
+ case "text-delta": {
223
+ const parts = open.get(part.id);
224
+ if (parts) parts.push(part);
225
+ else controller.enqueue(part);
226
+ return;
227
+ }
228
+ case "text-end":
229
+ if (open.has(part.id)) await release(part.id, part, controller);
230
+ else controller.enqueue(part);
231
+ return;
232
+ default:
233
+ controller.enqueue(part);
234
+ }
235
+ },
236
+ async flush(controller) {
237
+ for (const id of [...open.keys()]) await release(id, void 0, controller);
238
+ }
239
+ });
240
+ }
241
+ async #guardResult(toolName, output) {
242
+ const text = typeof output === "string" ? output : JSON.stringify(output);
243
+ if (text === void 0 || !text.trim()) return output;
244
+ let response;
245
+ try {
246
+ response = await this.#evaluate("tool-result", {
247
+ payload: toolsResult(text),
248
+ direction: "output",
249
+ protocol: "mcp",
250
+ attributes: { tool: { name: toolName } }
251
+ });
252
+ } catch (error) {
253
+ if (this.#failOpen("tool-result", error)) return output;
254
+ throw error;
255
+ }
256
+ if (response.status === "block") {
257
+ throw new TrustGuardBlockedError(
258
+ "tool-result",
259
+ response,
260
+ reasonFor(response, `TrustGuard withheld the result of ${toolName}`)
261
+ );
262
+ }
263
+ if (response.status !== "transform") return output;
264
+ const masked = transformedText(response, sentToolResult);
265
+ if (masked === void 0) {
266
+ throw new TrustGuardBlockedError(
267
+ "tool-result",
268
+ response,
269
+ `TrustGuard withheld the result of ${toolName}: the policy masked it but returned no masked text`
270
+ );
271
+ }
272
+ if (typeof output === "string") return masked;
273
+ try {
274
+ return JSON.parse(masked);
275
+ } catch {
276
+ return masked;
277
+ }
278
+ }
279
+ async *#guardIterableResult(toolName, output) {
280
+ let last;
281
+ let seen = false;
282
+ for await (const value of output) {
283
+ last = value;
284
+ seen = true;
285
+ yield value;
286
+ }
287
+ if (!seen) return;
288
+ const guarded = await this.#guardResult(toolName, last);
289
+ if (guarded !== last) yield guarded;
290
+ }
291
+ async #evaluate(phase, request) {
292
+ const response = await this.#client.guard({
293
+ ...request,
294
+ sessionId: this.#options.sessionId,
295
+ consumerId: this.#options.consumerId,
296
+ attributes: {
297
+ ...this.#options.attributes,
298
+ ...request.attributes,
299
+ source: { application: SOURCE_APPLICATION }
300
+ }
301
+ });
302
+ this.#options.onVerdict?.({ phase, response });
303
+ return response;
304
+ }
305
+ #failOpen(phase, error) {
306
+ this.#options.onError?.({ phase, error });
307
+ return this.#options.failMode === "open";
308
+ }
309
+ #blockedMessage() {
310
+ return this.#options.blockedMessage ?? DEFAULT_BLOCKED_MESSAGE;
311
+ }
312
+ };
313
+ function lastUserTurn(prompt) {
314
+ const last = prompt.length - 1;
315
+ return last >= 0 && prompt[last]?.role === "user" ? last : -1;
316
+ }
317
+ function joinText(parts) {
318
+ return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n");
319
+ }
320
+ function replaceText(parts, text) {
321
+ let placed = false;
322
+ const out = [];
323
+ for (const part of parts) {
324
+ if (part.type !== "text") {
325
+ out.push(part);
326
+ } else if (!placed) {
327
+ out.push({ ...part, text });
328
+ placed = true;
329
+ }
330
+ }
331
+ return out;
332
+ }
333
+ function transformedText(response, at) {
334
+ const payload = response.transformedPayload;
335
+ const text = payload ? at(payload) : void 0;
336
+ return typeof text === "string" ? text : void 0;
337
+ }
338
+ var sentInput = (payload) => payload.input;
339
+ var sentAssistant = (payload) => firstItem(payload.messages)?.content;
340
+ var sentToolResult = (payload) => firstItem(asRecord(payload.result)?.content)?.text;
341
+ function firstItem(value) {
342
+ return Array.isArray(value) ? asRecord(value[0]) : void 0;
343
+ }
344
+ function asRecord(value) {
345
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
346
+ }
347
+ function assistantTurn(text) {
348
+ return { messages: [{ role: "assistant", content: text }] };
349
+ }
350
+ function toolsResult(text) {
351
+ return { jsonrpc: "2.0", id: 1, result: { content: [{ type: "text", text }] } };
352
+ }
353
+ function toolsCall(name, input) {
354
+ const args = input !== null && typeof input === "object" && !Array.isArray(input) ? input : { input };
355
+ return { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name, arguments: args } };
356
+ }
357
+ function modelAttributes(model) {
358
+ return { model: { name: model.modelId, provider: model.provider } };
359
+ }
360
+ function reasonFor(response, reason) {
361
+ const finding = response.findings.find((f) => f.outcome?.action === response.status) ?? response.findings.find((f) => f.outcome);
362
+ const name = finding?.source.gateName || finding?.source.detectorName || finding?.signal?.type;
363
+ return name ? `${reason}: ${name}` : reason;
364
+ }
365
+ function isAsyncIterable(value) {
366
+ return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
367
+ }
368
+ // Annotate the CommonJS export names for ESM import in node:
369
+ 0 && (module.exports = {
370
+ TrustGuardBlockedError,
371
+ trustguard
372
+ });
373
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ai-sdk/index.ts","../../src/ai-sdk/trustguard.ts"],"sourcesContent":["export { trustguard, TrustGuardBlockedError } from \"./trustguard.js\";\nexport type { ApprovalToolCall, GuardPhase, TrustGuardAISDK, TrustGuardAISDKOptions } from \"./trustguard.js\";\n","import type {\n LanguageModelV4,\n LanguageModelV4CallOptions,\n LanguageModelV4Content,\n LanguageModelV4Middleware,\n LanguageModelV4Prompt,\n LanguageModelV4StreamPart,\n} from \"@ai-sdk/provider\";\nimport type { ToolApprovalStatus, ToolSet } from \"ai\";\n\nimport type { TrustGuard } from \"../client.js\";\nimport type { GuardRequest, GuardResponse } from \"../types.js\";\n\n/** Where in the agent loop an evaluation ran. */\nexport type GuardPhase = \"input\" | \"output\" | \"tool-call\" | \"tool-result\";\n\n/** Options for {@link trustguard}. Create one instance per request so every evaluation carries the right user. */\nexport interface TrustGuardAISDKOptions {\n /** The end user the request runs for. Findings are grouped under it and gates match it as `consumer.id`. */\n consumerId?: string;\n /** The conversation key. Without it TrustGuard synthesises one per evaluation and turns lose their grouping. */\n sessionId?: string;\n /** Extra attributes merged into every evaluation, for gates and rule conditions. */\n attributes?: Record<string, unknown>;\n /**\n * How streamed responses are guarded.\n * - `\"monitor\"` (default): text streams untouched and is evaluated when the response finishes. Findings are recorded, nothing is enforced.\n * - `\"buffer\"`: each text block is held until it ends, evaluated, then released, redacted or replaced. Enforces the policy at the cost of streaming.\n */\n stream?: \"monitor\" | \"buffer\";\n /** What an `ask` verdict on the prompt does. The model call has no one to ask, so the default is `\"block\"`. */\n promptAsk?: \"block\" | \"allow\";\n /** What an `ask` verdict on a tool call does. The default, `\"user-approval\"`, asks the user through the AI SDK approval flow. */\n toolAsk?: \"user-approval\" | \"deny\";\n /** Text that replaces a response the policy blocked. */\n blockedMessage?: string;\n /**\n * What happens when TrustGuard cannot be reached or returns an error.\n * `\"closed\"` (default) throws on the prompt and the response and denies the tool call. `\"open\"` lets the traffic through.\n * Monitor-mode stream evaluations never fail the stream.\n */\n failMode?: \"closed\" | \"open\";\n /** Called with every verdict, for logging. */\n onVerdict?: (event: { phase: GuardPhase; response: GuardResponse }) => void;\n /** Called with every evaluation error, including the ones `failMode: \"open\"` swallows. */\n onError?: (event: { phase: GuardPhase; error: unknown }) => void;\n}\n\n/** Thrown when the policy blocks the prompt or the response, and by guarded tools whose result was withheld. */\nexport class TrustGuardBlockedError extends Error {\n readonly phase: GuardPhase;\n readonly response: GuardResponse;\n\n constructor(phase: GuardPhase, response: GuardResponse, message: string) {\n super(message);\n this.name = \"TrustGuardBlockedError\";\n this.phase = phase;\n this.response = response;\n }\n}\n\n/** The tool call the approval function receives. Structural, so it matches static and dynamic tool calls alike. */\nexport interface ApprovalToolCall {\n toolName: string;\n toolCallId: string;\n input: unknown;\n}\n\nexport interface TrustGuardAISDK {\n /** Language model middleware for `wrapLanguageModel`: guards the prompt and the response. */\n middleware: LanguageModelV4Middleware;\n /** A `toolApproval` function for `generateText` / `streamText`: guards each tool call before it runs. */\n toolApproval: (options: { toolCall: ApprovalToolCall }) => Promise<ToolApprovalStatus>;\n /** Wraps a tool set so every result is evaluated before the model reads it. */\n tools: <TOOLS extends ToolSet>(tools: TOOLS) => TOOLS;\n}\n\nconst SOURCE_APPLICATION = \"vercel-ai-sdk\";\nconst DEFAULT_BLOCKED_MESSAGE = \"This response was blocked by policy.\";\n\n/**\n * Connects a TrustGuard client to the Vercel AI SDK.\n *\n * ```ts\n * const tg = trustguard(client, { consumerId: user.id, sessionId: chatId });\n * streamText({\n * model: wrapLanguageModel({ model, middleware: tg.middleware }),\n * tools: tg.tools(tools),\n * toolApproval: tg.toolApproval,\n * messages,\n * });\n * ```\n */\nexport function trustguard(client: TrustGuard, options: TrustGuardAISDKOptions = {}): TrustGuardAISDK {\n const guard = new Guard(client, options);\n return {\n middleware: guard.middleware(),\n toolApproval: (approval) => guard.approveToolCall(approval.toolCall),\n tools: (tools) => guard.wrapTools(tools),\n };\n}\n\nclass Guard {\n readonly #client: TrustGuard;\n readonly #options: TrustGuardAISDKOptions;\n\n constructor(client: TrustGuard, options: TrustGuardAISDKOptions) {\n this.#client = client;\n this.#options = options;\n }\n\n middleware(): LanguageModelV4Middleware {\n return {\n specificationVersion: \"v4\",\n transformParams: async ({ params, model }) => this.#guardPrompt(params, model),\n wrapGenerate: async ({ doGenerate, model }) => {\n const result = await doGenerate();\n return { ...result, content: await this.#guardContent(result.content, model) };\n },\n wrapStream: async ({ doStream, model }) => {\n const result = await doStream();\n const transform =\n this.#options.stream === \"buffer\" ? this.#bufferStream(model) : this.#monitorStream(model);\n return { ...result, stream: result.stream.pipeThrough(transform) };\n },\n };\n }\n\n async approveToolCall(call: ApprovalToolCall): Promise<ToolApprovalStatus> {\n let response: GuardResponse | undefined;\n try {\n response = await this.#evaluate(\"tool-call\", {\n payload: toolsCall(call.toolName, call.input),\n direction: \"input\",\n protocol: \"mcp\",\n attributes: { tool: { name: call.toolName } },\n });\n } catch (error) {\n if (this.#failOpen(\"tool-call\", error)) return undefined;\n return { type: \"denied\", reason: \"TrustGuard could not evaluate this tool call.\" };\n }\n\n switch (response.status) {\n case \"block\":\n return { type: \"denied\", reason: reasonFor(response, \"Blocked by TrustGuard\") };\n case \"ask\":\n return this.#options.toolAsk === \"deny\"\n ? { type: \"denied\", reason: reasonFor(response, \"TrustGuard requires approval\") }\n : { type: \"user-approval\", reason: reasonFor(response, \"TrustGuard requires approval\") };\n case \"transform\":\n // Approval cannot rewrite the arguments, and running the call unredacted would leak what the policy masks.\n return {\n type: \"denied\",\n reason: reasonFor(response, \"Blocked by TrustGuard because the arguments contain data the policy masks\"),\n };\n default:\n return undefined;\n }\n }\n\n wrapTools<TOOLS extends ToolSet>(tools: TOOLS): TOOLS {\n const wrapped: Record<string, unknown> = {};\n for (const [name, tool] of Object.entries(tools)) {\n const execute = tool.execute as ((input: unknown, options: unknown) => unknown) | undefined;\n if (typeof execute !== \"function\") {\n wrapped[name] = tool;\n continue;\n }\n wrapped[name] = {\n ...tool,\n execute: (input: unknown, executeOptions: unknown) => {\n const output = execute.call(tool, input, executeOptions);\n return isAsyncIterable(output)\n ? this.#guardIterableResult(name, output)\n : Promise.resolve(output).then((value) => this.#guardResult(name, value));\n },\n };\n }\n return wrapped as TOOLS;\n }\n\n async #guardPrompt(params: LanguageModelV4CallOptions, model: LanguageModelV4): Promise<LanguageModelV4CallOptions> {\n const index = lastUserTurn(params.prompt);\n if (index < 0) return params;\n const message = params.prompt[index] as Extract<LanguageModelV4Prompt[number], { role: \"user\" }>;\n const text = joinText(message.content);\n if (!text.trim()) return params;\n\n let response: GuardResponse;\n try {\n response = await this.#evaluate(\"input\", {\n payload: { input: text },\n direction: \"input\",\n protocol: \"llm\",\n attributes: modelAttributes(model),\n });\n } catch (error) {\n if (this.#failOpen(\"input\", error)) return params;\n throw error;\n }\n\n const status = response.status === \"ask\" && this.#options.promptAsk !== \"allow\" ? \"block\" : response.status;\n if (status === \"block\") {\n throw new TrustGuardBlockedError(\"input\", response, reasonFor(response, \"Prompt blocked by TrustGuard\"));\n }\n if (status !== \"transform\") return params;\n\n const masked = transformedText(response, sentInput);\n if (masked === undefined) {\n throw new TrustGuardBlockedError(\n \"input\",\n response,\n \"Prompt blocked by TrustGuard: the policy masked it but returned no masked text\",\n );\n }\n const prompt = [...params.prompt];\n prompt[index] = { ...message, content: replaceText(message.content, masked) };\n return { ...params, prompt };\n }\n\n async #guardContent(content: LanguageModelV4Content[], model: LanguageModelV4): Promise<LanguageModelV4Content[]> {\n const text = joinText(content);\n if (!text.trim()) return content;\n\n const replacement = await this.#outputReplacement(text, model);\n return replacement === undefined ? content : replaceText(content, replacement);\n }\n\n /** The text that must replace a response, or undefined when it can go out as is. Throws when failing closed. */\n async #outputReplacement(text: string, model: LanguageModelV4): Promise<string | undefined> {\n let response: GuardResponse;\n try {\n response = await this.#evaluate(\"output\", {\n payload: assistantTurn(text),\n direction: \"output\",\n protocol: \"llm\",\n attributes: modelAttributes(model),\n });\n } catch (error) {\n if (this.#failOpen(\"output\", error)) return undefined;\n throw error;\n }\n if (response.status === \"block\") return this.#blockedMessage();\n if (response.status === \"transform\") return transformedText(response, sentAssistant) ?? this.#blockedMessage();\n return undefined;\n }\n\n #monitorStream(model: LanguageModelV4): TransformStream<LanguageModelV4StreamPart, LanguageModelV4StreamPart> {\n let text = \"\";\n const evaluate = async () => {\n if (!text.trim()) return;\n try {\n await this.#evaluate(\"output\", {\n payload: assistantTurn(text),\n direction: \"output\",\n protocol: \"llm\",\n attributes: modelAttributes(model),\n });\n } catch (error) {\n this.#options.onError?.({ phase: \"output\", error });\n }\n };\n return new TransformStream({\n transform(part, controller) {\n if (part.type === \"text-delta\") text += part.delta;\n controller.enqueue(part);\n },\n // The stream does not close until this resolves, so a serverless function stays alive until the evaluation is sent.\n async flush() {\n await evaluate();\n },\n });\n }\n\n #bufferStream(model: LanguageModelV4): TransformStream<LanguageModelV4StreamPart, LanguageModelV4StreamPart> {\n const open = new Map<string, LanguageModelV4StreamPart[]>();\n const release = async (\n id: string,\n end: LanguageModelV4StreamPart | undefined,\n controller: TransformStreamDefaultController<LanguageModelV4StreamPart>,\n ) => {\n const parts = open.get(id) ?? [];\n open.delete(id);\n const text = parts.map((p) => (p.type === \"text-delta\" ? p.delta : \"\")).join(\"\");\n const replacement = text.trim() ? await this.#outputReplacement(text, model) : undefined;\n if (replacement === undefined) {\n for (const p of parts) controller.enqueue(p);\n } else {\n for (const p of parts) if (p.type !== \"text-delta\") controller.enqueue(p);\n controller.enqueue({ type: \"text-delta\", id, delta: replacement });\n }\n if (end) controller.enqueue(end);\n };\n return new TransformStream({\n async transform(part, controller) {\n switch (part.type) {\n case \"text-start\":\n open.set(part.id, [part]);\n return;\n case \"text-delta\": {\n const parts = open.get(part.id);\n if (parts) parts.push(part);\n else controller.enqueue(part);\n return;\n }\n case \"text-end\":\n if (open.has(part.id)) await release(part.id, part, controller);\n else controller.enqueue(part);\n return;\n default:\n controller.enqueue(part);\n }\n },\n async flush(controller) {\n // A stream that ends without text-end still goes through the policy before anything is released.\n for (const id of [...open.keys()]) await release(id, undefined, controller);\n },\n });\n }\n\n async #guardResult(toolName: string, output: unknown): Promise<unknown> {\n const text = typeof output === \"string\" ? output : JSON.stringify(output);\n if (text === undefined || !text.trim()) return output;\n\n let response: GuardResponse;\n try {\n response = await this.#evaluate(\"tool-result\", {\n payload: toolsResult(text),\n direction: \"output\",\n protocol: \"mcp\",\n attributes: { tool: { name: toolName } },\n });\n } catch (error) {\n if (this.#failOpen(\"tool-result\", error)) return output;\n throw error;\n }\n\n if (response.status === \"block\") {\n throw new TrustGuardBlockedError(\n \"tool-result\",\n response,\n reasonFor(response, `TrustGuard withheld the result of ${toolName}`),\n );\n }\n if (response.status !== \"transform\") return output;\n\n const masked = transformedText(response, sentToolResult);\n if (masked === undefined) {\n throw new TrustGuardBlockedError(\n \"tool-result\",\n response,\n `TrustGuard withheld the result of ${toolName}: the policy masked it but returned no masked text`,\n );\n }\n if (typeof output === \"string\") return masked;\n try {\n return JSON.parse(masked) as unknown;\n } catch {\n return masked;\n }\n }\n\n async *#guardIterableResult(toolName: string, output: AsyncIterable<unknown>): AsyncGenerator<unknown> {\n // Preliminary outputs reach the UI only. The last one is what the model reads, so that is the one guarded:\n // when the policy changes it, the guarded value is yielded after it and becomes the final output.\n let last: unknown;\n let seen = false;\n for await (const value of output) {\n last = value;\n seen = true;\n yield value;\n }\n if (!seen) return;\n const guarded = await this.#guardResult(toolName, last);\n if (guarded !== last) yield guarded;\n }\n\n async #evaluate(phase: GuardPhase, request: GuardRequest): Promise<GuardResponse> {\n const response = await this.#client.guard({\n ...request,\n sessionId: this.#options.sessionId,\n consumerId: this.#options.consumerId,\n attributes: {\n ...this.#options.attributes,\n ...request.attributes,\n source: { application: SOURCE_APPLICATION },\n },\n });\n this.#options.onVerdict?.({ phase, response });\n return response;\n }\n\n #failOpen(phase: GuardPhase, error: unknown): boolean {\n this.#options.onError?.({ phase, error });\n return this.#options.failMode === \"open\";\n }\n\n #blockedMessage(): string {\n return this.#options.blockedMessage ?? DEFAULT_BLOCKED_MESSAGE;\n }\n}\n\n/** Index of the user message that starts this model call, or -1 when the call continues a tool loop. */\nfunction lastUserTurn(prompt: LanguageModelV4Prompt): number {\n const last = prompt.length - 1;\n return last >= 0 && prompt[last]?.role === \"user\" ? last : -1;\n}\n\ntype TextLike = { type: string; text?: string };\n\nfunction joinText(parts: readonly TextLike[]): string {\n return parts\n .filter((p) => p.type === \"text\" && typeof p.text === \"string\")\n .map((p) => p.text)\n .join(\"\\n\");\n}\n\n/** Puts `text` in place of the first text part and drops the others, keeping every non-text part where it was. */\nfunction replaceText<T extends TextLike>(parts: readonly T[], text: string): T[] {\n let placed = false;\n const out: T[] = [];\n for (const part of parts) {\n if (part.type !== \"text\") {\n out.push(part);\n } else if (!placed) {\n out.push({ ...part, text });\n placed = true;\n }\n }\n return out;\n}\n\ntype Payload = Record<string, unknown>;\n\n/** The masked text of a transform verdict. TrustGuard returns the payload it was sent, rewritten, so `at` reads it from where it was sent. */\nfunction transformedText(response: GuardResponse, at: (payload: Payload) => unknown): string | undefined {\n const payload = response.transformedPayload;\n const text = payload ? at(payload) : undefined;\n return typeof text === \"string\" ? text : undefined;\n}\n\nconst sentInput = (payload: Payload): unknown => payload.input;\nconst sentAssistant = (payload: Payload): unknown => firstItem(payload.messages)?.content;\nconst sentToolResult = (payload: Payload): unknown => firstItem(asRecord(payload.result)?.content)?.text;\n\nfunction firstItem(value: unknown): Payload | undefined {\n return Array.isArray(value) ? asRecord(value[0]) : undefined;\n}\n\nfunction asRecord(value: unknown): Payload | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value) ? (value as Payload) : undefined;\n}\n\n// TrustGuard reads `{ input }` as the user's message, so on output Toxicity Detection and Custom Moderation would skip the\n// response. It is sent as the assistant turn it is.\nfunction assistantTurn(text: string): Payload {\n return { messages: [{ role: \"assistant\", content: text }] };\n}\n\n// An MCP payload must be a JSON-RPC envelope, so a tool result is sent as the result of the call that produced it.\nfunction toolsResult(text: string): Payload {\n return { jsonrpc: \"2.0\", id: 1, result: { content: [{ type: \"text\", text }] } };\n}\n\nfunction toolsCall(name: string, input: unknown): Record<string, unknown> {\n const args = input !== null && typeof input === \"object\" && !Array.isArray(input) ? input : { input };\n return { jsonrpc: \"2.0\", id: 1, method: \"tools/call\", params: { name, arguments: args } };\n}\n\nfunction modelAttributes(model: LanguageModelV4): Record<string, unknown> {\n return { model: { name: model.modelId, provider: model.provider } };\n}\n\n/** `reason`, followed by the gate or detector that produced the verdict when the response names one. */\nfunction reasonFor(response: GuardResponse, reason: string): string {\n const finding =\n response.findings.find((f) => f.outcome?.action === response.status) ?? response.findings.find((f) => f.outcome);\n const name = finding?.source.gateName || finding?.source.detectorName || finding?.signal?.type;\n return name ? `${reason}: ${name}` : reason;\n}\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return value !== null && typeof value === \"object\" && Symbol.asyncIterator in value;\n}\n\n"],"mappings":"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","names":[]}
@@ -0,0 +1,81 @@
1
+ import { LanguageModelV4Middleware } from '@ai-sdk/provider';
2
+ import { ToolApprovalStatus, ToolSet } from 'ai';
3
+ import { G as GuardResponse, T as TrustGuard } from '../client-ZoD3M6yg.cjs';
4
+
5
+ /** Where in the agent loop an evaluation ran. */
6
+ type GuardPhase = "input" | "output" | "tool-call" | "tool-result";
7
+ /** Options for {@link trustguard}. Create one instance per request so every evaluation carries the right user. */
8
+ interface TrustGuardAISDKOptions {
9
+ /** The end user the request runs for. Findings are grouped under it and gates match it as `consumer.id`. */
10
+ consumerId?: string;
11
+ /** The conversation key. Without it TrustGuard synthesises one per evaluation and turns lose their grouping. */
12
+ sessionId?: string;
13
+ /** Extra attributes merged into every evaluation, for gates and rule conditions. */
14
+ attributes?: Record<string, unknown>;
15
+ /**
16
+ * How streamed responses are guarded.
17
+ * - `"monitor"` (default): text streams untouched and is evaluated when the response finishes. Findings are recorded, nothing is enforced.
18
+ * - `"buffer"`: each text block is held until it ends, evaluated, then released, redacted or replaced. Enforces the policy at the cost of streaming.
19
+ */
20
+ stream?: "monitor" | "buffer";
21
+ /** What an `ask` verdict on the prompt does. The model call has no one to ask, so the default is `"block"`. */
22
+ promptAsk?: "block" | "allow";
23
+ /** What an `ask` verdict on a tool call does. The default, `"user-approval"`, asks the user through the AI SDK approval flow. */
24
+ toolAsk?: "user-approval" | "deny";
25
+ /** Text that replaces a response the policy blocked. */
26
+ blockedMessage?: string;
27
+ /**
28
+ * What happens when TrustGuard cannot be reached or returns an error.
29
+ * `"closed"` (default) throws on the prompt and the response and denies the tool call. `"open"` lets the traffic through.
30
+ * Monitor-mode stream evaluations never fail the stream.
31
+ */
32
+ failMode?: "closed" | "open";
33
+ /** Called with every verdict, for logging. */
34
+ onVerdict?: (event: {
35
+ phase: GuardPhase;
36
+ response: GuardResponse;
37
+ }) => void;
38
+ /** Called with every evaluation error, including the ones `failMode: "open"` swallows. */
39
+ onError?: (event: {
40
+ phase: GuardPhase;
41
+ error: unknown;
42
+ }) => void;
43
+ }
44
+ /** Thrown when the policy blocks the prompt or the response, and by guarded tools whose result was withheld. */
45
+ declare class TrustGuardBlockedError extends Error {
46
+ readonly phase: GuardPhase;
47
+ readonly response: GuardResponse;
48
+ constructor(phase: GuardPhase, response: GuardResponse, message: string);
49
+ }
50
+ /** The tool call the approval function receives. Structural, so it matches static and dynamic tool calls alike. */
51
+ interface ApprovalToolCall {
52
+ toolName: string;
53
+ toolCallId: string;
54
+ input: unknown;
55
+ }
56
+ interface TrustGuardAISDK {
57
+ /** Language model middleware for `wrapLanguageModel`: guards the prompt and the response. */
58
+ middleware: LanguageModelV4Middleware;
59
+ /** A `toolApproval` function for `generateText` / `streamText`: guards each tool call before it runs. */
60
+ toolApproval: (options: {
61
+ toolCall: ApprovalToolCall;
62
+ }) => Promise<ToolApprovalStatus>;
63
+ /** Wraps a tool set so every result is evaluated before the model reads it. */
64
+ tools: <TOOLS extends ToolSet>(tools: TOOLS) => TOOLS;
65
+ }
66
+ /**
67
+ * Connects a TrustGuard client to the Vercel AI SDK.
68
+ *
69
+ * ```ts
70
+ * const tg = trustguard(client, { consumerId: user.id, sessionId: chatId });
71
+ * streamText({
72
+ * model: wrapLanguageModel({ model, middleware: tg.middleware }),
73
+ * tools: tg.tools(tools),
74
+ * toolApproval: tg.toolApproval,
75
+ * messages,
76
+ * });
77
+ * ```
78
+ */
79
+ declare function trustguard(client: TrustGuard, options?: TrustGuardAISDKOptions): TrustGuardAISDK;
80
+
81
+ export { type ApprovalToolCall, type GuardPhase, type TrustGuardAISDK, type TrustGuardAISDKOptions, TrustGuardBlockedError, trustguard };
@@ -0,0 +1,81 @@
1
+ import { LanguageModelV4Middleware } from '@ai-sdk/provider';
2
+ import { ToolApprovalStatus, ToolSet } from 'ai';
3
+ import { G as GuardResponse, T as TrustGuard } from '../client-ZoD3M6yg.js';
4
+
5
+ /** Where in the agent loop an evaluation ran. */
6
+ type GuardPhase = "input" | "output" | "tool-call" | "tool-result";
7
+ /** Options for {@link trustguard}. Create one instance per request so every evaluation carries the right user. */
8
+ interface TrustGuardAISDKOptions {
9
+ /** The end user the request runs for. Findings are grouped under it and gates match it as `consumer.id`. */
10
+ consumerId?: string;
11
+ /** The conversation key. Without it TrustGuard synthesises one per evaluation and turns lose their grouping. */
12
+ sessionId?: string;
13
+ /** Extra attributes merged into every evaluation, for gates and rule conditions. */
14
+ attributes?: Record<string, unknown>;
15
+ /**
16
+ * How streamed responses are guarded.
17
+ * - `"monitor"` (default): text streams untouched and is evaluated when the response finishes. Findings are recorded, nothing is enforced.
18
+ * - `"buffer"`: each text block is held until it ends, evaluated, then released, redacted or replaced. Enforces the policy at the cost of streaming.
19
+ */
20
+ stream?: "monitor" | "buffer";
21
+ /** What an `ask` verdict on the prompt does. The model call has no one to ask, so the default is `"block"`. */
22
+ promptAsk?: "block" | "allow";
23
+ /** What an `ask` verdict on a tool call does. The default, `"user-approval"`, asks the user through the AI SDK approval flow. */
24
+ toolAsk?: "user-approval" | "deny";
25
+ /** Text that replaces a response the policy blocked. */
26
+ blockedMessage?: string;
27
+ /**
28
+ * What happens when TrustGuard cannot be reached or returns an error.
29
+ * `"closed"` (default) throws on the prompt and the response and denies the tool call. `"open"` lets the traffic through.
30
+ * Monitor-mode stream evaluations never fail the stream.
31
+ */
32
+ failMode?: "closed" | "open";
33
+ /** Called with every verdict, for logging. */
34
+ onVerdict?: (event: {
35
+ phase: GuardPhase;
36
+ response: GuardResponse;
37
+ }) => void;
38
+ /** Called with every evaluation error, including the ones `failMode: "open"` swallows. */
39
+ onError?: (event: {
40
+ phase: GuardPhase;
41
+ error: unknown;
42
+ }) => void;
43
+ }
44
+ /** Thrown when the policy blocks the prompt or the response, and by guarded tools whose result was withheld. */
45
+ declare class TrustGuardBlockedError extends Error {
46
+ readonly phase: GuardPhase;
47
+ readonly response: GuardResponse;
48
+ constructor(phase: GuardPhase, response: GuardResponse, message: string);
49
+ }
50
+ /** The tool call the approval function receives. Structural, so it matches static and dynamic tool calls alike. */
51
+ interface ApprovalToolCall {
52
+ toolName: string;
53
+ toolCallId: string;
54
+ input: unknown;
55
+ }
56
+ interface TrustGuardAISDK {
57
+ /** Language model middleware for `wrapLanguageModel`: guards the prompt and the response. */
58
+ middleware: LanguageModelV4Middleware;
59
+ /** A `toolApproval` function for `generateText` / `streamText`: guards each tool call before it runs. */
60
+ toolApproval: (options: {
61
+ toolCall: ApprovalToolCall;
62
+ }) => Promise<ToolApprovalStatus>;
63
+ /** Wraps a tool set so every result is evaluated before the model reads it. */
64
+ tools: <TOOLS extends ToolSet>(tools: TOOLS) => TOOLS;
65
+ }
66
+ /**
67
+ * Connects a TrustGuard client to the Vercel AI SDK.
68
+ *
69
+ * ```ts
70
+ * const tg = trustguard(client, { consumerId: user.id, sessionId: chatId });
71
+ * streamText({
72
+ * model: wrapLanguageModel({ model, middleware: tg.middleware }),
73
+ * tools: tg.tools(tools),
74
+ * toolApproval: tg.toolApproval,
75
+ * messages,
76
+ * });
77
+ * ```
78
+ */
79
+ declare function trustguard(client: TrustGuard, options?: TrustGuardAISDKOptions): TrustGuardAISDK;
80
+
81
+ export { type ApprovalToolCall, type GuardPhase, type TrustGuardAISDK, type TrustGuardAISDKOptions, TrustGuardBlockedError, trustguard };