@soleri/core 2.4.0 → 2.5.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 +7 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +56 -9
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/types.d.ts +2 -2
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/cognee/client.d.ts +3 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +17 -0
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +94 -0
- package/dist/cognee/sync-manager.d.ts.map +1 -0
- package/dist/cognee/sync-manager.js +293 -0
- package/dist/cognee/sync-manager.js.map +1 -0
- package/dist/curator/curator.d.ts +8 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +64 -1
- package/dist/curator/curator.js.map +1 -1
- package/dist/errors/classify.d.ts +13 -0
- package/dist/errors/classify.d.ts.map +1 -0
- package/dist/errors/classify.js +97 -0
- package/dist/errors/classify.js.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/retry.d.ts +40 -0
- package/dist/errors/retry.d.ts.map +1 -0
- package/dist/errors/retry.js +97 -0
- package/dist/errors/retry.js.map +1 -0
- package/dist/errors/types.d.ts +48 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/errors/types.js +59 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +14 -0
- package/dist/intake/content-classifier.d.ts.map +1 -0
- package/dist/intake/content-classifier.js +125 -0
- package/dist/intake/content-classifier.js.map +1 -0
- package/dist/intake/dedup-gate.d.ts +17 -0
- package/dist/intake/dedup-gate.d.ts.map +1 -0
- package/dist/intake/dedup-gate.js +66 -0
- package/dist/intake/dedup-gate.js.map +1 -0
- package/dist/intake/intake-pipeline.d.ts +63 -0
- package/dist/intake/intake-pipeline.d.ts.map +1 -0
- package/dist/intake/intake-pipeline.js +373 -0
- package/dist/intake/intake-pipeline.js.map +1 -0
- package/dist/intake/types.d.ts +65 -0
- package/dist/intake/types.d.ts.map +1 -0
- package/dist/intake/types.js +3 -0
- package/dist/intake/types.js.map +1 -0
- package/dist/intelligence/loader.js +1 -1
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +3 -1
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/loop/loop-manager.d.ts +58 -7
- package/dist/loop/loop-manager.d.ts.map +1 -1
- package/dist/loop/loop-manager.js +280 -6
- package/dist/loop/loop-manager.js.map +1 -1
- package/dist/loop/types.d.ts +69 -1
- package/dist/loop/types.d.ts.map +1 -1
- package/dist/loop/types.js +4 -1
- package/dist/loop/types.js.map +1 -1
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +25 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +59 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +36 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +8 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +47 -4
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +190 -13
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-types.d.ts +1 -1
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/planner.d.ts +277 -9
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +611 -46
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/generic/brainstorming.d.ts +9 -0
- package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
- package/dist/playbooks/generic/brainstorming.js +105 -0
- package/dist/playbooks/generic/brainstorming.js.map +1 -0
- package/dist/playbooks/generic/code-review.d.ts +11 -0
- package/dist/playbooks/generic/code-review.d.ts.map +1 -0
- package/dist/playbooks/generic/code-review.js +176 -0
- package/dist/playbooks/generic/code-review.js.map +1 -0
- package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
- package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
- package/dist/playbooks/generic/subagent-execution.js +68 -0
- package/dist/playbooks/generic/subagent-execution.js.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.js +87 -0
- package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
- package/dist/playbooks/generic/tdd.d.ts +9 -0
- package/dist/playbooks/generic/tdd.d.ts.map +1 -0
- package/dist/playbooks/generic/tdd.js +70 -0
- package/dist/playbooks/generic/tdd.js.map +1 -0
- package/dist/playbooks/generic/verification.d.ts +9 -0
- package/dist/playbooks/generic/verification.d.ts.map +1 -0
- package/dist/playbooks/generic/verification.js +74 -0
- package/dist/playbooks/generic/verification.js.map +1 -0
- package/dist/playbooks/index.d.ts +4 -0
- package/dist/playbooks/index.d.ts.map +1 -0
- package/dist/playbooks/index.js +5 -0
- package/dist/playbooks/index.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts +42 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -0
- package/dist/playbooks/playbook-registry.js +227 -0
- package/dist/playbooks/playbook-registry.js.map +1 -0
- package/dist/playbooks/playbook-seeder.d.ts +47 -0
- package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
- package/dist/playbooks/playbook-seeder.js +104 -0
- package/dist/playbooks/playbook-seeder.js.map +1 -0
- package/dist/playbooks/playbook-types.d.ts +132 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -0
- package/dist/playbooks/playbook-types.js +12 -0
- package/dist/playbooks/playbook-types.js.map +1 -0
- package/dist/project/project-registry.d.ts.map +1 -1
- package/dist/project/project-registry.js +9 -11
- package/dist/project/project-registry.js.map +1 -1
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/parser.d.ts +17 -0
- package/dist/prompts/parser.d.ts.map +1 -0
- package/dist/prompts/parser.js +47 -0
- package/dist/prompts/parser.js.map +1 -0
- package/dist/prompts/template-manager.d.ts +25 -0
- package/dist/prompts/template-manager.d.ts.map +1 -0
- package/dist/prompts/template-manager.js +71 -0
- package/dist/prompts/template-manager.js.map +1 -0
- package/dist/prompts/types.d.ts +26 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +5 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +5 -3
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +322 -11
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +10 -3
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +20 -2
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/cognee-sync-ops.d.ts +12 -0
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
- package/dist/runtime/cognee-sync-ops.js +55 -0
- package/dist/runtime/cognee-sync-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +8 -6
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +226 -9
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +2 -2
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +15 -3
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/domain-ops.js +2 -2
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts.map +1 -1
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/intake-ops.d.ts +14 -0
- package/dist/runtime/intake-ops.d.ts.map +1 -0
- package/dist/runtime/intake-ops.js +110 -0
- package/dist/runtime/intake-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +5 -4
- package/dist/runtime/loop-ops.d.ts.map +1 -1
- package/dist/runtime/loop-ops.js +84 -12
- package/dist/runtime/loop-ops.js.map +1 -1
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
- package/dist/runtime/memory-cross-project-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.js +5 -5
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +8 -2
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts +13 -5
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +381 -18
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/playbook-ops.d.ts +14 -0
- package/dist/runtime/playbook-ops.d.ts.map +1 -0
- package/dist/runtime/playbook-ops.js +141 -0
- package/dist/runtime/playbook-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts.map +1 -1
- package/dist/runtime/project-ops.js +7 -2
- package/dist/runtime/project-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +27 -8
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +8 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +3 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +345 -4
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/vault/playbook.d.ts +34 -0
- package/dist/vault/playbook.d.ts.map +1 -0
- package/dist/vault/playbook.js +60 -0
- package/dist/vault/playbook.js.map +1 -0
- package/dist/vault/vault.d.ts +31 -32
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +201 -181
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -3
- package/src/__tests__/admin-extra-ops.test.ts +62 -15
- package/src/__tests__/admin-ops.test.ts +2 -2
- package/src/__tests__/brain.test.ts +3 -3
- package/src/__tests__/cognee-integration.test.ts +80 -0
- package/src/__tests__/cognee-sync-manager.test.ts +103 -0
- package/src/__tests__/core-ops.test.ts +30 -4
- package/src/__tests__/curator-extra-ops.test.ts +24 -2
- package/src/__tests__/errors.test.ts +388 -0
- package/src/__tests__/grading-ops.test.ts +28 -7
- package/src/__tests__/intake-pipeline.test.ts +162 -0
- package/src/__tests__/loop-ops.test.ts +74 -3
- package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
- package/src/__tests__/orchestrate-ops.test.ts +8 -3
- package/src/__tests__/persistence.test.ts +225 -0
- package/src/__tests__/planner.test.ts +99 -21
- package/src/__tests__/planning-extra-ops.test.ts +168 -10
- package/src/__tests__/playbook-registry.test.ts +326 -0
- package/src/__tests__/playbook-seeder.test.ts +163 -0
- package/src/__tests__/playbook.test.ts +389 -0
- package/src/__tests__/project-ops.test.ts +18 -4
- package/src/__tests__/template-manager.test.ts +222 -0
- package/src/__tests__/vault-extra-ops.test.ts +82 -7
- package/src/brain/brain.ts +71 -9
- package/src/brain/types.ts +2 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/curator/curator.ts +88 -7
- package/src/errors/classify.ts +102 -0
- package/src/errors/index.ts +5 -0
- package/src/errors/retry.ts +132 -0
- package/src/errors/types.ts +81 -0
- package/src/index.ts +114 -3
- package/src/intake/content-classifier.ts +146 -0
- package/src/intake/dedup-gate.ts +92 -0
- package/src/intake/intake-pipeline.ts +503 -0
- package/src/intake/types.ts +69 -0
- package/src/intelligence/loader.ts +1 -1
- package/src/intelligence/types.ts +3 -1
- package/src/loop/loop-manager.ts +325 -7
- package/src/loop/types.ts +72 -1
- package/src/persistence/index.ts +7 -0
- package/src/persistence/sqlite-provider.ts +62 -0
- package/src/persistence/types.ts +44 -0
- package/src/planning/gap-analysis.ts +286 -17
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/planner.ts +828 -55
- package/src/playbooks/generic/brainstorming.ts +110 -0
- package/src/playbooks/generic/code-review.ts +181 -0
- package/src/playbooks/generic/subagent-execution.ts +74 -0
- package/src/playbooks/generic/systematic-debugging.ts +92 -0
- package/src/playbooks/generic/tdd.ts +75 -0
- package/src/playbooks/generic/verification.ts +79 -0
- package/src/playbooks/index.ts +27 -0
- package/src/playbooks/playbook-registry.ts +284 -0
- package/src/playbooks/playbook-seeder.ts +119 -0
- package/src/playbooks/playbook-types.ts +162 -0
- package/src/project/project-registry.ts +29 -17
- package/src/prompts/index.ts +3 -0
- package/src/prompts/parser.ts +59 -0
- package/src/prompts/template-manager.ts +77 -0
- package/src/prompts/types.ts +28 -0
- package/src/runtime/admin-extra-ops.ts +358 -13
- package/src/runtime/admin-ops.ts +17 -6
- package/src/runtime/capture-ops.ts +25 -6
- package/src/runtime/cognee-sync-ops.ts +63 -0
- package/src/runtime/core-ops.ts +258 -8
- package/src/runtime/curator-extra-ops.ts +17 -3
- package/src/runtime/domain-ops.ts +2 -2
- package/src/runtime/grading-ops.ts +11 -2
- package/src/runtime/intake-ops.ts +126 -0
- package/src/runtime/loop-ops.ts +96 -13
- package/src/runtime/memory-cross-project-ops.ts +1 -2
- package/src/runtime/memory-extra-ops.ts +5 -5
- package/src/runtime/orchestrate-ops.ts +8 -2
- package/src/runtime/planning-extra-ops.ts +414 -23
- package/src/runtime/playbook-ops.ts +169 -0
- package/src/runtime/project-ops.ts +9 -3
- package/src/runtime/runtime.ts +35 -9
- package/src/runtime/types.ts +8 -0
- package/src/runtime/vault-extra-ops.ts +385 -4
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +301 -235
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { parseVariables, resolveIncludes } from '../prompts/parser.js';
|
|
6
|
+
import { TemplateManager } from '../prompts/template-manager.js';
|
|
7
|
+
|
|
8
|
+
// ─── parseVariables ───────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
describe('parseVariables', () => {
|
|
11
|
+
it('extracts required variable', () => {
|
|
12
|
+
const vars = parseVariables('Hello {{name}}');
|
|
13
|
+
expect(vars).toHaveLength(1);
|
|
14
|
+
expect(vars[0]).toEqual({ name: 'name', required: true, defaultValue: undefined });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('extracts variable with default', () => {
|
|
18
|
+
const vars = parseVariables('Hello {{name:World}}');
|
|
19
|
+
expect(vars).toHaveLength(1);
|
|
20
|
+
expect(vars[0]).toEqual({ name: 'name', required: false, defaultValue: 'World' });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('extracts multiple unique variables', () => {
|
|
24
|
+
const vars = parseVariables('{{greeting}} {{name}}, welcome to {{place}}');
|
|
25
|
+
expect(vars).toHaveLength(3);
|
|
26
|
+
expect(vars.map((v) => v.name)).toEqual(['greeting', 'name', 'place']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('deduplicates same variable', () => {
|
|
30
|
+
const vars = parseVariables('{{name}} and {{name}} again');
|
|
31
|
+
expect(vars).toHaveLength(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('handles empty default', () => {
|
|
35
|
+
const vars = parseVariables('{{opt:}}');
|
|
36
|
+
expect(vars[0]).toEqual({ name: 'opt', required: false, defaultValue: '' });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns empty for no variables', () => {
|
|
40
|
+
expect(parseVariables('plain text')).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('handles mixed required and optional', () => {
|
|
44
|
+
const vars = parseVariables('{{required}} and {{optional:fallback}}');
|
|
45
|
+
expect(vars).toHaveLength(2);
|
|
46
|
+
expect(vars[0].required).toBe(true);
|
|
47
|
+
expect(vars[1].required).toBe(false);
|
|
48
|
+
expect(vars[1].defaultValue).toBe('fallback');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ─── resolveIncludes ──────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
describe('resolveIncludes', () => {
|
|
55
|
+
it('resolves single include', () => {
|
|
56
|
+
const result = resolveIncludes('before @include(header) after', (name) => {
|
|
57
|
+
if (name === 'header') return 'HEADER';
|
|
58
|
+
return '';
|
|
59
|
+
});
|
|
60
|
+
expect(result).toBe('before HEADER after');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('resolves nested includes', () => {
|
|
64
|
+
const result = resolveIncludes('start @include(a)', (name) => {
|
|
65
|
+
if (name === 'a') return 'A @include(b)';
|
|
66
|
+
if (name === 'b') return 'B';
|
|
67
|
+
return '';
|
|
68
|
+
});
|
|
69
|
+
expect(result).toBe('start A B');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('detects circular includes', () => {
|
|
73
|
+
expect(() =>
|
|
74
|
+
resolveIncludes('@include(a)', (name) => {
|
|
75
|
+
if (name === 'a') return '@include(b)';
|
|
76
|
+
if (name === 'b') return '@include(a)';
|
|
77
|
+
return '';
|
|
78
|
+
}),
|
|
79
|
+
).toThrow(/Circular include detected/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('throws on depth exceeding 10', () => {
|
|
83
|
+
// Each level includes a unique name so cycle detection doesn't fire
|
|
84
|
+
let counter = 0;
|
|
85
|
+
expect(() =>
|
|
86
|
+
resolveIncludes('@include(level0)', (name) => {
|
|
87
|
+
counter++;
|
|
88
|
+
return `@include(level${counter})`;
|
|
89
|
+
}),
|
|
90
|
+
).toThrow(/Include depth exceeded/);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('handles no includes', () => {
|
|
94
|
+
const result = resolveIncludes('no includes here', () => '');
|
|
95
|
+
expect(result).toBe('no includes here');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('handles multiple includes', () => {
|
|
99
|
+
const result = resolveIncludes('@include(a) + @include(b)', (name) => name.toUpperCase());
|
|
100
|
+
expect(result).toBe('A + B');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// ─── TemplateManager ──────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
describe('TemplateManager', () => {
|
|
107
|
+
let tempDir: string;
|
|
108
|
+
|
|
109
|
+
beforeEach(() => {
|
|
110
|
+
tempDir = join(tmpdir(), `templates-test-${Date.now()}`);
|
|
111
|
+
mkdirSync(tempDir, { recursive: true });
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
afterEach(() => {
|
|
115
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('loads .prompt files from directory', () => {
|
|
119
|
+
writeFileSync(join(tempDir, 'greeting.prompt'), 'Hello {{name}}!');
|
|
120
|
+
writeFileSync(join(tempDir, 'farewell.prompt'), 'Goodbye {{name}}.');
|
|
121
|
+
writeFileSync(join(tempDir, 'not-a-template.txt'), 'ignored');
|
|
122
|
+
|
|
123
|
+
const mgr = new TemplateManager(tempDir);
|
|
124
|
+
mgr.load();
|
|
125
|
+
|
|
126
|
+
expect(mgr.listTemplates().sort()).toEqual(['farewell', 'greeting']);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('handles nonexistent directory gracefully', () => {
|
|
130
|
+
const mgr = new TemplateManager('/nonexistent/path');
|
|
131
|
+
mgr.load(); // should not throw
|
|
132
|
+
expect(mgr.listTemplates()).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('renders template with variables', () => {
|
|
136
|
+
writeFileSync(join(tempDir, 'hello.prompt'), 'Hello {{name}}, you are {{role}}!');
|
|
137
|
+
const mgr = new TemplateManager(tempDir);
|
|
138
|
+
mgr.load();
|
|
139
|
+
|
|
140
|
+
const result = mgr.render('hello', { name: 'Atlas', role: 'advisor' });
|
|
141
|
+
expect(result).toBe('Hello Atlas, you are advisor!');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('uses default values', () => {
|
|
145
|
+
writeFileSync(join(tempDir, 'hello.prompt'), 'Hello {{name:World}}!');
|
|
146
|
+
const mgr = new TemplateManager(tempDir);
|
|
147
|
+
mgr.load();
|
|
148
|
+
|
|
149
|
+
expect(mgr.render('hello', {})).toBe('Hello World!');
|
|
150
|
+
expect(mgr.render('hello', { name: 'Custom' })).toBe('Hello Custom!');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('throws on missing required variable in strict mode', () => {
|
|
154
|
+
writeFileSync(join(tempDir, 'strict.prompt'), '{{required}}');
|
|
155
|
+
const mgr = new TemplateManager(tempDir);
|
|
156
|
+
mgr.load();
|
|
157
|
+
|
|
158
|
+
expect(() => mgr.render('strict', {})).toThrow(/Missing required variable: required/);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('leaves placeholder in non-strict mode', () => {
|
|
162
|
+
writeFileSync(join(tempDir, 'lax.prompt'), 'Hello {{name}}!');
|
|
163
|
+
const mgr = new TemplateManager(tempDir);
|
|
164
|
+
mgr.load();
|
|
165
|
+
|
|
166
|
+
const result = mgr.render('lax', {}, { strict: false });
|
|
167
|
+
expect(result).toBe('Hello {{name}}!');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('resolves @include directives', () => {
|
|
171
|
+
writeFileSync(join(tempDir, 'header.prompt'), '--- HEADER ---');
|
|
172
|
+
writeFileSync(join(tempDir, 'page.prompt'), '@include(header)\nContent here.');
|
|
173
|
+
const mgr = new TemplateManager(tempDir);
|
|
174
|
+
mgr.load();
|
|
175
|
+
|
|
176
|
+
const result = mgr.render('page', {});
|
|
177
|
+
expect(result).toBe('--- HEADER ---\nContent here.');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('throws on missing include', () => {
|
|
181
|
+
writeFileSync(join(tempDir, 'broken.prompt'), '@include(nonexistent)');
|
|
182
|
+
const mgr = new TemplateManager(tempDir);
|
|
183
|
+
mgr.load();
|
|
184
|
+
|
|
185
|
+
expect(() => mgr.render('broken', {})).toThrow(/Include not found: nonexistent/);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('throws on nonexistent template name', () => {
|
|
189
|
+
const mgr = new TemplateManager(tempDir);
|
|
190
|
+
mgr.load();
|
|
191
|
+
|
|
192
|
+
expect(() => mgr.render('missing', {})).toThrow(/Template not found: missing/);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('getTemplate returns raw template', () => {
|
|
196
|
+
writeFileSync(join(tempDir, 'raw.prompt'), 'Raw {{content}}');
|
|
197
|
+
const mgr = new TemplateManager(tempDir);
|
|
198
|
+
mgr.load();
|
|
199
|
+
|
|
200
|
+
const tmpl = mgr.getTemplate('raw');
|
|
201
|
+
expect(tmpl).not.toBeNull();
|
|
202
|
+
expect(tmpl!.name).toBe('raw');
|
|
203
|
+
expect(tmpl!.content).toBe('Raw {{content}}');
|
|
204
|
+
expect(tmpl!.variables).toHaveLength(1);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('getTemplate returns null for nonexistent', () => {
|
|
208
|
+
const mgr = new TemplateManager(tempDir);
|
|
209
|
+
mgr.load();
|
|
210
|
+
expect(mgr.getTemplate('nope')).toBeNull();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('variables in included templates get substituted', () => {
|
|
214
|
+
writeFileSync(join(tempDir, 'partial.prompt'), 'I am {{name}}');
|
|
215
|
+
writeFileSync(join(tempDir, 'main.prompt'), 'Hello: @include(partial)!');
|
|
216
|
+
const mgr = new TemplateManager(tempDir);
|
|
217
|
+
mgr.load();
|
|
218
|
+
|
|
219
|
+
const result = mgr.render('main', { name: 'Atlas' });
|
|
220
|
+
expect(result).toBe('Hello: I am Atlas!');
|
|
221
|
+
});
|
|
222
|
+
});
|
|
@@ -47,8 +47,8 @@ describe('createVaultExtraOps', () => {
|
|
|
47
47
|
return op;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
it('should return
|
|
51
|
-
expect(ops.length).toBe(
|
|
50
|
+
it('should return 20 ops', () => {
|
|
51
|
+
expect(ops.length).toBe(20);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
it('should have all expected op names', () => {
|
|
@@ -65,6 +65,17 @@ describe('createVaultExtraOps', () => {
|
|
|
65
65
|
expect(names).toContain('vault_seed');
|
|
66
66
|
expect(names).toContain('vault_backup');
|
|
67
67
|
expect(names).toContain('vault_age_report');
|
|
68
|
+
// #153: Seed canonical
|
|
69
|
+
expect(names).toContain('vault_seed_canonical');
|
|
70
|
+
// #155: Knowledge lifecycle
|
|
71
|
+
expect(names).toContain('knowledge_audit');
|
|
72
|
+
expect(names).toContain('knowledge_health');
|
|
73
|
+
expect(names).toContain('knowledge_merge');
|
|
74
|
+
expect(names).toContain('knowledge_reorganize');
|
|
75
|
+
// #89: Bi-temporal
|
|
76
|
+
expect(names).toContain('vault_set_temporal');
|
|
77
|
+
expect(names).toContain('vault_find_expiring');
|
|
78
|
+
expect(names).toContain('vault_find_expired');
|
|
68
79
|
});
|
|
69
80
|
|
|
70
81
|
// ─── vault_get ────────────────────────────────────────────────────
|
|
@@ -357,10 +368,7 @@ describe('createVaultExtraOps', () => {
|
|
|
357
368
|
// ─── vault_age_report ─────────────────────────────────────────────
|
|
358
369
|
|
|
359
370
|
it('vault_age_report should return age distribution', async () => {
|
|
360
|
-
runtime.vault.seed([
|
|
361
|
-
makeEntry({ id: 'va-1' }),
|
|
362
|
-
makeEntry({ id: 'va-2' }),
|
|
363
|
-
]);
|
|
371
|
+
runtime.vault.seed([makeEntry({ id: 'va-1' }), makeEntry({ id: 'va-2' })]);
|
|
364
372
|
const result = (await findOp('vault_age_report').handler({})) as {
|
|
365
373
|
total: number;
|
|
366
374
|
buckets: Array<{ label: string; count: number; minDays: number; maxDays: number }>;
|
|
@@ -390,7 +398,14 @@ describe('createVaultExtraOps', () => {
|
|
|
390
398
|
// ─── Auth levels ──────────────────────────────────────────────────
|
|
391
399
|
|
|
392
400
|
it('should assign correct auth levels', () => {
|
|
393
|
-
const readOps = [
|
|
401
|
+
const readOps = [
|
|
402
|
+
'vault_get',
|
|
403
|
+
'vault_tags',
|
|
404
|
+
'vault_domains',
|
|
405
|
+
'vault_recent',
|
|
406
|
+
'vault_backup',
|
|
407
|
+
'vault_age_report',
|
|
408
|
+
];
|
|
394
409
|
const writeOps = ['vault_update', 'vault_bulk_add', 'vault_import', 'vault_seed'];
|
|
395
410
|
const adminOps = ['vault_remove', 'vault_bulk_remove'];
|
|
396
411
|
|
|
@@ -404,4 +419,64 @@ describe('createVaultExtraOps', () => {
|
|
|
404
419
|
expect(findOp(name).auth).toBe('admin');
|
|
405
420
|
}
|
|
406
421
|
});
|
|
422
|
+
|
|
423
|
+
// ─── vault_set_temporal ─────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
describe('vault_set_temporal', () => {
|
|
426
|
+
it('should set validUntil on an entry', async () => {
|
|
427
|
+
runtime.vault.seed([makeEntry({ id: 'temporal-1' })]);
|
|
428
|
+
const result = (await findOp('vault_set_temporal').handler({
|
|
429
|
+
id: 'temporal-1',
|
|
430
|
+
validUntil: Math.floor(Date.now() / 1000) + 86400 * 30,
|
|
431
|
+
})) as { updated: boolean; id: string; validUntil: number | null };
|
|
432
|
+
expect(result.updated).toBe(true);
|
|
433
|
+
expect(result.id).toBe('temporal-1');
|
|
434
|
+
expect(result.validUntil).toBeGreaterThan(0);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should return error for missing entry', async () => {
|
|
438
|
+
const result = (await findOp('vault_set_temporal').handler({
|
|
439
|
+
id: 'nonexistent',
|
|
440
|
+
validUntil: 1000,
|
|
441
|
+
})) as { error: string };
|
|
442
|
+
expect(result.error).toBeDefined();
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// ─── vault_find_expiring ────────────────────────────────────────
|
|
447
|
+
|
|
448
|
+
describe('vault_find_expiring', () => {
|
|
449
|
+
it('should find entries expiring within N days', async () => {
|
|
450
|
+
const now = Math.floor(Date.now() / 1000);
|
|
451
|
+
runtime.vault.seed([
|
|
452
|
+
makeEntry({ id: 'exp-1' }),
|
|
453
|
+
makeEntry({ id: 'exp-2' }),
|
|
454
|
+
makeEntry({ id: 'exp-3' }),
|
|
455
|
+
]);
|
|
456
|
+
runtime.vault.setTemporal('exp-1', undefined, now + 86400 * 5);
|
|
457
|
+
runtime.vault.setTemporal('exp-2', undefined, now + 86400 * 60);
|
|
458
|
+
|
|
459
|
+
const result = (await findOp('vault_find_expiring').handler({
|
|
460
|
+
withinDays: 10,
|
|
461
|
+
})) as { entries: unknown[]; count: number };
|
|
462
|
+
expect(result.count).toBe(1);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// ─── vault_find_expired ─────────────────────────────────────────
|
|
467
|
+
|
|
468
|
+
describe('vault_find_expired', () => {
|
|
469
|
+
it('should find expired entries', async () => {
|
|
470
|
+
const now = Math.floor(Date.now() / 1000);
|
|
471
|
+
runtime.vault.seed([makeEntry({ id: 'past-1' }), makeEntry({ id: 'past-2' })]);
|
|
472
|
+
runtime.vault.setTemporal('past-1', undefined, now - 86400);
|
|
473
|
+
runtime.vault.setTemporal('past-2', undefined, now + 86400);
|
|
474
|
+
|
|
475
|
+
const result = (await findOp('vault_find_expired').handler({})) as {
|
|
476
|
+
entries: unknown[];
|
|
477
|
+
count: number;
|
|
478
|
+
};
|
|
479
|
+
expect(result.count).toBe(1);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
407
482
|
});
|
package/src/brain/brain.ts
CHANGED
|
@@ -47,7 +47,7 @@ const DEFAULT_WEIGHTS: ScoringWeights = {
|
|
|
47
47
|
semantic: 0.4,
|
|
48
48
|
vector: 0.0,
|
|
49
49
|
severity: 0.15,
|
|
50
|
-
|
|
50
|
+
temporalDecay: 0.15,
|
|
51
51
|
tagOverlap: 0.15,
|
|
52
52
|
domainMatch: 0.15,
|
|
53
53
|
};
|
|
@@ -56,7 +56,7 @@ const COGNEE_WEIGHTS: ScoringWeights = {
|
|
|
56
56
|
semantic: 0.25,
|
|
57
57
|
vector: 0.35,
|
|
58
58
|
severity: 0.1,
|
|
59
|
-
|
|
59
|
+
temporalDecay: 0.1,
|
|
60
60
|
tagOverlap: 0.1,
|
|
61
61
|
domainMatch: 0.1,
|
|
62
62
|
};
|
|
@@ -454,6 +454,42 @@ export class Brain {
|
|
|
454
454
|
return this.vocabulary.size;
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
async getDecayReport(
|
|
458
|
+
query: string,
|
|
459
|
+
limit: number = 10,
|
|
460
|
+
): Promise<
|
|
461
|
+
Array<{
|
|
462
|
+
id: string;
|
|
463
|
+
title: string;
|
|
464
|
+
decayScore: number;
|
|
465
|
+
validUntil: number | null;
|
|
466
|
+
status: 'active' | 'expiring' | 'expired';
|
|
467
|
+
}>
|
|
468
|
+
> {
|
|
469
|
+
const results = await this.intelligentSearch(query, { limit });
|
|
470
|
+
const now = Math.floor(Date.now() / 1000);
|
|
471
|
+
return results.map((r) => {
|
|
472
|
+
const validUntil = r.entry.validUntil ?? null;
|
|
473
|
+
let status: 'active' | 'expiring' | 'expired' = 'active';
|
|
474
|
+
if (validUntil) {
|
|
475
|
+
if (validUntil <= now) status = 'expired';
|
|
476
|
+
else {
|
|
477
|
+
const validFrom = r.entry.validFrom ?? now;
|
|
478
|
+
const totalWindow = validUntil - validFrom;
|
|
479
|
+
const remaining = validUntil - now;
|
|
480
|
+
if (remaining <= totalWindow * 0.25) status = 'expiring';
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
id: r.entry.id,
|
|
485
|
+
title: r.entry.title,
|
|
486
|
+
decayScore: r.breakdown.temporalDecay,
|
|
487
|
+
validUntil,
|
|
488
|
+
status,
|
|
489
|
+
};
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
457
493
|
// ─── Private methods ─────────────────────────────────────────────
|
|
458
494
|
|
|
459
495
|
private scoreEntry(
|
|
@@ -483,9 +519,7 @@ export class Brain {
|
|
|
483
519
|
|
|
484
520
|
const severity = SEVERITY_SCORES[entry.severity] ?? 0.4;
|
|
485
521
|
|
|
486
|
-
const
|
|
487
|
-
const halfLifeSeconds = RECENCY_HALF_LIFE_DAYS * 86400;
|
|
488
|
-
const recency = entryAge > 0 ? Math.exp((-Math.LN2 * entryAge) / halfLifeSeconds) : 1;
|
|
522
|
+
const temporalDecay = computeTemporalDecay(entry, now);
|
|
489
523
|
|
|
490
524
|
const tagOverlap = queryTags.length > 0 ? jaccardSimilarity(queryTags, entry.tags) : 0;
|
|
491
525
|
|
|
@@ -497,11 +531,11 @@ export class Brain {
|
|
|
497
531
|
w.semantic * semantic +
|
|
498
532
|
w.vector * vector +
|
|
499
533
|
w.severity * severity +
|
|
500
|
-
w.
|
|
534
|
+
w.temporalDecay * temporalDecay +
|
|
501
535
|
w.tagOverlap * tagOverlap +
|
|
502
536
|
w.domainMatch * domainMatch;
|
|
503
537
|
|
|
504
|
-
return { semantic, vector, severity,
|
|
538
|
+
return { semantic, vector, severity, temporalDecay, tagOverlap, domainMatch, total };
|
|
505
539
|
}
|
|
506
540
|
|
|
507
541
|
private generateTags(title: string, description: string, context?: string): string[] {
|
|
@@ -640,12 +674,12 @@ export class Brain {
|
|
|
640
674
|
const remaining = 1.0 - newWeights.semantic - newWeights.vector;
|
|
641
675
|
const otherSum =
|
|
642
676
|
DEFAULT_WEIGHTS.severity +
|
|
643
|
-
DEFAULT_WEIGHTS.
|
|
677
|
+
DEFAULT_WEIGHTS.temporalDecay +
|
|
644
678
|
DEFAULT_WEIGHTS.tagOverlap +
|
|
645
679
|
DEFAULT_WEIGHTS.domainMatch;
|
|
646
680
|
const scale = remaining / otherSum;
|
|
647
681
|
newWeights.severity = DEFAULT_WEIGHTS.severity * scale;
|
|
648
|
-
newWeights.
|
|
682
|
+
newWeights.temporalDecay = DEFAULT_WEIGHTS.temporalDecay * scale;
|
|
649
683
|
newWeights.tagOverlap = DEFAULT_WEIGHTS.tagOverlap * scale;
|
|
650
684
|
newWeights.domainMatch = DEFAULT_WEIGHTS.domainMatch * scale;
|
|
651
685
|
|
|
@@ -653,6 +687,34 @@ export class Brain {
|
|
|
653
687
|
}
|
|
654
688
|
}
|
|
655
689
|
|
|
690
|
+
function computeTemporalDecay(entry: IntelligenceEntry, now: number): number {
|
|
691
|
+
const entryRecord = entry as unknown as {
|
|
692
|
+
created_at?: number;
|
|
693
|
+
updated_at?: number;
|
|
694
|
+
valid_until?: number;
|
|
695
|
+
valid_from?: number;
|
|
696
|
+
};
|
|
697
|
+
const validUntil = entry.validUntil ?? entryRecord.valid_until;
|
|
698
|
+
|
|
699
|
+
if (!validUntil) {
|
|
700
|
+
// No expiry — use existing age-based exponential decay
|
|
701
|
+
const updatedAt = entryRecord.updated_at ?? entryRecord.created_at ?? now;
|
|
702
|
+
const ageSeconds = now - updatedAt;
|
|
703
|
+
const halfLifeSeconds = RECENCY_HALF_LIFE_DAYS * 86400;
|
|
704
|
+
return ageSeconds > 0 ? Math.exp((-Math.LN2 * ageSeconds) / halfLifeSeconds) : 1;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// With valid_until: linear ramp-down in last 25% of validity window
|
|
708
|
+
const validFrom = entry.validFrom ?? entryRecord.valid_from ?? entryRecord.created_at ?? now;
|
|
709
|
+
const totalWindow = validUntil - validFrom;
|
|
710
|
+
const remaining = validUntil - now;
|
|
711
|
+
if (remaining <= 0) return 0; // expired
|
|
712
|
+
if (totalWindow <= 0) return 1; // edge case: bad data
|
|
713
|
+
const decayZone = totalWindow * 0.25;
|
|
714
|
+
if (remaining > decayZone) return 1.0; // fully valid
|
|
715
|
+
return remaining / decayZone; // linear decay in last quarter
|
|
716
|
+
}
|
|
717
|
+
|
|
656
718
|
function clamp(value: number, min: number, max: number): number {
|
|
657
719
|
return Math.max(min, Math.min(max, value));
|
|
658
720
|
}
|
package/src/brain/types.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface ScoringWeights {
|
|
|
6
6
|
semantic: number;
|
|
7
7
|
vector: number;
|
|
8
8
|
severity: number;
|
|
9
|
-
|
|
9
|
+
temporalDecay: number;
|
|
10
10
|
tagOverlap: number;
|
|
11
11
|
domainMatch: number;
|
|
12
12
|
}
|
|
@@ -15,7 +15,7 @@ export interface ScoreBreakdown {
|
|
|
15
15
|
semantic: number;
|
|
16
16
|
vector: number;
|
|
17
17
|
severity: number;
|
|
18
|
-
|
|
18
|
+
temporalDecay: number;
|
|
19
19
|
tagOverlap: number;
|
|
20
20
|
domainMatch: number;
|
|
21
21
|
total: number;
|
package/src/cognee/client.ts
CHANGED
|
@@ -254,6 +254,24 @@ export class CogneeClient {
|
|
|
254
254
|
return this.healthCache?.status ?? null;
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
// ─── Delete ──────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
async deleteEntries(entryIds: string[]): Promise<{ deleted: number }> {
|
|
260
|
+
if (!this.isAvailable || entryIds.length === 0) return { deleted: 0 };
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const res = await this.post('/api/v1/delete', {
|
|
264
|
+
datasetName: this.config.dataset,
|
|
265
|
+
entryIds,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (!res.ok) return { deleted: 0 };
|
|
269
|
+
return { deleted: entryIds.length };
|
|
270
|
+
} catch {
|
|
271
|
+
return { deleted: 0 };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
257
275
|
// ─── Auth ──────────────────────────────────────────────────────
|
|
258
276
|
// Auto-register + login pattern from Salvador MCP.
|
|
259
277
|
// Tries login first (account may already exist), falls back to register.
|