@silmaril-security/sdk 0.7.7 → 0.7.8

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/dist/index.js CHANGED
@@ -14,6 +14,19 @@ var __export = (target, all) => {
14
14
  };
15
15
 
16
16
  // src/exceptions.ts
17
+ function isReviewCompatibilityError(error) {
18
+ if (typeof error !== "object" && typeof error !== "function" || error === null) {
19
+ return false;
20
+ }
21
+ try {
22
+ return Object.getOwnPropertyDescriptor(
23
+ error,
24
+ REVIEW_COMPATIBILITY_ERROR_BRAND
25
+ )?.value === true;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
17
30
  function parseErrorBody(body) {
18
31
  try {
19
32
  const parsed = JSON.parse(body);
@@ -30,7 +43,7 @@ function parseErrorBody(body) {
30
43
  return { details: void 0, error: void 0, apiMessage: void 0 };
31
44
  }
32
45
  }
33
- var MAX_PROMPT_DISPLAY_LEN, FirewallBlockedException, PromptBlockedException, SilmarilApiError;
46
+ var MAX_PROMPT_DISPLAY_LEN, FirewallBlockedException, PromptBlockedException, REVIEW_COMPATIBILITY_ERROR_BRAND, ReviewCompatibilityError, SilmarilApiError;
34
47
  var init_exceptions = __esm({
35
48
  "src/exceptions.ts"() {
36
49
  "use strict";
@@ -66,6 +79,27 @@ var init_exceptions = __esm({
66
79
  }
67
80
  };
68
81
  PromptBlockedException = FirewallBlockedException;
82
+ REVIEW_COMPATIBILITY_ERROR_BRAND = /* @__PURE__ */ Symbol.for(
83
+ "@silmaril-security/sdk/ReviewCompatibilityError"
84
+ );
85
+ ReviewCompatibilityError = class _ReviewCompatibilityError extends Error {
86
+ requestedReview;
87
+ acknowledgedReview;
88
+ constructor(requestedReview, acknowledgedReview) {
89
+ const detail = acknowledgedReview === void 0 ? "the response omitted its acknowledgement" : "the response acknowledgement did not match";
90
+ super(`Silmaril review compatibility error: ${detail} for requested ${requestedReview} review`);
91
+ this.name = "ReviewCompatibilityError";
92
+ this.requestedReview = requestedReview;
93
+ this.acknowledgedReview = acknowledgedReview;
94
+ Object.defineProperty(this, REVIEW_COMPATIBILITY_ERROR_BRAND, {
95
+ value: true,
96
+ enumerable: false,
97
+ configurable: false,
98
+ writable: false
99
+ });
100
+ Object.setPrototypeOf(this, _ReviewCompatibilityError.prototype);
101
+ }
102
+ };
69
103
  SilmarilApiError = class _SilmarilApiError extends Error {
70
104
  status;
71
105
  statusText;
@@ -282,6 +316,9 @@ async function createLangChainHandler(firewall, options = {}) {
282
316
  ...toolName !== void 0 ? { toolName } : {}
283
317
  });
284
318
  } catch (err) {
319
+ if (isReviewCompatibilityError(err)) {
320
+ throw err;
321
+ }
285
322
  if (!failOpen) {
286
323
  throw err;
287
324
  }
@@ -766,12 +803,32 @@ function sanitizeText(text) {
766
803
  }
767
804
 
768
805
  // src/firewall.ts
769
- var SDK_VERSION = "0.7.7";
806
+ var SDK_VERSION = "0.7.8";
770
807
  var DEFAULT_TIMEOUT_MS = 1e4;
771
808
  var DEFAULT_MAX_RETRIES = 5;
772
809
  var MAX_BACKOFF_SECONDS = 30;
773
810
  var MAX_ERROR_BODY_BYTES = 1 << 16;
774
811
  var MAX_TIMEOUT_MS = 2147483647;
812
+ function validateReview(value, name) {
813
+ if (value === void 0) {
814
+ return void 0;
815
+ }
816
+ if (value !== "standard" && value !== "deep") {
817
+ throw new Error(`Firewall: ${name} must be standard or deep`);
818
+ }
819
+ return value;
820
+ }
821
+ function resolveReview(perCallReview, clientReview) {
822
+ return perCallReview === void 0 ? clientReview : validateReview(perCallReview, "review");
823
+ }
824
+ function verifyReviewAcknowledgement(acknowledgedReview, requestedReview) {
825
+ if (requestedReview !== void 0 && acknowledgedReview !== requestedReview) {
826
+ throw new ReviewCompatibilityError(requestedReview, acknowledgedReview);
827
+ }
828
+ }
829
+ function reviewAcknowledgementFromResponse(response) {
830
+ return isRecord2(response) ? response.review : void 0;
831
+ }
775
832
  function resolveMode(value, requestedMode) {
776
833
  let responseMode;
777
834
  if (value === "shadow" || value === "warn" || value === "block") {
@@ -784,7 +841,7 @@ function resolveMode(value, requestedMode) {
784
841
  function legacyMode(shadowMode) {
785
842
  return shadowMode === void 0 ? void 0 : shadowMode ? "shadow" : "block";
786
843
  }
787
- function blockResultFromResponse(data, requestedMode) {
844
+ function blockResultFromResponse(data, requestedMode, requestedReview) {
788
845
  if (data.prediction !== "BENIGN" && data.prediction !== "MALICIOUS") {
789
846
  throw new Error("Firewall: response prediction must be BENIGN or MALICIOUS");
790
847
  }
@@ -793,6 +850,9 @@ function blockResultFromResponse(data, requestedMode) {
793
850
  score: Number(data.score),
794
851
  threshold: Number(data.threshold)
795
852
  };
853
+ if (requestedReview !== void 0) {
854
+ result.review = requestedReview;
855
+ }
796
856
  const mode = resolveMode(data.mode, requestedMode);
797
857
  if (mode !== void 0) {
798
858
  result.mode = mode;
@@ -1067,6 +1127,7 @@ var Firewall = class {
1067
1127
  timeoutMs;
1068
1128
  shadowMode;
1069
1129
  mode;
1130
+ review;
1070
1131
  headers;
1071
1132
  constructor(options) {
1072
1133
  if (!options.apiKey) {
@@ -1087,6 +1148,7 @@ var Firewall = class {
1087
1148
  );
1088
1149
  }
1089
1150
  this.mode = options.mode ?? legacyMode(options.shadowMode);
1151
+ this.review = validateReview(options.review, "review");
1090
1152
  this.shadowMode = this.mode === "shadow";
1091
1153
  this.headers = Object.freeze({
1092
1154
  "x-api-key": this.apiKey,
@@ -1126,9 +1188,13 @@ var Firewall = class {
1126
1188
  texts: texts.map((text) => sanitizeText(text))
1127
1189
  };
1128
1190
  const requestedMode = options.mode ?? this.mode;
1191
+ const requestedReview = resolveReview(options.review, this.review);
1129
1192
  if (requestedMode !== void 0) {
1130
1193
  payload.mode = requestedMode;
1131
1194
  }
1195
+ if (requestedReview !== void 0) {
1196
+ payload.review = requestedReview;
1197
+ }
1132
1198
  if (options.hooks && options.hooks.length > 0) {
1133
1199
  payload.hooks = options.hooks.map((h) => String(h));
1134
1200
  }
@@ -1148,6 +1214,7 @@ var Firewall = class {
1148
1214
  );
1149
1215
  }
1150
1216
  const data = await this.postWithRetry(payload, options.signal);
1217
+ verifyReviewAcknowledgement(reviewAcknowledgementFromResponse(data), requestedReview);
1151
1218
  if (!Array.isArray(data.predictions)) {
1152
1219
  throw new Error("Firewall: response predictions must be an array");
1153
1220
  }
@@ -1156,7 +1223,9 @@ var Firewall = class {
1156
1223
  `Firewall: response predictions length ${data.predictions.length} does not match submitted texts length ${payload.texts.length}`
1157
1224
  );
1158
1225
  }
1159
- return data.predictions.map((p) => blockResultFromResponse(p, requestedMode));
1226
+ return data.predictions.map(
1227
+ (p) => blockResultFromResponse(p, requestedMode, requestedReview)
1228
+ );
1160
1229
  }
1161
1230
  asLangChainHandler(options = {}) {
1162
1231
  return Promise.resolve().then(() => (init_langchain(), langchain_exports)).then(
@@ -1206,9 +1275,13 @@ var Firewall = class {
1206
1275
  async classifySingle(text, options, metadataInfo) {
1207
1276
  const payload = { text };
1208
1277
  const requestedMode = options.mode ?? this.mode;
1278
+ const requestedReview = resolveReview(options.review, this.review);
1209
1279
  if (requestedMode !== void 0) {
1210
1280
  payload.mode = requestedMode;
1211
1281
  }
1282
+ if (requestedReview !== void 0) {
1283
+ payload.review = requestedReview;
1284
+ }
1212
1285
  if (options.hook !== void 0) {
1213
1286
  payload.hook = options.hook;
1214
1287
  }
@@ -1223,7 +1296,8 @@ var Firewall = class {
1223
1296
  ...options.governance === void 0 ? {} : { governance: options.governance }
1224
1297
  });
1225
1298
  const data = await this.postWithRetry(payload, options.signal);
1226
- return blockResultFromResponse(data, requestedMode);
1299
+ verifyReviewAcknowledgement(reviewAcknowledgementFromResponse(data), requestedReview);
1300
+ return blockResultFromResponse(data, requestedMode, requestedReview);
1227
1301
  }
1228
1302
  };
1229
1303
 
@@ -1246,6 +1320,7 @@ export {
1246
1320
  Outcome,
1247
1321
  PRIMARY_OUTCOMES,
1248
1322
  PromptBlockedException,
1323
+ ReviewCompatibilityError,
1249
1324
  SilmarilApiError,
1250
1325
  isHarmfulOutcome,
1251
1326
  isPrimaryOutcome,