@soleri/core 2.8.0 → 2.10.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/extensions/index.d.ts +3 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/middleware.d.ts +13 -0
- package/dist/extensions/middleware.d.ts.map +1 -0
- package/dist/extensions/middleware.js +47 -0
- package/dist/extensions/middleware.js.map +1 -0
- package/dist/extensions/types.d.ts +64 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/types.js +2 -0
- package/dist/extensions/types.js.map +1 -0
- package/dist/index.d.ts +8 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -16
- package/dist/index.js.map +1 -1
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +3 -1
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/runtime/core-ops.d.ts +1 -1
- package/dist/runtime/core-ops.js +1 -1
- package/dist/runtime/facades/admin-facade.d.ts +8 -0
- package/dist/runtime/facades/admin-facade.d.ts.map +1 -0
- package/dist/runtime/facades/admin-facade.js +90 -0
- package/dist/runtime/facades/admin-facade.js.map +1 -0
- package/dist/runtime/facades/brain-facade.d.ts +8 -0
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -0
- package/dist/runtime/facades/brain-facade.js +294 -0
- package/dist/runtime/facades/brain-facade.js.map +1 -0
- package/dist/runtime/facades/cognee-facade.d.ts +8 -0
- package/dist/runtime/facades/cognee-facade.d.ts.map +1 -0
- package/dist/runtime/facades/cognee-facade.js +154 -0
- package/dist/runtime/facades/cognee-facade.js.map +1 -0
- package/dist/runtime/facades/control-facade.d.ts +8 -0
- package/dist/runtime/facades/control-facade.d.ts.map +1 -0
- package/dist/runtime/facades/control-facade.js +244 -0
- package/dist/runtime/facades/control-facade.js.map +1 -0
- package/dist/runtime/facades/curator-facade.d.ts +8 -0
- package/dist/runtime/facades/curator-facade.d.ts.map +1 -0
- package/dist/runtime/facades/curator-facade.js +117 -0
- package/dist/runtime/facades/curator-facade.js.map +1 -0
- package/dist/runtime/facades/index.d.ts +10 -0
- package/dist/runtime/facades/index.d.ts.map +1 -0
- package/dist/runtime/facades/index.js +71 -0
- package/dist/runtime/facades/index.js.map +1 -0
- package/dist/runtime/facades/loop-facade.d.ts +8 -0
- package/dist/runtime/facades/loop-facade.d.ts.map +1 -0
- package/dist/runtime/facades/loop-facade.js +9 -0
- package/dist/runtime/facades/loop-facade.js.map +1 -0
- package/dist/runtime/facades/memory-facade.d.ts +8 -0
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -0
- package/dist/runtime/facades/memory-facade.js +108 -0
- package/dist/runtime/facades/memory-facade.js.map +1 -0
- package/dist/runtime/facades/orchestrate-facade.d.ts +8 -0
- package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -0
- package/dist/runtime/facades/orchestrate-facade.js +58 -0
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -0
- package/dist/runtime/facades/plan-facade.d.ts +8 -0
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -0
- package/dist/runtime/facades/plan-facade.js +110 -0
- package/dist/runtime/facades/plan-facade.js.map +1 -0
- package/dist/runtime/facades/vault-facade.d.ts +8 -0
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -0
- package/dist/runtime/facades/vault-facade.js +194 -0
- package/dist/runtime/facades/vault-facade.js.map +1 -0
- package/dist/runtime/vault-extra-ops.d.ts +2 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +37 -2
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/streams/index.d.ts +4 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +3 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/normalize.d.ts +14 -0
- package/dist/streams/normalize.d.ts.map +1 -0
- package/dist/streams/normalize.js +43 -0
- package/dist/streams/normalize.js.map +1 -0
- package/dist/streams/replayable-stream.d.ts +19 -0
- package/dist/streams/replayable-stream.d.ts.map +1 -0
- package/dist/streams/replayable-stream.js +90 -0
- package/dist/streams/replayable-stream.js.map +1 -0
- package/dist/vault/content-hash.d.ts +16 -0
- package/dist/vault/content-hash.d.ts.map +1 -0
- package/dist/vault/content-hash.js +21 -0
- package/dist/vault/content-hash.js.map +1 -0
- package/dist/vault/vault.d.ts +9 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +49 -3
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/content-hash.test.ts +60 -0
- package/src/__tests__/core-ops.test.ts +10 -7
- package/src/__tests__/extensions.test.ts +233 -0
- package/src/__tests__/grading-ops.test.ts +2 -2
- package/src/__tests__/memory-cross-project-ops.test.ts +2 -2
- package/src/__tests__/normalize.test.ts +75 -0
- package/src/__tests__/playbook.test.ts +4 -4
- package/src/__tests__/replayable-stream.test.ts +66 -0
- package/src/__tests__/vault-extra-ops.test.ts +1 -1
- package/src/__tests__/vault.test.ts +72 -0
- package/src/extensions/index.ts +2 -0
- package/src/extensions/middleware.ts +53 -0
- package/src/extensions/types.ts +64 -0
- package/src/index.ts +14 -17
- package/src/planning/gap-analysis.ts +52 -7
- package/src/runtime/facades/admin-facade.ts +101 -0
- package/src/runtime/facades/brain-facade.ts +331 -0
- package/src/runtime/facades/cognee-facade.ts +162 -0
- package/src/runtime/facades/control-facade.ts +279 -0
- package/src/runtime/facades/curator-facade.ts +132 -0
- package/src/runtime/facades/index.ts +74 -0
- package/src/runtime/facades/loop-facade.ts +12 -0
- package/src/runtime/facades/memory-facade.ts +114 -0
- package/src/runtime/facades/orchestrate-facade.ts +68 -0
- package/src/runtime/facades/plan-facade.ts +119 -0
- package/src/runtime/facades/vault-facade.ts +223 -0
- package/src/runtime/vault-extra-ops.ts +38 -2
- package/src/streams/index.ts +3 -0
- package/src/streams/normalize.ts +56 -0
- package/src/streams/replayable-stream.ts +92 -0
- package/src/vault/content-hash.ts +31 -0
- package/src/vault/vault.ts +73 -3
- package/src/runtime/core-ops.ts +0 -1443
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-consumer async stream that replays from a buffer.
|
|
3
|
+
* Source executes exactly once — new iterators replay from buffer[0].
|
|
4
|
+
*/
|
|
5
|
+
export class ReplayableStream<T> implements AsyncIterable<T> {
|
|
6
|
+
private buffer: T[] = [];
|
|
7
|
+
private source: AsyncIterator<T>;
|
|
8
|
+
private done = false;
|
|
9
|
+
private error: unknown = undefined;
|
|
10
|
+
private waiters: Array<{
|
|
11
|
+
resolve: (result: IteratorResult<T, undefined>) => void;
|
|
12
|
+
reject: (err: unknown) => void;
|
|
13
|
+
}> = [];
|
|
14
|
+
private advancing = false;
|
|
15
|
+
|
|
16
|
+
constructor(source: AsyncIterable<T>) {
|
|
17
|
+
this.source = source[Symbol.asyncIterator]();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private async advance(): Promise<void> {
|
|
21
|
+
if (this.advancing || this.done) return;
|
|
22
|
+
this.advancing = true;
|
|
23
|
+
try {
|
|
24
|
+
const result = await this.source.next();
|
|
25
|
+
if (result.done) {
|
|
26
|
+
this.done = true;
|
|
27
|
+
for (const waiter of this.waiters) {
|
|
28
|
+
waiter.resolve({ value: undefined, done: true });
|
|
29
|
+
}
|
|
30
|
+
this.waiters.length = 0;
|
|
31
|
+
} else {
|
|
32
|
+
this.buffer.push(result.value);
|
|
33
|
+
for (const waiter of this.waiters) {
|
|
34
|
+
waiter.resolve({ value: result.value, done: false });
|
|
35
|
+
}
|
|
36
|
+
this.waiters.length = 0;
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
this.done = true;
|
|
40
|
+
this.error = err;
|
|
41
|
+
for (const waiter of this.waiters) {
|
|
42
|
+
waiter.reject(err);
|
|
43
|
+
}
|
|
44
|
+
this.waiters.length = 0;
|
|
45
|
+
} finally {
|
|
46
|
+
this.advancing = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
51
|
+
let index = 0;
|
|
52
|
+
return {
|
|
53
|
+
next: async (): Promise<IteratorResult<T>> => {
|
|
54
|
+
if (index < this.buffer.length) {
|
|
55
|
+
return { value: this.buffer[index++], done: false };
|
|
56
|
+
}
|
|
57
|
+
if (this.done) {
|
|
58
|
+
if (this.error) throw this.error;
|
|
59
|
+
return { value: undefined, done: true };
|
|
60
|
+
}
|
|
61
|
+
// Need to advance the source
|
|
62
|
+
return new Promise<IteratorResult<T, undefined>>((resolve, reject) => {
|
|
63
|
+
this.waiters.push({ resolve, reject });
|
|
64
|
+
if (!this.advancing) {
|
|
65
|
+
this.advance().catch(() => {
|
|
66
|
+
// Error already propagated to waiters via reject
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}).then((result) => {
|
|
70
|
+
if (!result.done) index++;
|
|
71
|
+
return result;
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async collect(): Promise<T[]> {
|
|
78
|
+
const items: T[] = [];
|
|
79
|
+
for await (const item of this) {
|
|
80
|
+
items.push(item);
|
|
81
|
+
}
|
|
82
|
+
return items;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get bufferedCount(): number {
|
|
86
|
+
return this.buffer.length;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get isDone(): boolean {
|
|
90
|
+
return this.done;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export interface HashableEntry {
|
|
4
|
+
type: string;
|
|
5
|
+
domain: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
tags?: string[];
|
|
9
|
+
example?: string;
|
|
10
|
+
counterExample?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Compute a deterministic SHA-256 content hash for a vault entry.
|
|
15
|
+
* Normalizes fields (lowercase domain, trim, sort tags/keys) before hashing.
|
|
16
|
+
* Returns 40-char hex string. Excludes mutable fields (id, severity, timestamps).
|
|
17
|
+
*/
|
|
18
|
+
export function computeContentHash(entry: HashableEntry): string {
|
|
19
|
+
const normalized = {
|
|
20
|
+
counterExample: (entry.counterExample ?? '').trim(),
|
|
21
|
+
description: entry.description.trim(),
|
|
22
|
+
domain: entry.domain.toLowerCase().trim(),
|
|
23
|
+
example: (entry.example ?? '').trim(),
|
|
24
|
+
tags: [...(entry.tags ?? [])].sort(),
|
|
25
|
+
title: entry.title.trim(),
|
|
26
|
+
type: entry.type.trim(),
|
|
27
|
+
};
|
|
28
|
+
// Keys already alphabetical — JSON.stringify preserves insertion order
|
|
29
|
+
const json = JSON.stringify(normalized);
|
|
30
|
+
return createHash('sha256').update(json, 'utf8').digest('hex').slice(0, 40);
|
|
31
|
+
}
|
package/src/vault/vault.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PersistenceProvider } from '../persistence/types.js';
|
|
2
2
|
import { SQLitePersistenceProvider } from '../persistence/sqlite-provider.js';
|
|
3
3
|
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
4
|
+
import { computeContentHash } from './content-hash.js';
|
|
4
5
|
|
|
5
6
|
export interface SearchResult {
|
|
6
7
|
entry: IntelligenceEntry;
|
|
@@ -177,6 +178,7 @@ export class Vault {
|
|
|
177
178
|
`);
|
|
178
179
|
this.migrateBrainSchema();
|
|
179
180
|
this.migrateTemporalSchema();
|
|
181
|
+
this.migrateContentHash();
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
private migrateTemporalSchema(): void {
|
|
@@ -192,6 +194,49 @@ export class Vault {
|
|
|
192
194
|
}
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
private migrateContentHash(): void {
|
|
198
|
+
try {
|
|
199
|
+
this.provider.run('ALTER TABLE entries ADD COLUMN content_hash TEXT');
|
|
200
|
+
} catch {
|
|
201
|
+
// Column already exists
|
|
202
|
+
}
|
|
203
|
+
this.provider.execSql(
|
|
204
|
+
'CREATE INDEX IF NOT EXISTS idx_entries_content_hash ON entries(content_hash) WHERE content_hash IS NOT NULL',
|
|
205
|
+
);
|
|
206
|
+
// Backfill existing entries that lack a hash
|
|
207
|
+
const unhashed = this.provider.all<{
|
|
208
|
+
id: string;
|
|
209
|
+
type: string;
|
|
210
|
+
domain: string;
|
|
211
|
+
title: string;
|
|
212
|
+
description: string;
|
|
213
|
+
tags: string;
|
|
214
|
+
example: string | null;
|
|
215
|
+
counter_example: string | null;
|
|
216
|
+
}>(
|
|
217
|
+
'SELECT id, type, domain, title, description, tags, example, counter_example FROM entries WHERE content_hash IS NULL',
|
|
218
|
+
);
|
|
219
|
+
if (unhashed.length > 0) {
|
|
220
|
+
this.provider.transaction(() => {
|
|
221
|
+
for (const row of unhashed) {
|
|
222
|
+
const hash = computeContentHash({
|
|
223
|
+
type: row.type,
|
|
224
|
+
domain: row.domain,
|
|
225
|
+
title: row.title,
|
|
226
|
+
description: row.description,
|
|
227
|
+
tags: JSON.parse(row.tags),
|
|
228
|
+
example: row.example ?? undefined,
|
|
229
|
+
counterExample: row.counter_example ?? undefined,
|
|
230
|
+
});
|
|
231
|
+
this.provider.run('UPDATE entries SET content_hash = @hash WHERE id = @id', {
|
|
232
|
+
hash,
|
|
233
|
+
id: row.id,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
195
240
|
private migrateBrainSchema(): void {
|
|
196
241
|
const columns = this.provider.all<{ name: string }>('PRAGMA table_info(brain_feedback)');
|
|
197
242
|
const hasSource = columns.some((c) => c.name === 'source');
|
|
@@ -236,11 +281,12 @@ export class Vault {
|
|
|
236
281
|
|
|
237
282
|
seed(entries: IntelligenceEntry[]): number {
|
|
238
283
|
const sql = `
|
|
239
|
-
INSERT INTO entries (id,type,domain,title,severity,description,context,example,counter_example,why,tags,applies_to,valid_from,valid_until)
|
|
240
|
-
VALUES (@id,@type,@domain,@title,@severity,@description,@context,@example,@counterExample,@why,@tags,@appliesTo,@validFrom,@validUntil)
|
|
284
|
+
INSERT INTO entries (id,type,domain,title,severity,description,context,example,counter_example,why,tags,applies_to,valid_from,valid_until,content_hash)
|
|
285
|
+
VALUES (@id,@type,@domain,@title,@severity,@description,@context,@example,@counterExample,@why,@tags,@appliesTo,@validFrom,@validUntil,@contentHash)
|
|
241
286
|
ON CONFLICT(id) DO UPDATE SET type=excluded.type,domain=excluded.domain,title=excluded.title,severity=excluded.severity,
|
|
242
287
|
description=excluded.description,context=excluded.context,example=excluded.example,counter_example=excluded.counter_example,
|
|
243
|
-
why=excluded.why,tags=excluded.tags,applies_to=excluded.applies_to,valid_from=excluded.valid_from,valid_until=excluded.valid_until,
|
|
288
|
+
why=excluded.why,tags=excluded.tags,applies_to=excluded.applies_to,valid_from=excluded.valid_from,valid_until=excluded.valid_until,
|
|
289
|
+
content_hash=excluded.content_hash,updated_at=unixepoch()
|
|
244
290
|
`;
|
|
245
291
|
return this.provider.transaction(() => {
|
|
246
292
|
let count = 0;
|
|
@@ -260,6 +306,7 @@ export class Vault {
|
|
|
260
306
|
appliesTo: JSON.stringify(entry.appliesTo ?? []),
|
|
261
307
|
validFrom: entry.validFrom ?? null,
|
|
262
308
|
validUntil: entry.validUntil ?? null,
|
|
309
|
+
contentHash: computeContentHash(entry),
|
|
263
310
|
});
|
|
264
311
|
count++;
|
|
265
312
|
if (this.syncManager) {
|
|
@@ -999,6 +1046,29 @@ export class Vault {
|
|
|
999
1046
|
throw new Error('getDb() is only available with SQLite provider');
|
|
1000
1047
|
}
|
|
1001
1048
|
|
|
1049
|
+
/** Check if an entry with this content hash already exists. Returns the existing ID or null. */
|
|
1050
|
+
findByContentHash(hash: string): string | null {
|
|
1051
|
+
const row = this.provider.get<{ id: string }>(
|
|
1052
|
+
'SELECT id FROM entries WHERE content_hash = @hash',
|
|
1053
|
+
{ hash },
|
|
1054
|
+
);
|
|
1055
|
+
return row?.id ?? null;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/** Get content hash stats for dedup reporting. */
|
|
1059
|
+
contentHashStats(): { total: number; hashed: number; uniqueHashes: number } {
|
|
1060
|
+
const total = this.provider.get<{ c: number }>('SELECT COUNT(*) as c FROM entries')?.c ?? 0;
|
|
1061
|
+
const hashed =
|
|
1062
|
+
this.provider.get<{ c: number }>(
|
|
1063
|
+
'SELECT COUNT(*) as c FROM entries WHERE content_hash IS NOT NULL',
|
|
1064
|
+
)?.c ?? 0;
|
|
1065
|
+
const uniqueHashes =
|
|
1066
|
+
this.provider.get<{ c: number }>(
|
|
1067
|
+
'SELECT COUNT(DISTINCT content_hash) as c FROM entries WHERE content_hash IS NOT NULL',
|
|
1068
|
+
)?.c ?? 0;
|
|
1069
|
+
return { total, hashed, uniqueHashes };
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1002
1072
|
close(): void {
|
|
1003
1073
|
this.provider.close();
|
|
1004
1074
|
}
|