@timmeck/brain-core 1.6.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/cli/colors.js +0 -1
- package/dist/cli/colors.js.map +1 -1
- package/dist/config/__tests__/loader.test.js +1 -0
- package/dist/config/__tests__/loader.test.js.map +1 -1
- package/dist/cross-brain/__tests__/subscription.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/subscription.test.js +75 -0
- package/dist/cross-brain/__tests__/subscription.test.js.map +1 -0
- package/dist/cross-brain/subscription.d.ts +33 -0
- package/dist/cross-brain/subscription.js +83 -0
- package/dist/cross-brain/subscription.js.map +1 -0
- package/dist/dashboard/__tests__/server.test.d.ts +1 -0
- package/dist/dashboard/__tests__/server.test.js +105 -0
- package/dist/dashboard/__tests__/server.test.js.map +1 -0
- package/dist/dashboard/server.d.ts +19 -0
- package/dist/dashboard/server.js +117 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/embeddings/__tests__/engine.test.d.ts +1 -0
- package/dist/embeddings/__tests__/engine.test.js +82 -0
- package/dist/embeddings/__tests__/engine.test.js.map +1 -0
- package/dist/embeddings/engine.d.ts +54 -0
- package/dist/embeddings/engine.js +115 -0
- package/dist/embeddings/engine.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc/server.d.ts +23 -1
- package/dist/ipc/server.js +44 -1
- package/dist/ipc/server.js.map +1 -1
- package/dist/memory/base-memory-engine.d.ts +28 -2
- package/dist/memory/base-memory-engine.js +40 -1
- package/dist/memory/base-memory-engine.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -24,7 +24,10 @@ Brain Core extracts the common infrastructure used across all Brain MCP servers
|
|
|
24
24
|
| **Logger** | Winston-based structured logging with file rotation |
|
|
25
25
|
| **Event Bus** | Generic typed event emitter |
|
|
26
26
|
| **Cross-Brain Client** | Discover and query peer brains over IPC named pipes |
|
|
27
|
-
| **Cross-Brain Notifier** | Push event notifications to peer brains
|
|
27
|
+
| **Cross-Brain Notifier** | Push event notifications to peer brains |
|
|
28
|
+
| **Cross-Brain Subscriptions** | Persistent event subscriptions between brains (new in v2.0) |
|
|
29
|
+
| **Embedding Engine** | Local vector embeddings with optional @huggingface/transformers (new in v2.0) |
|
|
30
|
+
| **Dashboard Server** | Generic SSE dashboard server with live stats (new in v2.0) |
|
|
28
31
|
| **Math — Wilson Score** | Statistical confidence intervals for win rates / rule confidence |
|
|
29
32
|
| **Math — Time Decay** | Exponential half-life decay for synapse and rule freshness |
|
|
30
33
|
| **Config Loader** | `deepMerge()` + `loadConfigFile()` for layered config |
|
|
@@ -151,10 +154,10 @@ class MyRouter implements IpcRouter {
|
|
|
151
154
|
|
|
152
155
|
| Brain | Version | Purpose | Ports |
|
|
153
156
|
|-------|---------|---------|-------|
|
|
154
|
-
| [Brain](https://github.com/timmeck/brain-ecosystem/tree/main/packages/brain) |
|
|
155
|
-
| [Trading Brain](https://github.com/timmeck/brain-ecosystem/tree/main/packages/trading-brain) |
|
|
156
|
-
| [Marketing Brain](https://github.com/timmeck/brain-ecosystem/tree/main/packages/marketing-brain) |
|
|
157
|
-
| [Brain Core](https://github.com/timmeck/brain-ecosystem/tree/main/packages/brain-core) |
|
|
157
|
+
| [Brain](https://github.com/timmeck/brain-ecosystem/tree/main/packages/brain) | v3.0.0 | Error memory, code intelligence & persistent context | 7777/7778 |
|
|
158
|
+
| [Trading Brain](https://github.com/timmeck/brain-ecosystem/tree/main/packages/trading-brain) | v2.0.0 | Adaptive trading intelligence with memory & sessions | 7779/7780 |
|
|
159
|
+
| [Marketing Brain](https://github.com/timmeck/brain-ecosystem/tree/main/packages/marketing-brain) | v1.1.0 | Content strategy & engagement with memory & sessions | 7781/7782/7783 |
|
|
160
|
+
| [Brain Core](https://github.com/timmeck/brain-ecosystem/tree/main/packages/brain-core) | v2.0.0 | Shared infrastructure (this package) | — |
|
|
158
161
|
|
|
159
162
|
All packages live in the [brain-ecosystem](https://github.com/timmeck/brain-ecosystem) monorepo with npm workspaces. Brain Core provides shared infrastructure that eliminates ~2,800 lines of duplicated code across the ecosystem.
|
|
160
163
|
|
package/dist/cli/colors.js
CHANGED
package/dist/cli/colors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/cli/colors.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/cli/colors.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,7 @@ describe('deepMerge', () => {
|
|
|
33
33
|
deepMerge(target, {
|
|
34
34
|
level1: { level2: { level3: { replace: 'new' } } },
|
|
35
35
|
});
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
37
|
expect(target.level1.level2.level3).toEqual({ keep: true, replace: 'new' });
|
|
37
38
|
});
|
|
38
39
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/config/__tests__/loader.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEzD,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAA6B,CAAC;QACzD,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAA6B,CAAC;QACjF,SAAS,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAA6B,CAAC;QAC/D,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAA6B,CAAC;QACnD,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,MAAM,GAAG;YACb,MAAM,EAAE;gBACN,MAAM,EAAE;oBACN,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;iBACvC;aACF;SACyB,CAAC;QAC7B,SAAS,CAAC,MAAM,EAAE;YAChB,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;SACnD,CAAC,CAAC;QACH,MAAM,CAAE,MAAc,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QAC3B,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE3E,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAE/E,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,sBAAsB,CAAC,CAAC;QAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,sBAAsB,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE3E,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC9C,cAAc,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC9C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;YACxD,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;SACpB,CAAC,CAAC,CAAC;QAEJ,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/config/__tests__/loader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
3
|
+
getLogger: () => ({
|
|
4
|
+
info: vi.fn(),
|
|
5
|
+
warn: vi.fn(),
|
|
6
|
+
error: vi.fn(),
|
|
7
|
+
debug: vi.fn(),
|
|
8
|
+
}),
|
|
9
|
+
}));
|
|
10
|
+
vi.mock('../../ipc/client.js', () => ({
|
|
11
|
+
IpcClient: vi.fn().mockImplementation(() => ({
|
|
12
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
13
|
+
request: vi.fn().mockResolvedValue(undefined),
|
|
14
|
+
disconnect: vi.fn(),
|
|
15
|
+
})),
|
|
16
|
+
}));
|
|
17
|
+
vi.mock('../../utils/paths.js', () => ({
|
|
18
|
+
getPipeName: vi.fn((name) => `\\\\.\\pipe\\${name}`),
|
|
19
|
+
}));
|
|
20
|
+
import { CrossBrainSubscriptionManager } from '../subscription.js';
|
|
21
|
+
describe('CrossBrainSubscriptionManager', () => {
|
|
22
|
+
let manager;
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
manager = new CrossBrainSubscriptionManager('brain');
|
|
25
|
+
});
|
|
26
|
+
describe('getSubscriptions', () => {
|
|
27
|
+
it('returns empty array initially', () => {
|
|
28
|
+
expect(manager.getSubscriptions()).toEqual([]);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('subscribe', () => {
|
|
32
|
+
it('stores the subscription so getSubscriptions reflects it', async () => {
|
|
33
|
+
const callback = vi.fn();
|
|
34
|
+
await manager.subscribe('trading-brain', ['error:reported', 'insight:created'], callback);
|
|
35
|
+
const subs = manager.getSubscriptions();
|
|
36
|
+
expect(subs).toHaveLength(1);
|
|
37
|
+
expect(subs[0].peer).toBe('trading-brain');
|
|
38
|
+
expect(subs[0].events).toEqual(['error:reported', 'insight:created']);
|
|
39
|
+
expect(subs[0].callback).toBe(callback);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('handleIncomingEvent', () => {
|
|
43
|
+
it('calls callback for subscribed events', async () => {
|
|
44
|
+
const callback = vi.fn();
|
|
45
|
+
await manager.subscribe('trading-brain', ['error:reported', 'insight:created'], callback);
|
|
46
|
+
manager.handleIncomingEvent('trading-brain', 'error:reported', { errorId: 42 });
|
|
47
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(callback).toHaveBeenCalledWith('error:reported', { errorId: 42 });
|
|
49
|
+
});
|
|
50
|
+
it('does NOT call callback for unsubscribed events', async () => {
|
|
51
|
+
const callback = vi.fn();
|
|
52
|
+
await manager.subscribe('trading-brain', ['error:reported'], callback);
|
|
53
|
+
manager.handleIncomingEvent('trading-brain', 'some:other:event', { data: 1 });
|
|
54
|
+
expect(callback).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
it('does NOT call callback for wrong peer', async () => {
|
|
57
|
+
const callback = vi.fn();
|
|
58
|
+
await manager.subscribe('trading-brain', ['error:reported'], callback);
|
|
59
|
+
manager.handleIncomingEvent('marketing-brain', 'error:reported', { errorId: 1 });
|
|
60
|
+
expect(callback).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('disconnectAll', () => {
|
|
64
|
+
it('clears all subscriptions', async () => {
|
|
65
|
+
const cb1 = vi.fn();
|
|
66
|
+
const cb2 = vi.fn();
|
|
67
|
+
await manager.subscribe('trading-brain', ['error:reported'], cb1);
|
|
68
|
+
await manager.subscribe('marketing-brain', ['insight:created'], cb2);
|
|
69
|
+
expect(manager.getSubscriptions()).toHaveLength(2);
|
|
70
|
+
await manager.disconnectAll();
|
|
71
|
+
expect(manager.getSubscriptions()).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=subscription.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.test.js","sourceRoot":"","sources":["../../../src/cross-brain/__tests__/subscription.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface EventSubscription {
|
|
2
|
+
peer: string;
|
|
3
|
+
events: string[];
|
|
4
|
+
callback: (event: string, data: unknown) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare class CrossBrainSubscriptionManager {
|
|
7
|
+
private subscriptions;
|
|
8
|
+
private connections;
|
|
9
|
+
private logger;
|
|
10
|
+
private selfName;
|
|
11
|
+
constructor(selfName: string);
|
|
12
|
+
/**
|
|
13
|
+
* Subscribe to events from a peer brain.
|
|
14
|
+
* Maintains a persistent IPC connection for receiving events.
|
|
15
|
+
*/
|
|
16
|
+
subscribe(peer: string, events: string[], callback: (event: string, data: unknown) => void): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Unsubscribe from a peer's events.
|
|
19
|
+
*/
|
|
20
|
+
unsubscribe(peer: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Handle incoming event from a peer (called by IPC server).
|
|
23
|
+
*/
|
|
24
|
+
handleIncomingEvent(peer: string, event: string, data: unknown): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get all active subscriptions.
|
|
27
|
+
*/
|
|
28
|
+
getSubscriptions(): EventSubscription[];
|
|
29
|
+
/**
|
|
30
|
+
* Disconnect all persistent connections.
|
|
31
|
+
*/
|
|
32
|
+
disconnectAll(): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { IpcClient } from '../ipc/client.js';
|
|
2
|
+
import { getLogger } from '../utils/logger.js';
|
|
3
|
+
import { getPipeName } from '../utils/paths.js';
|
|
4
|
+
export class CrossBrainSubscriptionManager {
|
|
5
|
+
subscriptions = new Map();
|
|
6
|
+
connections = new Map();
|
|
7
|
+
logger = getLogger();
|
|
8
|
+
selfName;
|
|
9
|
+
constructor(selfName) {
|
|
10
|
+
this.selfName = selfName;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Subscribe to events from a peer brain.
|
|
14
|
+
* Maintains a persistent IPC connection for receiving events.
|
|
15
|
+
*/
|
|
16
|
+
async subscribe(peer, events, callback) {
|
|
17
|
+
const key = peer;
|
|
18
|
+
// Store subscription
|
|
19
|
+
this.subscriptions.set(key, { peer, events, callback });
|
|
20
|
+
// Connect to peer
|
|
21
|
+
try {
|
|
22
|
+
const client = new IpcClient(getPipeName(peer), 5000);
|
|
23
|
+
await client.connect();
|
|
24
|
+
// Send subscription request
|
|
25
|
+
await client.request('cross-brain.subscribe', {
|
|
26
|
+
subscriber: this.selfName,
|
|
27
|
+
events,
|
|
28
|
+
});
|
|
29
|
+
this.connections.set(peer, client);
|
|
30
|
+
this.logger.info(`Subscribed to ${peer} events: ${events.join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
this.logger.warn(`Failed to subscribe to ${peer}: ${err}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Unsubscribe from a peer's events.
|
|
38
|
+
*/
|
|
39
|
+
async unsubscribe(peer) {
|
|
40
|
+
const client = this.connections.get(peer);
|
|
41
|
+
if (client) {
|
|
42
|
+
try {
|
|
43
|
+
await client.request('cross-brain.unsubscribe', {
|
|
44
|
+
subscriber: this.selfName,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch { /* peer may be offline */ }
|
|
48
|
+
client.disconnect();
|
|
49
|
+
this.connections.delete(peer);
|
|
50
|
+
}
|
|
51
|
+
this.subscriptions.delete(peer);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Handle incoming event from a peer (called by IPC server).
|
|
55
|
+
*/
|
|
56
|
+
handleIncomingEvent(peer, event, data) {
|
|
57
|
+
const sub = this.subscriptions.get(peer);
|
|
58
|
+
if (sub && sub.events.includes(event)) {
|
|
59
|
+
sub.callback(event, data);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get all active subscriptions.
|
|
64
|
+
*/
|
|
65
|
+
getSubscriptions() {
|
|
66
|
+
return Array.from(this.subscriptions.values());
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Disconnect all persistent connections.
|
|
70
|
+
*/
|
|
71
|
+
async disconnectAll() {
|
|
72
|
+
for (const [peer, client] of this.connections) {
|
|
73
|
+
try {
|
|
74
|
+
client.disconnect();
|
|
75
|
+
}
|
|
76
|
+
catch { /* ignore */ }
|
|
77
|
+
this.logger.debug(`Disconnected from ${peer}`);
|
|
78
|
+
}
|
|
79
|
+
this.connections.clear();
|
|
80
|
+
this.subscriptions.clear();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=subscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/cross-brain/subscription.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
4
|
+
getLogger: () => ({
|
|
5
|
+
info: vi.fn(),
|
|
6
|
+
warn: vi.fn(),
|
|
7
|
+
error: vi.fn(),
|
|
8
|
+
debug: vi.fn(),
|
|
9
|
+
}),
|
|
10
|
+
}));
|
|
11
|
+
import { DashboardServer } from '../server.js';
|
|
12
|
+
/** Helper: make an HTTP request and return { statusCode, headers, body }. */
|
|
13
|
+
function request(port, path, method = 'GET') {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const req = http.request({ hostname: '127.0.0.1', port, path, method }, (res) => {
|
|
16
|
+
let body = '';
|
|
17
|
+
res.on('data', (chunk) => (body += chunk));
|
|
18
|
+
res.on('end', () => resolve({ statusCode: res.statusCode, headers: res.headers, body }));
|
|
19
|
+
});
|
|
20
|
+
req.on('error', reject);
|
|
21
|
+
req.end();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** Start a DashboardServer on a random port and return { server, port }. */
|
|
25
|
+
function startServer(overrides = {}) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const dashboard = new DashboardServer({
|
|
28
|
+
port: 0,
|
|
29
|
+
getDashboardHtml: overrides.getDashboardHtml ?? (() => '<html><body></body></html>'),
|
|
30
|
+
getStats: overrides.getStats ?? (() => ({ memories: 42, uptime: 100 })),
|
|
31
|
+
});
|
|
32
|
+
dashboard.start();
|
|
33
|
+
// Access the underlying server to get the assigned port
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
const internal = dashboard.server;
|
|
36
|
+
internal.on('listening', () => {
|
|
37
|
+
const addr = internal.address();
|
|
38
|
+
resolve({ dashboard, port: addr.port });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
describe('DashboardServer', () => {
|
|
43
|
+
let dashboard = null;
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
dashboard?.stop();
|
|
46
|
+
dashboard = null;
|
|
47
|
+
});
|
|
48
|
+
it('GET / returns HTML with SSE script injected', async () => {
|
|
49
|
+
const result = await startServer({
|
|
50
|
+
getDashboardHtml: () => '<html><body><h1>Dashboard</h1></body></html>',
|
|
51
|
+
});
|
|
52
|
+
dashboard = result.dashboard;
|
|
53
|
+
const res = await request(result.port, '/');
|
|
54
|
+
expect(res.statusCode).toBe(200);
|
|
55
|
+
expect(res.headers['content-type']).toContain('text/html');
|
|
56
|
+
expect(res.body).toContain('EventSource');
|
|
57
|
+
expect(res.body).toContain('<h1>Dashboard</h1>');
|
|
58
|
+
});
|
|
59
|
+
it('GET /api/stats returns JSON from getStats callback', async () => {
|
|
60
|
+
const stats = { totalMemories: 99, activeSynapses: 12 };
|
|
61
|
+
const result = await startServer({ getStats: () => stats });
|
|
62
|
+
dashboard = result.dashboard;
|
|
63
|
+
const res = await request(result.port, '/api/stats');
|
|
64
|
+
expect(res.statusCode).toBe(200);
|
|
65
|
+
expect(res.headers['content-type']).toContain('application/json');
|
|
66
|
+
expect(JSON.parse(res.body)).toEqual(stats);
|
|
67
|
+
});
|
|
68
|
+
it('GET /nonexistent returns 404', async () => {
|
|
69
|
+
const result = await startServer();
|
|
70
|
+
dashboard = result.dashboard;
|
|
71
|
+
const res = await request(result.port, '/nonexistent');
|
|
72
|
+
expect(res.statusCode).toBe(404);
|
|
73
|
+
expect(res.body).toBe('Not Found');
|
|
74
|
+
});
|
|
75
|
+
it('OPTIONS returns 204 with CORS headers', async () => {
|
|
76
|
+
const result = await startServer();
|
|
77
|
+
dashboard = result.dashboard;
|
|
78
|
+
const res = await request(result.port, '/', 'OPTIONS');
|
|
79
|
+
expect(res.statusCode).toBe(204);
|
|
80
|
+
expect(res.headers['access-control-allow-origin']).toBe('*');
|
|
81
|
+
expect(res.headers['access-control-allow-methods']).toContain('GET');
|
|
82
|
+
});
|
|
83
|
+
it('stop() closes the server', async () => {
|
|
84
|
+
const result = await startServer();
|
|
85
|
+
dashboard = result.dashboard;
|
|
86
|
+
// Verify server responds before stop
|
|
87
|
+
const resBefore = await request(result.port, '/api/stats');
|
|
88
|
+
expect(resBefore.statusCode).toBe(200);
|
|
89
|
+
dashboard.stop();
|
|
90
|
+
dashboard = null;
|
|
91
|
+
// After stop, connection should be refused
|
|
92
|
+
await expect(request(result.port, '/')).rejects.toThrow();
|
|
93
|
+
});
|
|
94
|
+
it('sets CORS headers on all responses', async () => {
|
|
95
|
+
const result = await startServer();
|
|
96
|
+
dashboard = result.dashboard;
|
|
97
|
+
const resRoot = await request(result.port, '/');
|
|
98
|
+
expect(resRoot.headers['access-control-allow-origin']).toBe('*');
|
|
99
|
+
const resStats = await request(result.port, '/api/stats');
|
|
100
|
+
expect(resStats.headers['access-control-allow-origin']).toBe('*');
|
|
101
|
+
const res404 = await request(result.port, '/nope');
|
|
102
|
+
expect(res404.headers['access-control-allow-origin']).toBe('*');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../../src/dashboard/__tests__/server.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DashboardServerOptions {
|
|
2
|
+
port: number;
|
|
3
|
+
getDashboardHtml: () => string;
|
|
4
|
+
getStats: () => unknown;
|
|
5
|
+
eventNames?: readonly string[];
|
|
6
|
+
getEventBus?: () => {
|
|
7
|
+
on(event: string, handler: (data: unknown) => void): void;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare class DashboardServer {
|
|
11
|
+
private options;
|
|
12
|
+
private server;
|
|
13
|
+
private clients;
|
|
14
|
+
private logger;
|
|
15
|
+
constructor(options: DashboardServerOptions);
|
|
16
|
+
start(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
private broadcast;
|
|
19
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import { getLogger } from '../utils/logger.js';
|
|
3
|
+
export class DashboardServer {
|
|
4
|
+
options;
|
|
5
|
+
server = null;
|
|
6
|
+
clients = new Set();
|
|
7
|
+
logger = getLogger();
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
}
|
|
11
|
+
start() {
|
|
12
|
+
const { port, getDashboardHtml, getStats, eventNames, getEventBus } = this.options;
|
|
13
|
+
this.server = http.createServer((req, res) => {
|
|
14
|
+
const url = new URL(req.url ?? '/', `http://localhost:${port}`);
|
|
15
|
+
// CORS
|
|
16
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
17
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
18
|
+
if (req.method === 'OPTIONS') {
|
|
19
|
+
res.writeHead(204);
|
|
20
|
+
res.end();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (url.pathname === '/events') {
|
|
24
|
+
// SSE endpoint
|
|
25
|
+
res.writeHead(200, {
|
|
26
|
+
'Content-Type': 'text/event-stream',
|
|
27
|
+
'Cache-Control': 'no-cache',
|
|
28
|
+
'Connection': 'keep-alive',
|
|
29
|
+
});
|
|
30
|
+
res.write('data: {"type":"connected"}\n\n');
|
|
31
|
+
this.clients.add(res);
|
|
32
|
+
req.on('close', () => this.clients.delete(res));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (url.pathname === '/api/stats') {
|
|
36
|
+
const stats = getStats();
|
|
37
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
38
|
+
res.end(JSON.stringify(stats));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (url.pathname === '/' || url.pathname === '/dashboard') {
|
|
42
|
+
const html = getDashboardHtml();
|
|
43
|
+
// Inject SSE script into the dashboard
|
|
44
|
+
const sseScript = `
|
|
45
|
+
<script>
|
|
46
|
+
(function(){
|
|
47
|
+
const evtSource = new EventSource('/events');
|
|
48
|
+
evtSource.onmessage = function(e) {
|
|
49
|
+
try {
|
|
50
|
+
const data = JSON.parse(e.data);
|
|
51
|
+
if (data.type === 'stats_update') {
|
|
52
|
+
document.querySelectorAll('.stat-number').forEach(el => {
|
|
53
|
+
const key = el.parentElement?.querySelector('.stat-label')?.textContent?.toLowerCase();
|
|
54
|
+
if (key && data.stats[key] !== undefined) {
|
|
55
|
+
el.textContent = Number(data.stats[key]).toLocaleString();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (data.type === 'event') {
|
|
60
|
+
const dot = document.querySelector('.activity-dot');
|
|
61
|
+
if (dot) { dot.style.background = '#ff5577'; setTimeout(() => dot.style.background = '', 500); }
|
|
62
|
+
}
|
|
63
|
+
} catch {}
|
|
64
|
+
};
|
|
65
|
+
evtSource.onerror = function() { setTimeout(() => location.reload(), 5000); };
|
|
66
|
+
})();
|
|
67
|
+
</script>`;
|
|
68
|
+
const liveHtml = html.replace('</body>', sseScript + '</body>');
|
|
69
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
70
|
+
res.end(liveHtml);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
74
|
+
res.end('Not Found');
|
|
75
|
+
});
|
|
76
|
+
// Forward events to SSE clients
|
|
77
|
+
if (eventNames && getEventBus) {
|
|
78
|
+
const bus = getEventBus();
|
|
79
|
+
for (const eventName of eventNames) {
|
|
80
|
+
bus.on(eventName, (data) => {
|
|
81
|
+
this.broadcast({ type: 'event', event: eventName, data });
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Periodic stats broadcast (every 30s)
|
|
86
|
+
setInterval(() => {
|
|
87
|
+
if (this.clients.size > 0) {
|
|
88
|
+
const stats = getStats();
|
|
89
|
+
this.broadcast({ type: 'stats_update', stats });
|
|
90
|
+
}
|
|
91
|
+
}, 30_000);
|
|
92
|
+
this.server.listen(port, () => {
|
|
93
|
+
this.logger.info(`Dashboard server started on http://localhost:${port}`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
stop() {
|
|
97
|
+
for (const client of this.clients) {
|
|
98
|
+
client.end();
|
|
99
|
+
}
|
|
100
|
+
this.clients.clear();
|
|
101
|
+
this.server?.close();
|
|
102
|
+
this.server = null;
|
|
103
|
+
this.logger.info('Dashboard server stopped');
|
|
104
|
+
}
|
|
105
|
+
broadcast(data) {
|
|
106
|
+
const msg = `data: ${JSON.stringify(data)}\n\n`;
|
|
107
|
+
for (const client of this.clients) {
|
|
108
|
+
try {
|
|
109
|
+
client.write(msg);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
this.clients.delete(client);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
3
|
+
getLogger: () => ({
|
|
4
|
+
info: vi.fn(),
|
|
5
|
+
warn: vi.fn(),
|
|
6
|
+
error: vi.fn(),
|
|
7
|
+
debug: vi.fn(),
|
|
8
|
+
}),
|
|
9
|
+
}));
|
|
10
|
+
import { BaseEmbeddingEngine } from '../engine.js';
|
|
11
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
12
|
+
describe('BaseEmbeddingEngine', () => {
|
|
13
|
+
describe('constructor', () => {
|
|
14
|
+
it('sets modelName default to Xenova/all-MiniLM-L6-v2', () => {
|
|
15
|
+
const engine = new BaseEmbeddingEngine({ enabled: true });
|
|
16
|
+
expect(engine.modelName).toBe('Xenova/all-MiniLM-L6-v2');
|
|
17
|
+
});
|
|
18
|
+
it('respects custom modelName', () => {
|
|
19
|
+
const engine = new BaseEmbeddingEngine({
|
|
20
|
+
enabled: true,
|
|
21
|
+
modelName: 'custom/model-v1',
|
|
22
|
+
});
|
|
23
|
+
expect(engine.modelName).toBe('custom/model-v1');
|
|
24
|
+
});
|
|
25
|
+
it('sets enabled flag', () => {
|
|
26
|
+
const enabledEngine = new BaseEmbeddingEngine({ enabled: true });
|
|
27
|
+
const disabledEngine = new BaseEmbeddingEngine({ enabled: false });
|
|
28
|
+
expect(enabledEngine.enabled).toBe(true);
|
|
29
|
+
expect(disabledEngine.enabled).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('isReady', () => {
|
|
33
|
+
it('returns false before initialize', () => {
|
|
34
|
+
const engine = new BaseEmbeddingEngine({ enabled: true });
|
|
35
|
+
expect(engine.isReady()).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('embed', () => {
|
|
39
|
+
it('throws if not initialized', async () => {
|
|
40
|
+
const engine = new BaseEmbeddingEngine({ enabled: true });
|
|
41
|
+
await expect(engine.embed('hello world')).rejects.toThrow('EmbeddingEngine not initialized');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('embedBatch', () => {
|
|
45
|
+
it('returns nulls if not ready', async () => {
|
|
46
|
+
const engine = new BaseEmbeddingEngine({ enabled: true });
|
|
47
|
+
const results = await engine.embedBatch(['hello', 'world']);
|
|
48
|
+
expect(results).toEqual([null, null]);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('similarity', () => {
|
|
52
|
+
it('returns 1.0 for identical vectors', () => {
|
|
53
|
+
// Normalized vector: [0.6, 0.8] has magnitude 1.0
|
|
54
|
+
const a = new Float32Array([0.6, 0.8]);
|
|
55
|
+
const b = new Float32Array([0.6, 0.8]);
|
|
56
|
+
expect(BaseEmbeddingEngine.similarity(a, b)).toBeCloseTo(1.0, 5);
|
|
57
|
+
});
|
|
58
|
+
it('returns 0 for orthogonal vectors', () => {
|
|
59
|
+
// [1, 0] and [0, 1] are orthogonal — dot product = 0
|
|
60
|
+
const a = new Float32Array([1, 0]);
|
|
61
|
+
const b = new Float32Array([0, 1]);
|
|
62
|
+
expect(BaseEmbeddingEngine.similarity(a, b)).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
it('returns 0 for different length vectors', () => {
|
|
65
|
+
const a = new Float32Array([0.6, 0.8]);
|
|
66
|
+
const b = new Float32Array([0.6, 0.8, 0.0]);
|
|
67
|
+
expect(BaseEmbeddingEngine.similarity(a, b)).toBe(0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('serialize / deserialize', () => {
|
|
71
|
+
it('round-trip preserves data', () => {
|
|
72
|
+
const original = new Float32Array([0.1, 0.25, -0.5, 1.0, 0.0]);
|
|
73
|
+
const buffer = BaseEmbeddingEngine.serialize(original);
|
|
74
|
+
const restored = BaseEmbeddingEngine.deserialize(buffer);
|
|
75
|
+
expect(restored.length).toBe(original.length);
|
|
76
|
+
for (let i = 0; i < original.length; i++) {
|
|
77
|
+
expect(restored[i]).toBeCloseTo(original[i], 6);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=engine.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.test.js","sourceRoot":"","sources":["../../../src/embeddings/__tests__/engine.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the BaseEmbeddingEngine.
|
|
3
|
+
* Only core embedding settings — no sweep/domain logic.
|
|
4
|
+
*/
|
|
5
|
+
export interface EmbeddingConfig {
|
|
6
|
+
/** Whether embeddings are enabled */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/** HuggingFace model name (default: "Xenova/all-MiniLM-L6-v2") */
|
|
9
|
+
modelName?: string;
|
|
10
|
+
/** Local cache directory for model files */
|
|
11
|
+
cacheDir?: string;
|
|
12
|
+
}
|
|
13
|
+
type Pipeline = any;
|
|
14
|
+
/**
|
|
15
|
+
* Generic embedding engine that loads a HuggingFace transformer model
|
|
16
|
+
* and provides text embedding + cosine similarity.
|
|
17
|
+
*
|
|
18
|
+
* `@huggingface/transformers` is an optional peer dependency —
|
|
19
|
+
* it is dynamically imported at initialization time so consumers
|
|
20
|
+
* that don't need embeddings don't pay the cost.
|
|
21
|
+
*
|
|
22
|
+
* Subclasses (brain, trading-brain, marketing-brain) add their own
|
|
23
|
+
* domain-specific sweep / batch-embed logic on top.
|
|
24
|
+
*/
|
|
25
|
+
export declare class BaseEmbeddingEngine {
|
|
26
|
+
protected pipeline: Pipeline;
|
|
27
|
+
protected ready: boolean;
|
|
28
|
+
protected loading: boolean;
|
|
29
|
+
protected logger: import("winston").Logger;
|
|
30
|
+
protected modelName: string;
|
|
31
|
+
protected cacheDir: string | undefined;
|
|
32
|
+
protected enabled: boolean;
|
|
33
|
+
constructor(config: EmbeddingConfig);
|
|
34
|
+
/** Load the transformer model. Safe to call multiple times. */
|
|
35
|
+
initialize(): Promise<void>;
|
|
36
|
+
/** Whether the model has been loaded and is ready. */
|
|
37
|
+
isReady(): boolean;
|
|
38
|
+
/** Generate an embedding vector for a single text string. */
|
|
39
|
+
embed(text: string): Promise<Float32Array>;
|
|
40
|
+
/** Generate embeddings for a batch of texts. */
|
|
41
|
+
embedBatch(texts: string[]): Promise<(Float32Array | null)[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Cosine similarity between two L2-normalized embedding vectors.
|
|
44
|
+
* Since the vectors are already normalized, dot product equals cosine.
|
|
45
|
+
*/
|
|
46
|
+
static similarity(a: Float32Array, b: Float32Array): number;
|
|
47
|
+
/** Instance convenience wrapper for static similarity. */
|
|
48
|
+
similarity(a: Float32Array, b: Float32Array): number;
|
|
49
|
+
/** Serialize Float32Array to Buffer for SQLite BLOB storage. */
|
|
50
|
+
static serialize(embedding: Float32Array): Buffer;
|
|
51
|
+
/** Deserialize SQLite BLOB to Float32Array. */
|
|
52
|
+
static deserialize(buffer: Buffer): Float32Array;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generic embedding engine that loads a HuggingFace transformer model
|
|
4
|
+
* and provides text embedding + cosine similarity.
|
|
5
|
+
*
|
|
6
|
+
* `@huggingface/transformers` is an optional peer dependency —
|
|
7
|
+
* it is dynamically imported at initialization time so consumers
|
|
8
|
+
* that don't need embeddings don't pay the cost.
|
|
9
|
+
*
|
|
10
|
+
* Subclasses (brain, trading-brain, marketing-brain) add their own
|
|
11
|
+
* domain-specific sweep / batch-embed logic on top.
|
|
12
|
+
*/
|
|
13
|
+
export class BaseEmbeddingEngine {
|
|
14
|
+
pipeline = null;
|
|
15
|
+
ready = false;
|
|
16
|
+
loading = false;
|
|
17
|
+
logger = getLogger();
|
|
18
|
+
modelName;
|
|
19
|
+
cacheDir;
|
|
20
|
+
enabled;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.enabled = config.enabled;
|
|
23
|
+
this.modelName = config.modelName ?? 'Xenova/all-MiniLM-L6-v2';
|
|
24
|
+
this.cacheDir = config.cacheDir;
|
|
25
|
+
}
|
|
26
|
+
/** Load the transformer model. Safe to call multiple times. */
|
|
27
|
+
async initialize() {
|
|
28
|
+
if (!this.enabled || this.loading || this.ready)
|
|
29
|
+
return;
|
|
30
|
+
this.loading = true;
|
|
31
|
+
try {
|
|
32
|
+
// Dynamic import — @huggingface/transformers is an optional peer dep
|
|
33
|
+
let pipelineFn;
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
let env;
|
|
36
|
+
try {
|
|
37
|
+
const transformers = await import('@huggingface/transformers');
|
|
38
|
+
pipelineFn = transformers.pipeline;
|
|
39
|
+
env = transformers.env;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new Error('Embeddings require @huggingface/transformers. Install with: npm install @huggingface/transformers');
|
|
43
|
+
}
|
|
44
|
+
if (this.cacheDir) {
|
|
45
|
+
env.cacheDir = this.cacheDir;
|
|
46
|
+
}
|
|
47
|
+
this.pipeline = await pipelineFn('feature-extraction', this.modelName, { dtype: 'q8' });
|
|
48
|
+
this.ready = true;
|
|
49
|
+
this.logger.info(`Embedding model loaded: ${this.modelName}`);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
this.logger.warn(`Failed to load embedding model: ${err}`);
|
|
53
|
+
this.ready = false;
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
this.loading = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Whether the model has been loaded and is ready. */
|
|
61
|
+
isReady() {
|
|
62
|
+
return this.ready;
|
|
63
|
+
}
|
|
64
|
+
/** Generate an embedding vector for a single text string. */
|
|
65
|
+
async embed(text) {
|
|
66
|
+
if (!this.ready || !this.pipeline) {
|
|
67
|
+
throw new Error('EmbeddingEngine not initialized — call initialize() first');
|
|
68
|
+
}
|
|
69
|
+
const output = await this.pipeline(text, { pooling: 'mean', normalize: true });
|
|
70
|
+
const data = output.tolist()[0];
|
|
71
|
+
return new Float32Array(data);
|
|
72
|
+
}
|
|
73
|
+
/** Generate embeddings for a batch of texts. */
|
|
74
|
+
async embedBatch(texts) {
|
|
75
|
+
if (!this.ready || !this.pipeline || texts.length === 0) {
|
|
76
|
+
return texts.map(() => null);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const output = await this.pipeline(texts, { pooling: 'mean', normalize: true });
|
|
80
|
+
const list = output.tolist();
|
|
81
|
+
return list.map(v => new Float32Array(v));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
this.logger.error(`Batch embedding error: ${err}`);
|
|
85
|
+
return texts.map(() => null);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Cosine similarity between two L2-normalized embedding vectors.
|
|
90
|
+
* Since the vectors are already normalized, dot product equals cosine.
|
|
91
|
+
*/
|
|
92
|
+
static similarity(a, b) {
|
|
93
|
+
if (a.length !== b.length)
|
|
94
|
+
return 0;
|
|
95
|
+
let dot = 0;
|
|
96
|
+
for (let i = 0; i < a.length; i++) {
|
|
97
|
+
dot += a[i] * b[i];
|
|
98
|
+
}
|
|
99
|
+
return Math.max(0, Math.min(1, dot));
|
|
100
|
+
}
|
|
101
|
+
/** Instance convenience wrapper for static similarity. */
|
|
102
|
+
similarity(a, b) {
|
|
103
|
+
return BaseEmbeddingEngine.similarity(a, b);
|
|
104
|
+
}
|
|
105
|
+
/** Serialize Float32Array to Buffer for SQLite BLOB storage. */
|
|
106
|
+
static serialize(embedding) {
|
|
107
|
+
return Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
108
|
+
}
|
|
109
|
+
/** Deserialize SQLite BLOB to Float32Array. */
|
|
110
|
+
static deserialize(buffer) {
|
|
111
|
+
const copy = Buffer.from(buffer);
|
|
112
|
+
return new Float32Array(copy.buffer, copy.byteOffset, copy.length / 4);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/embeddings/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -30,9 +30,15 @@ export { BaseLearningEngine } from './learning/base-engine.js';
|
|
|
30
30
|
export type { LearningEngineConfig } from './learning/base-engine.js';
|
|
31
31
|
export { BaseResearchEngine } from './research/base-engine.js';
|
|
32
32
|
export type { ResearchEngineConfig } from './research/base-engine.js';
|
|
33
|
+
export { BaseEmbeddingEngine } from './embeddings/engine.js';
|
|
34
|
+
export type { EmbeddingConfig } from './embeddings/engine.js';
|
|
33
35
|
export type { MemoryRecord, SessionRecord, MemoryCategory, MemorySource, SessionOutcome, RememberInput, RecallInput, StartSessionInput, EndSessionInput, MemoryRepoInterface, SessionRepoInterface, MemoryEngineConfig, } from './memory/types.js';
|
|
34
36
|
export { BaseMemoryEngine } from './memory/base-memory-engine.js';
|
|
37
|
+
export { DashboardServer } from './dashboard/server.js';
|
|
38
|
+
export type { DashboardServerOptions } from './dashboard/server.js';
|
|
35
39
|
export { CrossBrainClient } from './cross-brain/client.js';
|
|
36
40
|
export type { BrainPeer } from './cross-brain/client.js';
|
|
37
41
|
export { CrossBrainNotifier } from './cross-brain/notifications.js';
|
|
38
42
|
export type { CrossBrainEvent } from './cross-brain/notifications.js';
|
|
43
|
+
export { CrossBrainSubscriptionManager } from './cross-brain/subscription.js';
|
|
44
|
+
export type { EventSubscription } from './cross-brain/subscription.js';
|
package/dist/index.js
CHANGED
|
@@ -29,8 +29,13 @@ export { BaseSynapseManager } from './synapses/synapse-manager.js';
|
|
|
29
29
|
// ── Engines ───────────────────────────────────────────────
|
|
30
30
|
export { BaseLearningEngine } from './learning/base-engine.js';
|
|
31
31
|
export { BaseResearchEngine } from './research/base-engine.js';
|
|
32
|
+
// ── Embeddings ──────────────────────────────────────────
|
|
33
|
+
export { BaseEmbeddingEngine } from './embeddings/engine.js';
|
|
32
34
|
export { BaseMemoryEngine } from './memory/base-memory-engine.js';
|
|
35
|
+
// ── Dashboard ────────────────────────────────────────────
|
|
36
|
+
export { DashboardServer } from './dashboard/server.js';
|
|
33
37
|
// ── Cross-Brain ────────────────────────────────────────────
|
|
34
38
|
export { CrossBrainClient } from './cross-brain/client.js';
|
|
35
39
|
export { CrossBrainNotifier } from './cross-brain/notifications.js';
|
|
40
|
+
export { CrossBrainSubscriptionManager } from './cross-brain/subscription.js';
|
|
36
41
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,8DAA8D;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQ/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,6DAA6D;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,8DAA8D;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQ/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,6DAA6D;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,2DAA2D;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAU7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC"}
|
package/dist/ipc/server.d.ts
CHANGED
|
@@ -7,10 +7,12 @@ export declare class IpcServer {
|
|
|
7
7
|
private router;
|
|
8
8
|
private pipeName;
|
|
9
9
|
private daemonName;
|
|
10
|
+
private selfName;
|
|
10
11
|
private server;
|
|
11
12
|
private clients;
|
|
13
|
+
private subscribers;
|
|
12
14
|
private logger;
|
|
13
|
-
constructor(router: IpcRouter, pipeName: string, daemonName?: string);
|
|
15
|
+
constructor(router: IpcRouter, pipeName: string, daemonName?: string, selfName?: string);
|
|
14
16
|
start(): void;
|
|
15
17
|
private createServer;
|
|
16
18
|
private listen;
|
|
@@ -18,5 +20,25 @@ export declare class IpcServer {
|
|
|
18
20
|
private handleMessage;
|
|
19
21
|
notify(clientId: string | null, notification: Omit<IpcMessage, 'id' | 'type'>): void;
|
|
20
22
|
getClientCount(): number;
|
|
23
|
+
/**
|
|
24
|
+
* Register a peer brain as a subscriber for specific events.
|
|
25
|
+
*/
|
|
26
|
+
addSubscriber(subscriber: string, events: string[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* Remove a peer brain from the subscriber list.
|
|
29
|
+
*/
|
|
30
|
+
removeSubscriber(subscriber: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Notify all subscribed peers when an event occurs.
|
|
33
|
+
* Fire-and-forget — does not block if a subscriber is offline.
|
|
34
|
+
*/
|
|
35
|
+
notifySubscribers(event: string, data: unknown): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get all current subscribers.
|
|
38
|
+
*/
|
|
39
|
+
getSubscribers(): Map<string, {
|
|
40
|
+
pipeName: string;
|
|
41
|
+
events: string[];
|
|
42
|
+
}>;
|
|
21
43
|
stop(): void;
|
|
22
44
|
}
|
package/dist/ipc/server.js
CHANGED
|
@@ -3,17 +3,22 @@ import fs from 'node:fs';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { getLogger } from '../utils/logger.js';
|
|
5
5
|
import { encodeMessage, MessageDecoder } from './protocol.js';
|
|
6
|
+
import { IpcClient } from './client.js';
|
|
7
|
+
import { getPipeName } from '../utils/paths.js';
|
|
6
8
|
export class IpcServer {
|
|
7
9
|
router;
|
|
8
10
|
pipeName;
|
|
9
11
|
daemonName;
|
|
12
|
+
selfName;
|
|
10
13
|
server = null;
|
|
11
14
|
clients = new Map();
|
|
15
|
+
subscribers = new Map();
|
|
12
16
|
logger = getLogger();
|
|
13
|
-
constructor(router, pipeName, daemonName = 'brain') {
|
|
17
|
+
constructor(router, pipeName, daemonName = 'brain', selfName = 'brain') {
|
|
14
18
|
this.router = router;
|
|
15
19
|
this.pipeName = pipeName;
|
|
16
20
|
this.daemonName = daemonName;
|
|
21
|
+
this.selfName = selfName;
|
|
17
22
|
}
|
|
18
23
|
start() {
|
|
19
24
|
this.createServer();
|
|
@@ -143,6 +148,44 @@ export class IpcServer {
|
|
|
143
148
|
getClientCount() {
|
|
144
149
|
return this.clients.size;
|
|
145
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Register a peer brain as a subscriber for specific events.
|
|
153
|
+
*/
|
|
154
|
+
addSubscriber(subscriber, events) {
|
|
155
|
+
this.subscribers.set(subscriber, {
|
|
156
|
+
pipeName: getPipeName(subscriber),
|
|
157
|
+
events,
|
|
158
|
+
});
|
|
159
|
+
this.logger.info(`Subscriber registered: ${subscriber} for events: ${events.join(', ')}`);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Remove a peer brain from the subscriber list.
|
|
163
|
+
*/
|
|
164
|
+
removeSubscriber(subscriber) {
|
|
165
|
+
this.subscribers.delete(subscriber);
|
|
166
|
+
this.logger.info(`Subscriber removed: ${subscriber}`);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Notify all subscribed peers when an event occurs.
|
|
170
|
+
* Fire-and-forget — does not block if a subscriber is offline.
|
|
171
|
+
*/
|
|
172
|
+
notifySubscribers(event, data) {
|
|
173
|
+
for (const [, sub] of this.subscribers) {
|
|
174
|
+
if (sub.events.includes(event)) {
|
|
175
|
+
const client = new IpcClient(sub.pipeName, 3000);
|
|
176
|
+
client.connect()
|
|
177
|
+
.then(() => client.request('cross-brain.event', { source: this.selfName, event, data }))
|
|
178
|
+
.catch(() => { })
|
|
179
|
+
.finally(() => client.disconnect());
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get all current subscribers.
|
|
185
|
+
*/
|
|
186
|
+
getSubscribers() {
|
|
187
|
+
return this.subscribers;
|
|
188
|
+
}
|
|
146
189
|
stop() {
|
|
147
190
|
for (const socket of this.clients.values()) {
|
|
148
191
|
socket.destroy();
|
package/dist/ipc/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ipc/server.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ipc/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { BaseEmbeddingEngine } from '../embeddings/engine.js';
|
|
2
|
+
import type { MemoryRecord, MemoryEngineConfig } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Abstract base class for memory engines.
|
|
4
5
|
* Handles timer lifecycle for periodic memory maintenance:
|
|
@@ -6,14 +7,39 @@ import type { MemoryEngineConfig } from './types.js';
|
|
|
6
7
|
* - Consolidation (merge similar memories)
|
|
7
8
|
* - Importance decay (reduce importance of never-recalled memories)
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* Optionally supports semantic recall via BaseEmbeddingEngine.
|
|
11
|
+
* Subclasses implement runCycle() and may override semanticRecall().
|
|
10
12
|
*/
|
|
11
13
|
export declare abstract class BaseMemoryEngine {
|
|
12
14
|
protected config: MemoryEngineConfig;
|
|
13
15
|
protected timer: ReturnType<typeof setInterval> | null;
|
|
14
16
|
protected logger: import("winston").Logger;
|
|
17
|
+
protected embeddingEngine: BaseEmbeddingEngine | null;
|
|
15
18
|
constructor(config: MemoryEngineConfig);
|
|
19
|
+
/** Attach an embedding engine for semantic recall support. */
|
|
20
|
+
setEmbeddingEngine(engine: BaseEmbeddingEngine): void;
|
|
16
21
|
start(): void;
|
|
17
22
|
stop(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Recall memories semantically similar to the query.
|
|
25
|
+
*
|
|
26
|
+
* Default implementation:
|
|
27
|
+
* - If an embedding engine is available and ready, embeds the query and
|
|
28
|
+
* compares against memory embeddings via cosine similarity.
|
|
29
|
+
* - Falls back to textSearch() if embeddings are unavailable.
|
|
30
|
+
*
|
|
31
|
+
* Subclasses may override for domain-specific retrieval.
|
|
32
|
+
*/
|
|
33
|
+
semanticRecall(query: string, limit?: number): Promise<MemoryRecord[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Return active memories that have pre-computed embeddings.
|
|
36
|
+
* Subclasses must implement this to query their database.
|
|
37
|
+
*/
|
|
38
|
+
protected abstract getActiveMemoriesWithEmbeddings(): MemoryRecord[];
|
|
39
|
+
/**
|
|
40
|
+
* Text-based fallback search for memories.
|
|
41
|
+
* Subclasses must implement this to query their database (e.g. FTS).
|
|
42
|
+
*/
|
|
43
|
+
protected abstract textSearch(query: string, limit: number): MemoryRecord[];
|
|
18
44
|
abstract runCycle(): unknown;
|
|
19
45
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
import { BaseEmbeddingEngine } from '../embeddings/engine.js';
|
|
2
3
|
/**
|
|
3
4
|
* Abstract base class for memory engines.
|
|
4
5
|
* Handles timer lifecycle for periodic memory maintenance:
|
|
@@ -6,15 +7,21 @@ import { getLogger } from '../utils/logger.js';
|
|
|
6
7
|
* - Consolidation (merge similar memories)
|
|
7
8
|
* - Importance decay (reduce importance of never-recalled memories)
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* Optionally supports semantic recall via BaseEmbeddingEngine.
|
|
11
|
+
* Subclasses implement runCycle() and may override semanticRecall().
|
|
10
12
|
*/
|
|
11
13
|
export class BaseMemoryEngine {
|
|
12
14
|
config;
|
|
13
15
|
timer = null;
|
|
14
16
|
logger = getLogger();
|
|
17
|
+
embeddingEngine = null;
|
|
15
18
|
constructor(config) {
|
|
16
19
|
this.config = config;
|
|
17
20
|
}
|
|
21
|
+
/** Attach an embedding engine for semantic recall support. */
|
|
22
|
+
setEmbeddingEngine(engine) {
|
|
23
|
+
this.embeddingEngine = engine;
|
|
24
|
+
}
|
|
18
25
|
start() {
|
|
19
26
|
this.timer = setInterval(() => {
|
|
20
27
|
try {
|
|
@@ -31,5 +38,37 @@ export class BaseMemoryEngine {
|
|
|
31
38
|
this.timer = null;
|
|
32
39
|
}
|
|
33
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Recall memories semantically similar to the query.
|
|
43
|
+
*
|
|
44
|
+
* Default implementation:
|
|
45
|
+
* - If an embedding engine is available and ready, embeds the query and
|
|
46
|
+
* compares against memory embeddings via cosine similarity.
|
|
47
|
+
* - Falls back to textSearch() if embeddings are unavailable.
|
|
48
|
+
*
|
|
49
|
+
* Subclasses may override for domain-specific retrieval.
|
|
50
|
+
*/
|
|
51
|
+
async semanticRecall(query, limit = 10) {
|
|
52
|
+
// Try embedding-based recall first
|
|
53
|
+
if (this.embeddingEngine?.isReady()) {
|
|
54
|
+
try {
|
|
55
|
+
const queryVec = await this.embeddingEngine.embed(query);
|
|
56
|
+
const memories = this.getActiveMemoriesWithEmbeddings();
|
|
57
|
+
const scored = memories
|
|
58
|
+
.map(m => ({
|
|
59
|
+
memory: m,
|
|
60
|
+
score: BaseEmbeddingEngine.similarity(queryVec, BaseEmbeddingEngine.deserialize(m.embedding)),
|
|
61
|
+
}))
|
|
62
|
+
.sort((a, b) => b.score - a.score)
|
|
63
|
+
.slice(0, limit);
|
|
64
|
+
return scored.map(s => s.memory);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
this.logger.warn(`Semantic recall failed, falling back to text search: ${err}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Fallback: text-based search
|
|
71
|
+
return this.textSearch(query, limit);
|
|
72
|
+
}
|
|
34
73
|
}
|
|
35
74
|
//# sourceMappingURL=base-memory-engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-memory-engine.js","sourceRoot":"","sources":["../../src/memory/base-memory-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"base-memory-engine.js","sourceRoot":"","sources":["../../src/memory/base-memory-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D;;;;;;;;;GASG;AACH,MAAM,OAAgB,gBAAgB;IAKd;IAJZ,KAAK,GAA0C,IAAI,CAAC;IACpD,MAAM,GAAG,SAAS,EAAE,CAAC;IACrB,eAAe,GAA+B,IAAI,CAAC;IAE7D,YAAsB,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;IAAG,CAAC;IAEpD,8DAA8D;IAC9D,kBAAkB,CAAC,MAA2B;QAC5C,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;IAChC,CAAC;IAED,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,2BAA2B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7B,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;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc,CAAC,KAAa,EAAE,KAAK,GAAG,EAAE;QAC5C,mCAAmC;QACnC,IAAI,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,+BAA+B,EAAE,CAAC;gBAExD,MAAM,MAAM,GAAG,QAAQ;qBACpB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACT,MAAM,EAAE,CAAC;oBACT,KAAK,EAAE,mBAAmB,CAAC,UAAU,CACnC,QAAQ,EACR,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC,SAAU,CAAC,CAC9C;iBACF,CAAC,CAAC;qBACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;qBACjC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBAEnB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,wDAAwD,GAAG,EAAE,CAAC,CAAC;YAClF,CAAC;QACH,CAAC;QAED,8BAA8B;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;CAeF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/paths.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/paths.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAErE,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC;QAC7C,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QACrD,kDAAkD;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,MAAM,GAAG,UAAU,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,IAAI,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timmeck/brain-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Shared core infrastructure for the Brain ecosystem — IPC, MCP, CLI, DB connection, and utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,9 +29,12 @@
|
|
|
29
29
|
"./synapses/activation": "./dist/synapses/activation.js",
|
|
30
30
|
"./synapses/pathfinder": "./dist/synapses/pathfinder.js",
|
|
31
31
|
"./synapses/synapse-manager": "./dist/synapses/synapse-manager.js",
|
|
32
|
+
"./dashboard/server": "./dist/dashboard/server.js",
|
|
32
33
|
"./cross-brain": "./dist/cross-brain/client.js",
|
|
34
|
+
"./cross-brain/subscription": "./dist/cross-brain/subscription.js",
|
|
33
35
|
"./memory/types": "./dist/memory/types.js",
|
|
34
|
-
"./memory/base-memory-engine": "./dist/memory/base-memory-engine.js"
|
|
36
|
+
"./memory/base-memory-engine": "./dist/memory/base-memory-engine.js",
|
|
37
|
+
"./embeddings/engine": "./dist/embeddings/engine.js"
|
|
35
38
|
},
|
|
36
39
|
"scripts": {
|
|
37
40
|
"build": "tsc",
|
|
@@ -65,6 +68,14 @@
|
|
|
65
68
|
"url": "https://github.com/timmeck/brain-ecosystem",
|
|
66
69
|
"directory": "packages/brain-core"
|
|
67
70
|
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"@huggingface/transformers": "^3.0.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependenciesMeta": {
|
|
75
|
+
"@huggingface/transformers": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
68
79
|
"dependencies": {
|
|
69
80
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
70
81
|
"better-sqlite3": "^11.7.0",
|