@neuraltrust/trustguard-sdk 0.1.5 → 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 +8 -2
- package/dist/ai-sdk/index.cjs +25 -9
- package/dist/ai-sdk/index.cjs.map +1 -1
- package/dist/ai-sdk/index.js +25 -9
- package/dist/ai-sdk/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,8 @@ if (response.isBlocked) {
|
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
35
|
const response = await client.guard({
|
|
36
|
-
|
|
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
|
|
@@ -88,7 +89,7 @@ import { openai } from "@ai-sdk/openai";
|
|
|
88
89
|
import { TrustGuard } from "@neuraltrust/trustguard-sdk";
|
|
89
90
|
import { trustguard } from "@neuraltrust/trustguard-sdk/ai-sdk";
|
|
90
91
|
|
|
91
|
-
const client = new TrustGuard({ baseUrl: process.env.
|
|
92
|
+
const client = new TrustGuard({ baseUrl: process.env.TRUSTGUARD_BASE_URL!, apiKey: process.env.TRUSTGUARD_API_KEY! });
|
|
92
93
|
|
|
93
94
|
// One instance per request, so every evaluation carries the user and the conversation.
|
|
94
95
|
const tg = trustguard(client, { consumerId: user.id, sessionId: chatId });
|
|
@@ -97,6 +98,7 @@ const result = streamText({
|
|
|
97
98
|
model: wrapLanguageModel({ model: openai("gpt-5.2"), middleware: tg.middleware }),
|
|
98
99
|
tools: tg.tools(tools),
|
|
99
100
|
toolApproval: tg.toolApproval,
|
|
101
|
+
experimental_toolApprovalSecret: process.env.TOOL_APPROVAL_SECRET, // signs approval requests
|
|
100
102
|
messages,
|
|
101
103
|
});
|
|
102
104
|
```
|
|
@@ -110,12 +112,16 @@ const result = streamText({
|
|
|
110
112
|
|
|
111
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())`.
|
|
112
114
|
|
|
115
|
+
A complete Next.js app, with approvals and a live verdict panel, is in [`examples/ai-sdk`](../examples/ai-sdk).
|
|
116
|
+
|
|
113
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.
|
|
114
118
|
|
|
115
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.
|
|
116
120
|
|
|
117
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.
|
|
118
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
|
+
|
|
119
125
|
**Composing approvals.** `toolApproval` returns `undefined` when TrustGuard lets a call through, so your own rules can follow it:
|
|
120
126
|
|
|
121
127
|
```typescript
|
package/dist/ai-sdk/index.cjs
CHANGED
|
@@ -136,7 +136,7 @@ var Guard = class {
|
|
|
136
136
|
throw new TrustGuardBlockedError("input", response, reasonFor(response, "Prompt blocked by TrustGuard"));
|
|
137
137
|
}
|
|
138
138
|
if (status !== "transform") return params;
|
|
139
|
-
const masked = transformedText(response);
|
|
139
|
+
const masked = transformedText(response, sentInput);
|
|
140
140
|
if (masked === void 0) {
|
|
141
141
|
throw new TrustGuardBlockedError(
|
|
142
142
|
"input",
|
|
@@ -159,7 +159,7 @@ var Guard = class {
|
|
|
159
159
|
let response;
|
|
160
160
|
try {
|
|
161
161
|
response = await this.#evaluate("output", {
|
|
162
|
-
payload:
|
|
162
|
+
payload: assistantTurn(text),
|
|
163
163
|
direction: "output",
|
|
164
164
|
protocol: "llm",
|
|
165
165
|
attributes: modelAttributes(model)
|
|
@@ -169,7 +169,7 @@ var Guard = class {
|
|
|
169
169
|
throw error;
|
|
170
170
|
}
|
|
171
171
|
if (response.status === "block") return this.#blockedMessage();
|
|
172
|
-
if (response.status === "transform") return transformedText(response) ?? this.#blockedMessage();
|
|
172
|
+
if (response.status === "transform") return transformedText(response, sentAssistant) ?? this.#blockedMessage();
|
|
173
173
|
return void 0;
|
|
174
174
|
}
|
|
175
175
|
#monitorStream(model) {
|
|
@@ -178,7 +178,7 @@ var Guard = class {
|
|
|
178
178
|
if (!text.trim()) return;
|
|
179
179
|
try {
|
|
180
180
|
await this.#evaluate("output", {
|
|
181
|
-
payload:
|
|
181
|
+
payload: assistantTurn(text),
|
|
182
182
|
direction: "output",
|
|
183
183
|
protocol: "llm",
|
|
184
184
|
attributes: modelAttributes(model)
|
|
@@ -244,7 +244,7 @@ var Guard = class {
|
|
|
244
244
|
let response;
|
|
245
245
|
try {
|
|
246
246
|
response = await this.#evaluate("tool-result", {
|
|
247
|
-
payload:
|
|
247
|
+
payload: toolsResult(text),
|
|
248
248
|
direction: "output",
|
|
249
249
|
protocol: "mcp",
|
|
250
250
|
attributes: { tool: { name: toolName } }
|
|
@@ -261,7 +261,7 @@ var Guard = class {
|
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
263
|
if (response.status !== "transform") return output;
|
|
264
|
-
const masked = transformedText(response);
|
|
264
|
+
const masked = transformedText(response, sentToolResult);
|
|
265
265
|
if (masked === void 0) {
|
|
266
266
|
throw new TrustGuardBlockedError(
|
|
267
267
|
"tool-result",
|
|
@@ -330,9 +330,25 @@ function replaceText(parts, text) {
|
|
|
330
330
|
}
|
|
331
331
|
return out;
|
|
332
332
|
}
|
|
333
|
-
function transformedText(response) {
|
|
334
|
-
const
|
|
335
|
-
|
|
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 }] } };
|
|
336
352
|
}
|
|
337
353
|
function toolsCall(name, input) {
|
|
338
354
|
const args = input !== null && typeof input === "object" && !Array.isArray(input) ? input : { input };
|
|
@@ -1 +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);\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: { input: 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) ?? 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: { input: 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: { input: 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);\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\nfunction transformedText(response: GuardResponse): string | undefined {\n const input = response.transformedPayload?.input;\n return typeof input === \"string\" ? input : undefined;\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":[]}
|
|
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":[]}
|
package/dist/ai-sdk/index.js
CHANGED
|
@@ -109,7 +109,7 @@ var Guard = class {
|
|
|
109
109
|
throw new TrustGuardBlockedError("input", response, reasonFor(response, "Prompt blocked by TrustGuard"));
|
|
110
110
|
}
|
|
111
111
|
if (status !== "transform") return params;
|
|
112
|
-
const masked = transformedText(response);
|
|
112
|
+
const masked = transformedText(response, sentInput);
|
|
113
113
|
if (masked === void 0) {
|
|
114
114
|
throw new TrustGuardBlockedError(
|
|
115
115
|
"input",
|
|
@@ -132,7 +132,7 @@ var Guard = class {
|
|
|
132
132
|
let response;
|
|
133
133
|
try {
|
|
134
134
|
response = await this.#evaluate("output", {
|
|
135
|
-
payload:
|
|
135
|
+
payload: assistantTurn(text),
|
|
136
136
|
direction: "output",
|
|
137
137
|
protocol: "llm",
|
|
138
138
|
attributes: modelAttributes(model)
|
|
@@ -142,7 +142,7 @@ var Guard = class {
|
|
|
142
142
|
throw error;
|
|
143
143
|
}
|
|
144
144
|
if (response.status === "block") return this.#blockedMessage();
|
|
145
|
-
if (response.status === "transform") return transformedText(response) ?? this.#blockedMessage();
|
|
145
|
+
if (response.status === "transform") return transformedText(response, sentAssistant) ?? this.#blockedMessage();
|
|
146
146
|
return void 0;
|
|
147
147
|
}
|
|
148
148
|
#monitorStream(model) {
|
|
@@ -151,7 +151,7 @@ var Guard = class {
|
|
|
151
151
|
if (!text.trim()) return;
|
|
152
152
|
try {
|
|
153
153
|
await this.#evaluate("output", {
|
|
154
|
-
payload:
|
|
154
|
+
payload: assistantTurn(text),
|
|
155
155
|
direction: "output",
|
|
156
156
|
protocol: "llm",
|
|
157
157
|
attributes: modelAttributes(model)
|
|
@@ -217,7 +217,7 @@ var Guard = class {
|
|
|
217
217
|
let response;
|
|
218
218
|
try {
|
|
219
219
|
response = await this.#evaluate("tool-result", {
|
|
220
|
-
payload:
|
|
220
|
+
payload: toolsResult(text),
|
|
221
221
|
direction: "output",
|
|
222
222
|
protocol: "mcp",
|
|
223
223
|
attributes: { tool: { name: toolName } }
|
|
@@ -234,7 +234,7 @@ var Guard = class {
|
|
|
234
234
|
);
|
|
235
235
|
}
|
|
236
236
|
if (response.status !== "transform") return output;
|
|
237
|
-
const masked = transformedText(response);
|
|
237
|
+
const masked = transformedText(response, sentToolResult);
|
|
238
238
|
if (masked === void 0) {
|
|
239
239
|
throw new TrustGuardBlockedError(
|
|
240
240
|
"tool-result",
|
|
@@ -303,9 +303,25 @@ function replaceText(parts, text) {
|
|
|
303
303
|
}
|
|
304
304
|
return out;
|
|
305
305
|
}
|
|
306
|
-
function transformedText(response) {
|
|
307
|
-
const
|
|
308
|
-
|
|
306
|
+
function transformedText(response, at) {
|
|
307
|
+
const payload = response.transformedPayload;
|
|
308
|
+
const text = payload ? at(payload) : void 0;
|
|
309
|
+
return typeof text === "string" ? text : void 0;
|
|
310
|
+
}
|
|
311
|
+
var sentInput = (payload) => payload.input;
|
|
312
|
+
var sentAssistant = (payload) => firstItem(payload.messages)?.content;
|
|
313
|
+
var sentToolResult = (payload) => firstItem(asRecord(payload.result)?.content)?.text;
|
|
314
|
+
function firstItem(value) {
|
|
315
|
+
return Array.isArray(value) ? asRecord(value[0]) : void 0;
|
|
316
|
+
}
|
|
317
|
+
function asRecord(value) {
|
|
318
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
319
|
+
}
|
|
320
|
+
function assistantTurn(text) {
|
|
321
|
+
return { messages: [{ role: "assistant", content: text }] };
|
|
322
|
+
}
|
|
323
|
+
function toolsResult(text) {
|
|
324
|
+
return { jsonrpc: "2.0", id: 1, result: { content: [{ type: "text", text }] } };
|
|
309
325
|
}
|
|
310
326
|
function toolsCall(name, input) {
|
|
311
327
|
const args = input !== null && typeof input === "object" && !Array.isArray(input) ? input : { input };
|
package/dist/ai-sdk/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ai-sdk/trustguard.ts"],"sourcesContent":["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);\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: { input: 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) ?? 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: { input: 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: { input: 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);\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\nfunction transformedText(response: GuardResponse): string | undefined {\n const input = response.transformedPayload?.input;\n return typeof input === \"string\" ? input : undefined;\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":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/ai-sdk/trustguard.ts"],"sourcesContent":["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":[]}
|