@plur-ai/core 0.4.0 → 0.4.2
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.
|
@@ -62,32 +62,42 @@ function computeIdf(engrams, queryTokens) {
|
|
|
62
62
|
}
|
|
63
63
|
return idf;
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
var BM25_K1 = 1.2;
|
|
66
|
+
var BM25_B = 0.75;
|
|
67
|
+
function ftsScore(engram, queryTokens, idfWeights, avgDocLength) {
|
|
66
68
|
const allTerms = ftsTokenize(engramSearchText(engram));
|
|
67
69
|
if (queryTokens.length === 0) return 0;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
const docLen = allTerms.length;
|
|
71
|
+
const avgdl = avgDocLength && avgDocLength > 0 ? avgDocLength : docLen;
|
|
72
|
+
const hasNonZeroIdf = idfWeights && Array.from(idfWeights.values()).some((v) => v > 0);
|
|
73
|
+
let score = 0;
|
|
70
74
|
for (const qt of queryTokens) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
let effectiveIdf;
|
|
76
|
+
if (!idfWeights) {
|
|
77
|
+
effectiveIdf = 1;
|
|
78
|
+
} else if (hasNonZeroIdf) {
|
|
79
|
+
effectiveIdf = idfWeights.get(qt) ?? 0;
|
|
80
|
+
if (effectiveIdf === 0) continue;
|
|
81
|
+
} else {
|
|
82
|
+
effectiveIdf = 1;
|
|
75
83
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
for (const qt of queryTokens) {
|
|
80
|
-
if (allTerms.some((t) => t.includes(qt) || qt.includes(t))) matches++;
|
|
84
|
+
let tf = 0;
|
|
85
|
+
for (const t of allTerms) {
|
|
86
|
+
if (t.includes(qt) || qt.includes(t)) tf++;
|
|
81
87
|
}
|
|
82
|
-
|
|
88
|
+
if (tf === 0) continue;
|
|
89
|
+
const numerator = tf * (BM25_K1 + 1);
|
|
90
|
+
const denominator = tf + BM25_K1 * (1 - BM25_B + BM25_B * docLen / avgdl);
|
|
91
|
+
score += effectiveIdf * (numerator / denominator);
|
|
83
92
|
}
|
|
84
|
-
return
|
|
93
|
+
return score;
|
|
85
94
|
}
|
|
86
95
|
function searchEngrams(engrams, query, limit = 20) {
|
|
87
96
|
const queryTokens = ftsTokenize(query);
|
|
88
97
|
if (queryTokens.length === 0) return [];
|
|
89
98
|
const idfWeights = computeIdf(engrams, queryTokens);
|
|
90
|
-
|
|
99
|
+
const avgDocLength = engrams.length > 0 ? engrams.reduce((sum, e) => sum + ftsTokenize(engramSearchText(e)).length, 0) / engrams.length : 0;
|
|
100
|
+
return engrams.map((e) => ({ engram: e, score: ftsScore(e, queryTokens, idfWeights, avgDocLength) })).filter((r) => r.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((r) => r.engram);
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
// src/embeddings.ts
|
|
@@ -97,7 +107,7 @@ import { createHash } from "crypto";
|
|
|
97
107
|
|
|
98
108
|
// src/sync.ts
|
|
99
109
|
import { execFileSync } from "child_process";
|
|
100
|
-
import { existsSync, writeFileSync, renameSync, mkdirSync } from "fs";
|
|
110
|
+
import { existsSync, writeFileSync, renameSync, mkdirSync, unlinkSync, statSync } from "fs";
|
|
101
111
|
import { join, dirname } from "path";
|
|
102
112
|
var GITIGNORE = `# PLUR \u2014 derived/cache files (regenerated automatically)
|
|
103
113
|
embeddings/
|
|
@@ -249,6 +259,44 @@ function sync(root, remote) {
|
|
|
249
259
|
files_changed: filesChanged
|
|
250
260
|
};
|
|
251
261
|
}
|
|
262
|
+
function withLock(filePath, fn, options) {
|
|
263
|
+
const lockPath = filePath + ".lock";
|
|
264
|
+
const maxRetries = options?.maxRetries ?? 5;
|
|
265
|
+
const baseDelay = options?.baseDelay ?? 100;
|
|
266
|
+
const staleThreshold = options?.staleThreshold ?? 1e4;
|
|
267
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
268
|
+
try {
|
|
269
|
+
writeFileSync(lockPath, `${process.pid}`, { flag: "wx" });
|
|
270
|
+
break;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
if (err.code !== "EEXIST") throw err;
|
|
273
|
+
try {
|
|
274
|
+
const stat = statSync(lockPath);
|
|
275
|
+
if (Date.now() - stat.mtimeMs > staleThreshold) {
|
|
276
|
+
unlinkSync(lockPath);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
} catch {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (attempt === maxRetries) {
|
|
283
|
+
throw new Error(`Failed to acquire lock on ${filePath} after ${maxRetries} retries`);
|
|
284
|
+
}
|
|
285
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
286
|
+
const end = Date.now() + delay;
|
|
287
|
+
while (Date.now() < end) {
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
return fn();
|
|
293
|
+
} finally {
|
|
294
|
+
try {
|
|
295
|
+
unlinkSync(lockPath);
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
252
300
|
function atomicWrite(filePath, content) {
|
|
253
301
|
const dir = dirname(filePath);
|
|
254
302
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
@@ -337,6 +385,7 @@ async function embeddingSearch(engrams, query, limit, storagePath) {
|
|
|
337
385
|
export {
|
|
338
386
|
getSyncStatus,
|
|
339
387
|
sync,
|
|
388
|
+
withLock,
|
|
340
389
|
atomicWrite,
|
|
341
390
|
ftsTokenize,
|
|
342
391
|
engramSearchText,
|
package/dist/index.d.ts
CHANGED
|
@@ -616,6 +616,7 @@ declare const PlurConfigSchema: z.ZodObject<{
|
|
|
616
616
|
co_access?: boolean | undefined;
|
|
617
617
|
}>>>;
|
|
618
618
|
allow_secrets: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
619
|
+
index: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
619
620
|
}, "strip", z.ZodTypeAny, {
|
|
620
621
|
auto_learn?: boolean | undefined;
|
|
621
622
|
auto_capture?: boolean | undefined;
|
|
@@ -629,6 +630,7 @@ declare const PlurConfigSchema: z.ZodObject<{
|
|
|
629
630
|
co_access: boolean;
|
|
630
631
|
} | undefined;
|
|
631
632
|
allow_secrets?: boolean | undefined;
|
|
633
|
+
index?: boolean | undefined;
|
|
632
634
|
}, {
|
|
633
635
|
auto_learn?: boolean | undefined;
|
|
634
636
|
auto_capture?: boolean | undefined;
|
|
@@ -642,6 +644,7 @@ declare const PlurConfigSchema: z.ZodObject<{
|
|
|
642
644
|
co_access?: boolean | undefined;
|
|
643
645
|
} | undefined;
|
|
644
646
|
allow_secrets?: boolean | undefined;
|
|
647
|
+
index?: boolean | undefined;
|
|
645
648
|
}>;
|
|
646
649
|
type PlurConfig = z.infer<typeof PlurConfigSchema>;
|
|
647
650
|
|
|
@@ -1164,9 +1167,36 @@ interface PlurPaths {
|
|
|
1164
1167
|
packs: string;
|
|
1165
1168
|
exchange: string;
|
|
1166
1169
|
config: string;
|
|
1170
|
+
db: string;
|
|
1167
1171
|
}
|
|
1168
1172
|
declare function detectPlurStorage(explicitPath?: string): PlurPaths;
|
|
1169
1173
|
|
|
1174
|
+
declare class IndexedStorage {
|
|
1175
|
+
private dbPath;
|
|
1176
|
+
private engramsPath;
|
|
1177
|
+
private db;
|
|
1178
|
+
constructor(engramsPath: string, dbPath: string);
|
|
1179
|
+
private getDb;
|
|
1180
|
+
/** Load all engrams from SQLite index. Auto-rebuilds if db missing. */
|
|
1181
|
+
loadAll(): Engram[];
|
|
1182
|
+
/** Load engrams with SQL-level filtering. */
|
|
1183
|
+
loadFiltered(filter: {
|
|
1184
|
+
status?: string;
|
|
1185
|
+
scope?: string;
|
|
1186
|
+
domain?: string;
|
|
1187
|
+
}): Engram[];
|
|
1188
|
+
/** Count engrams with optional status filter. */
|
|
1189
|
+
count(filter?: {
|
|
1190
|
+
status?: string;
|
|
1191
|
+
}): number;
|
|
1192
|
+
/** Sync SQLite index from YAML source of truth. */
|
|
1193
|
+
syncFromYaml(): void;
|
|
1194
|
+
/** Drop and rebuild the entire index from YAML. */
|
|
1195
|
+
reindex(): void;
|
|
1196
|
+
/** Close the database connection. */
|
|
1197
|
+
close(): void;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1170
1200
|
/**
|
|
1171
1201
|
* Non-blocking version check against npm registry.
|
|
1172
1202
|
* Caches result in memory — one fetch per process lifetime.
|
|
@@ -1212,6 +1242,7 @@ interface StatusResult {
|
|
|
1212
1242
|
declare class Plur {
|
|
1213
1243
|
private paths;
|
|
1214
1244
|
private config;
|
|
1245
|
+
private indexedStorage;
|
|
1215
1246
|
constructor(options?: {
|
|
1216
1247
|
path?: string;
|
|
1217
1248
|
});
|
|
@@ -1263,6 +1294,15 @@ declare class Plur {
|
|
|
1263
1294
|
updateEngram(updated: Engram): boolean;
|
|
1264
1295
|
/** Set engram status to 'retired'. */
|
|
1265
1296
|
forget(id: string, reason?: string): void;
|
|
1297
|
+
/** Remove retired engrams from storage. Returns count of removed and remaining. */
|
|
1298
|
+
compact(): {
|
|
1299
|
+
removed: number;
|
|
1300
|
+
remaining: number;
|
|
1301
|
+
};
|
|
1302
|
+
/** Rebuild SQLite index from YAML source of truth. Only works when index: true. */
|
|
1303
|
+
reindex(): void;
|
|
1304
|
+
/** Sync SQLite index after YAML write (no-op if index disabled) */
|
|
1305
|
+
private _syncIndex;
|
|
1266
1306
|
/** Capture an episodic memory. */
|
|
1267
1307
|
capture(summary: string, context?: CaptureContext): Episode;
|
|
1268
1308
|
/** Query the episode timeline. */
|
|
@@ -1294,4 +1334,4 @@ declare class Plur {
|
|
|
1294
1334
|
status(): StatusResult;
|
|
1295
1335
|
}
|
|
1296
1336
|
|
|
1297
|
-
export { type AlignmentResult, type Association, type CaptureContext, type DomainCoverage, DomainCoverageSchema, type Engram, type EngramCluster, type Episode, type EvidenceEntry, EvidenceEntrySchema, type ExtractOptions, type ExtractionResult, type Falsification, FalsificationSchema, type HierarchyPosition, HierarchyPositionSchema, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type MemberAlignment, type MetaConfidence, MetaConfidenceSchema, type MetaField, MetaFieldSchema, PLATITUDE_PATTERNS, type PackManifest, Plur, type PlurConfig, type PlurPaths, type RecallOptions, type RelationalAnalysis, type RelationalTriple, SessionBreadcrumbs, type StatusResult, type StructuralTemplate, StructuralTemplateSchema, type SyncResult, type SyncStatus, type TimelineQuery, type TypedRole, type ValidationResult, type VersionCheckResult, alignCluster, analyzeStructure, checkForUpdate, classifyPolarity, clearVersionCache, clusterByStructure, computeConfidence, computeMetaConfidence, confidenceBand, detectPlurStorage, detectSecrets, engramSearchText, extractMetaEngrams, formulateMetaEngram, generateGuardrails, getCachedUpdateCheck, isPlatitude, organizeHierarchy, tokenSimilarity, validateMetaEngram };
|
|
1337
|
+
export { type AlignmentResult, type Association, type CaptureContext, type DomainCoverage, DomainCoverageSchema, type Engram, type EngramCluster, type Episode, type EvidenceEntry, EvidenceEntrySchema, type ExtractOptions, type ExtractionResult, type Falsification, FalsificationSchema, type HierarchyPosition, HierarchyPositionSchema, IndexedStorage, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type MemberAlignment, type MetaConfidence, MetaConfidenceSchema, type MetaField, MetaFieldSchema, PLATITUDE_PATTERNS, type PackManifest, Plur, type PlurConfig, type PlurPaths, type RecallOptions, type RelationalAnalysis, type RelationalTriple, SessionBreadcrumbs, type StatusResult, type StructuralTemplate, StructuralTemplateSchema, type SyncResult, type SyncStatus, type TimelineQuery, type TypedRole, type ValidationResult, type VersionCheckResult, alignCluster, analyzeStructure, checkForUpdate, classifyPolarity, clearVersionCache, clusterByStructure, computeConfidence, computeMetaConfidence, confidenceBand, detectPlurStorage, detectSecrets, engramSearchText, extractMetaEngrams, formulateMetaEngram, generateGuardrails, getCachedUpdateCheck, isPlatitude, organizeHierarchy, tokenSimilarity, validateMetaEngram };
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
ftsTokenize,
|
|
7
7
|
getSyncStatus,
|
|
8
8
|
searchEngrams,
|
|
9
|
-
sync
|
|
10
|
-
|
|
9
|
+
sync,
|
|
10
|
+
withLock
|
|
11
|
+
} from "./chunk-KMVQYBNP.js";
|
|
11
12
|
import "./chunk-2ZDO52B4.js";
|
|
12
13
|
|
|
13
14
|
// src/storage.ts
|
|
@@ -26,98 +27,71 @@ function detectPlurStorage(explicitPath) {
|
|
|
26
27
|
candidates: join(root, "candidates.yaml"),
|
|
27
28
|
packs: packsDir,
|
|
28
29
|
exchange: join(root, "exchange"),
|
|
29
|
-
config: join(root, "config.yaml")
|
|
30
|
+
config: join(root, "config.yaml"),
|
|
31
|
+
db: join(root, "engrams.db")
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
// src/
|
|
34
|
-
import { existsSync as
|
|
35
|
-
import
|
|
36
|
-
|
|
37
|
-
// src/schemas/config.ts
|
|
38
|
-
import { z } from "zod";
|
|
39
|
-
var PlurConfigSchema = z.object({
|
|
40
|
-
auto_learn: z.boolean().default(true),
|
|
41
|
-
auto_capture: z.boolean().default(true),
|
|
42
|
-
injection_budget: z.number().default(2e3),
|
|
43
|
-
decay_enabled: z.boolean().default(true),
|
|
44
|
-
decay_threshold: z.number().default(0.15),
|
|
45
|
-
packs: z.array(z.string()).default([]),
|
|
46
|
-
injection: z.object({
|
|
47
|
-
spread_cap: z.number().default(3),
|
|
48
|
-
spread_budget: z.number().default(480),
|
|
49
|
-
co_access: z.boolean().default(true)
|
|
50
|
-
}).default({}),
|
|
51
|
-
allow_secrets: z.boolean().default(false)
|
|
52
|
-
}).partial();
|
|
53
|
-
|
|
54
|
-
// src/config.ts
|
|
55
|
-
function loadConfig(configPath) {
|
|
56
|
-
if (!existsSync2(configPath)) return PlurConfigSchema.parse({});
|
|
57
|
-
try {
|
|
58
|
-
const raw = yaml.load(readFileSync(configPath, "utf8"));
|
|
59
|
-
return PlurConfigSchema.parse(raw ?? {});
|
|
60
|
-
} catch {
|
|
61
|
-
return PlurConfigSchema.parse({});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
35
|
+
// src/storage-indexed.ts
|
|
36
|
+
import { existsSync as existsSync3 } from "fs";
|
|
37
|
+
import { createRequire } from "module";
|
|
64
38
|
|
|
65
39
|
// src/engrams.ts
|
|
66
40
|
import * as fs from "fs";
|
|
67
|
-
import * as
|
|
41
|
+
import * as yaml from "js-yaml";
|
|
68
42
|
|
|
69
43
|
// src/schemas/engram.ts
|
|
70
|
-
import { z
|
|
71
|
-
var ActivationSchema =
|
|
72
|
-
retrieval_strength:
|
|
73
|
-
storage_strength:
|
|
74
|
-
frequency:
|
|
75
|
-
last_accessed:
|
|
44
|
+
import { z } from "zod";
|
|
45
|
+
var ActivationSchema = z.object({
|
|
46
|
+
retrieval_strength: z.number().min(0).max(1),
|
|
47
|
+
storage_strength: z.number().min(0).max(1),
|
|
48
|
+
frequency: z.number().int().min(0),
|
|
49
|
+
last_accessed: z.string()
|
|
76
50
|
});
|
|
77
|
-
var KnowledgeTypeSchema =
|
|
78
|
-
memory_class:
|
|
79
|
-
cognitive_level:
|
|
51
|
+
var KnowledgeTypeSchema = z.object({
|
|
52
|
+
memory_class: z.enum(["semantic", "episodic", "procedural", "metacognitive"]),
|
|
53
|
+
cognitive_level: z.enum(["remember", "understand", "apply", "analyze", "evaluate", "create"])
|
|
80
54
|
});
|
|
81
|
-
var KnowledgeAnchorSchema =
|
|
82
|
-
path:
|
|
83
|
-
relevance:
|
|
84
|
-
snippet:
|
|
85
|
-
snippet_extracted_at:
|
|
55
|
+
var KnowledgeAnchorSchema = z.object({
|
|
56
|
+
path: z.string(),
|
|
57
|
+
relevance: z.enum(["primary", "supporting", "example"]).default("supporting"),
|
|
58
|
+
snippet: z.string().max(200).optional(),
|
|
59
|
+
snippet_extracted_at: z.string().optional()
|
|
86
60
|
});
|
|
87
|
-
var AssociationSchema =
|
|
88
|
-
target_type:
|
|
89
|
-
target:
|
|
90
|
-
strength:
|
|
91
|
-
type:
|
|
92
|
-
updated_at:
|
|
61
|
+
var AssociationSchema = z.object({
|
|
62
|
+
target_type: z.enum(["engram", "document"]),
|
|
63
|
+
target: z.string(),
|
|
64
|
+
strength: z.number().min(0).max(0.95),
|
|
65
|
+
type: z.enum(["semantic", "temporal", "causal", "co_accessed"]),
|
|
66
|
+
updated_at: z.string().optional()
|
|
93
67
|
});
|
|
94
|
-
var DualCodingSchema =
|
|
95
|
-
example:
|
|
96
|
-
analogy:
|
|
68
|
+
var DualCodingSchema = z.object({
|
|
69
|
+
example: z.string().optional(),
|
|
70
|
+
analogy: z.string().optional()
|
|
97
71
|
}).refine(
|
|
98
72
|
(d) => d.example || d.analogy,
|
|
99
73
|
"At least one of example or analogy must be provided"
|
|
100
74
|
);
|
|
101
|
-
var RelationsSchema =
|
|
102
|
-
broader:
|
|
103
|
-
narrower:
|
|
104
|
-
related:
|
|
105
|
-
conflicts:
|
|
75
|
+
var RelationsSchema = z.object({
|
|
76
|
+
broader: z.array(z.string()).default([]),
|
|
77
|
+
narrower: z.array(z.string()).default([]),
|
|
78
|
+
related: z.array(z.string()).default([]),
|
|
79
|
+
conflicts: z.array(z.string()).default([])
|
|
106
80
|
});
|
|
107
|
-
var ProvenanceSchema =
|
|
108
|
-
origin:
|
|
109
|
-
chain:
|
|
110
|
-
signature:
|
|
111
|
-
license:
|
|
81
|
+
var ProvenanceSchema = z.object({
|
|
82
|
+
origin: z.string(),
|
|
83
|
+
chain: z.array(z.string()).default([]),
|
|
84
|
+
signature: z.string().nullable().default(null),
|
|
85
|
+
license: z.string().default("cc-by-sa-4.0")
|
|
112
86
|
});
|
|
113
|
-
var FeedbackSignalsSchema =
|
|
114
|
-
positive:
|
|
115
|
-
negative:
|
|
116
|
-
neutral:
|
|
87
|
+
var FeedbackSignalsSchema = z.object({
|
|
88
|
+
positive: z.number().int().default(0),
|
|
89
|
+
negative: z.number().int().default(0),
|
|
90
|
+
neutral: z.number().int().default(0)
|
|
117
91
|
});
|
|
118
|
-
var EntityRefSchema =
|
|
119
|
-
name:
|
|
120
|
-
type:
|
|
92
|
+
var EntityRefSchema = z.object({
|
|
93
|
+
name: z.string(),
|
|
94
|
+
type: z.enum([
|
|
121
95
|
"person",
|
|
122
96
|
"organization",
|
|
123
97
|
"technology",
|
|
@@ -129,56 +103,56 @@ var EntityRefSchema = z2.object({
|
|
|
129
103
|
"standard",
|
|
130
104
|
"other"
|
|
131
105
|
]),
|
|
132
|
-
uri:
|
|
106
|
+
uri: z.string().url().optional()
|
|
133
107
|
});
|
|
134
|
-
var TemporalSchema =
|
|
135
|
-
learned_at:
|
|
136
|
-
valid_from:
|
|
137
|
-
valid_until:
|
|
138
|
-
ingested_at:
|
|
108
|
+
var TemporalSchema = z.object({
|
|
109
|
+
learned_at: z.string(),
|
|
110
|
+
valid_from: z.string().optional(),
|
|
111
|
+
valid_until: z.string().optional(),
|
|
112
|
+
ingested_at: z.string().optional()
|
|
139
113
|
});
|
|
140
|
-
var UsageStatsSchema =
|
|
141
|
-
injections:
|
|
142
|
-
hits:
|
|
143
|
-
misses:
|
|
144
|
-
last_hit_at:
|
|
114
|
+
var UsageStatsSchema = z.object({
|
|
115
|
+
injections: z.number().int().default(0),
|
|
116
|
+
hits: z.number().int().default(0),
|
|
117
|
+
misses: z.number().int().default(0),
|
|
118
|
+
last_hit_at: z.string().optional()
|
|
145
119
|
});
|
|
146
|
-
var EpisodicFieldsSchema =
|
|
147
|
-
emotional_weight:
|
|
148
|
-
confidence:
|
|
149
|
-
trigger_context:
|
|
150
|
-
journal_ref:
|
|
120
|
+
var EpisodicFieldsSchema = z.object({
|
|
121
|
+
emotional_weight: z.number().int().min(1).max(10).default(5),
|
|
122
|
+
confidence: z.number().int().min(1).max(10).default(5),
|
|
123
|
+
trigger_context: z.string().optional(),
|
|
124
|
+
journal_ref: z.string().optional()
|
|
151
125
|
});
|
|
152
|
-
var ExchangeMetadataSchema =
|
|
153
|
-
fitness_score:
|
|
154
|
-
environmental_diversity:
|
|
155
|
-
adoption_count:
|
|
156
|
-
contradiction_rate:
|
|
126
|
+
var ExchangeMetadataSchema = z.object({
|
|
127
|
+
fitness_score: z.number().min(0).max(1).optional(),
|
|
128
|
+
environmental_diversity: z.number().int().default(0),
|
|
129
|
+
adoption_count: z.number().int().default(0),
|
|
130
|
+
contradiction_rate: z.number().min(0).max(1).default(0)
|
|
157
131
|
});
|
|
158
|
-
var EngramSchema =
|
|
132
|
+
var EngramSchema = z.object({
|
|
159
133
|
// Identity
|
|
160
|
-
id:
|
|
161
|
-
version:
|
|
162
|
-
status:
|
|
163
|
-
consolidated:
|
|
164
|
-
type:
|
|
165
|
-
scope:
|
|
166
|
-
visibility:
|
|
134
|
+
id: z.string().regex(/^(ENG|ABS|META)-[A-Za-z0-9-]+$/),
|
|
135
|
+
version: z.number().int().min(1).default(2),
|
|
136
|
+
status: z.enum(["active", "dormant", "retired", "candidate"]),
|
|
137
|
+
consolidated: z.boolean().default(false),
|
|
138
|
+
type: z.enum(["behavioral", "terminological", "procedural", "architectural"]),
|
|
139
|
+
scope: z.string(),
|
|
140
|
+
visibility: z.enum(["private", "public", "template"]).default("private"),
|
|
167
141
|
// Content
|
|
168
|
-
statement:
|
|
169
|
-
rationale:
|
|
170
|
-
contraindications:
|
|
142
|
+
statement: z.string().min(1),
|
|
143
|
+
rationale: z.string().optional(),
|
|
144
|
+
contraindications: z.array(z.string()).optional(),
|
|
171
145
|
// Lineage
|
|
172
|
-
source:
|
|
173
|
-
source_patterns:
|
|
174
|
-
derivation_count:
|
|
175
|
-
pack:
|
|
176
|
-
abstract:
|
|
177
|
-
derived_from:
|
|
146
|
+
source: z.string().optional(),
|
|
147
|
+
source_patterns: z.array(z.string()).optional(),
|
|
148
|
+
derivation_count: z.number().int().min(0).default(1),
|
|
149
|
+
pack: z.string().nullable().default(null),
|
|
150
|
+
abstract: z.string().nullable().default(null),
|
|
151
|
+
derived_from: z.string().nullable().default(null),
|
|
178
152
|
// Classification
|
|
179
153
|
knowledge_type: KnowledgeTypeSchema.optional(),
|
|
180
|
-
domain:
|
|
181
|
-
tags:
|
|
154
|
+
domain: z.string().optional(),
|
|
155
|
+
tags: z.array(z.string()).default([]),
|
|
182
156
|
// Activation (ACT-R model)
|
|
183
157
|
activation: ActivationSchema.default({
|
|
184
158
|
retrieval_strength: 0.7,
|
|
@@ -188,8 +162,8 @@ var EngramSchema = z2.object({
|
|
|
188
162
|
}),
|
|
189
163
|
// Relations & grounding
|
|
190
164
|
relations: RelationsSchema.optional(),
|
|
191
|
-
associations:
|
|
192
|
-
knowledge_anchors:
|
|
165
|
+
associations: z.array(AssociationSchema).default([]),
|
|
166
|
+
knowledge_anchors: z.array(KnowledgeAnchorSchema).default([]),
|
|
193
167
|
dual_coding: DualCodingSchema.optional(),
|
|
194
168
|
// Provenance
|
|
195
169
|
provenance: ProvenanceSchema.optional(),
|
|
@@ -197,7 +171,7 @@ var EngramSchema = z2.object({
|
|
|
197
171
|
feedback_signals: FeedbackSignalsSchema.default({ positive: 0, negative: 0, neutral: 0 }),
|
|
198
172
|
// === NEW OPTIONAL FIELDS (v2.1) ===
|
|
199
173
|
/** Typed entity references extracted from statement. Enables graph queries. */
|
|
200
|
-
entities:
|
|
174
|
+
entities: z.array(EntityRefSchema).optional(),
|
|
201
175
|
/** Temporal validity window. When is this knowledge true? */
|
|
202
176
|
temporal: TemporalSchema.optional(),
|
|
203
177
|
/** Automatic usage tracking. Injections, hits, misses. */
|
|
@@ -207,33 +181,33 @@ var EngramSchema = z2.object({
|
|
|
207
181
|
/** Exchange marketplace metadata: fitness, adoption, diversity. */
|
|
208
182
|
exchange: ExchangeMetadataSchema.optional(),
|
|
209
183
|
/** Extensible key-value data for domain-specific fields. */
|
|
210
|
-
structured_data:
|
|
184
|
+
structured_data: z.record(z.string(), z.unknown()).optional(),
|
|
211
185
|
/** Polarity classification: 'do' for directives, 'dont' for prohibitions, null for unclassified. */
|
|
212
|
-
polarity:
|
|
186
|
+
polarity: z.enum(["do", "dont"]).nullable().default(null)
|
|
213
187
|
});
|
|
214
188
|
|
|
215
189
|
// src/schemas/pack.ts
|
|
216
|
-
import { z as
|
|
217
|
-
var PackManifestSchema =
|
|
218
|
-
name:
|
|
219
|
-
version:
|
|
220
|
-
description:
|
|
221
|
-
creator:
|
|
222
|
-
license:
|
|
223
|
-
tags:
|
|
224
|
-
metadata:
|
|
225
|
-
id:
|
|
226
|
-
injection_policy:
|
|
227
|
-
match_terms:
|
|
228
|
-
domain:
|
|
229
|
-
engram_count:
|
|
190
|
+
import { z as z2 } from "zod";
|
|
191
|
+
var PackManifestSchema = z2.object({
|
|
192
|
+
name: z2.string(),
|
|
193
|
+
version: z2.string(),
|
|
194
|
+
description: z2.string().optional(),
|
|
195
|
+
creator: z2.string().optional(),
|
|
196
|
+
license: z2.string().default("cc-by-sa-4.0"),
|
|
197
|
+
tags: z2.array(z2.string()).default([]),
|
|
198
|
+
metadata: z2.object({
|
|
199
|
+
id: z2.string().optional(),
|
|
200
|
+
injection_policy: z2.enum(["on_match", "on_request", "always"]).default("on_match"),
|
|
201
|
+
match_terms: z2.array(z2.string()).default([]),
|
|
202
|
+
domain: z2.string().optional(),
|
|
203
|
+
engram_count: z2.number().optional()
|
|
230
204
|
}).optional(),
|
|
231
|
-
"x-datacore":
|
|
232
|
-
id:
|
|
233
|
-
injection_policy:
|
|
234
|
-
match_terms:
|
|
235
|
-
domain:
|
|
236
|
-
engram_count:
|
|
205
|
+
"x-datacore": z2.object({
|
|
206
|
+
id: z2.string(),
|
|
207
|
+
injection_policy: z2.enum(["on_match", "on_request"]),
|
|
208
|
+
match_terms: z2.array(z2.string()).default([]),
|
|
209
|
+
domain: z2.string().optional(),
|
|
210
|
+
engram_count: z2.number().int().min(0)
|
|
237
211
|
}).optional()
|
|
238
212
|
});
|
|
239
213
|
|
|
@@ -260,7 +234,7 @@ var logger = {
|
|
|
260
234
|
function loadEngrams(filePath) {
|
|
261
235
|
if (!fs.existsSync(filePath)) return [];
|
|
262
236
|
try {
|
|
263
|
-
const raw =
|
|
237
|
+
const raw = yaml.load(fs.readFileSync(filePath, "utf8"));
|
|
264
238
|
if (!raw?.engrams || !Array.isArray(raw.engrams)) return [];
|
|
265
239
|
const valid = [];
|
|
266
240
|
let skipped = 0;
|
|
@@ -277,14 +251,14 @@ function loadEngrams(filePath) {
|
|
|
277
251
|
}
|
|
278
252
|
}
|
|
279
253
|
function saveEngrams(filePath, engrams) {
|
|
280
|
-
const content =
|
|
254
|
+
const content = yaml.dump({ engrams }, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
281
255
|
atomicWrite(filePath, content);
|
|
282
256
|
}
|
|
283
257
|
function parseSkillMdFrontmatter(filePath) {
|
|
284
258
|
const content = fs.readFileSync(filePath, "utf8");
|
|
285
259
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
286
260
|
if (!match) throw new Error(`No frontmatter found in ${filePath}`);
|
|
287
|
-
return
|
|
261
|
+
return yaml.load(match[1]);
|
|
288
262
|
}
|
|
289
263
|
function loadPack(packDir) {
|
|
290
264
|
const skillMdPath = `${packDir}/SKILL.md`;
|
|
@@ -294,7 +268,7 @@ function loadPack(packDir) {
|
|
|
294
268
|
if (fs.existsSync(skillMdPath)) {
|
|
295
269
|
rawManifest = parseSkillMdFrontmatter(skillMdPath);
|
|
296
270
|
} else if (fs.existsSync(manifestYamlPath)) {
|
|
297
|
-
rawManifest =
|
|
271
|
+
rawManifest = yaml.load(fs.readFileSync(manifestYamlPath, "utf8"));
|
|
298
272
|
} else {
|
|
299
273
|
throw new Error(`No SKILL.md or manifest.yaml found in ${packDir}`);
|
|
300
274
|
}
|
|
@@ -326,6 +300,166 @@ function generateEngramId(existing) {
|
|
|
326
300
|
return `${prefix}${String(next).padStart(3, "0")}`;
|
|
327
301
|
}
|
|
328
302
|
|
|
303
|
+
// src/storage-indexed.ts
|
|
304
|
+
var require2 = createRequire(import.meta.url);
|
|
305
|
+
var Database = null;
|
|
306
|
+
function getDatabase() {
|
|
307
|
+
if (!Database) {
|
|
308
|
+
try {
|
|
309
|
+
Database = require2("better-sqlite3");
|
|
310
|
+
} catch {
|
|
311
|
+
throw new Error(
|
|
312
|
+
"better-sqlite3 is required for index: true. Install it with: npm install better-sqlite3"
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return Database;
|
|
317
|
+
}
|
|
318
|
+
var IndexedStorage = class {
|
|
319
|
+
dbPath;
|
|
320
|
+
engramsPath;
|
|
321
|
+
db = null;
|
|
322
|
+
constructor(engramsPath, dbPath) {
|
|
323
|
+
this.engramsPath = engramsPath;
|
|
324
|
+
this.dbPath = dbPath;
|
|
325
|
+
}
|
|
326
|
+
getDb() {
|
|
327
|
+
if (!this.db) {
|
|
328
|
+
const DB = getDatabase();
|
|
329
|
+
this.db = new DB(this.dbPath);
|
|
330
|
+
this.db.pragma("journal_mode = WAL");
|
|
331
|
+
this.db.exec(`
|
|
332
|
+
CREATE TABLE IF NOT EXISTS engrams (
|
|
333
|
+
id TEXT PRIMARY KEY,
|
|
334
|
+
status TEXT NOT NULL,
|
|
335
|
+
scope TEXT NOT NULL,
|
|
336
|
+
domain TEXT,
|
|
337
|
+
last_accessed TEXT,
|
|
338
|
+
data TEXT NOT NULL
|
|
339
|
+
);
|
|
340
|
+
CREATE INDEX IF NOT EXISTS idx_status ON engrams(status);
|
|
341
|
+
CREATE INDEX IF NOT EXISTS idx_scope ON engrams(scope);
|
|
342
|
+
CREATE INDEX IF NOT EXISTS idx_domain ON engrams(domain);
|
|
343
|
+
`);
|
|
344
|
+
}
|
|
345
|
+
return this.db;
|
|
346
|
+
}
|
|
347
|
+
/** Load all engrams from SQLite index. Auto-rebuilds if db missing. */
|
|
348
|
+
loadAll() {
|
|
349
|
+
if (!existsSync3(this.dbPath)) {
|
|
350
|
+
this.reindex();
|
|
351
|
+
}
|
|
352
|
+
const db = this.getDb();
|
|
353
|
+
const rows = db.prepare("SELECT data FROM engrams").all();
|
|
354
|
+
return rows.map((r) => JSON.parse(r.data));
|
|
355
|
+
}
|
|
356
|
+
/** Load engrams with SQL-level filtering. */
|
|
357
|
+
loadFiltered(filter) {
|
|
358
|
+
if (!existsSync3(this.dbPath)) {
|
|
359
|
+
this.reindex();
|
|
360
|
+
}
|
|
361
|
+
const db = this.getDb();
|
|
362
|
+
const conditions = [];
|
|
363
|
+
const params = [];
|
|
364
|
+
if (filter.status) {
|
|
365
|
+
conditions.push("status = ?");
|
|
366
|
+
params.push(filter.status);
|
|
367
|
+
}
|
|
368
|
+
if (filter.scope) {
|
|
369
|
+
conditions.push("(scope = 'global' OR scope = ? OR scope LIKE ? || '%')");
|
|
370
|
+
params.push(filter.scope, filter.scope);
|
|
371
|
+
}
|
|
372
|
+
if (filter.domain) {
|
|
373
|
+
conditions.push("domain LIKE ? || '%'");
|
|
374
|
+
params.push(filter.domain);
|
|
375
|
+
}
|
|
376
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
377
|
+
const rows = db.prepare(`SELECT data FROM engrams ${where}`).all(...params);
|
|
378
|
+
return rows.map((r) => JSON.parse(r.data));
|
|
379
|
+
}
|
|
380
|
+
/** Count engrams with optional status filter. */
|
|
381
|
+
count(filter) {
|
|
382
|
+
if (!existsSync3(this.dbPath)) {
|
|
383
|
+
this.reindex();
|
|
384
|
+
}
|
|
385
|
+
const db = this.getDb();
|
|
386
|
+
if (filter?.status) {
|
|
387
|
+
return db.prepare("SELECT COUNT(*) as c FROM engrams WHERE status = ?").get(filter.status).c;
|
|
388
|
+
}
|
|
389
|
+
return db.prepare("SELECT COUNT(*) as c FROM engrams").get().c;
|
|
390
|
+
}
|
|
391
|
+
/** Sync SQLite index from YAML source of truth. */
|
|
392
|
+
syncFromYaml() {
|
|
393
|
+
const engrams = loadEngrams(this.engramsPath);
|
|
394
|
+
const db = this.getDb();
|
|
395
|
+
const upsert = db.prepare(`
|
|
396
|
+
INSERT OR REPLACE INTO engrams (id, status, scope, domain, last_accessed, data)
|
|
397
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
398
|
+
`);
|
|
399
|
+
const deleteStmt = db.prepare("DELETE FROM engrams WHERE id = ?");
|
|
400
|
+
const yamlIds = new Set(engrams.map((e) => e.id));
|
|
401
|
+
const dbIds = new Set(
|
|
402
|
+
db.prepare("SELECT id FROM engrams").all().map((r) => r.id)
|
|
403
|
+
);
|
|
404
|
+
const tx = db.transaction(() => {
|
|
405
|
+
for (const e of engrams) {
|
|
406
|
+
upsert.run(e.id, e.status, e.scope, e.domain ?? null, e.activation.last_accessed, JSON.stringify(e));
|
|
407
|
+
}
|
|
408
|
+
for (const id of dbIds) {
|
|
409
|
+
if (!yamlIds.has(id)) deleteStmt.run(id);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
tx();
|
|
413
|
+
}
|
|
414
|
+
/** Drop and rebuild the entire index from YAML. */
|
|
415
|
+
reindex() {
|
|
416
|
+
this.close();
|
|
417
|
+
const db = this.getDb();
|
|
418
|
+
db.exec("DELETE FROM engrams");
|
|
419
|
+
this.syncFromYaml();
|
|
420
|
+
}
|
|
421
|
+
/** Close the database connection. */
|
|
422
|
+
close() {
|
|
423
|
+
if (this.db) {
|
|
424
|
+
this.db.close();
|
|
425
|
+
this.db = null;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/config.ts
|
|
431
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
432
|
+
import yaml2 from "js-yaml";
|
|
433
|
+
|
|
434
|
+
// src/schemas/config.ts
|
|
435
|
+
import { z as z3 } from "zod";
|
|
436
|
+
var PlurConfigSchema = z3.object({
|
|
437
|
+
auto_learn: z3.boolean().default(true),
|
|
438
|
+
auto_capture: z3.boolean().default(true),
|
|
439
|
+
injection_budget: z3.number().default(2e3),
|
|
440
|
+
decay_enabled: z3.boolean().default(true),
|
|
441
|
+
decay_threshold: z3.number().default(0.15),
|
|
442
|
+
packs: z3.array(z3.string()).default([]),
|
|
443
|
+
injection: z3.object({
|
|
444
|
+
spread_cap: z3.number().default(3),
|
|
445
|
+
spread_budget: z3.number().default(480),
|
|
446
|
+
co_access: z3.boolean().default(true)
|
|
447
|
+
}).default({}),
|
|
448
|
+
allow_secrets: z3.boolean().default(false),
|
|
449
|
+
index: z3.boolean().default(false)
|
|
450
|
+
}).partial();
|
|
451
|
+
|
|
452
|
+
// src/config.ts
|
|
453
|
+
function loadConfig(configPath) {
|
|
454
|
+
if (!existsSync4(configPath)) return PlurConfigSchema.parse({});
|
|
455
|
+
try {
|
|
456
|
+
const raw = yaml2.load(readFileSync2(configPath, "utf8"));
|
|
457
|
+
return PlurConfigSchema.parse(raw ?? {});
|
|
458
|
+
} catch {
|
|
459
|
+
return PlurConfigSchema.parse({});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
329
463
|
// src/decay.ts
|
|
330
464
|
var DECAY_RATE = 0.05;
|
|
331
465
|
var FLOOR = 0.05;
|
|
@@ -653,7 +787,7 @@ function selectAndSpread(ctx, personalEngrams, packs, config, embeddingBoosts) {
|
|
|
653
787
|
}
|
|
654
788
|
|
|
655
789
|
// src/episodes.ts
|
|
656
|
-
import { existsSync as
|
|
790
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
657
791
|
import yaml3 from "js-yaml";
|
|
658
792
|
function generateEpisodeId() {
|
|
659
793
|
const ts = Date.now();
|
|
@@ -688,7 +822,7 @@ function queryTimeline(path2, query) {
|
|
|
688
822
|
return episodes;
|
|
689
823
|
}
|
|
690
824
|
function loadEpisodes(path2) {
|
|
691
|
-
if (!
|
|
825
|
+
if (!existsSync5(path2)) return [];
|
|
692
826
|
try {
|
|
693
827
|
const raw = yaml3.load(readFileSync3(path2, "utf8"));
|
|
694
828
|
return Array.isArray(raw) ? raw : [];
|
|
@@ -1097,7 +1231,7 @@ async function computeSimilarityMatrix(templates) {
|
|
|
1097
1231
|
const n = templates.length;
|
|
1098
1232
|
const matrix = Array.from({ length: n }, () => Array(n).fill(0));
|
|
1099
1233
|
try {
|
|
1100
|
-
const { embed, cosineSimilarity } = await import("./embeddings-
|
|
1234
|
+
const { embed, cosineSimilarity } = await import("./embeddings-2IODIQAF.js");
|
|
1101
1235
|
const embeddings = [];
|
|
1102
1236
|
for (const t of templates) {
|
|
1103
1237
|
embeddings.push(await embed(t));
|
|
@@ -1832,9 +1966,13 @@ var INGEST_PATTERNS = [
|
|
|
1832
1966
|
var Plur = class {
|
|
1833
1967
|
paths;
|
|
1834
1968
|
config;
|
|
1969
|
+
indexedStorage = null;
|
|
1835
1970
|
constructor(options) {
|
|
1836
1971
|
this.paths = detectPlurStorage(options?.path);
|
|
1837
1972
|
this.config = loadConfig(this.paths.config);
|
|
1973
|
+
if (this.config.index) {
|
|
1974
|
+
this.indexedStorage = new IndexedStorage(this.paths.engrams, this.paths.db);
|
|
1975
|
+
}
|
|
1838
1976
|
}
|
|
1839
1977
|
/** Create engram, detect conflicts, save. Returns the created engram. */
|
|
1840
1978
|
learn(statement, context) {
|
|
@@ -1844,48 +1982,51 @@ var Plur = class {
|
|
|
1844
1982
|
throw new Error(`Secret detected in statement: ${secrets[0].pattern}. Use config.allow_secrets to override.`);
|
|
1845
1983
|
}
|
|
1846
1984
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1985
|
+
return withLock(this.paths.engrams, () => {
|
|
1986
|
+
const engrams = loadEngrams(this.paths.engrams);
|
|
1987
|
+
const id = generateEngramId(engrams);
|
|
1988
|
+
const scope = context?.scope ?? "global";
|
|
1989
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1990
|
+
const conflictingEngrams = detectConflicts({ statement, scope }, engrams);
|
|
1991
|
+
const conflictIds = conflictingEngrams.map((e) => e.id);
|
|
1992
|
+
const engram = {
|
|
1993
|
+
id,
|
|
1994
|
+
version: 2,
|
|
1995
|
+
status: "active",
|
|
1996
|
+
consolidated: false,
|
|
1997
|
+
type: context?.type ?? "behavioral",
|
|
1998
|
+
scope,
|
|
1999
|
+
visibility: "private",
|
|
2000
|
+
statement,
|
|
2001
|
+
source: context?.source,
|
|
2002
|
+
domain: context?.domain,
|
|
2003
|
+
activation: {
|
|
2004
|
+
retrieval_strength: 0.7,
|
|
2005
|
+
storage_strength: 1,
|
|
2006
|
+
frequency: 0,
|
|
2007
|
+
last_accessed: now.slice(0, 10)
|
|
2008
|
+
},
|
|
2009
|
+
feedback_signals: { positive: 0, negative: 0, neutral: 0 },
|
|
2010
|
+
knowledge_anchors: [],
|
|
2011
|
+
associations: [],
|
|
2012
|
+
derivation_count: 1,
|
|
2013
|
+
tags: [],
|
|
2014
|
+
pack: null,
|
|
2015
|
+
abstract: null,
|
|
2016
|
+
derived_from: null,
|
|
2017
|
+
polarity: null,
|
|
2018
|
+
relations: conflictIds.length > 0 ? {
|
|
2019
|
+
broader: [],
|
|
2020
|
+
narrower: [],
|
|
2021
|
+
related: [],
|
|
2022
|
+
conflicts: conflictIds
|
|
2023
|
+
} : void 0
|
|
2024
|
+
};
|
|
2025
|
+
engrams.push(engram);
|
|
2026
|
+
saveEngrams(this.paths.engrams, engrams);
|
|
2027
|
+
this._syncIndex();
|
|
2028
|
+
return engram;
|
|
2029
|
+
});
|
|
1889
2030
|
}
|
|
1890
2031
|
/**
|
|
1891
2032
|
* Search engrams, filter by scope/domain/strength, reactivate accessed.
|
|
@@ -1939,75 +2080,89 @@ var Plur = class {
|
|
|
1939
2080
|
}
|
|
1940
2081
|
/** Filter engrams by scope/domain/strength (shared by both modes) */
|
|
1941
2082
|
_filterEngrams(options) {
|
|
1942
|
-
let engrams
|
|
1943
|
-
|
|
2083
|
+
let engrams;
|
|
2084
|
+
if (this.indexedStorage) {
|
|
2085
|
+
engrams = this.indexedStorage.loadFiltered({
|
|
2086
|
+
status: "active",
|
|
2087
|
+
scope: options?.scope,
|
|
2088
|
+
domain: options?.domain
|
|
2089
|
+
});
|
|
2090
|
+
} else {
|
|
2091
|
+
engrams = loadEngrams(this.paths.engrams);
|
|
2092
|
+
engrams = engrams.filter((e) => e.status === "active");
|
|
2093
|
+
if (options?.domain) {
|
|
2094
|
+
engrams = engrams.filter((e) => e.domain?.startsWith(options.domain));
|
|
2095
|
+
}
|
|
2096
|
+
if (options?.scope) {
|
|
2097
|
+
const scope = options.scope;
|
|
2098
|
+
engrams = engrams.filter(
|
|
2099
|
+
(e) => e.scope === "global" || e.scope === scope || e.scope.startsWith(scope)
|
|
2100
|
+
);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
1944
2103
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1945
2104
|
engrams = engrams.filter((e) => {
|
|
1946
2105
|
if (e.temporal?.valid_until && e.temporal.valid_until < today) return false;
|
|
1947
2106
|
if (e.temporal?.valid_from && e.temporal.valid_from > today) return false;
|
|
1948
2107
|
return true;
|
|
1949
2108
|
});
|
|
1950
|
-
if (options?.domain) {
|
|
1951
|
-
engrams = engrams.filter((e) => e.domain?.startsWith(options.domain));
|
|
1952
|
-
}
|
|
1953
2109
|
if (options?.min_strength !== void 0) {
|
|
1954
2110
|
engrams = engrams.filter((e) => e.activation.retrieval_strength >= options.min_strength);
|
|
1955
2111
|
}
|
|
1956
|
-
if (options?.scope) {
|
|
1957
|
-
const scope = options.scope;
|
|
1958
|
-
engrams = engrams.filter(
|
|
1959
|
-
(e) => e.scope === "global" || e.scope === scope || e.scope.startsWith(scope)
|
|
1960
|
-
);
|
|
1961
|
-
}
|
|
1962
2112
|
return engrams;
|
|
1963
2113
|
}
|
|
1964
2114
|
/** Reactivate accessed engrams and update co-access associations */
|
|
1965
2115
|
_reactivateResults(results) {
|
|
1966
2116
|
if (results.length === 0) return;
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2117
|
+
withLock(this.paths.engrams, () => {
|
|
2118
|
+
const allEngrams = loadEngrams(this.paths.engrams);
|
|
2119
|
+
const resultIds = new Set(results.map((e) => e.id));
|
|
2120
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2121
|
+
let modified = false;
|
|
2122
|
+
for (const e of allEngrams) {
|
|
2123
|
+
if (resultIds.has(e.id)) {
|
|
2124
|
+
e.activation.retrieval_strength = reactivate(e.activation.retrieval_strength);
|
|
2125
|
+
e.activation.last_accessed = today;
|
|
2126
|
+
e.activation.frequency += 1;
|
|
2127
|
+
modified = true;
|
|
2128
|
+
}
|
|
1977
2129
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
existing.updated_at = today;
|
|
1993
|
-
modified = true;
|
|
1994
|
-
} else {
|
|
1995
|
-
const coAccessCount = source.associations.filter((a) => a.type === "co_accessed").length;
|
|
1996
|
-
if (coAccessCount < 5) {
|
|
1997
|
-
source.associations.push({
|
|
1998
|
-
target_type: "engram",
|
|
1999
|
-
target: targetId,
|
|
2000
|
-
type: "co_accessed",
|
|
2001
|
-
strength: 0.3,
|
|
2002
|
-
updated_at: today
|
|
2003
|
-
});
|
|
2130
|
+
if (results.length >= 2 && this.config.injection?.co_access !== false) {
|
|
2131
|
+
const topHalf = results.slice(0, Math.max(2, Math.ceil(results.length / 2)));
|
|
2132
|
+
const topIds = topHalf.map((e) => e.id);
|
|
2133
|
+
for (const sourceId of topIds) {
|
|
2134
|
+
const source = allEngrams.find((e) => e.id === sourceId);
|
|
2135
|
+
if (!source) continue;
|
|
2136
|
+
for (const targetId of topIds) {
|
|
2137
|
+
if (targetId === sourceId) continue;
|
|
2138
|
+
const existing = source.associations.find(
|
|
2139
|
+
(a) => a.type === "co_accessed" && a.target === targetId
|
|
2140
|
+
);
|
|
2141
|
+
if (existing) {
|
|
2142
|
+
existing.strength = Math.min(0.95, existing.strength + 0.05);
|
|
2143
|
+
existing.updated_at = today;
|
|
2004
2144
|
modified = true;
|
|
2145
|
+
} else {
|
|
2146
|
+
const coAccessCount = source.associations.filter((a) => a.type === "co_accessed").length;
|
|
2147
|
+
if (coAccessCount < 5) {
|
|
2148
|
+
source.associations.push({
|
|
2149
|
+
target_type: "engram",
|
|
2150
|
+
target: targetId,
|
|
2151
|
+
type: "co_accessed",
|
|
2152
|
+
strength: 0.3,
|
|
2153
|
+
updated_at: today
|
|
2154
|
+
});
|
|
2155
|
+
modified = true;
|
|
2156
|
+
}
|
|
2005
2157
|
}
|
|
2006
2158
|
}
|
|
2007
2159
|
}
|
|
2008
2160
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2161
|
+
if (modified) {
|
|
2162
|
+
saveEngrams(this.paths.engrams, allEngrams);
|
|
2163
|
+
this._syncIndex();
|
|
2164
|
+
}
|
|
2165
|
+
});
|
|
2011
2166
|
}
|
|
2012
2167
|
/** Scored injection within token budget (BM25 only). Returns formatted strings. */
|
|
2013
2168
|
inject(task, options) {
|
|
@@ -2066,56 +2221,96 @@ var Plur = class {
|
|
|
2066
2221
|
}
|
|
2067
2222
|
/** Update feedback_signals and adjust retrieval_strength. */
|
|
2068
2223
|
feedback(id, signal) {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
engram.feedback_signals
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2224
|
+
withLock(this.paths.engrams, () => {
|
|
2225
|
+
const engrams = loadEngrams(this.paths.engrams);
|
|
2226
|
+
const engram = engrams.find((e) => e.id === id);
|
|
2227
|
+
if (!engram) throw new Error(`Engram not found: ${id}`);
|
|
2228
|
+
if (!engram.feedback_signals) {
|
|
2229
|
+
engram.feedback_signals = { positive: 0, negative: 0, neutral: 0 };
|
|
2230
|
+
}
|
|
2231
|
+
engram.feedback_signals[signal] += 1;
|
|
2232
|
+
if (signal === "positive") {
|
|
2233
|
+
engram.activation.retrieval_strength = Math.min(1, engram.activation.retrieval_strength + 0.05);
|
|
2234
|
+
} else if (signal === "negative") {
|
|
2235
|
+
engram.activation.retrieval_strength = Math.max(0, engram.activation.retrieval_strength - 0.1);
|
|
2236
|
+
}
|
|
2237
|
+
saveEngrams(this.paths.engrams, engrams);
|
|
2238
|
+
this._syncIndex();
|
|
2239
|
+
});
|
|
2082
2240
|
}
|
|
2083
2241
|
/** Save extracted meta-engrams to the engram store. Skips IDs that already exist. */
|
|
2084
2242
|
saveMetaEngrams(metas) {
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2243
|
+
return withLock(this.paths.engrams, () => {
|
|
2244
|
+
const engrams = loadEngrams(this.paths.engrams);
|
|
2245
|
+
const existingIds = new Set(engrams.map((e) => e.id));
|
|
2246
|
+
let saved = 0;
|
|
2247
|
+
let skipped = 0;
|
|
2248
|
+
for (const meta of metas) {
|
|
2249
|
+
if (existingIds.has(meta.id)) {
|
|
2250
|
+
skipped++;
|
|
2251
|
+
} else {
|
|
2252
|
+
engrams.push(meta);
|
|
2253
|
+
saved++;
|
|
2254
|
+
}
|
|
2095
2255
|
}
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2256
|
+
if (saved > 0) {
|
|
2257
|
+
saveEngrams(this.paths.engrams, engrams);
|
|
2258
|
+
this._syncIndex();
|
|
2259
|
+
}
|
|
2260
|
+
return { saved, skipped };
|
|
2261
|
+
});
|
|
2099
2262
|
}
|
|
2100
2263
|
/** Update an existing engram in the store by ID. Returns true if found and updated. */
|
|
2101
2264
|
updateEngram(updated) {
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2265
|
+
return withLock(this.paths.engrams, () => {
|
|
2266
|
+
const engrams = loadEngrams(this.paths.engrams);
|
|
2267
|
+
const idx = engrams.findIndex((e) => e.id === updated.id);
|
|
2268
|
+
if (idx === -1) return false;
|
|
2269
|
+
engrams[idx] = updated;
|
|
2270
|
+
saveEngrams(this.paths.engrams, engrams);
|
|
2271
|
+
this._syncIndex();
|
|
2272
|
+
return true;
|
|
2273
|
+
});
|
|
2108
2274
|
}
|
|
2109
2275
|
/** Set engram status to 'retired'. */
|
|
2110
2276
|
forget(id, reason) {
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
engram.rationale
|
|
2277
|
+
withLock(this.paths.engrams, () => {
|
|
2278
|
+
const engrams = loadEngrams(this.paths.engrams);
|
|
2279
|
+
const engram = engrams.find((e) => e.id === id);
|
|
2280
|
+
if (!engram) throw new Error(`Engram not found: ${id}`);
|
|
2281
|
+
engram.status = "retired";
|
|
2282
|
+
if (reason && !engram.rationale) {
|
|
2283
|
+
engram.rationale = `Retired: ${reason}`;
|
|
2284
|
+
}
|
|
2285
|
+
saveEngrams(this.paths.engrams, engrams);
|
|
2286
|
+
this._syncIndex();
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2289
|
+
/** Remove retired engrams from storage. Returns count of removed and remaining. */
|
|
2290
|
+
compact() {
|
|
2291
|
+
return withLock(this.paths.engrams, () => {
|
|
2292
|
+
const engrams = loadEngrams(this.paths.engrams);
|
|
2293
|
+
const active = engrams.filter((e) => e.status !== "retired");
|
|
2294
|
+
const removed = engrams.length - active.length;
|
|
2295
|
+
if (removed > 0) {
|
|
2296
|
+
saveEngrams(this.paths.engrams, active);
|
|
2297
|
+
this._syncIndex();
|
|
2298
|
+
}
|
|
2299
|
+
return { removed, remaining: active.length };
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
/** Rebuild SQLite index from YAML source of truth. Only works when index: true. */
|
|
2303
|
+
reindex() {
|
|
2304
|
+
if (!this.indexedStorage) {
|
|
2305
|
+
this.indexedStorage = new IndexedStorage(this.paths.engrams, this.paths.db);
|
|
2306
|
+
}
|
|
2307
|
+
this.indexedStorage.reindex();
|
|
2308
|
+
}
|
|
2309
|
+
/** Sync SQLite index after YAML write (no-op if index disabled) */
|
|
2310
|
+
_syncIndex() {
|
|
2311
|
+
if (this.indexedStorage) {
|
|
2312
|
+
this.indexedStorage.syncFromYaml();
|
|
2117
2313
|
}
|
|
2118
|
-
saveEngrams(this.paths.engrams, engrams);
|
|
2119
2314
|
}
|
|
2120
2315
|
/** Capture an episodic memory. */
|
|
2121
2316
|
capture(summary, context) {
|
|
@@ -2196,6 +2391,7 @@ export {
|
|
|
2196
2391
|
EvidenceEntrySchema,
|
|
2197
2392
|
FalsificationSchema,
|
|
2198
2393
|
HierarchyPositionSchema,
|
|
2394
|
+
IndexedStorage,
|
|
2199
2395
|
MetaConfidenceSchema,
|
|
2200
2396
|
MetaFieldSchema,
|
|
2201
2397
|
PLATITUDE_PATTERNS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plur-ai/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
"zod": "^3.23.0"
|
|
13
13
|
},
|
|
14
14
|
"optionalDependencies": {
|
|
15
|
-
"@huggingface/transformers": "^3.8.1"
|
|
15
|
+
"@huggingface/transformers": "^3.8.1",
|
|
16
|
+
"better-sqlite3": "^11.0.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
19
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
18
20
|
"@types/js-yaml": "^4.0.0",
|
|
19
21
|
"@types/node": "^25.5.0"
|
|
20
22
|
},
|