aira-sdk 0.3.1 → 1.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 CHANGED
@@ -1,30 +1,20 @@
1
- # Aira TypeScript SDK
2
-
3
- **AI compliance infrastructure for AI agents.**
1
+ # Aira TypeScript SDK — The authorization and audit layer for AI agents.
4
2
 
5
3
  [![npm version](https://img.shields.io/npm/v/aira-sdk.svg)](https://www.npmjs.com/package/aira-sdk)
6
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
5
 
8
- Aira produces cryptographic receipts for every action your AI agent takes. Ed25519 signatures and RFC 3161 timestamps create tamper-proof, court-admissible proof of what happened, who authorized it, and which model made the decision. Built for EU AI Act, SR 11-7, and GDPR compliance.
9
-
10
- ---
11
-
12
- ## Integration Matrix
13
-
14
- Drop Aira into your existing agent framework with one import:
6
+ Drop Aira into your agent stack in one line. Define policies without changing code. Get cryptographic proof of every decision for your auditors, your board, or a court. Not because regulation requires it. Because your agents are acting in production right now.
15
7
 
16
- | Framework | Import | Integration |
17
- |---|---|---|
18
- | **LangChain.js** | `import { AiraCallbackHandler } from "aira-sdk/extras/langchain"` | Callback handler |
19
- | **Vercel AI** | `import { AiraVercelMiddleware } from "aira-sdk/extras/vercel-ai"` | Middleware |
20
- | **OpenAI Agents** | `import { AiraGuardrail } from "aira-sdk/extras/openai-agents"` | Guardrail |
21
- | **MCP** | `import { createServer } from "aira-sdk/extras/mcp"` | MCP Server |
22
- | **Webhooks** | `import { verifySignature } from "aira-sdk/extras/webhooks"` | Verification |
8
+ ```bash
9
+ npm install aira-sdk
10
+ ```
23
11
 
24
- Or install the core SDK alone:
12
+ Framework integrations use sub-imports — just make sure the peer dependency is installed:
25
13
 
26
14
  ```bash
27
- npm install aira-sdk
15
+ npm install aira-sdk langchain # for LangChain.js
16
+ npm install aira-sdk ai # for Vercel AI
17
+ npm install aira-sdk @openai/agents # for OpenAI Agents
28
18
  ```
29
19
 
30
20
  ---
@@ -53,154 +43,13 @@ console.log(receipt.action_id); // uuid — publicly verifiable
53
43
 
54
44
  ---
55
45
 
56
- ## Framework Integrations
57
-
58
- ### LangChain.js
59
-
60
- `AiraCallbackHandler` notarizes every tool call, chain completion, and LLM invocation with a cryptographic receipt. No changes to your chain logic.
61
-
62
- ```typescript
63
- import { Aira } from "aira-sdk";
64
- import { AiraCallbackHandler } from "aira-sdk/extras/langchain";
65
-
66
- const aira = new Aira({ apiKey: "aira_live_xxx" });
67
- const handler = new AiraCallbackHandler({ client: aira, agentId: "research-agent", modelId: "gpt-5.2" });
68
-
69
- // Every tool call and chain completion gets a signed receipt
70
- const result = await chain.invoke({ input: "Analyze Q1 revenue" }, { callbacks: [handler] });
71
- ```
72
-
73
- ### Vercel AI
74
-
75
- `AiraVercelMiddleware` wraps your Vercel AI `streamText` / `generateText` calls so every model invocation is notarized with a tamper-proof receipt.
76
-
77
- ```typescript
78
- import { Aira } from "aira-sdk";
79
- import { AiraVercelMiddleware } from "aira-sdk/extras/vercel-ai";
80
-
81
- const aira = new Aira({ apiKey: "aira_live_xxx" });
82
- const middleware = new AiraVercelMiddleware({ client: aira, agentId: "assistant-agent" });
83
-
84
- // Wrap your Vercel AI calls — receipts at invocation and completion
85
- const result = await middleware.wrapGenerateText({
86
- model: openai("gpt-5.2"),
87
- prompt: "Summarize the contract terms",
88
- });
89
- ```
90
-
91
- ### OpenAI Agents SDK
92
-
93
- `AiraGuardrail` wraps any tool function to automatically notarize both invocation and result with cryptographic proof.
94
-
95
- ```typescript
96
- import { Aira } from "aira-sdk";
97
- import { AiraGuardrail } from "aira-sdk/extras/openai-agents";
98
-
99
- const aira = new Aira({ apiKey: "aira_live_xxx" });
100
- const guardrail = new AiraGuardrail({ client: aira, agentId: "assistant-agent" });
101
-
102
- // Wrap tools — every call and result gets a signed receipt
103
- const search = guardrail.wrapTool(searchTool, { toolName: "web_search" });
104
- const execute = guardrail.wrapTool(codeExecutor, { toolName: "code_exec" });
105
- ```
106
-
107
- ---
108
-
109
- ## MCP Server
110
-
111
- Expose Aira as an MCP tool server. Any MCP-compatible AI agent can notarize actions and verify receipts without SDK integration.
112
-
113
- ```typescript
114
- import { createServer } from "aira-sdk/extras/mcp";
115
-
116
- const server = createServer({ apiKey: "aira_live_xxx" });
117
- server.listen(); // stdio transport
118
- ```
119
-
120
- The server exposes three tools: `notarize_action`, `verify_action`, and `get_receipt` -- each producing cryptographically signed results.
121
-
122
- Add to your MCP client config:
123
-
124
- ```json
125
- {
126
- "mcpServers": {
127
- "aira": {
128
- "command": "npx",
129
- "args": ["aira-sdk", "mcp"],
130
- "env": { "AIRA_API_KEY": "aira_live_xxx" }
131
- }
132
- }
133
- }
134
- ```
135
-
136
- ---
137
-
138
- ## Session
139
-
140
- Pre-fill defaults for a block of related actions. Every `notarize()` call within the session inherits the agent identity, producing receipts that share a common provenance chain.
141
-
142
- ```typescript
143
- const sess = aira.session("onboarding-agent", { modelId: "claude-sonnet-4-6" });
144
-
145
- await sess.notarize({ actionType: "identity_verified", details: "Verified customer ID #4521" });
146
- await sess.notarize({ actionType: "account_created", details: "Created account for customer #4521" });
147
- await sess.notarize({ actionType: "welcome_sent", details: "Sent welcome email to customer #4521" });
148
- ```
149
-
150
- ---
151
-
152
- ## Offline Mode
153
-
154
- Queue notarizations locally when connectivity is unavailable. Cryptographic receipts are generated server-side when you sync -- nothing is lost.
155
-
156
- ```typescript
157
- const aira = new Aira({ apiKey: "aira_live_xxx", offline: true });
158
-
159
- // These queue locally — no network calls
160
- await aira.notarize({ actionType: "scan_completed", details: "Scanned document batch #77" });
161
- await aira.notarize({ actionType: "classification_done", details: "Classified 142 documents" });
162
-
163
- console.log(aira.pendingCount); // 2
164
-
165
- // Flush to API when back online — receipts are generated for each action
166
- const results = await aira.sync();
167
- ```
168
-
169
- ---
170
-
171
- ## Webhook Verification
172
-
173
- Verify that incoming webhooks are authentic Aira events, not forged requests. HMAC-SHA256 signature verification ensures tamper-proof delivery.
174
-
175
- ```typescript
176
- import { verifySignature, parseEvent } from "aira-sdk/extras/webhooks";
177
-
178
- // Verify the webhook signature (HMAC-SHA256)
179
- const isValid = verifySignature({
180
- payload: request.body,
181
- signature: request.headers["x-aira-signature"],
182
- secret: "whsec_xxx",
183
- });
184
-
185
- if (isValid) {
186
- const event = parseEvent(request.body);
187
- console.log(event.eventType); // "action.notarized"
188
- console.log(event.data); // Action data with cryptographic receipt
189
- console.log(event.deliveryId); // Unique delivery ID
190
- }
191
- ```
192
-
193
- Supported event types: `action.notarized`, `action.authorized`, `agent.registered`, `agent.decommissioned`, `evidence.sealed`, `escrow.deposited`, `escrow.released`, `escrow.disputed`, `compliance.snapshot_created`, `case.complete`, `case.requires_human_review`.
194
-
195
- ---
196
-
197
46
  ## Core SDK Methods
198
47
 
199
48
  All 52 methods on `Aira`. Every write operation produces a cryptographic receipt.
200
49
 
201
50
  | Category | Method | Description |
202
51
  |---|---|---|
203
- | **Actions** | `notarize()` | Notarize an action -- returns Ed25519-signed receipt |
52
+ | **Actions** | `notarize()` | Notarize an action -- returns Ed25519-signed receipt (supports `requireApproval`) |
204
53
  | | `getAction()` | Retrieve action details + receipt |
205
54
  | | `listActions()` | List actions with filters (type, agent, status) |
206
55
  | | `authorizeAction()` | Human co-signature on high-stakes action |
@@ -374,6 +223,224 @@ const handler = new AiraCallbackHandler(aira, "research-agent", {
374
223
 
375
224
  ---
376
225
 
226
+ ## Session
227
+
228
+ Pre-fill defaults for a block of related actions. Every `notarize()` call within the session inherits the agent identity, producing receipts that share a common provenance chain.
229
+
230
+ ```typescript
231
+ const sess = aira.session("onboarding-agent", { modelId: "claude-sonnet-4-6" });
232
+
233
+ await sess.notarize({ actionType: "identity_verified", details: "Verified customer ID #4521" });
234
+ await sess.notarize({ actionType: "account_created", details: "Created account for customer #4521" });
235
+ await sess.notarize({ actionType: "welcome_sent", details: "Sent welcome email to customer #4521" });
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Offline Mode
241
+
242
+ Queue notarizations locally when connectivity is unavailable. Cryptographic receipts are generated server-side when you sync -- nothing is lost.
243
+
244
+ ```typescript
245
+ const aira = new Aira({ apiKey: "aira_live_xxx", offline: true });
246
+
247
+ // These queue locally — no network calls
248
+ await aira.notarize({ actionType: "scan_completed", details: "Scanned document batch #77" });
249
+ await aira.notarize({ actionType: "classification_done", details: "Classified 142 documents" });
250
+
251
+ console.log(aira.pendingCount); // 2
252
+
253
+ // Flush to API when back online — receipts are generated for each action
254
+ const results = await aira.sync();
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Human Approval
260
+
261
+ Hold high-stakes actions for human review before the cryptographic receipt is issued. Approvers receive an email with Approve/Deny buttons.
262
+
263
+ ```typescript
264
+ const receipt = await aira.notarize({
265
+ actionType: "loan_decision",
266
+ details: "Approved €15,000 loan for Maria Schmidt",
267
+ agentId: "lending-agent",
268
+ requireApproval: true,
269
+ approvers: ["compliance@acme.com", "risk@acme.com"],
270
+ });
271
+ console.log(receipt.status); // "pending_approval"
272
+ console.log(receipt.receipt_id); // undefined — no receipt until approved
273
+
274
+ // Falls back to org default approvers (Settings → Approvers)
275
+ const receipt2 = await aira.notarize({
276
+ actionType: "wire_transfer",
277
+ details: "Transfer $50,000 to vendor account",
278
+ agentId: "payments-agent",
279
+ requireApproval: true,
280
+ });
281
+ ```
282
+
283
+ The approver clicks "Approve" in the email → receipt is minted with Ed25519 signature + RFC 3161 timestamp → `action.approved` webhook fires.
284
+
285
+ Configure default approvers in the [dashboard](https://app.airaproof.com/dashboard/settings/approvers) or via the `/approvers` API.
286
+
287
+ ### Automatic Policy Evaluation
288
+
289
+ Org admins configure policies in the dashboard — your code doesn't change. Every `notarize()` call is automatically evaluated against active policies before the receipt is issued.
290
+
291
+ Three evaluation modes:
292
+
293
+ - **Rules**: Deterministic conditions — instant, no LLM call
294
+ - **AI**: Single LLM evaluates action against a natural language policy (1-5s)
295
+ - **Consensus**: Multiple LLMs evaluate independently — disagreement triggers human review (3-10s)
296
+
297
+ ```typescript
298
+ // Your code stays the same — policies evaluate automatically
299
+ const receipt = await aira.notarize({
300
+ actionType: "wire_transfer",
301
+ details: "Transfer $50,000 to vendor account",
302
+ agentId: "billing-agent",
303
+ });
304
+
305
+ // If a policy triggers "require_approval":
306
+ console.log(receipt.status); // "pending_approval"
307
+ console.log(receipt.policy_evaluation); // { policy_name: "Wire transfers need approval", decision: "require_approval", ... }
308
+
309
+ // If a policy triggers "deny":
310
+ import { AiraError } from "aira-sdk";
311
+ try {
312
+ await aira.notarize({ actionType: "data_deletion", details: "Delete customer records" });
313
+ } catch (e) {
314
+ if (e instanceof AiraError && e.code === "POLICY_DENIED") {
315
+ console.log(e.message); // "Action denied by policy 'Block deletions': ..."
316
+ }
317
+ }
318
+ ```
319
+
320
+ Every policy evaluation produces a cryptographic receipt — proof the policy was checked. The SDK `requireApproval: true` override still works and skips policy evaluation entirely.
321
+
322
+ Configure policies at [Settings → Policies](https://app.airaproof.com/dashboard/policies).
323
+
324
+ ---
325
+
326
+ ## Framework Integrations
327
+
328
+ Drop Aira into your existing agent framework with one import:
329
+
330
+ | Framework | Import | Integration |
331
+ |---|---|---|
332
+ | **LangChain.js** | `import { AiraCallbackHandler } from "aira-sdk/extras/langchain"` | Callback handler |
333
+ | **Vercel AI** | `import { AiraVercelMiddleware } from "aira-sdk/extras/vercel-ai"` | Middleware |
334
+ | **OpenAI Agents** | `import { AiraGuardrail } from "aira-sdk/extras/openai-agents"` | Guardrail |
335
+ | **MCP** | `import { createServer } from "aira-sdk/extras/mcp"` | MCP Server |
336
+ | **Webhooks** | `import { verifySignature } from "aira-sdk/extras/webhooks"` | Verification |
337
+
338
+ ### LangChain.js
339
+
340
+ `AiraCallbackHandler` notarizes every tool call, chain completion, and LLM invocation with a cryptographic receipt. No changes to your chain logic.
341
+
342
+ ```typescript
343
+ import { Aira } from "aira-sdk";
344
+ import { AiraCallbackHandler } from "aira-sdk/extras/langchain";
345
+
346
+ const aira = new Aira({ apiKey: "aira_live_xxx" });
347
+ const handler = new AiraCallbackHandler({ client: aira, agentId: "research-agent", modelId: "gpt-5.2" });
348
+
349
+ // Every tool call and chain completion gets a signed receipt
350
+ const result = await chain.invoke({ input: "Analyze Q1 revenue" }, { callbacks: [handler] });
351
+ ```
352
+
353
+ ### Vercel AI
354
+
355
+ `AiraVercelMiddleware` wraps your Vercel AI `streamText` / `generateText` calls so every model invocation is notarized with a tamper-proof receipt.
356
+
357
+ ```typescript
358
+ import { Aira } from "aira-sdk";
359
+ import { AiraVercelMiddleware } from "aira-sdk/extras/vercel-ai";
360
+
361
+ const aira = new Aira({ apiKey: "aira_live_xxx" });
362
+ const middleware = new AiraVercelMiddleware({ client: aira, agentId: "assistant-agent" });
363
+
364
+ // Wrap your Vercel AI calls — receipts at invocation and completion
365
+ const result = await middleware.wrapGenerateText({
366
+ model: openai("gpt-5.2"),
367
+ prompt: "Summarize the contract terms",
368
+ });
369
+ ```
370
+
371
+ ### OpenAI Agents SDK
372
+
373
+ `AiraGuardrail` wraps any tool function to automatically notarize both invocation and result with cryptographic proof.
374
+
375
+ ```typescript
376
+ import { Aira } from "aira-sdk";
377
+ import { AiraGuardrail } from "aira-sdk/extras/openai-agents";
378
+
379
+ const aira = new Aira({ apiKey: "aira_live_xxx" });
380
+ const guardrail = new AiraGuardrail({ client: aira, agentId: "assistant-agent" });
381
+
382
+ // Wrap tools — every call and result gets a signed receipt
383
+ const search = guardrail.wrapTool(searchTool, { toolName: "web_search" });
384
+ const execute = guardrail.wrapTool(codeExecutor, { toolName: "code_exec" });
385
+ ```
386
+
387
+ ---
388
+
389
+ ## MCP Server
390
+
391
+ Expose Aira as an MCP tool server. Any MCP-compatible AI agent can notarize actions and verify receipts without SDK integration.
392
+
393
+ ```typescript
394
+ import { createServer } from "aira-sdk/extras/mcp";
395
+
396
+ const server = createServer({ apiKey: "aira_live_xxx" });
397
+ server.listen(); // stdio transport
398
+ ```
399
+
400
+ The server exposes three tools: `notarize_action`, `verify_action`, and `get_receipt` -- each producing cryptographically signed results.
401
+
402
+ Add to your MCP client config:
403
+
404
+ ```json
405
+ {
406
+ "mcpServers": {
407
+ "aira": {
408
+ "command": "npx",
409
+ "args": ["aira-sdk", "mcp"],
410
+ "env": { "AIRA_API_KEY": "aira_live_xxx" }
411
+ }
412
+ }
413
+ }
414
+ ```
415
+
416
+ ---
417
+
418
+ ## Webhook Verification
419
+
420
+ Verify that incoming webhooks are authentic Aira events, not forged requests. HMAC-SHA256 signature verification ensures tamper-proof delivery.
421
+
422
+ ```typescript
423
+ import { verifySignature, parseEvent } from "aira-sdk/extras/webhooks";
424
+
425
+ // Verify the webhook signature (HMAC-SHA256)
426
+ const isValid = verifySignature({
427
+ payload: request.body,
428
+ signature: request.headers["x-aira-signature"],
429
+ secret: "whsec_xxx",
430
+ });
431
+
432
+ if (isValid) {
433
+ const event = parseEvent(request.body);
434
+ console.log(event.eventType); // "action.notarized"
435
+ console.log(event.data); // Action data with cryptographic receipt
436
+ console.log(event.deliveryId); // Unique delivery ID
437
+ }
438
+ ```
439
+
440
+ Supported event types: `action.notarized`, `action.authorized`, `agent.registered`, `agent.decommissioned`, `evidence.sealed`, `escrow.deposited`, `escrow.released`, `escrow.disputed`, `compliance.snapshot_created`, `case.complete`, `case.requires_human_review`.
441
+
442
+ ---
443
+
377
444
  ## Error Handling
378
445
 
379
446
  ```typescript
package/dist/client.d.ts CHANGED
@@ -29,6 +29,8 @@ export declare class Aira {
29
29
  parentActionId?: string;
30
30
  storeDetails?: boolean;
31
31
  idempotencyKey?: string;
32
+ requireApproval?: boolean;
33
+ approvers?: string[];
32
34
  }): Promise<ActionReceipt>;
33
35
  getAction(actionId: string): Promise<ActionDetail>;
34
36
  listActions(params?: {
package/dist/client.js CHANGED
@@ -10,7 +10,7 @@ const MAX_DETAILS_LENGTH = 50_000;
10
10
  function buildBody(obj) {
11
11
  return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined && v !== null));
12
12
  }
13
- function sanitizeDetails(text) {
13
+ function truncateDetails(text) {
14
14
  return text.length > MAX_DETAILS_LENGTH ? text.slice(0, MAX_DETAILS_LENGTH) + "...[truncated]" : text;
15
15
  }
16
16
  class Aira {
@@ -69,9 +69,17 @@ class Aira {
69
69
  return this.request("POST", path, body);
70
70
  }
71
71
  put(path, body) {
72
+ if (this.queue) {
73
+ const qid = this.queue.enqueue("PUT", path, body);
74
+ return Promise.resolve({ _offline: true, _queue_id: qid });
75
+ }
72
76
  return this.request("PUT", path, body);
73
77
  }
74
78
  del(path) {
79
+ if (this.queue) {
80
+ const qid = this.queue.enqueue("DELETE", path, {});
81
+ return Promise.resolve({ _offline: true, _queue_id: qid });
82
+ }
75
83
  return this.request("DELETE", path);
76
84
  }
77
85
  paginated(data) {
@@ -82,7 +90,7 @@ class Aira {
82
90
  async notarize(params) {
83
91
  const body = buildBody({
84
92
  action_type: params.actionType,
85
- details: sanitizeDetails(params.details),
93
+ details: truncateDetails(params.details),
86
94
  agent_id: params.agentId,
87
95
  agent_version: params.agentVersion,
88
96
  model_id: params.modelId,
@@ -91,6 +99,8 @@ class Aira {
91
99
  parent_action_id: params.parentActionId,
92
100
  store_details: params.storeDetails || undefined,
93
101
  idempotency_key: params.idempotencyKey,
102
+ require_approval: params.requireApproval || undefined,
103
+ approvers: params.approvers,
94
104
  });
95
105
  return this.post("/actions", body);
96
106
  }
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.getTools = getTools;
13
13
  exports.handleToolCall = handleToolCall;
14
14
  exports.createServer = createServer;
15
+ const types_1 = require("../types");
15
16
  /** MCP tool definitions exposed by the Aira server. */
16
17
  function getTools() {
17
18
  return [
@@ -138,7 +139,10 @@ async function handleToolCall(client, name, args) {
138
139
  return [{ type: "text", text: JSON.stringify({ error: `Unknown tool: ${name}` }) }];
139
140
  }
140
141
  catch (e) {
141
- return [{ type: "text", text: JSON.stringify({ error: String(e) }) }];
142
+ if (e instanceof types_1.AiraError) {
143
+ return [{ type: "text", text: JSON.stringify({ error: e.message, code: e.code }) }];
144
+ }
145
+ return [{ type: "text", text: JSON.stringify({ error: "Internal error", code: "SDK_ERROR" }) }];
142
146
  }
143
147
  }
144
148
  /**
package/dist/session.d.ts CHANGED
@@ -16,5 +16,7 @@ export declare class AiraSession {
16
16
  parentActionId?: string;
17
17
  storeDetails?: boolean;
18
18
  idempotencyKey?: string;
19
+ requireApproval?: boolean;
20
+ approvers?: string[];
19
21
  }): Promise<ActionReceipt>;
20
22
  }
package/dist/types.d.ts CHANGED
@@ -1,15 +1,23 @@
1
1
  /** Cryptographic receipt from notarizing an action. */
2
2
  export interface ActionReceipt {
3
3
  action_id: string;
4
- receipt_id: string;
5
- payload_hash: string;
6
- signature: string;
7
- timestamp_token: string | null;
4
+ receipt_id?: string;
5
+ payload_hash?: string;
6
+ signature?: string;
7
+ timestamp_token?: string | null;
8
8
  created_at: string;
9
9
  request_id: string;
10
+ status?: string;
10
11
  action_type?: string;
11
12
  agent_id?: string | null;
12
13
  warnings?: string[] | null;
14
+ policy_evaluation?: {
15
+ policy_id: string;
16
+ policy_name: string;
17
+ decision: string;
18
+ reasoning: string | null;
19
+ confidence: number | null;
20
+ } | null;
13
21
  }
14
22
  /** Full action details including receipt and authorizations. */
15
23
  export interface ActionDetail {
@@ -63,6 +71,7 @@ export interface AgentVersion {
63
71
  changelog: string | null;
64
72
  status: string;
65
73
  published_at: string | null;
74
+ created_at: string;
66
75
  }
67
76
  /** Sealed evidence package. */
68
77
  export interface EvidencePackage {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aira-sdk",
3
- "version": "0.3.1",
4
- "description": "TypeScript SDK for Aira legal infrastructure for AI agents",
3
+ "version": "1.0.0",
4
+ "description": "The authorization and audit layer for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {