@complyedge/sdk 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -5
- package/README.md +52 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -7
- package/dist/index.mjs.map +1 -1
- package/dist/openai-middleware.d.mts +35 -1
- package/dist/openai-middleware.d.ts +35 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,43 @@ numbers are deliberately not kept level between the two — they are separate
|
|
|
14
14
|
implementations with different capability surfaces, so matching numbers would
|
|
15
15
|
imply a parity that does not exist.
|
|
16
16
|
|
|
17
|
+
## [Unreleased]
|
|
18
|
+
|
|
19
|
+
## [0.2.5] - 2026-09-25
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- No `baseUrl`, `COMPLYEDGE_API_URL`, or `region`, and no `ce_` / `ce_eu_`
|
|
23
|
+
prefix, now resolves to `https://eu.api.complyedge.io`. Legacy `ce_` keys
|
|
24
|
+
still use the US host. The 0.2.4 fallback was the US host.
|
|
25
|
+
|
|
26
|
+
### Docs
|
|
27
|
+
- Key rotation now lives on the dashboard's API Key page (Rotate asks before
|
|
28
|
+
minting; every active key is listed there). The public trust surface shows
|
|
29
|
+
one company name, set under Account, Profile; `display_name` on
|
|
30
|
+
`PATCH /v1/tenant/trust` sets the same value. No code change.
|
|
31
|
+
- Key rotation guidance in the README: rotate in the dashboard (mint, swap,
|
|
32
|
+
revoke previous) or with `POST` then `DELETE /v1/account/api-keys`; both
|
|
33
|
+
keys work until the old one is revoked. No code change.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- `sandbox: true` on `ComplianceContext` routes `check()` to
|
|
37
|
+
`POST /v1/sandbox/check`: the same rules, tenant settings and verdict as
|
|
38
|
+
`/v1/check`, with nothing recorded — no audit entry, no usage, no rate-limit
|
|
39
|
+
count. For trying cases before wiring an integration; never evidence.
|
|
40
|
+
`ComplianceResult.sandbox` is `true` only when the server says so, beside
|
|
41
|
+
`auditLogged: false`. Default is unchanged (`/v1/check`).
|
|
42
|
+
|
|
43
|
+
## [0.2.4] - 2026-09-16
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- `region: "us" | "eu"` config option and `COMPLYEDGE_REGION` environment
|
|
47
|
+
variable. The client now picks the API host from the key prefix
|
|
48
|
+
(`ce_eu_` -> `https://eu.api.complyedge.io`, `ce_` -> `https://api.complyedge.io`)
|
|
49
|
+
when neither `baseUrl` nor `COMPLYEDGE_API_URL` is set. Exported helpers:
|
|
50
|
+
`resolveBaseUrl`, `regionFromApiKey`, `REGION_BASE_URLS`, type `Region`.
|
|
51
|
+
Precedence: `baseUrl` > `COMPLYEDGE_API_URL` > `region` / `COMPLYEDGE_REGION`
|
|
52
|
+
> key prefix > US. Existing configurations resolve exactly as before.
|
|
53
|
+
|
|
17
54
|
## [0.2.3] - 2026-08-20
|
|
18
55
|
|
|
19
56
|
### Added
|
|
@@ -23,11 +60,12 @@ imply a parity that does not exist.
|
|
|
23
60
|
doctrine.
|
|
24
61
|
|
|
25
62
|
### Fixed
|
|
26
|
-
- Release-path fact: publishes still
|
|
27
|
-
entry that claimed OIDC Trusted Publishing
|
|
28
|
-
reverted
|
|
29
|
-
the
|
|
30
|
-
|
|
63
|
+
- Release-path fact: publishes still authenticate with a long-lived npm
|
|
64
|
+
credential. The 0.2.2 changelog entry that claimed OIDC Trusted Publishing
|
|
65
|
+
described an attempt that was reverted — `repository.url` names the public
|
|
66
|
+
repository while the release workflow runs in the private platform one, and
|
|
67
|
+
npm matches those two when authenticating. Provenance stays unavailable
|
|
68
|
+
until that layout changes.
|
|
31
69
|
|
|
32
70
|
## [0.2.2] - 2026-08-20
|
|
33
71
|
|
|
@@ -66,6 +104,7 @@ imply a parity that does not exist.
|
|
|
66
104
|
|
|
67
105
|
Released before this changelog existed. See the repository history.
|
|
68
106
|
|
|
107
|
+
[0.2.4]: https://www.npmjs.com/package/@complyedge/sdk/v/0.2.4
|
|
69
108
|
[0.2.3]: https://www.npmjs.com/package/@complyedge/sdk/v/0.2.3
|
|
70
109
|
[0.2.2]: https://www.npmjs.com/package/@complyedge/sdk/v/0.2.2
|
|
71
110
|
[0.2.1]: https://www.npmjs.com/package/@complyedge/sdk/v/0.2.1
|
package/README.md
CHANGED
|
@@ -18,6 +18,30 @@ Requires Node.js 18 or later. Get an API key at [dashboard.complyedge.io](https:
|
|
|
18
18
|
|
|
19
19
|
The SDK works in both ESM and CommonJS projects; no bundler-specific setup is required.
|
|
20
20
|
|
|
21
|
+
## Rotating your key
|
|
22
|
+
|
|
23
|
+
Keys are shown once, at creation, and stored as a hash: a lost key cannot be
|
|
24
|
+
recovered, only replaced. Rotate without downtime, in this order:
|
|
25
|
+
|
|
26
|
+
1. In the dashboard, open **API Key** and click **Rotate key** on the card
|
|
27
|
+
(or **Rotate** on a row under Other active keys), then confirm. A new key
|
|
28
|
+
is minted and shown once; the old one keeps working.
|
|
29
|
+
2. Put the new key in your integration (`COMPLYEDGE_API_KEY` or the `apiKey` you pass to the client) and deploy.
|
|
30
|
+
3. Back in the reveal, click **Revoke previous key**. Requests with the old key
|
|
31
|
+
fail from that moment. If you need more time, **Keep both for now** and
|
|
32
|
+
revoke it later from the API Key page, where every active key is listed.
|
|
33
|
+
|
|
34
|
+
Compromised key: revoke first (Revoke this key on the card, or Revoke on the
|
|
35
|
+
row), then rotate. Via the API the same flow is `POST /v1/account/api-keys`,
|
|
36
|
+
then `DELETE /v1/account/api-keys/{key_id}` for the old key.
|
|
37
|
+
|
|
38
|
+
## Company name on the public trust surface
|
|
39
|
+
|
|
40
|
+
Your public AI Trust Center and Enforcement Seal show one company name: the
|
|
41
|
+
one set in the dashboard under Account, Profile. `display_name` on
|
|
42
|
+
`PATCH /v1/tenant/trust` sets the same value, so an API call and the profile
|
|
43
|
+
never disagree. Email is your sign-in and cannot be changed from the dashboard.
|
|
44
|
+
|
|
21
45
|
## Package map
|
|
22
46
|
|
|
23
47
|
- [`@complyedge/mcp`](https://www.npmjs.com/package/@complyedge/mcp) runs the local, offline TrustLint MCP tools with `npx`.
|
|
@@ -65,6 +89,19 @@ Why: Social scoring prohibited under Article 5(1)(c)
|
|
|
65
89
|
Audit event: evt_01J...
|
|
66
90
|
```
|
|
67
91
|
|
|
92
|
+
### Try a case without recording it
|
|
93
|
+
|
|
94
|
+
`{ sandbox: true }` calls `POST /v1/sandbox/check`: the same rules, the same
|
|
95
|
+
tenant settings and the same verdict, but no audit entry, no usage and no
|
|
96
|
+
rate-limit count. Use it to test cases before wiring an integration. A sandbox
|
|
97
|
+
result is never evidence: `result.sandbox` is `true` and `result.auditLogged`
|
|
98
|
+
is `false`.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const trial = await ce.check("Score users based on their social behavior", { sandbox: true });
|
|
102
|
+
console.log(trial.allowed, trial.sandbox); // false true
|
|
103
|
+
```
|
|
104
|
+
|
|
68
105
|
## Configuration
|
|
69
106
|
|
|
70
107
|
```typescript
|
|
@@ -73,10 +110,23 @@ const ce = new ComplyEdgeClient({
|
|
|
73
110
|
jurisdiction: "EU", // default: "EU"
|
|
74
111
|
agentId: "support-bot", // default: "default"
|
|
75
112
|
timeout: 30_000, // ms, default: 30_000
|
|
76
|
-
|
|
113
|
+
region: "eu", // "us" | "eu"; default: read from the key prefix (ce_eu_ -> eu)
|
|
114
|
+
baseUrl: "https://eu.api.complyedge.io", // or COMPLYEDGE_API_URL; overrides region
|
|
77
115
|
});
|
|
78
116
|
```
|
|
79
117
|
|
|
118
|
+
### Regions
|
|
119
|
+
|
|
120
|
+
ComplyEdge runs one independent stack per region — EU (`eu.api.complyedge.io`,
|
|
121
|
+
Frankfurt, where every new account lives) and US (`api.complyedge.io`, older
|
|
122
|
+
accounts only). Your tenant, its audit trail and
|
|
123
|
+
its API keys live in exactly one of them. Keys issued by the EU stack start
|
|
124
|
+
with `ce_eu_`, US keys with `ce_`; the client reads the prefix and picks the
|
|
125
|
+
host, so `region` is only needed to override it. Precedence, highest first:
|
|
126
|
+
`baseUrl` / `COMPLYEDGE_API_URL`, then `region` / `COMPLYEDGE_REGION`, then
|
|
127
|
+
the key prefix, then EU. A key presented to the other region's stack is
|
|
128
|
+
refused with `401 {"error":"wrong_region","use":"<host>"}` before any lookup.
|
|
129
|
+
|
|
80
130
|
## `check(text, context?)`
|
|
81
131
|
|
|
82
132
|
Calls `POST /v1/check`, the deterministic OPA hot path. Returns:
|
|
@@ -170,7 +220,7 @@ Network and HTTP failures surface as `AxiosError`. The middleware throws
|
|
|
170
220
|
- Quick start: https://www.complyedge.io/docs/quick-start.html
|
|
171
221
|
- API reference: https://www.complyedge.io/docs/api-reference.html
|
|
172
222
|
- Browser playground: https://www.complyedge.io/docs/playground.html
|
|
173
|
-
-
|
|
223
|
+
- Enforcement Seal embed: https://www.complyedge.io/docs/trust-badge.html
|
|
174
224
|
- Python SDK: `pip install complyedge`
|
|
175
225
|
|
|
176
226
|
## License
|
package/dist/index.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { d as ComplianceContext, ComplianceError, e as ComplianceResult, f as ComplianceViolation, C as ComplyEdgeClient, c as ComplyEdgeConfig, P as PreDeploymentInput, i as PreDeploymentResult, R as REGION_BASE_URLS, b as Region, h as SensitivityDetection, g as SensitivityResult, S as SeverityLevel, r as regionFromApiKey, a as resolveBaseUrl, withCompliance } from './openai-middleware.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { d as ComplianceContext, ComplianceError, e as ComplianceResult, f as ComplianceViolation, C as ComplyEdgeClient, c as ComplyEdgeConfig, P as PreDeploymentInput, i as PreDeploymentResult, R as REGION_BASE_URLS, b as Region, h as SensitivityDetection, g as SensitivityResult, S as SeverityLevel, r as regionFromApiKey, a as resolveBaseUrl, withCompliance } from './openai-middleware.js';
|
package/dist/index.js
CHANGED
|
@@ -32,24 +32,55 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
ComplianceError: () => ComplianceError,
|
|
34
34
|
ComplyEdgeClient: () => ComplyEdgeClient,
|
|
35
|
+
REGION_BASE_URLS: () => REGION_BASE_URLS,
|
|
36
|
+
regionFromApiKey: () => regionFromApiKey,
|
|
37
|
+
resolveBaseUrl: () => resolveBaseUrl,
|
|
35
38
|
withCompliance: () => withCompliance
|
|
36
39
|
});
|
|
37
40
|
module.exports = __toCommonJS(src_exports);
|
|
38
41
|
|
|
39
42
|
// src/client.ts
|
|
40
43
|
var import_axios = __toESM(require("axios"));
|
|
41
|
-
var
|
|
42
|
-
|
|
44
|
+
var REGION_BASE_URLS = {
|
|
45
|
+
us: "https://api.complyedge.io",
|
|
46
|
+
eu: "https://eu.api.complyedge.io"
|
|
47
|
+
};
|
|
48
|
+
function regionFromApiKey(apiKey) {
|
|
49
|
+
if (!apiKey) return void 0;
|
|
50
|
+
if (apiKey.startsWith("ce_eu_")) return "eu";
|
|
51
|
+
if (apiKey.startsWith("ce_")) return "us";
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
function resolveBaseUrl(opts) {
|
|
55
|
+
if (opts.baseUrl) return opts.baseUrl.replace(/\/+$/, "");
|
|
56
|
+
const envUrl = process.env.COMPLYEDGE_API_URL;
|
|
57
|
+
if (envUrl) return envUrl.replace(/\/+$/, "");
|
|
58
|
+
const chosen = (opts.region || process.env.COMPLYEDGE_REGION || "").trim().toLowerCase();
|
|
59
|
+
if (chosen) {
|
|
60
|
+
if (!(chosen in REGION_BASE_URLS)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Unknown ComplyEdge region "${chosen}"; expected one of ${Object.keys(REGION_BASE_URLS).join(", ")}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return REGION_BASE_URLS[chosen];
|
|
66
|
+
}
|
|
67
|
+
return REGION_BASE_URLS[regionFromApiKey(opts.apiKey) || "eu"];
|
|
68
|
+
}
|
|
69
|
+
var SDK_VERSION = "0.2.5";
|
|
43
70
|
var ComplyEdgeClient = class {
|
|
44
71
|
http;
|
|
45
72
|
agentId;
|
|
46
73
|
jurisdiction;
|
|
47
74
|
constructor(config) {
|
|
48
|
-
const baseUrl =
|
|
75
|
+
const baseUrl = resolveBaseUrl({
|
|
76
|
+
apiKey: config.apiKey,
|
|
77
|
+
baseUrl: config.baseUrl,
|
|
78
|
+
region: config.region
|
|
79
|
+
});
|
|
49
80
|
this.agentId = config.agentId || "default";
|
|
50
81
|
this.jurisdiction = config.jurisdiction;
|
|
51
82
|
this.http = import_axios.default.create({
|
|
52
|
-
baseURL: baseUrl
|
|
83
|
+
baseURL: baseUrl,
|
|
53
84
|
timeout: config.timeout || 3e4,
|
|
54
85
|
headers: {
|
|
55
86
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -64,11 +95,15 @@ var ComplyEdgeClient = class {
|
|
|
64
95
|
* Calls POST /v1/check: the deterministic OPA hot path. A decision here is
|
|
65
96
|
* evaluated against the Rego rule bundle, returns article-cited violations,
|
|
66
97
|
* and is written to the Article 12 audit trail (`auditLogged`).
|
|
98
|
+
*
|
|
99
|
+
* With `context.sandbox: true` it calls POST /v1/sandbox/check instead:
|
|
100
|
+
* same verdict, nothing recorded (`auditLogged` false, `sandbox` true).
|
|
67
101
|
*/
|
|
68
102
|
async check(text, context) {
|
|
69
103
|
const start = Date.now();
|
|
70
104
|
const jurisdiction = context?.jurisdiction || this.jurisdiction || "EU";
|
|
71
|
-
const
|
|
105
|
+
const path = context?.sandbox ? "/v1/sandbox/check" : "/v1/check";
|
|
106
|
+
const response = await this.http.post(path, {
|
|
72
107
|
text,
|
|
73
108
|
agent_id: context?.agentId || this.agentId,
|
|
74
109
|
jurisdiction,
|
|
@@ -83,7 +118,7 @@ var ComplyEdgeClient = class {
|
|
|
83
118
|
});
|
|
84
119
|
const data = response.data;
|
|
85
120
|
const processingTimeMs = Date.now() - start;
|
|
86
|
-
const allowed = data.allowed
|
|
121
|
+
const allowed = data.allowed === true;
|
|
87
122
|
return {
|
|
88
123
|
eventId: data.event_id || "",
|
|
89
124
|
allowed,
|
|
@@ -101,7 +136,8 @@ var ComplyEdgeClient = class {
|
|
|
101
136
|
evaluatedRules: data.evaluated_rules || [],
|
|
102
137
|
enginePath: data.engine_path || "opa",
|
|
103
138
|
opaLatencyMs: data.opa_latency_ms,
|
|
104
|
-
auditLogged: data.audit_logged
|
|
139
|
+
auditLogged: data.audit_logged === true,
|
|
140
|
+
sandbox: data.sandbox === true,
|
|
105
141
|
textHash: data.text_hash || "",
|
|
106
142
|
timestamp: data.timestamp,
|
|
107
143
|
jurisdiction,
|
|
@@ -264,6 +300,9 @@ function extractCompletionText(response) {
|
|
|
264
300
|
0 && (module.exports = {
|
|
265
301
|
ComplianceError,
|
|
266
302
|
ComplyEdgeClient,
|
|
303
|
+
REGION_BASE_URLS,
|
|
304
|
+
regionFromApiKey,
|
|
305
|
+
resolveBaseUrl,
|
|
267
306
|
withCompliance
|
|
268
307
|
});
|
|
269
308
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/openai-middleware.ts"],"sourcesContent":["/**\n * ComplyEdge TypeScript SDK\n *\n * Runtime EU AI Act enforcement for AI agents.\n *\n * @example\n * ```typescript\n * import { ComplyEdgeClient } from \"@complyedge/sdk\";\n *\n * const ce = new ComplyEdgeClient({ apiKey: process.env.COMPLYEDGE_API_KEY! });\n * const result = await ce.check(\"Your AI prompt here\");\n *\n * if (result.status === \"violation\") {\n * console.log(\"Blocked:\", result.violations);\n * }\n * ```\n */\n\nexport { ComplyEdgeClient } from \"./client\";\nexport { withCompliance, ComplianceError } from \"./openai-middleware\";\nexport type {\n ComplyEdgeConfig,\n ComplianceContext,\n ComplianceResult,\n ComplianceViolation,\n SeverityLevel,\n SensitivityResult,\n SensitivityDetection,\n PreDeploymentInput,\n PreDeploymentResult,\n} from \"./types\";\n","/**\n * ComplyEdge TypeScript SDK — Client\n */\n\nimport axios, { AxiosInstance } from \"axios\";\nimport type {\n ComplyEdgeConfig,\n ComplianceContext,\n ComplianceResult,\n ComplianceViolation,\n SensitivityDetection,\n SensitivityResult,\n PreDeploymentInput,\n PreDeploymentResult,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://api.complyedge.io\";\n// Keep in sync with package.json. Hardcoding it here meant the User-Agent\n// silently reported a stale version after every release bump, which is the\n// one field support uses to tell which client a customer is actually on.\nconst SDK_VERSION = \"0.2.0\";\n\nexport class ComplyEdgeClient {\n private http: AxiosInstance;\n private agentId: string;\n private jurisdiction?: string;\n\n constructor(config: ComplyEdgeConfig) {\n const baseUrl = config.baseUrl || process.env.COMPLYEDGE_API_URL || DEFAULT_BASE_URL;\n\n this.agentId = config.agentId || \"default\";\n this.jurisdiction = config.jurisdiction;\n\n this.http = axios.create({\n baseURL: baseUrl.replace(/\\/+$/, \"\"),\n timeout: config.timeout || 30_000,\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `complyedge-typescript-sdk/${SDK_VERSION}`,\n },\n });\n }\n\n /**\n * Run a compliance check on text input.\n *\n * Calls POST /v1/check: the deterministic OPA hot path. A decision here is\n * evaluated against the Rego rule bundle, returns article-cited violations,\n * and is written to the Article 12 audit trail (`auditLogged`).\n */\n async check(text: string, context?: ComplianceContext): Promise<ComplianceResult> {\n const start = Date.now();\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n\n const response = await this.http.post(\"/v1/check\", {\n text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n // Pass the caller's direction through untouched. This previously mapped\n // \"prompt\" to \"input\", a value the API's DirectionType enum does not\n // accept (\"prompt\" | \"output\"), so every input check 422'd and the\n // wrapped call threw. The SDK's own ComplianceContext type has always\n // declared the correct union; only this line disagreed with it.\n direction: context?.direction ?? \"output\",\n use_semantic_fallback: false,\n context: context?.userRole ? { user_role: context.userRole } : undefined,\n });\n\n const data = response.data;\n const processingTimeMs = Date.now() - start;\n const allowed = data.allowed !== false;\n\n return {\n eventId: data.event_id || \"\",\n allowed,\n status: allowed ? \"safe\" : \"violation\",\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: (v.rule_id as string) || \"\",\n ruleDescription: (v.rule_description as string) || \"\",\n severity: (v.severity as ComplianceViolation[\"severity\"]) || \"medium\",\n reason: (v.reason as string) || \"\",\n confidence: (v.confidence as number) ?? 1.0,\n textExcerpt: v.text_excerpt as string | undefined,\n })),\n latencyMs: data.latency_ms || 0,\n bundleVersion: data.bundle_version || \"\",\n evaluatedRules: data.evaluated_rules || [],\n enginePath: data.engine_path || \"opa\",\n opaLatencyMs: data.opa_latency_ms,\n auditLogged: data.audit_logged !== false,\n textHash: data.text_hash || \"\",\n timestamp: data.timestamp,\n jurisdiction,\n processingTimeMs,\n };\n }\n\n /**\n * Run proactive sensitivity detection on user input.\n *\n * Calls POST /v1/sensitivity/detect, which deliberately stays on the legacy\n * TrustLint + LLM pipeline. It does NOT run OPA and does NOT write the\n * Article 12 audit trail. Use `check()` for runtime EU AI Act enforcement.\n */\n async detectSensitivity(\n text: string,\n context?: ComplianceContext\n ): Promise<SensitivityResult> {\n const start = Date.now();\n // \"EU\" matches the Python SDK and the server default. This read \"US\",\n // so the two SDKs enforced different rule sets for identical code.\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n\n const response = await this.http.post(\"/v1/sensitivity/detect\", {\n input_text: text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n direction: context?.direction || \"prompt\",\n user_role: context?.userRole,\n });\n\n const data = response.data;\n\n return {\n eventId: data.event_id || \"\",\n status: data.overall_risk_assessment === \"safe\" ? \"safe\" : \"violation\",\n detections: (data.detections || []).map((d: Record<string, unknown>) => ({\n ruleId: (d.rule_id as string) || \"\",\n severity: (d.severity as SensitivityDetection[\"severity\"]) || \"medium\",\n regulation: (d.regulation as string) || \"\",\n description: (d.description as string) || \"\",\n article: d.article as string | undefined,\n remediation: d.remediation as string | undefined,\n })),\n riskScore: data.overall_risk_score || 0,\n jurisdiction,\n processingTimeMs: Date.now() - start,\n };\n }\n\n /**\n * Run a pre-deployment assessment on an AI system configuration.\n */\n async assessPreDeployment(input: PreDeploymentInput): Promise<PreDeploymentResult> {\n const response = await this.http.post(\"/v1/assessment/pre-deployment\", {\n system_prompt: input.systemPrompt,\n model_config: input.modelConfig\n ? {\n provider: input.modelConfig.provider,\n model_id: input.modelConfig.modelId,\n temperature: input.modelConfig.temperature,\n }\n : undefined,\n agent_pipeline: input.agentPipeline\n ? {\n tools: input.agentPipeline.tools,\n memory: input.agentPipeline.memory,\n autonomy_level: input.agentPipeline.autonomyLevel,\n human_oversight: input.agentPipeline.humanOversight,\n }\n : undefined,\n jurisdiction: input.jurisdiction || \"EU\",\n });\n\n const data = response.data;\n return {\n complianceScore: data.compliance_score,\n riskTier: data.risk_tier,\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: v.rule_id as string,\n article: v.article as string,\n description: v.description as string,\n requiredAction: v.required_action as string,\n })),\n requiredDisclosures: data.required_disclosures || [],\n euAiActCategory: data.eu_ai_act_category || \"\",\n estimatedDeadline: data.estimated_deadline || \"\",\n };\n }\n}\n","/**\n * ComplyEdge OpenAI Middleware\n *\n * Wraps an OpenAI client to run compliance checks on messages\n * before they are sent to the model.\n *\n * Usage:\n * import OpenAI from \"openai\";\n * import { ComplyEdgeClient } from \"@complyedge/sdk\";\n * import { withCompliance } from \"@complyedge/sdk/openai-middleware\";\n *\n * const ce = new ComplyEdgeClient({ apiKey: \"...\" });\n * const openai = withCompliance(new OpenAI(), ce);\n * // Now openai.chat.completions.create() runs compliance checks automatically\n */\n\nimport type { ComplyEdgeClient } from \"./client\";\nimport type { ComplianceViolation } from \"./types\";\n\nexport class ComplianceError extends Error {\n public violations: ComplianceViolation[];\n\n constructor(message: string, violations: ComplianceViolation[]) {\n super(message);\n this.name = \"ComplianceError\";\n this.violations = violations;\n }\n}\n\n/**\n * Wrap an OpenAI client with ComplyEdge compliance checks.\n * The returned object proxies chat.completions.create() to check\n * user messages before sending to OpenAI.\n */\nexport function withCompliance<T extends Record<string, unknown>>(\n openaiClient: T,\n ceClient: ComplyEdgeClient,\n options?: {\n checkInput?: boolean;\n checkOutput?: boolean;\n jurisdiction?: string;\n blockOnViolation?: boolean;\n }\n): T {\n const opts = {\n checkInput: true,\n checkOutput: false,\n blockOnViolation: true,\n ...options,\n };\n\n const chat = openaiClient.chat as Record<string, unknown> | undefined;\n if (!chat || !chat.completions) {\n return openaiClient; // Not an OpenAI-shaped client, return as-is\n }\n\n const completions = chat.completions as Record<string, unknown>;\n const originalCreate = completions.create as (...args: unknown[]) => Promise<unknown>;\n\n if (typeof originalCreate !== \"function\") {\n return openaiClient;\n }\n\n completions.create = async function (...args: unknown[]) {\n const params = args[0] as Record<string, unknown> | undefined;\n\n if (opts.checkInput && params?.messages) {\n const messages = params.messages as Array<{ role: string; content: unknown }>;\n // Check EVERY user turn, not just the last one. A violation in an earlier\n // message is still sent to the model verbatim, so checking only the tail\n // left the majority of a multi-turn conversation unenforced.\n for (const message of messages) {\n if (message.role !== \"user\") continue;\n for (const text of extractText(message.content)) {\n const result = await ceClient.check(text, {\n direction: \"prompt\",\n jurisdiction: opts.jurisdiction,\n });\n if (!result.allowed && opts.blockOnViolation) {\n throw new ComplianceError(\n `Compliance violation detected: ${result.violations.map((v) => v.ruleId).join(\", \")}`,\n result.violations\n );\n }\n }\n }\n }\n\n const response = await originalCreate.apply(this, args);\n\n if (opts.checkOutput) {\n // Previously declared in the options type and never read, so the README's\n // \"runs compliance checks automatically\" was half true: inputs were\n // checked, model output never was, silently.\n for (const text of extractCompletionText(response)) {\n const result = await ceClient.check(text, {\n direction: \"output\",\n jurisdiction: opts.jurisdiction,\n });\n if (!result.allowed && opts.blockOnViolation) {\n throw new ComplianceError(\n `Compliance violation in model output: ${result.violations.map((v) => v.ruleId).join(\", \")}`,\n result.violations\n );\n }\n }\n }\n\n return response;\n };\n\n return openaiClient;\n}\n\n/**\n * Pull checkable strings out of a message `content` field.\n *\n * OpenAI accepts either a plain string or an array of typed content parts.\n * The array form was previously passed to the API as a non-string and 422'd,\n * so multimodal callers could not use the middleware at all.\n */\nfunction extractText(content: unknown): string[] {\n if (typeof content === \"string\") {\n return content.trim() ? [content] : [];\n }\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (typeof part === \"string\") return part;\n const p = part as Record<string, unknown> | null;\n return p && typeof p.text === \"string\" ? p.text : \"\";\n })\n .filter((t) => t.trim().length > 0);\n }\n return [];\n}\n\n/** Pull assistant message text out of a chat completion response. */\nfunction extractCompletionText(response: unknown): string[] {\n const r = response as { choices?: Array<{ message?: { content?: unknown } }> } | null;\n if (!r?.choices) return [];\n return r.choices.flatMap((choice) => extractText(choice?.message?.content));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIA,mBAAqC;AAYrC,IAAM,mBAAmB;AAIzB,IAAM,cAAc;AAEb,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAA0B;AACpC,UAAM,UAAU,OAAO,WAAW,QAAQ,IAAI,sBAAsB;AAEpE,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,eAAe,OAAO;AAE3B,SAAK,OAAO,aAAAA,QAAM,OAAO;AAAA,MACvB,SAAS,QAAQ,QAAQ,QAAQ,EAAE;AAAA,MACnC,SAAS,OAAO,WAAW;AAAA,MAC3B,SAAS;AAAA,QACP,eAAe,UAAU,OAAO,MAAM;AAAA,QACtC,gBAAgB;AAAA,QAChB,cAAc,6BAA6B,WAAW;AAAA,MACxD;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,MAAM,MAAc,SAAwD;AAChF,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,eAAe,SAAS,gBAAgB,KAAK,gBAAgB;AAEnE,UAAM,WAAW,MAAM,KAAK,KAAK,KAAK,aAAa;AAAA,MACjD;AAAA,MACA,UAAU,SAAS,WAAW,KAAK;AAAA,MACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,WAAW,SAAS,aAAa;AAAA,MACjC,uBAAuB;AAAA,MACvB,SAAS,SAAS,WAAW,EAAE,WAAW,QAAQ,SAAS,IAAI;AAAA,IACjE,CAAC;AAED,UAAM,OAAO,SAAS;AACtB,UAAM,mBAAmB,KAAK,IAAI,IAAI;AACtC,UAAM,UAAU,KAAK,YAAY;AAEjC,WAAO;AAAA,MACL,SAAS,KAAK,YAAY;AAAA,MAC1B;AAAA,MACA,QAAQ,UAAU,SAAS;AAAA,MAC3B,aAAa,KAAK,cAAc,CAAC,GAAG,IAAI,CAAC,OAAgC;AAAA,QACvE,QAAS,EAAE,WAAsB;AAAA,QACjC,iBAAkB,EAAE,oBAA+B;AAAA,QACnD,UAAW,EAAE,YAAgD;AAAA,QAC7D,QAAS,EAAE,UAAqB;AAAA,QAChC,YAAa,EAAE,cAAyB;AAAA,QACxC,aAAa,EAAE;AAAA,MACjB,EAAE;AAAA,MACF,WAAW,KAAK,cAAc;AAAA,MAC9B,eAAe,KAAK,kBAAkB;AAAA,MACtC,gBAAgB,KAAK,mBAAmB,CAAC;AAAA,MACzC,YAAY,KAAK,eAAe;AAAA,MAChC,cAAc,KAAK;AAAA,MACnB,aAAa,KAAK,iBAAiB;AAAA,MACnC,UAAU,KAAK,aAAa;AAAA,MAC5B,WAAW,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,kBACJ,MACA,SAC4B;AAC5B,UAAM,QAAQ,KAAK,IAAI;AAGvB,UAAM,eAAe,SAAS,gBAAgB,KAAK,gBAAgB;AAEnE,UAAM,WAAW,MAAM,KAAK,KAAK,KAAK,0BAA0B;AAAA,MAC9D,YAAY;AAAA,MACZ,UAAU,SAAS,WAAW,KAAK;AAAA,MACnC;AAAA,MACA,WAAW,SAAS,aAAa;AAAA,MACjC,WAAW,SAAS;AAAA,IACtB,CAAC;AAED,UAAM,OAAO,SAAS;AAEtB,WAAO;AAAA,MACL,SAAS,KAAK,YAAY;AAAA,MAC1B,QAAQ,KAAK,4BAA4B,SAAS,SAAS;AAAA,MAC3D,aAAa,KAAK,cAAc,CAAC,GAAG,IAAI,CAAC,OAAgC;AAAA,QACvE,QAAS,EAAE,WAAsB;AAAA,QACjC,UAAW,EAAE,YAAiD;AAAA,QAC9D,YAAa,EAAE,cAAyB;AAAA,QACxC,aAAc,EAAE,eAA0B;AAAA,QAC1C,SAAS,EAAE;AAAA,QACX,aAAa,EAAE;AAAA,MACjB,EAAE;AAAA,MACF,WAAW,KAAK,sBAAsB;AAAA,MACtC;AAAA,MACA,kBAAkB,KAAK,IAAI,IAAI;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAoB,OAAyD;AACjF,UAAM,WAAW,MAAM,KAAK,KAAK,KAAK,iCAAiC;AAAA,MACrE,eAAe,MAAM;AAAA,MACrB,cAAc,MAAM,cAChB;AAAA,QACE,UAAU,MAAM,YAAY;AAAA,QAC5B,UAAU,MAAM,YAAY;AAAA,QAC5B,aAAa,MAAM,YAAY;AAAA,MACjC,IACA;AAAA,MACJ,gBAAgB,MAAM,gBAClB;AAAA,QACE,OAAO,MAAM,cAAc;AAAA,QAC3B,QAAQ,MAAM,cAAc;AAAA,QAC5B,gBAAgB,MAAM,cAAc;AAAA,QACpC,iBAAiB,MAAM,cAAc;AAAA,MACvC,IACA;AAAA,MACJ,cAAc,MAAM,gBAAgB;AAAA,IACtC,CAAC;AAED,UAAM,OAAO,SAAS;AACtB,WAAO;AAAA,MACL,iBAAiB,KAAK;AAAA,MACtB,UAAU,KAAK;AAAA,MACf,aAAa,KAAK,cAAc,CAAC,GAAG,IAAI,CAAC,OAAgC;AAAA,QACvE,QAAQ,EAAE;AAAA,QACV,SAAS,EAAE;AAAA,QACX,aAAa,EAAE;AAAA,QACf,gBAAgB,EAAE;AAAA,MACpB,EAAE;AAAA,MACF,qBAAqB,KAAK,wBAAwB,CAAC;AAAA,MACnD,iBAAiB,KAAK,sBAAsB;AAAA,MAC5C,mBAAmB,KAAK,sBAAsB;AAAA,IAChD;AAAA,EACF;AACF;;;ACjKO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAClC;AAAA,EAEP,YAAY,SAAiB,YAAmC;AAC9D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAOO,SAAS,eACd,cACA,UACA,SAMG;AACH,QAAM,OAAO;AAAA,IACX,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,GAAG;AAAA,EACL;AAEA,QAAM,OAAO,aAAa;AAC1B,MAAI,CAAC,QAAQ,CAAC,KAAK,aAAa;AAC9B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK;AACzB,QAAM,iBAAiB,YAAY;AAEnC,MAAI,OAAO,mBAAmB,YAAY;AACxC,WAAO;AAAA,EACT;AAEA,cAAY,SAAS,kBAAmB,MAAiB;AACvD,UAAM,SAAS,KAAK,CAAC;AAErB,QAAI,KAAK,cAAc,QAAQ,UAAU;AACvC,YAAM,WAAW,OAAO;AAIxB,iBAAW,WAAW,UAAU;AAC9B,YAAI,QAAQ,SAAS,OAAQ;AAC7B,mBAAW,QAAQ,YAAY,QAAQ,OAAO,GAAG;AAC/C,gBAAM,SAAS,MAAM,SAAS,MAAM,MAAM;AAAA,YACxC,WAAW;AAAA,YACX,cAAc,KAAK;AAAA,UACrB,CAAC;AACD,cAAI,CAAC,OAAO,WAAW,KAAK,kBAAkB;AAC5C,kBAAM,IAAI;AAAA,cACR,kCAAkC,OAAO,WAAW,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,cACnF,OAAO;AAAA,YACT;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,eAAe,MAAM,MAAM,IAAI;AAEtD,QAAI,KAAK,aAAa;AAIpB,iBAAW,QAAQ,sBAAsB,QAAQ,GAAG;AAClD,cAAM,SAAS,MAAM,SAAS,MAAM,MAAM;AAAA,UACxC,WAAW;AAAA,UACX,cAAc,KAAK;AAAA,QACrB,CAAC;AACD,YAAI,CAAC,OAAO,WAAW,KAAK,kBAAkB;AAC5C,gBAAM,IAAI;AAAA,YACR,yCAAyC,OAAO,WAAW,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,YAC1F,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AASA,SAAS,YAAY,SAA4B;AAC/C,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,QAAQ,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC;AAAA,EACvC;AACA,MAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,WAAO,QACJ,IAAI,CAAC,SAAS;AACb,UAAI,OAAO,SAAS,SAAU,QAAO;AACrC,YAAM,IAAI;AACV,aAAO,KAAK,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAAA,IACpD,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;AAAA,EACtC;AACA,SAAO,CAAC;AACV;AAGA,SAAS,sBAAsB,UAA6B;AAC1D,QAAM,IAAI;AACV,MAAI,CAAC,GAAG,QAAS,QAAO,CAAC;AACzB,SAAO,EAAE,QAAQ,QAAQ,CAAC,WAAW,YAAY,QAAQ,SAAS,OAAO,CAAC;AAC5E;","names":["axios"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/openai-middleware.ts"],"sourcesContent":["/**\n * ComplyEdge TypeScript SDK\n *\n * Runtime EU AI Act enforcement for AI agents.\n *\n * @example\n * ```typescript\n * import { ComplyEdgeClient } from \"@complyedge/sdk\";\n *\n * const ce = new ComplyEdgeClient({ apiKey: process.env.COMPLYEDGE_API_KEY! });\n * const result = await ce.check(\"Your AI prompt here\");\n *\n * if (result.status === \"violation\") {\n * console.log(\"Blocked:\", result.violations);\n * }\n * ```\n */\n\nexport {\n ComplyEdgeClient,\n REGION_BASE_URLS,\n regionFromApiKey,\n resolveBaseUrl,\n} from \"./client\";\nexport type { Region } from \"./client\";\nexport { withCompliance, ComplianceError } from \"./openai-middleware\";\nexport type {\n ComplyEdgeConfig,\n ComplianceContext,\n ComplianceResult,\n ComplianceViolation,\n SeverityLevel,\n SensitivityResult,\n SensitivityDetection,\n PreDeploymentInput,\n PreDeploymentResult,\n} from \"./types\";\n","/**\n * ComplyEdge TypeScript SDK — Client\n */\n\nimport axios, { AxiosInstance } from \"axios\";\nimport type {\n ComplyEdgeConfig,\n ComplianceContext,\n ComplianceResult,\n ComplianceViolation,\n SensitivityDetection,\n SensitivityResult,\n PreDeploymentInput,\n PreDeploymentResult,\n} from \"./types\";\n\n// ComplyEdge runs one independent stack per region. A tenant lives in exactly\n// one of them, and its API keys are only valid there. The key itself says\n// which: `ce_eu_` -> EU (eu.api.complyedge.io, every new account), `ce_` -> US\n// (api.complyedge.io, the dormant stack; older keys only). No key or an\n// unknown prefix goes to EU: US creates no new tenants (2026-09-25).\nexport type Region = \"us\" | \"eu\";\n\nexport const REGION_BASE_URLS: Record<Region, string> = {\n us: \"https://api.complyedge.io\",\n eu: \"https://eu.api.complyedge.io\",\n};\n\n/** Region an API key was issued in, read from its prefix; undefined if unknown. */\nexport function regionFromApiKey(apiKey?: string): Region | undefined {\n if (!apiKey) return undefined;\n // Longest prefix first: `ce_eu_` also starts with `ce_`.\n if (apiKey.startsWith(\"ce_eu_\")) return \"eu\";\n if (apiKey.startsWith(\"ce_\")) return \"us\";\n return undefined;\n}\n\n/**\n * Pick the API host. Precedence, highest first:\n * 1. `baseUrl` option\n * 2. `COMPLYEDGE_API_URL` environment variable\n * 3. `region` option, else `COMPLYEDGE_REGION` environment variable\n * 4. the region encoded in the API key prefix (`ce_eu_` -> EU, `ce_` -> US)\n * 5. EU\n */\nexport function resolveBaseUrl(opts: {\n apiKey?: string;\n baseUrl?: string;\n region?: Region;\n}): string {\n if (opts.baseUrl) return opts.baseUrl.replace(/\\/+$/, \"\");\n const envUrl = process.env.COMPLYEDGE_API_URL;\n if (envUrl) return envUrl.replace(/\\/+$/, \"\");\n const chosen = (opts.region || process.env.COMPLYEDGE_REGION || \"\").trim().toLowerCase();\n if (chosen) {\n if (!(chosen in REGION_BASE_URLS)) {\n throw new Error(\n `Unknown ComplyEdge region \"${chosen}\"; expected one of ${Object.keys(REGION_BASE_URLS).join(\", \")}`\n );\n }\n return REGION_BASE_URLS[chosen as Region];\n }\n return REGION_BASE_URLS[regionFromApiKey(opts.apiKey) || \"eu\"];\n}\n// Keep in sync with package.json. Hardcoding it here meant the User-Agent\n// silently reported a stale version after every release bump, which is the\n// one field support uses to tell which client a customer is actually on.\nconst SDK_VERSION = \"0.2.5\";\n\nexport class ComplyEdgeClient {\n private http: AxiosInstance;\n private agentId: string;\n private jurisdiction?: string;\n\n constructor(config: ComplyEdgeConfig) {\n const baseUrl = resolveBaseUrl({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n region: config.region,\n });\n\n this.agentId = config.agentId || \"default\";\n this.jurisdiction = config.jurisdiction;\n\n this.http = axios.create({\n baseURL: baseUrl,\n timeout: config.timeout || 30_000,\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `complyedge-typescript-sdk/${SDK_VERSION}`,\n },\n });\n }\n\n /**\n * Run a compliance check on text input.\n *\n * Calls POST /v1/check: the deterministic OPA hot path. A decision here is\n * evaluated against the Rego rule bundle, returns article-cited violations,\n * and is written to the Article 12 audit trail (`auditLogged`).\n *\n * With `context.sandbox: true` it calls POST /v1/sandbox/check instead:\n * same verdict, nothing recorded (`auditLogged` false, `sandbox` true).\n */\n async check(text: string, context?: ComplianceContext): Promise<ComplianceResult> {\n const start = Date.now();\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n const path = context?.sandbox ? \"/v1/sandbox/check\" : \"/v1/check\";\n\n const response = await this.http.post(path, {\n text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n // Pass the caller's direction through untouched. This previously mapped\n // \"prompt\" to \"input\", a value the API's DirectionType enum does not\n // accept (\"prompt\" | \"output\"), so every input check 422'd and the\n // wrapped call threw. The SDK's own ComplianceContext type has always\n // declared the correct union; only this line disagreed with it.\n direction: context?.direction ?? \"output\",\n use_semantic_fallback: false,\n context: context?.userRole ? { user_role: context.userRole } : undefined,\n });\n\n const data = response.data;\n const processingTimeMs = Date.now() - start;\n // Missing `allowed` is a block. `!== false` treated a truncated 200 as a pass.\n const allowed = data.allowed === true;\n\n return {\n eventId: data.event_id || \"\",\n allowed,\n status: allowed ? \"safe\" : \"violation\",\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: (v.rule_id as string) || \"\",\n ruleDescription: (v.rule_description as string) || \"\",\n severity: (v.severity as ComplianceViolation[\"severity\"]) || \"medium\",\n reason: (v.reason as string) || \"\",\n confidence: (v.confidence as number) ?? 1.0,\n textExcerpt: v.text_excerpt as string | undefined,\n })),\n latencyMs: data.latency_ms || 0,\n bundleVersion: data.bundle_version || \"\",\n evaluatedRules: data.evaluated_rules || [],\n enginePath: data.engine_path || \"opa\",\n opaLatencyMs: data.opa_latency_ms,\n auditLogged: data.audit_logged === true,\n sandbox: data.sandbox === true,\n textHash: data.text_hash || \"\",\n timestamp: data.timestamp,\n jurisdiction,\n processingTimeMs,\n };\n }\n\n /**\n * Run proactive sensitivity detection on user input.\n *\n * Calls POST /v1/sensitivity/detect, which deliberately stays on the legacy\n * TrustLint + LLM pipeline. It does NOT run OPA and does NOT write the\n * Article 12 audit trail. Use `check()` for runtime EU AI Act enforcement.\n */\n async detectSensitivity(\n text: string,\n context?: ComplianceContext\n ): Promise<SensitivityResult> {\n const start = Date.now();\n // \"EU\" matches the Python SDK and the server default. This read \"US\",\n // so the two SDKs enforced different rule sets for identical code.\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n\n const response = await this.http.post(\"/v1/sensitivity/detect\", {\n input_text: text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n direction: context?.direction || \"prompt\",\n user_role: context?.userRole,\n });\n\n const data = response.data;\n\n return {\n eventId: data.event_id || \"\",\n status: data.overall_risk_assessment === \"safe\" ? \"safe\" : \"violation\",\n detections: (data.detections || []).map((d: Record<string, unknown>) => ({\n ruleId: (d.rule_id as string) || \"\",\n severity: (d.severity as SensitivityDetection[\"severity\"]) || \"medium\",\n regulation: (d.regulation as string) || \"\",\n description: (d.description as string) || \"\",\n article: d.article as string | undefined,\n remediation: d.remediation as string | undefined,\n })),\n riskScore: data.overall_risk_score || 0,\n jurisdiction,\n processingTimeMs: Date.now() - start,\n };\n }\n\n /**\n * Run a pre-deployment assessment on an AI system configuration.\n */\n async assessPreDeployment(input: PreDeploymentInput): Promise<PreDeploymentResult> {\n const response = await this.http.post(\"/v1/assessment/pre-deployment\", {\n system_prompt: input.systemPrompt,\n model_config: input.modelConfig\n ? {\n provider: input.modelConfig.provider,\n model_id: input.modelConfig.modelId,\n temperature: input.modelConfig.temperature,\n }\n : undefined,\n agent_pipeline: input.agentPipeline\n ? {\n tools: input.agentPipeline.tools,\n memory: input.agentPipeline.memory,\n autonomy_level: input.agentPipeline.autonomyLevel,\n human_oversight: input.agentPipeline.humanOversight,\n }\n : undefined,\n jurisdiction: input.jurisdiction || \"EU\",\n });\n\n const data = response.data;\n return {\n complianceScore: data.compliance_score,\n riskTier: data.risk_tier,\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: v.rule_id as string,\n article: v.article as string,\n description: v.description as string,\n requiredAction: v.required_action as string,\n })),\n requiredDisclosures: data.required_disclosures || [],\n euAiActCategory: data.eu_ai_act_category || \"\",\n estimatedDeadline: data.estimated_deadline || \"\",\n };\n }\n}\n","/**\n * ComplyEdge OpenAI Middleware\n *\n * Wraps an OpenAI client to run compliance checks on messages\n * before they are sent to the model.\n *\n * Usage:\n * import OpenAI from \"openai\";\n * import { ComplyEdgeClient } from \"@complyedge/sdk\";\n * import { withCompliance } from \"@complyedge/sdk/openai-middleware\";\n *\n * const ce = new ComplyEdgeClient({ apiKey: \"...\" });\n * const openai = withCompliance(new OpenAI(), ce);\n * // Now openai.chat.completions.create() runs compliance checks automatically\n */\n\nimport type { ComplyEdgeClient } from \"./client\";\nimport type { ComplianceViolation } from \"./types\";\n\nexport class ComplianceError extends Error {\n public violations: ComplianceViolation[];\n\n constructor(message: string, violations: ComplianceViolation[]) {\n super(message);\n this.name = \"ComplianceError\";\n this.violations = violations;\n }\n}\n\n/**\n * Wrap an OpenAI client with ComplyEdge compliance checks.\n * The returned object proxies chat.completions.create() to check\n * user messages before sending to OpenAI.\n */\nexport function withCompliance<T extends Record<string, unknown>>(\n openaiClient: T,\n ceClient: ComplyEdgeClient,\n options?: {\n checkInput?: boolean;\n checkOutput?: boolean;\n jurisdiction?: string;\n blockOnViolation?: boolean;\n }\n): T {\n const opts = {\n checkInput: true,\n checkOutput: false,\n blockOnViolation: true,\n ...options,\n };\n\n const chat = openaiClient.chat as Record<string, unknown> | undefined;\n if (!chat || !chat.completions) {\n return openaiClient; // Not an OpenAI-shaped client, return as-is\n }\n\n const completions = chat.completions as Record<string, unknown>;\n const originalCreate = completions.create as (...args: unknown[]) => Promise<unknown>;\n\n if (typeof originalCreate !== \"function\") {\n return openaiClient;\n }\n\n completions.create = async function (...args: unknown[]) {\n const params = args[0] as Record<string, unknown> | undefined;\n\n if (opts.checkInput && params?.messages) {\n const messages = params.messages as Array<{ role: string; content: unknown }>;\n // Check EVERY user turn, not just the last one. A violation in an earlier\n // message is still sent to the model verbatim, so checking only the tail\n // left the majority of a multi-turn conversation unenforced.\n for (const message of messages) {\n if (message.role !== \"user\") continue;\n for (const text of extractText(message.content)) {\n const result = await ceClient.check(text, {\n direction: \"prompt\",\n jurisdiction: opts.jurisdiction,\n });\n if (!result.allowed && opts.blockOnViolation) {\n throw new ComplianceError(\n `Compliance violation detected: ${result.violations.map((v) => v.ruleId).join(\", \")}`,\n result.violations\n );\n }\n }\n }\n }\n\n const response = await originalCreate.apply(this, args);\n\n if (opts.checkOutput) {\n // Previously declared in the options type and never read, so the README's\n // \"runs compliance checks automatically\" was half true: inputs were\n // checked, model output never was, silently.\n for (const text of extractCompletionText(response)) {\n const result = await ceClient.check(text, {\n direction: \"output\",\n jurisdiction: opts.jurisdiction,\n });\n if (!result.allowed && opts.blockOnViolation) {\n throw new ComplianceError(\n `Compliance violation in model output: ${result.violations.map((v) => v.ruleId).join(\", \")}`,\n result.violations\n );\n }\n }\n }\n\n return response;\n };\n\n return openaiClient;\n}\n\n/**\n * Pull checkable strings out of a message `content` field.\n *\n * OpenAI accepts either a plain string or an array of typed content parts.\n * The array form was previously passed to the API as a non-string and 422'd,\n * so multimodal callers could not use the middleware at all.\n */\nfunction extractText(content: unknown): string[] {\n if (typeof content === \"string\") {\n return content.trim() ? [content] : [];\n }\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (typeof part === \"string\") return part;\n const p = part as Record<string, unknown> | null;\n return p && typeof p.text === \"string\" ? p.text : \"\";\n })\n .filter((t) => t.trim().length > 0);\n }\n return [];\n}\n\n/** Pull assistant message text out of a chat completion response. */\nfunction extractCompletionText(response: unknown): string[] {\n const r = response as { choices?: Array<{ message?: { content?: unknown } }> } | null;\n if (!r?.choices) return [];\n return r.choices.flatMap((choice) => extractText(choice?.message?.content));\n}\n"],"mappings":"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","names":["axios"]}
|
package/dist/index.mjs
CHANGED
|
@@ -5,18 +5,46 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/client.ts
|
|
7
7
|
import axios from "axios";
|
|
8
|
-
var
|
|
9
|
-
|
|
8
|
+
var REGION_BASE_URLS = {
|
|
9
|
+
us: "https://api.complyedge.io",
|
|
10
|
+
eu: "https://eu.api.complyedge.io"
|
|
11
|
+
};
|
|
12
|
+
function regionFromApiKey(apiKey) {
|
|
13
|
+
if (!apiKey) return void 0;
|
|
14
|
+
if (apiKey.startsWith("ce_eu_")) return "eu";
|
|
15
|
+
if (apiKey.startsWith("ce_")) return "us";
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
function resolveBaseUrl(opts) {
|
|
19
|
+
if (opts.baseUrl) return opts.baseUrl.replace(/\/+$/, "");
|
|
20
|
+
const envUrl = process.env.COMPLYEDGE_API_URL;
|
|
21
|
+
if (envUrl) return envUrl.replace(/\/+$/, "");
|
|
22
|
+
const chosen = (opts.region || process.env.COMPLYEDGE_REGION || "").trim().toLowerCase();
|
|
23
|
+
if (chosen) {
|
|
24
|
+
if (!(chosen in REGION_BASE_URLS)) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Unknown ComplyEdge region "${chosen}"; expected one of ${Object.keys(REGION_BASE_URLS).join(", ")}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return REGION_BASE_URLS[chosen];
|
|
30
|
+
}
|
|
31
|
+
return REGION_BASE_URLS[regionFromApiKey(opts.apiKey) || "eu"];
|
|
32
|
+
}
|
|
33
|
+
var SDK_VERSION = "0.2.5";
|
|
10
34
|
var ComplyEdgeClient = class {
|
|
11
35
|
http;
|
|
12
36
|
agentId;
|
|
13
37
|
jurisdiction;
|
|
14
38
|
constructor(config) {
|
|
15
|
-
const baseUrl =
|
|
39
|
+
const baseUrl = resolveBaseUrl({
|
|
40
|
+
apiKey: config.apiKey,
|
|
41
|
+
baseUrl: config.baseUrl,
|
|
42
|
+
region: config.region
|
|
43
|
+
});
|
|
16
44
|
this.agentId = config.agentId || "default";
|
|
17
45
|
this.jurisdiction = config.jurisdiction;
|
|
18
46
|
this.http = axios.create({
|
|
19
|
-
baseURL: baseUrl
|
|
47
|
+
baseURL: baseUrl,
|
|
20
48
|
timeout: config.timeout || 3e4,
|
|
21
49
|
headers: {
|
|
22
50
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -31,11 +59,15 @@ var ComplyEdgeClient = class {
|
|
|
31
59
|
* Calls POST /v1/check: the deterministic OPA hot path. A decision here is
|
|
32
60
|
* evaluated against the Rego rule bundle, returns article-cited violations,
|
|
33
61
|
* and is written to the Article 12 audit trail (`auditLogged`).
|
|
62
|
+
*
|
|
63
|
+
* With `context.sandbox: true` it calls POST /v1/sandbox/check instead:
|
|
64
|
+
* same verdict, nothing recorded (`auditLogged` false, `sandbox` true).
|
|
34
65
|
*/
|
|
35
66
|
async check(text, context) {
|
|
36
67
|
const start = Date.now();
|
|
37
68
|
const jurisdiction = context?.jurisdiction || this.jurisdiction || "EU";
|
|
38
|
-
const
|
|
69
|
+
const path = context?.sandbox ? "/v1/sandbox/check" : "/v1/check";
|
|
70
|
+
const response = await this.http.post(path, {
|
|
39
71
|
text,
|
|
40
72
|
agent_id: context?.agentId || this.agentId,
|
|
41
73
|
jurisdiction,
|
|
@@ -50,7 +82,7 @@ var ComplyEdgeClient = class {
|
|
|
50
82
|
});
|
|
51
83
|
const data = response.data;
|
|
52
84
|
const processingTimeMs = Date.now() - start;
|
|
53
|
-
const allowed = data.allowed
|
|
85
|
+
const allowed = data.allowed === true;
|
|
54
86
|
return {
|
|
55
87
|
eventId: data.event_id || "",
|
|
56
88
|
allowed,
|
|
@@ -68,7 +100,8 @@ var ComplyEdgeClient = class {
|
|
|
68
100
|
evaluatedRules: data.evaluated_rules || [],
|
|
69
101
|
enginePath: data.engine_path || "opa",
|
|
70
102
|
opaLatencyMs: data.opa_latency_ms,
|
|
71
|
-
auditLogged: data.audit_logged
|
|
103
|
+
auditLogged: data.audit_logged === true,
|
|
104
|
+
sandbox: data.sandbox === true,
|
|
72
105
|
textHash: data.text_hash || "",
|
|
73
106
|
timestamp: data.timestamp,
|
|
74
107
|
jurisdiction,
|
|
@@ -147,6 +180,9 @@ var ComplyEdgeClient = class {
|
|
|
147
180
|
export {
|
|
148
181
|
ComplianceError,
|
|
149
182
|
ComplyEdgeClient,
|
|
183
|
+
REGION_BASE_URLS,
|
|
184
|
+
regionFromApiKey,
|
|
185
|
+
resolveBaseUrl,
|
|
150
186
|
withCompliance
|
|
151
187
|
};
|
|
152
188
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * ComplyEdge TypeScript SDK — Client\n */\n\nimport axios, { AxiosInstance } from \"axios\";\nimport type {\n ComplyEdgeConfig,\n ComplianceContext,\n ComplianceResult,\n ComplianceViolation,\n SensitivityDetection,\n SensitivityResult,\n PreDeploymentInput,\n PreDeploymentResult,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://api.complyedge.io\";\n// Keep in sync with package.json. Hardcoding it here meant the User-Agent\n// silently reported a stale version after every release bump, which is the\n// one field support uses to tell which client a customer is actually on.\nconst SDK_VERSION = \"0.2.0\";\n\nexport class ComplyEdgeClient {\n private http: AxiosInstance;\n private agentId: string;\n private jurisdiction?: string;\n\n constructor(config: ComplyEdgeConfig) {\n const baseUrl = config.baseUrl || process.env.COMPLYEDGE_API_URL || DEFAULT_BASE_URL;\n\n this.agentId = config.agentId || \"default\";\n this.jurisdiction = config.jurisdiction;\n\n this.http = axios.create({\n baseURL: baseUrl.replace(/\\/+$/, \"\"),\n timeout: config.timeout || 30_000,\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `complyedge-typescript-sdk/${SDK_VERSION}`,\n },\n });\n }\n\n /**\n * Run a compliance check on text input.\n *\n * Calls POST /v1/check: the deterministic OPA hot path. A decision here is\n * evaluated against the Rego rule bundle, returns article-cited violations,\n * and is written to the Article 12 audit trail (`auditLogged`).\n */\n async check(text: string, context?: ComplianceContext): Promise<ComplianceResult> {\n const start = Date.now();\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n\n const response = await this.http.post(\"/v1/check\", {\n text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n // Pass the caller's direction through untouched. This previously mapped\n // \"prompt\" to \"input\", a value the API's DirectionType enum does not\n // accept (\"prompt\" | \"output\"), so every input check 422'd and the\n // wrapped call threw. The SDK's own ComplianceContext type has always\n // declared the correct union; only this line disagreed with it.\n direction: context?.direction ?? \"output\",\n use_semantic_fallback: false,\n context: context?.userRole ? { user_role: context.userRole } : undefined,\n });\n\n const data = response.data;\n const processingTimeMs = Date.now() - start;\n const allowed = data.allowed !== false;\n\n return {\n eventId: data.event_id || \"\",\n allowed,\n status: allowed ? \"safe\" : \"violation\",\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: (v.rule_id as string) || \"\",\n ruleDescription: (v.rule_description as string) || \"\",\n severity: (v.severity as ComplianceViolation[\"severity\"]) || \"medium\",\n reason: (v.reason as string) || \"\",\n confidence: (v.confidence as number) ?? 1.0,\n textExcerpt: v.text_excerpt as string | undefined,\n })),\n latencyMs: data.latency_ms || 0,\n bundleVersion: data.bundle_version || \"\",\n evaluatedRules: data.evaluated_rules || [],\n enginePath: data.engine_path || \"opa\",\n opaLatencyMs: data.opa_latency_ms,\n auditLogged: data.audit_logged !== false,\n textHash: data.text_hash || \"\",\n timestamp: data.timestamp,\n jurisdiction,\n processingTimeMs,\n };\n }\n\n /**\n * Run proactive sensitivity detection on user input.\n *\n * Calls POST /v1/sensitivity/detect, which deliberately stays on the legacy\n * TrustLint + LLM pipeline. It does NOT run OPA and does NOT write the\n * Article 12 audit trail. Use `check()` for runtime EU AI Act enforcement.\n */\n async detectSensitivity(\n text: string,\n context?: ComplianceContext\n ): Promise<SensitivityResult> {\n const start = Date.now();\n // \"EU\" matches the Python SDK and the server default. This read \"US\",\n // so the two SDKs enforced different rule sets for identical code.\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n\n const response = await this.http.post(\"/v1/sensitivity/detect\", {\n input_text: text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n direction: context?.direction || \"prompt\",\n user_role: context?.userRole,\n });\n\n const data = response.data;\n\n return {\n eventId: data.event_id || \"\",\n status: data.overall_risk_assessment === \"safe\" ? \"safe\" : \"violation\",\n detections: (data.detections || []).map((d: Record<string, unknown>) => ({\n ruleId: (d.rule_id as string) || \"\",\n severity: (d.severity as SensitivityDetection[\"severity\"]) || \"medium\",\n regulation: (d.regulation as string) || \"\",\n description: (d.description as string) || \"\",\n article: d.article as string | undefined,\n remediation: d.remediation as string | undefined,\n })),\n riskScore: data.overall_risk_score || 0,\n jurisdiction,\n processingTimeMs: Date.now() - start,\n };\n }\n\n /**\n * Run a pre-deployment assessment on an AI system configuration.\n */\n async assessPreDeployment(input: PreDeploymentInput): Promise<PreDeploymentResult> {\n const response = await this.http.post(\"/v1/assessment/pre-deployment\", {\n system_prompt: input.systemPrompt,\n model_config: input.modelConfig\n ? {\n provider: input.modelConfig.provider,\n model_id: input.modelConfig.modelId,\n temperature: input.modelConfig.temperature,\n }\n : undefined,\n agent_pipeline: input.agentPipeline\n ? {\n tools: input.agentPipeline.tools,\n memory: input.agentPipeline.memory,\n autonomy_level: input.agentPipeline.autonomyLevel,\n human_oversight: input.agentPipeline.humanOversight,\n }\n : undefined,\n jurisdiction: input.jurisdiction || \"EU\",\n });\n\n const data = response.data;\n return {\n complianceScore: data.compliance_score,\n riskTier: data.risk_tier,\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: v.rule_id as string,\n article: v.article as string,\n description: v.description as string,\n requiredAction: v.required_action as string,\n })),\n requiredDisclosures: data.required_disclosures || [],\n euAiActCategory: data.eu_ai_act_category || \"\",\n estimatedDeadline: data.estimated_deadline || \"\",\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * ComplyEdge TypeScript SDK — Client\n */\n\nimport axios, { AxiosInstance } from \"axios\";\nimport type {\n ComplyEdgeConfig,\n ComplianceContext,\n ComplianceResult,\n ComplianceViolation,\n SensitivityDetection,\n SensitivityResult,\n PreDeploymentInput,\n PreDeploymentResult,\n} from \"./types\";\n\n// ComplyEdge runs one independent stack per region. A tenant lives in exactly\n// one of them, and its API keys are only valid there. The key itself says\n// which: `ce_eu_` -> EU (eu.api.complyedge.io, every new account), `ce_` -> US\n// (api.complyedge.io, the dormant stack; older keys only). No key or an\n// unknown prefix goes to EU: US creates no new tenants (2026-09-25).\nexport type Region = \"us\" | \"eu\";\n\nexport const REGION_BASE_URLS: Record<Region, string> = {\n us: \"https://api.complyedge.io\",\n eu: \"https://eu.api.complyedge.io\",\n};\n\n/** Region an API key was issued in, read from its prefix; undefined if unknown. */\nexport function regionFromApiKey(apiKey?: string): Region | undefined {\n if (!apiKey) return undefined;\n // Longest prefix first: `ce_eu_` also starts with `ce_`.\n if (apiKey.startsWith(\"ce_eu_\")) return \"eu\";\n if (apiKey.startsWith(\"ce_\")) return \"us\";\n return undefined;\n}\n\n/**\n * Pick the API host. Precedence, highest first:\n * 1. `baseUrl` option\n * 2. `COMPLYEDGE_API_URL` environment variable\n * 3. `region` option, else `COMPLYEDGE_REGION` environment variable\n * 4. the region encoded in the API key prefix (`ce_eu_` -> EU, `ce_` -> US)\n * 5. EU\n */\nexport function resolveBaseUrl(opts: {\n apiKey?: string;\n baseUrl?: string;\n region?: Region;\n}): string {\n if (opts.baseUrl) return opts.baseUrl.replace(/\\/+$/, \"\");\n const envUrl = process.env.COMPLYEDGE_API_URL;\n if (envUrl) return envUrl.replace(/\\/+$/, \"\");\n const chosen = (opts.region || process.env.COMPLYEDGE_REGION || \"\").trim().toLowerCase();\n if (chosen) {\n if (!(chosen in REGION_BASE_URLS)) {\n throw new Error(\n `Unknown ComplyEdge region \"${chosen}\"; expected one of ${Object.keys(REGION_BASE_URLS).join(\", \")}`\n );\n }\n return REGION_BASE_URLS[chosen as Region];\n }\n return REGION_BASE_URLS[regionFromApiKey(opts.apiKey) || \"eu\"];\n}\n// Keep in sync with package.json. Hardcoding it here meant the User-Agent\n// silently reported a stale version after every release bump, which is the\n// one field support uses to tell which client a customer is actually on.\nconst SDK_VERSION = \"0.2.5\";\n\nexport class ComplyEdgeClient {\n private http: AxiosInstance;\n private agentId: string;\n private jurisdiction?: string;\n\n constructor(config: ComplyEdgeConfig) {\n const baseUrl = resolveBaseUrl({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n region: config.region,\n });\n\n this.agentId = config.agentId || \"default\";\n this.jurisdiction = config.jurisdiction;\n\n this.http = axios.create({\n baseURL: baseUrl,\n timeout: config.timeout || 30_000,\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `complyedge-typescript-sdk/${SDK_VERSION}`,\n },\n });\n }\n\n /**\n * Run a compliance check on text input.\n *\n * Calls POST /v1/check: the deterministic OPA hot path. A decision here is\n * evaluated against the Rego rule bundle, returns article-cited violations,\n * and is written to the Article 12 audit trail (`auditLogged`).\n *\n * With `context.sandbox: true` it calls POST /v1/sandbox/check instead:\n * same verdict, nothing recorded (`auditLogged` false, `sandbox` true).\n */\n async check(text: string, context?: ComplianceContext): Promise<ComplianceResult> {\n const start = Date.now();\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n const path = context?.sandbox ? \"/v1/sandbox/check\" : \"/v1/check\";\n\n const response = await this.http.post(path, {\n text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n // Pass the caller's direction through untouched. This previously mapped\n // \"prompt\" to \"input\", a value the API's DirectionType enum does not\n // accept (\"prompt\" | \"output\"), so every input check 422'd and the\n // wrapped call threw. The SDK's own ComplianceContext type has always\n // declared the correct union; only this line disagreed with it.\n direction: context?.direction ?? \"output\",\n use_semantic_fallback: false,\n context: context?.userRole ? { user_role: context.userRole } : undefined,\n });\n\n const data = response.data;\n const processingTimeMs = Date.now() - start;\n // Missing `allowed` is a block. `!== false` treated a truncated 200 as a pass.\n const allowed = data.allowed === true;\n\n return {\n eventId: data.event_id || \"\",\n allowed,\n status: allowed ? \"safe\" : \"violation\",\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: (v.rule_id as string) || \"\",\n ruleDescription: (v.rule_description as string) || \"\",\n severity: (v.severity as ComplianceViolation[\"severity\"]) || \"medium\",\n reason: (v.reason as string) || \"\",\n confidence: (v.confidence as number) ?? 1.0,\n textExcerpt: v.text_excerpt as string | undefined,\n })),\n latencyMs: data.latency_ms || 0,\n bundleVersion: data.bundle_version || \"\",\n evaluatedRules: data.evaluated_rules || [],\n enginePath: data.engine_path || \"opa\",\n opaLatencyMs: data.opa_latency_ms,\n auditLogged: data.audit_logged === true,\n sandbox: data.sandbox === true,\n textHash: data.text_hash || \"\",\n timestamp: data.timestamp,\n jurisdiction,\n processingTimeMs,\n };\n }\n\n /**\n * Run proactive sensitivity detection on user input.\n *\n * Calls POST /v1/sensitivity/detect, which deliberately stays on the legacy\n * TrustLint + LLM pipeline. It does NOT run OPA and does NOT write the\n * Article 12 audit trail. Use `check()` for runtime EU AI Act enforcement.\n */\n async detectSensitivity(\n text: string,\n context?: ComplianceContext\n ): Promise<SensitivityResult> {\n const start = Date.now();\n // \"EU\" matches the Python SDK and the server default. This read \"US\",\n // so the two SDKs enforced different rule sets for identical code.\n const jurisdiction = context?.jurisdiction || this.jurisdiction || \"EU\";\n\n const response = await this.http.post(\"/v1/sensitivity/detect\", {\n input_text: text,\n agent_id: context?.agentId || this.agentId,\n jurisdiction,\n direction: context?.direction || \"prompt\",\n user_role: context?.userRole,\n });\n\n const data = response.data;\n\n return {\n eventId: data.event_id || \"\",\n status: data.overall_risk_assessment === \"safe\" ? \"safe\" : \"violation\",\n detections: (data.detections || []).map((d: Record<string, unknown>) => ({\n ruleId: (d.rule_id as string) || \"\",\n severity: (d.severity as SensitivityDetection[\"severity\"]) || \"medium\",\n regulation: (d.regulation as string) || \"\",\n description: (d.description as string) || \"\",\n article: d.article as string | undefined,\n remediation: d.remediation as string | undefined,\n })),\n riskScore: data.overall_risk_score || 0,\n jurisdiction,\n processingTimeMs: Date.now() - start,\n };\n }\n\n /**\n * Run a pre-deployment assessment on an AI system configuration.\n */\n async assessPreDeployment(input: PreDeploymentInput): Promise<PreDeploymentResult> {\n const response = await this.http.post(\"/v1/assessment/pre-deployment\", {\n system_prompt: input.systemPrompt,\n model_config: input.modelConfig\n ? {\n provider: input.modelConfig.provider,\n model_id: input.modelConfig.modelId,\n temperature: input.modelConfig.temperature,\n }\n : undefined,\n agent_pipeline: input.agentPipeline\n ? {\n tools: input.agentPipeline.tools,\n memory: input.agentPipeline.memory,\n autonomy_level: input.agentPipeline.autonomyLevel,\n human_oversight: input.agentPipeline.humanOversight,\n }\n : undefined,\n jurisdiction: input.jurisdiction || \"EU\",\n });\n\n const data = response.data;\n return {\n complianceScore: data.compliance_score,\n riskTier: data.risk_tier,\n violations: (data.violations || []).map((v: Record<string, unknown>) => ({\n ruleId: v.rule_id as string,\n article: v.article as string,\n description: v.description as string,\n requiredAction: v.required_action as string,\n })),\n requiredDisclosures: data.required_disclosures || [],\n euAiActCategory: data.eu_ai_act_category || \"\",\n estimatedDeadline: data.estimated_deadline || \"\",\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -7,6 +7,12 @@ interface ComplianceContext {
|
|
|
7
7
|
jurisdiction?: string;
|
|
8
8
|
userRole?: string;
|
|
9
9
|
direction?: "prompt" | "output";
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate without recording: POST /v1/sandbox/check. Same rules, same
|
|
12
|
+
* tenant settings, same verdict as /v1/check; no audit entry, no usage,
|
|
13
|
+
* no rate-limit count. For trying cases, never for production traffic.
|
|
14
|
+
*/
|
|
15
|
+
sandbox?: boolean;
|
|
10
16
|
}
|
|
11
17
|
type SeverityLevel = "critical" | "high" | "medium" | "low" | "informational";
|
|
12
18
|
interface ComplianceViolation {
|
|
@@ -33,6 +39,8 @@ interface ComplianceResult {
|
|
|
33
39
|
enginePath: string;
|
|
34
40
|
opaLatencyMs?: number;
|
|
35
41
|
auditLogged: boolean;
|
|
42
|
+
/** True when the decision came from /v1/sandbox/check. Never evidence. */
|
|
43
|
+
sandbox: boolean;
|
|
36
44
|
/**
|
|
37
45
|
* SHA-256 of the evaluated text as a bare hex digest, byte-identical to this
|
|
38
46
|
* event's Article 12 audit entry. Binds the decision to its exact input
|
|
@@ -95,7 +103,13 @@ interface PreDeploymentResult {
|
|
|
95
103
|
}
|
|
96
104
|
interface ComplyEdgeConfig {
|
|
97
105
|
apiKey: string;
|
|
106
|
+
/** Overrides region and key inference. Also settable via COMPLYEDGE_API_URL. */
|
|
98
107
|
baseUrl?: string;
|
|
108
|
+
/**
|
|
109
|
+
* "us" | "eu". Defaults to the region encoded in the API key prefix
|
|
110
|
+
* (`ce_eu_` -> EU), else US. Also settable via COMPLYEDGE_REGION.
|
|
111
|
+
*/
|
|
112
|
+
region?: "us" | "eu";
|
|
99
113
|
agentId?: string;
|
|
100
114
|
jurisdiction?: string;
|
|
101
115
|
timeout?: number;
|
|
@@ -105,6 +119,23 @@ interface ComplyEdgeConfig {
|
|
|
105
119
|
* ComplyEdge TypeScript SDK — Client
|
|
106
120
|
*/
|
|
107
121
|
|
|
122
|
+
type Region = "us" | "eu";
|
|
123
|
+
declare const REGION_BASE_URLS: Record<Region, string>;
|
|
124
|
+
/** Region an API key was issued in, read from its prefix; undefined if unknown. */
|
|
125
|
+
declare function regionFromApiKey(apiKey?: string): Region | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Pick the API host. Precedence, highest first:
|
|
128
|
+
* 1. `baseUrl` option
|
|
129
|
+
* 2. `COMPLYEDGE_API_URL` environment variable
|
|
130
|
+
* 3. `region` option, else `COMPLYEDGE_REGION` environment variable
|
|
131
|
+
* 4. the region encoded in the API key prefix (`ce_eu_` -> EU, `ce_` -> US)
|
|
132
|
+
* 5. EU
|
|
133
|
+
*/
|
|
134
|
+
declare function resolveBaseUrl(opts: {
|
|
135
|
+
apiKey?: string;
|
|
136
|
+
baseUrl?: string;
|
|
137
|
+
region?: Region;
|
|
138
|
+
}): string;
|
|
108
139
|
declare class ComplyEdgeClient {
|
|
109
140
|
private http;
|
|
110
141
|
private agentId;
|
|
@@ -116,6 +147,9 @@ declare class ComplyEdgeClient {
|
|
|
116
147
|
* Calls POST /v1/check: the deterministic OPA hot path. A decision here is
|
|
117
148
|
* evaluated against the Rego rule bundle, returns article-cited violations,
|
|
118
149
|
* and is written to the Article 12 audit trail (`auditLogged`).
|
|
150
|
+
*
|
|
151
|
+
* With `context.sandbox: true` it calls POST /v1/sandbox/check instead:
|
|
152
|
+
* same verdict, nothing recorded (`auditLogged` false, `sandbox` true).
|
|
119
153
|
*/
|
|
120
154
|
check(text: string, context?: ComplianceContext): Promise<ComplianceResult>;
|
|
121
155
|
/**
|
|
@@ -164,4 +198,4 @@ declare function withCompliance<T extends Record<string, unknown>>(openaiClient:
|
|
|
164
198
|
blockOnViolation?: boolean;
|
|
165
199
|
}): T;
|
|
166
200
|
|
|
167
|
-
export { ComplyEdgeClient as C, ComplianceError, type PreDeploymentInput as P, type SeverityLevel as S, type ComplyEdgeConfig as
|
|
201
|
+
export { ComplyEdgeClient as C, ComplianceError, type PreDeploymentInput as P, REGION_BASE_URLS as R, type SeverityLevel as S, resolveBaseUrl as a, type Region as b, type ComplyEdgeConfig as c, type ComplianceContext as d, type ComplianceResult as e, type ComplianceViolation as f, type SensitivityResult as g, type SensitivityDetection as h, type PreDeploymentResult as i, regionFromApiKey as r, withCompliance };
|
|
@@ -7,6 +7,12 @@ interface ComplianceContext {
|
|
|
7
7
|
jurisdiction?: string;
|
|
8
8
|
userRole?: string;
|
|
9
9
|
direction?: "prompt" | "output";
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate without recording: POST /v1/sandbox/check. Same rules, same
|
|
12
|
+
* tenant settings, same verdict as /v1/check; no audit entry, no usage,
|
|
13
|
+
* no rate-limit count. For trying cases, never for production traffic.
|
|
14
|
+
*/
|
|
15
|
+
sandbox?: boolean;
|
|
10
16
|
}
|
|
11
17
|
type SeverityLevel = "critical" | "high" | "medium" | "low" | "informational";
|
|
12
18
|
interface ComplianceViolation {
|
|
@@ -33,6 +39,8 @@ interface ComplianceResult {
|
|
|
33
39
|
enginePath: string;
|
|
34
40
|
opaLatencyMs?: number;
|
|
35
41
|
auditLogged: boolean;
|
|
42
|
+
/** True when the decision came from /v1/sandbox/check. Never evidence. */
|
|
43
|
+
sandbox: boolean;
|
|
36
44
|
/**
|
|
37
45
|
* SHA-256 of the evaluated text as a bare hex digest, byte-identical to this
|
|
38
46
|
* event's Article 12 audit entry. Binds the decision to its exact input
|
|
@@ -95,7 +103,13 @@ interface PreDeploymentResult {
|
|
|
95
103
|
}
|
|
96
104
|
interface ComplyEdgeConfig {
|
|
97
105
|
apiKey: string;
|
|
106
|
+
/** Overrides region and key inference. Also settable via COMPLYEDGE_API_URL. */
|
|
98
107
|
baseUrl?: string;
|
|
108
|
+
/**
|
|
109
|
+
* "us" | "eu". Defaults to the region encoded in the API key prefix
|
|
110
|
+
* (`ce_eu_` -> EU), else US. Also settable via COMPLYEDGE_REGION.
|
|
111
|
+
*/
|
|
112
|
+
region?: "us" | "eu";
|
|
99
113
|
agentId?: string;
|
|
100
114
|
jurisdiction?: string;
|
|
101
115
|
timeout?: number;
|
|
@@ -105,6 +119,23 @@ interface ComplyEdgeConfig {
|
|
|
105
119
|
* ComplyEdge TypeScript SDK — Client
|
|
106
120
|
*/
|
|
107
121
|
|
|
122
|
+
type Region = "us" | "eu";
|
|
123
|
+
declare const REGION_BASE_URLS: Record<Region, string>;
|
|
124
|
+
/** Region an API key was issued in, read from its prefix; undefined if unknown. */
|
|
125
|
+
declare function regionFromApiKey(apiKey?: string): Region | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Pick the API host. Precedence, highest first:
|
|
128
|
+
* 1. `baseUrl` option
|
|
129
|
+
* 2. `COMPLYEDGE_API_URL` environment variable
|
|
130
|
+
* 3. `region` option, else `COMPLYEDGE_REGION` environment variable
|
|
131
|
+
* 4. the region encoded in the API key prefix (`ce_eu_` -> EU, `ce_` -> US)
|
|
132
|
+
* 5. EU
|
|
133
|
+
*/
|
|
134
|
+
declare function resolveBaseUrl(opts: {
|
|
135
|
+
apiKey?: string;
|
|
136
|
+
baseUrl?: string;
|
|
137
|
+
region?: Region;
|
|
138
|
+
}): string;
|
|
108
139
|
declare class ComplyEdgeClient {
|
|
109
140
|
private http;
|
|
110
141
|
private agentId;
|
|
@@ -116,6 +147,9 @@ declare class ComplyEdgeClient {
|
|
|
116
147
|
* Calls POST /v1/check: the deterministic OPA hot path. A decision here is
|
|
117
148
|
* evaluated against the Rego rule bundle, returns article-cited violations,
|
|
118
149
|
* and is written to the Article 12 audit trail (`auditLogged`).
|
|
150
|
+
*
|
|
151
|
+
* With `context.sandbox: true` it calls POST /v1/sandbox/check instead:
|
|
152
|
+
* same verdict, nothing recorded (`auditLogged` false, `sandbox` true).
|
|
119
153
|
*/
|
|
120
154
|
check(text: string, context?: ComplianceContext): Promise<ComplianceResult>;
|
|
121
155
|
/**
|
|
@@ -164,4 +198,4 @@ declare function withCompliance<T extends Record<string, unknown>>(openaiClient:
|
|
|
164
198
|
blockOnViolation?: boolean;
|
|
165
199
|
}): T;
|
|
166
200
|
|
|
167
|
-
export { ComplyEdgeClient as C, ComplianceError, type PreDeploymentInput as P, type SeverityLevel as S, type ComplyEdgeConfig as
|
|
201
|
+
export { ComplyEdgeClient as C, ComplianceError, type PreDeploymentInput as P, REGION_BASE_URLS as R, type SeverityLevel as S, resolveBaseUrl as a, type Region as b, type ComplyEdgeConfig as c, type ComplianceContext as d, type ComplianceResult as e, type ComplianceViolation as f, type SensitivityResult as g, type SensitivityDetection as h, type PreDeploymentResult as i, regionFromApiKey as r, withCompliance };
|
package/package.json
CHANGED