@plur-ai/core 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +259 -104
  2. package/dist/index.js +317 -14
  3. package/package.json +2 -1
package/dist/index.d.ts CHANGED
@@ -48,6 +48,9 @@ declare const EngramSchema: z.ZodObject<{
48
48
  contraindications: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
49
49
  source_patterns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
50
50
  derivation_count: z.ZodDefault<z.ZodNumber>;
51
+ pack: z.ZodDefault<z.ZodNullable<z.ZodString>>;
52
+ abstract: z.ZodDefault<z.ZodNullable<z.ZodString>>;
53
+ derived_from: z.ZodDefault<z.ZodNullable<z.ZodString>>;
51
54
  knowledge_type: z.ZodOptional<z.ZodObject<{
52
55
  memory_class: z.ZodEnum<["semantic", "episodic", "procedural", "metacognitive"]>;
53
56
  cognitive_level: z.ZodEnum<["remember", "understand", "apply", "analyze", "evaluate", "create"]>;
@@ -59,6 +62,23 @@ declare const EngramSchema: z.ZodObject<{
59
62
  cognitive_level: "remember" | "understand" | "apply" | "analyze" | "evaluate" | "create";
60
63
  }>>;
61
64
  domain: z.ZodOptional<z.ZodString>;
65
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
66
+ activation: z.ZodDefault<z.ZodObject<{
67
+ retrieval_strength: z.ZodNumber;
68
+ storage_strength: z.ZodNumber;
69
+ frequency: z.ZodNumber;
70
+ last_accessed: z.ZodString;
71
+ }, "strip", z.ZodTypeAny, {
72
+ retrieval_strength: number;
73
+ storage_strength: number;
74
+ frequency: number;
75
+ last_accessed: string;
76
+ }, {
77
+ retrieval_strength: number;
78
+ storage_strength: number;
79
+ frequency: number;
80
+ last_accessed: string;
81
+ }>>;
62
82
  relations: z.ZodOptional<z.ZodObject<{
63
83
  broader: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
64
84
  narrower: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
@@ -75,21 +95,56 @@ declare const EngramSchema: z.ZodObject<{
75
95
  related?: string[] | undefined;
76
96
  conflicts?: string[] | undefined;
77
97
  }>>;
78
- activation: z.ZodDefault<z.ZodObject<{
79
- retrieval_strength: z.ZodNumber;
80
- storage_strength: z.ZodNumber;
81
- frequency: z.ZodNumber;
82
- last_accessed: z.ZodString;
98
+ associations: z.ZodDefault<z.ZodArray<z.ZodObject<{
99
+ target_type: z.ZodEnum<["engram", "document"]>;
100
+ target: z.ZodString;
101
+ strength: z.ZodNumber;
102
+ type: z.ZodEnum<["semantic", "temporal", "causal", "co_accessed"]>;
103
+ updated_at: z.ZodOptional<z.ZodString>;
83
104
  }, "strip", z.ZodTypeAny, {
84
- retrieval_strength: number;
85
- storage_strength: number;
86
- frequency: number;
87
- last_accessed: string;
105
+ type: "semantic" | "temporal" | "causal" | "co_accessed";
106
+ target_type: "engram" | "document";
107
+ target: string;
108
+ strength: number;
109
+ updated_at?: string | undefined;
88
110
  }, {
89
- retrieval_strength: number;
90
- storage_strength: number;
91
- frequency: number;
92
- last_accessed: string;
111
+ type: "semantic" | "temporal" | "causal" | "co_accessed";
112
+ target_type: "engram" | "document";
113
+ target: string;
114
+ strength: number;
115
+ updated_at?: string | undefined;
116
+ }>, "many">>;
117
+ knowledge_anchors: z.ZodDefault<z.ZodArray<z.ZodObject<{
118
+ path: z.ZodString;
119
+ relevance: z.ZodDefault<z.ZodEnum<["primary", "supporting", "example"]>>;
120
+ snippet: z.ZodOptional<z.ZodString>;
121
+ snippet_extracted_at: z.ZodOptional<z.ZodString>;
122
+ }, "strip", z.ZodTypeAny, {
123
+ path: string;
124
+ relevance: "primary" | "supporting" | "example";
125
+ snippet?: string | undefined;
126
+ snippet_extracted_at?: string | undefined;
127
+ }, {
128
+ path: string;
129
+ relevance?: "primary" | "supporting" | "example" | undefined;
130
+ snippet?: string | undefined;
131
+ snippet_extracted_at?: string | undefined;
132
+ }>, "many">>;
133
+ dual_coding: z.ZodOptional<z.ZodEffects<z.ZodObject<{
134
+ example: z.ZodOptional<z.ZodString>;
135
+ analogy: z.ZodOptional<z.ZodString>;
136
+ }, "strip", z.ZodTypeAny, {
137
+ example?: string | undefined;
138
+ analogy?: string | undefined;
139
+ }, {
140
+ example?: string | undefined;
141
+ analogy?: string | undefined;
142
+ }>, {
143
+ example?: string | undefined;
144
+ analogy?: string | undefined;
145
+ }, {
146
+ example?: string | undefined;
147
+ analogy?: string | undefined;
93
148
  }>>;
94
149
  provenance: z.ZodOptional<z.ZodObject<{
95
150
  origin: z.ZodString;
@@ -120,61 +175,90 @@ declare const EngramSchema: z.ZodObject<{
120
175
  negative?: number | undefined;
121
176
  neutral?: number | undefined;
122
177
  }>>;
123
- knowledge_anchors: z.ZodDefault<z.ZodArray<z.ZodObject<{
124
- path: z.ZodString;
125
- relevance: z.ZodDefault<z.ZodEnum<["primary", "supporting", "example"]>>;
126
- snippet: z.ZodOptional<z.ZodString>;
127
- snippet_extracted_at: z.ZodOptional<z.ZodString>;
178
+ /** Typed entity references extracted from statement. Enables graph queries. */
179
+ entities: z.ZodOptional<z.ZodArray<z.ZodObject<{
180
+ name: z.ZodString;
181
+ type: z.ZodEnum<["person", "organization", "technology", "concept", "project", "tool", "place", "event", "standard", "other"]>;
182
+ uri: z.ZodOptional<z.ZodString>;
128
183
  }, "strip", z.ZodTypeAny, {
129
- path: string;
130
- relevance: "primary" | "supporting" | "example";
131
- snippet?: string | undefined;
132
- snippet_extracted_at?: string | undefined;
184
+ type: "person" | "organization" | "technology" | "concept" | "project" | "tool" | "place" | "event" | "standard" | "other";
185
+ name: string;
186
+ uri?: string | undefined;
133
187
  }, {
134
- path: string;
135
- relevance?: "primary" | "supporting" | "example" | undefined;
136
- snippet?: string | undefined;
137
- snippet_extracted_at?: string | undefined;
188
+ type: "person" | "organization" | "technology" | "concept" | "project" | "tool" | "place" | "event" | "standard" | "other";
189
+ name: string;
190
+ uri?: string | undefined;
138
191
  }>, "many">>;
139
- associations: z.ZodDefault<z.ZodArray<z.ZodObject<{
140
- target_type: z.ZodEnum<["engram", "document"]>;
141
- target: z.ZodString;
142
- strength: z.ZodNumber;
143
- type: z.ZodEnum<["semantic", "temporal", "causal", "co_accessed"]>;
144
- updated_at: z.ZodOptional<z.ZodString>;
192
+ /** Temporal validity window. When is this knowledge true? */
193
+ temporal: z.ZodOptional<z.ZodObject<{
194
+ learned_at: z.ZodString;
195
+ valid_from: z.ZodOptional<z.ZodString>;
196
+ valid_until: z.ZodOptional<z.ZodString>;
197
+ ingested_at: z.ZodOptional<z.ZodString>;
145
198
  }, "strip", z.ZodTypeAny, {
146
- type: "semantic" | "temporal" | "causal" | "co_accessed";
147
- target_type: "engram" | "document";
148
- target: string;
149
- strength: number;
150
- updated_at?: string | undefined;
199
+ learned_at: string;
200
+ valid_from?: string | undefined;
201
+ valid_until?: string | undefined;
202
+ ingested_at?: string | undefined;
151
203
  }, {
152
- type: "semantic" | "temporal" | "causal" | "co_accessed";
153
- target_type: "engram" | "document";
154
- target: string;
155
- strength: number;
156
- updated_at?: string | undefined;
157
- }>, "many">>;
158
- dual_coding: z.ZodOptional<z.ZodEffects<z.ZodObject<{
159
- example: z.ZodOptional<z.ZodString>;
160
- analogy: z.ZodOptional<z.ZodString>;
204
+ learned_at: string;
205
+ valid_from?: string | undefined;
206
+ valid_until?: string | undefined;
207
+ ingested_at?: string | undefined;
208
+ }>>;
209
+ /** Automatic usage tracking. Injections, hits, misses. */
210
+ usage: z.ZodOptional<z.ZodObject<{
211
+ injections: z.ZodDefault<z.ZodNumber>;
212
+ hits: z.ZodDefault<z.ZodNumber>;
213
+ misses: z.ZodDefault<z.ZodNumber>;
214
+ last_hit_at: z.ZodOptional<z.ZodString>;
161
215
  }, "strip", z.ZodTypeAny, {
162
- example?: string | undefined;
163
- analogy?: string | undefined;
216
+ injections: number;
217
+ hits: number;
218
+ misses: number;
219
+ last_hit_at?: string | undefined;
164
220
  }, {
165
- example?: string | undefined;
166
- analogy?: string | undefined;
167
- }>, {
168
- example?: string | undefined;
169
- analogy?: string | undefined;
221
+ injections?: number | undefined;
222
+ hits?: number | undefined;
223
+ misses?: number | undefined;
224
+ last_hit_at?: string | undefined;
225
+ }>>;
226
+ /** Episodic context: emotional weight, confidence, trigger. */
227
+ episodic: z.ZodOptional<z.ZodObject<{
228
+ emotional_weight: z.ZodDefault<z.ZodNumber>;
229
+ confidence: z.ZodDefault<z.ZodNumber>;
230
+ trigger_context: z.ZodOptional<z.ZodString>;
231
+ journal_ref: z.ZodOptional<z.ZodString>;
232
+ }, "strip", z.ZodTypeAny, {
233
+ emotional_weight: number;
234
+ confidence: number;
235
+ trigger_context?: string | undefined;
236
+ journal_ref?: string | undefined;
170
237
  }, {
171
- example?: string | undefined;
172
- analogy?: string | undefined;
238
+ emotional_weight?: number | undefined;
239
+ confidence?: number | undefined;
240
+ trigger_context?: string | undefined;
241
+ journal_ref?: string | undefined;
173
242
  }>>;
174
- tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
175
- pack: z.ZodDefault<z.ZodNullable<z.ZodString>>;
176
- abstract: z.ZodDefault<z.ZodNullable<z.ZodString>>;
177
- derived_from: z.ZodDefault<z.ZodNullable<z.ZodString>>;
243
+ /** Exchange marketplace metadata: fitness, adoption, diversity. */
244
+ exchange: z.ZodOptional<z.ZodObject<{
245
+ fitness_score: z.ZodOptional<z.ZodNumber>;
246
+ environmental_diversity: z.ZodDefault<z.ZodNumber>;
247
+ adoption_count: z.ZodDefault<z.ZodNumber>;
248
+ contradiction_rate: z.ZodDefault<z.ZodNumber>;
249
+ }, "strip", z.ZodTypeAny, {
250
+ environmental_diversity: number;
251
+ adoption_count: number;
252
+ contradiction_rate: number;
253
+ fitness_score?: number | undefined;
254
+ }, {
255
+ fitness_score?: number | undefined;
256
+ environmental_diversity?: number | undefined;
257
+ adoption_count?: number | undefined;
258
+ contradiction_rate?: number | undefined;
259
+ }>>;
260
+ /** Extensible key-value data for domain-specific fields. */
261
+ structured_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
178
262
  }, "strip", z.ZodTypeAny, {
179
263
  type: "behavioral" | "architectural" | "procedural" | "terminological";
180
264
  status: "active" | "dormant" | "retired" | "candidate";
@@ -185,23 +269,16 @@ declare const EngramSchema: z.ZodObject<{
185
269
  visibility: "private" | "public" | "template";
186
270
  statement: string;
187
271
  derivation_count: number;
272
+ pack: string | null;
273
+ abstract: string | null;
274
+ derived_from: string | null;
275
+ tags: string[];
188
276
  activation: {
189
277
  retrieval_strength: number;
190
278
  storage_strength: number;
191
279
  frequency: number;
192
280
  last_accessed: string;
193
281
  };
194
- feedback_signals: {
195
- positive: number;
196
- negative: number;
197
- neutral: number;
198
- };
199
- knowledge_anchors: {
200
- path: string;
201
- relevance: "primary" | "supporting" | "example";
202
- snippet?: string | undefined;
203
- snippet_extracted_at?: string | undefined;
204
- }[];
205
282
  associations: {
206
283
  type: "semantic" | "temporal" | "causal" | "co_accessed";
207
284
  target_type: "engram" | "document";
@@ -209,13 +286,26 @@ declare const EngramSchema: z.ZodObject<{
209
286
  strength: number;
210
287
  updated_at?: string | undefined;
211
288
  }[];
212
- tags: string[];
213
- pack: string | null;
214
- abstract: string | null;
215
- derived_from: string | null;
289
+ knowledge_anchors: {
290
+ path: string;
291
+ relevance: "primary" | "supporting" | "example";
292
+ snippet?: string | undefined;
293
+ snippet_extracted_at?: string | undefined;
294
+ }[];
295
+ feedback_signals: {
296
+ positive: number;
297
+ negative: number;
298
+ neutral: number;
299
+ };
216
300
  rationale?: string | undefined;
217
301
  contraindications?: string[] | undefined;
218
302
  source_patterns?: string[] | undefined;
303
+ episodic?: {
304
+ emotional_weight: number;
305
+ confidence: number;
306
+ trigger_context?: string | undefined;
307
+ journal_ref?: string | undefined;
308
+ } | undefined;
219
309
  knowledge_type?: {
220
310
  memory_class: "procedural" | "semantic" | "episodic" | "metacognitive";
221
311
  cognitive_level: "remember" | "understand" | "apply" | "analyze" | "evaluate" | "create";
@@ -227,16 +317,40 @@ declare const EngramSchema: z.ZodObject<{
227
317
  related: string[];
228
318
  conflicts: string[];
229
319
  } | undefined;
320
+ temporal?: {
321
+ learned_at: string;
322
+ valid_from?: string | undefined;
323
+ valid_until?: string | undefined;
324
+ ingested_at?: string | undefined;
325
+ } | undefined;
326
+ dual_coding?: {
327
+ example?: string | undefined;
328
+ analogy?: string | undefined;
329
+ } | undefined;
230
330
  provenance?: {
231
331
  origin: string;
232
332
  chain: string[];
233
333
  signature: string | null;
234
334
  license: string;
235
335
  } | undefined;
236
- dual_coding?: {
237
- example?: string | undefined;
238
- analogy?: string | undefined;
336
+ entities?: {
337
+ type: "person" | "organization" | "technology" | "concept" | "project" | "tool" | "place" | "event" | "standard" | "other";
338
+ name: string;
339
+ uri?: string | undefined;
340
+ }[] | undefined;
341
+ usage?: {
342
+ injections: number;
343
+ hits: number;
344
+ misses: number;
345
+ last_hit_at?: string | undefined;
239
346
  } | undefined;
347
+ exchange?: {
348
+ environmental_diversity: number;
349
+ adoption_count: number;
350
+ contradiction_rate: number;
351
+ fitness_score?: number | undefined;
352
+ } | undefined;
353
+ structured_data?: Record<string, unknown> | undefined;
240
354
  }, {
241
355
  type: "behavioral" | "architectural" | "procedural" | "terminological";
242
356
  status: "active" | "dormant" | "retired" | "candidate";
@@ -250,22 +364,55 @@ declare const EngramSchema: z.ZodObject<{
250
364
  contraindications?: string[] | undefined;
251
365
  source_patterns?: string[] | undefined;
252
366
  derivation_count?: number | undefined;
367
+ pack?: string | null | undefined;
368
+ abstract?: string | null | undefined;
369
+ derived_from?: string | null | undefined;
370
+ episodic?: {
371
+ emotional_weight?: number | undefined;
372
+ confidence?: number | undefined;
373
+ trigger_context?: string | undefined;
374
+ journal_ref?: string | undefined;
375
+ } | undefined;
253
376
  knowledge_type?: {
254
377
  memory_class: "procedural" | "semantic" | "episodic" | "metacognitive";
255
378
  cognitive_level: "remember" | "understand" | "apply" | "analyze" | "evaluate" | "create";
256
379
  } | undefined;
257
380
  domain?: string | undefined;
381
+ tags?: string[] | undefined;
382
+ activation?: {
383
+ retrieval_strength: number;
384
+ storage_strength: number;
385
+ frequency: number;
386
+ last_accessed: string;
387
+ } | undefined;
258
388
  relations?: {
259
389
  broader?: string[] | undefined;
260
390
  narrower?: string[] | undefined;
261
391
  related?: string[] | undefined;
262
392
  conflicts?: string[] | undefined;
263
393
  } | undefined;
264
- activation?: {
265
- retrieval_strength: number;
266
- storage_strength: number;
267
- frequency: number;
268
- last_accessed: string;
394
+ temporal?: {
395
+ learned_at: string;
396
+ valid_from?: string | undefined;
397
+ valid_until?: string | undefined;
398
+ ingested_at?: string | undefined;
399
+ } | undefined;
400
+ associations?: {
401
+ type: "semantic" | "temporal" | "causal" | "co_accessed";
402
+ target_type: "engram" | "document";
403
+ target: string;
404
+ strength: number;
405
+ updated_at?: string | undefined;
406
+ }[] | undefined;
407
+ knowledge_anchors?: {
408
+ path: string;
409
+ relevance?: "primary" | "supporting" | "example" | undefined;
410
+ snippet?: string | undefined;
411
+ snippet_extracted_at?: string | undefined;
412
+ }[] | undefined;
413
+ dual_coding?: {
414
+ example?: string | undefined;
415
+ analogy?: string | undefined;
269
416
  } | undefined;
270
417
  provenance?: {
271
418
  origin: string;
@@ -278,27 +425,24 @@ declare const EngramSchema: z.ZodObject<{
278
425
  negative?: number | undefined;
279
426
  neutral?: number | undefined;
280
427
  } | undefined;
281
- knowledge_anchors?: {
282
- path: string;
283
- relevance?: "primary" | "supporting" | "example" | undefined;
284
- snippet?: string | undefined;
285
- snippet_extracted_at?: string | undefined;
286
- }[] | undefined;
287
- associations?: {
288
- type: "semantic" | "temporal" | "causal" | "co_accessed";
289
- target_type: "engram" | "document";
290
- target: string;
291
- strength: number;
292
- updated_at?: string | undefined;
428
+ entities?: {
429
+ type: "person" | "organization" | "technology" | "concept" | "project" | "tool" | "place" | "event" | "standard" | "other";
430
+ name: string;
431
+ uri?: string | undefined;
293
432
  }[] | undefined;
294
- dual_coding?: {
295
- example?: string | undefined;
296
- analogy?: string | undefined;
433
+ usage?: {
434
+ injections?: number | undefined;
435
+ hits?: number | undefined;
436
+ misses?: number | undefined;
437
+ last_hit_at?: string | undefined;
297
438
  } | undefined;
298
- tags?: string[] | undefined;
299
- pack?: string | null | undefined;
300
- abstract?: string | null | undefined;
301
- derived_from?: string | null | undefined;
439
+ exchange?: {
440
+ fitness_score?: number | undefined;
441
+ environmental_diversity?: number | undefined;
442
+ adoption_count?: number | undefined;
443
+ contradiction_rate?: number | undefined;
444
+ } | undefined;
445
+ structured_data?: Record<string, unknown> | undefined;
302
446
  }>;
303
447
  type Engram = z.infer<typeof EngramSchema>;
304
448
  type KnowledgeAnchor = z.infer<typeof KnowledgeAnchorSchema>;
@@ -351,8 +495,8 @@ declare const PackManifestSchema: z.ZodObject<{
351
495
  }>>;
352
496
  }, "strip", z.ZodTypeAny, {
353
497
  version: string;
354
- license: string;
355
498
  tags: string[];
499
+ license: string;
356
500
  name: string;
357
501
  description?: string | undefined;
358
502
  creator?: string | undefined;
@@ -373,8 +517,8 @@ declare const PackManifestSchema: z.ZodObject<{
373
517
  }, {
374
518
  version: string;
375
519
  name: string;
376
- license?: string | undefined;
377
520
  tags?: string[] | undefined;
521
+ license?: string | undefined;
378
522
  description?: string | undefined;
379
523
  creator?: string | undefined;
380
524
  metadata?: {
@@ -522,6 +666,9 @@ interface TimelineQuery {
522
666
  search?: string;
523
667
  }
524
668
 
669
+ /** Build searchable text from all engram fields */
670
+ declare function engramSearchText(engram: Engram): string;
671
+
525
672
  interface IngestOptions {
526
673
  source?: string;
527
674
  extract_only?: boolean;
@@ -560,6 +707,14 @@ declare class Plur {
560
707
  recallAsync(query: string, options: RecallOptions & {
561
708
  llm: LlmFunction;
562
709
  }): Promise<Engram[]>;
710
+ /** Search engrams using local embeddings (transformers.js). Async, no API calls. */
711
+ recallSemantic(query: string, options?: Omit<RecallOptions, 'mode' | 'llm'>): Promise<Engram[]>;
712
+ /** Hybrid search: BM25 + embeddings merged via Reciprocal Rank Fusion. Async, no API calls. */
713
+ recallHybrid(query: string, options?: Omit<RecallOptions, 'mode' | 'llm'>): Promise<Engram[]>;
714
+ /** Expanded search: LLM query expansion + hybrid search + RRF merge. Opt-in, requires LLM function. */
715
+ recallExpanded(query: string, options: RecallOptions & {
716
+ llm: LlmFunction;
717
+ }): Promise<Engram[]>;
563
718
  /** Filter engrams by scope/domain/strength (shared by both modes) */
564
719
  private _filterEngrams;
565
720
  /** Reactivate accessed engrams (bump retrieval strength, frequency, last_accessed) */
@@ -597,4 +752,4 @@ declare class Plur {
597
752
  status(): StatusResult;
598
753
  }
599
754
 
600
- export { type Association, type CaptureContext, type Engram, type Episode, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type PackManifest, Plur, type PlurConfig, type RecallOptions, type StatusResult, type TimelineQuery };
755
+ export { type Association, type CaptureContext, type Engram, type Episode, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type PackManifest, Plur, type PlurConfig, type RecallOptions, type StatusResult, type TimelineQuery, engramSearchText };
package/dist/index.js CHANGED
@@ -102,7 +102,48 @@ var FeedbackSignalsSchema = z2.object({
102
102
  negative: z2.number().int().default(0),
103
103
  neutral: z2.number().int().default(0)
104
104
  });
105
+ var EntityRefSchema = z2.object({
106
+ name: z2.string(),
107
+ type: z2.enum([
108
+ "person",
109
+ "organization",
110
+ "technology",
111
+ "concept",
112
+ "project",
113
+ "tool",
114
+ "place",
115
+ "event",
116
+ "standard",
117
+ "other"
118
+ ]),
119
+ uri: z2.string().url().optional()
120
+ });
121
+ var TemporalSchema = z2.object({
122
+ learned_at: z2.string(),
123
+ valid_from: z2.string().optional(),
124
+ valid_until: z2.string().optional(),
125
+ ingested_at: z2.string().optional()
126
+ });
127
+ var UsageStatsSchema = z2.object({
128
+ injections: z2.number().int().default(0),
129
+ hits: z2.number().int().default(0),
130
+ misses: z2.number().int().default(0),
131
+ last_hit_at: z2.string().optional()
132
+ });
133
+ var EpisodicFieldsSchema = z2.object({
134
+ emotional_weight: z2.number().int().min(1).max(10).default(5),
135
+ confidence: z2.number().int().min(1).max(10).default(5),
136
+ trigger_context: z2.string().optional(),
137
+ journal_ref: z2.string().optional()
138
+ });
139
+ var ExchangeMetadataSchema = z2.object({
140
+ fitness_score: z2.number().min(0).max(1).optional(),
141
+ environmental_diversity: z2.number().int().default(0),
142
+ adoption_count: z2.number().int().default(0),
143
+ contradiction_rate: z2.number().min(0).max(1).default(0)
144
+ });
105
145
  var EngramSchema = z2.object({
146
+ // Identity
106
147
  id: z2.string().regex(/^(ENG|ABS)-[A-Za-z0-9-]+$/),
107
148
  version: z2.number().int().min(1).default(2),
108
149
  status: z2.enum(["active", "dormant", "retired", "candidate"]),
@@ -110,24 +151,49 @@ var EngramSchema = z2.object({
110
151
  type: z2.enum(["behavioral", "terminological", "procedural", "architectural"]),
111
152
  scope: z2.string(),
112
153
  visibility: z2.enum(["private", "public", "template"]).default("private"),
154
+ // Content
113
155
  statement: z2.string().min(1),
114
156
  rationale: z2.string().optional(),
115
157
  contraindications: z2.array(z2.string()).optional(),
158
+ // Lineage
116
159
  source_patterns: z2.array(z2.string()).optional(),
117
160
  derivation_count: z2.number().int().min(0).default(1),
161
+ pack: z2.string().nullable().default(null),
162
+ abstract: z2.string().nullable().default(null),
163
+ derived_from: z2.string().nullable().default(null),
164
+ // Classification
118
165
  knowledge_type: KnowledgeTypeSchema.optional(),
119
166
  domain: z2.string().optional(),
167
+ tags: z2.array(z2.string()).default([]),
168
+ // Activation (ACT-R model)
169
+ activation: ActivationSchema.default({
170
+ retrieval_strength: 0.7,
171
+ storage_strength: 1,
172
+ frequency: 0,
173
+ last_accessed: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
174
+ }),
175
+ // Relations & grounding
120
176
  relations: RelationsSchema.optional(),
121
- activation: ActivationSchema.default({ retrieval_strength: 0.7, storage_strength: 1, frequency: 0, last_accessed: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) }),
122
- provenance: ProvenanceSchema.optional(),
123
- feedback_signals: FeedbackSignalsSchema.default({ positive: 0, negative: 0, neutral: 0 }),
124
- knowledge_anchors: z2.array(KnowledgeAnchorSchema).default([]),
125
177
  associations: z2.array(AssociationSchema).default([]),
178
+ knowledge_anchors: z2.array(KnowledgeAnchorSchema).default([]),
126
179
  dual_coding: DualCodingSchema.optional(),
127
- tags: z2.array(z2.string()).default([]),
128
- pack: z2.string().nullable().default(null),
129
- abstract: z2.string().nullable().default(null),
130
- derived_from: z2.string().nullable().default(null)
180
+ // Provenance
181
+ provenance: ProvenanceSchema.optional(),
182
+ // Feedback
183
+ feedback_signals: FeedbackSignalsSchema.default({ positive: 0, negative: 0, neutral: 0 }),
184
+ // === NEW OPTIONAL FIELDS (v2.1) ===
185
+ /** Typed entity references extracted from statement. Enables graph queries. */
186
+ entities: z2.array(EntityRefSchema).optional(),
187
+ /** Temporal validity window. When is this knowledge true? */
188
+ temporal: TemporalSchema.optional(),
189
+ /** Automatic usage tracking. Injections, hits, misses. */
190
+ usage: UsageStatsSchema.optional(),
191
+ /** Episodic context: emotional weight, confidence, trigger. */
192
+ episodic: EpisodicFieldsSchema.optional(),
193
+ /** Exchange marketplace metadata: fitness, adoption, diversity. */
194
+ exchange: ExchangeMetadataSchema.optional(),
195
+ /** Extensible key-value data for domain-specific fields. */
196
+ structured_data: z2.record(z2.string(), z2.unknown()).optional()
131
197
  });
132
198
 
133
199
  // src/schemas/pack.ts
@@ -275,11 +341,25 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
275
341
  function ftsTokenize(text) {
276
342
  return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 2).filter((w) => !STOP_WORDS.has(w));
277
343
  }
344
+ function engramSearchText(engram) {
345
+ const parts = [engram.statement];
346
+ if (engram.domain) parts.push(engram.domain.replace(/\./g, " "));
347
+ if (engram.tags.length > 0) parts.push(engram.tags.join(" "));
348
+ if (engram.entities) {
349
+ for (const e of engram.entities) {
350
+ parts.push(e.name);
351
+ if (e.type !== "other") parts.push(e.type);
352
+ }
353
+ }
354
+ if (engram.temporal) {
355
+ if (engram.temporal.valid_from) parts.push(engram.temporal.valid_from);
356
+ if (engram.temporal.valid_until) parts.push(engram.temporal.valid_until);
357
+ }
358
+ if (engram.rationale) parts.push(engram.rationale);
359
+ return parts.join(" ");
360
+ }
278
361
  function ftsScore(engram, queryTokens) {
279
- const statementTokens = ftsTokenize(engram.statement);
280
- const domainTokens = engram.domain ? ftsTokenize(engram.domain.replace(/\./g, " ")) : [];
281
- const tagTokens = engram.tags.map((t) => t.toLowerCase());
282
- const allTerms = [...statementTokens, ...domainTokens, ...tagTokens];
362
+ const allTerms = ftsTokenize(engramSearchText(engram));
283
363
  let matches = 0;
284
364
  for (const qt of queryTokens) {
285
365
  if (allTerms.some((t) => t.includes(qt) || qt.includes(t))) matches++;
@@ -608,7 +688,14 @@ async function agenticSearch(engrams, query, limit, llm) {
608
688
  return agenticRerank(candidates, query, limit, llm);
609
689
  }
610
690
  async function agenticRerank(candidates, query, limit, llm) {
611
- const numbered = candidates.map((e, i) => `${i + 1}. [${e.id}] ${e.statement}`).join("\n");
691
+ const numbered = candidates.map((e, i) => {
692
+ const extra = [];
693
+ if (e.entities?.length) extra.push(`entities: ${e.entities.map((x) => x.name).join(", ")}`);
694
+ if (e.temporal?.valid_from) extra.push(`valid from: ${e.temporal.valid_from}`);
695
+ if (e.temporal?.valid_until) extra.push(`valid until: ${e.temporal.valid_until}`);
696
+ const suffix = extra.length > 0 ? ` (${extra.join("; ")})` : "";
697
+ return `${i + 1}. [${e.id}] ${e.statement}${suffix}`;
698
+ }).join("\n");
612
699
  const prompt = `You are a memory retrieval system. Given a query and a list of memories, select the ${limit} most relevant memories. Return ONLY the numbers of the relevant memories, comma-separated, in order of relevance (most relevant first).
613
700
 
614
701
  Query: "${query}"
@@ -641,6 +728,197 @@ Rules:
641
728
  }
642
729
  }
643
730
 
731
+ // src/embeddings.ts
732
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
733
+ import { join as join2 } from "path";
734
+ var embedPipeline = null;
735
+ async function getEmbedder() {
736
+ if (!embedPipeline) {
737
+ const { pipeline } = await import("@huggingface/transformers");
738
+ embedPipeline = await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5", {
739
+ dtype: "fp32"
740
+ });
741
+ }
742
+ return embedPipeline;
743
+ }
744
+ async function embed(text) {
745
+ const embedder = await getEmbedder();
746
+ const result = await embedder(text, { pooling: "cls", normalize: true });
747
+ return new Float32Array(result.data);
748
+ }
749
+ function cosineSimilarity(a, b) {
750
+ let dot = 0;
751
+ for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
752
+ return dot;
753
+ }
754
+ function loadCache(cachePath) {
755
+ if (!existsSync5(cachePath)) return {};
756
+ try {
757
+ return JSON.parse(readFileSync4(cachePath, "utf8"));
758
+ } catch {
759
+ return {};
760
+ }
761
+ }
762
+ function saveCache(cachePath, cache) {
763
+ const dir = cachePath.substring(0, cachePath.lastIndexOf("/"));
764
+ if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
765
+ writeFileSync3(cachePath, JSON.stringify(cache));
766
+ }
767
+ function hashStatement(statement) {
768
+ let hash = 0;
769
+ for (let i = 0; i < statement.length; i++) {
770
+ hash = (hash << 5) - hash + statement.charCodeAt(i) | 0;
771
+ }
772
+ return hash.toString(36);
773
+ }
774
+ async function embeddingSearch(engrams, query, limit, storagePath) {
775
+ if (engrams.length === 0) return [];
776
+ const cachePath = storagePath ? join2(storagePath, ".embeddings-cache.json") : ".embeddings-cache.json";
777
+ const cache = loadCache(cachePath);
778
+ const queryEmbedding = await embed(query);
779
+ const similarities = [];
780
+ for (const engram of engrams) {
781
+ const searchText = engramSearchText(engram);
782
+ const hash = hashStatement(searchText);
783
+ let engramEmbedding;
784
+ if (cache[engram.id]?.hash === hash) {
785
+ engramEmbedding = new Float32Array(cache[engram.id].embedding);
786
+ } else {
787
+ engramEmbedding = await embed(searchText);
788
+ cache[engram.id] = {
789
+ hash,
790
+ embedding: Array.from(engramEmbedding)
791
+ };
792
+ }
793
+ const score = cosineSimilarity(queryEmbedding, engramEmbedding);
794
+ similarities.push({ engram, score });
795
+ }
796
+ saveCache(cachePath, cache);
797
+ similarities.sort((a, b) => b.score - a.score);
798
+ return similarities.slice(0, limit).map((s) => s.engram);
799
+ }
800
+
801
+ // src/hybrid-search.ts
802
+ function rrfMerge(resultSets, k = 60) {
803
+ const scores = /* @__PURE__ */ new Map();
804
+ for (const results of resultSets) {
805
+ for (let rank = 0; rank < results.length; rank++) {
806
+ const engram = results[rank];
807
+ const existing = scores.get(engram.id);
808
+ const rrfScore = 1 / (k + rank + 1);
809
+ if (existing) {
810
+ existing.score += rrfScore;
811
+ } else {
812
+ scores.set(engram.id, { engram, score: rrfScore });
813
+ }
814
+ }
815
+ }
816
+ return Array.from(scores.values()).sort((a, b) => b.score - a.score).map((s) => s.engram);
817
+ }
818
+ var AGGREGATION_PATTERNS = [
819
+ /how many/i,
820
+ /how much/i,
821
+ /total (?:number|amount|cost|time|hours|days|spent)/i,
822
+ /all (?:the|my)/i,
823
+ /every (?:time|instance)/i,
824
+ /(?:count|sum|add up|combine|altogether)/i,
825
+ /in the (?:past|last) (?:week|month|year|few)/i,
826
+ /did I (?:attend|visit|go to|buy|spend|do)/i
827
+ ];
828
+ function isAggregationQuery(query) {
829
+ return AGGREGATION_PATTERNS.some((p) => p.test(query));
830
+ }
831
+ async function hybridSearch(engrams, query, limit, storagePath) {
832
+ if (engrams.length === 0) return [];
833
+ const exhaustive = isAggregationQuery(query);
834
+ const effectiveLimit = exhaustive ? Math.max(limit, 50) : limit;
835
+ const bm25Limit = Math.min(engrams.length, exhaustive ? effectiveLimit * 5 : effectiveLimit * 3);
836
+ const embLimit = Math.min(engrams.length, exhaustive ? effectiveLimit * 3 : effectiveLimit * 2);
837
+ const [bm25Results, embResults] = await Promise.all([
838
+ Promise.resolve(searchEngrams(engrams, query, bm25Limit)),
839
+ embeddingSearch(engrams, query, embLimit, storagePath)
840
+ ]);
841
+ const merged = rrfMerge([bm25Results, embResults]);
842
+ return merged.slice(0, effectiveLimit);
843
+ }
844
+
845
+ // src/query-expansion.ts
846
+ var EXPANSION_PROMPT = `You are a search query expander. Given a user query, generate 3 alternative search queries that capture the same intent but use different words, synonyms, or related concepts.
847
+
848
+ Return ONLY the 3 queries, one per line, nothing else. No numbering, no explanations.
849
+
850
+ Query: "{query}"`;
851
+ var AGGREGATION_EXPANSION_PROMPT = `You are a search query expander for an aggregation query. The user wants to COUNT or TOTAL something across multiple conversations. Generate 5 search queries that will find ALL individual instances \u2014 each query should target a different way the item might have been mentioned.
852
+
853
+ For example, if the query is "How many weddings did I attend?", generate queries like:
854
+ - wedding ceremony attended
855
+ - went to wedding reception
856
+ - wedding invitation RSVP
857
+ - marriage celebration
858
+ - attended wedding of
859
+
860
+ Return ONLY the 5 queries, one per line. No numbering, no explanations.
861
+
862
+ Query: "{query}"`;
863
+ var AGGREGATION_PATTERNS2 = [
864
+ /how many/i,
865
+ /how much/i,
866
+ /total (?:number|amount|cost|time|hours|days|spent)/i,
867
+ /all (?:the|my)/i,
868
+ /every (?:time|instance)/i,
869
+ /(?:count|sum|add up|combine|altogether)/i,
870
+ /in the (?:past|last) (?:week|month|year|few)/i,
871
+ /did I (?:attend|visit|go to|buy|spend|do)/i
872
+ ];
873
+ function isAggregationQuery2(query) {
874
+ return AGGREGATION_PATTERNS2.some((p) => p.test(query));
875
+ }
876
+ function parseVariants(response, original) {
877
+ const variants = response.split("\n").map((line) => line.replace(/^\d+[\.\)]\s*/, "").trim()).filter((line) => line.length > 3 && line.length < 200).slice(0, 3);
878
+ return [original, ...variants];
879
+ }
880
+ function rrfMerge2(resultSets, k = 60) {
881
+ const scores = /* @__PURE__ */ new Map();
882
+ for (const results of resultSets) {
883
+ for (let rank = 0; rank < results.length; rank++) {
884
+ const engram = results[rank];
885
+ const existing = scores.get(engram.id);
886
+ const rrfScore = 1 / (k + rank + 1);
887
+ if (existing) {
888
+ existing.score += rrfScore;
889
+ } else {
890
+ scores.set(engram.id, { engram, score: rrfScore });
891
+ }
892
+ }
893
+ }
894
+ return Array.from(scores.values()).sort((a, b) => b.score - a.score).map((s) => s.engram);
895
+ }
896
+ async function expandedSearch(engrams, query, limit, llm, storagePath) {
897
+ if (engrams.length === 0) return [];
898
+ const aggregation = isAggregationQuery2(query);
899
+ let variants;
900
+ try {
901
+ const promptTemplate = aggregation ? AGGREGATION_EXPANSION_PROMPT : EXPANSION_PROMPT;
902
+ const prompt = promptTemplate.replace("{query}", query);
903
+ const response = await llm(prompt);
904
+ variants = parseVariants(response, query);
905
+ if (aggregation) {
906
+ const extra = response.split("\n").map((line) => line.replace(/^\d+[\.\)]\s*/, "").trim()).filter((line) => line.length > 3 && line.length < 200).slice(0, 5);
907
+ variants = [query, ...extra];
908
+ }
909
+ } catch {
910
+ variants = [query];
911
+ }
912
+ const perVariantLimit = aggregation ? Math.max(limit, 50) : limit;
913
+ const searchPromises = variants.map(
914
+ (v) => hybridSearch(engrams, v, perVariantLimit, storagePath)
915
+ );
916
+ const resultSets = await Promise.all(searchPromises);
917
+ const merged = rrfMerge2(resultSets);
918
+ const effectiveLimit = aggregation ? Math.max(limit, 50) : limit;
919
+ return merged.slice(0, effectiveLimit);
920
+ }
921
+
644
922
  // src/packs.ts
645
923
  import * as fs2 from "fs";
646
924
  import * as path from "path";
@@ -798,6 +1076,30 @@ var Plur = class {
798
1076
  this._reactivateResults(results);
799
1077
  return results;
800
1078
  }
1079
+ /** Search engrams using local embeddings (transformers.js). Async, no API calls. */
1080
+ async recallSemantic(query, options) {
1081
+ const filtered = this._filterEngrams(options);
1082
+ const limit = options?.limit ?? 20;
1083
+ const results = await embeddingSearch(filtered, query, limit, this.paths.root);
1084
+ this._reactivateResults(results);
1085
+ return results;
1086
+ }
1087
+ /** Hybrid search: BM25 + embeddings merged via Reciprocal Rank Fusion. Async, no API calls. */
1088
+ async recallHybrid(query, options) {
1089
+ const filtered = this._filterEngrams(options);
1090
+ const limit = options?.limit ?? 20;
1091
+ const results = await hybridSearch(filtered, query, limit, this.paths.root);
1092
+ this._reactivateResults(results);
1093
+ return results;
1094
+ }
1095
+ /** Expanded search: LLM query expansion + hybrid search + RRF merge. Opt-in, requires LLM function. */
1096
+ async recallExpanded(query, options) {
1097
+ const filtered = this._filterEngrams(options);
1098
+ const limit = options?.limit ?? 20;
1099
+ const results = await expandedSearch(filtered, query, limit, options.llm, this.paths.root);
1100
+ this._reactivateResults(results);
1101
+ return results;
1102
+ }
801
1103
  /** Filter engrams by scope/domain/strength (shared by both modes) */
802
1104
  _filterEngrams(options) {
803
1105
  let engrams = loadEngrams(this.paths.engrams);
@@ -958,5 +1260,6 @@ var Plur = class {
958
1260
  }
959
1261
  };
960
1262
  export {
961
- Plur
1263
+ Plur,
1264
+ engramSearchText
962
1265
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plur-ai/core",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,6 +8,7 @@
8
8
  "dist"
9
9
  ],
10
10
  "dependencies": {
11
+ "@huggingface/transformers": "^3.8.1",
11
12
  "js-yaml": "^4.1.0",
12
13
  "zod": "^3.23.0"
13
14
  },