@atbash/sdk 0.1.2
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 +28 -0
- package/README.md +296 -0
- package/dist/index.cjs +309 -0
- package/dist/index.d.cts +123 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +264 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Atbash CLI — Proprietary Software License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atbash,
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
19
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
|
|
26
|
+
The views and conclusions contained in the software and documentation are those
|
|
27
|
+
of the authors and should not be interpreted as representing official policies,
|
|
28
|
+
either expressed or implied, Atbash.
|
package/README.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# @atbash/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the Atbash judge and risk-engine APIs. Evaluate agent actions against operator-defined policy before execution — programmatically, from any Node.js backend.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @atbash/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18 or higher. Server-side only — private keys are sent in request bodies and must never be exposed to browsers.
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createAgent, judgeAction } from "@atbash/sdk";
|
|
17
|
+
|
|
18
|
+
// 1. Load your agent identity — paste the private key from the Atbash
|
|
19
|
+
// dashboard (https://atbash.ai/risk-engine/agents). createAgent()
|
|
20
|
+
// validates the key and derives the matching public key for you.
|
|
21
|
+
const agent = createAgent(process.env.ATBASH_AGENT_PRIVKEY!);
|
|
22
|
+
|
|
23
|
+
// 2. Submit an action for judgment, before executing it
|
|
24
|
+
const result = await judgeAction(
|
|
25
|
+
"Transfer $50,000 to external wallet 0xabc",
|
|
26
|
+
"Outbound AML check — new recipient, over threshold",
|
|
27
|
+
agent,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// 3. Enforce the verdict
|
|
31
|
+
switch (result.verdict) {
|
|
32
|
+
case "ALLOW":
|
|
33
|
+
// Proceed with the action
|
|
34
|
+
break;
|
|
35
|
+
case "HOLD":
|
|
36
|
+
// Held — operator must approve in the dashboard
|
|
37
|
+
console.log("Held for review:", result.tool_call_id);
|
|
38
|
+
break;
|
|
39
|
+
case "BLOCK":
|
|
40
|
+
// Refused — agent is jailed in Enforcement tier
|
|
41
|
+
throw new Error(`Blocked: ${result.reason}`);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Before this works, the agent must be onboarded at [atbash.ai](https://atbash.ai/) — assigned to an org, with a policy pack attached, and the org tier set to Audit+ or Enforcement.
|
|
46
|
+
|
|
47
|
+
### Don't have an agent yet?
|
|
48
|
+
|
|
49
|
+
Generate one programmatically for local development:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { generateKeyPair, createAgent } from "@atbash/sdk";
|
|
53
|
+
|
|
54
|
+
const { privKey } = generateKeyPair();
|
|
55
|
+
console.log("Save this private key somewhere safe:", privKey);
|
|
56
|
+
const agent = createAgent(privKey);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For production, always create agents in the dashboard so operators can attach policy packs and manage tier / jail state.
|
|
60
|
+
|
|
61
|
+
### Secret storage
|
|
62
|
+
|
|
63
|
+
The private key authenticates every call. Treat it like any other long-lived credential:
|
|
64
|
+
|
|
65
|
+
- Load it from an environment variable (`ATBASH_AGENT_PRIVKEY`) or a secret manager (AWS Secrets Manager, HashiCorp Vault, 1Password, etc.) — never hardcode it.
|
|
66
|
+
- Never commit `.env` files containing the key. Add them to `.gitignore`.
|
|
67
|
+
- If a key leaks, revoke the agent and create a new one in the [Atbash dashboard](https://atbash.ai/risk-engine/agents).
|
|
68
|
+
- The SDK is **server-side only** — the key must never ship to a browser bundle.
|
|
69
|
+
|
|
70
|
+
## Verdicts
|
|
71
|
+
|
|
72
|
+
Every `judgeAction` call returns one of three verdicts:
|
|
73
|
+
|
|
74
|
+
| Verdict | Meaning | What your code should do |
|
|
75
|
+
|---------|---------|-------------------------|
|
|
76
|
+
| `ALLOW` | Action is within policy | Proceed with execution |
|
|
77
|
+
| `HOLD` | Requires operator review | Pause — poll `getJudgmentStatus` until resolved |
|
|
78
|
+
| `BLOCK` | Violates a red line | Abort — agent is jailed in Enforcement tier |
|
|
79
|
+
|
|
80
|
+
## API
|
|
81
|
+
|
|
82
|
+
### Judge
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
judgeAction(
|
|
86
|
+
action: string,
|
|
87
|
+
context: string,
|
|
88
|
+
auth: AgentAuth,
|
|
89
|
+
opts?: JudgeOptions,
|
|
90
|
+
): Promise<JudgeResult>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Submit an action for judgment before execution. Returns the verdict, reason, confidence, provider, latency, and tool call ID.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
interface AgentAuth {
|
|
97
|
+
pubkey: string; // 66-char hex, secp256k1 compressed public key
|
|
98
|
+
privkey: string; // 64-char hex private key
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface JudgeOptions {
|
|
102
|
+
endpoint?: string; // API base URL (default: https://atbash.ai)
|
|
103
|
+
timeout?: number; // Request timeout in ms
|
|
104
|
+
provider?: string; // "atbash" | "openai" | "google" | "microsoft" | "custom"
|
|
105
|
+
apiKey?: string; // API key for non-atbash providers
|
|
106
|
+
providerEndpoint?: string; // Endpoint for microsoft/custom providers
|
|
107
|
+
model?: string; // Model override (e.g. "gpt-4o-mini")
|
|
108
|
+
toolName?: string; // Tool name for audit trail
|
|
109
|
+
toolArgsJson?: string; // Tool arguments JSON for audit trail
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface JudgeResult {
|
|
113
|
+
verdict: string; // "ALLOW", "HOLD", or "BLOCK"
|
|
114
|
+
action_type: string; // "allow", "hold_for_user_confirm", or "block"
|
|
115
|
+
reason: string; // Human-readable explanation
|
|
116
|
+
confidence: number; // 0–1
|
|
117
|
+
provider: string; // Which provider evaluated the action
|
|
118
|
+
latency_ms: number; // Inference time
|
|
119
|
+
tool_call_id: string; // Unique ID for this judgment
|
|
120
|
+
on_chain: boolean; // Whether the record was written on-chain
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Poll judgment status
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
getJudgmentStatus(judgmentId: string, opts?: ClientOpts): Promise<JudgmentStatus>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Check whether a held action has been approved or rejected by an operator.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
interface JudgmentStatus {
|
|
134
|
+
status: "pending" | "answered" | "error";
|
|
135
|
+
verdict: string;
|
|
136
|
+
reason: string;
|
|
137
|
+
judgmentId: string;
|
|
138
|
+
onChain?: boolean;
|
|
139
|
+
cached?: boolean;
|
|
140
|
+
responseTimeMs?: number;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Agent identity
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
createAgent(privkey: string): AgentAuth
|
|
148
|
+
generateKeyPair(): { privKey: string; pubKey: string }
|
|
149
|
+
derivePublicKey(privKeyHex: string): string
|
|
150
|
+
isValidPrivateKey(hex: string): boolean
|
|
151
|
+
toPubkeyHex(val: unknown): string
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`createAgent(privkey)` is the canonical loader — pass in the private key from the dashboard, get back `{ pubkey, privkey }` ready for `judgeAction`. It accepts `0x`-prefixed, padded, or mixed-case input and throws on malformed keys. Use `generateKeyPair()` only for local development; for production, create agents in the dashboard so operators can attach policies.
|
|
155
|
+
|
|
156
|
+
### Query APIs
|
|
157
|
+
|
|
158
|
+
Read from the Atbash audit trail and operator queues:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// Tool call history
|
|
162
|
+
getToolCalls(maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>
|
|
163
|
+
getOrgToolCalls(orgName: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>
|
|
164
|
+
getAgentToolCalls(agentPubkey: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>
|
|
165
|
+
getToolCallCount(opts?: ClientOpts): Promise<number>
|
|
166
|
+
getToolCallFull(toolCallId: string, opts?: ClientOpts): Promise<ToolCallFull | null>
|
|
167
|
+
|
|
168
|
+
// Org and agent info
|
|
169
|
+
getOrgTierInfo(orgName: string, opts?: ClientOpts): Promise<TierInfo | null>
|
|
170
|
+
getAgentDetail(agentPubkey: string, opts?: ClientOpts): Promise<Record<string, unknown>>
|
|
171
|
+
getAgentPolicy(agentPubkey: string, opts?: ClientOpts): Promise<{ policy: string; is_jailed: boolean; is_custom: boolean; default_policy: string }>
|
|
172
|
+
|
|
173
|
+
// Operator review queue
|
|
174
|
+
getPendingHeldActions(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldAction[]>
|
|
175
|
+
getHeldActionReviews(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldActionReview[]>
|
|
176
|
+
|
|
177
|
+
// Chain-wide stats
|
|
178
|
+
getSafetyStats(opts?: ClientOpts): Promise<Record<string, unknown>>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
All query functions accept an optional `ClientOpts` to override the endpoint:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
interface ClientOpts {
|
|
185
|
+
endpoint?: string; // Default: https://atbash.ai
|
|
186
|
+
timeout?: number;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Configuration
|
|
191
|
+
|
|
192
|
+
The SDK reads no environment variables and has no global state. Pass configuration explicitly:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// Custom endpoint
|
|
196
|
+
const result = await judgeAction(action, context, auth, {
|
|
197
|
+
endpoint: "https://your-instance.example.com",
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Custom provider
|
|
201
|
+
const result = await judgeAction(action, context, auth, {
|
|
202
|
+
provider: "openai",
|
|
203
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
204
|
+
model: "gpt-4o",
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Integration patterns
|
|
209
|
+
|
|
210
|
+
### Pre-execution gate
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
async function safeExecute(action: string, context: string, execute: () => Promise<void>) {
|
|
214
|
+
const result = await judgeAction(action, context, auth);
|
|
215
|
+
if (result.verdict === "BLOCK") throw new Error(`Blocked: ${result.reason}`);
|
|
216
|
+
if (result.verdict === "HOLD") throw new Error(`Held for review: ${result.tool_call_id}`);
|
|
217
|
+
await execute();
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Polling a held action
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
async function waitForApproval(toolCallId: string): Promise<string> {
|
|
225
|
+
while (true) {
|
|
226
|
+
const status = await getJudgmentStatus(toolCallId);
|
|
227
|
+
if (status.status === "answered") return status.verdict;
|
|
228
|
+
if (status.status === "error") throw new Error(status.reason);
|
|
229
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Checking agent jail status before a batch
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const policy = await getAgentPolicy(pubKey);
|
|
238
|
+
if (policy.is_jailed) {
|
|
239
|
+
console.error("Agent is jailed — unjail via dashboard before retrying.");
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Error handling
|
|
245
|
+
|
|
246
|
+
The SDK throws standard `Error` objects. Known failure modes are enriched with a pointer to the relevant dashboard page, so the error message tells you where to fix the problem:
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
API error 404: {"error":"Agent not registered..."}
|
|
250
|
+
→ Onboard the agent at https://atbash.ai/risk-engine/agents
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
| Error | Cause | Where to fix |
|
|
254
|
+
|---|---|---|
|
|
255
|
+
| `API error 404: Agent not registered` | Agent not onboarded | [atbash.ai/risk-engine/agents](https://atbash.ai/risk-engine/agents) |
|
|
256
|
+
| `API error 400: Agent has no policy` | No policy attached to agent | [atbash.ai/risk-engine/agents](https://atbash.ai/risk-engine/agents) |
|
|
257
|
+
| `Agent is jailed` | BLOCK verdict triggered auto-jail (Enforcement tier) | [atbash.ai/risk-engine/agents](https://atbash.ai/risk-engine/agents) |
|
|
258
|
+
| `Org tier does not support verdicts` | Org is on Audit tier | [atbash.ai/risk-engine/settings](https://atbash.ai/risk-engine/settings) |
|
|
259
|
+
| `API error 400: action is required` | Empty action string | Fix caller |
|
|
260
|
+
| `API error 502: Incorrect API key` | Invalid provider API key | Check `apiKey` in `JudgeOptions` |
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
try {
|
|
264
|
+
const result = await judgeAction(action, context, auth);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
if (err.message.includes("Agent not registered")) {
|
|
267
|
+
// Point the user at https://atbash.ai/risk-engine/agents to onboard.
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## SDK vs CLI
|
|
273
|
+
|
|
274
|
+
| | `@atbash/sdk` | `@atbash/cli` |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| Use case | Backend services, agents, pipelines | Terminal, operator review, interactive use |
|
|
277
|
+
| Programmatic control | Full | Shell commands |
|
|
278
|
+
| Install | `npm install @atbash/sdk` | `npm install -g @atbash/cli` |
|
|
279
|
+
| Policy authoring | No (dashboard only) | No (dashboard only) |
|
|
280
|
+
|
|
281
|
+
The CLI is built on top of the SDK. Both talk to the same APIs, the same policy engine, and the same on-chain audit trail.
|
|
282
|
+
|
|
283
|
+
## Design
|
|
284
|
+
|
|
285
|
+
- **Zero runtime dependencies** — uses only `node:crypto` and global `fetch`
|
|
286
|
+
- **TypeScript-first** — full type definitions for all inputs and outputs
|
|
287
|
+
- **No global state** — every function takes explicit arguments
|
|
288
|
+
- **No browser support** — private keys in request bodies are not safe for client-side use
|
|
289
|
+
|
|
290
|
+
## Dashboard
|
|
291
|
+
|
|
292
|
+
Policy authoring, operator reviews, and agent management happen at [atbash.ai](https://atbash.ai/). The SDK is the programmatic interface; the dashboard is the operator interface.
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
Proprietary — all rights reserved. See [LICENSE](./LICENSE).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DEFAULT_ENDPOINT: () => DEFAULT_ENDPOINT,
|
|
24
|
+
createAgent: () => createAgent,
|
|
25
|
+
derivePublicKey: () => derivePublicKey,
|
|
26
|
+
generateKeyPair: () => generateKeyPair,
|
|
27
|
+
getAgentDetail: () => getAgentDetail,
|
|
28
|
+
getAgentPolicy: () => getAgentPolicy,
|
|
29
|
+
getAgentToolCalls: () => getAgentToolCalls,
|
|
30
|
+
getHeldActionReviews: () => getHeldActionReviews,
|
|
31
|
+
getJudgmentStatus: () => getJudgmentStatus,
|
|
32
|
+
getOrgTierInfo: () => getOrgTierInfo,
|
|
33
|
+
getOrgToolCalls: () => getOrgToolCalls,
|
|
34
|
+
getPendingHeldActions: () => getPendingHeldActions,
|
|
35
|
+
getSafetyStats: () => getSafetyStats,
|
|
36
|
+
getToolCallCount: () => getToolCallCount,
|
|
37
|
+
getToolCallFull: () => getToolCallFull,
|
|
38
|
+
getToolCalls: () => getToolCalls,
|
|
39
|
+
isValidPrivateKey: () => isValidPrivateKey,
|
|
40
|
+
judgeAction: () => judgeAction,
|
|
41
|
+
toPubkeyHex: () => toPubkeyHex
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
|
+
|
|
45
|
+
// src/client.ts
|
|
46
|
+
var import_crypto = require("crypto");
|
|
47
|
+
var DEFAULT_ENDPOINT = "https://atbash.ai";
|
|
48
|
+
function isValidPrivateKey(hex) {
|
|
49
|
+
return /^[0-9a-fA-F]{64}$/.test(hex);
|
|
50
|
+
}
|
|
51
|
+
function derivePublicKey(privKeyHex) {
|
|
52
|
+
const ecdh = (0, import_crypto.createECDH)("secp256k1");
|
|
53
|
+
ecdh.setPrivateKey(Buffer.from(privKeyHex, "hex"));
|
|
54
|
+
return ecdh.getPublicKey("hex", "compressed");
|
|
55
|
+
}
|
|
56
|
+
function generateKeyPair() {
|
|
57
|
+
const ecdh = (0, import_crypto.createECDH)("secp256k1");
|
|
58
|
+
ecdh.generateKeys();
|
|
59
|
+
return {
|
|
60
|
+
privKey: ecdh.getPrivateKey("hex"),
|
|
61
|
+
pubKey: ecdh.getPublicKey("hex", "compressed")
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createAgent(privkey) {
|
|
65
|
+
const clean = privkey.replace(/^0x/, "").trim().toLowerCase();
|
|
66
|
+
if (!isValidPrivateKey(clean)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Invalid private key \u2014 must be 64 hex characters (secp256k1)."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return { privkey: clean, pubkey: derivePublicKey(clean) };
|
|
72
|
+
}
|
|
73
|
+
function hasDataArray(val) {
|
|
74
|
+
return typeof val === "object" && val !== null && "data" in val && Array.isArray(val.data);
|
|
75
|
+
}
|
|
76
|
+
function toPubkeyHex(val) {
|
|
77
|
+
if (!val) return "";
|
|
78
|
+
if (typeof val === "string") return val;
|
|
79
|
+
if (Buffer.isBuffer(val)) return val.toString("hex");
|
|
80
|
+
if (hasDataArray(val)) return Buffer.from(val.data).toString("hex");
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
function baseUrl(opts) {
|
|
84
|
+
return opts?.endpoint || DEFAULT_ENDPOINT;
|
|
85
|
+
}
|
|
86
|
+
function normalizeVerdict(raw) {
|
|
87
|
+
const v = String(raw || "").toUpperCase();
|
|
88
|
+
if (v === "ALLOW" || v === "GREEN") return "ALLOW";
|
|
89
|
+
if (v === "HOLD" || v === "YELLOW") return "HOLD";
|
|
90
|
+
if (v === "BLOCK" || v === "RED") return "BLOCK";
|
|
91
|
+
return "BLOCK";
|
|
92
|
+
}
|
|
93
|
+
function normalizeStatus(raw) {
|
|
94
|
+
const s = String(raw || "").toLowerCase();
|
|
95
|
+
if (s === "pending" || s === "answered" || s === "error") return s;
|
|
96
|
+
return "error";
|
|
97
|
+
}
|
|
98
|
+
function enrichError(status, body, statusText, opts) {
|
|
99
|
+
const dashboard = (opts?.endpoint || DEFAULT_ENDPOINT).replace(/\/$/, "");
|
|
100
|
+
let message = `API error ${status}: ${body || statusText}`;
|
|
101
|
+
if (/Agent not registered/i.test(body)) {
|
|
102
|
+
message += `
|
|
103
|
+
\u2192 Onboard the agent at ${dashboard}/risk-engine/agents`;
|
|
104
|
+
} else if (/Agent has no policy|no policy configured/i.test(body)) {
|
|
105
|
+
message += `
|
|
106
|
+
\u2192 Attach a policy at ${dashboard}/risk-engine/agents`;
|
|
107
|
+
} else if (/Agent is jailed|agent.*jailed/i.test(body)) {
|
|
108
|
+
message += `
|
|
109
|
+
\u2192 Unjail the agent at ${dashboard}/risk-engine/agents`;
|
|
110
|
+
} else if (/audit tier|verdict.*(disabled|not supported)/i.test(body)) {
|
|
111
|
+
message += `
|
|
112
|
+
\u2192 Upgrade the org tier at ${dashboard}/risk-engine/settings`;
|
|
113
|
+
} else if (status >= 400 && status < 500) {
|
|
114
|
+
message += `
|
|
115
|
+
\u2192 Dashboard: ${dashboard}/risk-engine/feed`;
|
|
116
|
+
}
|
|
117
|
+
return new Error(message);
|
|
118
|
+
}
|
|
119
|
+
async function postJson(url, body, opts) {
|
|
120
|
+
const resp = await fetch(url, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: { "Content-Type": "application/json" },
|
|
123
|
+
body: JSON.stringify(body),
|
|
124
|
+
signal: opts?.timeout ? AbortSignal.timeout(opts.timeout) : void 0
|
|
125
|
+
});
|
|
126
|
+
if (!resp.ok) {
|
|
127
|
+
const text = await resp.text().catch(() => "");
|
|
128
|
+
throw enrichError(resp.status, text, resp.statusText, opts);
|
|
129
|
+
}
|
|
130
|
+
const ct = resp.headers.get("content-type") || "";
|
|
131
|
+
return ct.includes("application/json") ? resp.json() : {};
|
|
132
|
+
}
|
|
133
|
+
async function getJson(url, opts) {
|
|
134
|
+
const resp = await fetch(url, {
|
|
135
|
+
method: "GET",
|
|
136
|
+
signal: opts?.timeout ? AbortSignal.timeout(opts.timeout) : void 0
|
|
137
|
+
});
|
|
138
|
+
if (!resp.ok) {
|
|
139
|
+
const text = await resp.text().catch(() => "");
|
|
140
|
+
throw enrichError(resp.status, text, resp.statusText, opts);
|
|
141
|
+
}
|
|
142
|
+
return resp.json();
|
|
143
|
+
}
|
|
144
|
+
async function judgeAction(action, context, auth, opts) {
|
|
145
|
+
const url = `${baseUrl(opts)}/api/v1/judge`;
|
|
146
|
+
const data = await postJson(
|
|
147
|
+
url,
|
|
148
|
+
{
|
|
149
|
+
provider: opts?.provider || "",
|
|
150
|
+
action,
|
|
151
|
+
context: context || "",
|
|
152
|
+
agent_pubkey: auth.pubkey,
|
|
153
|
+
agent_privkey: auth.privkey,
|
|
154
|
+
tool_name: opts?.toolName || "",
|
|
155
|
+
tool_args_json: opts?.toolArgsJson || "",
|
|
156
|
+
api_key: opts?.apiKey || "",
|
|
157
|
+
endpoint_url: opts?.providerEndpoint || "",
|
|
158
|
+
model: opts?.model || ""
|
|
159
|
+
},
|
|
160
|
+
opts
|
|
161
|
+
);
|
|
162
|
+
return {
|
|
163
|
+
verdict: normalizeVerdict(data.verdict),
|
|
164
|
+
action_type: String(data.action_type || ""),
|
|
165
|
+
reason: String(data.reason || ""),
|
|
166
|
+
confidence: Number(data.confidence ?? 0),
|
|
167
|
+
provider: String(data.provider || ""),
|
|
168
|
+
latency_ms: Number(data.latency_ms ?? 0),
|
|
169
|
+
tool_call_id: String(data.tool_call_id || ""),
|
|
170
|
+
on_chain: Boolean(data.on_chain)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async function getJudgmentStatus(judgmentId, opts) {
|
|
174
|
+
const url = `${baseUrl(opts)}/api/v1/judge?tool_call_id=${encodeURIComponent(judgmentId)}`;
|
|
175
|
+
const data = await getJson(url, opts);
|
|
176
|
+
return {
|
|
177
|
+
status: normalizeStatus(data.status),
|
|
178
|
+
verdict: normalizeVerdict(data.verdict),
|
|
179
|
+
reason: String(data.reason || ""),
|
|
180
|
+
judgmentId: String(data.judgmentId || judgmentId),
|
|
181
|
+
onChain: Boolean(data.onChain),
|
|
182
|
+
cached: Boolean(data.cached),
|
|
183
|
+
responseTimeMs: Number(data.responseTimeMs ?? 0)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function riskEngineUrl(action, params, opts) {
|
|
187
|
+
const url = new URL(`${baseUrl(opts)}/api/risk-engine`);
|
|
188
|
+
url.searchParams.set("action", action);
|
|
189
|
+
for (const [k, v] of Object.entries(params)) {
|
|
190
|
+
if (v) url.searchParams.set(k, v);
|
|
191
|
+
}
|
|
192
|
+
return url.toString();
|
|
193
|
+
}
|
|
194
|
+
async function getToolCalls(maxCount, opts) {
|
|
195
|
+
return getJson(
|
|
196
|
+
riskEngineUrl("tool-calls", { limit: String(maxCount) }, opts),
|
|
197
|
+
opts
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
async function getOrgToolCalls(orgName, maxCount, opts) {
|
|
201
|
+
return getJson(
|
|
202
|
+
riskEngineUrl(
|
|
203
|
+
"org-tool-calls",
|
|
204
|
+
{ org: orgName, limit: String(maxCount) },
|
|
205
|
+
opts
|
|
206
|
+
),
|
|
207
|
+
opts
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
async function getAgentToolCalls(agentPubkey, maxCount, opts) {
|
|
211
|
+
return getJson(
|
|
212
|
+
riskEngineUrl(
|
|
213
|
+
"agent-tool-calls",
|
|
214
|
+
{ agent: agentPubkey, limit: String(maxCount) },
|
|
215
|
+
opts
|
|
216
|
+
),
|
|
217
|
+
opts
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
async function getToolCallCount(opts) {
|
|
221
|
+
const result = await getJson(
|
|
222
|
+
riskEngineUrl("tool-call-count", {}, opts),
|
|
223
|
+
opts
|
|
224
|
+
);
|
|
225
|
+
return typeof result === "number" ? result : Number(result ?? 0);
|
|
226
|
+
}
|
|
227
|
+
async function getToolCallFull(toolCallId, opts) {
|
|
228
|
+
return getJson(
|
|
229
|
+
riskEngineUrl("tool-call-full", { tool_call_id: toolCallId }, opts),
|
|
230
|
+
opts
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
async function getOrgTierInfo(orgName, opts) {
|
|
234
|
+
return getJson(
|
|
235
|
+
riskEngineUrl("org-tier-info", { org: orgName }, opts),
|
|
236
|
+
opts
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
async function getPendingHeldActions(orgName, maxCount, opts) {
|
|
240
|
+
const raw = await getJson(
|
|
241
|
+
riskEngineUrl(
|
|
242
|
+
"pending-held-actions",
|
|
243
|
+
{ org: orgName, limit: String(maxCount) },
|
|
244
|
+
opts
|
|
245
|
+
),
|
|
246
|
+
opts
|
|
247
|
+
);
|
|
248
|
+
return raw.map((h) => ({ ...h, verdict: normalizeVerdict(h.verdict) }));
|
|
249
|
+
}
|
|
250
|
+
async function getHeldActionReviews(orgName, maxCount, opts) {
|
|
251
|
+
return getJson(
|
|
252
|
+
riskEngineUrl(
|
|
253
|
+
"held-action-reviews",
|
|
254
|
+
{ org: orgName, limit: String(maxCount) },
|
|
255
|
+
opts
|
|
256
|
+
),
|
|
257
|
+
opts
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
function riskEnginePostUrl(opts) {
|
|
261
|
+
return `${baseUrl(opts)}/api/risk-engine`;
|
|
262
|
+
}
|
|
263
|
+
async function getAgentDetail(agentPubkey, opts) {
|
|
264
|
+
return postJson(
|
|
265
|
+
riskEnginePostUrl(opts),
|
|
266
|
+
{
|
|
267
|
+
action: "agent-detail-batch",
|
|
268
|
+
agent: agentPubkey
|
|
269
|
+
},
|
|
270
|
+
opts
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
async function getAgentPolicy(agentPubkey, opts) {
|
|
274
|
+
return postJson(
|
|
275
|
+
riskEnginePostUrl(opts),
|
|
276
|
+
{
|
|
277
|
+
action: "agent-policy-batch",
|
|
278
|
+
agent: agentPubkey
|
|
279
|
+
},
|
|
280
|
+
opts
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
async function getSafetyStats(opts) {
|
|
284
|
+
const url = `${baseUrl(opts)}/api/insurance?action=safety-stats`;
|
|
285
|
+
const result = await getJson(url, opts);
|
|
286
|
+
return result?.data || result;
|
|
287
|
+
}
|
|
288
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
289
|
+
0 && (module.exports = {
|
|
290
|
+
DEFAULT_ENDPOINT,
|
|
291
|
+
createAgent,
|
|
292
|
+
derivePublicKey,
|
|
293
|
+
generateKeyPair,
|
|
294
|
+
getAgentDetail,
|
|
295
|
+
getAgentPolicy,
|
|
296
|
+
getAgentToolCalls,
|
|
297
|
+
getHeldActionReviews,
|
|
298
|
+
getJudgmentStatus,
|
|
299
|
+
getOrgTierInfo,
|
|
300
|
+
getOrgToolCalls,
|
|
301
|
+
getPendingHeldActions,
|
|
302
|
+
getSafetyStats,
|
|
303
|
+
getToolCallCount,
|
|
304
|
+
getToolCallFull,
|
|
305
|
+
getToolCalls,
|
|
306
|
+
isValidPrivateKey,
|
|
307
|
+
judgeAction,
|
|
308
|
+
toPubkeyHex
|
|
309
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
declare const DEFAULT_ENDPOINT = "https://atbash.ai";
|
|
2
|
+
type Verdict = "ALLOW" | "HOLD" | "BLOCK";
|
|
3
|
+
type Provider = "atbash" | "openai" | "google" | "microsoft" | "custom" | (string & {});
|
|
4
|
+
type Tier = "audit" | "audit_plus" | "enforcement" | (string & {});
|
|
5
|
+
type ActionType = "allow" | "hold_for_user_confirm" | "block" | (string & {});
|
|
6
|
+
type PubkeyValue = string | Buffer | {
|
|
7
|
+
data: number[];
|
|
8
|
+
};
|
|
9
|
+
type JudgmentStatusState = "pending" | "answered" | "error";
|
|
10
|
+
declare function isValidPrivateKey(hex: string): boolean;
|
|
11
|
+
declare function derivePublicKey(privKeyHex: string): string;
|
|
12
|
+
declare function generateKeyPair(): {
|
|
13
|
+
privKey: string;
|
|
14
|
+
pubKey: string;
|
|
15
|
+
};
|
|
16
|
+
interface AgentAuth {
|
|
17
|
+
pubkey: string;
|
|
18
|
+
privkey: string;
|
|
19
|
+
}
|
|
20
|
+
declare function createAgent(privkey: string): AgentAuth;
|
|
21
|
+
declare function toPubkeyHex(val: unknown): string;
|
|
22
|
+
interface ClientOpts {
|
|
23
|
+
endpoint?: string;
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
interface JudgeResult {
|
|
27
|
+
verdict: Verdict;
|
|
28
|
+
action_type: ActionType;
|
|
29
|
+
reason: string;
|
|
30
|
+
confidence: number;
|
|
31
|
+
provider: Provider;
|
|
32
|
+
latency_ms: number;
|
|
33
|
+
tool_call_id: string;
|
|
34
|
+
on_chain: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface TierInfo {
|
|
37
|
+
org_name: string;
|
|
38
|
+
tier: Tier;
|
|
39
|
+
verdict_enabled: boolean;
|
|
40
|
+
enforcement_enabled: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface ToolCallRecord {
|
|
43
|
+
tool_call_id: string;
|
|
44
|
+
agent_pubkey: PubkeyValue;
|
|
45
|
+
tool_name: string;
|
|
46
|
+
command_text: string;
|
|
47
|
+
tool_args_json: string;
|
|
48
|
+
context_text: string;
|
|
49
|
+
org_name: string;
|
|
50
|
+
rowid: number;
|
|
51
|
+
}
|
|
52
|
+
interface ToolCallFull {
|
|
53
|
+
tool_call_id: string;
|
|
54
|
+
agent_pubkey: PubkeyValue;
|
|
55
|
+
tool_name: string;
|
|
56
|
+
command_text: string;
|
|
57
|
+
context_text: string;
|
|
58
|
+
tool_args_json?: string;
|
|
59
|
+
org_name: string;
|
|
60
|
+
created_at?: number;
|
|
61
|
+
action_type?: ActionType;
|
|
62
|
+
result_status?: string;
|
|
63
|
+
verdict_color?: string;
|
|
64
|
+
verdict_reason?: string;
|
|
65
|
+
verdict_source?: string;
|
|
66
|
+
verdict_response_time_ms?: number;
|
|
67
|
+
}
|
|
68
|
+
interface HeldAction {
|
|
69
|
+
judgment_id: string;
|
|
70
|
+
agent_pubkey: PubkeyValue;
|
|
71
|
+
action_text: string;
|
|
72
|
+
action_context: string;
|
|
73
|
+
verdict: Verdict;
|
|
74
|
+
reason: string;
|
|
75
|
+
created_at: number;
|
|
76
|
+
}
|
|
77
|
+
interface HeldActionReview {
|
|
78
|
+
judgment_id: string;
|
|
79
|
+
action_text: string;
|
|
80
|
+
status: string;
|
|
81
|
+
review_note: string;
|
|
82
|
+
reviewed_by: PubkeyValue | null;
|
|
83
|
+
reviewed_at: number;
|
|
84
|
+
created_at: number;
|
|
85
|
+
}
|
|
86
|
+
interface JudgmentStatus {
|
|
87
|
+
status: JudgmentStatusState;
|
|
88
|
+
verdict: Verdict;
|
|
89
|
+
reason: string;
|
|
90
|
+
judgmentId: string;
|
|
91
|
+
onChain?: boolean;
|
|
92
|
+
cached?: boolean;
|
|
93
|
+
responseTimeMs?: number;
|
|
94
|
+
}
|
|
95
|
+
interface JudgeOptions extends ClientOpts {
|
|
96
|
+
provider?: Provider;
|
|
97
|
+
apiKey?: string;
|
|
98
|
+
providerEndpoint?: string;
|
|
99
|
+
model?: string;
|
|
100
|
+
toolName?: string;
|
|
101
|
+
toolArgsJson?: string;
|
|
102
|
+
}
|
|
103
|
+
interface AgentPolicy {
|
|
104
|
+
policy: string;
|
|
105
|
+
is_jailed: boolean;
|
|
106
|
+
is_custom: boolean;
|
|
107
|
+
default_policy: string;
|
|
108
|
+
}
|
|
109
|
+
declare function judgeAction(action: string, context: string, auth: AgentAuth, opts?: JudgeOptions): Promise<JudgeResult>;
|
|
110
|
+
declare function getJudgmentStatus(judgmentId: string, opts?: ClientOpts): Promise<JudgmentStatus>;
|
|
111
|
+
declare function getToolCalls(maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>;
|
|
112
|
+
declare function getOrgToolCalls(orgName: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>;
|
|
113
|
+
declare function getAgentToolCalls(agentPubkey: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>;
|
|
114
|
+
declare function getToolCallCount(opts?: ClientOpts): Promise<number>;
|
|
115
|
+
declare function getToolCallFull(toolCallId: string, opts?: ClientOpts): Promise<ToolCallFull | null>;
|
|
116
|
+
declare function getOrgTierInfo(orgName: string, opts?: ClientOpts): Promise<TierInfo | null>;
|
|
117
|
+
declare function getPendingHeldActions(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldAction[]>;
|
|
118
|
+
declare function getHeldActionReviews(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldActionReview[]>;
|
|
119
|
+
declare function getAgentDetail(agentPubkey: string, opts?: ClientOpts): Promise<Record<string, unknown>>;
|
|
120
|
+
declare function getAgentPolicy(agentPubkey: string, opts?: ClientOpts): Promise<AgentPolicy>;
|
|
121
|
+
declare function getSafetyStats(opts?: ClientOpts): Promise<Record<string, unknown>>;
|
|
122
|
+
|
|
123
|
+
export { type AgentAuth, type ClientOpts, DEFAULT_ENDPOINT, type HeldAction, type HeldActionReview, type JudgeOptions, type JudgeResult, type JudgmentStatus, type TierInfo, type ToolCallFull, type ToolCallRecord, type Verdict, createAgent, derivePublicKey, generateKeyPair, getAgentDetail, getAgentPolicy, getAgentToolCalls, getHeldActionReviews, getJudgmentStatus, getOrgTierInfo, getOrgToolCalls, getPendingHeldActions, getSafetyStats, getToolCallCount, getToolCallFull, getToolCalls, isValidPrivateKey, judgeAction, toPubkeyHex };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
declare const DEFAULT_ENDPOINT = "https://atbash.ai";
|
|
2
|
+
type Verdict = "ALLOW" | "HOLD" | "BLOCK";
|
|
3
|
+
type Provider = "atbash" | "openai" | "google" | "microsoft" | "custom" | (string & {});
|
|
4
|
+
type Tier = "audit" | "audit_plus" | "enforcement" | (string & {});
|
|
5
|
+
type ActionType = "allow" | "hold_for_user_confirm" | "block" | (string & {});
|
|
6
|
+
type PubkeyValue = string | Buffer | {
|
|
7
|
+
data: number[];
|
|
8
|
+
};
|
|
9
|
+
type JudgmentStatusState = "pending" | "answered" | "error";
|
|
10
|
+
declare function isValidPrivateKey(hex: string): boolean;
|
|
11
|
+
declare function derivePublicKey(privKeyHex: string): string;
|
|
12
|
+
declare function generateKeyPair(): {
|
|
13
|
+
privKey: string;
|
|
14
|
+
pubKey: string;
|
|
15
|
+
};
|
|
16
|
+
interface AgentAuth {
|
|
17
|
+
pubkey: string;
|
|
18
|
+
privkey: string;
|
|
19
|
+
}
|
|
20
|
+
declare function createAgent(privkey: string): AgentAuth;
|
|
21
|
+
declare function toPubkeyHex(val: unknown): string;
|
|
22
|
+
interface ClientOpts {
|
|
23
|
+
endpoint?: string;
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
interface JudgeResult {
|
|
27
|
+
verdict: Verdict;
|
|
28
|
+
action_type: ActionType;
|
|
29
|
+
reason: string;
|
|
30
|
+
confidence: number;
|
|
31
|
+
provider: Provider;
|
|
32
|
+
latency_ms: number;
|
|
33
|
+
tool_call_id: string;
|
|
34
|
+
on_chain: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface TierInfo {
|
|
37
|
+
org_name: string;
|
|
38
|
+
tier: Tier;
|
|
39
|
+
verdict_enabled: boolean;
|
|
40
|
+
enforcement_enabled: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface ToolCallRecord {
|
|
43
|
+
tool_call_id: string;
|
|
44
|
+
agent_pubkey: PubkeyValue;
|
|
45
|
+
tool_name: string;
|
|
46
|
+
command_text: string;
|
|
47
|
+
tool_args_json: string;
|
|
48
|
+
context_text: string;
|
|
49
|
+
org_name: string;
|
|
50
|
+
rowid: number;
|
|
51
|
+
}
|
|
52
|
+
interface ToolCallFull {
|
|
53
|
+
tool_call_id: string;
|
|
54
|
+
agent_pubkey: PubkeyValue;
|
|
55
|
+
tool_name: string;
|
|
56
|
+
command_text: string;
|
|
57
|
+
context_text: string;
|
|
58
|
+
tool_args_json?: string;
|
|
59
|
+
org_name: string;
|
|
60
|
+
created_at?: number;
|
|
61
|
+
action_type?: ActionType;
|
|
62
|
+
result_status?: string;
|
|
63
|
+
verdict_color?: string;
|
|
64
|
+
verdict_reason?: string;
|
|
65
|
+
verdict_source?: string;
|
|
66
|
+
verdict_response_time_ms?: number;
|
|
67
|
+
}
|
|
68
|
+
interface HeldAction {
|
|
69
|
+
judgment_id: string;
|
|
70
|
+
agent_pubkey: PubkeyValue;
|
|
71
|
+
action_text: string;
|
|
72
|
+
action_context: string;
|
|
73
|
+
verdict: Verdict;
|
|
74
|
+
reason: string;
|
|
75
|
+
created_at: number;
|
|
76
|
+
}
|
|
77
|
+
interface HeldActionReview {
|
|
78
|
+
judgment_id: string;
|
|
79
|
+
action_text: string;
|
|
80
|
+
status: string;
|
|
81
|
+
review_note: string;
|
|
82
|
+
reviewed_by: PubkeyValue | null;
|
|
83
|
+
reviewed_at: number;
|
|
84
|
+
created_at: number;
|
|
85
|
+
}
|
|
86
|
+
interface JudgmentStatus {
|
|
87
|
+
status: JudgmentStatusState;
|
|
88
|
+
verdict: Verdict;
|
|
89
|
+
reason: string;
|
|
90
|
+
judgmentId: string;
|
|
91
|
+
onChain?: boolean;
|
|
92
|
+
cached?: boolean;
|
|
93
|
+
responseTimeMs?: number;
|
|
94
|
+
}
|
|
95
|
+
interface JudgeOptions extends ClientOpts {
|
|
96
|
+
provider?: Provider;
|
|
97
|
+
apiKey?: string;
|
|
98
|
+
providerEndpoint?: string;
|
|
99
|
+
model?: string;
|
|
100
|
+
toolName?: string;
|
|
101
|
+
toolArgsJson?: string;
|
|
102
|
+
}
|
|
103
|
+
interface AgentPolicy {
|
|
104
|
+
policy: string;
|
|
105
|
+
is_jailed: boolean;
|
|
106
|
+
is_custom: boolean;
|
|
107
|
+
default_policy: string;
|
|
108
|
+
}
|
|
109
|
+
declare function judgeAction(action: string, context: string, auth: AgentAuth, opts?: JudgeOptions): Promise<JudgeResult>;
|
|
110
|
+
declare function getJudgmentStatus(judgmentId: string, opts?: ClientOpts): Promise<JudgmentStatus>;
|
|
111
|
+
declare function getToolCalls(maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>;
|
|
112
|
+
declare function getOrgToolCalls(orgName: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>;
|
|
113
|
+
declare function getAgentToolCalls(agentPubkey: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>;
|
|
114
|
+
declare function getToolCallCount(opts?: ClientOpts): Promise<number>;
|
|
115
|
+
declare function getToolCallFull(toolCallId: string, opts?: ClientOpts): Promise<ToolCallFull | null>;
|
|
116
|
+
declare function getOrgTierInfo(orgName: string, opts?: ClientOpts): Promise<TierInfo | null>;
|
|
117
|
+
declare function getPendingHeldActions(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldAction[]>;
|
|
118
|
+
declare function getHeldActionReviews(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldActionReview[]>;
|
|
119
|
+
declare function getAgentDetail(agentPubkey: string, opts?: ClientOpts): Promise<Record<string, unknown>>;
|
|
120
|
+
declare function getAgentPolicy(agentPubkey: string, opts?: ClientOpts): Promise<AgentPolicy>;
|
|
121
|
+
declare function getSafetyStats(opts?: ClientOpts): Promise<Record<string, unknown>>;
|
|
122
|
+
|
|
123
|
+
export { type AgentAuth, type ClientOpts, DEFAULT_ENDPOINT, type HeldAction, type HeldActionReview, type JudgeOptions, type JudgeResult, type JudgmentStatus, type TierInfo, type ToolCallFull, type ToolCallRecord, type Verdict, createAgent, derivePublicKey, generateKeyPair, getAgentDetail, getAgentPolicy, getAgentToolCalls, getHeldActionReviews, getJudgmentStatus, getOrgTierInfo, getOrgToolCalls, getPendingHeldActions, getSafetyStats, getToolCallCount, getToolCallFull, getToolCalls, isValidPrivateKey, judgeAction, toPubkeyHex };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { createECDH } from "crypto";
|
|
3
|
+
var DEFAULT_ENDPOINT = "https://atbash.ai";
|
|
4
|
+
function isValidPrivateKey(hex) {
|
|
5
|
+
return /^[0-9a-fA-F]{64}$/.test(hex);
|
|
6
|
+
}
|
|
7
|
+
function derivePublicKey(privKeyHex) {
|
|
8
|
+
const ecdh = createECDH("secp256k1");
|
|
9
|
+
ecdh.setPrivateKey(Buffer.from(privKeyHex, "hex"));
|
|
10
|
+
return ecdh.getPublicKey("hex", "compressed");
|
|
11
|
+
}
|
|
12
|
+
function generateKeyPair() {
|
|
13
|
+
const ecdh = createECDH("secp256k1");
|
|
14
|
+
ecdh.generateKeys();
|
|
15
|
+
return {
|
|
16
|
+
privKey: ecdh.getPrivateKey("hex"),
|
|
17
|
+
pubKey: ecdh.getPublicKey("hex", "compressed")
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createAgent(privkey) {
|
|
21
|
+
const clean = privkey.replace(/^0x/, "").trim().toLowerCase();
|
|
22
|
+
if (!isValidPrivateKey(clean)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
"Invalid private key \u2014 must be 64 hex characters (secp256k1)."
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return { privkey: clean, pubkey: derivePublicKey(clean) };
|
|
28
|
+
}
|
|
29
|
+
function hasDataArray(val) {
|
|
30
|
+
return typeof val === "object" && val !== null && "data" in val && Array.isArray(val.data);
|
|
31
|
+
}
|
|
32
|
+
function toPubkeyHex(val) {
|
|
33
|
+
if (!val) return "";
|
|
34
|
+
if (typeof val === "string") return val;
|
|
35
|
+
if (Buffer.isBuffer(val)) return val.toString("hex");
|
|
36
|
+
if (hasDataArray(val)) return Buffer.from(val.data).toString("hex");
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
function baseUrl(opts) {
|
|
40
|
+
return opts?.endpoint || DEFAULT_ENDPOINT;
|
|
41
|
+
}
|
|
42
|
+
function normalizeVerdict(raw) {
|
|
43
|
+
const v = String(raw || "").toUpperCase();
|
|
44
|
+
if (v === "ALLOW" || v === "GREEN") return "ALLOW";
|
|
45
|
+
if (v === "HOLD" || v === "YELLOW") return "HOLD";
|
|
46
|
+
if (v === "BLOCK" || v === "RED") return "BLOCK";
|
|
47
|
+
return "BLOCK";
|
|
48
|
+
}
|
|
49
|
+
function normalizeStatus(raw) {
|
|
50
|
+
const s = String(raw || "").toLowerCase();
|
|
51
|
+
if (s === "pending" || s === "answered" || s === "error") return s;
|
|
52
|
+
return "error";
|
|
53
|
+
}
|
|
54
|
+
function enrichError(status, body, statusText, opts) {
|
|
55
|
+
const dashboard = (opts?.endpoint || DEFAULT_ENDPOINT).replace(/\/$/, "");
|
|
56
|
+
let message = `API error ${status}: ${body || statusText}`;
|
|
57
|
+
if (/Agent not registered/i.test(body)) {
|
|
58
|
+
message += `
|
|
59
|
+
\u2192 Onboard the agent at ${dashboard}/risk-engine/agents`;
|
|
60
|
+
} else if (/Agent has no policy|no policy configured/i.test(body)) {
|
|
61
|
+
message += `
|
|
62
|
+
\u2192 Attach a policy at ${dashboard}/risk-engine/agents`;
|
|
63
|
+
} else if (/Agent is jailed|agent.*jailed/i.test(body)) {
|
|
64
|
+
message += `
|
|
65
|
+
\u2192 Unjail the agent at ${dashboard}/risk-engine/agents`;
|
|
66
|
+
} else if (/audit tier|verdict.*(disabled|not supported)/i.test(body)) {
|
|
67
|
+
message += `
|
|
68
|
+
\u2192 Upgrade the org tier at ${dashboard}/risk-engine/settings`;
|
|
69
|
+
} else if (status >= 400 && status < 500) {
|
|
70
|
+
message += `
|
|
71
|
+
\u2192 Dashboard: ${dashboard}/risk-engine/feed`;
|
|
72
|
+
}
|
|
73
|
+
return new Error(message);
|
|
74
|
+
}
|
|
75
|
+
async function postJson(url, body, opts) {
|
|
76
|
+
const resp = await fetch(url, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: { "Content-Type": "application/json" },
|
|
79
|
+
body: JSON.stringify(body),
|
|
80
|
+
signal: opts?.timeout ? AbortSignal.timeout(opts.timeout) : void 0
|
|
81
|
+
});
|
|
82
|
+
if (!resp.ok) {
|
|
83
|
+
const text = await resp.text().catch(() => "");
|
|
84
|
+
throw enrichError(resp.status, text, resp.statusText, opts);
|
|
85
|
+
}
|
|
86
|
+
const ct = resp.headers.get("content-type") || "";
|
|
87
|
+
return ct.includes("application/json") ? resp.json() : {};
|
|
88
|
+
}
|
|
89
|
+
async function getJson(url, opts) {
|
|
90
|
+
const resp = await fetch(url, {
|
|
91
|
+
method: "GET",
|
|
92
|
+
signal: opts?.timeout ? AbortSignal.timeout(opts.timeout) : void 0
|
|
93
|
+
});
|
|
94
|
+
if (!resp.ok) {
|
|
95
|
+
const text = await resp.text().catch(() => "");
|
|
96
|
+
throw enrichError(resp.status, text, resp.statusText, opts);
|
|
97
|
+
}
|
|
98
|
+
return resp.json();
|
|
99
|
+
}
|
|
100
|
+
async function judgeAction(action, context, auth, opts) {
|
|
101
|
+
const url = `${baseUrl(opts)}/api/v1/judge`;
|
|
102
|
+
const data = await postJson(
|
|
103
|
+
url,
|
|
104
|
+
{
|
|
105
|
+
provider: opts?.provider || "",
|
|
106
|
+
action,
|
|
107
|
+
context: context || "",
|
|
108
|
+
agent_pubkey: auth.pubkey,
|
|
109
|
+
agent_privkey: auth.privkey,
|
|
110
|
+
tool_name: opts?.toolName || "",
|
|
111
|
+
tool_args_json: opts?.toolArgsJson || "",
|
|
112
|
+
api_key: opts?.apiKey || "",
|
|
113
|
+
endpoint_url: opts?.providerEndpoint || "",
|
|
114
|
+
model: opts?.model || ""
|
|
115
|
+
},
|
|
116
|
+
opts
|
|
117
|
+
);
|
|
118
|
+
return {
|
|
119
|
+
verdict: normalizeVerdict(data.verdict),
|
|
120
|
+
action_type: String(data.action_type || ""),
|
|
121
|
+
reason: String(data.reason || ""),
|
|
122
|
+
confidence: Number(data.confidence ?? 0),
|
|
123
|
+
provider: String(data.provider || ""),
|
|
124
|
+
latency_ms: Number(data.latency_ms ?? 0),
|
|
125
|
+
tool_call_id: String(data.tool_call_id || ""),
|
|
126
|
+
on_chain: Boolean(data.on_chain)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function getJudgmentStatus(judgmentId, opts) {
|
|
130
|
+
const url = `${baseUrl(opts)}/api/v1/judge?tool_call_id=${encodeURIComponent(judgmentId)}`;
|
|
131
|
+
const data = await getJson(url, opts);
|
|
132
|
+
return {
|
|
133
|
+
status: normalizeStatus(data.status),
|
|
134
|
+
verdict: normalizeVerdict(data.verdict),
|
|
135
|
+
reason: String(data.reason || ""),
|
|
136
|
+
judgmentId: String(data.judgmentId || judgmentId),
|
|
137
|
+
onChain: Boolean(data.onChain),
|
|
138
|
+
cached: Boolean(data.cached),
|
|
139
|
+
responseTimeMs: Number(data.responseTimeMs ?? 0)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function riskEngineUrl(action, params, opts) {
|
|
143
|
+
const url = new URL(`${baseUrl(opts)}/api/risk-engine`);
|
|
144
|
+
url.searchParams.set("action", action);
|
|
145
|
+
for (const [k, v] of Object.entries(params)) {
|
|
146
|
+
if (v) url.searchParams.set(k, v);
|
|
147
|
+
}
|
|
148
|
+
return url.toString();
|
|
149
|
+
}
|
|
150
|
+
async function getToolCalls(maxCount, opts) {
|
|
151
|
+
return getJson(
|
|
152
|
+
riskEngineUrl("tool-calls", { limit: String(maxCount) }, opts),
|
|
153
|
+
opts
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
async function getOrgToolCalls(orgName, maxCount, opts) {
|
|
157
|
+
return getJson(
|
|
158
|
+
riskEngineUrl(
|
|
159
|
+
"org-tool-calls",
|
|
160
|
+
{ org: orgName, limit: String(maxCount) },
|
|
161
|
+
opts
|
|
162
|
+
),
|
|
163
|
+
opts
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
async function getAgentToolCalls(agentPubkey, maxCount, opts) {
|
|
167
|
+
return getJson(
|
|
168
|
+
riskEngineUrl(
|
|
169
|
+
"agent-tool-calls",
|
|
170
|
+
{ agent: agentPubkey, limit: String(maxCount) },
|
|
171
|
+
opts
|
|
172
|
+
),
|
|
173
|
+
opts
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
async function getToolCallCount(opts) {
|
|
177
|
+
const result = await getJson(
|
|
178
|
+
riskEngineUrl("tool-call-count", {}, opts),
|
|
179
|
+
opts
|
|
180
|
+
);
|
|
181
|
+
return typeof result === "number" ? result : Number(result ?? 0);
|
|
182
|
+
}
|
|
183
|
+
async function getToolCallFull(toolCallId, opts) {
|
|
184
|
+
return getJson(
|
|
185
|
+
riskEngineUrl("tool-call-full", { tool_call_id: toolCallId }, opts),
|
|
186
|
+
opts
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
async function getOrgTierInfo(orgName, opts) {
|
|
190
|
+
return getJson(
|
|
191
|
+
riskEngineUrl("org-tier-info", { org: orgName }, opts),
|
|
192
|
+
opts
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
async function getPendingHeldActions(orgName, maxCount, opts) {
|
|
196
|
+
const raw = await getJson(
|
|
197
|
+
riskEngineUrl(
|
|
198
|
+
"pending-held-actions",
|
|
199
|
+
{ org: orgName, limit: String(maxCount) },
|
|
200
|
+
opts
|
|
201
|
+
),
|
|
202
|
+
opts
|
|
203
|
+
);
|
|
204
|
+
return raw.map((h) => ({ ...h, verdict: normalizeVerdict(h.verdict) }));
|
|
205
|
+
}
|
|
206
|
+
async function getHeldActionReviews(orgName, maxCount, opts) {
|
|
207
|
+
return getJson(
|
|
208
|
+
riskEngineUrl(
|
|
209
|
+
"held-action-reviews",
|
|
210
|
+
{ org: orgName, limit: String(maxCount) },
|
|
211
|
+
opts
|
|
212
|
+
),
|
|
213
|
+
opts
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
function riskEnginePostUrl(opts) {
|
|
217
|
+
return `${baseUrl(opts)}/api/risk-engine`;
|
|
218
|
+
}
|
|
219
|
+
async function getAgentDetail(agentPubkey, opts) {
|
|
220
|
+
return postJson(
|
|
221
|
+
riskEnginePostUrl(opts),
|
|
222
|
+
{
|
|
223
|
+
action: "agent-detail-batch",
|
|
224
|
+
agent: agentPubkey
|
|
225
|
+
},
|
|
226
|
+
opts
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
async function getAgentPolicy(agentPubkey, opts) {
|
|
230
|
+
return postJson(
|
|
231
|
+
riskEnginePostUrl(opts),
|
|
232
|
+
{
|
|
233
|
+
action: "agent-policy-batch",
|
|
234
|
+
agent: agentPubkey
|
|
235
|
+
},
|
|
236
|
+
opts
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
async function getSafetyStats(opts) {
|
|
240
|
+
const url = `${baseUrl(opts)}/api/insurance?action=safety-stats`;
|
|
241
|
+
const result = await getJson(url, opts);
|
|
242
|
+
return result?.data || result;
|
|
243
|
+
}
|
|
244
|
+
export {
|
|
245
|
+
DEFAULT_ENDPOINT,
|
|
246
|
+
createAgent,
|
|
247
|
+
derivePublicKey,
|
|
248
|
+
generateKeyPair,
|
|
249
|
+
getAgentDetail,
|
|
250
|
+
getAgentPolicy,
|
|
251
|
+
getAgentToolCalls,
|
|
252
|
+
getHeldActionReviews,
|
|
253
|
+
getJudgmentStatus,
|
|
254
|
+
getOrgTierInfo,
|
|
255
|
+
getOrgToolCalls,
|
|
256
|
+
getPendingHeldActions,
|
|
257
|
+
getSafetyStats,
|
|
258
|
+
getToolCallCount,
|
|
259
|
+
getToolCallFull,
|
|
260
|
+
getToolCalls,
|
|
261
|
+
isValidPrivateKey,
|
|
262
|
+
judgeAction,
|
|
263
|
+
toPubkeyHex
|
|
264
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atbash/sdk",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "TypeScript SDK for the Atbash risk-engine / judge API",
|
|
5
|
+
"homepage": "https://atbash.ai",
|
|
6
|
+
"author": "Atbash",
|
|
7
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/index.cjs",
|
|
10
|
+
"module": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
30
|
+
"release": "npm version patch --no-git-tag-version && npm run build && npx npm@10 publish --access public"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.19.39",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.4.0"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"atbash",
|
|
39
|
+
"risk-engine",
|
|
40
|
+
"agent-safety",
|
|
41
|
+
"ai-safety"
|
|
42
|
+
]
|
|
43
|
+
}
|