@timmeck/brain-core 1.1.0 → 1.5.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 +66 -14
- package/brain.log +6 -0
- package/dist/config/__tests__/loader.test.d.ts +1 -0
- package/dist/config/__tests__/loader.test.js +85 -0
- package/dist/config/__tests__/loader.test.js.map +1 -0
- package/dist/config/loader.d.ts +15 -0
- package/dist/config/loader.js +39 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/cross-brain/__tests__/client.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/client.test.js +31 -0
- package/dist/cross-brain/__tests__/client.test.js.map +1 -0
- package/dist/cross-brain/__tests__/notifications.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/notifications.test.js +52 -0
- package/dist/cross-brain/__tests__/notifications.test.js.map +1 -0
- package/dist/cross-brain/notifications.d.ts +25 -0
- package/dist/cross-brain/notifications.js +51 -0
- package/dist/cross-brain/notifications.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
- package/dist/ipc/__tests__/protocol.test.js +69 -0
- package/dist/ipc/__tests__/protocol.test.js.map +1 -0
- package/dist/learning/__tests__/base-engine.test.d.ts +1 -0
- package/dist/learning/__tests__/base-engine.test.js +49 -0
- package/dist/learning/__tests__/base-engine.test.js.map +1 -0
- package/dist/learning/base-engine.d.ts +16 -0
- package/dist/learning/base-engine.js +30 -0
- package/dist/learning/base-engine.js.map +1 -0
- package/dist/math/__tests__/time-decay.test.d.ts +1 -0
- package/dist/math/__tests__/time-decay.test.js +37 -0
- package/dist/math/__tests__/time-decay.test.js.map +1 -0
- package/dist/math/__tests__/wilson-score.test.d.ts +1 -0
- package/dist/math/__tests__/wilson-score.test.js +43 -0
- package/dist/math/__tests__/wilson-score.test.js.map +1 -0
- package/dist/math/time-decay.d.ts +10 -0
- package/dist/math/time-decay.js +16 -0
- package/dist/math/time-decay.js.map +1 -0
- package/dist/math/wilson-score.d.ts +10 -0
- package/dist/math/wilson-score.js +21 -0
- package/dist/math/wilson-score.js.map +1 -0
- package/dist/research/__tests__/base-engine.test.d.ts +1 -0
- package/dist/research/__tests__/base-engine.test.js +56 -0
- package/dist/research/__tests__/base-engine.test.js.map +1 -0
- package/dist/research/base-engine.d.ts +20 -0
- package/dist/research/base-engine.js +46 -0
- package/dist/research/base-engine.js.map +1 -0
- package/dist/synapses/__tests__/activation.test.d.ts +1 -0
- package/dist/synapses/__tests__/activation.test.js +87 -0
- package/dist/synapses/__tests__/activation.test.js.map +1 -0
- package/dist/synapses/__tests__/decay.test.d.ts +1 -0
- package/dist/synapses/__tests__/decay.test.js +73 -0
- package/dist/synapses/__tests__/decay.test.js.map +1 -0
- package/dist/synapses/__tests__/hebbian.test.d.ts +1 -0
- package/dist/synapses/__tests__/hebbian.test.js +95 -0
- package/dist/synapses/__tests__/hebbian.test.js.map +1 -0
- package/dist/synapses/__tests__/pathfinder.test.d.ts +1 -0
- package/dist/synapses/__tests__/pathfinder.test.js +74 -0
- package/dist/synapses/__tests__/pathfinder.test.js.map +1 -0
- package/dist/synapses/__tests__/synapse-manager.test.d.ts +1 -0
- package/dist/synapses/__tests__/synapse-manager.test.js +94 -0
- package/dist/synapses/__tests__/synapse-manager.test.js.map +1 -0
- package/dist/synapses/activation.d.ts +6 -0
- package/dist/synapses/activation.js +54 -0
- package/dist/synapses/activation.js.map +1 -0
- package/dist/synapses/decay.d.ts +9 -0
- package/dist/synapses/decay.js +26 -0
- package/dist/synapses/decay.js.map +1 -0
- package/dist/synapses/hebbian.d.ts +12 -0
- package/dist/synapses/hebbian.js +45 -0
- package/dist/synapses/hebbian.js.map +1 -0
- package/dist/synapses/pathfinder.d.ts +6 -0
- package/dist/synapses/pathfinder.js +54 -0
- package/dist/synapses/pathfinder.js.map +1 -0
- package/dist/synapses/synapse-manager.d.ts +35 -0
- package/dist/synapses/synapse-manager.js +72 -0
- package/dist/synapses/synapse-manager.js.map +1 -0
- package/dist/synapses/types.d.ts +85 -0
- package/dist/synapses/types.js +7 -0
- package/dist/synapses/types.js.map +1 -0
- package/dist/utils/__tests__/events.test.d.ts +1 -0
- package/dist/utils/__tests__/events.test.js +38 -0
- package/dist/utils/__tests__/events.test.js.map +1 -0
- package/dist/utils/__tests__/hash.test.d.ts +1 -0
- package/dist/utils/__tests__/hash.test.js +25 -0
- package/dist/utils/__tests__/hash.test.js.map +1 -0
- package/dist/utils/__tests__/logger.test.d.ts +1 -0
- package/dist/utils/__tests__/logger.test.js +29 -0
- package/dist/utils/__tests__/logger.test.js.map +1 -0
- package/dist/utils/__tests__/paths.test.d.ts +1 -0
- package/dist/utils/__tests__/paths.test.js +50 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -0
- package/eslint.config.js +14 -0
- package/package.json +17 -2
- package/.github/FUNDING.yml +0 -1
- package/.github/workflows/ci.yml +0 -21
- package/src/api/server.ts +0 -210
- package/src/cli/colors.ts +0 -105
- package/src/cross-brain/client.ts +0 -94
- package/src/db/connection.ts +0 -22
- package/src/index.ts +0 -35
- package/src/ipc/client.ts +0 -117
- package/src/ipc/protocol.ts +0 -35
- package/src/ipc/server.ts +0 -170
- package/src/mcp/http-server.ts +0 -148
- package/src/mcp/server.ts +0 -84
- package/src/types/ipc.types.ts +0 -8
- package/src/utils/events.ts +0 -30
- package/src/utils/hash.ts +0 -5
- package/src/utils/logger.ts +0 -67
- package/src/utils/paths.ts +0 -24
- package/tsconfig.json +0 -18
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { BaseLearningEngine } from '../base-engine.js';
|
|
3
|
+
class TestLearningEngine extends BaseLearningEngine {
|
|
4
|
+
cycleCount = 0;
|
|
5
|
+
runCycle() {
|
|
6
|
+
this.cycleCount++;
|
|
7
|
+
return { count: this.cycleCount };
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
describe('BaseLearningEngine', () => {
|
|
11
|
+
beforeEach(() => { vi.useFakeTimers(); });
|
|
12
|
+
afterEach(() => { vi.useRealTimers(); });
|
|
13
|
+
it('should run cycles at the configured interval', () => {
|
|
14
|
+
const engine = new TestLearningEngine({ intervalMs: 1000 });
|
|
15
|
+
engine.start();
|
|
16
|
+
expect(engine.cycleCount).toBe(0);
|
|
17
|
+
vi.advanceTimersByTime(1000);
|
|
18
|
+
expect(engine.cycleCount).toBe(1);
|
|
19
|
+
vi.advanceTimersByTime(2000);
|
|
20
|
+
expect(engine.cycleCount).toBe(3);
|
|
21
|
+
engine.stop();
|
|
22
|
+
});
|
|
23
|
+
it('should stop the timer', () => {
|
|
24
|
+
const engine = new TestLearningEngine({ intervalMs: 500 });
|
|
25
|
+
engine.start();
|
|
26
|
+
vi.advanceTimersByTime(500);
|
|
27
|
+
expect(engine.cycleCount).toBe(1);
|
|
28
|
+
engine.stop();
|
|
29
|
+
vi.advanceTimersByTime(2000);
|
|
30
|
+
expect(engine.cycleCount).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
it('should handle errors in runCycle gracefully', () => {
|
|
33
|
+
class FailingEngine extends BaseLearningEngine {
|
|
34
|
+
runCycle() { throw new Error('boom'); }
|
|
35
|
+
}
|
|
36
|
+
const engine = new FailingEngine({ intervalMs: 100 });
|
|
37
|
+
engine.start();
|
|
38
|
+
// Should not throw
|
|
39
|
+
expect(() => vi.advanceTimersByTime(100)).not.toThrow();
|
|
40
|
+
engine.stop();
|
|
41
|
+
});
|
|
42
|
+
it('should be safe to call stop multiple times', () => {
|
|
43
|
+
const engine = new TestLearningEngine({ intervalMs: 1000 });
|
|
44
|
+
engine.start();
|
|
45
|
+
engine.stop();
|
|
46
|
+
engine.stop(); // No error
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=base-engine.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-engine.test.js","sourceRoot":"","sources":["../../../src/learning/__tests__/base-engine.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface LearningEngineConfig {
|
|
2
|
+
intervalMs: number;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for learning engines.
|
|
6
|
+
* Handles timer lifecycle — subclasses implement runCycle().
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class BaseLearningEngine {
|
|
9
|
+
protected engineConfig: LearningEngineConfig;
|
|
10
|
+
protected timer: ReturnType<typeof setInterval> | null;
|
|
11
|
+
protected logger: import("winston").Logger;
|
|
12
|
+
constructor(engineConfig: LearningEngineConfig);
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
abstract runCycle(): unknown;
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for learning engines.
|
|
4
|
+
* Handles timer lifecycle — subclasses implement runCycle().
|
|
5
|
+
*/
|
|
6
|
+
export class BaseLearningEngine {
|
|
7
|
+
engineConfig;
|
|
8
|
+
timer = null;
|
|
9
|
+
logger = getLogger();
|
|
10
|
+
constructor(engineConfig) {
|
|
11
|
+
this.engineConfig = engineConfig;
|
|
12
|
+
}
|
|
13
|
+
start() {
|
|
14
|
+
this.timer = setInterval(() => {
|
|
15
|
+
try {
|
|
16
|
+
this.runCycle();
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
this.logger.error('Learning cycle error', { error: String(err) });
|
|
20
|
+
}
|
|
21
|
+
}, this.engineConfig.intervalMs);
|
|
22
|
+
}
|
|
23
|
+
stop() {
|
|
24
|
+
if (this.timer) {
|
|
25
|
+
clearInterval(this.timer);
|
|
26
|
+
this.timer = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=base-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-engine.js","sourceRoot":"","sources":["../../src/learning/base-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAM/C;;;GAGG;AACH,MAAM,OAAgB,kBAAkB;IAIhB;IAHZ,KAAK,GAA0C,IAAI,CAAC;IACpD,MAAM,GAAG,SAAS,EAAE,CAAC;IAE/B,YAAsB,YAAkC;QAAlC,iBAAY,GAAZ,YAAY,CAAsB;IAAG,CAAC;IAE5D,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC;gBACH,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;CAGF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
+
import { timeDecayFactor } from '../time-decay.js';
|
|
3
|
+
describe('timeDecayFactor', () => {
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
vi.restoreAllMocks();
|
|
6
|
+
});
|
|
7
|
+
it('returns ~1.0 for just-activated item', () => {
|
|
8
|
+
const now = new Date().toISOString();
|
|
9
|
+
const factor = timeDecayFactor(now, 30);
|
|
10
|
+
expect(factor).toBeGreaterThan(0.99);
|
|
11
|
+
expect(factor).toBeLessThanOrEqual(1.0);
|
|
12
|
+
});
|
|
13
|
+
it('returns ~0.5 at exactly one half-life', () => {
|
|
14
|
+
const halfLifeDays = 30;
|
|
15
|
+
const activatedAt = new Date(Date.now() - halfLifeDays * 24 * 60 * 60 * 1000).toISOString();
|
|
16
|
+
const factor = timeDecayFactor(activatedAt, halfLifeDays);
|
|
17
|
+
expect(factor).toBeCloseTo(0.5, 1);
|
|
18
|
+
});
|
|
19
|
+
it('returns ~0.25 at two half-lives', () => {
|
|
20
|
+
const halfLifeDays = 14;
|
|
21
|
+
const activatedAt = new Date(Date.now() - 28 * 24 * 60 * 60 * 1000).toISOString();
|
|
22
|
+
const factor = timeDecayFactor(activatedAt, halfLifeDays);
|
|
23
|
+
expect(factor).toBeCloseTo(0.25, 1);
|
|
24
|
+
});
|
|
25
|
+
it('returns very small value for very old items', () => {
|
|
26
|
+
const activatedAt = new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString();
|
|
27
|
+
const factor = timeDecayFactor(activatedAt, 30);
|
|
28
|
+
expect(factor).toBeLessThan(0.01);
|
|
29
|
+
});
|
|
30
|
+
it('works with different half-life values', () => {
|
|
31
|
+
const activatedAt = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
32
|
+
const shortHalfLife = timeDecayFactor(activatedAt, 7);
|
|
33
|
+
const longHalfLife = timeDecayFactor(activatedAt, 45);
|
|
34
|
+
expect(shortHalfLife).toBeLessThan(longHalfLife);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=time-decay.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-decay.test.js","sourceRoot":"","sources":["../../../src/math/__tests__/time-decay.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,YAAY,GAAG,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5F,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,YAAY,GAAG,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClF,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACnF,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACjF,MAAM,aAAa,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACtD,MAAM,YAAY,GAAG,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { wilsonScore } from '../wilson-score.js';
|
|
3
|
+
describe('wilsonScore', () => {
|
|
4
|
+
it('returns 0 for zero trials', () => {
|
|
5
|
+
expect(wilsonScore(0, 0)).toBe(0);
|
|
6
|
+
});
|
|
7
|
+
it('returns low confidence for single success', () => {
|
|
8
|
+
const score = wilsonScore(1, 1);
|
|
9
|
+
expect(score).toBeGreaterThan(0);
|
|
10
|
+
expect(score).toBeLessThan(0.8);
|
|
11
|
+
});
|
|
12
|
+
it('converges toward actual rate with large samples', () => {
|
|
13
|
+
const score = wilsonScore(950, 1000);
|
|
14
|
+
expect(score).toBeGreaterThan(0.93);
|
|
15
|
+
expect(score).toBeLessThan(0.96);
|
|
16
|
+
});
|
|
17
|
+
it('penalizes small sample sizes', () => {
|
|
18
|
+
const small = wilsonScore(5, 5);
|
|
19
|
+
const large = wilsonScore(500, 500);
|
|
20
|
+
expect(large).toBeGreaterThan(small);
|
|
21
|
+
});
|
|
22
|
+
it('returns higher confidence with lower z-score', () => {
|
|
23
|
+
const z90 = wilsonScore(7, 10, 1.64);
|
|
24
|
+
const z95 = wilsonScore(7, 10, 1.96);
|
|
25
|
+
const z99 = wilsonScore(7, 10, 2.33);
|
|
26
|
+
expect(z90).toBeGreaterThan(z95);
|
|
27
|
+
expect(z95).toBeGreaterThan(z99);
|
|
28
|
+
});
|
|
29
|
+
it('handles 0 successes', () => {
|
|
30
|
+
const score = wilsonScore(0, 10);
|
|
31
|
+
expect(score).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
it('handles 50/50 split', () => {
|
|
34
|
+
const score = wilsonScore(50, 100);
|
|
35
|
+
expect(score).toBeGreaterThan(0.39);
|
|
36
|
+
expect(score).toBeLessThan(0.51);
|
|
37
|
+
});
|
|
38
|
+
it('never returns negative', () => {
|
|
39
|
+
expect(wilsonScore(0, 1)).toBeGreaterThanOrEqual(0);
|
|
40
|
+
expect(wilsonScore(0, 100)).toBeGreaterThanOrEqual(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=wilson-score.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wilson-score.test.js","sourceRoot":"","sources":["../../../src/math/__tests__/wilson-score.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponential time-decay factor based on half-life.
|
|
3
|
+
* Returns a multiplier between 0 and 1 — recent items are near 1.0,
|
|
4
|
+
* older items decay toward 0.
|
|
5
|
+
*
|
|
6
|
+
* @param lastActivatedAt ISO date string of last activation
|
|
7
|
+
* @param halfLifeDays Number of days for half-life
|
|
8
|
+
* @returns Decay factor (0-1)
|
|
9
|
+
*/
|
|
10
|
+
export declare function timeDecayFactor(lastActivatedAt: string, halfLifeDays: number): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponential time-decay factor based on half-life.
|
|
3
|
+
* Returns a multiplier between 0 and 1 — recent items are near 1.0,
|
|
4
|
+
* older items decay toward 0.
|
|
5
|
+
*
|
|
6
|
+
* @param lastActivatedAt ISO date string of last activation
|
|
7
|
+
* @param halfLifeDays Number of days for half-life
|
|
8
|
+
* @returns Decay factor (0-1)
|
|
9
|
+
*/
|
|
10
|
+
export function timeDecayFactor(lastActivatedAt, halfLifeDays) {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
const activated = new Date(lastActivatedAt).getTime();
|
|
13
|
+
const ageDays = (now - activated) / (1000 * 60 * 60 * 24);
|
|
14
|
+
return Math.pow(0.5, ageDays / halfLifeDays);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=time-decay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-decay.js","sourceRoot":"","sources":["../../src/math/time-decay.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,eAAuB,EAAE,YAAoB;IAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wilson Score Interval lower bound for low-sample-size confidence.
|
|
3
|
+
* Prevents unrealistic 100% from single success/failure.
|
|
4
|
+
*
|
|
5
|
+
* @param successes Number of successes
|
|
6
|
+
* @param total Total number of trials
|
|
7
|
+
* @param z Z-score for confidence level (1.64=90%, 1.96=95%, 2.33=99%)
|
|
8
|
+
* @returns Conservative lower bound estimate (0-1)
|
|
9
|
+
*/
|
|
10
|
+
export declare function wilsonScore(successes: number, total: number, z?: number): number;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wilson Score Interval lower bound for low-sample-size confidence.
|
|
3
|
+
* Prevents unrealistic 100% from single success/failure.
|
|
4
|
+
*
|
|
5
|
+
* @param successes Number of successes
|
|
6
|
+
* @param total Total number of trials
|
|
7
|
+
* @param z Z-score for confidence level (1.64=90%, 1.96=95%, 2.33=99%)
|
|
8
|
+
* @returns Conservative lower bound estimate (0-1)
|
|
9
|
+
*/
|
|
10
|
+
export function wilsonScore(successes, total, z = 1.96) {
|
|
11
|
+
if (total === 0)
|
|
12
|
+
return 0;
|
|
13
|
+
const p = successes / total;
|
|
14
|
+
const z2 = z * z;
|
|
15
|
+
const n = total;
|
|
16
|
+
const denominator = 1 + z2 / n;
|
|
17
|
+
const centre = p + z2 / (2 * n);
|
|
18
|
+
const spread = z * Math.sqrt((p * (1 - p) + z2 / (4 * n)) / n);
|
|
19
|
+
return Math.max(0, (centre - spread) / denominator);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=wilson-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wilson-score.js","sourceRoot":"","sources":["../../src/math/wilson-score.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,KAAa,EAAE,IAAY,IAAI;IAC5E,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE1B,MAAM,CAAC,GAAG,SAAS,GAAG,KAAK,CAAC;IAC5B,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,CAAC,GAAG,KAAK,CAAC;IAEhB,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAE/D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { BaseResearchEngine } from '../base-engine.js';
|
|
3
|
+
class TestResearchEngine extends BaseResearchEngine {
|
|
4
|
+
cycleCount = 0;
|
|
5
|
+
runCycle() {
|
|
6
|
+
this.cycleCount++;
|
|
7
|
+
return { count: this.cycleCount };
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
describe('BaseResearchEngine', () => {
|
|
11
|
+
beforeEach(() => { vi.useFakeTimers(); });
|
|
12
|
+
afterEach(() => { vi.useRealTimers(); });
|
|
13
|
+
it('should run without initial delay when not configured', () => {
|
|
14
|
+
const engine = new TestResearchEngine({ intervalMs: 1000 });
|
|
15
|
+
engine.start();
|
|
16
|
+
vi.advanceTimersByTime(1000);
|
|
17
|
+
expect(engine.cycleCount).toBe(1);
|
|
18
|
+
engine.stop();
|
|
19
|
+
});
|
|
20
|
+
it('should delay first cycle when initialDelayMs is set', () => {
|
|
21
|
+
const engine = new TestResearchEngine({ intervalMs: 1000, initialDelayMs: 5000 });
|
|
22
|
+
engine.start();
|
|
23
|
+
vi.advanceTimersByTime(4999);
|
|
24
|
+
expect(engine.cycleCount).toBe(0);
|
|
25
|
+
vi.advanceTimersByTime(1); // 5000ms total
|
|
26
|
+
expect(engine.cycleCount).toBe(1); // First cycle from delay
|
|
27
|
+
vi.advanceTimersByTime(1000);
|
|
28
|
+
expect(engine.cycleCount).toBe(2); // Second from interval
|
|
29
|
+
engine.stop();
|
|
30
|
+
});
|
|
31
|
+
it('should clean up both timers on stop', () => {
|
|
32
|
+
const engine = new TestResearchEngine({ intervalMs: 1000, initialDelayMs: 5000 });
|
|
33
|
+
engine.start();
|
|
34
|
+
engine.stop();
|
|
35
|
+
vi.advanceTimersByTime(10000);
|
|
36
|
+
expect(engine.cycleCount).toBe(0);
|
|
37
|
+
});
|
|
38
|
+
it('should handle errors in runCycle gracefully', () => {
|
|
39
|
+
class FailingEngine extends BaseResearchEngine {
|
|
40
|
+
runCycle() { throw new Error('boom'); }
|
|
41
|
+
}
|
|
42
|
+
const engine = new FailingEngine({ intervalMs: 100 });
|
|
43
|
+
engine.start();
|
|
44
|
+
expect(() => vi.advanceTimersByTime(100)).not.toThrow();
|
|
45
|
+
engine.stop();
|
|
46
|
+
});
|
|
47
|
+
it('should be safe to call stop before delay fires', () => {
|
|
48
|
+
const engine = new TestResearchEngine({ intervalMs: 1000, initialDelayMs: 5000 });
|
|
49
|
+
engine.start();
|
|
50
|
+
vi.advanceTimersByTime(2000);
|
|
51
|
+
engine.stop();
|
|
52
|
+
vi.advanceTimersByTime(10000);
|
|
53
|
+
expect(engine.cycleCount).toBe(0);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=base-engine.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-engine.test.js","sourceRoot":"","sources":["../../../src/research/__tests__/base-engine.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ResearchEngineConfig {
|
|
2
|
+
intervalMs: number;
|
|
3
|
+
initialDelayMs?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Abstract base class for research engines.
|
|
7
|
+
* Supports optional initial delay before first cycle.
|
|
8
|
+
* Handles timer lifecycle — subclasses implement runCycle().
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class BaseResearchEngine {
|
|
11
|
+
protected engineConfig: ResearchEngineConfig;
|
|
12
|
+
protected timer: ReturnType<typeof setInterval> | null;
|
|
13
|
+
protected delayTimer: ReturnType<typeof setTimeout> | null;
|
|
14
|
+
protected logger: import("winston").Logger;
|
|
15
|
+
constructor(engineConfig: ResearchEngineConfig);
|
|
16
|
+
start(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
private safeRunCycle;
|
|
19
|
+
abstract runCycle(): unknown;
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for research engines.
|
|
4
|
+
* Supports optional initial delay before first cycle.
|
|
5
|
+
* Handles timer lifecycle — subclasses implement runCycle().
|
|
6
|
+
*/
|
|
7
|
+
export class BaseResearchEngine {
|
|
8
|
+
engineConfig;
|
|
9
|
+
timer = null;
|
|
10
|
+
delayTimer = null;
|
|
11
|
+
logger = getLogger();
|
|
12
|
+
constructor(engineConfig) {
|
|
13
|
+
this.engineConfig = engineConfig;
|
|
14
|
+
}
|
|
15
|
+
start() {
|
|
16
|
+
const delay = this.engineConfig.initialDelayMs;
|
|
17
|
+
if (delay && delay > 0) {
|
|
18
|
+
this.delayTimer = setTimeout(() => {
|
|
19
|
+
this.safeRunCycle();
|
|
20
|
+
this.timer = setInterval(() => this.safeRunCycle(), this.engineConfig.intervalMs);
|
|
21
|
+
}, delay);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.timer = setInterval(() => this.safeRunCycle(), this.engineConfig.intervalMs);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
stop() {
|
|
28
|
+
if (this.delayTimer) {
|
|
29
|
+
clearTimeout(this.delayTimer);
|
|
30
|
+
this.delayTimer = null;
|
|
31
|
+
}
|
|
32
|
+
if (this.timer) {
|
|
33
|
+
clearInterval(this.timer);
|
|
34
|
+
this.timer = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
safeRunCycle() {
|
|
38
|
+
try {
|
|
39
|
+
this.runCycle();
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
this.logger.error('Research cycle error', { error: String(err) });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=base-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-engine.js","sourceRoot":"","sources":["../../src/research/base-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAO/C;;;;GAIG;AACH,MAAM,OAAgB,kBAAkB;IAKhB;IAJZ,KAAK,GAA0C,IAAI,CAAC;IACpD,UAAU,GAAyC,IAAI,CAAC;IACxD,MAAM,GAAG,SAAS,EAAE,CAAC;IAE/B,YAAsB,YAAkC;QAAlC,iBAAY,GAAZ,YAAY,CAAsB;IAAG,CAAC;IAE5D,KAAK;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC;QAC/C,IAAI,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;gBAChC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YACpF,CAAC,EAAE,KAAK,CAAC,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC;YACH,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;CAGF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { spreadingActivation } from '../activation.js';
|
|
3
|
+
function synapse(overrides) {
|
|
4
|
+
return {
|
|
5
|
+
id: 1,
|
|
6
|
+
source_type: 'a', source_id: 1,
|
|
7
|
+
target_type: 'b', target_id: 1,
|
|
8
|
+
synapse_type: 'test',
|
|
9
|
+
weight: 0.8,
|
|
10
|
+
activation_count: 1,
|
|
11
|
+
last_activated_at: new Date().toISOString(),
|
|
12
|
+
metadata: null,
|
|
13
|
+
created_at: new Date().toISOString(),
|
|
14
|
+
updated_at: new Date().toISOString(),
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function mockRepo(outgoingMap = {}, incomingMap = {}) {
|
|
19
|
+
return {
|
|
20
|
+
findBySourceTarget: vi.fn(),
|
|
21
|
+
create: vi.fn(),
|
|
22
|
+
getById: vi.fn(),
|
|
23
|
+
update: vi.fn(),
|
|
24
|
+
delete: vi.fn(),
|
|
25
|
+
getOutgoing: vi.fn().mockImplementation((type, id) => outgoingMap[`${type}:${id}`] ?? []),
|
|
26
|
+
getIncoming: vi.fn().mockImplementation((type, id) => incomingMap[`${type}:${id}`] ?? []),
|
|
27
|
+
findInactiveSince: vi.fn().mockReturnValue([]),
|
|
28
|
+
topByWeight: vi.fn().mockReturnValue([]),
|
|
29
|
+
topDiverse: vi.fn().mockReturnValue([]),
|
|
30
|
+
countNodes: vi.fn().mockReturnValue(0),
|
|
31
|
+
totalCount: vi.fn().mockReturnValue(0),
|
|
32
|
+
avgWeight: vi.fn().mockReturnValue(0),
|
|
33
|
+
countByType: vi.fn().mockReturnValue({}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
describe('spreadingActivation', () => {
|
|
37
|
+
it('returns empty for isolated node', () => {
|
|
38
|
+
const repo = mockRepo();
|
|
39
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 });
|
|
40
|
+
expect(results).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
it('finds directly connected nodes', () => {
|
|
43
|
+
const s = synapse({ target_type: 'b', target_id: 2, weight: 0.8 });
|
|
44
|
+
const repo = mockRepo({ 'a:1': [s] });
|
|
45
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 });
|
|
46
|
+
expect(results).toHaveLength(1);
|
|
47
|
+
expect(results[0].node).toEqual({ type: 'b', id: 2 });
|
|
48
|
+
expect(results[0].activation).toBe(0.8);
|
|
49
|
+
expect(results[0].depth).toBe(1);
|
|
50
|
+
});
|
|
51
|
+
it('propagates through multiple hops', () => {
|
|
52
|
+
const s1 = synapse({ target_type: 'b', target_id: 2, weight: 0.8 });
|
|
53
|
+
const s2 = synapse({ target_type: 'c', target_id: 3, weight: 0.5 });
|
|
54
|
+
const repo = mockRepo({ 'a:1': [s1], 'b:2': [s2] });
|
|
55
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 });
|
|
56
|
+
expect(results).toHaveLength(2);
|
|
57
|
+
expect(results.find(r => r.node.type === 'c')?.activation).toBeCloseTo(0.4);
|
|
58
|
+
});
|
|
59
|
+
it('respects maxDepth', () => {
|
|
60
|
+
const s1 = synapse({ target_type: 'b', target_id: 2, weight: 0.9 });
|
|
61
|
+
const s2 = synapse({ target_type: 'c', target_id: 3, weight: 0.9 });
|
|
62
|
+
const repo = mockRepo({ 'a:1': [s1], 'b:2': [s2] });
|
|
63
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 }, 1);
|
|
64
|
+
expect(results).toHaveLength(1);
|
|
65
|
+
});
|
|
66
|
+
it('filters by minWeight', () => {
|
|
67
|
+
const s = synapse({ target_type: 'b', target_id: 2, weight: 0.1 });
|
|
68
|
+
const repo = mockRepo({ 'a:1': [s] });
|
|
69
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 }, 3, 0.2);
|
|
70
|
+
expect(results).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
it('follows incoming synapses too', () => {
|
|
73
|
+
const s = synapse({ source_type: 'b', source_id: 2, weight: 0.7 });
|
|
74
|
+
const repo = mockRepo({}, { 'a:1': [s] });
|
|
75
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 });
|
|
76
|
+
expect(results).toHaveLength(1);
|
|
77
|
+
expect(results[0].node).toEqual({ type: 'b', id: 2 });
|
|
78
|
+
});
|
|
79
|
+
it('sorts by activation descending', () => {
|
|
80
|
+
const s1 = synapse({ target_type: 'b', target_id: 2, weight: 0.3 });
|
|
81
|
+
const s2 = synapse({ target_type: 'c', target_id: 3, weight: 0.9 });
|
|
82
|
+
const repo = mockRepo({ 'a:1': [s1, s2] });
|
|
83
|
+
const results = spreadingActivation(repo, { type: 'a', id: 1 });
|
|
84
|
+
expect(results[0].activation).toBeGreaterThan(results[1].activation);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=activation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activation.test.js","sourceRoot":"","sources":["../../../src/synapses/__tests__/activation.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { decayAll } from '../decay.js';
|
|
3
|
+
function synapse(overrides) {
|
|
4
|
+
return {
|
|
5
|
+
id: 1,
|
|
6
|
+
source_type: 'a', source_id: 1,
|
|
7
|
+
target_type: 'b', target_id: 1,
|
|
8
|
+
synapse_type: 'test',
|
|
9
|
+
weight: 0.5,
|
|
10
|
+
activation_count: 1,
|
|
11
|
+
last_activated_at: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
12
|
+
metadata: null,
|
|
13
|
+
created_at: new Date().toISOString(),
|
|
14
|
+
updated_at: new Date().toISOString(),
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function mockRepo(stale = []) {
|
|
19
|
+
return {
|
|
20
|
+
findBySourceTarget: vi.fn(),
|
|
21
|
+
create: vi.fn(),
|
|
22
|
+
getById: vi.fn(),
|
|
23
|
+
update: vi.fn(),
|
|
24
|
+
delete: vi.fn(),
|
|
25
|
+
getOutgoing: vi.fn().mockReturnValue([]),
|
|
26
|
+
getIncoming: vi.fn().mockReturnValue([]),
|
|
27
|
+
findInactiveSince: vi.fn().mockReturnValue(stale),
|
|
28
|
+
topByWeight: vi.fn().mockReturnValue([]),
|
|
29
|
+
topDiverse: vi.fn().mockReturnValue([]),
|
|
30
|
+
countNodes: vi.fn().mockReturnValue(0),
|
|
31
|
+
totalCount: vi.fn().mockReturnValue(0),
|
|
32
|
+
avgWeight: vi.fn().mockReturnValue(0),
|
|
33
|
+
countByType: vi.fn().mockReturnValue({}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const config = { decayHalfLifeDays: 30, decayAfterDays: 14, pruneThreshold: 0.05 };
|
|
37
|
+
describe('decayAll', () => {
|
|
38
|
+
it('returns zeros when no stale synapses', () => {
|
|
39
|
+
const repo = mockRepo();
|
|
40
|
+
const result = decayAll(repo, config);
|
|
41
|
+
expect(result).toEqual({ decayed: 0, pruned: 0 });
|
|
42
|
+
});
|
|
43
|
+
it('decays stale synapses', () => {
|
|
44
|
+
const s = synapse({ id: 1, weight: 0.5 });
|
|
45
|
+
const repo = mockRepo([s]);
|
|
46
|
+
const result = decayAll(repo, config);
|
|
47
|
+
expect(result.decayed).toBe(1);
|
|
48
|
+
expect(repo.update).toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
it('prunes synapses that decay below threshold', () => {
|
|
51
|
+
const s = synapse({
|
|
52
|
+
id: 1,
|
|
53
|
+
weight: 0.03,
|
|
54
|
+
last_activated_at: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString(),
|
|
55
|
+
});
|
|
56
|
+
const repo = mockRepo([s]);
|
|
57
|
+
const result = decayAll(repo, config);
|
|
58
|
+
expect(result.pruned).toBe(1);
|
|
59
|
+
expect(repo.delete).toHaveBeenCalledWith(1);
|
|
60
|
+
});
|
|
61
|
+
it('handles mix of decayed and pruned', () => {
|
|
62
|
+
const healthy = synapse({ id: 1, weight: 0.8 });
|
|
63
|
+
const weak = synapse({
|
|
64
|
+
id: 2,
|
|
65
|
+
weight: 0.02,
|
|
66
|
+
last_activated_at: new Date(Date.now() - 120 * 24 * 60 * 60 * 1000).toISOString(),
|
|
67
|
+
});
|
|
68
|
+
const repo = mockRepo([healthy, weak]);
|
|
69
|
+
const result = decayAll(repo, config);
|
|
70
|
+
expect(result.decayed + result.pruned).toBe(2);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=decay.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decay.test.js","sourceRoot":"","sources":["../../../src/synapses/__tests__/decay.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,SAAS,OAAO,CAAC,SAAiC;IAChD,OAAO;QACL,EAAE,EAAE,CAAC;QACL,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QAC9B,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QAC9B,YAAY,EAAE,MAAM;QACpB,MAAM,EAAE,GAAG;QACX,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;QAChF,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,QAAyB,EAAE;IAC3C,OAAO;QACL,kBAAkB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC3B,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;QACf,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;QAChB,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;QACf,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QACxC,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QACxC,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;QACjD,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QACxC,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QACvC,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACtC,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACtC,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACrC,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;AAEnF,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,OAAO,CAAC;YAChB,EAAE,EAAE,CAAC;YACL,MAAM,EAAE,IAAI;YACZ,iBAAiB,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;SACjF,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,OAAO,CAAC;YACnB,EAAE,EAAE,CAAC;YACL,MAAM,EAAE,IAAI;YACZ,iBAAiB,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;SAClF,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|