@yobekasbah/sdk 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -0
- package/package.json +29 -0
- package/src/index.d.ts +147 -0
- package/src/index.js +701 -0
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# @kasbah/sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@kasbah/sdk)
|
|
4
|
+
[](https://bekasbah.com)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://bekasbah.com)
|
|
7
|
+
|
|
8
|
+
Official Node.js SDK for [KasbahOS](https://bekasbah.com) — govern any AI agent in 5 lines. Cryptographic receipts, 37 detection systems, drop-in proxy for OpenAI and Anthropic.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @kasbah/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 5-line quick start
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const Kasbah = require('@kasbah/sdk');
|
|
20
|
+
|
|
21
|
+
const k = new Kasbah({ apiKey: process.env.KASBAH_API_KEY });
|
|
22
|
+
|
|
23
|
+
const result = await k.govern({ prompt: userInput, agent: 'my-agent' });
|
|
24
|
+
if (result.verdict === 'DENY') throw new Error('Blocked: ' + result.threats.join(', '));
|
|
25
|
+
|
|
26
|
+
console.log(`Safe — proof: ${result.proof}`);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Drop-in OpenAI proxy
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const Kasbah = require('@kasbah/sdk');
|
|
33
|
+
const OpenAI = require('openai');
|
|
34
|
+
|
|
35
|
+
const kasbah = new Kasbah({ apiKey: process.env.KASBAH_API_KEY });
|
|
36
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
37
|
+
|
|
38
|
+
// Create a governed agent session
|
|
39
|
+
const session = await kasbah.createAgent({
|
|
40
|
+
agentId: 'research-bot',
|
|
41
|
+
passportId: process.env.KASBAH_PASSPORT_ID,
|
|
42
|
+
goal: 'Summarize recent papers on LLM safety',
|
|
43
|
+
manifest: { allowedTools: ['search', 'read_url'], maxTokens: 50000 },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Wrap OpenAI — every call is auto-governed
|
|
47
|
+
const governed = kasbah.wrapOpenAI(openai, session.sessionId);
|
|
48
|
+
|
|
49
|
+
// This call transparently goes through KasbahOS governance
|
|
50
|
+
const completion = await governed.chat.completions.create({
|
|
51
|
+
model: 'gpt-4o',
|
|
52
|
+
messages: [{ role: 'user', content: 'Summarize this paper...' }],
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Drop-in Anthropic proxy
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const Kasbah = require('@kasbah/sdk');
|
|
60
|
+
const Anthropic = require('@anthropic-ai/sdk');
|
|
61
|
+
|
|
62
|
+
const kasbah = new Kasbah({ apiKey: process.env.KASBAH_API_KEY });
|
|
63
|
+
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
64
|
+
|
|
65
|
+
const session = await kasbah.createAgent({
|
|
66
|
+
agentId: 'claude-agent',
|
|
67
|
+
passportId: process.env.KASBAH_PASSPORT_ID,
|
|
68
|
+
goal: 'Answer customer questions safely',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const governed = kasbah.wrapAnthropic(anthropic, session.sessionId);
|
|
72
|
+
const message = await governed.messages.create({ model: 'claude-opus-4-5', ... });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Per-agent passports
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// Issue a passport for an agent — limits what it can do
|
|
79
|
+
const passport = await k.passport.issue({
|
|
80
|
+
agentId: 'data-pipeline',
|
|
81
|
+
capabilities: ['read_database', 'write_report'],
|
|
82
|
+
trustLevel: 'medium',
|
|
83
|
+
spendPolicy: { maxTokensPerDay: 100000, hardBlockOnExceed: true },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Every govern() call tracks passport trust score
|
|
87
|
+
const result = await k.govern({
|
|
88
|
+
prompt: userInput,
|
|
89
|
+
passportId: passport.passportId,
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Scan for secrets and threats
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
const scan = await k.scan(codeSnippet);
|
|
97
|
+
if (!scan.safe) {
|
|
98
|
+
console.log('Threats:', scan.threats);
|
|
99
|
+
// e.g. ['AWS Access Key', 'Hardcoded Password']
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Verify a governance proof (auditors)
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
// Zero-trust: verify any governance decision mathematically, no KasbahOS trust needed
|
|
107
|
+
const verified = await fetch('https://api.bekasbah.com/v1/zk/verify', {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
body: JSON.stringify({ proof: result.proof, publicInput: { verdict: 'ALLOW' } }),
|
|
110
|
+
});
|
|
111
|
+
// { valid: true, rulesVerified: ['no_injection', 'no_pii', 'no_secrets'] }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## All methods
|
|
115
|
+
|
|
116
|
+
| Method | Description |
|
|
117
|
+
|--------|-------------|
|
|
118
|
+
| `govern(opts)` | Govern a prompt/response through all 37 systems |
|
|
119
|
+
| `scan(text)` | Lightweight threat scan |
|
|
120
|
+
| `explain(prompt)` | Explain why a prompt would be blocked |
|
|
121
|
+
| `bells(text)` | Bell's Inequality AI detection (CHSH S-value) |
|
|
122
|
+
| `maqasid(text)` | 5-pillar ethics scoring |
|
|
123
|
+
| `rtp(text)` | RTP governance pipeline |
|
|
124
|
+
| `passport.issue(opts)` | Create agent passport |
|
|
125
|
+
| `passport.revoke(id)` | Revoke a passport |
|
|
126
|
+
| `delegation.issue(opts)` | Issue delegation token (agent A acts as B) |
|
|
127
|
+
| `honeytoken.deploy(service)` | Plant a canary credential |
|
|
128
|
+
| `honeytoken.check(text)` | Check text for leaked canaries |
|
|
129
|
+
| `createAgent(opts)` | Create governed agent session |
|
|
130
|
+
| `governTool(sessionId, tool, args)` | Govern a tool call before executing |
|
|
131
|
+
| `scanToolResult(sessionId, tool, output)` | Scan tool output for injections |
|
|
132
|
+
| `wrapOpenAI(client, sessionId)` | Auto-govern all OpenAI calls |
|
|
133
|
+
| `wrapAnthropic(client, sessionId)` | Auto-govern all Anthropic calls |
|
|
134
|
+
| `proxyOpenAI(body, key)` | Proxy an OpenAI completion |
|
|
135
|
+
| `proxyAnthropic(body, key)` | Proxy an Anthropic message |
|
|
136
|
+
| `getUsage(passportId)` | Token/cost usage for a passport |
|
|
137
|
+
| `setBudget(passportId, policy)` | Set hard token/cost limits |
|
|
138
|
+
| `health()` | API health check |
|
|
139
|
+
| `stats()` | Governance statistics |
|
|
140
|
+
| `auditChain()` | Tamper-evident audit ledger |
|
|
141
|
+
|
|
142
|
+
## TypeScript
|
|
143
|
+
|
|
144
|
+
Full TypeScript types are included:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import Kasbah, { GovernResult, Verdict, Passport, AgentSession } from '@kasbah/sdk';
|
|
148
|
+
|
|
149
|
+
const k = new Kasbah({ apiKey: process.env.KASBAH_API_KEY! });
|
|
150
|
+
|
|
151
|
+
const result: GovernResult = await k.govern({ prompt: 'Hello world' });
|
|
152
|
+
const verdict: Verdict = result.verdict; // 'ALLOW' | 'WARN' | 'DENY'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Configuration
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
const k = new Kasbah({
|
|
159
|
+
apiKey: process.env.KASBAH_API_KEY, // your API key
|
|
160
|
+
baseUrl: 'http://your-server:8788', // self-hosted instance
|
|
161
|
+
timeout: 10000, // ms (default: 10000)
|
|
162
|
+
autoRetry: true, // retry on 5xx (default: true)
|
|
163
|
+
passportId: 'pp_default_agent', // default passport for all govern() calls
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Self-hosted
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
docker run -p 8788:8788 kasbahguard/kasbah-os:latest
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Then:
|
|
174
|
+
```js
|
|
175
|
+
const k = new Kasbah({ baseUrl: 'http://localhost:8788' });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
[bekasbah.com](https://bekasbah.com) | [GitHub](https://github.com/kasbah-guard) | [Docs](https://bekasbah.com/public/glassbox.html)
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yobekasbah/sdk",
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "KasbahOS — govern any AI agent in one line. Cryptographic receipts, multi-layer threat detection, drop-in middleware for OpenAI, Anthropic, LangChain.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"src/index.js",
|
|
9
|
+
"src/index.d.ts",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node src/test.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai", "security", "governance", "llm", "openai", "anthropic",
|
|
17
|
+
"langchain", "prompt-injection", "kasbah", "kasbah-os", "receipt",
|
|
18
|
+
"audit", "compliance", "hipaa", "soc2", "middleware"
|
|
19
|
+
],
|
|
20
|
+
"author": "Kasbah Guard <hello@bekasbah.com>",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/kasbah-guard/kasbah"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://bekasbah.com",
|
|
27
|
+
"bugs": { "url": "https://github.com/kasbah-guard/kasbah/issues" },
|
|
28
|
+
"engines": { "node": ">=18.0.0" }
|
|
29
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KasbahOS SDK — TypeScript definitions
|
|
3
|
+
* @version 6.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface GovernOptions {
|
|
7
|
+
agent?: string;
|
|
8
|
+
passportId?: string;
|
|
9
|
+
context?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface GovernResult {
|
|
13
|
+
verdict: 'ALLOW' | 'WARN' | 'DENY';
|
|
14
|
+
score: number;
|
|
15
|
+
threats: string[];
|
|
16
|
+
receipt: string;
|
|
17
|
+
receiptPayload?: string;
|
|
18
|
+
ticket?: { id: string; expiresAt: number; ttlMs: number };
|
|
19
|
+
latency_ms: number;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
requestId: string;
|
|
22
|
+
passport?: Record<string, unknown>;
|
|
23
|
+
pii?: { detected: boolean; count: number; riskScore: number };
|
|
24
|
+
steg?: { detected: boolean; method: string; confidence: number };
|
|
25
|
+
sentinel?: { score: number; threats: string[]; deontic?: string };
|
|
26
|
+
secrets?: { found: boolean; count: number; types: string[] };
|
|
27
|
+
intent?: { intent: string; confidence: number; riskLevel: string };
|
|
28
|
+
manipulation?: { patterns: number; risk: string };
|
|
29
|
+
bells?: { sValue: number; isAI: boolean };
|
|
30
|
+
maqasid?: { compliance: number; concerns: string[] };
|
|
31
|
+
rtp?: { intent: string; modulatorScore: number; obfuscationScore: number };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ScanResult {
|
|
35
|
+
verdict: 'ALLOW' | 'WARN' | 'DENY';
|
|
36
|
+
threats: string[];
|
|
37
|
+
risk_score: number;
|
|
38
|
+
safe: boolean;
|
|
39
|
+
latency_ms: number;
|
|
40
|
+
timestamp: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface HealthResult {
|
|
44
|
+
status: string;
|
|
45
|
+
version: string;
|
|
46
|
+
engine: string;
|
|
47
|
+
uptime_s: string;
|
|
48
|
+
persistence: 'redis' | 'memory';
|
|
49
|
+
systems: { active: number; total: number; wasm: boolean };
|
|
50
|
+
timestamp: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ReceiptVerification {
|
|
54
|
+
valid: boolean;
|
|
55
|
+
verdict?: string;
|
|
56
|
+
requestId?: string;
|
|
57
|
+
timestamp?: number;
|
|
58
|
+
error?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface PassportResult {
|
|
62
|
+
passportId: string;
|
|
63
|
+
agentId: string;
|
|
64
|
+
publicKey: string;
|
|
65
|
+
privateKey: string;
|
|
66
|
+
issuedAt: string;
|
|
67
|
+
warning: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface StreamHandle {
|
|
71
|
+
close(): void;
|
|
72
|
+
on(event: 'error', handler: (err: Error) => void): StreamHandle;
|
|
73
|
+
on(event: 'open', handler: () => void): StreamHandle;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ProxyResult {
|
|
77
|
+
id: string;
|
|
78
|
+
object: string;
|
|
79
|
+
choices: unknown[];
|
|
80
|
+
usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };
|
|
81
|
+
_kasbahReceipt?: string;
|
|
82
|
+
_kasbahVerdict?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface UsageResult {
|
|
86
|
+
passportId: string;
|
|
87
|
+
spendLedger: { tokens1m: number; tokens24h: number; costCents24h: number };
|
|
88
|
+
watcherState: { band: string; brittleness: number; tokensPerHour: number; costPerHour: number };
|
|
89
|
+
timestamp: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface BudgetPolicy {
|
|
93
|
+
maxTokensPerMin?: number;
|
|
94
|
+
maxTokensPerDay?: number;
|
|
95
|
+
maxCostCentsPerDay?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export declare class KasbahError extends Error {
|
|
99
|
+
statusCode?: number;
|
|
100
|
+
verdict?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export declare class Kasbah {
|
|
104
|
+
constructor(options: { apiKey: string; baseUrl?: string; timeout?: number });
|
|
105
|
+
|
|
106
|
+
/** Govern any prompt — returns verdict, receipt, threats */
|
|
107
|
+
govern(prompt: string | { prompt: string; [key: string]: unknown }, options?: GovernOptions): Promise<GovernResult>;
|
|
108
|
+
|
|
109
|
+
/** Lightweight text scan without full policy gate */
|
|
110
|
+
scan(text: string): Promise<ScanResult>;
|
|
111
|
+
|
|
112
|
+
/** Check API health */
|
|
113
|
+
health(): Promise<HealthResult>;
|
|
114
|
+
|
|
115
|
+
/** Verify a governance receipt offline — no API call needed */
|
|
116
|
+
verifyReceipt(receipt: string, payload?: string): ReceiptVerification;
|
|
117
|
+
|
|
118
|
+
/** Issue a Governance Passport for an agent */
|
|
119
|
+
issuePassport(agentId: string, capabilities?: string[]): Promise<PassportResult>;
|
|
120
|
+
|
|
121
|
+
/** Stream real-time governance events via SSE */
|
|
122
|
+
stream(onEvent: (event: GovernResult) => void): StreamHandle;
|
|
123
|
+
|
|
124
|
+
/** Proxy an OpenAI chat completion through Kasbah governance */
|
|
125
|
+
proxyOpenAI(body: Record<string, unknown>, upstreamKey: string, options?: GovernOptions): Promise<ProxyResult>;
|
|
126
|
+
|
|
127
|
+
/** Proxy an Anthropic messages call through Kasbah governance */
|
|
128
|
+
proxyAnthropic(body: Record<string, unknown>, upstreamKey: string, options?: GovernOptions): Promise<ProxyResult>;
|
|
129
|
+
|
|
130
|
+
/** Get token/cost usage for a passport */
|
|
131
|
+
getUsage(passportId: string): Promise<UsageResult>;
|
|
132
|
+
|
|
133
|
+
/** Set token/cost budget policy for a passport */
|
|
134
|
+
setBudget(passportId: string, policy: BudgetPolicy): Promise<{ ok: boolean; passportId: string; policy: BudgetPolicy }>;
|
|
135
|
+
|
|
136
|
+
/** Get current budget policy for a passport */
|
|
137
|
+
getBudget(passportId: string): Promise<{ passportId: string; policy: BudgetPolicy; usage: UsageResult }>;
|
|
138
|
+
|
|
139
|
+
readonly passport: {
|
|
140
|
+
issue(options: { agentId?: string; capabilities?: string[] }): Promise<PassportResult>;
|
|
141
|
+
get(passportId: string): Promise<Record<string, unknown>>;
|
|
142
|
+
revoke(passportId: string): Promise<{ ok: boolean }>;
|
|
143
|
+
list(): Promise<{ passports: PassportResult[]; total: number }>;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default Kasbah;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Kasbah Guard SDK — Official Node.js Client
|
|
5
|
+
*
|
|
6
|
+
* Govern AI agents from any Node.js app. Wraps the Kasbah API
|
|
7
|
+
* with automatic passport management, retry logic, and streaming.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* const Kasbah = require('@yobekasbah/sdk');
|
|
11
|
+
* const k = new Kasbah({ apiKey: 'kg_...', baseUrl: 'https://api.bekasbah.com' });
|
|
12
|
+
*
|
|
13
|
+
* // Govern a prompt
|
|
14
|
+
* const result = await k.govern({ prompt: 'User message here' });
|
|
15
|
+
* if (result.verdict === 'DENY') throw new Error('Blocked: ' + result.threats.join(', '));
|
|
16
|
+
*
|
|
17
|
+
* // Issue a passport for an agent
|
|
18
|
+
* const passport = await k.passport.issue({ agentId: 'my-agent', capabilities: ['search'] });
|
|
19
|
+
*
|
|
20
|
+
* // Check system health
|
|
21
|
+
* const health = await k.health();
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const http = require('http');
|
|
25
|
+
const https = require('https');
|
|
26
|
+
const url = require('url');
|
|
27
|
+
|
|
28
|
+
const SDK_VERSION = '6.0.0';
|
|
29
|
+
|
|
30
|
+
class KasbahError extends Error {
|
|
31
|
+
constructor(message, status, body) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = 'KasbahError';
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.body = body;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class Kasbah {
|
|
40
|
+
/**
|
|
41
|
+
* @param {object} opts
|
|
42
|
+
* @param {string} opts.apiKey — API key (default: 'kg_desktop_builtin')
|
|
43
|
+
* @param {string} opts.baseUrl — API base URL (default: 'https://api.bekasbah.com')
|
|
44
|
+
* @param {number} opts.timeout — Request timeout ms (default: 10000)
|
|
45
|
+
* @param {boolean} opts.autoRetry — Retry on 5xx with backoff (default: true)
|
|
46
|
+
*/
|
|
47
|
+
constructor(opts = {}) {
|
|
48
|
+
this.apiKey = opts.apiKey || 'kg_desktop_builtin';
|
|
49
|
+
this.baseUrl = (opts.baseUrl || 'https://api.bekasbah.com').replace(/\/$/, '');
|
|
50
|
+
this.timeout = opts.timeout || 10_000;
|
|
51
|
+
this.autoRetry = opts.autoRetry !== false;
|
|
52
|
+
this._passportId = opts.passportId || null;
|
|
53
|
+
|
|
54
|
+
// Sub-clients
|
|
55
|
+
this.passport = new PassportClient(this);
|
|
56
|
+
this.delegation = new DelegationClient(this);
|
|
57
|
+
this.honeytoken = new HoneytokenClient(this);
|
|
58
|
+
this.spend = new SpendClient(this);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Core govern ─────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Govern a prompt/response through all 17 frontier systems.
|
|
65
|
+
* @param {object} body — { prompt?, response?, passportId?, delegationTokenId?, agent? }
|
|
66
|
+
* @returns {Promise<GovernResult>}
|
|
67
|
+
*/
|
|
68
|
+
async govern(body = {}) {
|
|
69
|
+
if (this._passportId && !body.passportId) body.passportId = this._passportId;
|
|
70
|
+
return this._post('/v1/govern', body);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Scan text for threats (lightweight — no passport/delegation processing).
|
|
75
|
+
* @param {string} text
|
|
76
|
+
* @returns {Promise<ScanResult>}
|
|
77
|
+
*/
|
|
78
|
+
async scan(text) {
|
|
79
|
+
const raw = await this._post('/v1/scan', { text });
|
|
80
|
+
// /v1/scan returns {threats, risk, risk_score, safe, latency_ms} — normalize
|
|
81
|
+
// to {decision, verdict, blocked, risk, threats, ...raw} so callers can rely on it.
|
|
82
|
+
const threats = raw.threats || raw.violations || [];
|
|
83
|
+
const risk = raw.risk_score ?? raw.riskScore ?? raw.risk ?? 0;
|
|
84
|
+
const safe = raw.safe !== false;
|
|
85
|
+
const verdict = raw.verdict || raw.decision || (safe ? 'ALLOW' : (risk < 0.7 ? 'WARN' : 'DENY'));
|
|
86
|
+
return Object.assign({}, raw, {
|
|
87
|
+
verdict,
|
|
88
|
+
decision: verdict,
|
|
89
|
+
blocked: verdict === 'DENY',
|
|
90
|
+
risk,
|
|
91
|
+
threats
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Explain why a prompt would be blocked/warned.
|
|
97
|
+
* @param {string} prompt
|
|
98
|
+
* @returns {Promise<ExplainResult>}
|
|
99
|
+
*/
|
|
100
|
+
async explain(prompt) {
|
|
101
|
+
return this._post('/v1/explain', { prompt });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Evaluate an action against compliance + biometric-surveillance rule packs
|
|
106
|
+
* BEFORE it runs. Packs default to HIPAA, GDPR, SOC2 and BIOMETRIC_GUARD —
|
|
107
|
+
* the latter blocks facial-micro-expression / silent-speech / subvocal / BCI /
|
|
108
|
+
* QAI intent-surveillance vectors.
|
|
109
|
+
* @param {object} opts - { verb, target?, content?, packs? }
|
|
110
|
+
* @returns {Promise<{decision, reason, violations}>}
|
|
111
|
+
*/
|
|
112
|
+
async checkCompliance({ verb, target = '', content = '', packs = 'HIPAA,GDPR,SOC2,BIOMETRIC_GUARD' } = {}) {
|
|
113
|
+
if (!verb) throw new KasbahError('checkCompliance requires a verb');
|
|
114
|
+
return this._post('/v1/algorithms/policy-engine/run', { verb, target, content, packs });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Defensive check: is this action a silent-speech / biometric surveillance
|
|
119
|
+
* attempt? Returns DENY (with cited BIOMETRIC_GUARD rule) if so. Kasbah never
|
|
120
|
+
* captures or analyzes facial/biometric data — it blocks those attempts.
|
|
121
|
+
* @param {object} opts - { verb?, target?, content? }
|
|
122
|
+
*/
|
|
123
|
+
async guardBiometric({ verb = 'CAPTURE_VIDEO', target = 'facial_expression', content = '' } = {}) {
|
|
124
|
+
return this._post('/v1/algorithms/policy-engine/run', { verb, target, content, packs: 'BIOMETRIC_GUARD' });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Governance Proxy (wrap an LLM turn) ─────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Govern a full LLM turn: pass message history + proposed tool calls, get a
|
|
131
|
+
* bounded advisory verdict (CLEAR / WARN / REQUIRE_APPROVAL) plus authoritative
|
|
132
|
+
* DENY for forbidden verbs / non-whitelisted domains. Includes multi-step
|
|
133
|
+
* attack-trajectory detection and a forward-secure audit receipt.
|
|
134
|
+
*/
|
|
135
|
+
async governTurn({ messages = [], tools = [], clearance = 2 } = {}) {
|
|
136
|
+
return this._post('/v1/proxy/turn', { messages, tools, clearance });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** List actions queued for human approval (REQUIRE_APPROVAL). */
|
|
140
|
+
async pendingApprovals() { return this._request('GET', '/v1/proxy/pending'); }
|
|
141
|
+
|
|
142
|
+
/** Approve a queued action (requires OPERATOR clearance ≥ 3). */
|
|
143
|
+
async approveAction(actionId, clearance = 3, approver = 'sdk') {
|
|
144
|
+
return this._post(`/v1/proxy/approve/${encodeURIComponent(actionId)}`, { clearance, approver });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Run a single advisory analyzer directly (detectgpt, intent, conformal, …). */
|
|
148
|
+
async advisory(module, payload = {}) {
|
|
149
|
+
return this._post(`/v1/advisory/${module}`, payload);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Access the kasbah-os Python engine (defensive live; offensive introspect-only). */
|
|
153
|
+
async kasbahOS(action, params = {}) {
|
|
154
|
+
if (action === 'govern') return this._post('/v1/kasbah-os/governance/turn', params);
|
|
155
|
+
const path = action === 'audit-verify' ? 'audit/verify' : action === 'offensive-info' ? 'offensive/info' : 'health';
|
|
156
|
+
return this._request('GET', `/v1/kasbah-os/${path}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── New v4.0 endpoints ──────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Run Bell's Inequality AI text detection (CHSH S-value).
|
|
163
|
+
* S > 2.0 = AI-generated text with non-local semantic correlations.
|
|
164
|
+
*/
|
|
165
|
+
async bells(text) {
|
|
166
|
+
return this._post('/v1/bells', { text });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Run Maqasid Ethics Framework (5-pillar Islamic ethics scoring).
|
|
171
|
+
*/
|
|
172
|
+
async maqasid(text) {
|
|
173
|
+
return this._post('/v1/maqasid', { text });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Run RTP pipeline (f0–f5 governance stages, no side-effects).
|
|
178
|
+
*/
|
|
179
|
+
async rtp(text, passportId = null) {
|
|
180
|
+
return this._post('/v1/rtp', { text, passportId });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Run Witt-Crystalline Cohomology Analysis.
|
|
185
|
+
* Novel mathematical framework: de Rham + crystalline + Witt vectors.
|
|
186
|
+
* Computes [ω] ∈ H^k → W(ω) = η → lim σ_n (perfect governance state).
|
|
187
|
+
*/
|
|
188
|
+
async witt(data) {
|
|
189
|
+
return this._post('/v1/witt/analyze', { data });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Run Nexus unified engine (RTP + Kernel + Bell's + Detector in one call).
|
|
194
|
+
*/
|
|
195
|
+
async nexus(text) {
|
|
196
|
+
return this._post('/v1/nexus', { text });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Verify and consume an execution ticket.
|
|
201
|
+
*/
|
|
202
|
+
async verifyTicket(ticketId, hmac) {
|
|
203
|
+
return this._post('/v1/ticket/verify', { ticketId, hmac });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── System ──────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
async health() { return this._get('/v1/health'); }
|
|
209
|
+
async stats() { return this._get('/v1/stats'); }
|
|
210
|
+
async audit(n=50) { return this._get(`/v1/audit?limit=${n}`); }
|
|
211
|
+
async integrity() { return this._get('/v1/integrity'); }
|
|
212
|
+
async forecast() { return this._get('/v1/forecast'); }
|
|
213
|
+
async swarm() { return this._get('/v1/swarm/status'); }
|
|
214
|
+
async auditChain() { return this._get('/v1/audit/chain'); }
|
|
215
|
+
async policy() { return this._get('/v1/policy/active'); }
|
|
216
|
+
|
|
217
|
+
async setPolicy(policy) { return this._request('PUT', '/v1/policy/active', policy); }
|
|
218
|
+
|
|
219
|
+
// ── Products (all 16 engines) ─────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
async productsStatus() { return this._get('/v1/products/status'); }
|
|
222
|
+
async productScan(product, t) { return this._post(`/v1/products/${product}`, { text: t }); }
|
|
223
|
+
|
|
224
|
+
// ── Trinity ───────────────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
async forgeKSI(text) { return this._post('/v1/forge/ksi', { text }); }
|
|
227
|
+
async forgeConfig() { return this._get('/v1/forge/config'); }
|
|
228
|
+
async nexusCommand(cmd) { return this._post('/v1/nexus/command', { command: cmd }); }
|
|
229
|
+
async nexusStatus() { return this._get('/v1/nexus/status'); }
|
|
230
|
+
async cortexExec(cmd) { return this._post('/v1/cortex/exec', { command: cmd }); }
|
|
231
|
+
|
|
232
|
+
// ── Crypto ────────────────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
async shamirSplit(s, n, k) { return this._post('/v1/crypto/shamir/split', { secret: s, n, k }); }
|
|
235
|
+
async shamirCombine(shares) { return this._post('/v1/crypto/shamir/combine', { shares }); }
|
|
236
|
+
async mmrAppend(data) { return this._post('/v1/crypto/mmr/append', { data }); }
|
|
237
|
+
async vdfEval(input, it) { return this._post('/v1/crypto/vdf/eval', { input, iterations: it }); }
|
|
238
|
+
|
|
239
|
+
// ── Silent speech + Zero-trust audit ──────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
async silentSpeech(opts) { return this._post('/v1/silent-speech', opts); }
|
|
242
|
+
async auditReceipt(opts) { return this._post('/v1/audit/receipt', opts); }
|
|
243
|
+
|
|
244
|
+
// ── Receipts (v3) ─────────────────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
async signReceipt(opts) { return this._post('/v1/sign', opts); }
|
|
247
|
+
async signReceiptV3(opts) { return this._post('/v1/sign?v=3', opts); }
|
|
248
|
+
async verifyReceipt(opts) { return this._post('/v1/receipt/verify', opts); }
|
|
249
|
+
async verifyReceiptBatch(opts) { return this._post('/v1/receipt/verify-batch', opts); }
|
|
250
|
+
|
|
251
|
+
// ── KasbahOS Python core ──────────────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
async kasbahOSIngest(opts) { return this._post('/v1/kasbah-os/ingest', opts); }
|
|
254
|
+
async kasbahOSHealth() { return this._get('/v1/kasbah-os/health'); }
|
|
255
|
+
async kasbahOSRetrieve(id) { return this._post('/v1/kasbah-os/retrieve', { receipt_id: id }); }
|
|
256
|
+
|
|
257
|
+
// ── v4.0 Mythos-Class Engines ────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
// Crypto
|
|
260
|
+
async kasbahOSKeygen() { return this._post('/v1/kasbah-os/crypto/keygen'); }
|
|
261
|
+
async kasbahOSSign(opts) { return this._post('/v1/kasbah-os/crypto/sign', opts); }
|
|
262
|
+
async kasbahOSVerify(opts) { return this._post('/v1/kasbah-os/crypto/verify', opts); }
|
|
263
|
+
async kasbahOSPubkey(seedHex) { return this._post('/v1/kasbah-os/crypto/pubkey', { seed_hex: seedHex }); }
|
|
264
|
+
|
|
265
|
+
// Audit / Zero-Trust
|
|
266
|
+
async kasbahOSAuditEvent(opts) { return this._post('/v1/kasbah-os/audit/event', opts); }
|
|
267
|
+
async kasbahOSAuditReport() { return this._get('/v1/kasbah-os/audit/report'); }
|
|
268
|
+
async kasbahOSAuditChain() { return this._get('/v1/kasbah-os/audit/chain'); }
|
|
269
|
+
async kasbahOSAuditChainImport(chain) { return this._post('/v1/kasbah-os/audit/chain/import', { chain }); }
|
|
270
|
+
async kasbahOSAuditTrust(engine) { return this._post('/v1/kasbah-os/audit/trust', { engine: engine || 'bridge' }); }
|
|
271
|
+
|
|
272
|
+
// Merkle
|
|
273
|
+
async kasbahOSMerkleBuild(entries) { return this._post('/v1/kasbah-os/merkle/build', { entries }); }
|
|
274
|
+
async kasbahOSMerkleVerify(opts) { return this._post('/v1/kasbah-os/merkle/verify', opts); }
|
|
275
|
+
|
|
276
|
+
// Binary Analysis
|
|
277
|
+
async kasbahOSBinaryAnalyze(hex, sampleId) {
|
|
278
|
+
return this._post('/v1/kasbah-os/binary/analyze', { binary_hex: hex, sample_id: sampleId || 'sdk' });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Pipeline / Orchestration
|
|
282
|
+
async kasbahOSPipelineRun(hex, targetInfo) {
|
|
283
|
+
return this._post('/v1/kasbah-os/pipeline/run', { binary_hex: hex, target_info: targetInfo || {} });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// System
|
|
287
|
+
async kasbahOSModules() { return this._get('/v1/kasbah-os/modules'); }
|
|
288
|
+
async kasbahOSPing() { return this._get('/v1/kasbah-os/ping'); }
|
|
289
|
+
|
|
290
|
+
// ── Governance Proxy ─────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
async kasbahOSGovernanceProcess(messages, tools, clearance) {
|
|
293
|
+
return this._post('/v1/kasbah-os/governance/process', {
|
|
294
|
+
messages: messages || [], tools: tools || [], clearance: clearance ?? 2
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async kasbahOSGovernanceApprove(actionId) {
|
|
298
|
+
return this._post('/v1/kasbah-os/governance/approve/' + actionId);
|
|
299
|
+
}
|
|
300
|
+
async kasbahOSGovernanceHealth() { return this._get('/v1/kasbah-os/governance/health'); }
|
|
301
|
+
|
|
302
|
+
// ── Unified pipeline ──────────────────────────────────────────────────────
|
|
303
|
+
|
|
304
|
+
async pipelineUnified(text) { return this._post('/v1/pipeline/unified', { text }); }
|
|
305
|
+
|
|
306
|
+
// ── Keys ──────────────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
async keys() { return this._get('/v1/keys'); }
|
|
309
|
+
async wellKnownKeys() { return this._get('/.well-known/kasbah-keys.json'); }
|
|
310
|
+
async models() { return this._get('/v1/models'); }
|
|
311
|
+
|
|
312
|
+
// ── Multi-MCP Governance ──────────────────────────────────────────────────
|
|
313
|
+
|
|
314
|
+
async mcpServers() { return this._get('/v1/mcp/servers'); }
|
|
315
|
+
async mcpCreateTeam(name, admin) { return this._post('/v1/mcp/teams', { name, adminPassportId: admin }); }
|
|
316
|
+
async mcpAddMember(teamId, pid) { return this._post(`/v1/mcp/teams/${teamId}/members`, { passportId: pid }); }
|
|
317
|
+
async mcpAddServer(teamId, mcp) { return this._post(`/v1/mcp/teams/${teamId}/mcp`, { mcpServer: mcp }); }
|
|
318
|
+
async mcpPolicies() { return this._get('/v1/mcp/policies'); }
|
|
319
|
+
async mcpApplyPolicy(teamId, pol){ return this._post(`/v1/mcp/teams/${teamId}/policies`, { policyId: pol }); }
|
|
320
|
+
|
|
321
|
+
// ── Proxy helpers ───────────────────────────────────────────────────────
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Proxy an OpenAI chat completion through Kasbah governance.
|
|
325
|
+
* Drop-in replacement for OpenAI's /v1/chat/completions
|
|
326
|
+
* @param {object} body - OpenAI chat completion request body
|
|
327
|
+
* @param {string} upstreamKey - Your OpenAI API key
|
|
328
|
+
* @param {object} [options]
|
|
329
|
+
* @returns OpenAI-compatible response with _kasbahReceipt captured
|
|
330
|
+
*/
|
|
331
|
+
async proxyOpenAI(body, upstreamKey, options = {}) {
|
|
332
|
+
const extraHeaders = { 'Authorization': `Bearer ${upstreamKey}` };
|
|
333
|
+
const { body: json, headers } = await this._requestRaw(
|
|
334
|
+
'POST', '/v1/proxy/openai/v1/chat/completions', body, extraHeaders
|
|
335
|
+
);
|
|
336
|
+
return { ...json, _kasbahReceipt: headers['x-kasbah-receipt'] || null };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Proxy an Anthropic messages call through Kasbah governance.
|
|
341
|
+
* Drop-in replacement for Anthropic's /v1/messages
|
|
342
|
+
* @param {object} body - Anthropic messages request body
|
|
343
|
+
* @param {string} upstreamKey - Your Anthropic API key
|
|
344
|
+
* @param {object} [options]
|
|
345
|
+
* @returns Anthropic-compatible response with _kasbahReceipt captured
|
|
346
|
+
*/
|
|
347
|
+
async proxyAnthropic(body, upstreamKey, options = {}) {
|
|
348
|
+
const extraHeaders = { 'x-api-key': upstreamKey };
|
|
349
|
+
const { body: json, headers } = await this._requestRaw(
|
|
350
|
+
'POST', '/v1/proxy/anthropic/v1/messages', body, extraHeaders
|
|
351
|
+
);
|
|
352
|
+
return { ...json, _kasbahReceipt: headers['x-kasbah-receipt'] || null };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ── Budget management ───────────────────────────────────────────────────
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Get token/cost usage for a passport.
|
|
359
|
+
* @param {string} passportId
|
|
360
|
+
*/
|
|
361
|
+
async getUsage(passportId) {
|
|
362
|
+
return this._request('GET', `/v1/usage/${passportId}`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Set token/cost budget policy for a passport.
|
|
367
|
+
* @param {string} passportId
|
|
368
|
+
* @param {object} policy - { maxTokensPerMin?, maxTokensPerDay?, maxCostCentsPerDay? }
|
|
369
|
+
*/
|
|
370
|
+
async setBudget(passportId, policy) {
|
|
371
|
+
return this._request('POST', `/v1/budgets/${passportId}`, policy);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get current budget policy for a passport.
|
|
376
|
+
* @param {string} passportId
|
|
377
|
+
*/
|
|
378
|
+
async getBudget(passportId) {
|
|
379
|
+
return this._request('GET', `/v1/budgets/${passportId}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ── Agentic Layer ───────────────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Create a governed agent session.
|
|
386
|
+
* Returns { sessionId, agentId, status, manifest, ... }
|
|
387
|
+
* @param {object} opts
|
|
388
|
+
* @param {string} opts.agentId - human-readable agent name
|
|
389
|
+
* @param {string} opts.passportId - Kasbah passport for this agent
|
|
390
|
+
* @param {string} opts.goal - agent objective (logged)
|
|
391
|
+
* @param {object} opts.manifest - { allowedTools, maxTokens, maxCostCents, ... }
|
|
392
|
+
*/
|
|
393
|
+
async createAgent(opts = {}) {
|
|
394
|
+
return this._request('POST', '/v1/agents', opts);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Govern a tool call for an active session.
|
|
399
|
+
* Call this BEFORE executing any tool.
|
|
400
|
+
* Returns { ok, verdict, reason?, receipt?, riskScore }
|
|
401
|
+
* @param {string} sessionId
|
|
402
|
+
* @param {string} tool - tool name, e.g. 'bash', 'write_file'
|
|
403
|
+
* @param {object} args - raw args object
|
|
404
|
+
* @param {string} argsText - flattened text for content scanning
|
|
405
|
+
*/
|
|
406
|
+
async governTool(sessionId, tool, args = {}, argsText = '') {
|
|
407
|
+
return this._request('POST', `/v1/agents/${sessionId}/tool`, { tool, args, argsText });
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Scan a tool result for injected instructions.
|
|
412
|
+
* Call this AFTER receiving a tool result, before feeding it back to the agent.
|
|
413
|
+
* Returns { ok, verdict?, reason?, riskScore }
|
|
414
|
+
* @param {string} sessionId
|
|
415
|
+
* @param {string} tool
|
|
416
|
+
* @param {string} result - raw tool output text
|
|
417
|
+
*/
|
|
418
|
+
async scanToolResult(sessionId, tool, result) {
|
|
419
|
+
return this._request('POST', `/v1/agents/${sessionId}/tool-result`, { tool, result });
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Record a conversation turn for budget and chain tracking.
|
|
424
|
+
* @param {string} sessionId
|
|
425
|
+
* @param {string} role - 'user' | 'assistant' | 'system' | 'tool'
|
|
426
|
+
* @param {string} content
|
|
427
|
+
* @param {number} tokens - token count for this turn
|
|
428
|
+
* @param {number} costCents - cost in cents for this turn
|
|
429
|
+
*/
|
|
430
|
+
async recordTurn(sessionId, role, content, tokens = 0, costCents = 0) {
|
|
431
|
+
return this._request('POST', `/v1/agents/${sessionId}/turn`, { role, content, tokens, costCents });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Get the current state of a governed session.
|
|
436
|
+
* @param {string} sessionId
|
|
437
|
+
*/
|
|
438
|
+
async getAgent(sessionId) {
|
|
439
|
+
return this._request('GET', `/v1/agents/${sessionId}`);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* List all active governed sessions.
|
|
444
|
+
*/
|
|
445
|
+
async listAgents() {
|
|
446
|
+
return this._request('GET', '/v1/agents');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Emergency kill: immediately stop a governed agent session.
|
|
451
|
+
* @param {string} sessionId
|
|
452
|
+
* @param {string} reason
|
|
453
|
+
*/
|
|
454
|
+
async killAgent(sessionId, reason = 'manual') {
|
|
455
|
+
return this._request('DELETE', `/v1/agents/${sessionId}`, { reason });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Mark a session as completed normally.
|
|
460
|
+
* @param {string} sessionId
|
|
461
|
+
*/
|
|
462
|
+
async completeAgent(sessionId) {
|
|
463
|
+
return this._request('POST', `/v1/agents/${sessionId}/complete`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Wrap an OpenAI client so every API call is auto-governed.
|
|
468
|
+
*
|
|
469
|
+
* The wrapped client is a Proxy — it has the exact same interface as the
|
|
470
|
+
* original. All method calls transparently go through governTool() first.
|
|
471
|
+
*
|
|
472
|
+
* Usage:
|
|
473
|
+
* const session = await kasbah.createAgent({ agentId: 'my-bot', ... });
|
|
474
|
+
* const governed = kasbah.wrapOpenAI(openai, session.sessionId);
|
|
475
|
+
* // now governed.chat.completions.create(...) is auto-governed
|
|
476
|
+
*
|
|
477
|
+
* @param {object} openaiClient - OpenAI SDK client instance
|
|
478
|
+
* @param {string} sessionId
|
|
479
|
+
*/
|
|
480
|
+
wrapOpenAI(openaiClient, sessionId) {
|
|
481
|
+
return this._wrapClient(openaiClient, sessionId, 'openai');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Wrap an Anthropic client so every API call is auto-governed.
|
|
486
|
+
* @param {object} anthropicClient - Anthropic SDK client instance
|
|
487
|
+
* @param {string} sessionId
|
|
488
|
+
*/
|
|
489
|
+
wrapAnthropic(anthropicClient, sessionId) {
|
|
490
|
+
return this._wrapClient(anthropicClient, sessionId, 'anthropic');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
_wrapClient(client, sessionId, type) {
|
|
494
|
+
const self = this;
|
|
495
|
+
const intercept = async (methodPath, originalFn, args) => {
|
|
496
|
+
// Extract text for governance
|
|
497
|
+
const firstArg = args[0] || {};
|
|
498
|
+
const messages = firstArg.messages || firstArg.prompt || '';
|
|
499
|
+
const argsText = typeof messages === 'string'
|
|
500
|
+
? messages
|
|
501
|
+
: Array.isArray(messages)
|
|
502
|
+
? messages.map(m => (typeof m.content === 'string' ? m.content : JSON.stringify(m.content))).join('\n')
|
|
503
|
+
: JSON.stringify(firstArg).slice(0, 800);
|
|
504
|
+
|
|
505
|
+
const decision = await self.governTool(sessionId, methodPath, firstArg, argsText);
|
|
506
|
+
if (!decision.ok) {
|
|
507
|
+
const err = new Error(`KasbahOS blocked: ${decision.reason}`);
|
|
508
|
+
err.kasbah = decision;
|
|
509
|
+
err.verdict = decision.verdict;
|
|
510
|
+
throw err;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Execute the real call
|
|
514
|
+
const result = await originalFn.apply(client, args);
|
|
515
|
+
|
|
516
|
+
// Scan response for injection
|
|
517
|
+
const responseText = type === 'openai'
|
|
518
|
+
? result?.choices?.[0]?.message?.content || ''
|
|
519
|
+
: result?.content?.[0]?.text || '';
|
|
520
|
+
if (responseText) {
|
|
521
|
+
await self.scanToolResult(sessionId, methodPath, responseText);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Record token usage
|
|
525
|
+
const usage = result?.usage;
|
|
526
|
+
const tokens = usage
|
|
527
|
+
? (usage.total_tokens || (usage.input_tokens || 0) + (usage.output_tokens || 0))
|
|
528
|
+
: 0;
|
|
529
|
+
if (tokens > 0) {
|
|
530
|
+
await self.recordTurn(sessionId, 'assistant', responseText.slice(0, 400), tokens, 0);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return result;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// Build a recursive proxy
|
|
537
|
+
const makeProxy = (target, pathPrefix) => new Proxy(target, {
|
|
538
|
+
get(inner, prop) {
|
|
539
|
+
const val = inner[prop];
|
|
540
|
+
if (typeof val === 'function') {
|
|
541
|
+
return (...args) => intercept(`${pathPrefix}.${String(prop)}`, val, args);
|
|
542
|
+
}
|
|
543
|
+
if (val && typeof val === 'object') {
|
|
544
|
+
return makeProxy(val, `${pathPrefix}.${String(prop)}`);
|
|
545
|
+
}
|
|
546
|
+
return val;
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
return makeProxy(client, type);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// ── HTTP internals ──────────────────────────────────────────────────────
|
|
554
|
+
|
|
555
|
+
async _get(path) { return this._request('GET', path); }
|
|
556
|
+
async _post(path, body) { return this._request('POST', path, body); }
|
|
557
|
+
|
|
558
|
+
async _request(method, path, body = null, attempt = 1) {
|
|
559
|
+
const parsed = new url.URL(this.baseUrl + path);
|
|
560
|
+
const isHttps = parsed.protocol === 'https:';
|
|
561
|
+
const lib = isHttps ? https : http;
|
|
562
|
+
|
|
563
|
+
const bodyStr = body ? JSON.stringify(body) : null;
|
|
564
|
+
const headers = {
|
|
565
|
+
'Content-Type': 'application/json',
|
|
566
|
+
'x-api-key': this.apiKey,
|
|
567
|
+
'x-kasbah-sdk': `node/${SDK_VERSION}`,
|
|
568
|
+
};
|
|
569
|
+
if (bodyStr) headers['Content-Length'] = Buffer.byteLength(bodyStr);
|
|
570
|
+
|
|
571
|
+
return new Promise((resolve, reject) => {
|
|
572
|
+
const req = lib.request({
|
|
573
|
+
hostname: parsed.hostname,
|
|
574
|
+
port: parsed.port || (isHttps ? 443 : 80),
|
|
575
|
+
path: parsed.pathname + parsed.search,
|
|
576
|
+
method,
|
|
577
|
+
headers,
|
|
578
|
+
timeout: this.timeout,
|
|
579
|
+
}, (res) => {
|
|
580
|
+
let data = '';
|
|
581
|
+
res.on('data', chunk => { data += chunk; });
|
|
582
|
+
res.on('end', () => {
|
|
583
|
+
try {
|
|
584
|
+
const json = JSON.parse(data);
|
|
585
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
586
|
+
resolve(json);
|
|
587
|
+
} else if (res.statusCode >= 500 && this.autoRetry && attempt < 3) {
|
|
588
|
+
setTimeout(() => this._request(method, path, body, attempt + 1).then(resolve).catch(reject),
|
|
589
|
+
Math.pow(2, attempt) * 200);
|
|
590
|
+
} else {
|
|
591
|
+
reject(new KasbahError(json.error || `HTTP ${res.statusCode}`, res.statusCode, json));
|
|
592
|
+
}
|
|
593
|
+
} catch (e) {
|
|
594
|
+
reject(new KasbahError('Invalid JSON response', res.statusCode, data));
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
req.on('error', reject);
|
|
600
|
+
req.on('timeout', () => { req.destroy(); reject(new KasbahError('Request timeout', 408)); });
|
|
601
|
+
if (bodyStr) req.write(bodyStr);
|
|
602
|
+
req.end();
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Like _request but returns { body, headers } so callers can inspect
|
|
608
|
+
* response headers (e.g. x-kasbah-receipt for proxy endpoints).
|
|
609
|
+
* @param {string} method
|
|
610
|
+
* @param {string} path
|
|
611
|
+
* @param {object|null} body
|
|
612
|
+
* @param {object} [extraHeaders] - Additional headers to merge in (e.g. Authorization)
|
|
613
|
+
* @param {number} [attempt]
|
|
614
|
+
* @returns {Promise<{ body: object, headers: object }>}
|
|
615
|
+
*/
|
|
616
|
+
async _requestRaw(method, path, body = null, extraHeaders = {}, attempt = 1) {
|
|
617
|
+
const parsed = new url.URL(this.baseUrl + path);
|
|
618
|
+
const isHttps = parsed.protocol === 'https:';
|
|
619
|
+
const lib = isHttps ? https : http;
|
|
620
|
+
|
|
621
|
+
const bodyStr = body ? JSON.stringify(body) : null;
|
|
622
|
+
const headers = {
|
|
623
|
+
'Content-Type': 'application/json',
|
|
624
|
+
'x-api-key': this.apiKey,
|
|
625
|
+
'x-kasbah-sdk': `node/${SDK_VERSION}`,
|
|
626
|
+
...extraHeaders,
|
|
627
|
+
};
|
|
628
|
+
if (bodyStr) headers['Content-Length'] = Buffer.byteLength(bodyStr);
|
|
629
|
+
|
|
630
|
+
return new Promise((resolve, reject) => {
|
|
631
|
+
const req = lib.request({
|
|
632
|
+
hostname: parsed.hostname,
|
|
633
|
+
port: parsed.port || (isHttps ? 443 : 80),
|
|
634
|
+
path: parsed.pathname + parsed.search,
|
|
635
|
+
method,
|
|
636
|
+
headers,
|
|
637
|
+
timeout: this.timeout,
|
|
638
|
+
}, (res) => {
|
|
639
|
+
let data = '';
|
|
640
|
+
res.on('data', chunk => { data += chunk; });
|
|
641
|
+
res.on('end', () => {
|
|
642
|
+
try {
|
|
643
|
+
const json = JSON.parse(data);
|
|
644
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
645
|
+
resolve({ body: json, headers: res.headers });
|
|
646
|
+
} else if (res.statusCode >= 500 && this.autoRetry && attempt < 3) {
|
|
647
|
+
setTimeout(() => this._requestRaw(method, path, body, extraHeaders, attempt + 1).then(resolve).catch(reject),
|
|
648
|
+
Math.pow(2, attempt) * 200);
|
|
649
|
+
} else {
|
|
650
|
+
reject(new KasbahError(json.error || `HTTP ${res.statusCode}`, res.statusCode, json));
|
|
651
|
+
}
|
|
652
|
+
} catch (e) {
|
|
653
|
+
reject(new KasbahError('Invalid JSON response', res.statusCode, data));
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
req.on('error', reject);
|
|
659
|
+
req.on('timeout', () => { req.destroy(); reject(new KasbahError('Request timeout', 408)); });
|
|
660
|
+
if (bodyStr) req.write(bodyStr);
|
|
661
|
+
req.end();
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// ── Sub-clients ──────────────────────────────────────────────────────────────
|
|
667
|
+
|
|
668
|
+
class PassportClient {
|
|
669
|
+
constructor(k) { this._k = k; }
|
|
670
|
+
async issue(opts = {}) { return this._k._post('/v1/passport/issue', opts); }
|
|
671
|
+
async list() { return this._k._get('/v1/passport/list'); }
|
|
672
|
+
async get(id) { return this._k._get(`/v1/passport/${id}`); }
|
|
673
|
+
async revoke(id) { return this._k._request('DELETE', `/v1/passport/${id}`); }
|
|
674
|
+
async spend(id) { return this._k._get(`/v1/passport/${id}/spend`); }
|
|
675
|
+
async setSpendPolicy(id, policy) { return this._k._post(`/v1/passport/${id}/spend/policy`, policy); }
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
class DelegationClient {
|
|
679
|
+
constructor(k) { this._k = k; }
|
|
680
|
+
async issue(opts = {}) { return this._k._post('/v1/delegation/issue', opts); }
|
|
681
|
+
async verify(tokenId) { return this._k._post('/v1/delegation/verify', { tokenId }); }
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
class HoneytokenClient {
|
|
685
|
+
constructor(k) { this._k = k; }
|
|
686
|
+
async list() { return this._k._get('/v1/honeytokens'); }
|
|
687
|
+
async deploy(service) { return this._k._post('/v1/honeytokens/deploy', { service }); }
|
|
688
|
+
async check(text) { return this._k._post('/v1/honeytokens/check', { text }); }
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
class SpendClient {
|
|
692
|
+
constructor(k) { this._k = k; }
|
|
693
|
+
async get(passportId) { return this._k._get(`/v1/passport/${passportId}/spend`); }
|
|
694
|
+
async setPolicy(passportId, policy) { return this._k._post(`/v1/passport/${passportId}/spend/policy`, policy); }
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
module.exports = Kasbah;
|
|
698
|
+
module.exports.Kasbah = Kasbah;
|
|
699
|
+
module.exports.KasbahError = KasbahError;
|
|
700
|
+
// Public-key receipt v2 — world-proof Ed25519 verification.
|
|
701
|
+
module.exports.receipt = require('./receipt');
|