@soleri/core 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/agency/agency-manager.d.ts +27 -1
  2. package/dist/agency/agency-manager.d.ts.map +1 -1
  3. package/dist/agency/agency-manager.js +180 -9
  4. package/dist/agency/agency-manager.js.map +1 -1
  5. package/dist/agency/default-rules.d.ts +7 -0
  6. package/dist/agency/default-rules.d.ts.map +1 -0
  7. package/dist/agency/default-rules.js +79 -0
  8. package/dist/agency/default-rules.js.map +1 -0
  9. package/dist/agency/types.d.ts +48 -0
  10. package/dist/agency/types.d.ts.map +1 -1
  11. package/dist/brain/brain.d.ts +17 -2
  12. package/dist/brain/brain.d.ts.map +1 -1
  13. package/dist/brain/brain.js +118 -8
  14. package/dist/brain/brain.js.map +1 -1
  15. package/dist/brain/knowledge-synthesizer.d.ts +37 -0
  16. package/dist/brain/knowledge-synthesizer.d.ts.map +1 -0
  17. package/dist/brain/knowledge-synthesizer.js +161 -0
  18. package/dist/brain/knowledge-synthesizer.js.map +1 -0
  19. package/dist/brain/learning-radar.d.ts +96 -0
  20. package/dist/brain/learning-radar.d.ts.map +1 -0
  21. package/dist/brain/learning-radar.js +202 -0
  22. package/dist/brain/learning-radar.js.map +1 -0
  23. package/dist/brain/types.d.ts +15 -0
  24. package/dist/brain/types.d.ts.map +1 -1
  25. package/dist/context/context-engine.d.ts.map +1 -1
  26. package/dist/context/context-engine.js +82 -17
  27. package/dist/context/context-engine.js.map +1 -1
  28. package/dist/context/types.d.ts +5 -0
  29. package/dist/context/types.d.ts.map +1 -1
  30. package/dist/control/intent-router.d.ts +12 -1
  31. package/dist/control/intent-router.d.ts.map +1 -1
  32. package/dist/control/intent-router.js +68 -0
  33. package/dist/control/intent-router.js.map +1 -1
  34. package/dist/control/types.d.ts +17 -0
  35. package/dist/control/types.d.ts.map +1 -1
  36. package/dist/curator/classifier.d.ts +18 -0
  37. package/dist/curator/classifier.d.ts.map +1 -0
  38. package/dist/curator/classifier.js +61 -0
  39. package/dist/curator/classifier.js.map +1 -0
  40. package/dist/curator/quality-gate.d.ts +29 -0
  41. package/dist/curator/quality-gate.d.ts.map +1 -0
  42. package/dist/curator/quality-gate.js +88 -0
  43. package/dist/curator/quality-gate.js.map +1 -0
  44. package/dist/engine/bin/soleri-engine.js +1 -0
  45. package/dist/engine/bin/soleri-engine.js.map +1 -1
  46. package/dist/events/event-bus.d.ts +30 -0
  47. package/dist/events/event-bus.d.ts.map +1 -0
  48. package/dist/events/event-bus.js +51 -0
  49. package/dist/events/event-bus.js.map +1 -0
  50. package/dist/flows/chain-runner.d.ts +46 -0
  51. package/dist/flows/chain-runner.d.ts.map +1 -0
  52. package/dist/flows/chain-runner.js +271 -0
  53. package/dist/flows/chain-runner.js.map +1 -0
  54. package/dist/flows/chain-types.d.ts +103 -0
  55. package/dist/flows/chain-types.d.ts.map +1 -0
  56. package/dist/flows/chain-types.js +23 -0
  57. package/dist/flows/chain-types.js.map +1 -0
  58. package/dist/health/doctor-checks.d.ts +15 -0
  59. package/dist/health/doctor-checks.d.ts.map +1 -0
  60. package/dist/health/doctor-checks.js +98 -0
  61. package/dist/health/doctor-checks.js.map +1 -0
  62. package/dist/intake/text-ingester.d.ts +52 -0
  63. package/dist/intake/text-ingester.d.ts.map +1 -0
  64. package/dist/intake/text-ingester.js +181 -0
  65. package/dist/intake/text-ingester.js.map +1 -0
  66. package/dist/llm/llm-client.d.ts.map +1 -1
  67. package/dist/llm/llm-client.js +37 -1
  68. package/dist/llm/llm-client.js.map +1 -1
  69. package/dist/llm/oauth-discovery.d.ts +26 -0
  70. package/dist/llm/oauth-discovery.d.ts.map +1 -0
  71. package/dist/llm/oauth-discovery.js +149 -0
  72. package/dist/llm/oauth-discovery.js.map +1 -0
  73. package/dist/planning/evidence-collector.d.ts +41 -0
  74. package/dist/planning/evidence-collector.d.ts.map +1 -0
  75. package/dist/planning/evidence-collector.js +194 -0
  76. package/dist/planning/evidence-collector.js.map +1 -0
  77. package/dist/planning/planner.d.ts +4 -0
  78. package/dist/planning/planner.d.ts.map +1 -1
  79. package/dist/planning/planner.js +11 -0
  80. package/dist/planning/planner.js.map +1 -1
  81. package/dist/queue/job-queue.d.ts +92 -0
  82. package/dist/queue/job-queue.d.ts.map +1 -0
  83. package/dist/queue/job-queue.js +180 -0
  84. package/dist/queue/job-queue.js.map +1 -0
  85. package/dist/queue/pipeline-runner.d.ts +62 -0
  86. package/dist/queue/pipeline-runner.d.ts.map +1 -0
  87. package/dist/queue/pipeline-runner.js +126 -0
  88. package/dist/queue/pipeline-runner.js.map +1 -0
  89. package/dist/runtime/admin-setup-ops.d.ts +20 -0
  90. package/dist/runtime/admin-setup-ops.d.ts.map +1 -0
  91. package/dist/runtime/admin-setup-ops.js +583 -0
  92. package/dist/runtime/admin-setup-ops.js.map +1 -0
  93. package/dist/runtime/chain-ops.d.ts +9 -0
  94. package/dist/runtime/chain-ops.d.ts.map +1 -0
  95. package/dist/runtime/chain-ops.js +107 -0
  96. package/dist/runtime/chain-ops.js.map +1 -0
  97. package/dist/runtime/claude-md-helpers.d.ts +65 -0
  98. package/dist/runtime/claude-md-helpers.d.ts.map +1 -0
  99. package/dist/runtime/claude-md-helpers.js +173 -0
  100. package/dist/runtime/claude-md-helpers.js.map +1 -0
  101. package/dist/runtime/curator-extra-ops.d.ts +3 -2
  102. package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
  103. package/dist/runtime/curator-extra-ops.js +81 -3
  104. package/dist/runtime/curator-extra-ops.js.map +1 -1
  105. package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
  106. package/dist/runtime/facades/admin-facade.js +4 -0
  107. package/dist/runtime/facades/admin-facade.js.map +1 -1
  108. package/dist/runtime/facades/agency-facade.d.ts.map +1 -1
  109. package/dist/runtime/facades/agency-facade.js +64 -0
  110. package/dist/runtime/facades/agency-facade.js.map +1 -1
  111. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  112. package/dist/runtime/facades/brain-facade.js +122 -1
  113. package/dist/runtime/facades/brain-facade.js.map +1 -1
  114. package/dist/runtime/facades/control-facade.d.ts.map +1 -1
  115. package/dist/runtime/facades/control-facade.js +42 -0
  116. package/dist/runtime/facades/control-facade.js.map +1 -1
  117. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  118. package/dist/runtime/facades/memory-facade.js +20 -2
  119. package/dist/runtime/facades/memory-facade.js.map +1 -1
  120. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  121. package/dist/runtime/facades/plan-facade.js +2 -0
  122. package/dist/runtime/facades/plan-facade.js.map +1 -1
  123. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  124. package/dist/runtime/facades/vault-facade.js +25 -5
  125. package/dist/runtime/facades/vault-facade.js.map +1 -1
  126. package/dist/runtime/intake-ops.d.ts +7 -5
  127. package/dist/runtime/intake-ops.d.ts.map +1 -1
  128. package/dist/runtime/intake-ops.js +98 -5
  129. package/dist/runtime/intake-ops.js.map +1 -1
  130. package/dist/runtime/memory-extra-ops.d.ts +6 -3
  131. package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
  132. package/dist/runtime/memory-extra-ops.js +292 -4
  133. package/dist/runtime/memory-extra-ops.js.map +1 -1
  134. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  135. package/dist/runtime/planning-extra-ops.js +85 -0
  136. package/dist/runtime/planning-extra-ops.js.map +1 -1
  137. package/dist/runtime/playbook-ops.js +1 -1
  138. package/dist/runtime/playbook-ops.js.map +1 -1
  139. package/dist/runtime/runtime.d.ts.map +1 -1
  140. package/dist/runtime/runtime.js +143 -2
  141. package/dist/runtime/runtime.js.map +1 -1
  142. package/dist/runtime/session-briefing.d.ts +23 -0
  143. package/dist/runtime/session-briefing.d.ts.map +1 -0
  144. package/dist/runtime/session-briefing.js +140 -0
  145. package/dist/runtime/session-briefing.js.map +1 -0
  146. package/dist/runtime/types.d.ts +23 -0
  147. package/dist/runtime/types.d.ts.map +1 -1
  148. package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
  149. package/dist/runtime/vault-linking-ops.js +1 -3
  150. package/dist/runtime/vault-linking-ops.js.map +1 -1
  151. package/dist/vault/vault.d.ts +25 -0
  152. package/dist/vault/vault.d.ts.map +1 -1
  153. package/dist/vault/vault.js +67 -3
  154. package/dist/vault/vault.js.map +1 -1
  155. package/package.json +1 -1
  156. package/src/__tests__/admin-setup-ops.test.ts +355 -0
  157. package/src/__tests__/async-infrastructure.test.ts +307 -0
  158. package/src/__tests__/cognee-client-gaps.test.ts +6 -2
  159. package/src/__tests__/cognee-hybrid-search.test.ts +49 -35
  160. package/src/__tests__/cognee-sync-manager-deep.test.ts +89 -65
  161. package/src/__tests__/curator-extra-ops.test.ts +6 -2
  162. package/src/__tests__/curator-pipeline-e2e.test.ts +358 -0
  163. package/src/__tests__/memory-extra-ops.test.ts +2 -2
  164. package/src/__tests__/planning-extra-ops.test.ts +2 -2
  165. package/src/__tests__/second-brain-features.test.ts +583 -0
  166. package/src/agency/agency-manager.ts +217 -9
  167. package/src/agency/default-rules.ts +83 -0
  168. package/src/agency/types.ts +61 -0
  169. package/src/brain/brain.ts +110 -8
  170. package/src/brain/knowledge-synthesizer.ts +218 -0
  171. package/src/brain/learning-radar.ts +340 -0
  172. package/src/brain/types.ts +16 -0
  173. package/src/context/context-engine.ts +114 -15
  174. package/src/context/types.ts +5 -0
  175. package/src/control/intent-router.ts +107 -0
  176. package/src/control/types.ts +10 -0
  177. package/src/curator/classifier.ts +88 -0
  178. package/src/curator/quality-gate.ts +129 -0
  179. package/src/engine/bin/soleri-engine.ts +1 -0
  180. package/src/events/event-bus.ts +58 -0
  181. package/src/flows/chain-runner.ts +369 -0
  182. package/src/flows/chain-types.ts +57 -0
  183. package/src/health/doctor-checks.ts +115 -0
  184. package/src/intake/text-ingester.ts +234 -0
  185. package/src/llm/llm-client.ts +38 -1
  186. package/src/llm/oauth-discovery.ts +169 -0
  187. package/src/planning/evidence-collector.ts +247 -0
  188. package/src/planning/planner.ts +11 -0
  189. package/src/queue/job-queue.ts +281 -0
  190. package/src/queue/pipeline-runner.ts +149 -0
  191. package/src/runtime/admin-setup-ops.ts +664 -0
  192. package/src/runtime/chain-ops.ts +121 -0
  193. package/src/runtime/claude-md-helpers.ts +236 -0
  194. package/src/runtime/curator-extra-ops.ts +86 -3
  195. package/src/runtime/facades/admin-facade.ts +4 -0
  196. package/src/runtime/facades/agency-facade.ts +68 -0
  197. package/src/runtime/facades/brain-facade.ts +142 -1
  198. package/src/runtime/facades/control-facade.ts +45 -0
  199. package/src/runtime/facades/memory-facade.ts +20 -2
  200. package/src/runtime/facades/plan-facade.ts +2 -0
  201. package/src/runtime/facades/vault-facade.ts +28 -5
  202. package/src/runtime/intake-ops.ts +107 -5
  203. package/src/runtime/memory-extra-ops.ts +312 -4
  204. package/src/runtime/planning-extra-ops.ts +94 -0
  205. package/src/runtime/playbook-ops.ts +1 -1
  206. package/src/runtime/runtime.ts +138 -2
  207. package/src/runtime/session-briefing.ts +161 -0
  208. package/src/runtime/types.ts +23 -0
  209. package/src/runtime/vault-linking-ops.ts +1 -3
  210. package/src/vault/vault.ts +79 -4
@@ -15,9 +15,6 @@
15
15
  */
16
16
 
17
17
  import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
18
- import { mkdirSync, rmSync } from 'node:fs';
19
- import { join } from 'node:path';
20
- import { tmpdir } from 'node:os';
21
18
  import { Brain } from '../brain/brain.js';
22
19
  import { Vault } from '../vault/vault.js';
23
20
  import { CogneeClient } from '../cognee/client.js';
@@ -46,16 +43,22 @@ function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntr
46
43
  * Create a mock CogneeClient that returns controlled search results.
47
44
  * isAvailable is a getter — can be toggled at runtime.
48
45
  */
49
- function makeMockCognee(opts: {
50
- available?: boolean;
51
- searchResults?: CogneeSearchResult[];
52
- } = {}): CogneeClient & { _setAvailable: (v: boolean) => void } {
46
+ function makeMockCognee(
47
+ opts: {
48
+ available?: boolean;
49
+ searchResults?: CogneeSearchResult[];
50
+ } = {},
51
+ ): CogneeClient & { _setAvailable: (v: boolean) => void } {
53
52
  let available = opts.available ?? true;
54
53
  const searchResults = opts.searchResults ?? [];
55
54
 
56
55
  return {
57
- get isAvailable() { return available; },
58
- _setAvailable(v: boolean) { available = v; },
56
+ get isAvailable() {
57
+ return available;
58
+ },
59
+ _setAvailable(v: boolean) {
60
+ available = v;
61
+ },
59
62
  healthCheck: vi.fn().mockResolvedValue({ available, url: 'mock', latencyMs: 1 }),
60
63
  addEntries: vi.fn().mockResolvedValue({ added: 0 }),
61
64
  deleteEntries: vi.fn().mockResolvedValue({ deleted: 0 }),
@@ -74,7 +77,8 @@ const SEED_ENTRIES: IntelligenceEntry[] = [
74
77
  makeEntry({
75
78
  id: 'pattern-retry-backoff',
76
79
  title: 'Retry with Exponential Backoff',
77
- description: 'Always use exponential backoff when retrying failed network requests to avoid thundering herd.',
80
+ description:
81
+ 'Always use exponential backoff when retrying failed network requests to avoid thundering herd.',
78
82
  domain: 'architecture',
79
83
  severity: 'critical',
80
84
  tags: ['networking', 'retry', 'resilience'],
@@ -83,7 +87,8 @@ const SEED_ENTRIES: IntelligenceEntry[] = [
83
87
  makeEntry({
84
88
  id: 'pattern-circuit-breaker',
85
89
  title: 'Circuit Breaker for External Services',
86
- description: 'Wrap external service calls in a circuit breaker to prevent cascade failures when downstream is unhealthy.',
90
+ description:
91
+ 'Wrap external service calls in a circuit breaker to prevent cascade failures when downstream is unhealthy.',
87
92
  domain: 'architecture',
88
93
  severity: 'critical',
89
94
  tags: ['networking', 'resilience', 'microservices'],
@@ -93,7 +98,8 @@ const SEED_ENTRIES: IntelligenceEntry[] = [
93
98
  id: 'anti-pattern-polling-no-timeout',
94
99
  type: 'anti-pattern',
95
100
  title: 'Polling Without Timeout',
96
- description: 'Never poll an external service without a maximum timeout or circuit breaker. Leads to resource exhaustion.',
101
+ description:
102
+ 'Never poll an external service without a maximum timeout or circuit breaker. Leads to resource exhaustion.',
97
103
  domain: 'architecture',
98
104
  severity: 'critical',
99
105
  tags: ['networking', 'polling', 'timeout'],
@@ -101,7 +107,8 @@ const SEED_ENTRIES: IntelligenceEntry[] = [
101
107
  makeEntry({
102
108
  id: 'pattern-token-semantic',
103
109
  title: 'Semantic Token Priority',
104
- description: 'Use semantic tokens over primitive tokens. Semantic tokens communicate intent, not just values.',
110
+ description:
111
+ 'Use semantic tokens over primitive tokens. Semantic tokens communicate intent, not just values.',
105
112
  domain: 'design',
106
113
  severity: 'warning',
107
114
  tags: ['tokens', 'design-system', 'css'],
@@ -109,7 +116,8 @@ const SEED_ENTRIES: IntelligenceEntry[] = [
109
116
  makeEntry({
110
117
  id: 'pattern-fts5-search',
111
118
  title: 'FTS5 Full-Text Search with Porter Stemming',
112
- description: 'Use SQLite FTS5 with porter tokenizer for all text search in the vault. BM25 ranking for relevance.',
119
+ description:
120
+ 'Use SQLite FTS5 with porter tokenizer for all text search in the vault. BM25 ranking for relevance.',
113
121
  domain: 'architecture',
114
122
  severity: 'suggestion',
115
123
  tags: ['search', 'sqlite', 'fts5'],
@@ -129,7 +137,11 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
129
137
 
130
138
  // Create Zettelkasten links between related entries
131
139
  linkManager.addLink('pattern-retry-backoff', 'pattern-circuit-breaker', 'extends');
132
- linkManager.addLink('anti-pattern-polling-no-timeout', 'pattern-circuit-breaker', 'contradicts');
140
+ linkManager.addLink(
141
+ 'anti-pattern-polling-no-timeout',
142
+ 'pattern-circuit-breaker',
143
+ 'contradicts',
144
+ );
133
145
  linkManager.addLink('pattern-retry-backoff', 'anti-pattern-polling-no-timeout', 'contradicts');
134
146
  });
135
147
 
@@ -147,14 +159,14 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
147
159
  const results = await brain.intelligentSearch('retry network requests');
148
160
  expect(results.length).toBeGreaterThan(0);
149
161
  // Vector score should be 0 for all results (no Cognee)
150
- expect(results.every(r => r.breakdown.vector === 0)).toBe(true);
162
+ expect(results.every((r) => r.breakdown.vector === 0)).toBe(true);
151
163
  });
152
164
 
153
165
  it('should match by keyword in FTS5', async () => {
154
166
  const brain = new Brain(vault); // No Cognee at all
155
167
 
156
168
  const results = await brain.intelligentSearch('exponential backoff');
157
- const ids = results.map(r => r.entry.id);
169
+ const ids = results.map((r) => r.entry.id);
158
170
  expect(ids).toContain('pattern-retry-backoff');
159
171
  });
160
172
 
@@ -163,8 +175,8 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
163
175
 
164
176
  const results = await brain.intelligentSearch('networking resilience');
165
177
  // Critical entries should rank above suggestion entries for same domain
166
- const criticalIdx = results.findIndex(r => r.entry.id === 'pattern-retry-backoff');
167
- const suggestionIdx = results.findIndex(r => r.entry.id === 'pattern-fts5-search');
178
+ const criticalIdx = results.findIndex((r) => r.entry.id === 'pattern-retry-backoff');
179
+ const suggestionIdx = results.findIndex((r) => r.entry.id === 'pattern-fts5-search');
168
180
  if (criticalIdx >= 0 && suggestionIdx >= 0) {
169
181
  expect(criticalIdx).toBeLessThan(suggestionIdx);
170
182
  }
@@ -192,7 +204,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
192
204
  const results = await brain.intelligentSearch('how to handle failing external services');
193
205
 
194
206
  // Circuit breaker should rank high due to Cognee vector boost
195
- const cbResult = results.find(r => r.entry.id === 'pattern-circuit-breaker');
207
+ const cbResult = results.find((r) => r.entry.id === 'pattern-circuit-breaker');
196
208
  expect(cbResult).toBeDefined();
197
209
  expect(cbResult!.breakdown.vector).toBeGreaterThan(0);
198
210
  });
@@ -212,7 +224,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
212
224
  const brain = new Brain(vault, mockCognee);
213
225
 
214
226
  const results = await brain.intelligentSearch('retry strategy');
215
- const retryResult = results.find(r => r.entry.id === 'pattern-retry-backoff');
227
+ const retryResult = results.find((r) => r.entry.id === 'pattern-retry-backoff');
216
228
  expect(retryResult).toBeDefined();
217
229
  // Should have vector score from Cognee cross-reference
218
230
  expect(retryResult!.breakdown.vector).toBeGreaterThan(0);
@@ -234,7 +246,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
234
246
  const brain = new Brain(vault, mockCognee);
235
247
 
236
248
  const results = await brain.intelligentSearch('design tokens priority');
237
- const tokenResult = results.find(r => r.entry.id === 'pattern-token-semantic');
249
+ const tokenResult = results.find((r) => r.entry.id === 'pattern-token-semantic');
238
250
  expect(tokenResult).toBeDefined();
239
251
  expect(tokenResult!.breakdown.vector).toBeGreaterThan(0);
240
252
  });
@@ -254,7 +266,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
254
266
  const brain = new Brain(vault, mockCognee);
255
267
 
256
268
  const results = await brain.intelligentSearch('retry');
257
- const retryResult = results.find(r => r.entry.id === 'pattern-retry-backoff');
269
+ const retryResult = results.find((r) => r.entry.id === 'pattern-retry-backoff');
258
270
  expect(retryResult).toBeDefined();
259
271
  // With COGNEE_WEIGHTS, vector component is 35% of total
260
272
  // So vector score should contribute meaningfully
@@ -271,7 +283,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
271
283
 
272
284
  const results = await brain.intelligentSearch('exponential backoff');
273
285
  // All results should have vector=0 (no Cognee matches)
274
- expect(results.every(r => r.breakdown.vector === 0)).toBe(true);
286
+ expect(results.every((r) => r.breakdown.vector === 0)).toBe(true);
275
287
  });
276
288
  });
277
289
 
@@ -280,14 +292,16 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
280
292
  describe('graceful degradation', () => {
281
293
  it('should fall back to FTS5 when Cognee search throws', async () => {
282
294
  const mockCognee = makeMockCognee({ available: true });
283
- (mockCognee.search as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('Cognee crashed'));
295
+ (mockCognee.search as ReturnType<typeof vi.fn>).mockRejectedValue(
296
+ new Error('Cognee crashed'),
297
+ );
284
298
  const brain = new Brain(vault, mockCognee);
285
299
 
286
300
  const results = await brain.intelligentSearch('retry backoff');
287
301
  // Should still return FTS5 results despite Cognee failure
288
302
  expect(results.length).toBeGreaterThan(0);
289
303
  // All vector scores should be 0 (Cognee failed)
290
- expect(results.every(r => r.breakdown.vector === 0)).toBe(true);
304
+ expect(results.every((r) => r.breakdown.vector === 0)).toBe(true);
291
305
  });
292
306
 
293
307
  it('should handle Cognee becoming unavailable mid-session', async () => {
@@ -306,7 +320,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
306
320
 
307
321
  // First search — Cognee available
308
322
  const results1 = await brain.intelligentSearch('retry');
309
- const hasVector1 = results1.some(r => r.breakdown.vector > 0);
323
+ const hasVector1 = results1.some((r) => r.breakdown.vector > 0);
310
324
  expect(hasVector1).toBe(true);
311
325
 
312
326
  // Cognee goes down
@@ -315,7 +329,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
315
329
  // Second search — should fall back gracefully
316
330
  const results2 = await brain.intelligentSearch('retry');
317
331
  expect(results2.length).toBeGreaterThan(0);
318
- expect(results2.every(r => r.breakdown.vector === 0)).toBe(true);
332
+ expect(results2.every((r) => r.breakdown.vector === 0)).toBe(true);
319
333
  });
320
334
  });
321
335
 
@@ -356,8 +370,8 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
356
370
  const withoutDomain = await brain.intelligentSearch('resilience');
357
371
 
358
372
  // When domain filter matches, domainMatch should be higher
359
- const archResult = withDomain.find(r => r.entry.domain === 'architecture');
360
- const noFilterResult = withoutDomain.find(r => r.entry.id === archResult?.entry.id);
373
+ const archResult = withDomain.find((r) => r.entry.domain === 'architecture');
374
+ const noFilterResult = withoutDomain.find((r) => r.entry.id === archResult?.entry.id);
361
375
 
362
376
  if (archResult && noFilterResult) {
363
377
  expect(archResult.breakdown.domainMatch).toBeGreaterThanOrEqual(
@@ -373,7 +387,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
373
387
  it('should find connected entries via link traversal', () => {
374
388
  // Search finds retry-backoff → traverse links → discover circuit-breaker
375
389
  const links = linkManager.getLinks('pattern-retry-backoff');
376
- const linkedIds = links.map(l =>
390
+ const linkedIds = links.map((l) =>
377
391
  l.sourceId === 'pattern-retry-backoff' ? l.targetId : l.sourceId,
378
392
  );
379
393
  expect(linkedIds).toContain('pattern-circuit-breaker');
@@ -383,14 +397,14 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
383
397
  it('should traverse 2 hops to discover indirect connections', () => {
384
398
  // retry-backoff → circuit-breaker → polling-no-timeout (via contradicts)
385
399
  const traversed = linkManager.traverse('pattern-retry-backoff', 2);
386
- const ids = traversed.map(e => e.id);
400
+ const ids = traversed.map((e) => e.id);
387
401
  expect(ids).toContain('pattern-circuit-breaker');
388
402
  expect(ids).toContain('anti-pattern-polling-no-timeout');
389
403
  });
390
404
 
391
405
  it('should identify contradicting anti-patterns', () => {
392
406
  const links = linkManager.getLinks('anti-pattern-polling-no-timeout');
393
- const contradictions = links.filter(l => l.linkType === 'contradicts');
407
+ const contradictions = links.filter((l) => l.linkType === 'contradicts');
394
408
  expect(contradictions.length).toBeGreaterThan(0);
395
409
  });
396
410
  });
@@ -424,7 +438,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
424
438
  type: 'anti-pattern',
425
439
  });
426
440
  if (results.length > 0) {
427
- expect(results.every(r => r.entry.type === 'anti-pattern')).toBe(true);
441
+ expect(results.every((r) => r.entry.type === 'anti-pattern')).toBe(true);
428
442
  }
429
443
  });
430
444
 
@@ -450,7 +464,7 @@ describe('Hybrid search: Cognee → FTS5 → Zettelkasten', () => {
450
464
 
451
465
  const results = await brain.intelligentSearch('retry');
452
466
  // Should not crash and should use the higher score
453
- const retryResult = results.find(r => r.entry.id === 'pattern-retry-backoff');
467
+ const retryResult = results.find((r) => r.entry.id === 'pattern-retry-backoff');
454
468
  expect(retryResult).toBeDefined();
455
469
  // Should use max score (0.9, not 0.7)
456
470
  expect(retryResult!.breakdown.vector).toBeCloseTo(0.9, 1);
@@ -37,15 +37,19 @@ function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntr
37
37
  /**
38
38
  * Create a mock CogneeClient with controllable availability and behavior.
39
39
  */
40
- function makeMockCognee(overrides: {
41
- available?: boolean;
42
- addResult?: { added: number };
43
- addShouldFail?: boolean;
44
- deleteResult?: { deleted: number };
45
- } = {}): CogneeClient {
40
+ function makeMockCognee(
41
+ overrides: {
42
+ available?: boolean;
43
+ addResult?: { added: number };
44
+ addShouldFail?: boolean;
45
+ deleteResult?: { deleted: number };
46
+ } = {},
47
+ ): CogneeClient {
46
48
  const available = overrides.available ?? true;
47
49
  const client = {
48
- get isAvailable() { return available; },
50
+ get isAvailable() {
51
+ return available;
52
+ },
49
53
  healthCheck: vi.fn().mockResolvedValue({ available, url: 'http://mock:8000', latencyMs: 1 }),
50
54
  addEntries: vi.fn().mockImplementation(async () => {
51
55
  if (overrides.addShouldFail) throw new Error('Cognee ingest failed');
@@ -102,25 +106,19 @@ describe('CogneeSyncManager — deep coverage', () => {
102
106
  it('should change when title changes', () => {
103
107
  const base = makeEntry({ id: 'same-id' });
104
108
  const modified = { ...base, title: 'Different Title' };
105
- expect(CogneeSyncManager.contentHash(base)).not.toBe(
106
- CogneeSyncManager.contentHash(modified),
107
- );
109
+ expect(CogneeSyncManager.contentHash(base)).not.toBe(CogneeSyncManager.contentHash(modified));
108
110
  });
109
111
 
110
112
  it('should change when description changes', () => {
111
113
  const base = makeEntry({ id: 'same-id' });
112
114
  const modified = { ...base, description: 'Updated description' };
113
- expect(CogneeSyncManager.contentHash(base)).not.toBe(
114
- CogneeSyncManager.contentHash(modified),
115
- );
115
+ expect(CogneeSyncManager.contentHash(base)).not.toBe(CogneeSyncManager.contentHash(modified));
116
116
  });
117
117
 
118
118
  it('should change when tags change', () => {
119
119
  const base = makeEntry({ id: 'same-id', tags: ['a'] });
120
120
  const modified = { ...base, tags: ['a', 'b'] };
121
- expect(CogneeSyncManager.contentHash(base)).not.toBe(
122
- CogneeSyncManager.contentHash(modified),
123
- );
121
+ expect(CogneeSyncManager.contentHash(base)).not.toBe(CogneeSyncManager.contentHash(modified));
124
122
  });
125
123
 
126
124
  it('should NOT change when only updatedAt changes (not in hash)', () => {
@@ -147,18 +145,22 @@ describe('CogneeSyncManager — deep coverage', () => {
147
145
  const syncMgr = runtime.syncManager;
148
146
  const entry = makeEntry({ id: 'hash-test' });
149
147
  syncMgr.enqueue('ingest', entry.id, entry);
150
- const row = runtime.vault.getProvider().get<{ content_hash: string }>(
151
- `SELECT content_hash FROM cognee_sync_queue WHERE entry_id = 'hash-test'`,
152
- );
148
+ const row = runtime.vault
149
+ .getProvider()
150
+ .get<{ content_hash: string }>(
151
+ `SELECT content_hash FROM cognee_sync_queue WHERE entry_id = 'hash-test'`,
152
+ );
153
153
  expect(row?.content_hash).toMatch(/^[0-9a-f]{16}$/);
154
154
  });
155
155
 
156
156
  it('should store null hash when entry is not provided (delete op)', () => {
157
157
  const syncMgr = runtime.syncManager;
158
158
  syncMgr.enqueue('delete', 'deleted-entry');
159
- const row = runtime.vault.getProvider().get<{ content_hash: string | null }>(
160
- `SELECT content_hash FROM cognee_sync_queue WHERE entry_id = 'deleted-entry'`,
161
- );
159
+ const row = runtime.vault
160
+ .getProvider()
161
+ .get<{ content_hash: string | null }>(
162
+ `SELECT content_hash FROM cognee_sync_queue WHERE entry_id = 'deleted-entry'`,
163
+ );
162
164
  expect(row?.content_hash).toBeNull();
163
165
  });
164
166
  });
@@ -186,10 +188,12 @@ describe('CogneeSyncManager — deep coverage', () => {
186
188
  expect(mockCognee.addEntries).toHaveBeenCalledTimes(1);
187
189
 
188
190
  // Verify ingested hash was updated on the entries table
189
- const row = runtime.vault.getProvider().get<{ cognee_ingested_hash: string }>(
190
- `SELECT cognee_ingested_hash FROM entries WHERE id = @id`,
191
- { id: entry.id },
192
- );
191
+ const row = runtime.vault
192
+ .getProvider()
193
+ .get<{ cognee_ingested_hash: string }>(
194
+ `SELECT cognee_ingested_hash FROM entries WHERE id = @id`,
195
+ { id: entry.id },
196
+ );
193
197
  expect(row?.cognee_ingested_hash).toMatch(/^[0-9a-f]{16}$/);
194
198
 
195
199
  // Verify queue item is marked completed
@@ -214,10 +218,12 @@ describe('CogneeSyncManager — deep coverage', () => {
214
218
  expect(processed).toBe(1);
215
219
 
216
220
  // Verify ingested hash was cleared
217
- const row = runtime.vault.getProvider().get<{ cognee_ingested_hash: string | null }>(
218
- `SELECT cognee_ingested_hash FROM entries WHERE id = @id`,
219
- { id: entry.id },
220
- );
221
+ const row = runtime.vault
222
+ .getProvider()
223
+ .get<{ cognee_ingested_hash: string | null }>(
224
+ `SELECT cognee_ingested_hash FROM entries WHERE id = @id`,
225
+ { id: entry.id },
226
+ );
221
227
  expect(row?.cognee_ingested_hash).toBeNull();
222
228
  });
223
229
 
@@ -321,9 +327,11 @@ describe('CogneeSyncManager — deep coverage', () => {
321
327
  await syncMgr.drain();
322
328
  await syncMgr.drain();
323
329
 
324
- const row = runtime.vault.getProvider().get<{ error: string; status: string }>(
325
- `SELECT error, status FROM cognee_sync_queue WHERE entry_id = 'error-msg-test' AND dataset = 'test-dataset'`,
326
- );
330
+ const row = runtime.vault
331
+ .getProvider()
332
+ .get<{ error: string; status: string }>(
333
+ `SELECT error, status FROM cognee_sync_queue WHERE entry_id = 'error-msg-test' AND dataset = 'test-dataset'`,
334
+ );
327
335
  expect(row?.status).toBe('failed');
328
336
  expect(row?.error).toContain('Cognee ingest failed');
329
337
  });
@@ -336,13 +344,15 @@ describe('CogneeSyncManager — deep coverage', () => {
336
344
  // Mock: fail only for bad-entry
337
345
  let callCount = 0;
338
346
  const mockCognee = makeMockCognee({ available: true });
339
- (mockCognee.addEntries as ReturnType<typeof vi.fn>).mockImplementation(async (entries: IntelligenceEntry[]) => {
340
- callCount++;
341
- if (entries.some(e => e.id === 'bad-entry')) {
342
- throw new Error('Selective failure');
343
- }
344
- return { added: entries.length };
345
- });
347
+ (mockCognee.addEntries as ReturnType<typeof vi.fn>).mockImplementation(
348
+ async (entries: IntelligenceEntry[]) => {
349
+ callCount++;
350
+ if (entries.some((e) => e.id === 'bad-entry')) {
351
+ throw new Error('Selective failure');
352
+ }
353
+ return { added: entries.length };
354
+ },
355
+ );
346
356
 
347
357
  const syncMgr = new CogneeSyncManager(
348
358
  runtime.vault.getProvider(),
@@ -379,10 +389,11 @@ describe('CogneeSyncManager — deep coverage', () => {
379
389
  runtime.vault.seed([entry]);
380
390
 
381
391
  // Simulate a previously-synced entry by setting a hash
382
- runtime.vault.getProvider().run(
383
- `UPDATE entries SET cognee_ingested_hash = 'old-hash-value-xx' WHERE id = @id`,
384
- { id: entry.id },
385
- );
392
+ runtime.vault
393
+ .getProvider()
394
+ .run(`UPDATE entries SET cognee_ingested_hash = 'old-hash-value-xx' WHERE id = @id`, {
395
+ id: entry.id,
396
+ });
386
397
 
387
398
  // Clear queue
388
399
  runtime.vault.getProvider().run('DELETE FROM cognee_sync_queue');
@@ -399,10 +410,12 @@ describe('CogneeSyncManager — deep coverage', () => {
399
410
 
400
411
  // Set the correct hash
401
412
  const correctHash = CogneeSyncManager.contentHash(entry);
402
- runtime.vault.getProvider().run(
403
- `UPDATE entries SET cognee_ingested_hash = @hash WHERE id = @id`,
404
- { hash: correctHash, id: entry.id },
405
- );
413
+ runtime.vault
414
+ .getProvider()
415
+ .run(`UPDATE entries SET cognee_ingested_hash = @hash WHERE id = @id`, {
416
+ hash: correctHash,
417
+ id: entry.id,
418
+ });
406
419
 
407
420
  // Clear queue
408
421
  runtime.vault.getProvider().run('DELETE FROM cognee_sync_queue');
@@ -423,9 +436,11 @@ describe('CogneeSyncManager — deep coverage', () => {
423
436
  syncMgr.reconcile();
424
437
  syncMgr.reconcile(); // Second reconcile should not create duplicates
425
438
 
426
- const rows = runtime.vault.getProvider().all<{ id: number }>(
427
- `SELECT id FROM cognee_sync_queue WHERE entry_id = 'no-dupes' AND status = 'pending'`,
428
- );
439
+ const rows = runtime.vault
440
+ .getProvider()
441
+ .all<{ id: number }>(
442
+ `SELECT id FROM cognee_sync_queue WHERE entry_id = 'no-dupes' AND status = 'pending'`,
443
+ );
429
444
  expect(rows.length).toBe(1);
430
445
  });
431
446
 
@@ -437,22 +452,25 @@ describe('CogneeSyncManager — deep coverage', () => {
437
452
  // Entry 2: previously synced (stale hash)
438
453
  const entry2 = makeEntry({ id: 'stale-hash-entry' });
439
454
  runtime.vault.seed([entry2]);
440
- runtime.vault.getProvider().run(
441
- `UPDATE entries SET cognee_ingested_hash = 'stale-value-xxxxx' WHERE id = @id`,
442
- { id: entry2.id },
443
- );
455
+ runtime.vault
456
+ .getProvider()
457
+ .run(`UPDATE entries SET cognee_ingested_hash = 'stale-value-xxxxx' WHERE id = @id`, {
458
+ id: entry2.id,
459
+ });
444
460
 
445
461
  // Clear queue
446
462
  runtime.vault.getProvider().run('DELETE FROM cognee_sync_queue');
447
463
 
448
464
  runtime.syncManager.reconcile();
449
465
 
450
- const row1 = runtime.vault.getProvider().get<{ op: string }>(
451
- `SELECT op FROM cognee_sync_queue WHERE entry_id = 'null-hash-entry'`,
452
- );
453
- const row2 = runtime.vault.getProvider().get<{ op: string }>(
454
- `SELECT op FROM cognee_sync_queue WHERE entry_id = 'stale-hash-entry'`,
455
- );
466
+ const row1 = runtime.vault
467
+ .getProvider()
468
+ .get<{ op: string }>(`SELECT op FROM cognee_sync_queue WHERE entry_id = 'null-hash-entry'`);
469
+ const row2 = runtime.vault
470
+ .getProvider()
471
+ .get<{ op: string }>(
472
+ `SELECT op FROM cognee_sync_queue WHERE entry_id = 'stale-hash-entry'`,
473
+ );
456
474
 
457
475
  expect(row1?.op).toBe('ingest');
458
476
  expect(row2?.op).toBe('update');
@@ -469,8 +487,12 @@ describe('CogneeSyncManager — deep coverage', () => {
469
487
  // Start with unavailable Cognee
470
488
  let isAvailable = false;
471
489
  const mockCognee = {
472
- get isAvailable() { return isAvailable; },
473
- healthCheck: vi.fn().mockImplementation(async () => ({ available: isAvailable, url: 'mock', latencyMs: 1 })),
490
+ get isAvailable() {
491
+ return isAvailable;
492
+ },
493
+ healthCheck: vi
494
+ .fn()
495
+ .mockImplementation(async () => ({ available: isAvailable, url: 'mock', latencyMs: 1 })),
474
496
  addEntries: vi.fn().mockResolvedValue({ added: 1 }),
475
497
  deleteEntries: vi.fn().mockResolvedValue({ deleted: 1 }),
476
498
  cognify: vi.fn().mockResolvedValue({ status: 'ok' }),
@@ -502,9 +524,11 @@ describe('CogneeSyncManager — deep coverage', () => {
502
524
  });
503
525
 
504
526
  it('should NOT drain when Cognee stays available (no flip)', async () => {
505
- let isAvailable = true;
527
+ const isAvailable = true;
506
528
  const mockCognee = {
507
- get isAvailable() { return isAvailable; },
529
+ get isAvailable() {
530
+ return isAvailable;
531
+ },
508
532
  healthCheck: vi.fn().mockResolvedValue({ available: true, url: 'mock', latencyMs: 1 }),
509
533
  addEntries: vi.fn().mockResolvedValue({ added: 1 }),
510
534
  deleteEntries: vi.fn(),
@@ -26,9 +26,9 @@ describe('createCuratorExtraOps', () => {
26
26
  ops = createCuratorExtraOps(runtime);
27
27
  }
28
28
 
29
- it('should return 5 ops', () => {
29
+ it('should return 9 ops', () => {
30
30
  setup();
31
- expect(ops).toHaveLength(5);
31
+ expect(ops).toHaveLength(9);
32
32
  const names = ops.map((o) => o.name);
33
33
  expect(names).toEqual([
34
34
  'curator_entry_history',
@@ -36,6 +36,10 @@ describe('createCuratorExtraOps', () => {
36
36
  'curator_queue_stats',
37
37
  'curator_enrich',
38
38
  'curator_hybrid_contradictions',
39
+ 'curator_pipeline_status',
40
+ 'curator_enqueue_pipeline',
41
+ 'curator_schedule_start',
42
+ 'curator_schedule_stop',
39
43
  ]);
40
44
  });
41
45