chainlesschain 0.37.12 → 0.40.1

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 (48) hide show
  1. package/package.json +3 -2
  2. package/src/commands/agent.js +7 -1
  3. package/src/commands/ask.js +24 -9
  4. package/src/commands/chat.js +7 -1
  5. package/src/commands/cli-anything.js +266 -0
  6. package/src/commands/compliance.js +216 -0
  7. package/src/commands/dao.js +312 -0
  8. package/src/commands/dlp.js +278 -0
  9. package/src/commands/evomap.js +558 -0
  10. package/src/commands/hardening.js +230 -0
  11. package/src/commands/matrix.js +168 -0
  12. package/src/commands/nostr.js +185 -0
  13. package/src/commands/pqc.js +162 -0
  14. package/src/commands/scim.js +218 -0
  15. package/src/commands/serve.js +109 -0
  16. package/src/commands/siem.js +156 -0
  17. package/src/commands/social.js +480 -0
  18. package/src/commands/terraform.js +148 -0
  19. package/src/constants.js +1 -0
  20. package/src/index.js +60 -0
  21. package/src/lib/autonomous-agent.js +487 -0
  22. package/src/lib/cli-anything-bridge.js +379 -0
  23. package/src/lib/cli-context-engineering.js +472 -0
  24. package/src/lib/compliance-manager.js +290 -0
  25. package/src/lib/content-recommender.js +205 -0
  26. package/src/lib/dao-governance.js +296 -0
  27. package/src/lib/dlp-engine.js +304 -0
  28. package/src/lib/evomap-client.js +135 -0
  29. package/src/lib/evomap-federation.js +240 -0
  30. package/src/lib/evomap-governance.js +250 -0
  31. package/src/lib/evomap-manager.js +227 -0
  32. package/src/lib/git-integration.js +1 -1
  33. package/src/lib/hardening-manager.js +275 -0
  34. package/src/lib/llm-providers.js +14 -1
  35. package/src/lib/matrix-bridge.js +196 -0
  36. package/src/lib/nostr-bridge.js +195 -0
  37. package/src/lib/permanent-memory.js +370 -0
  38. package/src/lib/plan-mode.js +211 -0
  39. package/src/lib/pqc-manager.js +196 -0
  40. package/src/lib/scim-manager.js +212 -0
  41. package/src/lib/session-manager.js +38 -0
  42. package/src/lib/siem-exporter.js +137 -0
  43. package/src/lib/social-manager.js +283 -0
  44. package/src/lib/task-model-selector.js +232 -0
  45. package/src/lib/terraform-manager.js +201 -0
  46. package/src/lib/ws-server.js +474 -0
  47. package/src/repl/agent-repl.js +796 -41
  48. package/src/repl/chat-repl.js +14 -6
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Compliance Manager — evidence collection, report generation,
3
+ * data classification, compliance scanning, and policy management.
4
+ */
5
+
6
+ import crypto from "crypto";
7
+
8
+ /* ── In-memory stores ──────────────────────────────────────── */
9
+ const _evidence = new Map();
10
+ const _reports = new Map();
11
+ const _policies = new Map();
12
+
13
+ const FRAMEWORKS = ["gdpr", "soc2", "hipaa", "iso27001"];
14
+ const POLICY_TYPES = [
15
+ "retention",
16
+ "access_control",
17
+ "encryption",
18
+ "data_classification",
19
+ "audit_trail",
20
+ ];
21
+
22
+ /* ── Schema ────────────────────────────────────────────────── */
23
+
24
+ export function ensureComplianceTables(db) {
25
+ db.exec(`
26
+ CREATE TABLE IF NOT EXISTS compliance_evidence (
27
+ id TEXT PRIMARY KEY,
28
+ framework TEXT NOT NULL,
29
+ type TEXT,
30
+ description TEXT,
31
+ source TEXT,
32
+ status TEXT DEFAULT 'collected',
33
+ collected_at TEXT DEFAULT (datetime('now'))
34
+ )
35
+ `);
36
+ db.exec(`
37
+ CREATE TABLE IF NOT EXISTS compliance_reports (
38
+ id TEXT PRIMARY KEY,
39
+ framework TEXT NOT NULL,
40
+ title TEXT,
41
+ summary TEXT,
42
+ score REAL DEFAULT 0,
43
+ content TEXT,
44
+ generated_at TEXT DEFAULT (datetime('now'))
45
+ )
46
+ `);
47
+ db.exec(`
48
+ CREATE TABLE IF NOT EXISTS compliance_policies (
49
+ id TEXT PRIMARY KEY,
50
+ name TEXT NOT NULL,
51
+ type TEXT,
52
+ framework TEXT,
53
+ rules TEXT,
54
+ enabled INTEGER DEFAULT 1,
55
+ severity TEXT DEFAULT 'medium',
56
+ created_at TEXT DEFAULT (datetime('now'))
57
+ )
58
+ `);
59
+ }
60
+
61
+ /* ── Evidence Collection ──────────────────────────────────── */
62
+
63
+ export function collectEvidence(db, framework, type, description, source) {
64
+ if (!framework) throw new Error("Framework is required");
65
+ if (!FRAMEWORKS.includes(framework)) {
66
+ throw new Error(
67
+ `Unknown framework: ${framework}. Valid: ${FRAMEWORKS.join(", ")}`,
68
+ );
69
+ }
70
+
71
+ const id = crypto.randomUUID();
72
+ const now = new Date().toISOString();
73
+
74
+ const evidence = {
75
+ id,
76
+ framework,
77
+ type: type || "general",
78
+ description: description || "",
79
+ source: source || "cli",
80
+ status: "collected",
81
+ collectedAt: now,
82
+ };
83
+
84
+ _evidence.set(id, evidence);
85
+
86
+ db.prepare(
87
+ `INSERT INTO compliance_evidence (id, framework, type, description, source, status, collected_at)
88
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
89
+ ).run(
90
+ id,
91
+ framework,
92
+ evidence.type,
93
+ evidence.description,
94
+ evidence.source,
95
+ "collected",
96
+ now,
97
+ );
98
+
99
+ return evidence;
100
+ }
101
+
102
+ /* ── Report Generation ────────────────────────────────────── */
103
+
104
+ export function generateReport(db, framework, title) {
105
+ if (!framework) throw new Error("Framework is required");
106
+
107
+ const id = crypto.randomUUID();
108
+ const now = new Date().toISOString();
109
+
110
+ const evidenceList = [..._evidence.values()].filter(
111
+ (e) => e.framework === framework,
112
+ );
113
+ const policyList = [..._policies.values()].filter(
114
+ (p) => p.framework === framework && p.enabled,
115
+ );
116
+
117
+ const score =
118
+ policyList.length > 0 ? Math.min(100, evidenceList.length * 20) : 0;
119
+
120
+ const report = {
121
+ id,
122
+ framework,
123
+ title: title || `${framework.toUpperCase()} Compliance Report`,
124
+ summary: `${evidenceList.length} evidence items, ${policyList.length} active policies`,
125
+ score,
126
+ content: { evidence: evidenceList.length, policies: policyList.length },
127
+ generatedAt: now,
128
+ };
129
+
130
+ _reports.set(id, report);
131
+
132
+ db.prepare(
133
+ `INSERT INTO compliance_reports (id, framework, title, summary, score, content, generated_at)
134
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
135
+ ).run(
136
+ id,
137
+ framework,
138
+ report.title,
139
+ report.summary,
140
+ score,
141
+ JSON.stringify(report.content),
142
+ now,
143
+ );
144
+
145
+ return report;
146
+ }
147
+
148
+ /* ── Data Classification ────────────────────────────────────── */
149
+
150
+ export function classifyData(content) {
151
+ if (!content) throw new Error("Content is required");
152
+
153
+ const patterns = {
154
+ pii: /\b\d{3}-\d{2}-\d{4}\b|\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}\b/i,
155
+ financial:
156
+ /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b|\baccount\s*#?\s*\d+/i,
157
+ health: /\bdiagnosis\b|\bpatient\b|\bmedical\b|\bprescription\b/i,
158
+ };
159
+
160
+ const classifications = [];
161
+ for (const [type, regex] of Object.entries(patterns)) {
162
+ if (regex.test(content)) {
163
+ classifications.push(type);
164
+ }
165
+ }
166
+
167
+ return {
168
+ hasClassifiedData: classifications.length > 0,
169
+ classifications,
170
+ sensitivity:
171
+ classifications.length > 1
172
+ ? "high"
173
+ : classifications.length === 1
174
+ ? "medium"
175
+ : "low",
176
+ };
177
+ }
178
+
179
+ /* ── Compliance Scanning ──────────────────────────────────── */
180
+
181
+ export function scanCompliance(db, framework) {
182
+ if (!framework) throw new Error("Framework is required");
183
+
184
+ const policies = [..._policies.values()].filter(
185
+ (p) => p.framework === framework && p.enabled,
186
+ );
187
+ const evidence = [..._evidence.values()].filter(
188
+ (e) => e.framework === framework,
189
+ );
190
+
191
+ const checks = policies.map((policy) => {
192
+ const relatedEvidence = evidence.filter((e) => e.type === policy.type);
193
+ return {
194
+ policyId: policy.id,
195
+ policyName: policy.name,
196
+ status: relatedEvidence.length > 0 ? "pass" : "fail",
197
+ evidenceCount: relatedEvidence.length,
198
+ };
199
+ });
200
+
201
+ const passed = checks.filter((c) => c.status === "pass").length;
202
+ const total = checks.length;
203
+ const score = total > 0 ? Math.round((passed / total) * 100) : 0;
204
+
205
+ return { framework, score, total, passed, failed: total - passed, checks };
206
+ }
207
+
208
+ /* ── Policy Management ────────────────────────────────────── */
209
+
210
+ export function listPolicies(db, filter = {}) {
211
+ let policies = [..._policies.values()];
212
+ if (filter.framework) {
213
+ policies = policies.filter((p) => p.framework === filter.framework);
214
+ }
215
+ if (filter.type) {
216
+ policies = policies.filter((p) => p.type === filter.type);
217
+ }
218
+ return policies;
219
+ }
220
+
221
+ export function addPolicy(db, name, type, framework, rules, severity) {
222
+ if (!name) throw new Error("Policy name is required");
223
+ if (!POLICY_TYPES.includes(type)) {
224
+ throw new Error(
225
+ `Invalid policy type: ${type}. Valid: ${POLICY_TYPES.join(", ")}`,
226
+ );
227
+ }
228
+ if (!FRAMEWORKS.includes(framework)) {
229
+ throw new Error(`Invalid framework: ${framework}`);
230
+ }
231
+
232
+ const id = crypto.randomUUID();
233
+ const now = new Date().toISOString();
234
+
235
+ const policy = {
236
+ id,
237
+ name,
238
+ type,
239
+ framework,
240
+ rules: rules || {},
241
+ enabled: true,
242
+ severity: severity || "medium",
243
+ createdAt: now,
244
+ };
245
+
246
+ _policies.set(id, policy);
247
+
248
+ db.prepare(
249
+ `INSERT INTO compliance_policies (id, name, type, framework, rules, enabled, severity, created_at)
250
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
251
+ ).run(
252
+ id,
253
+ name,
254
+ type,
255
+ framework,
256
+ JSON.stringify(rules || {}),
257
+ 1,
258
+ policy.severity,
259
+ now,
260
+ );
261
+
262
+ return policy;
263
+ }
264
+
265
+ /* ── Access Check ─────────────────────────────────────────── */
266
+
267
+ export function checkAccess(resource, action, role) {
268
+ // Simplified RBAC check
269
+ const allowedRoles = {
270
+ admin: ["read", "write", "delete", "audit"],
271
+ auditor: ["read", "audit"],
272
+ user: ["read"],
273
+ };
274
+
275
+ const allowed = allowedRoles[role] || [];
276
+ return {
277
+ resource,
278
+ action,
279
+ role,
280
+ granted: allowed.includes(action),
281
+ };
282
+ }
283
+
284
+ /* ── Reset (for testing) ───────────────────────────────────── */
285
+
286
+ export function _resetState() {
287
+ _evidence.clear();
288
+ _reports.clear();
289
+ _policies.clear();
290
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * CLI Content Recommender — tool similarity, chain detection, and skill matching.
3
+ *
4
+ * Uses TF-IDF for tool feature extraction and cosine similarity,
5
+ * plus sequential chain frequency for next-tool prediction.
6
+ * BM25 for skill recommendation.
7
+ */
8
+
9
+ import { BM25Search } from "./bm25-search.js";
10
+
11
+ // Exported for test injection
12
+ export const _deps = {
13
+ BM25Search,
14
+ };
15
+
16
+ export class CLIContentRecommender {
17
+ constructor() {
18
+ this._toolFeatures = new Map(); // tool → feature vector
19
+ this._toolUsageLog = []; // sequential tool usage log
20
+ this._chainFrequency = new Map(); // "toolA→toolB" → count
21
+ this._skillIndex = null;
22
+ this._vocabulary = new Set();
23
+ }
24
+
25
+ /**
26
+ * Build TF-IDF feature vectors for tools.
27
+ * @param {Array<{ name: string, description: string }>} tools
28
+ */
29
+ buildToolFeatures(tools) {
30
+ if (!tools || tools.length === 0) return;
31
+
32
+ this._vocabulary = new Set();
33
+ const docTokens = [];
34
+
35
+ // Tokenize each tool description
36
+ for (const tool of tools) {
37
+ const text = `${tool.name} ${tool.description || ""}`.toLowerCase();
38
+ const tokens = text.split(/\W+/).filter((t) => t.length > 2);
39
+ docTokens.push({ name: tool.name, tokens });
40
+ for (const t of tokens) this._vocabulary.add(t);
41
+ }
42
+
43
+ // Calculate TF-IDF
44
+ const N = docTokens.length;
45
+ const df = new Map(); // term → doc frequency
46
+
47
+ for (const { tokens } of docTokens) {
48
+ const unique = new Set(tokens);
49
+ for (const t of unique) {
50
+ df.set(t, (df.get(t) || 0) + 1);
51
+ }
52
+ }
53
+
54
+ for (const { name, tokens } of docTokens) {
55
+ const tf = new Map();
56
+ for (const t of tokens) {
57
+ tf.set(t, (tf.get(t) || 0) + 1);
58
+ }
59
+
60
+ const vector = new Map();
61
+ for (const [term, count] of tf) {
62
+ const idf = Math.log(N / (df.get(term) || 1));
63
+ vector.set(term, (count / tokens.length) * idf);
64
+ }
65
+
66
+ this._toolFeatures.set(name, vector);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Calculate cosine similarity between two tools.
72
+ */
73
+ calculateSimilarity(tool1, tool2) {
74
+ const v1 = this._toolFeatures.get(tool1);
75
+ const v2 = this._toolFeatures.get(tool2);
76
+ if (!v1 || !v2) return 0;
77
+
78
+ let dotProduct = 0;
79
+ let norm1 = 0;
80
+ let norm2 = 0;
81
+
82
+ for (const [term, val] of v1) {
83
+ norm1 += val * val;
84
+ if (v2.has(term)) {
85
+ dotProduct += val * v2.get(term);
86
+ }
87
+ }
88
+ for (const [, val] of v2) {
89
+ norm2 += val * val;
90
+ }
91
+
92
+ if (norm1 === 0 || norm2 === 0) return 0;
93
+ return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
94
+ }
95
+
96
+ /**
97
+ * Record a tool usage for chain detection.
98
+ */
99
+ recordToolUse(toolName, context = {}) {
100
+ const entry = {
101
+ tool: toolName,
102
+ timestamp: Date.now(),
103
+ context: context.query || "",
104
+ };
105
+ this._toolUsageLog.push(entry);
106
+
107
+ // Update chain frequency (look at previous tool)
108
+ if (this._toolUsageLog.length >= 2) {
109
+ const prev = this._toolUsageLog[this._toolUsageLog.length - 2];
110
+ const key = `${prev.tool}→${toolName}`;
111
+ this._chainFrequency.set(key, (this._chainFrequency.get(key) || 0) + 1);
112
+ }
113
+
114
+ // Keep log bounded
115
+ if (this._toolUsageLog.length > 1000) {
116
+ this._toolUsageLog = this._toolUsageLog.slice(-500);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Recommend next tool based on usage chains.
122
+ * @returns {Array<{ tool: string, confidence: number }>}
123
+ */
124
+ recommendNextTool(currentTool, _context = {}) {
125
+ const candidates = [];
126
+ let totalFromCurrent = 0;
127
+
128
+ for (const [key, count] of this._chainFrequency) {
129
+ if (key.startsWith(`${currentTool}→`)) {
130
+ const nextTool = key.split("→")[1];
131
+ candidates.push({ tool: nextTool, count });
132
+ totalFromCurrent += count;
133
+ }
134
+ }
135
+
136
+ if (totalFromCurrent === 0) return [];
137
+
138
+ return candidates
139
+ .map((c) => ({
140
+ tool: c.tool,
141
+ confidence: Math.round((c.count / totalFromCurrent) * 100) / 100,
142
+ }))
143
+ .sort((a, b) => b.confidence - a.confidence)
144
+ .slice(0, 5);
145
+ }
146
+
147
+ /**
148
+ * Recommend a skill based on user query using BM25.
149
+ * @param {string} userQuery
150
+ * @param {Array<{ id: string, description: string, category: string }>} skills
151
+ */
152
+ recommendSkill(userQuery, skills) {
153
+ if (!userQuery || !skills || skills.length === 0) return [];
154
+
155
+ // Build BM25 index
156
+ const bm25 = new _deps.BM25Search();
157
+ const docs = skills.map((s) => ({
158
+ id: s.id,
159
+ title: s.id,
160
+ content: `${s.id} ${s.description || ""} ${s.category || ""}`,
161
+ }));
162
+ bm25.indexDocuments(docs);
163
+
164
+ const results = bm25.search(userQuery, { topK: 5, threshold: 0.1 });
165
+ return results.map((r) => ({
166
+ skillId: r.doc.id,
167
+ score: r.score,
168
+ }));
169
+ }
170
+
171
+ /**
172
+ * Get chain statistics.
173
+ */
174
+ getChainStats() {
175
+ const chains = [...this._chainFrequency.entries()]
176
+ .map(([key, count]) => ({ chain: key, count }))
177
+ .sort((a, b) => b.count - a.count);
178
+
179
+ return {
180
+ totalUsages: this._toolUsageLog.length,
181
+ uniqueChains: chains.length,
182
+ topChains: chains.slice(0, 10),
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Get tool similarity matrix.
188
+ */
189
+ getSimilarityMatrix() {
190
+ const tools = [...this._toolFeatures.keys()];
191
+ const matrix = {};
192
+
193
+ for (const t1 of tools) {
194
+ matrix[t1] = {};
195
+ for (const t2 of tools) {
196
+ if (t1 !== t2) {
197
+ matrix[t1][t2] =
198
+ Math.round(this.calculateSimilarity(t1, t2) * 1000) / 1000;
199
+ }
200
+ }
201
+ }
202
+
203
+ return matrix;
204
+ }
205
+ }