chainlesschain 0.37.10 → 0.37.12

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 (39) hide show
  1. package/README.md +166 -10
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +374 -0
  4. package/src/commands/bi.js +240 -0
  5. package/src/commands/cowork.js +317 -0
  6. package/src/commands/economy.js +375 -0
  7. package/src/commands/evolution.js +398 -0
  8. package/src/commands/hmemory.js +273 -0
  9. package/src/commands/hook.js +260 -0
  10. package/src/commands/init.js +184 -0
  11. package/src/commands/lowcode.js +320 -0
  12. package/src/commands/plugin.js +55 -2
  13. package/src/commands/sandbox.js +366 -0
  14. package/src/commands/skill.js +254 -201
  15. package/src/commands/workflow.js +359 -0
  16. package/src/commands/zkp.js +277 -0
  17. package/src/index.js +44 -0
  18. package/src/lib/a2a-protocol.js +371 -0
  19. package/src/lib/agent-coordinator.js +273 -0
  20. package/src/lib/agent-economy.js +369 -0
  21. package/src/lib/app-builder.js +377 -0
  22. package/src/lib/bi-engine.js +299 -0
  23. package/src/lib/cowork/ab-comparator-cli.js +180 -0
  24. package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
  25. package/src/lib/cowork/debate-review-cli.js +144 -0
  26. package/src/lib/cowork/decision-kb-cli.js +153 -0
  27. package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
  28. package/src/lib/cowork-adapter.js +106 -0
  29. package/src/lib/evolution-system.js +508 -0
  30. package/src/lib/hierarchical-memory.js +471 -0
  31. package/src/lib/hook-manager.js +387 -0
  32. package/src/lib/plugin-manager.js +118 -0
  33. package/src/lib/project-detector.js +53 -0
  34. package/src/lib/sandbox-v2.js +503 -0
  35. package/src/lib/service-container.js +183 -0
  36. package/src/lib/skill-loader.js +274 -0
  37. package/src/lib/workflow-engine.js +503 -0
  38. package/src/lib/zkp-engine.js +241 -0
  39. package/src/repl/agent-repl.js +117 -112
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Hierarchical Memory 2.0 for CLI
3
+ *
4
+ * Four-layer memory system: working → short-term → long-term → core
5
+ * with Ebbinghaus forgetting curve and spacing effect reinforcement.
6
+ */
7
+
8
+ // ─── Configuration ───────────────────────────────────────────────
9
+ export const MEMORY_CONFIG = {
10
+ workingCapacity: 50,
11
+ shortTermCapacity: 500,
12
+ longTermCapacity: 10000,
13
+ forgettingRate: 0.1,
14
+ recallThreshold: 0.3,
15
+ };
16
+
17
+ // ─── In-memory layers ────────────────────────────────────────────
18
+ // Map<id, { id, content, type, importance, accessCount, createdAt, lastAccessed }>
19
+ export const _working = new Map();
20
+ export const _shortTerm = new Map();
21
+
22
+ // ─── Helpers ─────────────────────────────────────────────────────
23
+ function generateId() {
24
+ return `hmem-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
25
+ }
26
+
27
+ function nowISO() {
28
+ return new Date().toISOString();
29
+ }
30
+
31
+ /**
32
+ * Calculate retention using Ebbinghaus forgetting curve.
33
+ * retention = e^(-decay_rate * age_hours)
34
+ */
35
+ function calcRetention(
36
+ createdOrAccessed,
37
+ decayRate = MEMORY_CONFIG.forgettingRate,
38
+ ) {
39
+ const ageMs = Date.now() - new Date(createdOrAccessed).getTime();
40
+ const ageHours = Math.max(0, ageMs / (1000 * 60 * 60));
41
+ return Math.exp(-decayRate * ageHours);
42
+ }
43
+
44
+ // ─── Table setup ─────────────────────────────────────────────────
45
+
46
+ /**
47
+ * Create persistent memory tables for long-term / core / sharing
48
+ */
49
+ export function ensureMemoryTables(db) {
50
+ db.exec(`
51
+ CREATE TABLE IF NOT EXISTS memory_long_term (
52
+ id TEXT PRIMARY KEY,
53
+ content TEXT NOT NULL,
54
+ type TEXT DEFAULT 'episodic',
55
+ importance REAL DEFAULT 0.5,
56
+ access_count INTEGER DEFAULT 0,
57
+ retention REAL DEFAULT 1.0,
58
+ created_at TEXT DEFAULT (datetime('now')),
59
+ last_accessed TEXT DEFAULT (datetime('now')),
60
+ layer TEXT DEFAULT 'long-term'
61
+ )
62
+ `);
63
+
64
+ db.exec(`
65
+ CREATE TABLE IF NOT EXISTS memory_core (
66
+ id TEXT PRIMARY KEY,
67
+ content TEXT NOT NULL,
68
+ type TEXT DEFAULT 'semantic',
69
+ importance REAL DEFAULT 1.0,
70
+ access_count INTEGER DEFAULT 0,
71
+ created_at TEXT DEFAULT (datetime('now')),
72
+ last_accessed TEXT DEFAULT (datetime('now'))
73
+ )
74
+ `);
75
+
76
+ db.exec(`
77
+ CREATE TABLE IF NOT EXISTS memory_sharing (
78
+ id TEXT PRIMARY KEY,
79
+ memory_id TEXT NOT NULL,
80
+ source_agent TEXT DEFAULT 'local',
81
+ target_agent TEXT NOT NULL,
82
+ privacy_level TEXT DEFAULT 'filtered',
83
+ shared_at TEXT DEFAULT (datetime('now'))
84
+ )
85
+ `);
86
+ }
87
+
88
+ // ─── Store ───────────────────────────────────────────────────────
89
+
90
+ /**
91
+ * Store a memory at the appropriate layer based on importance.
92
+ * core >= 0.9, long-term >= 0.6, short-term >= 0.3, working < 0.3
93
+ */
94
+ export function storeMemory(db, content, options = {}) {
95
+ if (!content || !content.trim()) {
96
+ throw new Error("Memory content cannot be empty");
97
+ }
98
+
99
+ const importance = Math.max(
100
+ 0,
101
+ Math.min(1, parseFloat(options.importance) || 0.5),
102
+ );
103
+ const type = options.type || "episodic";
104
+ const id = generateId();
105
+ const now = nowISO();
106
+
107
+ let layer;
108
+ if (importance >= 0.9) {
109
+ layer = "core";
110
+ ensureMemoryTables(db);
111
+ db.prepare(
112
+ `INSERT INTO memory_core (id, content, type, importance, created_at, last_accessed) VALUES (?, ?, ?, ?, ?, ?)`,
113
+ ).run(id, content, type, importance, now, now);
114
+ } else if (importance >= 0.6) {
115
+ layer = "long-term";
116
+ ensureMemoryTables(db);
117
+ db.prepare(
118
+ `INSERT INTO memory_long_term (id, content, type, importance, layer, created_at, last_accessed) VALUES (?, ?, ?, ?, ?, ?, ?)`,
119
+ ).run(id, content, type, importance, "long-term", now, now);
120
+ } else if (importance >= 0.3) {
121
+ layer = "short-term";
122
+ if (_shortTerm.size >= MEMORY_CONFIG.shortTermCapacity) {
123
+ // Evict oldest
124
+ const oldest = [..._shortTerm.entries()].sort(
125
+ (a, b) => new Date(a[1].lastAccessed) - new Date(b[1].lastAccessed),
126
+ )[0];
127
+ if (oldest) _shortTerm.delete(oldest[0]);
128
+ }
129
+ _shortTerm.set(id, {
130
+ id,
131
+ content,
132
+ type,
133
+ importance,
134
+ accessCount: 0,
135
+ createdAt: now,
136
+ lastAccessed: now,
137
+ });
138
+ } else {
139
+ layer = "working";
140
+ if (_working.size >= MEMORY_CONFIG.workingCapacity) {
141
+ const oldest = [..._working.entries()].sort(
142
+ (a, b) => new Date(a[1].lastAccessed) - new Date(b[1].lastAccessed),
143
+ )[0];
144
+ if (oldest) _working.delete(oldest[0]);
145
+ }
146
+ _working.set(id, {
147
+ id,
148
+ content,
149
+ type,
150
+ importance,
151
+ accessCount: 0,
152
+ createdAt: now,
153
+ lastAccessed: now,
154
+ });
155
+ }
156
+
157
+ return { id, layer };
158
+ }
159
+
160
+ // ─── Recall ──────────────────────────────────────────────────────
161
+
162
+ /**
163
+ * Search all memory layers with Ebbinghaus forgetting curve.
164
+ * Strengthens recalled memories (spacing effect).
165
+ */
166
+ export function recallMemory(db, query, options = {}) {
167
+ if (!query || !query.trim()) return [];
168
+
169
+ const limit = Math.max(1, parseInt(options.limit) || 20);
170
+ const pattern = query.toLowerCase();
171
+ const results = [];
172
+
173
+ // Search working memory
174
+ for (const mem of _working.values()) {
175
+ if (mem.content.toLowerCase().includes(pattern)) {
176
+ const retention = calcRetention(mem.lastAccessed);
177
+ if (retention >= MEMORY_CONFIG.recallThreshold) {
178
+ mem.accessCount++;
179
+ mem.lastAccessed = nowISO();
180
+ results.push({ ...mem, layer: "working", retention });
181
+ }
182
+ }
183
+ }
184
+
185
+ // Search short-term memory
186
+ for (const mem of _shortTerm.values()) {
187
+ if (mem.content.toLowerCase().includes(pattern)) {
188
+ const retention = calcRetention(mem.lastAccessed);
189
+ if (retention >= MEMORY_CONFIG.recallThreshold) {
190
+ mem.accessCount++;
191
+ mem.lastAccessed = nowISO();
192
+ results.push({ ...mem, layer: "short-term", retention });
193
+ }
194
+ }
195
+ }
196
+
197
+ // Search long-term memory (DB)
198
+ ensureMemoryTables(db);
199
+ const ltRows = db
200
+ .prepare(`SELECT * FROM memory_long_term WHERE content LIKE ? LIMIT ?`)
201
+ .all(`%${query}%`, limit);
202
+ for (const row of ltRows) {
203
+ const retention = calcRetention(row.last_accessed);
204
+ if (retention >= MEMORY_CONFIG.recallThreshold) {
205
+ db.prepare(
206
+ `UPDATE memory_long_term SET access_count = access_count + 1, last_accessed = ? WHERE id = ?`,
207
+ ).run(nowISO(), row.id);
208
+ results.push({
209
+ id: row.id,
210
+ content: row.content,
211
+ type: row.type,
212
+ importance: row.importance,
213
+ accessCount: row.access_count + 1,
214
+ createdAt: row.created_at,
215
+ lastAccessed: nowISO(),
216
+ layer: "long-term",
217
+ retention,
218
+ });
219
+ }
220
+ }
221
+
222
+ // Search core memory (DB) — core memories don't decay
223
+ const coreRows = db
224
+ .prepare(`SELECT * FROM memory_core WHERE content LIKE ? LIMIT ?`)
225
+ .all(`%${query}%`, limit);
226
+ for (const row of coreRows) {
227
+ db.prepare(
228
+ `UPDATE memory_core SET access_count = access_count + 1, last_accessed = ? WHERE id = ?`,
229
+ ).run(nowISO(), row.id);
230
+ results.push({
231
+ id: row.id,
232
+ content: row.content,
233
+ type: row.type,
234
+ importance: row.importance,
235
+ accessCount: row.access_count + 1,
236
+ createdAt: row.created_at,
237
+ lastAccessed: nowISO(),
238
+ layer: "core",
239
+ retention: 1.0,
240
+ });
241
+ }
242
+
243
+ // Sort by retention * importance descending
244
+ results.sort(
245
+ (a, b) => b.retention * b.importance - a.retention * a.importance,
246
+ );
247
+ return results.slice(0, limit);
248
+ }
249
+
250
+ // ─── Consolidate ─────────────────────────────────────────────────
251
+
252
+ /**
253
+ * Promote memories up the hierarchy and forget stale entries.
254
+ * working → short-term (accessCount >= 3)
255
+ * short-term → long-term (accessCount >= 5)
256
+ * Forget entries below recall threshold.
257
+ */
258
+ export function consolidateMemory(db) {
259
+ ensureMemoryTables(db);
260
+ let promoted = 0;
261
+ let forgotten = 0;
262
+
263
+ // Promote working → short-term
264
+ for (const [id, mem] of _working) {
265
+ const retention = calcRetention(mem.lastAccessed);
266
+ if (retention < MEMORY_CONFIG.recallThreshold) {
267
+ _working.delete(id);
268
+ forgotten++;
269
+ } else if (mem.accessCount >= 3) {
270
+ _working.delete(id);
271
+ _shortTerm.set(id, { ...mem, lastAccessed: nowISO() });
272
+ promoted++;
273
+ }
274
+ }
275
+
276
+ // Promote short-term → long-term
277
+ for (const [id, mem] of _shortTerm) {
278
+ const retention = calcRetention(mem.lastAccessed);
279
+ if (retention < MEMORY_CONFIG.recallThreshold) {
280
+ _shortTerm.delete(id);
281
+ forgotten++;
282
+ } else if (mem.accessCount >= 5) {
283
+ _shortTerm.delete(id);
284
+ const now = nowISO();
285
+ db.prepare(
286
+ `INSERT INTO memory_long_term (id, content, type, importance, access_count, layer, created_at, last_accessed) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
287
+ ).run(
288
+ id,
289
+ mem.content,
290
+ mem.type,
291
+ mem.importance,
292
+ mem.accessCount,
293
+ "long-term",
294
+ mem.createdAt,
295
+ now,
296
+ );
297
+ promoted++;
298
+ }
299
+ }
300
+
301
+ return { promoted, forgotten };
302
+ }
303
+
304
+ // ─── Episodic / Semantic search ──────────────────────────────────
305
+
306
+ /**
307
+ * Search episodic-type memories across all layers
308
+ */
309
+ export function searchEpisodic(db, query, options = {}) {
310
+ return _searchByType(db, query, "episodic", options);
311
+ }
312
+
313
+ /**
314
+ * Search semantic-type memories across all layers
315
+ */
316
+ export function searchSemantic(db, query, options = {}) {
317
+ return _searchByType(db, query, "semantic", options);
318
+ }
319
+
320
+ function _searchByType(db, query, type, options = {}) {
321
+ if (!query || !query.trim()) return [];
322
+
323
+ const limit = Math.max(1, parseInt(options.limit) || 20);
324
+ const pattern = query.toLowerCase();
325
+ const results = [];
326
+
327
+ // In-memory layers
328
+ for (const mem of _working.values()) {
329
+ if (mem.type === type && mem.content.toLowerCase().includes(pattern)) {
330
+ results.push({ ...mem, layer: "working" });
331
+ }
332
+ }
333
+ for (const mem of _shortTerm.values()) {
334
+ if (mem.type === type && mem.content.toLowerCase().includes(pattern)) {
335
+ results.push({ ...mem, layer: "short-term" });
336
+ }
337
+ }
338
+
339
+ // DB layers
340
+ ensureMemoryTables(db);
341
+ const ltRows = db
342
+ .prepare(
343
+ `SELECT * FROM memory_long_term WHERE type = ? AND content LIKE ? LIMIT ?`,
344
+ )
345
+ .all(type, `%${query}%`, limit);
346
+ for (const row of ltRows) {
347
+ results.push({
348
+ id: row.id,
349
+ content: row.content,
350
+ type: row.type,
351
+ importance: row.importance,
352
+ accessCount: row.access_count,
353
+ createdAt: row.created_at,
354
+ lastAccessed: row.last_accessed,
355
+ layer: "long-term",
356
+ });
357
+ }
358
+
359
+ const coreRows = db
360
+ .prepare(
361
+ `SELECT * FROM memory_core WHERE type = ? AND content LIKE ? LIMIT ?`,
362
+ )
363
+ .all(type, `%${query}%`, limit);
364
+ for (const row of coreRows) {
365
+ results.push({
366
+ id: row.id,
367
+ content: row.content,
368
+ type: row.type,
369
+ importance: row.importance,
370
+ accessCount: row.access_count,
371
+ createdAt: row.created_at,
372
+ lastAccessed: row.last_accessed,
373
+ layer: "core",
374
+ });
375
+ }
376
+
377
+ return results.slice(0, limit);
378
+ }
379
+
380
+ // ─── Sharing ─────────────────────────────────────────────────────
381
+
382
+ /**
383
+ * Share a memory with another agent
384
+ */
385
+ export function shareMemory(
386
+ db,
387
+ memoryId,
388
+ targetAgentId,
389
+ privacyLevel = "filtered",
390
+ ) {
391
+ ensureMemoryTables(db);
392
+
393
+ if (!memoryId || !targetAgentId) {
394
+ throw new Error("memoryId and targetAgentId are required");
395
+ }
396
+
397
+ const id = `share-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
398
+ db.prepare(
399
+ `INSERT INTO memory_sharing (id, memory_id, target_agent, privacy_level) VALUES (?, ?, ?, ?)`,
400
+ ).run(id, memoryId, targetAgentId, privacyLevel);
401
+
402
+ return { id, memoryId, targetAgentId, privacyLevel };
403
+ }
404
+
405
+ // ─── Prune ───────────────────────────────────────────────────────
406
+
407
+ /**
408
+ * Remove weak old memories from all layers
409
+ */
410
+ export function pruneMemory(db, options = {}) {
411
+ ensureMemoryTables(db);
412
+ const maxAgeHours = parseFloat(options.maxAge) || 720; // 30 days default
413
+ let pruned = 0;
414
+
415
+ // Prune in-memory layers by retention
416
+ for (const [id, mem] of _working) {
417
+ const retention = calcRetention(mem.lastAccessed);
418
+ if (retention < MEMORY_CONFIG.recallThreshold) {
419
+ _working.delete(id);
420
+ pruned++;
421
+ }
422
+ }
423
+ for (const [id, mem] of _shortTerm) {
424
+ const retention = calcRetention(mem.lastAccessed);
425
+ if (retention < MEMORY_CONFIG.recallThreshold) {
426
+ _shortTerm.delete(id);
427
+ pruned++;
428
+ }
429
+ }
430
+
431
+ // Prune long-term DB entries older than maxAge with low access
432
+ const cutoff = new Date(
433
+ Date.now() - maxAgeHours * 60 * 60 * 1000,
434
+ ).toISOString();
435
+ const result = db
436
+ .prepare(
437
+ `DELETE FROM memory_long_term WHERE last_accessed < ? AND access_count < 3 AND importance < 0.5`,
438
+ )
439
+ .run(cutoff);
440
+ pruned += result.changes;
441
+
442
+ return { pruned };
443
+ }
444
+
445
+ // ─── Stats ───────────────────────────────────────────────────────
446
+
447
+ /**
448
+ * Return memory counts per layer
449
+ */
450
+ export function getMemoryStats(db) {
451
+ ensureMemoryTables(db);
452
+
453
+ const ltCount = db
454
+ .prepare(`SELECT COUNT(*) as count FROM memory_long_term`)
455
+ .get();
456
+ const coreCount = db
457
+ .prepare(`SELECT COUNT(*) as count FROM memory_core`)
458
+ .get();
459
+ const shareCount = db
460
+ .prepare(`SELECT COUNT(*) as count FROM memory_sharing`)
461
+ .get();
462
+
463
+ return {
464
+ working: _working.size,
465
+ shortTerm: _shortTerm.size,
466
+ longTerm: ltCount.count,
467
+ core: coreCount.count,
468
+ shared: shareCount.count,
469
+ total: _working.size + _shortTerm.size + ltCount.count + coreCount.count,
470
+ };
471
+ }