@cereworker/hippocampus 0.6.0 → 2026.313.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=curator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curator.test.d.ts","sourceRoot":"","sources":["../src/curator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,158 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+ import { mkdtempSync, rmSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { HippocampusStore } from './store.js';
6
+ import { HippocampusCurator } from './curator.js';
7
+ function setup() {
8
+ const dir = mkdtempSync(join(tmpdir(), 'cereworker-curator-test-'));
9
+ const store = new HippocampusStore(dir);
10
+ const generator = { generate: vi.fn() };
11
+ const curator = new HippocampusCurator(store, generator);
12
+ return { dir, store, generator, curator };
13
+ }
14
+ describe('HippocampusCurator', () => {
15
+ let dir;
16
+ afterEach(() => {
17
+ rmSync(dir, { recursive: true, force: true });
18
+ });
19
+ describe('curate', () => {
20
+ it('returns empty when no uncurated content', async () => {
21
+ const ctx = setup();
22
+ dir = ctx.dir;
23
+ const result = await ctx.curator.curate();
24
+ expect(result.pairs).toHaveLength(0);
25
+ expect(result.skipped).toBe(0);
26
+ expect(result.errors).toHaveLength(0);
27
+ });
28
+ it('sends content to generator and parses JSON response', async () => {
29
+ const ctx = setup();
30
+ dir = ctx.dir;
31
+ writeFileSync(join(dir, 'MEMORY.md'), 'User prefers dark mode', 'utf-8');
32
+ const pairs = [{ instruction: 'What theme?', response: 'dark mode', source: 'MEMORY.md' }];
33
+ ctx.generator.generate.mockResolvedValue(JSON.stringify(pairs));
34
+ const result = await ctx.curator.curate();
35
+ expect(result.pairs).toHaveLength(1);
36
+ expect(result.pairs[0].instruction).toBe('What theme?');
37
+ expect(result.pairs[0].response).toBe('dark mode');
38
+ expect(ctx.generator.generate).toHaveBeenCalledOnce();
39
+ });
40
+ it('handles generator failure gracefully', async () => {
41
+ const ctx = setup();
42
+ dir = ctx.dir;
43
+ writeFileSync(join(dir, 'MEMORY.md'), 'some content', 'utf-8');
44
+ ctx.generator.generate.mockRejectedValue(new Error('LLM down'));
45
+ const result = await ctx.curator.curate();
46
+ expect(result.pairs).toHaveLength(0);
47
+ expect(result.errors).toContain('Cerebrum call failed: LLM down');
48
+ });
49
+ it('handles non-array response', async () => {
50
+ const ctx = setup();
51
+ dir = ctx.dir;
52
+ writeFileSync(join(dir, 'MEMORY.md'), 'content', 'utf-8');
53
+ ctx.generator.generate.mockResolvedValue('"not an array"');
54
+ const result = await ctx.curator.curate();
55
+ expect(result.pairs).toHaveLength(0);
56
+ expect(result.errors.some((e) => e.includes('not an array'))).toBe(true);
57
+ });
58
+ it('handles markdown code blocks in response', async () => {
59
+ const ctx = setup();
60
+ dir = ctx.dir;
61
+ writeFileSync(join(dir, 'MEMORY.md'), 'info', 'utf-8');
62
+ const json = JSON.stringify([{ instruction: 'q', response: 'a', source: 'x' }]);
63
+ ctx.generator.generate.mockResolvedValue('```json\n' + json + '\n```');
64
+ const result = await ctx.curator.curate();
65
+ expect(result.pairs).toHaveLength(1);
66
+ });
67
+ it('filters entries missing instruction or response', async () => {
68
+ const ctx = setup();
69
+ dir = ctx.dir;
70
+ writeFileSync(join(dir, 'MEMORY.md'), 'content', 'utf-8');
71
+ const data = [
72
+ { instruction: 'valid', response: 'ok', source: 'x' },
73
+ { instruction: 'no response' },
74
+ { response: 'no instruction' },
75
+ {},
76
+ ];
77
+ ctx.generator.generate.mockResolvedValue(JSON.stringify(data));
78
+ const result = await ctx.curator.curate();
79
+ expect(result.pairs).toHaveLength(1);
80
+ expect(result.pairs[0].instruction).toBe('valid');
81
+ });
82
+ it('saves pairs to pending.jsonl', async () => {
83
+ const ctx = setup();
84
+ dir = ctx.dir;
85
+ writeFileSync(join(dir, 'MEMORY.md'), 'content', 'utf-8');
86
+ const pairs = [{ instruction: 'q', response: 'a', source: 'x' }];
87
+ ctx.generator.generate.mockResolvedValue(JSON.stringify(pairs));
88
+ await ctx.curator.curate();
89
+ const pendingPath = join(ctx.store.finetuneDir, 'pending.jsonl');
90
+ expect(existsSync(pendingPath)).toBe(true);
91
+ const content = readFileSync(pendingPath, 'utf-8');
92
+ expect(content).toContain('"instruction":"q"');
93
+ });
94
+ it('updates curated marker', async () => {
95
+ const ctx = setup();
96
+ dir = ctx.dir;
97
+ writeFileSync(join(dir, 'MEMORY.md'), 'content', 'utf-8');
98
+ ctx.generator.generate.mockResolvedValue('[]');
99
+ await ctx.curator.curate();
100
+ const marker = readFileSync(join(dir, '.curated-marker'), 'utf-8').trim();
101
+ expect(marker).toMatch(/^\d{4}-\d{2}-\d{2}$/);
102
+ });
103
+ });
104
+ describe('getPendingPairs', () => {
105
+ it('returns empty when no pending file', () => {
106
+ const ctx = setup();
107
+ dir = ctx.dir;
108
+ expect(ctx.curator.getPendingPairs()).toEqual([]);
109
+ });
110
+ it('parses JSONL lines', () => {
111
+ const ctx = setup();
112
+ dir = ctx.dir;
113
+ const ftDir = ctx.store.finetuneDir;
114
+ const lines = [
115
+ JSON.stringify({ instruction: 'q1', response: 'a1', source: 's1', createdAt: 1 }),
116
+ JSON.stringify({ instruction: 'q2', response: 'a2', source: 's2', createdAt: 2 }),
117
+ ].join('\n');
118
+ writeFileSync(join(ftDir, 'pending.jsonl'), lines + '\n', 'utf-8');
119
+ const pairs = ctx.curator.getPendingPairs();
120
+ expect(pairs).toHaveLength(2);
121
+ expect(pairs[0].instruction).toBe('q1');
122
+ expect(pairs[1].instruction).toBe('q2');
123
+ });
124
+ it('skips malformed lines', () => {
125
+ const ctx = setup();
126
+ dir = ctx.dir;
127
+ const ftDir = ctx.store.finetuneDir;
128
+ const lines = [
129
+ JSON.stringify({ instruction: 'q1', response: 'a1', source: 's1', createdAt: 1 }),
130
+ 'not valid json {{{',
131
+ ].join('\n');
132
+ writeFileSync(join(ftDir, 'pending.jsonl'), lines + '\n', 'utf-8');
133
+ const pairs = ctx.curator.getPendingPairs();
134
+ expect(pairs).toHaveLength(1);
135
+ });
136
+ });
137
+ describe('markConsumed', () => {
138
+ it('moves pending to consumed dir and clears pending', () => {
139
+ const ctx = setup();
140
+ dir = ctx.dir;
141
+ const ftDir = ctx.store.finetuneDir;
142
+ writeFileSync(join(ftDir, 'pending.jsonl'), '{"a":1}\n', 'utf-8');
143
+ ctx.curator.markConsumed();
144
+ // Pending file should be empty
145
+ expect(readFileSync(join(ftDir, 'pending.jsonl'), 'utf-8')).toBe('');
146
+ // Consumed dir should exist with today's file
147
+ const today = new Date().toISOString().slice(0, 10);
148
+ expect(existsSync(join(ftDir, 'consumed', `${today}.jsonl`))).toBe(true);
149
+ });
150
+ it('is a no-op when no pending file exists', () => {
151
+ const ctx = setup();
152
+ dir = ctx.dir;
153
+ // Should not throw
154
+ expect(() => ctx.curator.markConsumed()).not.toThrow();
155
+ });
156
+ });
157
+ });
158
+ //# sourceMappingURL=curator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curator.test.js","sourceRoot":"","sources":["../src/curator.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAsB,MAAM,cAAc,CAAC;AAEtE,SAAS,KAAK;IACZ,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,0BAA0B,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,SAAS,GAAkB,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACvD,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACzD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAC5C,CAAC;AAED,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,GAAW,CAAC;IAEhB,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAAC;YAEzE,MAAM,KAAK,GAAG,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAC1F,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAE9F,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACxD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;YAC9D,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YAE9F,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACzD,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;YAEzF,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YAC/E,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC;YAErG,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG;gBACX,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;gBACrD,EAAE,WAAW,EAAE,aAAa,EAAE;gBAC9B,EAAE,QAAQ,EAAE,gBAAgB,EAAE;gBAC9B,EAAE;aACH,CAAC;YACD,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAE7F,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAChE,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAE9F,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;YACjE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;YACtC,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACzD,GAAG,CAAC,SAAS,CAAC,QAAqC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAE7E,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1E,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;YAC5B,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC;YACpC,MAAM,KAAK,GAAG;gBACZ,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;gBACjF,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;aAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;YAEnE,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC;YACpC,MAAM,KAAK,GAAG;gBACZ,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;gBACjF,oBAAoB;aACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;YAEnE,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC;YACpC,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YAElE,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAE3B,+BAA+B;YAC/B,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACrE,8CAA8C;YAC9C,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACd,mBAAmB;YACnB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../src/store.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,166 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { mkdtempSync, rmSync, writeFileSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { HippocampusStore } from './store.js';
6
+ describe('HippocampusStore', () => {
7
+ let dir;
8
+ afterEach(() => {
9
+ rmSync(dir, { recursive: true, force: true });
10
+ });
11
+ it('creates directory on construction', () => {
12
+ dir = join(mkdtempSync(join(tmpdir(), 'cereworker-store-test-')), 'sub');
13
+ const store = new HippocampusStore(dir);
14
+ expect(existsSync(dir)).toBe(true);
15
+ });
16
+ it('exposes directory via getter', () => {
17
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
18
+ const store = new HippocampusStore(dir);
19
+ expect(store.directory).toBe(dir);
20
+ });
21
+ describe('readMemory / writeMemory', () => {
22
+ it('returns empty string when no MEMORY.md exists', () => {
23
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
24
+ const store = new HippocampusStore(dir);
25
+ expect(store.readMemory()).toBe('');
26
+ });
27
+ it('round-trips content', () => {
28
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
29
+ const store = new HippocampusStore(dir);
30
+ store.writeMemory('# Hello\nWorld');
31
+ expect(store.readMemory()).toBe('# Hello\nWorld');
32
+ });
33
+ it('overwrites existing content', () => {
34
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
35
+ const store = new HippocampusStore(dir);
36
+ store.writeMemory('old');
37
+ store.writeMemory('new');
38
+ expect(store.readMemory()).toBe('new');
39
+ });
40
+ });
41
+ describe('appendDailyLog / readDailyLogs', () => {
42
+ it('creates a date-stamped log file', () => {
43
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
44
+ const store = new HippocampusStore(dir);
45
+ store.appendDailyLog('test entry');
46
+ const today = new Date().toISOString().slice(0, 10);
47
+ expect(existsSync(join(dir, `${today}.md`))).toBe(true);
48
+ });
49
+ it('readDailyLogs returns recent entries', () => {
50
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
51
+ const store = new HippocampusStore(dir);
52
+ store.appendDailyLog('entry');
53
+ const logs = store.readDailyLogs(7);
54
+ expect(logs.length).toBeGreaterThanOrEqual(1);
55
+ expect(logs[0].content).toContain('entry');
56
+ });
57
+ it('readDailyLogs filters by day range', () => {
58
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
59
+ const store = new HippocampusStore(dir);
60
+ // Create an old log file manually
61
+ writeFileSync(join(dir, '2020-01-01.md'), 'old entry', 'utf-8');
62
+ store.appendDailyLog('recent entry');
63
+ const logs = store.readDailyLogs(7);
64
+ const dates = logs.map((l) => l.date);
65
+ expect(dates).not.toContain('2020-01-01');
66
+ });
67
+ });
68
+ describe('listLogFiles', () => {
69
+ it('returns empty for empty directory', () => {
70
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
71
+ const store = new HippocampusStore(dir);
72
+ expect(store.listLogFiles()).toEqual([]);
73
+ });
74
+ it('returns sorted date strings', () => {
75
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
76
+ const store = new HippocampusStore(dir);
77
+ writeFileSync(join(dir, '2024-03-15.md'), 'a', 'utf-8');
78
+ writeFileSync(join(dir, '2024-03-10.md'), 'b', 'utf-8');
79
+ expect(store.listLogFiles()).toEqual(['2024-03-10', '2024-03-15']);
80
+ });
81
+ it('ignores non-date files', () => {
82
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
83
+ const store = new HippocampusStore(dir);
84
+ writeFileSync(join(dir, 'MEMORY.md'), 'x', 'utf-8');
85
+ writeFileSync(join(dir, '2024-03-15.md'), 'y', 'utf-8');
86
+ expect(store.listLogFiles()).toEqual(['2024-03-15']);
87
+ });
88
+ });
89
+ describe('listAll', () => {
90
+ it('lists all .md files', () => {
91
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
92
+ const store = new HippocampusStore(dir);
93
+ writeFileSync(join(dir, 'MEMORY.md'), '', 'utf-8');
94
+ writeFileSync(join(dir, '2024-01-01.md'), '', 'utf-8');
95
+ writeFileSync(join(dir, 'notes.txt'), '', 'utf-8');
96
+ const files = store.listAll();
97
+ expect(files).toContain('MEMORY.md');
98
+ expect(files).toContain('2024-01-01.md');
99
+ expect(files).not.toContain('notes.txt');
100
+ });
101
+ });
102
+ describe('readFile', () => {
103
+ it('reads a specific file', () => {
104
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
105
+ const store = new HippocampusStore(dir);
106
+ writeFileSync(join(dir, 'test.md'), 'content', 'utf-8');
107
+ expect(store.readFile('test.md')).toBe('content');
108
+ });
109
+ it('returns null for missing file', () => {
110
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
111
+ const store = new HippocampusStore(dir);
112
+ expect(store.readFile('nope.md')).toBeNull();
113
+ });
114
+ it('sanitizes path traversal', () => {
115
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
116
+ const store = new HippocampusStore(dir);
117
+ // Path traversal chars get stripped, so it looks for a local file
118
+ expect(store.readFile('../../../etc/passwd')).toBeNull();
119
+ });
120
+ });
121
+ describe('search', () => {
122
+ it('finds matching content case-insensitively', () => {
123
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
124
+ const store = new HippocampusStore(dir);
125
+ writeFileSync(join(dir, 'notes.md'), 'TypeScript is great', 'utf-8');
126
+ const results = store.search('typescript');
127
+ expect(results).toHaveLength(1);
128
+ expect(results[0].filename).toBe('notes.md');
129
+ });
130
+ it('returns empty for no matches', () => {
131
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
132
+ const store = new HippocampusStore(dir);
133
+ writeFileSync(join(dir, 'notes.md'), 'hello', 'utf-8');
134
+ expect(store.search('zzzzz')).toHaveLength(0);
135
+ });
136
+ });
137
+ describe('pruneOldLogs', () => {
138
+ it('deletes old logs and returns count', () => {
139
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
140
+ const store = new HippocampusStore(dir);
141
+ writeFileSync(join(dir, '2020-01-01.md'), 'old', 'utf-8');
142
+ writeFileSync(join(dir, '2020-01-02.md'), 'old', 'utf-8');
143
+ store.appendDailyLog('recent');
144
+ const pruned = store.pruneOldLogs(7);
145
+ expect(pruned).toBe(2);
146
+ expect(existsSync(join(dir, '2020-01-01.md'))).toBe(false);
147
+ });
148
+ it('keeps recent logs', () => {
149
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
150
+ const store = new HippocampusStore(dir);
151
+ store.appendDailyLog('today');
152
+ const pruned = store.pruneOldLogs(7);
153
+ expect(pruned).toBe(0);
154
+ });
155
+ });
156
+ describe('finetuneDir', () => {
157
+ it('returns finetune path and creates directory', () => {
158
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-store-test-'));
159
+ const store = new HippocampusStore(dir);
160
+ const ftDir = store.finetuneDir;
161
+ expect(ftDir).toBe(join(dir, 'finetune'));
162
+ expect(existsSync(ftDir)).toBe(true);
163
+ });
164
+ });
165
+ });
166
+ //# sourceMappingURL=store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.js","sourceRoot":"","sources":["../src/store.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tools.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.test.d.ts","sourceRoot":"","sources":["../src/tools.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { HippocampusStore } from './store.js';
6
+ import { createMemoryTools } from './tools.js';
7
+ describe('createMemoryTools', () => {
8
+ let dir;
9
+ afterEach(() => {
10
+ rmSync(dir, { recursive: true, force: true });
11
+ });
12
+ function setup() {
13
+ dir = mkdtempSync(join(tmpdir(), 'cereworker-memtools-test-'));
14
+ const store = new HippocampusStore(dir);
15
+ return createMemoryTools(store);
16
+ }
17
+ describe('executeMemoryRead', () => {
18
+ it('reads MEMORY.md by default', async () => {
19
+ const tools = setup();
20
+ writeFileSync(join(dir, 'MEMORY.md'), '# Memory', 'utf-8');
21
+ const result = await tools.executeMemoryRead({ file: 'MEMORY.md' });
22
+ expect(result).toBe('# Memory');
23
+ });
24
+ it('reads a specific file', async () => {
25
+ const tools = setup();
26
+ writeFileSync(join(dir, 'notes.md'), 'my notes', 'utf-8');
27
+ const result = await tools.executeMemoryRead({ file: 'notes.md' });
28
+ expect(result).toBe('my notes');
29
+ });
30
+ it('shows guidance when no files exist', async () => {
31
+ const tools = setup();
32
+ const result = await tools.executeMemoryRead({ file: 'MEMORY.md' });
33
+ expect(result).toContain('No memory files found');
34
+ expect(result).toContain('memory_write');
35
+ });
36
+ it('lists available files when file not found', async () => {
37
+ const tools = setup();
38
+ writeFileSync(join(dir, 'MEMORY.md'), 'x', 'utf-8');
39
+ const result = await tools.executeMemoryRead({ file: 'missing.md' });
40
+ expect(result).toContain('not found');
41
+ expect(result).toContain('MEMORY.md');
42
+ });
43
+ it('returns (empty file) for empty content', async () => {
44
+ const tools = setup();
45
+ writeFileSync(join(dir, 'empty.md'), '', 'utf-8');
46
+ const result = await tools.executeMemoryRead({ file: 'empty.md' });
47
+ expect(result).toBe('(empty file)');
48
+ });
49
+ });
50
+ describe('executeMemoryWrite', () => {
51
+ it('writes content and returns success', async () => {
52
+ const tools = setup();
53
+ const result = await tools.executeMemoryWrite({ content: '# Hello' });
54
+ expect(result).toContain('updated successfully');
55
+ const readBack = await tools.executeMemoryRead({ file: 'MEMORY.md' });
56
+ expect(readBack).toBe('# Hello');
57
+ });
58
+ });
59
+ describe('executeMemoryLog', () => {
60
+ it('appends to daily log and returns date', async () => {
61
+ const tools = setup();
62
+ const result = await tools.executeMemoryLog({ content: 'test log' });
63
+ const today = new Date().toISOString().slice(0, 10);
64
+ expect(result).toContain(today);
65
+ });
66
+ });
67
+ describe('executeMemorySearch', () => {
68
+ it('finds matching lines', async () => {
69
+ const tools = setup();
70
+ writeFileSync(join(dir, 'notes.md'), 'TypeScript rocks\nPython too', 'utf-8');
71
+ const result = await tools.executeMemorySearch({ query: 'TypeScript' });
72
+ expect(result).toContain('notes.md');
73
+ expect(result).toContain('TypeScript rocks');
74
+ });
75
+ it('returns no matches message', async () => {
76
+ const tools = setup();
77
+ writeFileSync(join(dir, 'notes.md'), 'hello', 'utf-8');
78
+ const result = await tools.executeMemorySearch({ query: 'zzzzz' });
79
+ expect(result).toContain('No matches found');
80
+ });
81
+ });
82
+ });
83
+ //# sourceMappingURL=tools.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.test.js","sourceRoot":"","sources":["../src/tools.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,GAAW,CAAC;IAEhB,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,SAAS,KAAK;QACZ,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,2BAA2B,CAAC,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACpE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;YACrC,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACnE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACpE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;YAClD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;YACrE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACnE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;YACtE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;YACjD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACtE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;YACrE,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;YACpC,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,8BAA8B,EAAE,OAAO,CAAC,CAAC;YAC9E,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,mBAAmB,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YACxE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACnE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cereworker/hippocampus",
3
- "version": "0.6.0",
3
+ "version": "2026.313.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,8 +17,8 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "zod": "^3.24.0",
20
- "@cereworker/core": "0.6.0",
21
- "@cereworker/config": "0.6.0"
20
+ "@cereworker/core": "2026.313.0",
21
+ "@cereworker/config": "2026.313.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "typescript": "^5.7.3"