@soleri/core 2.0.2 → 2.4.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.
- package/dist/brain/brain.d.ts +14 -50
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +207 -16
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts +86 -0
- package/dist/brain/intelligence.d.ts.map +1 -0
- package/dist/brain/intelligence.js +771 -0
- package/dist/brain/intelligence.js.map +1 -0
- package/dist/brain/types.d.ts +197 -0
- package/dist/brain/types.d.ts.map +1 -0
- package/dist/brain/types.js +2 -0
- package/dist/brain/types.js.map +1 -0
- package/dist/cognee/client.d.ts +35 -0
- package/dist/cognee/client.d.ts.map +1 -0
- package/dist/cognee/client.js +291 -0
- package/dist/cognee/client.js.map +1 -0
- package/dist/cognee/types.d.ts +46 -0
- package/dist/cognee/types.d.ts.map +1 -0
- package/dist/cognee/types.js +3 -0
- package/dist/cognee/types.js.map +1 -0
- package/dist/control/identity-manager.d.ts +22 -0
- package/dist/control/identity-manager.d.ts.map +1 -0
- package/dist/control/identity-manager.js +233 -0
- package/dist/control/identity-manager.js.map +1 -0
- package/dist/control/intent-router.d.ts +32 -0
- package/dist/control/intent-router.d.ts.map +1 -0
- package/dist/control/intent-router.js +242 -0
- package/dist/control/intent-router.js.map +1 -0
- package/dist/control/types.d.ts +68 -0
- package/dist/control/types.d.ts.map +1 -0
- package/dist/control/types.js +9 -0
- package/dist/control/types.js.map +1 -0
- package/dist/curator/curator.d.ts +29 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +142 -5
- package/dist/curator/curator.js.map +1 -1
- package/dist/facades/types.d.ts +1 -1
- package/dist/governance/governance.d.ts +42 -0
- package/dist/governance/governance.d.ts.map +1 -0
- package/dist/governance/governance.js +488 -0
- package/dist/governance/governance.js.map +1 -0
- package/dist/governance/index.d.ts +3 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/governance/types.d.ts +102 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/governance/types.js +3 -0
- package/dist/governance/types.js.map +1 -0
- package/dist/index.d.ts +35 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/llm-client.d.ts.map +1 -1
- package/dist/llm/llm-client.js +9 -2
- package/dist/llm/llm-client.js.map +1 -1
- package/dist/logging/logger.d.ts +37 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +145 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/types.d.ts +19 -0
- package/dist/logging/types.d.ts.map +1 -0
- package/dist/logging/types.js +2 -0
- package/dist/logging/types.js.map +1 -0
- package/dist/loop/loop-manager.d.ts +49 -0
- package/dist/loop/loop-manager.d.ts.map +1 -0
- package/dist/loop/loop-manager.js +105 -0
- package/dist/loop/loop-manager.js.map +1 -0
- package/dist/loop/types.d.ts +35 -0
- package/dist/loop/types.d.ts.map +1 -0
- package/dist/loop/types.js +8 -0
- package/dist/loop/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +29 -0
- package/dist/planning/gap-analysis.d.ts.map +1 -0
- package/dist/planning/gap-analysis.js +265 -0
- package/dist/planning/gap-analysis.js.map +1 -0
- package/dist/planning/gap-types.d.ts +29 -0
- package/dist/planning/gap-types.d.ts.map +1 -0
- package/dist/planning/gap-types.js +28 -0
- package/dist/planning/gap-types.js.map +1 -0
- package/dist/planning/planner.d.ts +150 -1
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +365 -2
- package/dist/planning/planner.js.map +1 -1
- package/dist/project/project-registry.d.ts +79 -0
- package/dist/project/project-registry.d.ts.map +1 -0
- package/dist/project/project-registry.js +276 -0
- package/dist/project/project-registry.js.map +1 -0
- package/dist/project/types.d.ts +28 -0
- package/dist/project/types.d.ts.map +1 -0
- package/dist/project/types.js +5 -0
- package/dist/project/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +13 -0
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -0
- package/dist/runtime/admin-extra-ops.js +284 -0
- package/dist/runtime/admin-extra-ops.js.map +1 -0
- package/dist/runtime/admin-ops.d.ts +15 -0
- package/dist/runtime/admin-ops.d.ts.map +1 -0
- package/dist/runtime/admin-ops.js +322 -0
- package/dist/runtime/admin-ops.js.map +1 -0
- package/dist/runtime/capture-ops.d.ts +15 -0
- package/dist/runtime/capture-ops.d.ts.map +1 -0
- package/dist/runtime/capture-ops.js +345 -0
- package/dist/runtime/capture-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +7 -3
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +646 -15
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +9 -0
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -0
- package/dist/runtime/curator-extra-ops.js +59 -0
- package/dist/runtime/curator-extra-ops.js.map +1 -0
- package/dist/runtime/domain-ops.d.ts.map +1 -1
- package/dist/runtime/domain-ops.js +59 -13
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts +14 -0
- package/dist/runtime/grading-ops.d.ts.map +1 -0
- package/dist/runtime/grading-ops.js +105 -0
- package/dist/runtime/grading-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +13 -0
- package/dist/runtime/loop-ops.d.ts.map +1 -0
- package/dist/runtime/loop-ops.js +179 -0
- package/dist/runtime/loop-ops.js.map +1 -0
- package/dist/runtime/memory-cross-project-ops.d.ts +12 -0
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -0
- package/dist/runtime/memory-cross-project-ops.js +165 -0
- package/dist/runtime/memory-cross-project-ops.js.map +1 -0
- package/dist/runtime/memory-extra-ops.d.ts +13 -0
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -0
- package/dist/runtime/memory-extra-ops.js +173 -0
- package/dist/runtime/memory-extra-ops.js.map +1 -0
- package/dist/runtime/orchestrate-ops.d.ts +17 -0
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -0
- package/dist/runtime/orchestrate-ops.js +240 -0
- package/dist/runtime/orchestrate-ops.js.map +1 -0
- package/dist/runtime/planning-extra-ops.d.ts +17 -0
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -0
- package/dist/runtime/planning-extra-ops.js +300 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts +15 -0
- package/dist/runtime/project-ops.d.ts.map +1 -0
- package/dist/runtime/project-ops.js +181 -0
- package/dist/runtime/project-ops.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +48 -1
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +23 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +9 -0
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -0
- package/dist/runtime/vault-extra-ops.js +195 -0
- package/dist/runtime/vault-extra-ops.js.map +1 -0
- package/dist/telemetry/telemetry.d.ts +48 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -0
- package/dist/telemetry/telemetry.js +87 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/vault/vault.d.ts +94 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +340 -1
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/admin-extra-ops.test.ts +420 -0
- package/src/__tests__/admin-ops.test.ts +271 -0
- package/src/__tests__/brain-intelligence.test.ts +828 -0
- package/src/__tests__/brain.test.ts +396 -27
- package/src/__tests__/capture-ops.test.ts +509 -0
- package/src/__tests__/cognee-client.test.ts +524 -0
- package/src/__tests__/core-ops.test.ts +341 -49
- package/src/__tests__/curator-extra-ops.test.ts +359 -0
- package/src/__tests__/curator.test.ts +126 -31
- package/src/__tests__/domain-ops.test.ts +111 -9
- package/src/__tests__/governance.test.ts +522 -0
- package/src/__tests__/grading-ops.test.ts +340 -0
- package/src/__tests__/identity-manager.test.ts +243 -0
- package/src/__tests__/intent-router.test.ts +222 -0
- package/src/__tests__/logger.test.ts +200 -0
- package/src/__tests__/loop-ops.test.ts +398 -0
- package/src/__tests__/memory-cross-project-ops.test.ts +246 -0
- package/src/__tests__/memory-extra-ops.test.ts +352 -0
- package/src/__tests__/orchestrate-ops.test.ts +284 -0
- package/src/__tests__/planner.test.ts +331 -0
- package/src/__tests__/planning-extra-ops.test.ts +548 -0
- package/src/__tests__/project-ops.test.ts +367 -0
- package/src/__tests__/runtime.test.ts +13 -11
- package/src/__tests__/vault-extra-ops.test.ts +407 -0
- package/src/brain/brain.ts +308 -72
- package/src/brain/intelligence.ts +1230 -0
- package/src/brain/types.ts +214 -0
- package/src/cognee/client.ts +352 -0
- package/src/cognee/types.ts +62 -0
- package/src/control/identity-manager.ts +354 -0
- package/src/control/intent-router.ts +326 -0
- package/src/control/types.ts +102 -0
- package/src/curator/curator.ts +265 -15
- package/src/governance/governance.ts +698 -0
- package/src/governance/index.ts +18 -0
- package/src/governance/types.ts +111 -0
- package/src/index.ts +128 -3
- package/src/llm/llm-client.ts +18 -24
- package/src/logging/logger.ts +154 -0
- package/src/logging/types.ts +21 -0
- package/src/loop/loop-manager.ts +130 -0
- package/src/loop/types.ts +44 -0
- package/src/planning/gap-analysis.ts +506 -0
- package/src/planning/gap-types.ts +58 -0
- package/src/planning/planner.ts +478 -2
- package/src/project/project-registry.ts +358 -0
- package/src/project/types.ts +31 -0
- package/src/runtime/admin-extra-ops.ts +307 -0
- package/src/runtime/admin-ops.ts +329 -0
- package/src/runtime/capture-ops.ts +385 -0
- package/src/runtime/core-ops.ts +747 -26
- package/src/runtime/curator-extra-ops.ts +71 -0
- package/src/runtime/domain-ops.ts +65 -13
- package/src/runtime/grading-ops.ts +121 -0
- package/src/runtime/loop-ops.ts +194 -0
- package/src/runtime/memory-cross-project-ops.ts +192 -0
- package/src/runtime/memory-extra-ops.ts +186 -0
- package/src/runtime/orchestrate-ops.ts +272 -0
- package/src/runtime/planning-extra-ops.ts +327 -0
- package/src/runtime/project-ops.ts +196 -0
- package/src/runtime/runtime.ts +54 -1
- package/src/runtime/types.ts +23 -0
- package/src/runtime/vault-extra-ops.ts +225 -0
- package/src/telemetry/telemetry.ts +118 -0
- package/src/vault/vault.ts +412 -1
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { createAgentRuntime } from '../runtime/runtime.js';
|
|
3
|
+
import { createCaptureOps } from '../runtime/capture-ops.js';
|
|
4
|
+
import type { AgentRuntime } from '../runtime/types.js';
|
|
5
|
+
import type { OpDefinition } from '../facades/types.js';
|
|
6
|
+
|
|
7
|
+
describe('createCaptureOps', () => {
|
|
8
|
+
let runtime: AgentRuntime;
|
|
9
|
+
let ops: OpDefinition[];
|
|
10
|
+
|
|
11
|
+
function findOp(name: string): OpDefinition {
|
|
12
|
+
const op = ops.find((o) => o.name === name);
|
|
13
|
+
if (!op) throw new Error(`Op "${name}" not found`);
|
|
14
|
+
return op;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
runtime?.close();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function setup() {
|
|
22
|
+
runtime = createAgentRuntime({
|
|
23
|
+
agentId: 'test-capture',
|
|
24
|
+
vaultPath: ':memory:',
|
|
25
|
+
});
|
|
26
|
+
ops = createCaptureOps(runtime);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
it('should return 4 ops', () => {
|
|
30
|
+
setup();
|
|
31
|
+
expect(ops).toHaveLength(4);
|
|
32
|
+
const names = ops.map((o) => o.name);
|
|
33
|
+
expect(names).toEqual([
|
|
34
|
+
'capture_knowledge',
|
|
35
|
+
'capture_quick',
|
|
36
|
+
'search_intelligent',
|
|
37
|
+
'search_feedback',
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ─── capture_knowledge ──────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
describe('capture_knowledge', () => {
|
|
44
|
+
it('should batch-capture multiple entries', async () => {
|
|
45
|
+
setup();
|
|
46
|
+
const result = (await findOp('capture_knowledge').handler({
|
|
47
|
+
projectPath: '/test',
|
|
48
|
+
entries: [
|
|
49
|
+
{
|
|
50
|
+
type: 'pattern',
|
|
51
|
+
domain: 'testing',
|
|
52
|
+
title: 'Batch capture pattern one',
|
|
53
|
+
description: 'First test pattern for batch capture.',
|
|
54
|
+
tags: ['test', 'batch'],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'anti-pattern',
|
|
58
|
+
domain: 'testing',
|
|
59
|
+
title: 'Batch capture anti-pattern two',
|
|
60
|
+
description: 'Second test entry for batch capture.',
|
|
61
|
+
tags: ['test'],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
})) as { captured: number; proposed: number; rejected: number; results: unknown[] };
|
|
65
|
+
|
|
66
|
+
expect(result.captured).toBe(2);
|
|
67
|
+
expect(result.proposed).toBe(0);
|
|
68
|
+
expect(result.rejected).toBe(0);
|
|
69
|
+
expect(result.results).toHaveLength(2);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should auto-generate ID when not provided', async () => {
|
|
73
|
+
setup();
|
|
74
|
+
const result = (await findOp('capture_knowledge').handler({
|
|
75
|
+
entries: [
|
|
76
|
+
{
|
|
77
|
+
type: 'rule',
|
|
78
|
+
domain: 'api',
|
|
79
|
+
title: 'Auto ID test',
|
|
80
|
+
description: 'Should get an auto-generated ID.',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
})) as { results: Array<{ id: string; action: string }> };
|
|
84
|
+
|
|
85
|
+
expect(result.results[0].id).toContain('api-');
|
|
86
|
+
expect(result.results[0].action).toBe('capture');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should use provided ID when specified', async () => {
|
|
90
|
+
setup();
|
|
91
|
+
const result = (await findOp('capture_knowledge').handler({
|
|
92
|
+
entries: [
|
|
93
|
+
{
|
|
94
|
+
id: 'custom-id-123',
|
|
95
|
+
type: 'pattern',
|
|
96
|
+
domain: 'testing',
|
|
97
|
+
title: 'Custom ID test',
|
|
98
|
+
description: 'Has a custom ID.',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
})) as { results: Array<{ id: string; action: string }> };
|
|
102
|
+
|
|
103
|
+
expect(result.results[0].id).toBe('custom-id-123');
|
|
104
|
+
expect(result.results[0].action).toBe('capture');
|
|
105
|
+
|
|
106
|
+
// Verify entry actually in vault
|
|
107
|
+
const entry = runtime.vault.get('custom-id-123');
|
|
108
|
+
expect(entry).not.toBeNull();
|
|
109
|
+
expect(entry!.title).toBe('Custom ID test');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should map extended types to vault-compatible types', async () => {
|
|
113
|
+
setup();
|
|
114
|
+
const result = (await findOp('capture_knowledge').handler({
|
|
115
|
+
entries: [
|
|
116
|
+
{
|
|
117
|
+
id: 'wf-1',
|
|
118
|
+
type: 'workflow',
|
|
119
|
+
domain: 'ops',
|
|
120
|
+
title: 'Workflow test',
|
|
121
|
+
description: 'A workflow entry.',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'pr-1',
|
|
125
|
+
type: 'principle',
|
|
126
|
+
domain: 'ops',
|
|
127
|
+
title: 'Principle test',
|
|
128
|
+
description: 'A principle entry.',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'ref-1',
|
|
132
|
+
type: 'reference',
|
|
133
|
+
domain: 'ops',
|
|
134
|
+
title: 'Reference test',
|
|
135
|
+
description: 'A reference entry.',
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
})) as { captured: number };
|
|
139
|
+
|
|
140
|
+
expect(result.captured).toBe(3);
|
|
141
|
+
|
|
142
|
+
// All mapped to 'rule' in vault
|
|
143
|
+
expect(runtime.vault.get('wf-1')!.type).toBe('rule');
|
|
144
|
+
expect(runtime.vault.get('pr-1')!.type).toBe('rule');
|
|
145
|
+
expect(runtime.vault.get('ref-1')!.type).toBe('rule');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should map severity info to suggestion', async () => {
|
|
149
|
+
setup();
|
|
150
|
+
await findOp('capture_knowledge').handler({
|
|
151
|
+
entries: [
|
|
152
|
+
{
|
|
153
|
+
id: 'sev-1',
|
|
154
|
+
type: 'pattern',
|
|
155
|
+
domain: 'testing',
|
|
156
|
+
title: 'Severity mapping test',
|
|
157
|
+
severity: 'info',
|
|
158
|
+
description: 'Info severity entry.',
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const entry = runtime.vault.get('sev-1');
|
|
164
|
+
expect(entry).not.toBeNull();
|
|
165
|
+
expect(entry!.severity).toBe('suggestion');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should include optional fields', async () => {
|
|
169
|
+
setup();
|
|
170
|
+
await findOp('capture_knowledge').handler({
|
|
171
|
+
entries: [
|
|
172
|
+
{
|
|
173
|
+
id: 'full-1',
|
|
174
|
+
type: 'pattern',
|
|
175
|
+
domain: 'testing',
|
|
176
|
+
title: 'Full entry test',
|
|
177
|
+
severity: 'critical',
|
|
178
|
+
description: 'Full entry with all fields.',
|
|
179
|
+
context: 'Test context',
|
|
180
|
+
example: 'Good example',
|
|
181
|
+
counterExample: 'Bad example',
|
|
182
|
+
why: 'Because testing.',
|
|
183
|
+
tags: ['full', 'complete'],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const entry = runtime.vault.get('full-1');
|
|
189
|
+
expect(entry).not.toBeNull();
|
|
190
|
+
expect(entry!.severity).toBe('critical');
|
|
191
|
+
expect(entry!.context).toBe('Test context');
|
|
192
|
+
expect(entry!.example).toBe('Good example');
|
|
193
|
+
expect(entry!.counterExample).toBe('Bad example');
|
|
194
|
+
expect(entry!.why).toBe('Because testing.');
|
|
195
|
+
expect(entry!.tags).toContain('full');
|
|
196
|
+
expect(entry!.tags).toContain('complete');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ─── capture_knowledge with governance gating ───────────────────
|
|
201
|
+
|
|
202
|
+
describe('capture_knowledge governance gating', () => {
|
|
203
|
+
it('should route entries to proposals under strict preset', async () => {
|
|
204
|
+
setup();
|
|
205
|
+
runtime.governance.applyPreset('/test', 'strict');
|
|
206
|
+
|
|
207
|
+
const result = (await findOp('capture_knowledge').handler({
|
|
208
|
+
projectPath: '/test',
|
|
209
|
+
entries: [
|
|
210
|
+
{
|
|
211
|
+
type: 'pattern',
|
|
212
|
+
domain: 'testing',
|
|
213
|
+
title: 'Strict gated entry',
|
|
214
|
+
description: 'Should be proposed, not captured.',
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
})) as { captured: number; proposed: number; results: Array<{ action: string }> };
|
|
218
|
+
|
|
219
|
+
expect(result.captured).toBe(0);
|
|
220
|
+
expect(result.proposed).toBe(1);
|
|
221
|
+
expect(result.results[0].action).toBe('propose');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// ─── capture_quick ──────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
describe('capture_quick', () => {
|
|
228
|
+
it('should capture a single entry with minimal fields', async () => {
|
|
229
|
+
setup();
|
|
230
|
+
const result = (await findOp('capture_quick').handler({
|
|
231
|
+
type: 'pattern',
|
|
232
|
+
domain: 'testing',
|
|
233
|
+
title: 'Quick capture test',
|
|
234
|
+
description: 'A quickly captured pattern.',
|
|
235
|
+
tags: ['quick'],
|
|
236
|
+
})) as { captured: boolean; id: string; governance: { action: string } };
|
|
237
|
+
|
|
238
|
+
expect(result.captured).toBe(true);
|
|
239
|
+
expect(result.id).toContain('testing-');
|
|
240
|
+
expect(result.governance.action).toBe('capture');
|
|
241
|
+
|
|
242
|
+
// Verify in vault
|
|
243
|
+
const entry = runtime.vault.get(result.id);
|
|
244
|
+
expect(entry).not.toBeNull();
|
|
245
|
+
expect(entry!.title).toBe('Quick capture test');
|
|
246
|
+
expect(entry!.severity).toBe('suggestion'); // defaults to info -> suggestion
|
|
247
|
+
expect(entry!.tags).toContain('quick');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should propose under strict governance', async () => {
|
|
251
|
+
setup();
|
|
252
|
+
runtime.governance.applyPreset('/test', 'strict');
|
|
253
|
+
|
|
254
|
+
const result = (await findOp('capture_quick').handler({
|
|
255
|
+
projectPath: '/test',
|
|
256
|
+
type: 'anti-pattern',
|
|
257
|
+
domain: 'security',
|
|
258
|
+
title: 'Quick gated entry',
|
|
259
|
+
description: 'Should be proposed.',
|
|
260
|
+
})) as { captured: boolean; governance: { action: string; reason?: string } };
|
|
261
|
+
|
|
262
|
+
expect(result.captured).toBe(false);
|
|
263
|
+
expect(result.governance.action).toBe('propose');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should default tags to empty array', async () => {
|
|
267
|
+
setup();
|
|
268
|
+
const result = (await findOp('capture_quick').handler({
|
|
269
|
+
type: 'rule',
|
|
270
|
+
domain: 'testing',
|
|
271
|
+
title: 'No tags test',
|
|
272
|
+
description: 'Entry without tags.',
|
|
273
|
+
})) as { captured: boolean; id: string };
|
|
274
|
+
|
|
275
|
+
expect(result.captured).toBe(true);
|
|
276
|
+
const entry = runtime.vault.get(result.id);
|
|
277
|
+
expect(entry).not.toBeNull();
|
|
278
|
+
// Tags may include auto-generated ones from brain enrichment
|
|
279
|
+
expect(Array.isArray(entry!.tags)).toBe(true);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// ─── search_intelligent ─────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
describe('search_intelligent', () => {
|
|
286
|
+
it('should return ranked results from vault', async () => {
|
|
287
|
+
setup();
|
|
288
|
+
runtime.vault.seed([
|
|
289
|
+
{
|
|
290
|
+
id: 'si-1',
|
|
291
|
+
type: 'pattern',
|
|
292
|
+
domain: 'testing',
|
|
293
|
+
title: 'Intelligent search pattern',
|
|
294
|
+
severity: 'warning',
|
|
295
|
+
description: 'A pattern for intelligent search testing.',
|
|
296
|
+
tags: ['search', 'intelligent'],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
id: 'si-2',
|
|
300
|
+
type: 'rule',
|
|
301
|
+
domain: 'testing',
|
|
302
|
+
title: 'Another search rule',
|
|
303
|
+
severity: 'critical',
|
|
304
|
+
description: 'Another entry for search testing.',
|
|
305
|
+
tags: ['search'],
|
|
306
|
+
},
|
|
307
|
+
]);
|
|
308
|
+
runtime.brain.rebuildVocabulary();
|
|
309
|
+
|
|
310
|
+
// Recreate ops after seeding
|
|
311
|
+
ops = createCaptureOps(runtime);
|
|
312
|
+
const results = (await findOp('search_intelligent').handler({
|
|
313
|
+
query: 'intelligent search pattern',
|
|
314
|
+
})) as Array<{ source: string; score?: number }>;
|
|
315
|
+
|
|
316
|
+
expect(results.length).toBeGreaterThan(0);
|
|
317
|
+
expect(results[0].source).toBe('vault');
|
|
318
|
+
expect(typeof (results[0] as { score: number }).score).toBe('number');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should filter by domain', async () => {
|
|
322
|
+
setup();
|
|
323
|
+
runtime.vault.seed([
|
|
324
|
+
{
|
|
325
|
+
id: 'sd-1',
|
|
326
|
+
type: 'pattern',
|
|
327
|
+
domain: 'security',
|
|
328
|
+
title: 'Security pattern',
|
|
329
|
+
severity: 'critical',
|
|
330
|
+
description: 'Security search test.',
|
|
331
|
+
tags: ['sec'],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: 'sd-2',
|
|
335
|
+
type: 'pattern',
|
|
336
|
+
domain: 'testing',
|
|
337
|
+
title: 'Testing pattern',
|
|
338
|
+
severity: 'warning',
|
|
339
|
+
description: 'Testing search test.',
|
|
340
|
+
tags: ['test'],
|
|
341
|
+
},
|
|
342
|
+
]);
|
|
343
|
+
runtime.brain.rebuildVocabulary();
|
|
344
|
+
ops = createCaptureOps(runtime);
|
|
345
|
+
|
|
346
|
+
const results = (await findOp('search_intelligent').handler({
|
|
347
|
+
query: 'pattern',
|
|
348
|
+
domain: 'security',
|
|
349
|
+
})) as Array<{ entry?: { domain: string } }>;
|
|
350
|
+
|
|
351
|
+
for (const r of results) {
|
|
352
|
+
if (r.entry) {
|
|
353
|
+
expect(r.entry.domain).toBe('security');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should include memories when includeMemories is true', async () => {
|
|
359
|
+
setup();
|
|
360
|
+
// Seed vault entries
|
|
361
|
+
runtime.vault.seed([
|
|
362
|
+
{
|
|
363
|
+
id: 'sm-1',
|
|
364
|
+
type: 'pattern',
|
|
365
|
+
domain: 'testing',
|
|
366
|
+
title: 'Memory search vault entry',
|
|
367
|
+
severity: 'warning',
|
|
368
|
+
description: 'Vault entry for memory search test.',
|
|
369
|
+
tags: ['mem'],
|
|
370
|
+
},
|
|
371
|
+
]);
|
|
372
|
+
// Capture a memory
|
|
373
|
+
runtime.vault.captureMemory({
|
|
374
|
+
projectPath: '/test',
|
|
375
|
+
type: 'lesson',
|
|
376
|
+
context: 'Testing search with memories',
|
|
377
|
+
summary: 'Memory search integration test works correctly',
|
|
378
|
+
topics: ['testing', 'search'],
|
|
379
|
+
filesModified: [],
|
|
380
|
+
toolsUsed: [],
|
|
381
|
+
});
|
|
382
|
+
runtime.brain.rebuildVocabulary();
|
|
383
|
+
ops = createCaptureOps(runtime);
|
|
384
|
+
|
|
385
|
+
const results = (await findOp('search_intelligent').handler({
|
|
386
|
+
query: 'memory search',
|
|
387
|
+
includeMemories: true,
|
|
388
|
+
})) as Array<{ source: string }>;
|
|
389
|
+
|
|
390
|
+
const sources = results.map((r) => r.source);
|
|
391
|
+
// Should have both vault and memory sources
|
|
392
|
+
expect(sources).toContain('vault');
|
|
393
|
+
expect(sources).toContain('memory');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should not include memories when includeMemories is false', async () => {
|
|
397
|
+
setup();
|
|
398
|
+
runtime.vault.captureMemory({
|
|
399
|
+
projectPath: '/test',
|
|
400
|
+
type: 'lesson',
|
|
401
|
+
context: 'Hidden memory',
|
|
402
|
+
summary: 'This memory should not appear without includeMemories',
|
|
403
|
+
topics: ['hidden'],
|
|
404
|
+
filesModified: [],
|
|
405
|
+
toolsUsed: [],
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
const results = (await findOp('search_intelligent').handler({
|
|
409
|
+
query: 'hidden memory',
|
|
410
|
+
includeMemories: false,
|
|
411
|
+
})) as Array<{ source: string }>;
|
|
412
|
+
|
|
413
|
+
const memorySources = results.filter((r) => r.source === 'memory');
|
|
414
|
+
expect(memorySources).toHaveLength(0);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('should respect limit parameter', async () => {
|
|
418
|
+
setup();
|
|
419
|
+
const seedEntries = Array.from({ length: 10 }, (_, i) => ({
|
|
420
|
+
id: `lim-${i}`,
|
|
421
|
+
type: 'pattern' as const,
|
|
422
|
+
domain: 'testing',
|
|
423
|
+
title: `Limit test pattern ${i}`,
|
|
424
|
+
severity: 'warning' as const,
|
|
425
|
+
description: `Testing limit functionality pattern number ${i}.`,
|
|
426
|
+
tags: ['limit'],
|
|
427
|
+
}));
|
|
428
|
+
runtime.vault.seed(seedEntries);
|
|
429
|
+
runtime.brain.rebuildVocabulary();
|
|
430
|
+
ops = createCaptureOps(runtime);
|
|
431
|
+
|
|
432
|
+
const results = (await findOp('search_intelligent').handler({
|
|
433
|
+
query: 'limit test pattern',
|
|
434
|
+
limit: 3,
|
|
435
|
+
})) as unknown[];
|
|
436
|
+
|
|
437
|
+
expect(results.length).toBeLessThanOrEqual(3);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ─── search_feedback ────────────────────────────────────────────
|
|
442
|
+
|
|
443
|
+
describe('search_feedback', () => {
|
|
444
|
+
it('should record positive feedback', async () => {
|
|
445
|
+
setup();
|
|
446
|
+
runtime.vault.seed([
|
|
447
|
+
{
|
|
448
|
+
id: 'fb-1',
|
|
449
|
+
type: 'pattern',
|
|
450
|
+
domain: 'testing',
|
|
451
|
+
title: 'Feedback test entry',
|
|
452
|
+
severity: 'warning',
|
|
453
|
+
description: 'Entry for feedback testing.',
|
|
454
|
+
tags: ['feedback'],
|
|
455
|
+
},
|
|
456
|
+
]);
|
|
457
|
+
|
|
458
|
+
const result = (await findOp('search_feedback').handler({
|
|
459
|
+
query: 'feedback test',
|
|
460
|
+
entryId: 'fb-1',
|
|
461
|
+
helpful: true,
|
|
462
|
+
})) as { recorded: boolean; action: string };
|
|
463
|
+
|
|
464
|
+
expect(result.recorded).toBe(true);
|
|
465
|
+
expect(result.action).toBe('accepted');
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('should record negative feedback', async () => {
|
|
469
|
+
setup();
|
|
470
|
+
const result = (await findOp('search_feedback').handler({
|
|
471
|
+
query: 'irrelevant query',
|
|
472
|
+
entryId: 'some-entry',
|
|
473
|
+
helpful: false,
|
|
474
|
+
})) as { recorded: boolean; action: string };
|
|
475
|
+
|
|
476
|
+
expect(result.recorded).toBe(true);
|
|
477
|
+
expect(result.action).toBe('dismissed');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('should include context when provided', async () => {
|
|
481
|
+
setup();
|
|
482
|
+
const result = (await findOp('search_feedback').handler({
|
|
483
|
+
query: 'test query',
|
|
484
|
+
entryId: 'some-entry',
|
|
485
|
+
helpful: true,
|
|
486
|
+
context: 'This was very relevant to my task',
|
|
487
|
+
})) as { recorded: boolean; context: string | null };
|
|
488
|
+
|
|
489
|
+
expect(result.recorded).toBe(true);
|
|
490
|
+
expect(result.context).toBe('This was very relevant to my task');
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// ─── Auth levels ───────────────────────────────────────────────
|
|
495
|
+
|
|
496
|
+
describe('auth levels', () => {
|
|
497
|
+
it('should use read auth for search ops', () => {
|
|
498
|
+
setup();
|
|
499
|
+
expect(findOp('search_intelligent').auth).toBe('read');
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should use write auth for capture and feedback ops', () => {
|
|
503
|
+
setup();
|
|
504
|
+
expect(findOp('capture_knowledge').auth).toBe('write');
|
|
505
|
+
expect(findOp('capture_quick').auth).toBe('write');
|
|
506
|
+
expect(findOp('search_feedback').auth).toBe('write');
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
});
|