cognitive-core 0.0.1 → 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.
- package/README.md +363 -2
- package/SKILL.md +193 -0
- package/dist/agents/index.d.ts +3 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +5 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/mock-provider.d.ts +23 -0
- package/dist/agents/mock-provider.d.ts.map +1 -0
- package/dist/agents/mock-provider.js +71 -0
- package/dist/agents/mock-provider.js.map +1 -0
- package/dist/agents/types.d.ts +98 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +44 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/atlas.d.ts +196 -0
- package/dist/atlas.d.ts.map +1 -0
- package/dist/atlas.js +373 -0
- package/dist/atlas.js.map +1 -0
- package/dist/bin/cognitive-core.d.ts +18 -0
- package/dist/bin/cognitive-core.d.ts.map +1 -0
- package/dist/bin/cognitive-core.js +419 -0
- package/dist/bin/cognitive-core.js.map +1 -0
- package/dist/embeddings/bm25.d.ts +104 -0
- package/dist/embeddings/bm25.d.ts.map +1 -0
- package/dist/embeddings/bm25.js +264 -0
- package/dist/embeddings/bm25.js.map +1 -0
- package/dist/embeddings/index.d.ts +12 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +16 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/manager.d.ts +112 -0
- package/dist/embeddings/manager.d.ts.map +1 -0
- package/dist/embeddings/manager.js +215 -0
- package/dist/embeddings/manager.js.map +1 -0
- package/dist/embeddings/provider.d.ts +101 -0
- package/dist/embeddings/provider.d.ts.map +1 -0
- package/dist/embeddings/provider.js +232 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/embeddings/vector-store.d.ts +101 -0
- package/dist/embeddings/vector-store.d.ts.map +1 -0
- package/dist/embeddings/vector-store.js +256 -0
- package/dist/embeddings/vector-store.js.map +1 -0
- package/dist/factory.d.ts +193 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +109 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/learning/analyzer.d.ts +110 -0
- package/dist/learning/analyzer.d.ts.map +1 -0
- package/dist/learning/analyzer.js +213 -0
- package/dist/learning/analyzer.js.map +1 -0
- package/dist/learning/effectiveness.d.ts +158 -0
- package/dist/learning/effectiveness.d.ts.map +1 -0
- package/dist/learning/effectiveness.js +251 -0
- package/dist/learning/effectiveness.js.map +1 -0
- package/dist/learning/index.d.ts +8 -0
- package/dist/learning/index.d.ts.map +1 -0
- package/dist/learning/index.js +11 -0
- package/dist/learning/index.js.map +1 -0
- package/dist/learning/llm-extractor.d.ts +88 -0
- package/dist/learning/llm-extractor.d.ts.map +1 -0
- package/dist/learning/llm-extractor.js +372 -0
- package/dist/learning/llm-extractor.js.map +1 -0
- package/dist/learning/meta-learner.d.ts +80 -0
- package/dist/learning/meta-learner.d.ts.map +1 -0
- package/dist/learning/meta-learner.js +355 -0
- package/dist/learning/meta-learner.js.map +1 -0
- package/dist/learning/pipeline.d.ts +65 -0
- package/dist/learning/pipeline.d.ts.map +1 -0
- package/dist/learning/pipeline.js +170 -0
- package/dist/learning/pipeline.js.map +1 -0
- package/dist/learning/playbook-extractor.d.ts +113 -0
- package/dist/learning/playbook-extractor.d.ts.map +1 -0
- package/dist/learning/playbook-extractor.js +523 -0
- package/dist/learning/playbook-extractor.js.map +1 -0
- package/dist/learning/usage-inference.d.ts +82 -0
- package/dist/learning/usage-inference.d.ts.map +1 -0
- package/dist/learning/usage-inference.js +261 -0
- package/dist/learning/usage-inference.js.map +1 -0
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +6 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/playbook-server.d.ts +120 -0
- package/dist/mcp/playbook-server.d.ts.map +1 -0
- package/dist/mcp/playbook-server.js +427 -0
- package/dist/mcp/playbook-server.js.map +1 -0
- package/dist/memory/curated-loader.d.ts +62 -0
- package/dist/memory/curated-loader.d.ts.map +1 -0
- package/dist/memory/curated-loader.js +106 -0
- package/dist/memory/curated-loader.js.map +1 -0
- package/dist/memory/experience.d.ts +122 -0
- package/dist/memory/experience.d.ts.map +1 -0
- package/dist/memory/experience.js +392 -0
- package/dist/memory/experience.js.map +1 -0
- package/dist/memory/index.d.ts +6 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/meta.d.ts +90 -0
- package/dist/memory/meta.d.ts.map +1 -0
- package/dist/memory/meta.js +362 -0
- package/dist/memory/meta.js.map +1 -0
- package/dist/memory/playbook.d.ts +133 -0
- package/dist/memory/playbook.d.ts.map +1 -0
- package/dist/memory/playbook.js +357 -0
- package/dist/memory/playbook.js.map +1 -0
- package/dist/memory/system.d.ts +167 -0
- package/dist/memory/system.d.ts.map +1 -0
- package/dist/memory/system.js +383 -0
- package/dist/memory/system.js.map +1 -0
- package/dist/runtime/backends/acp.d.ts +67 -0
- package/dist/runtime/backends/acp.d.ts.map +1 -0
- package/dist/runtime/backends/acp.js +290 -0
- package/dist/runtime/backends/acp.js.map +1 -0
- package/dist/runtime/backends/index.d.ts +5 -0
- package/dist/runtime/backends/index.d.ts.map +1 -0
- package/dist/runtime/backends/index.js +6 -0
- package/dist/runtime/backends/index.js.map +1 -0
- package/dist/runtime/backends/mock.d.ts +67 -0
- package/dist/runtime/backends/mock.d.ts.map +1 -0
- package/dist/runtime/backends/mock.js +153 -0
- package/dist/runtime/backends/mock.js.map +1 -0
- package/dist/runtime/backends/subprocess.d.ts +56 -0
- package/dist/runtime/backends/subprocess.d.ts.map +1 -0
- package/dist/runtime/backends/subprocess.js +260 -0
- package/dist/runtime/backends/subprocess.js.map +1 -0
- package/dist/runtime/flows/learning.d.ts +73 -0
- package/dist/runtime/flows/learning.d.ts.map +1 -0
- package/dist/runtime/flows/learning.js +116 -0
- package/dist/runtime/flows/learning.js.map +1 -0
- package/dist/runtime/flows/validation.d.ts +122 -0
- package/dist/runtime/flows/validation.d.ts.map +1 -0
- package/dist/runtime/flows/validation.js +223 -0
- package/dist/runtime/flows/validation.js.map +1 -0
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/manager.d.ts +116 -0
- package/dist/runtime/manager.d.ts.map +1 -0
- package/dist/runtime/manager.js +416 -0
- package/dist/runtime/manager.js.map +1 -0
- package/dist/runtime/types.d.ts +138 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +2 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/search/evaluator.d.ts +102 -0
- package/dist/search/evaluator.d.ts.map +1 -0
- package/dist/search/evaluator.js +352 -0
- package/dist/search/evaluator.js.map +1 -0
- package/dist/search/index.d.ts +7 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +11 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/refinement-loop.d.ts +73 -0
- package/dist/search/refinement-loop.d.ts.map +1 -0
- package/dist/search/refinement-loop.js +245 -0
- package/dist/search/refinement-loop.js.map +1 -0
- package/dist/search/refinement-types.d.ts +154 -0
- package/dist/search/refinement-types.d.ts.map +1 -0
- package/dist/search/refinement-types.js +99 -0
- package/dist/search/refinement-types.js.map +1 -0
- package/dist/search/router.d.ts +61 -0
- package/dist/search/router.d.ts.map +1 -0
- package/dist/search/router.js +197 -0
- package/dist/search/router.js.map +1 -0
- package/dist/search/solver.d.ts +75 -0
- package/dist/search/solver.d.ts.map +1 -0
- package/dist/search/solver.js +216 -0
- package/dist/search/solver.js.map +1 -0
- package/dist/search/verification-runner.d.ts +125 -0
- package/dist/search/verification-runner.d.ts.map +1 -0
- package/dist/search/verification-runner.js +440 -0
- package/dist/search/verification-runner.js.map +1 -0
- package/dist/surfacing/index.d.ts +2 -0
- package/dist/surfacing/index.d.ts.map +1 -0
- package/dist/surfacing/index.js +2 -0
- package/dist/surfacing/index.js.map +1 -0
- package/dist/surfacing/skill-library.d.ts +158 -0
- package/dist/surfacing/skill-library.d.ts.map +1 -0
- package/dist/surfacing/skill-library.js +429 -0
- package/dist/surfacing/skill-library.js.map +1 -0
- package/dist/types/config.d.ts +1113 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +274 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +14 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/memory.d.ts +339 -0
- package/dist/types/memory.d.ts.map +1 -0
- package/dist/types/memory.js +207 -0
- package/dist/types/memory.js.map +1 -0
- package/dist/types/meta.d.ts +146 -0
- package/dist/types/meta.d.ts.map +1 -0
- package/dist/types/meta.js +51 -0
- package/dist/types/meta.js.map +1 -0
- package/dist/types/outcome.d.ts +42 -0
- package/dist/types/outcome.d.ts.map +1 -0
- package/dist/types/outcome.js +50 -0
- package/dist/types/outcome.js.map +1 -0
- package/dist/types/playbook.d.ts +119 -0
- package/dist/types/playbook.d.ts.map +1 -0
- package/dist/types/playbook.js +71 -0
- package/dist/types/playbook.js.map +1 -0
- package/dist/types/step.d.ts +44 -0
- package/dist/types/step.d.ts.map +1 -0
- package/dist/types/step.js +32 -0
- package/dist/types/step.js.map +1 -0
- package/dist/types/task.d.ts +91 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +39 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/trajectory.d.ts +221 -0
- package/dist/types/trajectory.d.ts.map +1 -0
- package/dist/types/trajectory.js +60 -0
- package/dist/types/trajectory.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/similarity.d.ts +31 -0
- package/dist/utils/similarity.d.ts.map +1 -0
- package/dist/utils/similarity.js +107 -0
- package/dist/utils/similarity.js.map +1 -0
- package/dist/utils/storage.d.ts +106 -0
- package/dist/utils/storage.d.ts.map +1 -0
- package/dist/utils/storage.js +203 -0
- package/dist/utils/storage.js.map +1 -0
- package/dist/utils/validation.d.ts +129 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +171 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +61 -9
- package/scripts/migrate-to-playbooks.ts +307 -0
- package/src/agents/index.ts +14 -0
- package/src/agents/mock-provider.ts +93 -0
- package/src/agents/types.ts +137 -0
- package/src/atlas.ts +560 -0
- package/src/bin/cognitive-core.ts +470 -0
- package/src/embeddings/bm25.ts +337 -0
- package/src/embeddings/index.ts +39 -0
- package/src/embeddings/manager.ts +288 -0
- package/src/embeddings/provider.ts +311 -0
- package/src/embeddings/vector-store.ts +353 -0
- package/src/factory.ts +263 -0
- package/src/index.ts +246 -0
- package/src/learning/analyzer.ts +335 -0
- package/src/learning/effectiveness.ts +428 -0
- package/src/learning/index.ts +58 -0
- package/src/learning/llm-extractor.ts +542 -0
- package/src/learning/meta-learner.ts +516 -0
- package/src/learning/pipeline.ts +244 -0
- package/src/learning/playbook-extractor.ts +702 -0
- package/src/learning/usage-inference.ts +372 -0
- package/src/mcp/index.ts +12 -0
- package/src/mcp/playbook-server.ts +565 -0
- package/src/memory/curated-loader.ts +160 -0
- package/src/memory/experience.ts +515 -0
- package/src/memory/index.ts +27 -0
- package/src/memory/meta.ts +506 -0
- package/src/memory/playbook.ts +493 -0
- package/src/memory/system.ts +551 -0
- package/src/runtime/backends/acp.ts +378 -0
- package/src/runtime/backends/index.ts +24 -0
- package/src/runtime/backends/mock.ts +218 -0
- package/src/runtime/backends/subprocess.ts +356 -0
- package/src/runtime/flows/learning.ts +183 -0
- package/src/runtime/flows/validation.ts +381 -0
- package/src/runtime/index.ts +53 -0
- package/src/runtime/manager.ts +541 -0
- package/src/runtime/types.ts +157 -0
- package/src/search/evaluator.ts +474 -0
- package/src/search/index.ts +59 -0
- package/src/search/refinement-loop.ts +363 -0
- package/src/search/refinement-types.ts +159 -0
- package/src/search/router.ts +261 -0
- package/src/search/solver.ts +303 -0
- package/src/search/verification-runner.ts +570 -0
- package/src/surfacing/index.ts +6 -0
- package/src/surfacing/skill-library.ts +594 -0
- package/src/types/config.ts +333 -0
- package/src/types/index.ts +130 -0
- package/src/types/memory.ts +270 -0
- package/src/types/meta.ts +218 -0
- package/src/types/outcome.ts +66 -0
- package/src/types/playbook.ts +196 -0
- package/src/types/step.ts +40 -0
- package/src/types/task.ts +52 -0
- package/src/types/trajectory.ts +80 -0
- package/src/utils/index.ts +38 -0
- package/src/utils/similarity.ts +139 -0
- package/src/utils/storage.ts +249 -0
- package/src/utils/validation.ts +286 -0
- package/tests/embeddings/bm25.test.ts +130 -0
- package/tests/embeddings/manager.test.ts +205 -0
- package/tests/integration/atlas.test.ts +266 -0
- package/tests/integration/e2e.test.ts +929 -0
- package/tests/learning/analyzer.test.ts +426 -0
- package/tests/learning/effectiveness.test.ts +542 -0
- package/tests/learning/pipeline.test.ts +176 -0
- package/tests/learning/playbook-extractor-provenance.test.ts +114 -0
- package/tests/learning/usage-inference.test.ts +254 -0
- package/tests/mcp/playbook-server.test.ts +252 -0
- package/tests/memory/experience.test.ts +198 -0
- package/tests/memory/playbook.test.ts +338 -0
- package/tests/memory/provenance.test.ts +639 -0
- package/tests/memory/system.test.ts +325 -0
- package/tests/runtime/agent-manager.test.ts +512 -0
- package/tests/runtime/mock-backend.test.ts +248 -0
- package/tests/search/refinement-loop.test.ts +468 -0
- package/tests/search/refinement.test.ts +267 -0
- package/tests/search/router.test.ts +427 -0
- package/tests/surfacing/skill-library.test.ts +292 -0
- package/tests/types/outcome.test.ts +147 -0
- package/tests/types/step.test.ts +133 -0
- package/tests/types/task.test.ts +158 -0
- package/tests/types/trajectory.test.ts +253 -0
- package/tests/utils/similarity.test.ts +188 -0
- package/tests/utils/validation.test.ts +252 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +22 -0
- package/index.d.ts +0 -4
- package/index.js +0 -4
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import type { Experience } from '../types/index.js';
|
|
2
|
+
import { JsonStore } from '../utils/storage.js';
|
|
3
|
+
import { findTopK } from '../utils/similarity.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result of pruning operation
|
|
7
|
+
*/
|
|
8
|
+
export interface PruneResult {
|
|
9
|
+
/** IDs of pruned experiences */
|
|
10
|
+
prunedIds: string[];
|
|
11
|
+
/** IDs organized by pruning reason */
|
|
12
|
+
prunedByReason: {
|
|
13
|
+
age: string[];
|
|
14
|
+
usage: string[];
|
|
15
|
+
capacity: string[];
|
|
16
|
+
redundancy: string[];
|
|
17
|
+
failedOld: string[];
|
|
18
|
+
};
|
|
19
|
+
/** Total number pruned */
|
|
20
|
+
totalPruned: number;
|
|
21
|
+
/** Count remaining after pruning */
|
|
22
|
+
remainingCount: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Experience Memory - ReMem-style task-level retrieval
|
|
27
|
+
* Stores complete (task, solution, feedback) tuples
|
|
28
|
+
*/
|
|
29
|
+
export class ExperienceMemory {
|
|
30
|
+
private _store: JsonStore<Experience>;
|
|
31
|
+
private initialized = false;
|
|
32
|
+
|
|
33
|
+
constructor(baseDir: string) {
|
|
34
|
+
this._store = new JsonStore<Experience>(baseDir, 'experiences', {
|
|
35
|
+
autoSaveInterval: 60000,
|
|
36
|
+
pretty: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async init(): Promise<void> {
|
|
41
|
+
if (this.initialized) return;
|
|
42
|
+
await this._store.init();
|
|
43
|
+
this.initialized = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Store a new experience
|
|
48
|
+
*/
|
|
49
|
+
async add(experience: Experience): Promise<void> {
|
|
50
|
+
await this.init();
|
|
51
|
+
this._store.set(experience.id, experience);
|
|
52
|
+
await this._store.save(experience.id);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Retrieve an experience by ID
|
|
57
|
+
*/
|
|
58
|
+
async get(id: string): Promise<Experience | undefined> {
|
|
59
|
+
await this.init();
|
|
60
|
+
return this._store.get(id);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Find similar experiences
|
|
65
|
+
* Uses embedding similarity if available, falls back to text similarity
|
|
66
|
+
*/
|
|
67
|
+
async findSimilar(
|
|
68
|
+
query: string | number[],
|
|
69
|
+
options?: {
|
|
70
|
+
k?: number;
|
|
71
|
+
domain?: string;
|
|
72
|
+
minScore?: number;
|
|
73
|
+
successOnly?: boolean;
|
|
74
|
+
}
|
|
75
|
+
): Promise<Array<{ experience: Experience; score: number }>> {
|
|
76
|
+
await this.init();
|
|
77
|
+
|
|
78
|
+
const k = options?.k ?? 4; // ReMem default
|
|
79
|
+
let experiences = this._store.values();
|
|
80
|
+
|
|
81
|
+
// Filter by domain if specified
|
|
82
|
+
if (options?.domain) {
|
|
83
|
+
experiences = experiences.filter((e) => e.domain === options.domain);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Filter by success if specified
|
|
87
|
+
if (options?.successOnly) {
|
|
88
|
+
experiences = experiences.filter((e) => e.success);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Find similar experiences
|
|
92
|
+
const results = findTopK(
|
|
93
|
+
query,
|
|
94
|
+
experiences,
|
|
95
|
+
(e) => (Array.isArray(query) ? e.embedding : e.taskInput),
|
|
96
|
+
k
|
|
97
|
+
).map(({ item, score }) => ({
|
|
98
|
+
experience: item,
|
|
99
|
+
score,
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
// Filter by minimum score
|
|
103
|
+
if (options?.minScore !== undefined) {
|
|
104
|
+
return results.filter((r) => r.score >= options.minScore!);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update an experience (e.g., after refinement)
|
|
112
|
+
*/
|
|
113
|
+
async update(
|
|
114
|
+
id: string,
|
|
115
|
+
updates: Partial<Experience>
|
|
116
|
+
): Promise<Experience | undefined> {
|
|
117
|
+
await this.init();
|
|
118
|
+
const existing = this._store.get(id);
|
|
119
|
+
if (!existing) return undefined;
|
|
120
|
+
|
|
121
|
+
const updated = {
|
|
122
|
+
...existing,
|
|
123
|
+
...updates,
|
|
124
|
+
id: existing.id, // Preserve ID
|
|
125
|
+
};
|
|
126
|
+
this._store.set(id, updated);
|
|
127
|
+
await this._store.save(id);
|
|
128
|
+
return updated;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Increment usage count
|
|
133
|
+
*/
|
|
134
|
+
async recordUsage(id: string): Promise<void> {
|
|
135
|
+
await this.init();
|
|
136
|
+
const existing = this._store.get(id);
|
|
137
|
+
if (existing) {
|
|
138
|
+
existing.usageCount++;
|
|
139
|
+
existing.lastAccessedAt = new Date();
|
|
140
|
+
this._store.set(id, existing);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get all experiences
|
|
146
|
+
*/
|
|
147
|
+
async getAll(): Promise<Experience[]> {
|
|
148
|
+
await this.init();
|
|
149
|
+
return this._store.values();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get experiences by trajectory
|
|
154
|
+
*/
|
|
155
|
+
async getByTrajectory(trajectoryId: string): Promise<Experience[]> {
|
|
156
|
+
await this.init();
|
|
157
|
+
return this._store.values().filter((e) => e.trajectoryId === trajectoryId);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Prune low-value experiences
|
|
162
|
+
*/
|
|
163
|
+
async prune(options?: {
|
|
164
|
+
maxCount?: number;
|
|
165
|
+
minUsageCount?: number;
|
|
166
|
+
maxAgeDays?: number;
|
|
167
|
+
removeFailedOlderThanDays?: number;
|
|
168
|
+
similarityThreshold?: number;
|
|
169
|
+
preserveDomainCoverage?: boolean;
|
|
170
|
+
}): Promise<PruneResult> {
|
|
171
|
+
await this.init();
|
|
172
|
+
const pruned: string[] = [];
|
|
173
|
+
const prunedByReason: PruneResult['prunedByReason'] = {
|
|
174
|
+
age: [],
|
|
175
|
+
usage: [],
|
|
176
|
+
capacity: [],
|
|
177
|
+
redundancy: [],
|
|
178
|
+
failedOld: [],
|
|
179
|
+
};
|
|
180
|
+
let experiences = this._store.values();
|
|
181
|
+
|
|
182
|
+
// 1. Prune by age
|
|
183
|
+
if (options?.maxAgeDays) {
|
|
184
|
+
const cutoff = Date.now() - options.maxAgeDays * 24 * 60 * 60 * 1000;
|
|
185
|
+
const oldExperiences = experiences.filter(
|
|
186
|
+
(e) => e.createdAt.getTime() < cutoff
|
|
187
|
+
);
|
|
188
|
+
for (const exp of oldExperiences) {
|
|
189
|
+
this._store.delete(exp.id);
|
|
190
|
+
pruned.push(exp.id);
|
|
191
|
+
prunedByReason.age.push(exp.id);
|
|
192
|
+
}
|
|
193
|
+
experiences = experiences.filter((e) => e.createdAt.getTime() >= cutoff);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 2. Prune failed experiences older than threshold
|
|
197
|
+
if (options?.removeFailedOlderThanDays) {
|
|
198
|
+
const cutoff =
|
|
199
|
+
Date.now() - options.removeFailedOlderThanDays * 24 * 60 * 60 * 1000;
|
|
200
|
+
const failedOld = experiences.filter(
|
|
201
|
+
(e) => !e.success && e.createdAt.getTime() < cutoff
|
|
202
|
+
);
|
|
203
|
+
for (const exp of failedOld) {
|
|
204
|
+
this._store.delete(exp.id);
|
|
205
|
+
pruned.push(exp.id);
|
|
206
|
+
prunedByReason.failedOld.push(exp.id);
|
|
207
|
+
}
|
|
208
|
+
experiences = experiences.filter(
|
|
209
|
+
(e) => e.success || e.createdAt.getTime() >= cutoff
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 3. Prune by minimum usage count
|
|
214
|
+
if (options?.minUsageCount !== undefined) {
|
|
215
|
+
const lowUsage = experiences.filter(
|
|
216
|
+
(e) => e.usageCount < options.minUsageCount!
|
|
217
|
+
);
|
|
218
|
+
// Only prune low-usage if they're also old (>7 days)
|
|
219
|
+
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
220
|
+
for (const exp of lowUsage) {
|
|
221
|
+
if (exp.createdAt.getTime() < sevenDaysAgo) {
|
|
222
|
+
this._store.delete(exp.id);
|
|
223
|
+
pruned.push(exp.id);
|
|
224
|
+
prunedByReason.usage.push(exp.id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
experiences = this._store.values(); // Refresh
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 4. Prune redundant/similar experiences
|
|
231
|
+
if (options?.similarityThreshold) {
|
|
232
|
+
const redundant = await this.findRedundant(
|
|
233
|
+
experiences,
|
|
234
|
+
options.similarityThreshold,
|
|
235
|
+
options.preserveDomainCoverage
|
|
236
|
+
);
|
|
237
|
+
for (const exp of redundant) {
|
|
238
|
+
this._store.delete(exp.id);
|
|
239
|
+
pruned.push(exp.id);
|
|
240
|
+
prunedByReason.redundancy.push(exp.id);
|
|
241
|
+
}
|
|
242
|
+
experiences = this._store.values(); // Refresh
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 5. Prune by count (keep most valuable)
|
|
246
|
+
if (options?.maxCount && experiences.length > options.maxCount) {
|
|
247
|
+
// Sort by value
|
|
248
|
+
experiences.sort((a, b) => {
|
|
249
|
+
const scoreA = this.computeValue(a);
|
|
250
|
+
const scoreB = this.computeValue(b);
|
|
251
|
+
return scoreB - scoreA;
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// If preserving domain coverage, ensure each domain keeps some
|
|
255
|
+
if (options.preserveDomainCoverage) {
|
|
256
|
+
const kept = this.selectWithDomainCoverage(
|
|
257
|
+
experiences,
|
|
258
|
+
options.maxCount
|
|
259
|
+
);
|
|
260
|
+
const keptIds = new Set(kept.map((e) => e.id));
|
|
261
|
+
for (const exp of experiences) {
|
|
262
|
+
if (!keptIds.has(exp.id)) {
|
|
263
|
+
this._store.delete(exp.id);
|
|
264
|
+
pruned.push(exp.id);
|
|
265
|
+
prunedByReason.capacity.push(exp.id);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
const toRemove = experiences.slice(options.maxCount);
|
|
270
|
+
for (const exp of toRemove) {
|
|
271
|
+
this._store.delete(exp.id);
|
|
272
|
+
pruned.push(exp.id);
|
|
273
|
+
prunedByReason.capacity.push(exp.id);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Save changes
|
|
279
|
+
if (pruned.length > 0) {
|
|
280
|
+
await this._store.flush();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
prunedIds: pruned,
|
|
285
|
+
prunedByReason,
|
|
286
|
+
totalPruned: pruned.length,
|
|
287
|
+
remainingCount: this._store.size(),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Find redundant experiences (similar to others, lower value)
|
|
293
|
+
*/
|
|
294
|
+
private async findRedundant(
|
|
295
|
+
experiences: Experience[],
|
|
296
|
+
threshold: number,
|
|
297
|
+
preserveDomainCoverage = true
|
|
298
|
+
): Promise<Experience[]> {
|
|
299
|
+
const redundant: Experience[] = [];
|
|
300
|
+
const kept = new Set<string>();
|
|
301
|
+
const domainCounts: Record<string, number> = {};
|
|
302
|
+
|
|
303
|
+
// Sort by value (highest first)
|
|
304
|
+
const sorted = [...experiences].sort((a, b) => {
|
|
305
|
+
return this.computeValue(b) - this.computeValue(a);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
for (const exp of sorted) {
|
|
309
|
+
// Check if similar to any kept experience
|
|
310
|
+
let isDuplicate = false;
|
|
311
|
+
for (const keptId of kept) {
|
|
312
|
+
const keptExp = experiences.find((e) => e.id === keptId);
|
|
313
|
+
if (keptExp && this.computeSimilarity(exp, keptExp) > threshold) {
|
|
314
|
+
isDuplicate = true;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (isDuplicate) {
|
|
320
|
+
// Check domain coverage
|
|
321
|
+
if (preserveDomainCoverage) {
|
|
322
|
+
const domainCount = domainCounts[exp.domain] ?? 0;
|
|
323
|
+
if (domainCount < 3) {
|
|
324
|
+
// Keep at least 3 per domain
|
|
325
|
+
kept.add(exp.id);
|
|
326
|
+
domainCounts[exp.domain] = domainCount + 1;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
redundant.push(exp);
|
|
331
|
+
} else {
|
|
332
|
+
kept.add(exp.id);
|
|
333
|
+
domainCounts[exp.domain] = (domainCounts[exp.domain] ?? 0) + 1;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return redundant;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Compute similarity between two experiences
|
|
342
|
+
*/
|
|
343
|
+
private computeSimilarity(a: Experience, b: Experience): number {
|
|
344
|
+
// If both have embeddings, use cosine similarity
|
|
345
|
+
if (a.embedding && b.embedding) {
|
|
346
|
+
let dot = 0;
|
|
347
|
+
let normA = 0;
|
|
348
|
+
let normB = 0;
|
|
349
|
+
for (let i = 0; i < a.embedding.length; i++) {
|
|
350
|
+
dot += a.embedding[i] * b.embedding[i];
|
|
351
|
+
normA += a.embedding[i] * a.embedding[i];
|
|
352
|
+
normB += b.embedding[i] * b.embedding[i];
|
|
353
|
+
}
|
|
354
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB) || 1);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Fall back to Jaccard similarity on task input words
|
|
358
|
+
const wordsA = new Set(a.taskInput.toLowerCase().split(/\s+/));
|
|
359
|
+
const wordsB = new Set(b.taskInput.toLowerCase().split(/\s+/));
|
|
360
|
+
const intersection = new Set([...wordsA].filter((x) => wordsB.has(x)));
|
|
361
|
+
const union = new Set([...wordsA, ...wordsB]);
|
|
362
|
+
return intersection.size / union.size;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Select experiences while preserving domain coverage
|
|
367
|
+
*/
|
|
368
|
+
private selectWithDomainCoverage(
|
|
369
|
+
experiences: Experience[],
|
|
370
|
+
maxCount: number
|
|
371
|
+
): Experience[] {
|
|
372
|
+
const selected: Experience[] = [];
|
|
373
|
+
const domainCounts: Record<string, number> = {};
|
|
374
|
+
const domains = [...new Set(experiences.map((e) => e.domain))];
|
|
375
|
+
const minPerDomain = Math.max(1, Math.floor(maxCount / (domains.length * 2)));
|
|
376
|
+
|
|
377
|
+
// First pass: ensure minimum per domain
|
|
378
|
+
for (const domain of domains) {
|
|
379
|
+
const domainExps = experiences
|
|
380
|
+
.filter((e) => e.domain === domain)
|
|
381
|
+
.sort((a, b) => this.computeValue(b) - this.computeValue(a));
|
|
382
|
+
|
|
383
|
+
for (let i = 0; i < Math.min(minPerDomain, domainExps.length); i++) {
|
|
384
|
+
if (selected.length < maxCount) {
|
|
385
|
+
selected.push(domainExps[i]);
|
|
386
|
+
domainCounts[domain] = (domainCounts[domain] ?? 0) + 1;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Second pass: fill remaining slots with highest value
|
|
392
|
+
const selectedIds = new Set(selected.map((e) => e.id));
|
|
393
|
+
const remaining = experiences
|
|
394
|
+
.filter((e) => !selectedIds.has(e.id))
|
|
395
|
+
.sort((a, b) => this.computeValue(b) - this.computeValue(a));
|
|
396
|
+
|
|
397
|
+
for (const exp of remaining) {
|
|
398
|
+
if (selected.length >= maxCount) break;
|
|
399
|
+
selected.push(exp);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return selected;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Compute value score for an experience
|
|
407
|
+
*/
|
|
408
|
+
private computeValue(exp: Experience): number {
|
|
409
|
+
const ageMs = Date.now() - exp.createdAt.getTime();
|
|
410
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
411
|
+
const recencyScore = Math.exp(-ageDays / 30); // Decay over ~30 days
|
|
412
|
+
|
|
413
|
+
// Access recency bonus
|
|
414
|
+
const accessAgeMs = exp.lastAccessedAt
|
|
415
|
+
? Date.now() - exp.lastAccessedAt.getTime()
|
|
416
|
+
: ageMs;
|
|
417
|
+
const accessAgeDays = accessAgeMs / (1000 * 60 * 60 * 24);
|
|
418
|
+
const accessRecencyScore = Math.exp(-accessAgeDays / 14); // Decay over ~14 days
|
|
419
|
+
|
|
420
|
+
return (
|
|
421
|
+
(exp.success ? 1 : 0.3) * // Success bonus
|
|
422
|
+
(1 + Math.log(exp.usageCount + 1)) * // Usage bonus (log scale)
|
|
423
|
+
(recencyScore * 0.5 + accessRecencyScore * 0.5) // Combined recency
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Get pruning statistics
|
|
429
|
+
*/
|
|
430
|
+
async getPruningStats(): Promise<{
|
|
431
|
+
total: number;
|
|
432
|
+
byDomain: Record<string, number>;
|
|
433
|
+
bySuccess: { success: number; failure: number };
|
|
434
|
+
oldestDate: Date | null;
|
|
435
|
+
newestDate: Date | null;
|
|
436
|
+
averageUsage: number;
|
|
437
|
+
suggestedPrunes: {
|
|
438
|
+
lowUsage: number;
|
|
439
|
+
old: number;
|
|
440
|
+
failedOld: number;
|
|
441
|
+
};
|
|
442
|
+
}> {
|
|
443
|
+
await this.init();
|
|
444
|
+
const experiences = this._store.values();
|
|
445
|
+
|
|
446
|
+
const byDomain: Record<string, number> = {};
|
|
447
|
+
let successCount = 0;
|
|
448
|
+
let totalUsage = 0;
|
|
449
|
+
let oldestDate: Date | null = null;
|
|
450
|
+
let newestDate: Date | null = null;
|
|
451
|
+
|
|
452
|
+
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
453
|
+
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
454
|
+
let lowUsageCount = 0;
|
|
455
|
+
let oldCount = 0;
|
|
456
|
+
let failedOldCount = 0;
|
|
457
|
+
|
|
458
|
+
for (const exp of experiences) {
|
|
459
|
+
// Domain counts
|
|
460
|
+
byDomain[exp.domain] = (byDomain[exp.domain] ?? 0) + 1;
|
|
461
|
+
|
|
462
|
+
// Success counts
|
|
463
|
+
if (exp.success) successCount++;
|
|
464
|
+
|
|
465
|
+
// Usage sum
|
|
466
|
+
totalUsage += exp.usageCount;
|
|
467
|
+
|
|
468
|
+
// Date tracking
|
|
469
|
+
const createdAt = exp.createdAt;
|
|
470
|
+
if (!oldestDate || createdAt < oldestDate) oldestDate = createdAt;
|
|
471
|
+
if (!newestDate || createdAt > newestDate) newestDate = createdAt;
|
|
472
|
+
|
|
473
|
+
// Suggested prunes
|
|
474
|
+
if (exp.usageCount === 0 && createdAt.getTime() < sevenDaysAgo) {
|
|
475
|
+
lowUsageCount++;
|
|
476
|
+
}
|
|
477
|
+
if (createdAt.getTime() < thirtyDaysAgo) {
|
|
478
|
+
oldCount++;
|
|
479
|
+
if (!exp.success) failedOldCount++;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
total: experiences.length,
|
|
485
|
+
byDomain,
|
|
486
|
+
bySuccess: {
|
|
487
|
+
success: successCount,
|
|
488
|
+
failure: experiences.length - successCount,
|
|
489
|
+
},
|
|
490
|
+
oldestDate,
|
|
491
|
+
newestDate,
|
|
492
|
+
averageUsage: experiences.length > 0 ? totalUsage / experiences.length : 0,
|
|
493
|
+
suggestedPrunes: {
|
|
494
|
+
lowUsage: lowUsageCount,
|
|
495
|
+
old: oldCount,
|
|
496
|
+
failedOld: failedOldCount,
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Get count
|
|
503
|
+
*/
|
|
504
|
+
async count(): Promise<number> {
|
|
505
|
+
await this.init();
|
|
506
|
+
return this._store.size();
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Close the store
|
|
511
|
+
*/
|
|
512
|
+
async close(): Promise<void> {
|
|
513
|
+
await this._store.close();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { ExperienceMemory, type PruneResult } from './experience.js';
|
|
2
|
+
export {
|
|
3
|
+
MemorySystem,
|
|
4
|
+
createMemorySystem,
|
|
5
|
+
type MemoryQueryResult,
|
|
6
|
+
type MemoryQueryResultV2,
|
|
7
|
+
} from './system.js';
|
|
8
|
+
|
|
9
|
+
// Playbook-based memory
|
|
10
|
+
export {
|
|
11
|
+
PlaybookLibrary,
|
|
12
|
+
createPlaybookLibrary,
|
|
13
|
+
type PlaybookMatch,
|
|
14
|
+
} from './playbook.js';
|
|
15
|
+
|
|
16
|
+
// Meta-learning memory
|
|
17
|
+
export {
|
|
18
|
+
MetaMemory,
|
|
19
|
+
createMetaMemory,
|
|
20
|
+
} from './meta.js';
|
|
21
|
+
|
|
22
|
+
// Curated playbook loading
|
|
23
|
+
export {
|
|
24
|
+
loadCuratedPlaybooks,
|
|
25
|
+
type CuratedPlaybookFile,
|
|
26
|
+
type CuratedLoadResult,
|
|
27
|
+
} from './curated-loader.js';
|