@n24q02m/mnemo-plugin 1.1.0 → 1.2.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 +7 -5
- package/build/src/bridge.d.ts +12 -0
- package/build/src/bridge.d.ts.map +1 -1
- package/build/src/bridge.js +64 -5
- package/build/src/bridge.js.map +1 -1
- package/build/src/hooks/auto-capture.js +3 -0
- package/build/src/hooks/auto-capture.js.map +1 -1
- package/build/src/hooks/compaction.d.ts.map +1 -1
- package/build/src/hooks/compaction.js +3 -0
- package/build/src/hooks/compaction.js.map +1 -1
- package/build/src/hooks/system-prompt.d.ts.map +1 -1
- package/build/src/hooks/system-prompt.js +5 -2
- package/build/src/hooks/system-prompt.js.map +1 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +6 -3
- package/build/src/index.js.map +1 -1
- package/build/tests/auto-capture.test.d.ts +8 -0
- package/build/tests/auto-capture.test.d.ts.map +1 -0
- package/build/tests/auto-capture.test.js +168 -0
- package/build/tests/auto-capture.test.js.map +1 -0
- package/build/tests/bridge.test.js +1 -1
- package/build/tests/bridge.test.js.map +1 -1
- package/build/tests/bridge.unit.test.d.ts +7 -0
- package/build/tests/bridge.unit.test.d.ts.map +1 -0
- package/build/tests/bridge.unit.test.js +271 -0
- package/build/tests/bridge.unit.test.js.map +1 -0
- package/build/tests/compaction.test.d.ts +5 -0
- package/build/tests/compaction.test.d.ts.map +1 -0
- package/build/tests/compaction.test.js +117 -0
- package/build/tests/compaction.test.js.map +1 -0
- package/build/tests/system-prompt.test.d.ts +5 -0
- package/build/tests/system-prompt.test.d.ts.map +1 -0
- package/build/tests/system-prompt.test.js +113 -0
- package/build/tests/system-prompt.test.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for MnemoBridge — circuit breaker, timeouts, isAvailable, response parsing.
|
|
3
|
+
*
|
|
4
|
+
* Mocks the private doConnect method to avoid needing real MCP SDK constructors.
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
// We need to mock the SDK modules before importing bridge, but we use
|
|
8
|
+
// a different strategy: spy on the doConnect method after instantiation.
|
|
9
|
+
// This avoids the "is not a constructor" problem with vi.mock for classes.
|
|
10
|
+
vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({
|
|
11
|
+
Client: vi.fn()
|
|
12
|
+
}));
|
|
13
|
+
vi.mock('@modelcontextprotocol/sdk/client/stdio.js', () => ({
|
|
14
|
+
StdioClientTransport: vi.fn()
|
|
15
|
+
}));
|
|
16
|
+
vi.mock('@modelcontextprotocol/sdk/types.js', () => ({
|
|
17
|
+
CallToolResultSchema: {}
|
|
18
|
+
}));
|
|
19
|
+
import { MnemoBridge } from '../src/bridge.js';
|
|
20
|
+
/**
|
|
21
|
+
* Helper: create a bridge instance with doConnect mocked to set up
|
|
22
|
+
* the internal state as if a real connection was established.
|
|
23
|
+
*/
|
|
24
|
+
function setupBridgeWithMockConnect(bridge, opts) {
|
|
25
|
+
const mockClient = {
|
|
26
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
27
|
+
listTools: vi.fn().mockResolvedValue({
|
|
28
|
+
tools: (opts?.tools ?? ['memory', 'config']).map((n) => ({ name: n }))
|
|
29
|
+
}),
|
|
30
|
+
callTool: vi.fn().mockResolvedValue({
|
|
31
|
+
isError: false,
|
|
32
|
+
content: [{ type: 'text', text: '{"status":"ok"}' }]
|
|
33
|
+
})
|
|
34
|
+
};
|
|
35
|
+
const mockTransport = { close: vi.fn().mockResolvedValue(undefined) };
|
|
36
|
+
// Override doConnect to simulate successful connection
|
|
37
|
+
vi.spyOn(bridge, 'doConnect').mockImplementation(async () => {
|
|
38
|
+
if (opts?.connectError)
|
|
39
|
+
throw opts.connectError;
|
|
40
|
+
bridge.transport = mockTransport;
|
|
41
|
+
bridge.client = mockClient;
|
|
42
|
+
bridge.availableTools = new Set(opts?.tools ?? ['memory', 'config']);
|
|
43
|
+
return mockClient;
|
|
44
|
+
});
|
|
45
|
+
return { mockClient, mockTransport };
|
|
46
|
+
}
|
|
47
|
+
describe('MnemoBridge', () => {
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
// Reset singleton between tests
|
|
50
|
+
;
|
|
51
|
+
MnemoBridge.instance = undefined;
|
|
52
|
+
});
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.restoreAllMocks();
|
|
55
|
+
});
|
|
56
|
+
describe('getInstance', () => {
|
|
57
|
+
it('returns a singleton instance', () => {
|
|
58
|
+
const a = MnemoBridge.getInstance();
|
|
59
|
+
const b = MnemoBridge.getInstance();
|
|
60
|
+
expect(a).toBe(b);
|
|
61
|
+
});
|
|
62
|
+
it('creates a new instance after singleton reset', () => {
|
|
63
|
+
const a = MnemoBridge.getInstance();
|
|
64
|
+
MnemoBridge.instance = undefined;
|
|
65
|
+
const b = MnemoBridge.getInstance();
|
|
66
|
+
expect(a).not.toBe(b);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('isAvailable', () => {
|
|
70
|
+
it('returns true when client is connected', async () => {
|
|
71
|
+
const bridge = MnemoBridge.getInstance();
|
|
72
|
+
setupBridgeWithMockConnect(bridge);
|
|
73
|
+
await bridge.connect();
|
|
74
|
+
expect(bridge.isAvailable()).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it('returns true when failCount is below MAX_FAILURES', () => {
|
|
77
|
+
const bridge = MnemoBridge.getInstance();
|
|
78
|
+
bridge.failCount = 2;
|
|
79
|
+
expect(bridge.isAvailable()).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
it('returns false when circuit breaker is open within cooldown', () => {
|
|
82
|
+
const bridge = MnemoBridge.getInstance();
|
|
83
|
+
bridge.failCount = 3;
|
|
84
|
+
bridge.lastFailTime = Date.now();
|
|
85
|
+
expect(bridge.isAvailable()).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
it('returns true when circuit breaker cooldown has elapsed', () => {
|
|
88
|
+
const bridge = MnemoBridge.getInstance();
|
|
89
|
+
bridge.failCount = 3;
|
|
90
|
+
bridge.lastFailTime = Date.now() - 300_001;
|
|
91
|
+
expect(bridge.isAvailable()).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('connect', () => {
|
|
95
|
+
it('connects and caches available tools', async () => {
|
|
96
|
+
const bridge = MnemoBridge.getInstance();
|
|
97
|
+
setupBridgeWithMockConnect(bridge);
|
|
98
|
+
await bridge.connect();
|
|
99
|
+
expect(bridge.availableTools).toBeInstanceOf(Set);
|
|
100
|
+
expect(bridge.availableTools.has('memory')).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it('reuses existing connection', async () => {
|
|
103
|
+
const bridge = MnemoBridge.getInstance();
|
|
104
|
+
setupBridgeWithMockConnect(bridge);
|
|
105
|
+
const c1 = await bridge.connect();
|
|
106
|
+
const c2 = await bridge.connect();
|
|
107
|
+
expect(c1).toBe(c2);
|
|
108
|
+
// doConnect should only be called once
|
|
109
|
+
expect(bridge.doConnect).toHaveBeenCalledTimes(1);
|
|
110
|
+
});
|
|
111
|
+
it('rejects when circuit breaker is open', async () => {
|
|
112
|
+
const bridge = MnemoBridge.getInstance();
|
|
113
|
+
bridge.failCount = 3;
|
|
114
|
+
bridge.lastFailTime = Date.now();
|
|
115
|
+
await expect(bridge.connect()).rejects.toThrow('circuit breaker open');
|
|
116
|
+
});
|
|
117
|
+
it('resets circuit breaker and retries after cooldown', async () => {
|
|
118
|
+
const bridge = MnemoBridge.getInstance();
|
|
119
|
+
bridge.failCount = 3;
|
|
120
|
+
bridge.lastFailTime = Date.now() - 300_001;
|
|
121
|
+
setupBridgeWithMockConnect(bridge);
|
|
122
|
+
await bridge.connect();
|
|
123
|
+
expect(bridge.failCount).toBe(0);
|
|
124
|
+
});
|
|
125
|
+
it('increments failCount on failure', async () => {
|
|
126
|
+
const bridge = MnemoBridge.getInstance();
|
|
127
|
+
setupBridgeWithMockConnect(bridge, {
|
|
128
|
+
connectError: new Error('connection refused')
|
|
129
|
+
});
|
|
130
|
+
try {
|
|
131
|
+
await bridge.connect();
|
|
132
|
+
}
|
|
133
|
+
catch { }
|
|
134
|
+
expect(bridge.failCount).toBe(1);
|
|
135
|
+
});
|
|
136
|
+
it('records lastFailTime on failure', async () => {
|
|
137
|
+
const bridge = MnemoBridge.getInstance();
|
|
138
|
+
setupBridgeWithMockConnect(bridge, {
|
|
139
|
+
connectError: new Error('fail')
|
|
140
|
+
});
|
|
141
|
+
const before = Date.now();
|
|
142
|
+
try {
|
|
143
|
+
await bridge.connect();
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
expect(bridge.lastFailTime).toBeGreaterThanOrEqual(before);
|
|
147
|
+
});
|
|
148
|
+
it('resets failCount on success after previous failures', async () => {
|
|
149
|
+
const bridge = MnemoBridge.getInstance();
|
|
150
|
+
bridge.failCount = 2;
|
|
151
|
+
setupBridgeWithMockConnect(bridge);
|
|
152
|
+
await bridge.connect();
|
|
153
|
+
expect(bridge.failCount).toBe(0);
|
|
154
|
+
});
|
|
155
|
+
it('coalesces concurrent connect calls', async () => {
|
|
156
|
+
const bridge = MnemoBridge.getInstance();
|
|
157
|
+
// Make doConnect slow to test concurrency
|
|
158
|
+
const mockClient = { connect: vi.fn(), listTools: vi.fn(), callTool: vi.fn() };
|
|
159
|
+
vi.spyOn(bridge, 'doConnect').mockImplementation(async () => {
|
|
160
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
161
|
+
bridge.client = mockClient;
|
|
162
|
+
bridge.availableTools = new Set(['memory']);
|
|
163
|
+
bridge.transport = { close: vi.fn() };
|
|
164
|
+
return mockClient;
|
|
165
|
+
});
|
|
166
|
+
const [c1, c2] = await Promise.all([bridge.connect(), bridge.connect()]);
|
|
167
|
+
expect(c1).toBe(c2);
|
|
168
|
+
expect(bridge.doConnect).toHaveBeenCalledTimes(1);
|
|
169
|
+
});
|
|
170
|
+
it('clears connecting promise after resolution', async () => {
|
|
171
|
+
const bridge = MnemoBridge.getInstance();
|
|
172
|
+
setupBridgeWithMockConnect(bridge);
|
|
173
|
+
await bridge.connect();
|
|
174
|
+
expect(bridge.connecting).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
it('clears connecting promise after rejection', async () => {
|
|
177
|
+
const bridge = MnemoBridge.getInstance();
|
|
178
|
+
setupBridgeWithMockConnect(bridge, {
|
|
179
|
+
connectError: new Error('fail')
|
|
180
|
+
});
|
|
181
|
+
try {
|
|
182
|
+
await bridge.connect();
|
|
183
|
+
}
|
|
184
|
+
catch { }
|
|
185
|
+
expect(bridge.connecting).toBeNull();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('callTool', () => {
|
|
189
|
+
it('parses JSON response', async () => {
|
|
190
|
+
const bridge = MnemoBridge.getInstance();
|
|
191
|
+
setupBridgeWithMockConnect(bridge);
|
|
192
|
+
const result = await bridge.callTool('memory', { action: 'stats' });
|
|
193
|
+
expect(result).toEqual({ status: 'ok' });
|
|
194
|
+
});
|
|
195
|
+
it('auto-connects if not connected', async () => {
|
|
196
|
+
const bridge = MnemoBridge.getInstance();
|
|
197
|
+
setupBridgeWithMockConnect(bridge);
|
|
198
|
+
expect(bridge.client).toBeNull();
|
|
199
|
+
await bridge.callTool('memory', { action: 'stats' });
|
|
200
|
+
expect(bridge.client).not.toBeNull();
|
|
201
|
+
});
|
|
202
|
+
it('throws on unknown tool name', async () => {
|
|
203
|
+
const bridge = MnemoBridge.getInstance();
|
|
204
|
+
setupBridgeWithMockConnect(bridge);
|
|
205
|
+
await bridge.connect();
|
|
206
|
+
await expect(bridge.callTool('nonexistent', {})).rejects.toThrow('Tool "nonexistent" not found');
|
|
207
|
+
});
|
|
208
|
+
it('throws on error response from server', async () => {
|
|
209
|
+
const bridge = MnemoBridge.getInstance();
|
|
210
|
+
const { mockClient } = setupBridgeWithMockConnect(bridge);
|
|
211
|
+
mockClient.callTool.mockResolvedValue({
|
|
212
|
+
isError: true,
|
|
213
|
+
content: [{ type: 'text', text: 'Something went wrong' }]
|
|
214
|
+
});
|
|
215
|
+
await expect(bridge.callTool('memory', {})).rejects.toThrow('mnemo-mcp error');
|
|
216
|
+
});
|
|
217
|
+
it('throws on empty content response', async () => {
|
|
218
|
+
const bridge = MnemoBridge.getInstance();
|
|
219
|
+
const { mockClient } = setupBridgeWithMockConnect(bridge);
|
|
220
|
+
mockClient.callTool.mockResolvedValue({
|
|
221
|
+
isError: false,
|
|
222
|
+
content: [{ type: 'image', data: 'abc' }]
|
|
223
|
+
});
|
|
224
|
+
await expect(bridge.callTool('memory', {})).rejects.toThrow('empty content');
|
|
225
|
+
});
|
|
226
|
+
it('returns raw text when response is not valid JSON', async () => {
|
|
227
|
+
const bridge = MnemoBridge.getInstance();
|
|
228
|
+
const { mockClient } = setupBridgeWithMockConnect(bridge);
|
|
229
|
+
mockClient.callTool.mockResolvedValue({
|
|
230
|
+
isError: false,
|
|
231
|
+
content: [{ type: 'text', text: 'plain text response' }]
|
|
232
|
+
});
|
|
233
|
+
const result = await bridge.callTool('memory', {});
|
|
234
|
+
expect(result).toBe('plain text response');
|
|
235
|
+
});
|
|
236
|
+
it('includes available tools in error message for unknown tool', async () => {
|
|
237
|
+
const bridge = MnemoBridge.getInstance();
|
|
238
|
+
setupBridgeWithMockConnect(bridge);
|
|
239
|
+
await bridge.connect();
|
|
240
|
+
try {
|
|
241
|
+
await bridge.callTool('unknown', {});
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
expect(e.message).toContain('memory');
|
|
245
|
+
expect(e.message).toContain('config');
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
describe('shutdown', () => {
|
|
250
|
+
it('closes transport and clears all state', async () => {
|
|
251
|
+
const bridge = MnemoBridge.getInstance();
|
|
252
|
+
const { mockTransport } = setupBridgeWithMockConnect(bridge);
|
|
253
|
+
await bridge.connect();
|
|
254
|
+
await bridge.shutdown();
|
|
255
|
+
expect(mockTransport.close).toHaveBeenCalled();
|
|
256
|
+
expect(bridge.client).toBeNull();
|
|
257
|
+
expect(bridge.transport).toBeNull();
|
|
258
|
+
expect(bridge.availableTools).toBeNull();
|
|
259
|
+
});
|
|
260
|
+
it('is safe to call when not connected', async () => {
|
|
261
|
+
const bridge = MnemoBridge.getInstance();
|
|
262
|
+
await expect(bridge.shutdown()).resolves.toBeUndefined();
|
|
263
|
+
});
|
|
264
|
+
it('does not close transport if none exists', async () => {
|
|
265
|
+
const bridge = MnemoBridge.getInstance();
|
|
266
|
+
await bridge.shutdown();
|
|
267
|
+
// No error thrown
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
//# sourceMappingURL=bridge.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.unit.test.js","sourceRoot":"","sources":["../../tests/bridge.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.test.d.ts","sourceRoot":"","sources":["../../tests/compaction.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for compactionHook — memory preservation during context compaction.
|
|
3
|
+
*/
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
const mockIsAvailable = vi.fn();
|
|
6
|
+
const mockCallTool = vi.fn();
|
|
7
|
+
vi.mock('../src/bridge.js', () => ({
|
|
8
|
+
MnemoBridge: {
|
|
9
|
+
getInstance: () => ({
|
|
10
|
+
isAvailable: mockIsAvailable,
|
|
11
|
+
callTool: mockCallTool
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
}));
|
|
15
|
+
import { compactionHook } from '../src/hooks/compaction.js';
|
|
16
|
+
describe('compactionHook', () => {
|
|
17
|
+
let output;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
output = { context: [] };
|
|
20
|
+
mockIsAvailable.mockReturnValue(true);
|
|
21
|
+
mockCallTool.mockReset();
|
|
22
|
+
});
|
|
23
|
+
it('skips memory fetch when bridge is unavailable', async () => {
|
|
24
|
+
mockIsAvailable.mockReturnValue(false);
|
|
25
|
+
await compactionHook({ sessionID: 'test-session' }, output);
|
|
26
|
+
expect(mockCallTool).not.toHaveBeenCalled();
|
|
27
|
+
expect(output.context.length).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
it('always adds preservation instruction when memories exist', async () => {
|
|
30
|
+
mockCallTool.mockImplementation((_tool, args) => {
|
|
31
|
+
if (args.action === 'stats')
|
|
32
|
+
return { total_memories: 3 };
|
|
33
|
+
if (args.action === 'list') {
|
|
34
|
+
return {
|
|
35
|
+
results: [
|
|
36
|
+
{ category: 'project', content: 'Use TypeScript' },
|
|
37
|
+
{ category: 'preference', content: 'Prefer Vitest' }
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {};
|
|
42
|
+
});
|
|
43
|
+
await compactionHook({ sessionID: 'test-session' }, output);
|
|
44
|
+
// Should have memory context + preservation instruction
|
|
45
|
+
expect(output.context.length).toBe(2);
|
|
46
|
+
expect(output.context[1]).toContain('preserve');
|
|
47
|
+
});
|
|
48
|
+
it('always adds preservation instruction even with zero memories', async () => {
|
|
49
|
+
mockCallTool.mockResolvedValue({ total_memories: 0 });
|
|
50
|
+
await compactionHook({ sessionID: 'test-session' }, output);
|
|
51
|
+
expect(output.context.length).toBe(1);
|
|
52
|
+
expect(output.context[0]).toContain('preserve');
|
|
53
|
+
});
|
|
54
|
+
it('injects formatted memory list into context', async () => {
|
|
55
|
+
mockCallTool.mockImplementation((_tool, args) => {
|
|
56
|
+
if (args.action === 'stats')
|
|
57
|
+
return { total_memories: 2 };
|
|
58
|
+
if (args.action === 'list') {
|
|
59
|
+
return {
|
|
60
|
+
results: [
|
|
61
|
+
{ category: 'tech', content: 'Python is a language' },
|
|
62
|
+
{ category: 'work', content: 'Meeting at 3pm' }
|
|
63
|
+
]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return {};
|
|
67
|
+
});
|
|
68
|
+
await compactionHook({ sessionID: 'test' }, output);
|
|
69
|
+
expect(output.context[0]).toContain('[Mnemo Persistent Memory');
|
|
70
|
+
expect(output.context[0]).toContain('- [tech] Python is a language');
|
|
71
|
+
expect(output.context[0]).toContain('- [work] Meeting at 3pm');
|
|
72
|
+
});
|
|
73
|
+
it('calls stats then list with limit 10', async () => {
|
|
74
|
+
mockCallTool.mockImplementation((_tool, args) => {
|
|
75
|
+
if (args.action === 'stats')
|
|
76
|
+
return { total_memories: 5 };
|
|
77
|
+
if (args.action === 'list')
|
|
78
|
+
return { results: [] };
|
|
79
|
+
return {};
|
|
80
|
+
});
|
|
81
|
+
await compactionHook({ sessionID: 'test' }, output);
|
|
82
|
+
expect(mockCallTool).toHaveBeenCalledWith('memory', { action: 'stats' });
|
|
83
|
+
expect(mockCallTool).toHaveBeenCalledWith('memory', { action: 'list', limit: 10 });
|
|
84
|
+
});
|
|
85
|
+
it('handles empty results from list', async () => {
|
|
86
|
+
mockCallTool.mockImplementation((_tool, args) => {
|
|
87
|
+
if (args.action === 'stats')
|
|
88
|
+
return { total_memories: 1 };
|
|
89
|
+
if (args.action === 'list')
|
|
90
|
+
return { results: [] };
|
|
91
|
+
return {};
|
|
92
|
+
});
|
|
93
|
+
await compactionHook({ sessionID: 'test' }, output);
|
|
94
|
+
// Only preservation instruction (no memory context since results empty)
|
|
95
|
+
expect(output.context.length).toBe(1);
|
|
96
|
+
expect(output.context[0]).toContain('preserve');
|
|
97
|
+
});
|
|
98
|
+
it('handles null results from list', async () => {
|
|
99
|
+
mockCallTool.mockImplementation((_tool, args) => {
|
|
100
|
+
if (args.action === 'stats')
|
|
101
|
+
return { total_memories: 1 };
|
|
102
|
+
if (args.action === 'list')
|
|
103
|
+
return { results: null };
|
|
104
|
+
return {};
|
|
105
|
+
});
|
|
106
|
+
await compactionHook({ sessionID: 'test' }, output);
|
|
107
|
+
expect(output.context.length).toBe(1);
|
|
108
|
+
});
|
|
109
|
+
it('catches errors without throwing', async () => {
|
|
110
|
+
mockCallTool.mockRejectedValue(new Error('bridge error'));
|
|
111
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
112
|
+
await compactionHook({ sessionID: 'test' }, output);
|
|
113
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('bridge error'));
|
|
114
|
+
consoleSpy.mockRestore();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=compaction.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.test.js","sourceRoot":"","sources":["../../tests/compaction.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-prompt.test.d.ts","sourceRoot":"","sources":["../../tests/system-prompt.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for systemPromptHook — self-awareness injection, budget, memory search.
|
|
3
|
+
*/
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
const mockIsAvailable = vi.fn();
|
|
6
|
+
const mockCallTool = vi.fn();
|
|
7
|
+
vi.mock('../src/bridge.js', () => ({
|
|
8
|
+
MnemoBridge: {
|
|
9
|
+
getInstance: () => ({
|
|
10
|
+
isAvailable: mockIsAvailable,
|
|
11
|
+
callTool: mockCallTool
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
}));
|
|
15
|
+
import { systemPromptHook } from '../src/hooks/system-prompt.js';
|
|
16
|
+
describe('systemPromptHook', () => {
|
|
17
|
+
let output;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
output = { system: [] };
|
|
20
|
+
mockIsAvailable.mockReturnValue(true);
|
|
21
|
+
mockCallTool.mockResolvedValue({ count: 0, results: [] });
|
|
22
|
+
vi.restoreAllMocks();
|
|
23
|
+
// Re-apply defaults after restoreAllMocks
|
|
24
|
+
mockIsAvailable.mockReturnValue(true);
|
|
25
|
+
mockCallTool.mockResolvedValue({ count: 0, results: [] });
|
|
26
|
+
});
|
|
27
|
+
it('always injects self-awareness block', async () => {
|
|
28
|
+
mockIsAvailable.mockReturnValue(false);
|
|
29
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, '/home/user/projects/my-app');
|
|
30
|
+
expect(output.system.length).toBe(1);
|
|
31
|
+
expect(output.system[0]).toContain('mnemo_search');
|
|
32
|
+
expect(output.system[0]).toContain('mnemo_remember');
|
|
33
|
+
expect(output.system[0]).toContain('mnemo_forget');
|
|
34
|
+
});
|
|
35
|
+
it('skips memory injection when bridge is unavailable', async () => {
|
|
36
|
+
mockIsAvailable.mockReturnValue(false);
|
|
37
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, '/home/user/my-app');
|
|
38
|
+
// Only SELF_AWARENESS, no memory injection
|
|
39
|
+
expect(output.system.length).toBe(1);
|
|
40
|
+
expect(mockCallTool).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
it('searches memories with project name from directory path', async () => {
|
|
43
|
+
mockCallTool.mockResolvedValue({ count: 0, results: [] });
|
|
44
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, '/home/user/projects/my-cool-project');
|
|
45
|
+
expect(mockCallTool).toHaveBeenCalledWith('memory', {
|
|
46
|
+
action: 'search',
|
|
47
|
+
query: 'my-cool-project',
|
|
48
|
+
limit: 10
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
it('extracts project name from Windows-style path', async () => {
|
|
52
|
+
mockCallTool.mockResolvedValue({ count: 0, results: [] });
|
|
53
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, 'C:\\Users\\dev\\projects\\win-app');
|
|
54
|
+
expect(mockCallTool).toHaveBeenCalledWith('memory', expect.objectContaining({ query: 'win-app' }));
|
|
55
|
+
});
|
|
56
|
+
it('injects memories within budget', async () => {
|
|
57
|
+
mockCallTool.mockResolvedValue({
|
|
58
|
+
count: 2,
|
|
59
|
+
results: [
|
|
60
|
+
{ id: '1', category: 'project', content: 'Use TypeScript', tags: [] },
|
|
61
|
+
{ id: '2', category: 'preference', content: 'Prefer Vitest', tags: [] }
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
await systemPromptHook({ model: { limit: { context: 200_000 } } }, output, '/home/user/my-app');
|
|
65
|
+
// SELF_AWARENESS + memory injection
|
|
66
|
+
expect(output.system.length).toBe(2);
|
|
67
|
+
expect(output.system[1]).toContain('Use TypeScript');
|
|
68
|
+
expect(output.system[1]).toContain('Prefer Vitest');
|
|
69
|
+
expect(output.system[1]).toContain('[Mnemo Context for "my-app"]');
|
|
70
|
+
});
|
|
71
|
+
it('does not inject when search returns zero results', async () => {
|
|
72
|
+
mockCallTool.mockResolvedValue({ count: 0, results: [] });
|
|
73
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, '/home/user/my-app');
|
|
74
|
+
expect(output.system.length).toBe(1);
|
|
75
|
+
});
|
|
76
|
+
it('truncates memory that partially fits budget', async () => {
|
|
77
|
+
// Create a memory with very long content
|
|
78
|
+
const longContent = 'A'.repeat(10_000);
|
|
79
|
+
mockCallTool.mockResolvedValue({
|
|
80
|
+
count: 1,
|
|
81
|
+
results: [{ id: '1', category: 'info', content: longContent, tags: [] }]
|
|
82
|
+
});
|
|
83
|
+
// Use a model with small context so budget is MIN_BUDGET (600)
|
|
84
|
+
await systemPromptHook({ model: { limit: { context: 1000 } } }, output, '/home/user/app');
|
|
85
|
+
expect(output.system.length).toBe(2);
|
|
86
|
+
// The injection should contain truncated content ending with '...'
|
|
87
|
+
expect(output.system[1]).toContain('...');
|
|
88
|
+
expect(output.system[1].length).toBeLessThanOrEqual(700);
|
|
89
|
+
});
|
|
90
|
+
it('uses MIN_BUDGET when model has no context limit', async () => {
|
|
91
|
+
mockCallTool.mockResolvedValue({
|
|
92
|
+
count: 1,
|
|
93
|
+
results: [{ id: '1', category: 'info', content: 'Short memory', tags: [] }]
|
|
94
|
+
});
|
|
95
|
+
await systemPromptHook({ model: {} }, output, '/home/user/app');
|
|
96
|
+
expect(output.system.length).toBe(2);
|
|
97
|
+
expect(output.system[1]).toContain('Short memory');
|
|
98
|
+
});
|
|
99
|
+
it('catches and logs errors without throwing', async () => {
|
|
100
|
+
mockCallTool.mockRejectedValue(new Error('Network error'));
|
|
101
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
102
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, '/home/user/app');
|
|
103
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Network error'));
|
|
104
|
+
consoleSpy.mockRestore();
|
|
105
|
+
});
|
|
106
|
+
it('handles null searchRes gracefully', async () => {
|
|
107
|
+
mockCallTool.mockResolvedValue(null);
|
|
108
|
+
await systemPromptHook({ model: { limit: { context: 100_000 } } }, output, '/home/user/app');
|
|
109
|
+
// Only SELF_AWARENESS
|
|
110
|
+
expect(output.system.length).toBe(1);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=system-prompt.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-prompt.test.js","sourceRoot":"","sources":["../../tests/system-prompt.test.ts"],"names":[],"mappings":"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"}
|