@timmeck/brain-core 1.2.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.
Files changed (76) hide show
  1. package/README.md +64 -14
  2. package/brain.log +6 -0
  3. package/dist/config/__tests__/loader.test.d.ts +1 -0
  4. package/dist/config/__tests__/loader.test.js +85 -0
  5. package/dist/config/__tests__/loader.test.js.map +1 -0
  6. package/dist/config/loader.d.ts +15 -0
  7. package/dist/config/loader.js +39 -0
  8. package/dist/config/loader.js.map +1 -0
  9. package/dist/cross-brain/__tests__/notifications.test.d.ts +1 -0
  10. package/dist/cross-brain/__tests__/notifications.test.js +52 -0
  11. package/dist/cross-brain/__tests__/notifications.test.js.map +1 -0
  12. package/dist/cross-brain/notifications.d.ts +25 -0
  13. package/dist/cross-brain/notifications.js +51 -0
  14. package/dist/cross-brain/notifications.js.map +1 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.js +14 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/learning/__tests__/base-engine.test.d.ts +1 -0
  19. package/dist/learning/__tests__/base-engine.test.js +49 -0
  20. package/dist/learning/__tests__/base-engine.test.js.map +1 -0
  21. package/dist/learning/base-engine.d.ts +16 -0
  22. package/dist/learning/base-engine.js +30 -0
  23. package/dist/learning/base-engine.js.map +1 -0
  24. package/dist/math/__tests__/time-decay.test.d.ts +1 -0
  25. package/dist/math/__tests__/time-decay.test.js +37 -0
  26. package/dist/math/__tests__/time-decay.test.js.map +1 -0
  27. package/dist/math/__tests__/wilson-score.test.d.ts +1 -0
  28. package/dist/math/__tests__/wilson-score.test.js +43 -0
  29. package/dist/math/__tests__/wilson-score.test.js.map +1 -0
  30. package/dist/math/time-decay.d.ts +10 -0
  31. package/dist/math/time-decay.js +16 -0
  32. package/dist/math/time-decay.js.map +1 -0
  33. package/dist/math/wilson-score.d.ts +10 -0
  34. package/dist/math/wilson-score.js +21 -0
  35. package/dist/math/wilson-score.js.map +1 -0
  36. package/dist/research/__tests__/base-engine.test.d.ts +1 -0
  37. package/dist/research/__tests__/base-engine.test.js +56 -0
  38. package/dist/research/__tests__/base-engine.test.js.map +1 -0
  39. package/dist/research/base-engine.d.ts +20 -0
  40. package/dist/research/base-engine.js +46 -0
  41. package/dist/research/base-engine.js.map +1 -0
  42. package/dist/synapses/__tests__/activation.test.d.ts +1 -0
  43. package/dist/synapses/__tests__/activation.test.js +87 -0
  44. package/dist/synapses/__tests__/activation.test.js.map +1 -0
  45. package/dist/synapses/__tests__/decay.test.d.ts +1 -0
  46. package/dist/synapses/__tests__/decay.test.js +73 -0
  47. package/dist/synapses/__tests__/decay.test.js.map +1 -0
  48. package/dist/synapses/__tests__/hebbian.test.d.ts +1 -0
  49. package/dist/synapses/__tests__/hebbian.test.js +95 -0
  50. package/dist/synapses/__tests__/hebbian.test.js.map +1 -0
  51. package/dist/synapses/__tests__/pathfinder.test.d.ts +1 -0
  52. package/dist/synapses/__tests__/pathfinder.test.js +74 -0
  53. package/dist/synapses/__tests__/pathfinder.test.js.map +1 -0
  54. package/dist/synapses/__tests__/synapse-manager.test.d.ts +1 -0
  55. package/dist/synapses/__tests__/synapse-manager.test.js +94 -0
  56. package/dist/synapses/__tests__/synapse-manager.test.js.map +1 -0
  57. package/dist/synapses/activation.d.ts +6 -0
  58. package/dist/synapses/activation.js +54 -0
  59. package/dist/synapses/activation.js.map +1 -0
  60. package/dist/synapses/decay.d.ts +9 -0
  61. package/dist/synapses/decay.js +26 -0
  62. package/dist/synapses/decay.js.map +1 -0
  63. package/dist/synapses/hebbian.d.ts +12 -0
  64. package/dist/synapses/hebbian.js +45 -0
  65. package/dist/synapses/hebbian.js.map +1 -0
  66. package/dist/synapses/pathfinder.d.ts +6 -0
  67. package/dist/synapses/pathfinder.js +54 -0
  68. package/dist/synapses/pathfinder.js.map +1 -0
  69. package/dist/synapses/synapse-manager.d.ts +35 -0
  70. package/dist/synapses/synapse-manager.js +72 -0
  71. package/dist/synapses/synapse-manager.js.map +1 -0
  72. package/dist/synapses/types.d.ts +85 -0
  73. package/dist/synapses/types.js +7 -0
  74. package/dist/synapses/types.js.map +1 -0
  75. package/eslint.config.js +14 -0
  76. package/package.json +17 -2
@@ -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":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { strengthen, weaken } from '../hebbian.js';
3
+ function makeSynapse(overrides = {}) {
4
+ return {
5
+ id: 1,
6
+ source_type: 'error', source_id: 1,
7
+ target_type: 'solution', target_id: 1,
8
+ synapse_type: 'solves',
9
+ weight: 0.5,
10
+ activation_count: 3,
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(existing) {
19
+ return {
20
+ findBySourceTarget: vi.fn().mockReturnValue(existing),
21
+ create: vi.fn().mockReturnValue(42),
22
+ getById: vi.fn().mockReturnValue(existing ?? makeSynapse({ id: 42, weight: 0.1, activation_count: 1 })),
23
+ update: vi.fn(),
24
+ delete: vi.fn(),
25
+ getOutgoing: vi.fn().mockReturnValue([]),
26
+ getIncoming: vi.fn().mockReturnValue([]),
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
+ const config = { initialWeight: 0.1, learningRate: 0.15, pruneThreshold: 0.05 };
37
+ describe('strengthen', () => {
38
+ it('creates a new synapse when none exists', () => {
39
+ const repo = mockRepo(undefined);
40
+ strengthen(repo, { type: 'error', id: 1 }, { type: 'solution', id: 2 }, 'solves', config);
41
+ expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({
42
+ weight: 0.1,
43
+ source_type: 'error',
44
+ target_type: 'solution',
45
+ }));
46
+ });
47
+ it('strengthens existing synapse asymptotically', () => {
48
+ const existing = makeSynapse({ weight: 0.5 });
49
+ const repo = mockRepo(existing);
50
+ const result = strengthen(repo, { type: 'error', id: 1 }, { type: 'solution', id: 1 }, 'solves', config);
51
+ expect(result.weight).toBeCloseTo(0.5 + (1.0 - 0.5) * 0.15);
52
+ expect(repo.update).toHaveBeenCalled();
53
+ });
54
+ it('never exceeds 1.0', () => {
55
+ const existing = makeSynapse({ weight: 0.99 });
56
+ const repo = mockRepo(existing);
57
+ const result = strengthen(repo, { type: 'error', id: 1 }, { type: 'solution', id: 1 }, 'solves', config);
58
+ expect(result.weight).toBeLessThanOrEqual(1.0);
59
+ });
60
+ it('increments activation count', () => {
61
+ const existing = makeSynapse({ activation_count: 5 });
62
+ const repo = mockRepo(existing);
63
+ const result = strengthen(repo, { type: 'error', id: 1 }, { type: 'solution', id: 1 }, 'solves', config);
64
+ expect(result.activation_count).toBe(6);
65
+ });
66
+ it('stores context as JSON metadata', () => {
67
+ const repo = mockRepo(undefined);
68
+ strengthen(repo, { type: 'error', id: 1 }, { type: 'solution', id: 2 }, 'solves', config, { reason: 'test' });
69
+ expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({
70
+ metadata: '{"reason":"test"}',
71
+ }));
72
+ });
73
+ });
74
+ describe('weaken', () => {
75
+ it('reduces weight by factor', () => {
76
+ const existing = makeSynapse({ weight: 0.5 });
77
+ const repo = mockRepo(existing);
78
+ weaken(repo, 1, config, 0.5);
79
+ expect(repo.update).toHaveBeenCalledWith(1, { weight: 0.25 });
80
+ });
81
+ it('prunes when below threshold', () => {
82
+ const existing = makeSynapse({ weight: 0.08 });
83
+ const repo = mockRepo(existing);
84
+ weaken(repo, 1, config, 0.5);
85
+ expect(repo.delete).toHaveBeenCalledWith(1);
86
+ });
87
+ it('does nothing for non-existent synapse', () => {
88
+ const repo = mockRepo(undefined);
89
+ repo.getById.mockReturnValue(undefined);
90
+ weaken(repo, 999, config);
91
+ expect(repo.update).not.toHaveBeenCalled();
92
+ expect(repo.delete).not.toHaveBeenCalled();
93
+ });
94
+ });
95
+ //# sourceMappingURL=hebbian.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hebbian.test.js","sourceRoot":"","sources":["../../../src/synapses/__tests__/hebbian.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { findPath } from '../pathfinder.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('findPath', () => {
37
+ it('returns null when no path exists', () => {
38
+ const repo = mockRepo();
39
+ const result = findPath(repo, { type: 'a', id: 1 }, { type: 'z', id: 99 });
40
+ expect(result).toBeNull();
41
+ });
42
+ it('finds direct path', () => {
43
+ const s = synapse({ target_type: 'b', target_id: 2, weight: 0.8 });
44
+ const repo = mockRepo({ 'a:1': [s] });
45
+ const result = findPath(repo, { type: 'a', id: 1 }, { type: 'b', id: 2 });
46
+ expect(result).not.toBeNull();
47
+ expect(result.hops).toBe(1);
48
+ expect(result.totalWeight).toBeCloseTo(0.8);
49
+ });
50
+ it('finds multi-hop path', () => {
51
+ const s1 = synapse({ target_type: 'b', target_id: 2, weight: 0.8 });
52
+ const s2 = synapse({ target_type: 'c', target_id: 3, weight: 0.5 });
53
+ const repo = mockRepo({ 'a:1': [s1], 'b:2': [s2] });
54
+ const result = findPath(repo, { type: 'a', id: 1 }, { type: 'c', id: 3 });
55
+ expect(result).not.toBeNull();
56
+ expect(result.hops).toBe(2);
57
+ expect(result.totalWeight).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 result = findPath(repo, { type: 'a', id: 1 }, { type: 'c', id: 3 }, 1);
64
+ expect(result).toBeNull();
65
+ });
66
+ it('follows incoming synapses', () => {
67
+ const s = synapse({ source_type: 'b', source_id: 2, weight: 0.7 });
68
+ const repo = mockRepo({}, { 'a:1': [s] });
69
+ const result = findPath(repo, { type: 'a', id: 1 }, { type: 'b', id: 2 });
70
+ expect(result).not.toBeNull();
71
+ expect(result.hops).toBe(1);
72
+ });
73
+ });
74
+ //# sourceMappingURL=pathfinder.test.js.map