@averta-security/sdk-core 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,26 @@
1
+ # Averta Proprietary SDK License
2
+
3
+ Draft template. Have counsel approve before publishing.
4
+
5
+ Copyright (c) 2026 Averta LTD. All rights reserved.
6
+
7
+ This SDK is licensed, not sold.
8
+
9
+ Subject to an active Averta account, subscription, order form, or other written agreement between you and Averta LTD (the "Agreement"), Averta LTD grants you a limited, non-exclusive, non-transferable, revocable license to install, copy, and use this SDK solely to integrate your applications with Averta services.
10
+
11
+ You may reproduce this SDK only as reasonably necessary for development, testing, build, deployment, backup, and production operation of applications that are authorized to use Averta services.
12
+
13
+ You may not, except as expressly permitted by the Agreement or applicable law:
14
+
15
+ 1. sell, sublicense, redistribute, publish, rent, lease, or make this SDK available as a standalone product;
16
+ 2. use this SDK to build, train, operate, or support a competing product or service;
17
+ 3. reverse engineer, decompile, disassemble, or attempt to derive source code or non-public implementation details from this SDK;
18
+ 4. remove or alter proprietary notices, license notices, or attribution notices;
19
+ 5. use this SDK to access Averta services without authorization or in violation of applicable usage limits, security controls, or documentation;
20
+ 6. use this SDK in a way that violates law, infringes third-party rights, or compromises the security or integrity of Averta services.
21
+
22
+ No rights are granted except those expressly stated in this license or the Agreement. Averta LTD and its licensors retain all right, title, and interest in and to this SDK.
23
+
24
+ This license terminates automatically when your Agreement terminates or when you stop being authorized to use Averta services. Upon termination, you must stop using this SDK and remove it from systems where continued use is not authorized.
25
+
26
+ Support, service-level commitments, warranties, indemnities, liability limits, and governing law are controlled by the Agreement. If no Agreement applies, this SDK is provided "as is" and "as available", without warranties of any kind, and Averta LTD will not be liable for damages arising from use of this SDK.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # @averta-security/sdk-core
2
+
3
+ Shared runtime primitives for Averta JavaScript SDK adapters.
4
+
5
+ Most applications should install a provider adapter such as `@averta-security/sdk-openai` or `@averta-security/sdk-anthropic` instead of importing this package directly. Install `@averta-security/sdk-core` directly only when you are building an Averta adapter or handling shared SDK types and errors.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install @averta-security/sdk-core
11
+ ```
12
+
13
+ Requirements:
14
+
15
+ - Node.js 18 or newer.
16
+ - ESM runtime or bundler support.
17
+
18
+ ## Exports
19
+
20
+ ```ts
21
+ import {
22
+ AvertaDecisionClient,
23
+ AvertaSdkError,
24
+ ensureRequestContext,
25
+ } from "@averta-security/sdk-core";
26
+ import type { AvertaRequestContext, DecisionResponse } from "@averta-security/sdk-core";
27
+ ```
28
+
29
+ The public surface includes:
30
+
31
+ - `AvertaDecisionClient` for low-level calls to Averta's `/v1/decide` endpoint.
32
+ - `AvertaSdkError` for structured SDK errors.
33
+ - Request context helpers for `conversationId`, `requestId`, and `traceId`.
34
+ - Shared lifecycle helpers used by provider adapters.
35
+ - Shared types for decision payloads, provider metadata, tool calls, tool results, and output decisions.
36
+
37
+ ## Decision Client
38
+
39
+ ```ts
40
+ import { AvertaDecisionClient } from "@averta-security/sdk-core";
41
+
42
+ const client = new AvertaDecisionClient({
43
+ avertaApiKey: process.env.AVERTA_API_KEY!,
44
+ sdk: {
45
+ name: "custom-adapter",
46
+ version: "0.1.0",
47
+ },
48
+ });
49
+ ```
50
+
51
+ Options:
52
+
53
+ - `avertaApiKey`: required Averta API key.
54
+ - `avertaBaseUrl`: optional Averta API base URL. Defaults to `https://api.averta.io`.
55
+ - `decisionTimeoutMs`: optional decision request timeout in milliseconds. Defaults to `30000`. Set to `0` to disable the SDK-managed timeout.
56
+ - `fetch`: optional custom `fetch` implementation.
57
+ - `sdk`: adapter name and version metadata sent to Averta.
58
+
59
+ ## Errors
60
+
61
+ SDK failures throw `AvertaSdkError`.
62
+
63
+ ```ts
64
+ try {
65
+ await guardedOperation();
66
+ } catch (error) {
67
+ if (error instanceof AvertaSdkError) {
68
+ console.error(error.code, error.statusCode);
69
+ console.error(error.checkpointDecision);
70
+ }
71
+ }
72
+ ```
73
+
74
+ `checkpointDecision` is present when the error came from a block or failed checkpoint decision.
75
+
76
+ ## Packaging
77
+
78
+ This package publishes built JavaScript and TypeScript declarations from `dist`. TypeScript source, tests, and build configuration are not included in the npm package.
79
+
80
+ This package is proprietary software. See `LICENSE.md`.
@@ -0,0 +1,16 @@
1
+ import type { DecisionClientOptions, DecisionPreflightInput, OutputDecisionInput, DecisionResponse, ToolCallDecisionInput, ToolResultDecisionInput } from "./types.ts";
2
+ export declare class AvertaDecisionClient {
3
+ private readonly avertaBaseUrl;
4
+ private readonly decisionTimeoutMs;
5
+ private readonly fetchImpl;
6
+ private readonly sdk;
7
+ constructor(options: DecisionClientOptions);
8
+ private readonly avertaApiKey;
9
+ preflight(input: Omit<DecisionPreflightInput, "sdk">): Promise<DecisionResponse>;
10
+ checkToolCall(input: ToolCallDecisionInput): Promise<DecisionResponse>;
11
+ checkToolResult(input: ToolResultDecisionInput): Promise<DecisionResponse>;
12
+ checkOutput(input: OutputDecisionInput): Promise<DecisionResponse>;
13
+ private sendDecisionRequest;
14
+ private createTransportError;
15
+ private createRequestError;
16
+ }
@@ -0,0 +1,485 @@
1
+ import { AvertaSdkError } from "./errors.js";
2
+ import { ensureRequestContext } from "./ids.js";
3
+ import { isObject } from "./values.js";
4
+ const DEFAULT_AVERTA_BASE_URL = "https://api.averta.io";
5
+ const DEFAULT_DECISION_TIMEOUT_MS = 30_000;
6
+ function parseReasons(payload) {
7
+ if (!Array.isArray(payload)) {
8
+ return null;
9
+ }
10
+ const reasons = payload.filter((reason) => isObject(reason) &&
11
+ typeof reason.code === "string" &&
12
+ typeof reason.message === "string");
13
+ if (reasons.length !== payload.length) {
14
+ return null;
15
+ }
16
+ return reasons;
17
+ }
18
+ function parseBlockedTools(payload) {
19
+ if (payload === undefined) {
20
+ return undefined;
21
+ }
22
+ if (!Array.isArray(payload)) {
23
+ return null;
24
+ }
25
+ if (!payload.every((tool) => typeof tool === "string")) {
26
+ return null;
27
+ }
28
+ return payload;
29
+ }
30
+ function parseTool(payload) {
31
+ if (payload === undefined) {
32
+ return undefined;
33
+ }
34
+ if (!isObject(payload) ||
35
+ typeof payload.kind !== "string" ||
36
+ typeof payload.name !== "string") {
37
+ return null;
38
+ }
39
+ return {
40
+ kind: payload.kind,
41
+ name: payload.name,
42
+ };
43
+ }
44
+ function parseRewriteAction(payload) {
45
+ if (payload === undefined) {
46
+ return undefined;
47
+ }
48
+ if (!isObject(payload) ||
49
+ !isObject(payload.rewrite)) {
50
+ return null;
51
+ }
52
+ const category = payload.rewrite.category;
53
+ if (category !== "secret_disclosure" &&
54
+ category !== "system_prompt_disclosure" &&
55
+ category !== "harmful_output") {
56
+ return null;
57
+ }
58
+ return {
59
+ rewrite: {
60
+ category,
61
+ },
62
+ };
63
+ }
64
+ function parseRunId(payload) {
65
+ if (payload === undefined) {
66
+ return undefined;
67
+ }
68
+ if (typeof payload !== "string" || !payload.trim()) {
69
+ return null;
70
+ }
71
+ return payload;
72
+ }
73
+ function parseDecisionResponse(payload) {
74
+ if (!isObject(payload) ||
75
+ payload.decision !== "allow" &&
76
+ payload.decision !== "block" &&
77
+ payload.decision !== "restrict_tools" &&
78
+ payload.decision !== "rewrite" ||
79
+ typeof payload.decision_id !== "string" ||
80
+ typeof payload.event_id !== "string" ||
81
+ typeof payload.policy_id !== "string") {
82
+ throw new AvertaSdkError("Averta returned an invalid decision response.", "invalid_decision_response");
83
+ }
84
+ const blockedTools = parseBlockedTools(payload.blocked_tools);
85
+ const tool = parseTool(payload.tool);
86
+ const actions = parseRewriteAction(payload.actions);
87
+ const reasons = parseReasons(payload.reasons);
88
+ const runId = parseRunId(payload.run_id);
89
+ if (blockedTools === null ||
90
+ tool === null ||
91
+ actions === null ||
92
+ reasons === null ||
93
+ runId === null ||
94
+ (payload.decision === "rewrite" && actions?.rewrite === undefined) ||
95
+ (payload.decision === "restrict_tools" && blockedTools === undefined)) {
96
+ throw new AvertaSdkError("Averta returned an invalid decision response.", "invalid_decision_response");
97
+ }
98
+ return {
99
+ actions,
100
+ decision: payload.decision,
101
+ decisionId: payload.decision_id,
102
+ eventId: payload.event_id,
103
+ policyId: payload.policy_id,
104
+ runId,
105
+ reasons,
106
+ blockedTools,
107
+ tool,
108
+ };
109
+ }
110
+ function assertDecisionForCheckpoint(checkpointType, decision) {
111
+ const isValidDecision = checkpointType === "request"
112
+ ? decision === "allow" ||
113
+ decision === "block" ||
114
+ decision === "restrict_tools"
115
+ : checkpointType === "tool_call"
116
+ ? decision === "allow" || decision === "block"
117
+ : checkpointType === "tool_result"
118
+ ? decision === "allow" || decision === "block"
119
+ : decision === "allow" ||
120
+ decision === "block" ||
121
+ decision === "rewrite";
122
+ if (!isValidDecision) {
123
+ throw new AvertaSdkError("Averta returned an invalid decision response.", "invalid_decision_response");
124
+ }
125
+ }
126
+ function hasForwardedTools(input) {
127
+ if (input.decision === "block") {
128
+ return false;
129
+ }
130
+ if (!input.tools || input.tools.length === 0) {
131
+ return false;
132
+ }
133
+ const blockedTools = new Set(input.blockedTools ?? []);
134
+ return input.tools.some((tool) => !blockedTools.has(tool.name));
135
+ }
136
+ function toProviderPayload(provider) {
137
+ if (typeof provider.model !== "string" || !provider.model.trim()) {
138
+ throw new AvertaSdkError("Guarded provider requests must include a model.", "missing_provider_model");
139
+ }
140
+ return {
141
+ model: provider.model,
142
+ name: provider.name,
143
+ operation: provider.operation,
144
+ streaming: provider.streaming ?? false,
145
+ };
146
+ }
147
+ function toToolsPayload(tools) {
148
+ if (!tools) {
149
+ return undefined;
150
+ }
151
+ return tools.map((tool) => ({
152
+ ...(tool.description ? { description: tool.description } : {}),
153
+ ...(tool.inputSchema ? { input_schema: tool.inputSchema } : {}),
154
+ name: tool.name,
155
+ type: tool.type,
156
+ }));
157
+ }
158
+ function toToolCallPayload(toolCall) {
159
+ return {
160
+ ...(toolCall.arguments && toolCall.arguments.value !== undefined
161
+ ? {
162
+ arguments: {
163
+ value: toolCall.arguments.value,
164
+ },
165
+ }
166
+ : {}),
167
+ call_id: toolCall.callId,
168
+ tool: {
169
+ kind: toolCall.tool.kind,
170
+ name: toolCall.tool.name,
171
+ },
172
+ };
173
+ }
174
+ function toToolCallContextPayload(context) {
175
+ return {
176
+ messages: context.messages.map((message) => {
177
+ if (message.role === "assistant") {
178
+ return {
179
+ ...(message.text !== undefined ? { text: message.text } : {}),
180
+ ...(message.toolCalls
181
+ ? {
182
+ tool_calls: message.toolCalls.map(toToolCallPayload),
183
+ }
184
+ : {}),
185
+ role: "assistant",
186
+ };
187
+ }
188
+ if (message.role === "tool") {
189
+ return {
190
+ call_id: message.callId,
191
+ content: {
192
+ value: message.content.value,
193
+ },
194
+ role: "tool",
195
+ };
196
+ }
197
+ return {
198
+ role: "user",
199
+ text: message.text,
200
+ };
201
+ }),
202
+ };
203
+ }
204
+ function toCheckpointPayload(input) {
205
+ if (input.checkpointType === "request") {
206
+ return {
207
+ payload: input.payload,
208
+ ...(input.tools ? { tools: toToolsPayload(input.tools) } : {}),
209
+ };
210
+ }
211
+ if (input.checkpointType === "tool_call") {
212
+ return {
213
+ context: toToolCallContextPayload(input.context),
214
+ model_response_tool_calls: input.modelResponseToolCalls.map(toToolCallPayload),
215
+ tool_call: input.toolCall,
216
+ };
217
+ }
218
+ if (input.checkpointType === "tool_result") {
219
+ return {
220
+ tool_result: input.toolResult,
221
+ };
222
+ }
223
+ return {
224
+ output: input.output,
225
+ };
226
+ }
227
+ function normalizeDecisionTimeoutMs(timeoutMs) {
228
+ if (timeoutMs === undefined) {
229
+ return DEFAULT_DECISION_TIMEOUT_MS;
230
+ }
231
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
232
+ throw new AvertaSdkError("Averta decision timeout must be a non-negative finite number.", "invalid_decision_timeout");
233
+ }
234
+ return timeoutMs;
235
+ }
236
+ function createDecisionAbortState(input) {
237
+ let rejectAbortPromise;
238
+ let timeoutId;
239
+ let timedOut = false;
240
+ const abortPromise = new Promise((_resolve, reject) => {
241
+ rejectAbortPromise = reject;
242
+ });
243
+ if (input.timeoutMs === 0) {
244
+ if (!input.signal) {
245
+ return {
246
+ signal: undefined,
247
+ abortPromise: undefined,
248
+ didCallerAbort: () => false,
249
+ didTimeout: () => false,
250
+ dispose: () => { },
251
+ };
252
+ }
253
+ const abortFromCaller = () => {
254
+ rejectAbortPromise?.(new AvertaSdkError("Averta decision request was aborted.", "decision_request_aborted", {
255
+ cause: input.signal?.reason,
256
+ }));
257
+ };
258
+ input.signal.addEventListener("abort", abortFromCaller, { once: true });
259
+ return {
260
+ signal: input.signal,
261
+ abortPromise,
262
+ didCallerAbort: () => input.signal?.aborted === true,
263
+ didTimeout: () => false,
264
+ dispose: () => {
265
+ input.signal?.removeEventListener("abort", abortFromCaller);
266
+ },
267
+ };
268
+ }
269
+ const controller = new AbortController();
270
+ const abortFromCaller = () => {
271
+ controller.abort(input.signal?.reason);
272
+ rejectAbortPromise?.(new AvertaSdkError("Averta decision request was aborted.", "decision_request_aborted", {
273
+ cause: input.signal?.reason,
274
+ }));
275
+ };
276
+ if (input.signal) {
277
+ input.signal.addEventListener("abort", abortFromCaller, { once: true });
278
+ if (input.signal.aborted) {
279
+ abortFromCaller();
280
+ }
281
+ }
282
+ timeoutId = setTimeout(() => {
283
+ timedOut = true;
284
+ controller.abort();
285
+ rejectAbortPromise?.(new AvertaSdkError("Averta decision request timed out.", "decision_request_timeout"));
286
+ }, input.timeoutMs);
287
+ return {
288
+ signal: controller.signal,
289
+ abortPromise,
290
+ didCallerAbort: () => input.signal?.aborted === true && !timedOut,
291
+ didTimeout: () => timedOut,
292
+ dispose: () => {
293
+ if (timeoutId) {
294
+ clearTimeout(timeoutId);
295
+ }
296
+ input.signal?.removeEventListener("abort", abortFromCaller);
297
+ },
298
+ };
299
+ }
300
+ export class AvertaDecisionClient {
301
+ avertaBaseUrl;
302
+ decisionTimeoutMs;
303
+ fetchImpl;
304
+ sdk;
305
+ constructor(options) {
306
+ this.avertaBaseUrl = options.avertaBaseUrl ?? DEFAULT_AVERTA_BASE_URL;
307
+ this.decisionTimeoutMs = normalizeDecisionTimeoutMs(options.decisionTimeoutMs);
308
+ this.fetchImpl = options.fetch ?? fetch;
309
+ this.sdk = options.sdk;
310
+ this.avertaApiKey = options.avertaApiKey;
311
+ }
312
+ avertaApiKey;
313
+ async preflight(input) {
314
+ const response = await this.sendDecisionRequest({
315
+ checkpointType: "request",
316
+ provider: input.provider,
317
+ payload: input.payload,
318
+ ...(input.tools ? { tools: input.tools } : {}),
319
+ requestContext: input.requestContext,
320
+ signal: input.signal,
321
+ });
322
+ assertDecisionForCheckpoint("request", response.decision);
323
+ if (hasForwardedTools({
324
+ blockedTools: response.blockedTools,
325
+ decision: response.decision,
326
+ tools: input.tools,
327
+ }) &&
328
+ !response.runId) {
329
+ throw new AvertaSdkError("Averta returned an invalid decision response.", "invalid_decision_response");
330
+ }
331
+ return response;
332
+ }
333
+ async checkToolCall(input) {
334
+ const response = await this.sendDecisionRequest({
335
+ checkpointType: "tool_call",
336
+ provider: input.provider,
337
+ requestContext: input.requestContext,
338
+ signal: input.signal,
339
+ context: input.context,
340
+ modelResponseToolCalls: input.modelResponseToolCalls,
341
+ toolCall: {
342
+ ...(input.arguments && input.arguments.value !== undefined
343
+ ? {
344
+ arguments: {
345
+ value: input.arguments.value,
346
+ },
347
+ }
348
+ : {}),
349
+ call_id: input.callId,
350
+ run_id: input.runId,
351
+ tool: {
352
+ kind: input.tool.kind,
353
+ name: input.tool.name,
354
+ },
355
+ },
356
+ });
357
+ assertDecisionForCheckpoint("tool_call", response.decision);
358
+ if (!response.tool) {
359
+ throw new AvertaSdkError("Averta returned an invalid decision response.", "invalid_decision_response");
360
+ }
361
+ return response;
362
+ }
363
+ async checkToolResult(input) {
364
+ const response = await this.sendDecisionRequest({
365
+ checkpointType: "tool_result",
366
+ toolResult: {
367
+ call_id: input.callId,
368
+ content: {
369
+ value: input.content.value,
370
+ },
371
+ },
372
+ provider: input.provider,
373
+ requestContext: input.requestContext,
374
+ signal: input.signal,
375
+ });
376
+ assertDecisionForCheckpoint("tool_result", response.decision);
377
+ if (!response.runId || !response.tool) {
378
+ throw new AvertaSdkError("Averta returned an invalid decision response.", "invalid_decision_response");
379
+ }
380
+ return response;
381
+ }
382
+ async checkOutput(input) {
383
+ const response = await this.sendDecisionRequest({
384
+ checkpointType: "output",
385
+ output: {
386
+ content: {
387
+ value: input.content.value,
388
+ },
389
+ rewrite_attempt: input.rewriteAttempt,
390
+ },
391
+ provider: input.provider,
392
+ requestContext: input.requestContext,
393
+ signal: input.signal,
394
+ });
395
+ assertDecisionForCheckpoint("output", response.decision);
396
+ return response;
397
+ }
398
+ async sendDecisionRequest(input) {
399
+ if (input.signal?.aborted) {
400
+ throw new AvertaSdkError("Averta decision request was aborted.", "decision_request_aborted", {
401
+ cause: input.signal.reason,
402
+ });
403
+ }
404
+ const abortState = createDecisionAbortState({
405
+ signal: input.signal,
406
+ timeoutMs: this.decisionTimeoutMs,
407
+ });
408
+ try {
409
+ const requestPromise = (async () => {
410
+ const response = await this.fetchImpl(`${this.avertaBaseUrl}/v1/decide`, {
411
+ method: "POST",
412
+ headers: {
413
+ "content-type": "application/json",
414
+ authorization: `Bearer ${this.avertaApiKey}`,
415
+ },
416
+ ...(abortState.signal ? { signal: abortState.signal } : {}),
417
+ body: JSON.stringify({
418
+ checkpoint_type: input.checkpointType,
419
+ sdk: this.sdk,
420
+ provider: toProviderPayload(input.provider),
421
+ ...toCheckpointPayload(input),
422
+ request_context: toRequestContextPayload(ensureRequestContext(input.requestContext)),
423
+ }),
424
+ });
425
+ const payload = await response.json().catch((error) => {
426
+ if (error instanceof SyntaxError) {
427
+ return null;
428
+ }
429
+ throw error;
430
+ });
431
+ if (!response.ok) {
432
+ throw this.createRequestError(payload, "Averta decision request failed.", "decision_request_failed", response.status);
433
+ }
434
+ return parseDecisionResponse(payload);
435
+ })();
436
+ return abortState.abortPromise
437
+ ? await Promise.race([requestPromise, abortState.abortPromise])
438
+ : await requestPromise;
439
+ }
440
+ catch (error) {
441
+ if (error instanceof AvertaSdkError) {
442
+ throw error;
443
+ }
444
+ throw this.createTransportError(error, abortState);
445
+ }
446
+ finally {
447
+ abortState.dispose();
448
+ }
449
+ }
450
+ createTransportError(cause, abortState) {
451
+ if (abortState.didTimeout()) {
452
+ return new AvertaSdkError("Averta decision request timed out.", "decision_request_timeout", {
453
+ cause,
454
+ });
455
+ }
456
+ if (abortState.didCallerAbort()) {
457
+ return new AvertaSdkError("Averta decision request was aborted.", "decision_request_aborted", {
458
+ cause,
459
+ });
460
+ }
461
+ return new AvertaSdkError("Averta decision request failed.", "decision_request_failed", {
462
+ cause,
463
+ });
464
+ }
465
+ createRequestError(payload, fallbackMessage, fallbackCode, statusCode) {
466
+ const message = isObject(payload) &&
467
+ isObject(payload.error) &&
468
+ typeof payload.error.message === "string"
469
+ ? payload.error.message
470
+ : fallbackMessage;
471
+ const code = isObject(payload) &&
472
+ isObject(payload.error) &&
473
+ typeof payload.error.code === "string"
474
+ ? payload.error.code
475
+ : fallbackCode;
476
+ return new AvertaSdkError(message, code, statusCode);
477
+ }
478
+ }
479
+ function toRequestContextPayload(input) {
480
+ return {
481
+ ...(input.conversationId ? { conversation_id: input.conversationId } : {}),
482
+ request_id: input.requestId,
483
+ trace_id: input.traceId,
484
+ };
485
+ }
@@ -0,0 +1,12 @@
1
+ import type { DecisionResponse } from "./types.ts";
2
+ export type AvertaSdkErrorOptions = {
3
+ cause?: unknown;
4
+ checkpointDecision?: DecisionResponse;
5
+ statusCode?: number;
6
+ };
7
+ export declare class AvertaSdkError extends Error {
8
+ readonly checkpointDecision?: DecisionResponse;
9
+ readonly code: string;
10
+ readonly statusCode?: number;
11
+ constructor(message: string, code: string, statusCodeOrOptions?: number | AvertaSdkErrorOptions, options?: AvertaSdkErrorOptions);
12
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,20 @@
1
+ export class AvertaSdkError extends Error {
2
+ checkpointDecision;
3
+ code;
4
+ statusCode;
5
+ constructor(message, code, statusCodeOrOptions, options) {
6
+ const resolvedOptions = typeof statusCodeOrOptions === "number" ? options : statusCodeOrOptions;
7
+ super(message, resolvedOptions?.cause === undefined
8
+ ? undefined
9
+ : {
10
+ cause: resolvedOptions.cause,
11
+ });
12
+ this.name = "AvertaSdkError";
13
+ this.code = code;
14
+ this.statusCode =
15
+ typeof statusCodeOrOptions === "number"
16
+ ? statusCodeOrOptions
17
+ : resolvedOptions?.statusCode;
18
+ this.checkpointDecision = resolvedOptions?.checkpointDecision;
19
+ }
20
+ }
@@ -0,0 +1 @@
1
+ export declare function filterToolsByIdentity<T>(tools: readonly T[] | undefined, blockedTools: readonly string[] | undefined, getIdentity: (tool: T) => string | null | undefined): T[] | undefined;
@@ -0,0 +1,13 @@
1
+ export function filterToolsByIdentity(tools, blockedTools, getIdentity) {
2
+ if (!tools) {
3
+ return undefined;
4
+ }
5
+ if (!blockedTools || blockedTools.length === 0) {
6
+ return [...tools];
7
+ }
8
+ const blocked = new Set(blockedTools);
9
+ return tools.filter((tool) => {
10
+ const identity = getIdentity(tool);
11
+ return !identity || !blocked.has(identity);
12
+ });
13
+ }
@@ -0,0 +1,90 @@
1
+ import { AvertaSdkError } from "./errors.ts";
2
+ import type { AvertaProviderMetadata, AvertaRequestContext, DecisionPayload, DecisionResponse, NormalizedToolDefinition, OutputDecisionInput, ToolCallContext, ToolCallDecisionInput, ToolCallDecisionCandidate, ToolResultDecisionInput } from "./types.ts";
3
+ type DecisionClientWithPreflight = {
4
+ preflight(input: {
5
+ provider: AvertaProviderMetadata;
6
+ payload: DecisionPayload;
7
+ requestContext?: AvertaRequestContext;
8
+ tools?: NormalizedToolDefinition[];
9
+ }): Promise<DecisionResponse>;
10
+ };
11
+ type DecisionClientWithToolResultDecision = {
12
+ checkToolResult(input: ToolResultDecisionInput): Promise<DecisionResponse>;
13
+ };
14
+ type DecisionClientWithToolCallDecision = {
15
+ checkToolCall(input: ToolCallDecisionInput): Promise<DecisionResponse>;
16
+ };
17
+ type DecisionClientWithOutputDecision = {
18
+ checkOutput(input: OutputDecisionInput): Promise<DecisionResponse>;
19
+ };
20
+ export type PendingToolResultDecisionCandidate = {
21
+ callId: string;
22
+ content: {
23
+ value: unknown;
24
+ };
25
+ };
26
+ export type PreparedGuardedRequest<TTool> = {
27
+ blockedTools?: string[];
28
+ filteredTools: TTool[] | undefined;
29
+ runId?: string;
30
+ };
31
+ export declare function mergeBlockedTools(...blockedToolLists: Array<readonly string[] | undefined>): string[] | undefined;
32
+ export declare function resolveContinuationRunId(decisions: Array<{
33
+ runId?: string;
34
+ }>): string;
35
+ export declare function throwDecisionError(decision: DecisionResponse, fallbackMessage: string, fallbackCode: string): never;
36
+ export declare function createDecisionError(decision: DecisionResponse, fallbackMessage: string, fallbackCode: string): AvertaSdkError;
37
+ export declare function checkToolCallsForRun(input: {
38
+ context: ToolCallContext;
39
+ decisionClient: DecisionClientWithToolCallDecision;
40
+ provider: AvertaProviderMetadata;
41
+ requestContext?: AvertaRequestContext;
42
+ responseToolCalls: ToolCallDecisionCandidate[];
43
+ runId: string | undefined;
44
+ }): Promise<DecisionResponse[]>;
45
+ export declare function prepareGuardedRequest<TTool>(input: {
46
+ decisionClient: DecisionClientWithPreflight & DecisionClientWithToolResultDecision;
47
+ filterTools: (tools: readonly TTool[] | undefined, blockedTools: readonly string[] | undefined) => TTool[] | undefined;
48
+ getPreflightInput: () => {
49
+ payload: DecisionPayload;
50
+ tools?: NormalizedToolDefinition[];
51
+ };
52
+ onRequestDecision?: (event: {
53
+ decision: DecisionResponse;
54
+ forwardedTools: TTool[];
55
+ originalTools: TTool[];
56
+ provider: AvertaProviderMetadata;
57
+ }) => void;
58
+ onToolResultDecision?: (event: {
59
+ decision: DecisionResponse;
60
+ provider: AvertaProviderMetadata;
61
+ tool: {
62
+ kind: string;
63
+ name: string;
64
+ };
65
+ }) => void;
66
+ originalTools: readonly TTool[] | undefined;
67
+ pendingToolResults: readonly PendingToolResultDecisionCandidate[];
68
+ provider: AvertaProviderMetadata;
69
+ requestContext?: AvertaRequestContext;
70
+ }): Promise<PreparedGuardedRequest<TTool>>;
71
+ export declare function guardNonStreamingOutput<TResult>(input: {
72
+ decisionClient: DecisionClientWithOutputDecision;
73
+ hasToolCalls: (result: TResult) => boolean;
74
+ initialResult: TResult;
75
+ onDecision?: (event: {
76
+ decision: DecisionResponse;
77
+ outputText: string;
78
+ provider: AvertaProviderMetadata;
79
+ rewriteAttempt: number;
80
+ }) => void;
81
+ provider: AvertaProviderMetadata;
82
+ readOutputText: (result: TResult) => string;
83
+ requestContext?: AvertaRequestContext;
84
+ rewrite: (input: {
85
+ decision: DecisionResponse;
86
+ outputText: string;
87
+ rewriteInstruction: string;
88
+ }) => Promise<TResult>;
89
+ }): Promise<TResult>;
90
+ export {};
@@ -0,0 +1,180 @@
1
+ import { AvertaSdkError } from "./errors.js";
2
+ import { buildOutputRewriteInstruction } from "./output-rewrite.js";
3
+ export function mergeBlockedTools(...blockedToolLists) {
4
+ const merged = new Set();
5
+ for (const blockedTools of blockedToolLists) {
6
+ if (!blockedTools) {
7
+ continue;
8
+ }
9
+ for (const blockedTool of blockedTools) {
10
+ merged.add(blockedTool);
11
+ }
12
+ }
13
+ return merged.size > 0 ? [...merged] : undefined;
14
+ }
15
+ export function resolveContinuationRunId(decisions) {
16
+ const runId = [...decisions]
17
+ .reverse()
18
+ .map((decision) => decision.runId)
19
+ .find((candidate) => typeof candidate === "string" && candidate.length > 0);
20
+ if (!runId) {
21
+ throw new AvertaSdkError("Averta did not return a run identifier for an active tool chain.", "missing_tool_run_id");
22
+ }
23
+ return runId;
24
+ }
25
+ export function throwDecisionError(decision, fallbackMessage, fallbackCode) {
26
+ throw createDecisionError(decision, fallbackMessage, fallbackCode);
27
+ }
28
+ export function createDecisionError(decision, fallbackMessage, fallbackCode) {
29
+ const reason = decision.reasons[0];
30
+ return new AvertaSdkError(reason?.message ?? fallbackMessage, reason?.code ?? fallbackCode, {
31
+ checkpointDecision: decision,
32
+ });
33
+ }
34
+ export async function checkToolCallsForRun(input) {
35
+ if (input.responseToolCalls.length === 0) {
36
+ return [];
37
+ }
38
+ if (!input.runId) {
39
+ throw new AvertaSdkError("Averta did not return a run identifier for an active tool chain.", "missing_tool_run_id");
40
+ }
41
+ const runId = input.runId;
42
+ return Promise.all(input.responseToolCalls.map((toolCall) => input.decisionClient.checkToolCall({
43
+ ...(toolCall.arguments && toolCall.arguments.value !== undefined
44
+ ? {
45
+ arguments: {
46
+ value: toolCall.arguments.value,
47
+ },
48
+ }
49
+ : {}),
50
+ callId: toolCall.callId,
51
+ context: input.context,
52
+ modelResponseToolCalls: input.responseToolCalls,
53
+ provider: input.provider,
54
+ requestContext: input.requestContext,
55
+ runId,
56
+ tool: {
57
+ kind: toolCall.tool.kind,
58
+ name: toolCall.tool.name,
59
+ },
60
+ })));
61
+ }
62
+ export async function prepareGuardedRequest(input) {
63
+ let blockedTools;
64
+ let filteredTools;
65
+ let runId;
66
+ if (input.pendingToolResults.length > 0) {
67
+ const decisions = [];
68
+ for (const toolResult of input.pendingToolResults) {
69
+ const decision = await input.decisionClient.checkToolResult({
70
+ callId: toolResult.callId,
71
+ content: {
72
+ value: toolResult.content.value,
73
+ },
74
+ provider: input.provider,
75
+ requestContext: input.requestContext,
76
+ });
77
+ decisions.push(decision);
78
+ blockedTools = mergeBlockedTools(blockedTools, decision.blockedTools);
79
+ input.onToolResultDecision?.({
80
+ decision,
81
+ provider: input.provider,
82
+ tool: decision.tool,
83
+ });
84
+ if (decision.decision === "block") {
85
+ throwDecisionError(decision, "The tool result was blocked by Averta.", "tool_result_blocked");
86
+ }
87
+ }
88
+ filteredTools = input.filterTools(input.originalTools, blockedTools);
89
+ runId = resolveContinuationRunId(decisions);
90
+ }
91
+ else {
92
+ const preflightInput = input.getPreflightInput();
93
+ const decision = await input.decisionClient.preflight({
94
+ provider: input.provider,
95
+ payload: preflightInput.payload,
96
+ requestContext: input.requestContext,
97
+ tools: preflightInput.tools,
98
+ });
99
+ blockedTools = mergeBlockedTools(blockedTools, decision.blockedTools);
100
+ filteredTools = input.filterTools(input.originalTools, blockedTools);
101
+ runId = decision.runId;
102
+ input.onRequestDecision?.({
103
+ decision,
104
+ originalTools: input.originalTools ? [...input.originalTools] : [],
105
+ forwardedTools: filteredTools ?? [],
106
+ provider: input.provider,
107
+ });
108
+ if (decision.decision === "block") {
109
+ throwDecisionError(decision, "The request was blocked by Averta.", "request_blocked");
110
+ }
111
+ }
112
+ return {
113
+ blockedTools,
114
+ filteredTools,
115
+ runId,
116
+ };
117
+ }
118
+ export async function guardNonStreamingOutput(input) {
119
+ if (input.hasToolCalls(input.initialResult)) {
120
+ return input.initialResult;
121
+ }
122
+ const outputText = input.readOutputText(input.initialResult);
123
+ if (!outputText) {
124
+ return input.initialResult;
125
+ }
126
+ const outputDecision = await input.decisionClient.checkOutput({
127
+ content: {
128
+ value: outputText,
129
+ },
130
+ provider: input.provider,
131
+ requestContext: input.requestContext,
132
+ rewriteAttempt: 0,
133
+ });
134
+ input.onDecision?.({
135
+ decision: outputDecision,
136
+ outputText,
137
+ provider: input.provider,
138
+ rewriteAttempt: 0,
139
+ });
140
+ if (outputDecision.decision === "allow") {
141
+ return input.initialResult;
142
+ }
143
+ if (outputDecision.decision === "block") {
144
+ throwDecisionError(outputDecision, "The output was blocked by Averta.", "output_blocked");
145
+ }
146
+ const rewriteInstruction = buildOutputRewriteInstruction(outputDecision);
147
+ if (!rewriteInstruction) {
148
+ throw new AvertaSdkError("Averta returned an invalid rewrite decision.", "invalid_decision_response");
149
+ }
150
+ const rewrittenResult = await input.rewrite({
151
+ decision: outputDecision,
152
+ outputText,
153
+ rewriteInstruction,
154
+ });
155
+ if (input.hasToolCalls(rewrittenResult)) {
156
+ throw new AvertaSdkError("Averta rewrite responses must not create new tool calls.", "invalid_rewrite_response");
157
+ }
158
+ const rewrittenOutputText = input.readOutputText(rewrittenResult);
159
+ if (!rewrittenOutputText) {
160
+ return rewrittenResult;
161
+ }
162
+ const rewrittenDecision = await input.decisionClient.checkOutput({
163
+ content: {
164
+ value: rewrittenOutputText,
165
+ },
166
+ provider: input.provider,
167
+ requestContext: input.requestContext,
168
+ rewriteAttempt: 1,
169
+ });
170
+ input.onDecision?.({
171
+ decision: rewrittenDecision,
172
+ outputText: rewrittenOutputText,
173
+ provider: input.provider,
174
+ rewriteAttempt: 1,
175
+ });
176
+ if (rewrittenDecision.decision !== "allow") {
177
+ throwDecisionError(rewrittenDecision, "The rewritten output was blocked by Averta.", "output_rewrite_blocked");
178
+ }
179
+ return rewrittenResult;
180
+ }
package/dist/ids.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { AvertaRequestContext, PreparedAvertaRequestContext } from "./types.ts";
2
+ export declare function createRequestId(): string;
3
+ export declare function createTraceId(): string;
4
+ export declare function ensureRequestContext(requestContext?: AvertaRequestContext): PreparedAvertaRequestContext;
package/dist/ids.js ADDED
@@ -0,0 +1,16 @@
1
+ function buildId(prefix) {
2
+ return `${prefix}_${crypto.randomUUID()}`;
3
+ }
4
+ export function createRequestId() {
5
+ return buildId("req");
6
+ }
7
+ export function createTraceId() {
8
+ return buildId("trc");
9
+ }
10
+ export function ensureRequestContext(requestContext) {
11
+ return {
12
+ ...requestContext,
13
+ requestId: requestContext?.requestId ?? createRequestId(),
14
+ traceId: requestContext?.traceId ?? createTraceId(),
15
+ };
16
+ }
@@ -0,0 +1,11 @@
1
+ export { AvertaDecisionClient } from "./decision-client.ts";
2
+ export { AvertaSdkError } from "./errors.ts";
3
+ export { createRequestId, createTraceId, ensureRequestContext } from "./ids.ts";
4
+ export { filterToolsByIdentity } from "./filter-tools.ts";
5
+ export { checkToolCallsForRun, guardNonStreamingOutput, mergeBlockedTools, prepareGuardedRequest, resolveContinuationRunId, throwDecisionError, } from "./guard-flow.ts";
6
+ export { buildOutputRewriteInstruction } from "./output-rewrite.ts";
7
+ export { buildForwardedToolInput, omitProviderFields } from "./provider-input.ts";
8
+ export { StreamingOutputGuard } from "./streaming-output-guard.ts";
9
+ export { isObject, readTrimmedString } from "./values.ts";
10
+ export type { AvertaSdkErrorOptions, } from "./errors.ts";
11
+ export type { AvertaProviderMetadata, AvertaRequestContext, AvertaSdkMetadata, DecisionClientOptions, DecisionPayload, DecisionPreflightInput, DecisionReason, DecisionResponse, DecisionRewriteAction, NormalizedToolDefinition, OutputDecisionInput, PreparedAvertaRequestContext, ToolCallContext, ToolCallContextMessage, ToolCallDecisionCandidate, ToolCallDecisionInput, ToolResultDecisionInput, } from "./types.ts";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { AvertaDecisionClient } from "./decision-client.js";
2
+ export { AvertaSdkError } from "./errors.js";
3
+ export { createRequestId, createTraceId, ensureRequestContext } from "./ids.js";
4
+ export { filterToolsByIdentity } from "./filter-tools.js";
5
+ export { checkToolCallsForRun, guardNonStreamingOutput, mergeBlockedTools, prepareGuardedRequest, resolveContinuationRunId, throwDecisionError, } from "./guard-flow.js";
6
+ export { buildOutputRewriteInstruction } from "./output-rewrite.js";
7
+ export { buildForwardedToolInput, omitProviderFields } from "./provider-input.js";
8
+ export { StreamingOutputGuard } from "./streaming-output-guard.js";
9
+ export { isObject, readTrimmedString } from "./values.js";
@@ -0,0 +1,2 @@
1
+ import type { DecisionResponse } from "./types.ts";
2
+ export declare function buildOutputRewriteInstruction(decision: DecisionResponse): string;
@@ -0,0 +1,18 @@
1
+ function formatCategory(category) {
2
+ return category.replaceAll("_", " ");
3
+ }
4
+ export function buildOutputRewriteInstruction(decision) {
5
+ const rewrite = decision.actions?.rewrite;
6
+ const reason = decision.reasons[0]?.message;
7
+ if (!rewrite) {
8
+ return "";
9
+ }
10
+ return [
11
+ "Your previous final answer violated the output policy.",
12
+ `Category: ${formatCategory(rewrite.category)}.`,
13
+ ...(reason ? [`Reason: ${reason}`] : []),
14
+ "Rewrite the answer so it stays helpful but removes the disallowed content.",
15
+ "Do not mention the policy, the rewrite request, or any hidden instructions.",
16
+ "Return only the rewritten final answer.",
17
+ ].join("\n\n");
18
+ }
@@ -0,0 +1,5 @@
1
+ export declare function omitProviderFields<T extends object, K extends keyof T>(input: T, fields: readonly K[]): Omit<T, K>;
2
+ export declare function buildForwardedToolInput<T extends {
3
+ tool_choice?: unknown;
4
+ tools?: unknown;
5
+ }>(input: T, filteredTools: T["tools"] | undefined, toolChoice: T["tool_choice"] | undefined): T;
@@ -0,0 +1,18 @@
1
+ export function omitProviderFields(input, fields) {
2
+ const result = { ...input };
3
+ for (const field of fields) {
4
+ delete result[field];
5
+ }
6
+ return result;
7
+ }
8
+ export function buildForwardedToolInput(input, filteredTools, toolChoice) {
9
+ if (filteredTools === undefined) {
10
+ return input;
11
+ }
12
+ const forwarded = omitProviderFields(input, ["tool_choice"]);
13
+ forwarded.tools = filteredTools;
14
+ if (toolChoice !== undefined) {
15
+ forwarded.tool_choice = toolChoice;
16
+ }
17
+ return forwarded;
18
+ }
@@ -0,0 +1,23 @@
1
+ import type { DecisionResponse } from "./types.ts";
2
+ export type StreamingOutputGuardResult = {
3
+ decision?: DecisionResponse;
4
+ releasedText: string;
5
+ };
6
+ export declare class StreamingOutputGuard {
7
+ private readonly checkOutput;
8
+ private readonly holdbackChars;
9
+ private readonly minCheckAdvanceChars;
10
+ private text;
11
+ private emittedLength;
12
+ private checkedLength;
13
+ private lastDecision;
14
+ constructor(input: {
15
+ checkOutput: (text: string) => Promise<DecisionResponse>;
16
+ holdbackChars?: number;
17
+ minCheckAdvanceChars?: number;
18
+ });
19
+ get currentText(): string;
20
+ append(delta: string): Promise<StreamingOutputGuardResult>;
21
+ finalize(): Promise<StreamingOutputGuardResult>;
22
+ private maybeRelease;
23
+ }
@@ -0,0 +1,69 @@
1
+ import { AvertaSdkError } from "./errors.js";
2
+ import { createDecisionError } from "./guard-flow.js";
3
+ const DEFAULT_HOLDBACK_CHARS = 96;
4
+ const DEFAULT_MIN_CHECK_ADVANCE_CHARS = 48;
5
+ export class StreamingOutputGuard {
6
+ checkOutput;
7
+ holdbackChars;
8
+ minCheckAdvanceChars;
9
+ text = "";
10
+ emittedLength = 0;
11
+ checkedLength = 0;
12
+ lastDecision;
13
+ constructor(input) {
14
+ this.checkOutput = input.checkOutput;
15
+ this.holdbackChars = input.holdbackChars ?? DEFAULT_HOLDBACK_CHARS;
16
+ this.minCheckAdvanceChars =
17
+ input.minCheckAdvanceChars ?? DEFAULT_MIN_CHECK_ADVANCE_CHARS;
18
+ }
19
+ get currentText() {
20
+ return this.text;
21
+ }
22
+ async append(delta) {
23
+ this.text += delta;
24
+ return this.maybeRelease(false);
25
+ }
26
+ async finalize() {
27
+ return this.maybeRelease(true);
28
+ }
29
+ async maybeRelease(finalize) {
30
+ const releasableLength = finalize
31
+ ? this.text.length
32
+ : Math.max(0, this.text.length - this.holdbackChars);
33
+ if (releasableLength <= this.checkedLength) {
34
+ return {
35
+ decision: finalize && this.checkedLength === this.text.length
36
+ ? this.lastDecision
37
+ : undefined,
38
+ releasedText: "",
39
+ };
40
+ }
41
+ const uncheckedLength = releasableLength - this.checkedLength;
42
+ if (!finalize && uncheckedLength < this.minCheckAdvanceChars) {
43
+ return {
44
+ decision: undefined,
45
+ releasedText: "",
46
+ };
47
+ }
48
+ const nextText = this.text.slice(0, releasableLength);
49
+ const decision = await this.checkOutput(nextText);
50
+ if (decision.decision === "block") {
51
+ throw createDecisionError(decision, "The output was blocked by Averta.", "output_blocked");
52
+ }
53
+ if (decision.decision === "rewrite") {
54
+ const reason = decision.reasons[0];
55
+ throw new AvertaSdkError(reason?.message ??
56
+ "Averta required an output rewrite during streaming, which is not supported yet.", reason?.code ?? "streaming_output_rewrite_required", {
57
+ checkpointDecision: decision,
58
+ });
59
+ }
60
+ this.lastDecision = decision;
61
+ this.checkedLength = releasableLength;
62
+ const releasedText = this.text.slice(this.emittedLength, releasableLength);
63
+ this.emittedLength = releasableLength;
64
+ return {
65
+ decision: finalize && releasableLength === this.text.length ? decision : undefined,
66
+ releasedText,
67
+ };
68
+ }
69
+ }
@@ -0,0 +1,131 @@
1
+ export type AvertaRequestContext = {
2
+ conversationId?: string;
3
+ requestId?: string;
4
+ traceId?: string;
5
+ };
6
+ export type PreparedAvertaRequestContext = AvertaRequestContext & {
7
+ requestId: string;
8
+ traceId: string;
9
+ };
10
+ export type AvertaSdkMetadata = {
11
+ name: string;
12
+ version: string;
13
+ };
14
+ export type AvertaProviderMetadata = {
15
+ name: string;
16
+ operation: string;
17
+ model: string;
18
+ streaming?: boolean;
19
+ };
20
+ export type NormalizedToolDefinition = {
21
+ name: string;
22
+ type: string;
23
+ description?: string;
24
+ inputSchema?: Record<string, unknown>;
25
+ };
26
+ export type DecisionPayload = {
27
+ text: string;
28
+ };
29
+ export type DecisionPreflightInput = {
30
+ sdk: AvertaSdkMetadata;
31
+ provider: AvertaProviderMetadata;
32
+ payload: unknown;
33
+ signal?: AbortSignal;
34
+ tools?: NormalizedToolDefinition[];
35
+ requestContext?: AvertaRequestContext;
36
+ };
37
+ export type ToolResultDecisionInput = {
38
+ callId: string;
39
+ content: {
40
+ value: unknown;
41
+ };
42
+ provider: AvertaProviderMetadata;
43
+ requestContext?: AvertaRequestContext;
44
+ signal?: AbortSignal;
45
+ };
46
+ export type ToolCallDecisionInput = {
47
+ arguments?: {
48
+ value: unknown;
49
+ };
50
+ callId: string;
51
+ context: ToolCallContext;
52
+ modelResponseToolCalls: ToolCallDecisionCandidate[];
53
+ provider: AvertaProviderMetadata;
54
+ requestContext?: AvertaRequestContext;
55
+ runId: string;
56
+ signal?: AbortSignal;
57
+ tool: {
58
+ kind: string;
59
+ name: string;
60
+ };
61
+ };
62
+ export type ToolCallDecisionCandidate = {
63
+ arguments?: {
64
+ value: unknown;
65
+ };
66
+ callId: string;
67
+ tool: {
68
+ kind: string;
69
+ name: string;
70
+ };
71
+ };
72
+ export type ToolCallContextMessage = {
73
+ role: "user";
74
+ text: string;
75
+ } | {
76
+ role: "assistant";
77
+ text?: string;
78
+ toolCalls?: ToolCallDecisionCandidate[];
79
+ } | {
80
+ callId: string;
81
+ content: {
82
+ value: unknown;
83
+ };
84
+ role: "tool";
85
+ };
86
+ export type ToolCallContext = {
87
+ messages: ToolCallContextMessage[];
88
+ };
89
+ export type OutputDecisionInput = {
90
+ content: {
91
+ value: string;
92
+ };
93
+ provider: AvertaProviderMetadata;
94
+ requestContext?: AvertaRequestContext;
95
+ rewriteAttempt: number;
96
+ signal?: AbortSignal;
97
+ };
98
+ export type DecisionReason = {
99
+ code: string;
100
+ message: string;
101
+ };
102
+ export type DecisionRewriteAction = {
103
+ category: "secret_disclosure" | "system_prompt_disclosure" | "harmful_output";
104
+ };
105
+ export type DecisionResponse = {
106
+ actions?: {
107
+ rewrite?: DecisionRewriteAction;
108
+ };
109
+ blockedTools?: string[];
110
+ decision: "allow" | "block" | "restrict_tools" | "rewrite";
111
+ decisionId: string;
112
+ eventId: string;
113
+ policyId: string;
114
+ runId?: string;
115
+ reasons: DecisionReason[];
116
+ tool?: {
117
+ kind: string;
118
+ name: string;
119
+ };
120
+ };
121
+ export type DecisionClientOptions = {
122
+ avertaApiKey: string;
123
+ avertaBaseUrl?: string;
124
+ /**
125
+ * Maximum time to wait for an Averta decision request. Defaults to 30000.
126
+ * Set to 0 to disable the SDK-managed timeout.
127
+ */
128
+ decisionTimeoutMs?: number;
129
+ fetch?: typeof fetch;
130
+ sdk: AvertaSdkMetadata;
131
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function isObject(value: unknown): value is Record<string, unknown>;
2
+ export declare function readTrimmedString(value: unknown): string | null;
package/dist/values.js ADDED
@@ -0,0 +1,6 @@
1
+ export function isObject(value) {
2
+ return typeof value === "object" && value !== null;
3
+ }
4
+ export function readTrimmedString(value) {
5
+ return typeof value === "string" && value.trim() ? value.trim() : null;
6
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@averta-security/sdk-core",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "Shared Averta SDK decision-plane runtime primitives.",
5
+ "license": "SEE LICENSE IN LICENSE.md",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "LICENSE.md",
19
+ "README.md"
20
+ ],
21
+ "sideEffects": false,
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "scripts": {
29
+ "clean": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true });\"",
30
+ "build": "npm run clean && node ../../node_modules/typescript/bin/tsc -p tsconfig.build.json",
31
+ "prepack": "npm run build",
32
+ "typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
33
+ "test": "node --test --experimental-strip-types \"src/**/*.test.ts\""
34
+ }
35
+ }