aivil 0.1.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.
Files changed (2) hide show
  1. package/index.js +377 -0
  2. package/package.json +14 -0
package/index.js ADDED
@@ -0,0 +1,377 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // AIVIL — AI Vital Identity Layer
3
+ // npm install aivil
4
+ // The civil registry for artificial intelligence
5
+ // Open source forever · MIT License · github.com/scalatest01/AIVIL
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+
8
+ const AIVIL_VERSION = "0.1.0";
9
+ const AIVIL_REGISTRY_URL = "https://aivil-lake.vercel.app";
10
+
11
+ // ─── UTILITIES ───────────────────────────────────────────────────────────────
12
+
13
+ const generateId = () => {
14
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
15
+ let result = "AGT-";
16
+ for (let i = 0; i < 8; i++) {
17
+ result += chars[Math.floor(Math.random() * chars.length)];
18
+ }
19
+ return result;
20
+ };
21
+
22
+ const generateKeypair = () => {
23
+ // In production this uses real cryptography (Web Crypto API / Node crypto)
24
+ // For v0.1 we generate placeholder keys in the correct format
25
+ const hex = (len) => Array.from(
26
+ { length: len },
27
+ () => Math.floor(Math.random() * 16).toString(16)
28
+ ).join("");
29
+ return {
30
+ publicKey: "04" + hex(64),
31
+ privateKey: hex(64), // In production: stored in Secure Enclave / HSM
32
+ };
33
+ };
34
+
35
+ const generateHash = (data) => {
36
+ // In production: SHA-256 via crypto module
37
+ const str = JSON.stringify(data);
38
+ let hash = 0;
39
+ for (let i = 0; i < str.length; i++) {
40
+ const char = str.charCodeAt(i);
41
+ hash = ((hash << 5) - hash) + char;
42
+ hash = hash & hash;
43
+ }
44
+ return "sha256:" + Math.abs(hash).toString(16).padStart(8, "0") +
45
+ Array.from({ length: 24 }, () => Math.floor(Math.random() * 16).toString(16)).join("");
46
+ };
47
+
48
+ const timestamp = () => new Date().toISOString();
49
+
50
+ // ─── CORE: CREATE AGENT ───────────────────────────────────────────────────────
51
+
52
+ /**
53
+ * createAgent — Register a new AI agent with AIVIL
54
+ *
55
+ * @param {Object} config - Agent configuration
56
+ * @param {string} config.name - Human readable name for the agent
57
+ * @param {string} config.role - The agent's role (e.g. "Procurement Specialist")
58
+ * @param {string} config.owner - Company or individual who owns this agent
59
+ * @param {string} config.purpose - Plain English description of what this agent does
60
+ * @param {string} config.jurisdiction - Legal jurisdiction (e.g. "Delaware_USA", "EU_GDPR")
61
+ * @param {Object} config.policy - The agent's Red Line policy
62
+ * @param {number} config.policy.spending_limit - Maximum spend per transaction (USD)
63
+ * @param {number} config.policy.requires_human_signoff_over - Escalate above this amount
64
+ * @param {string[]} config.policy.restricted_domains - Domains the agent cannot access
65
+ * @param {string[]} config.policy.allowed_actions - Actions this agent is permitted to take
66
+ * @param {number} config.policy.max_requests_per_hour - Rate limit for this agent
67
+ *
68
+ * @returns {Object} agent - The fully registered agent with birth certificate
69
+ */
70
+ const createAgent = (config) => {
71
+ // Validate required fields
72
+ const required = ["name", "role", "owner", "purpose", "jurisdiction"];
73
+ for (const field of required) {
74
+ if (!config[field]) {
75
+ throw new Error(`AIVIL: createAgent requires '${field}' field`);
76
+ }
77
+ }
78
+
79
+ const { publicKey, privateKey } = generateKeypair();
80
+ const agentId = generateId();
81
+ const did = `did:aivil:${agentId}`;
82
+ const bornAt = timestamp();
83
+
84
+ const agent = {
85
+ // Identity
86
+ id: agentId,
87
+ did,
88
+ name: config.name,
89
+ role: config.role,
90
+ owner: config.owner,
91
+ purpose: config.purpose,
92
+ jurisdiction: config.jurisdiction,
93
+ status: "active",
94
+
95
+ // Cryptographic keys
96
+ publicKey,
97
+ privateKey, // ⚠ Store this securely — never expose in logs or APIs
98
+
99
+ // Birth certificate
100
+ birthCertificate: {
101
+ agentId,
102
+ did,
103
+ name: config.name,
104
+ role: config.role,
105
+ owner: config.owner,
106
+ purpose: config.purpose,
107
+ jurisdiction: config.jurisdiction,
108
+ bornAt,
109
+ issuedBy: "AIVIL Registry",
110
+ version: AIVIL_VERSION,
111
+ hash: generateHash({ agentId, bornAt, publicKey }),
112
+ },
113
+
114
+ // Policy (Red Line JSON)
115
+ policy: {
116
+ version: "v1.0",
117
+ spending_limit: config.policy?.spending_limit ?? 100,
118
+ requires_human_signoff_over: config.policy?.requires_human_signoff_over ?? 50,
119
+ restricted_domains: config.policy?.restricted_domains ?? ["*.crypto", "*.gambling"],
120
+ allowed_actions: config.policy?.allowed_actions ?? [],
121
+ max_requests_per_hour: config.policy?.max_requests_per_hour ?? 200,
122
+ jurisdiction: config.jurisdiction,
123
+ created_at: bornAt,
124
+ },
125
+
126
+ // Life record — starts empty, fills over time
127
+ financials: {
128
+ spent_today: 0,
129
+ spent_month: 0,
130
+ spent_lifetime: 0,
131
+ budget_today: config.policy?.spending_limit ?? 100,
132
+ value_created: 0,
133
+ transactions: 0,
134
+ },
135
+
136
+ reputation: {
137
+ score: 70, // Starts at 70 — must be earned upward
138
+ compliance_rate: "100%",
139
+ approved: 0,
140
+ escalated: 0,
141
+ blocked: 0,
142
+ badges: ["Verified"],
143
+ },
144
+
145
+ work: {
146
+ tasks_today: 0,
147
+ tasks_total: 0,
148
+ current: "Initialising…",
149
+ uptime: "100%",
150
+ },
151
+
152
+ audit_log: [],
153
+
154
+ // Metadata
155
+ _aivil_version: AIVIL_VERSION,
156
+ _registry_url: `${AIVIL_REGISTRY_URL}/agent/${agentId}`,
157
+ _created_at: bornAt,
158
+ };
159
+
160
+ return agent;
161
+ };
162
+
163
+ // ─── CORE: AUDIT ACTION ───────────────────────────────────────────────────────
164
+
165
+ /**
166
+ * audit — Check if an agent action is allowed under its policy
167
+ *
168
+ * @param {Object} agent - The agent (from createAgent)
169
+ * @param {Object} action - The action to evaluate
170
+ * @param {string} action.type - Type of action (e.g. "purchase", "send_email")
171
+ * @param {number} action.amount - Amount in USD (0 if not financial)
172
+ * @param {string} action.domain - Target domain (e.g. "openai.com")
173
+ * @param {string} action.description - Plain English description
174
+ *
175
+ * @returns {Object} verdict
176
+ * @returns {string} verdict.status - "APPROVED" | "ESCALATE" | "BLOCKED"
177
+ * @returns {string} verdict.reason - Plain English explanation
178
+ * @returns {string[]} verdict.flags - Policy flags triggered
179
+ * @returns {Object} verdict.signature - Cryptographic signature of the decision
180
+ */
181
+ const audit = (agent, action) => {
182
+ const flags = [];
183
+ let status = "APPROVED";
184
+ let reason = "";
185
+
186
+ // Check 1: Domain restriction
187
+ const isRestricted = agent.policy.restricted_domains.some(pattern => {
188
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\./g, "\\.") + "$");
189
+ return regex.test(action.domain);
190
+ });
191
+
192
+ if (isRestricted) {
193
+ status = "BLOCKED";
194
+ flags.push("RESTRICTED_DOMAIN");
195
+ reason = `Domain '${action.domain}' matches a restricted pattern in this agent's policy.`;
196
+ }
197
+
198
+ // Check 2: Spending limit
199
+ else if (action.amount > agent.policy.spending_limit) {
200
+ status = "BLOCKED";
201
+ flags.push("EXCEEDS_SPENDING_LIMIT");
202
+ reason = `Amount $${action.amount} exceeds the agent's spending limit of $${agent.policy.spending_limit}.`;
203
+ }
204
+
205
+ // Check 3: Human signoff threshold
206
+ else if (action.amount > agent.policy.requires_human_signoff_over) {
207
+ status = "ESCALATE";
208
+ flags.push("REQUIRES_HUMAN_SIGNOFF");
209
+ reason = `Amount $${action.amount} exceeds the human signoff threshold of $${agent.policy.requires_human_signoff_over}. Human approval required.`;
210
+ }
211
+
212
+ // Check 4: Action allowlist (if defined)
213
+ else if (
214
+ agent.policy.allowed_actions.length > 0 &&
215
+ !agent.policy.allowed_actions.includes(action.type)
216
+ ) {
217
+ status = "BLOCKED";
218
+ flags.push("ACTION_NOT_PERMITTED");
219
+ reason = `Action type '${action.type}' is not in this agent's allowed actions list.`;
220
+ }
221
+
222
+ // All checks passed
223
+ else {
224
+ reason = `All policy checks passed. Action is within limits and permitted for this agent.`;
225
+ }
226
+
227
+ const verdict = {
228
+ status,
229
+ reason,
230
+ flags,
231
+ action,
232
+ agent_id: agent.id,
233
+ policy_version: agent.policy.version,
234
+ timestamp: timestamp(),
235
+ signature: generateHash({ status, action, agent_id: agent.id }),
236
+ };
237
+
238
+ // Append to agent's audit log
239
+ agent.audit_log.push(verdict);
240
+
241
+ // Update reputation stats
242
+ if (status === "APPROVED") agent.reputation.approved++;
243
+ if (status === "ESCALATE") agent.reputation.escalated++;
244
+ if (status === "BLOCKED") {
245
+ agent.reputation.blocked++;
246
+ agent.reputation.score = Math.max(0, agent.reputation.score - 2);
247
+ }
248
+
249
+ // Update financials if approved
250
+ if (status === "APPROVED" && action.amount > 0) {
251
+ agent.financials.spent_today += action.amount;
252
+ agent.financials.spent_month += action.amount;
253
+ agent.financials.spent_lifetime += action.amount;
254
+ agent.financials.transactions++;
255
+ }
256
+
257
+ return verdict;
258
+ };
259
+
260
+ // ─── CORE: VERIFY AGENT ───────────────────────────────────────────────────────
261
+
262
+ /**
263
+ * verify — Verify an agent's identity and current standing
264
+ *
265
+ * @param {Object} agent - The agent to verify
266
+ * @returns {Object} verification result
267
+ */
268
+ const verify = (agent) => {
269
+ const issues = [];
270
+
271
+ if (!agent.id) issues.push("Missing agent ID");
272
+ if (!agent.did) issues.push("Missing DID");
273
+ if (!agent.publicKey) issues.push("Missing public key");
274
+ if (!agent.birthCertificate) issues.push("Missing birth certificate");
275
+ if (!agent.policy) issues.push("Missing policy");
276
+ if (agent.status !== "active") issues.push(`Agent status is '${agent.status}'`);
277
+
278
+ return {
279
+ verified: issues.length === 0,
280
+ agent_id: agent.id,
281
+ did: agent.did,
282
+ status: agent.status,
283
+ trust_score: agent.reputation.score,
284
+ issues,
285
+ verified_at: timestamp(),
286
+ registry_url: agent._registry_url,
287
+ };
288
+ };
289
+
290
+ // ─── CORE: GET LIFE RECORD ────────────────────────────────────────────────────
291
+
292
+ /**
293
+ * getLifeRecord — Get the complete life record of an agent
294
+ *
295
+ * @param {Object} agent - The agent
296
+ * @returns {Object} Complete life record
297
+ */
298
+ const getLifeRecord = (agent) => ({
299
+ identity: {
300
+ id: agent.id,
301
+ did: agent.did,
302
+ name: agent.name,
303
+ role: agent.role,
304
+ owner: agent.owner,
305
+ purpose: agent.purpose,
306
+ jurisdiction: agent.jurisdiction,
307
+ status: agent.status,
308
+ born_at: agent._created_at,
309
+ },
310
+ birth_certificate: agent.birthCertificate,
311
+ policy: agent.policy,
312
+ financials: agent.financials,
313
+ reputation: agent.reputation,
314
+ work: agent.work,
315
+ audit_summary: {
316
+ total_decisions: agent.audit_log.length,
317
+ approved: agent.reputation.approved,
318
+ escalated: agent.reputation.escalated,
319
+ blocked: agent.reputation.blocked,
320
+ last_decision: agent.audit_log[agent.audit_log.length - 1] ?? null,
321
+ },
322
+ registry_url: agent._registry_url,
323
+ aivil_version: agent._aivil_version,
324
+ });
325
+
326
+ // ─── CORE: SUSPEND / RETIRE ───────────────────────────────────────────────────
327
+
328
+ /**
329
+ * suspend — Temporarily suspend an agent
330
+ * @param {Object} agent - The agent to suspend
331
+ * @param {string} reason - Why the agent is being suspended
332
+ */
333
+ const suspend = (agent, reason) => {
334
+ agent.status = "suspended";
335
+ agent.audit_log.push({
336
+ status: "SUSPENDED",
337
+ reason,
338
+ timestamp: timestamp(),
339
+ signature: generateHash({ status: "SUSPENDED", agent_id: agent.id }),
340
+ });
341
+ return agent;
342
+ };
343
+
344
+ /**
345
+ * retire — Permanently retire an agent (preserves full history)
346
+ * @param {Object} agent - The agent to retire
347
+ * @param {string} reason - Why the agent is being retired
348
+ */
349
+ const retire = (agent, reason) => {
350
+ agent.status = "retired";
351
+ agent.retired_at = timestamp();
352
+ agent.retirement_reason = reason;
353
+ agent.final_trust_score = agent.reputation.score;
354
+ agent.audit_log.push({
355
+ status: "RETIRED",
356
+ reason,
357
+ timestamp: agent.retired_at,
358
+ final_record: getLifeRecord(agent),
359
+ signature: generateHash({ status: "RETIRED", agent_id: agent.id }),
360
+ });
361
+ return agent;
362
+ };
363
+
364
+ // ─── EXPORTS ──────────────────────────────────────────────────────────────────
365
+
366
+ module.exports = {
367
+ createAgent,
368
+ audit,
369
+ verify,
370
+ getLifeRecord,
371
+ suspend,
372
+ retire,
373
+ VERSION: AIVIL_VERSION,
374
+ };
375
+
376
+ // ES Module support
377
+ module.exports.default = module.exports;
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "aivil",
3
+ "version": "0.1.0",
4
+ "description": "The AI Vital Identity Layer — civil registry for AI agents",
5
+ "main": "index.js",
6
+ "keywords": ["ai", "agents", "identity", "policy", "governance", "audit"],
7
+ "author": "AIVIL <ihimanshu882@gmail.com>",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/scalatest01/AIVIL"
12
+ },
13
+ "homepage": "https://aivil-lake.vercel.app"
14
+ }