@steno-ai/engine 0.1.1 → 0.1.3

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 (116) hide show
  1. package/dist/adapters/storage.d.ts +1 -0
  2. package/dist/adapters/storage.d.ts.map +1 -1
  3. package/dist/extraction/llm-extractor.d.ts.map +1 -1
  4. package/dist/extraction/llm-extractor.js +5 -3
  5. package/dist/extraction/llm-extractor.js.map +1 -1
  6. package/dist/extraction/pipeline.d.ts.map +1 -1
  7. package/dist/extraction/pipeline.js +5 -1
  8. package/dist/extraction/pipeline.js.map +1 -1
  9. package/dist/extraction/prompts.d.ts +2 -2
  10. package/dist/extraction/prompts.d.ts.map +1 -1
  11. package/dist/extraction/prompts.js +12 -3
  12. package/dist/extraction/prompts.js.map +1 -1
  13. package/package.json +6 -2
  14. package/src/adapters/cache.js +2 -0
  15. package/src/adapters/embedding.js +2 -0
  16. package/src/adapters/llm.js +2 -0
  17. package/src/adapters/perplexity-embedding.js +78 -0
  18. package/src/adapters/storage.js +2 -0
  19. package/src/adapters/storage.ts +1 -0
  20. package/src/config.d.ts +211 -1
  21. package/src/config.d.ts.map +1 -1
  22. package/src/config.js +92 -0
  23. package/src/config.js.map +1 -1
  24. package/src/extraction/contradiction.js +23 -0
  25. package/src/extraction/dedup.js +93 -0
  26. package/src/extraction/dedup.js.map +1 -1
  27. package/src/extraction/entity-extractor.d.ts.map +1 -1
  28. package/src/extraction/entity-extractor.js +145 -0
  29. package/src/extraction/entity-extractor.js.map +1 -1
  30. package/src/extraction/hasher.js +8 -0
  31. package/src/extraction/heuristic.js +282 -0
  32. package/src/extraction/llm-extractor.d.ts +3 -1
  33. package/src/extraction/llm-extractor.d.ts.map +1 -1
  34. package/src/extraction/llm-extractor.js +238 -0
  35. package/src/extraction/llm-extractor.js.map +1 -1
  36. package/src/extraction/llm-extractor.ts +7 -5
  37. package/src/extraction/pipeline.d.ts +3 -0
  38. package/src/extraction/pipeline.d.ts.map +1 -1
  39. package/src/extraction/pipeline.js +398 -0
  40. package/src/extraction/pipeline.js.map +1 -1
  41. package/src/extraction/pipeline.ts +6 -1
  42. package/src/extraction/prompts.d.ts +28 -0
  43. package/src/extraction/prompts.d.ts.map +1 -0
  44. package/src/extraction/prompts.js +196 -0
  45. package/src/extraction/prompts.js.map +1 -1
  46. package/src/extraction/prompts.ts +12 -3
  47. package/src/extraction/sliding-window.js +84 -0
  48. package/src/extraction/sliding-window.js.map +1 -1
  49. package/src/extraction/types.d.ts +12 -0
  50. package/src/extraction/types.d.ts.map +1 -1
  51. package/src/extraction/types.js +2 -0
  52. package/src/feedback/tracker.js +90 -0
  53. package/src/models/api-key.d.ts +2 -2
  54. package/src/models/api-key.js +21 -0
  55. package/src/models/edge.d.ts +6 -6
  56. package/src/models/edge.js +29 -0
  57. package/src/models/entity.d.ts +2 -2
  58. package/src/models/entity.js +22 -0
  59. package/src/models/extraction.d.ts +6 -6
  60. package/src/models/extraction.js +40 -0
  61. package/src/models/fact-entity.js +14 -0
  62. package/src/models/fact.d.ts +191 -0
  63. package/src/models/fact.d.ts.map +1 -0
  64. package/src/models/fact.js +72 -0
  65. package/src/models/fact.js.map +1 -0
  66. package/src/models/index.js +13 -0
  67. package/src/models/memory-access.d.ts +4 -4
  68. package/src/models/memory-access.js +33 -0
  69. package/src/models/session.js +23 -0
  70. package/src/models/tenant.d.ts +248 -14
  71. package/src/models/tenant.d.ts.map +1 -1
  72. package/src/models/tenant.js +23 -0
  73. package/src/models/trigger.d.ts +5 -5
  74. package/src/models/trigger.js +41 -0
  75. package/src/models/usage-record.js +14 -0
  76. package/src/models/webhook.d.ts +1 -1
  77. package/src/models/webhook.js +25 -0
  78. package/src/retrieval/compound-search.d.ts.map +1 -1
  79. package/src/retrieval/compound-search.js +87 -0
  80. package/src/retrieval/compound-search.js.map +1 -1
  81. package/src/retrieval/contradiction-surfacer.js +64 -0
  82. package/src/retrieval/embedding-cache.js +56 -0
  83. package/src/retrieval/fusion.d.ts +1 -0
  84. package/src/retrieval/fusion.d.ts.map +1 -1
  85. package/src/retrieval/fusion.js +87 -0
  86. package/src/retrieval/fusion.js.map +1 -1
  87. package/src/retrieval/graph-traversal.d.ts +2 -1
  88. package/src/retrieval/graph-traversal.d.ts.map +1 -1
  89. package/src/retrieval/graph-traversal.js +208 -0
  90. package/src/retrieval/graph-traversal.js.map +1 -1
  91. package/src/retrieval/query-expansion.js +76 -0
  92. package/src/retrieval/reranker.js +47 -0
  93. package/src/retrieval/salience-scorer.js +41 -0
  94. package/src/retrieval/search.d.ts.map +1 -1
  95. package/src/retrieval/search.js +228 -0
  96. package/src/retrieval/search.js.map +1 -1
  97. package/src/retrieval/temporal-scorer.d.ts +18 -0
  98. package/src/retrieval/temporal-scorer.d.ts.map +1 -0
  99. package/src/retrieval/temporal-scorer.js +106 -0
  100. package/src/retrieval/temporal-scorer.js.map +1 -0
  101. package/src/retrieval/trigger-matcher.d.ts.map +1 -1
  102. package/src/retrieval/trigger-matcher.js +134 -0
  103. package/src/retrieval/trigger-matcher.js.map +1 -1
  104. package/src/retrieval/types.d.ts +4 -0
  105. package/src/retrieval/types.d.ts.map +1 -1
  106. package/src/retrieval/types.js +9 -0
  107. package/src/retrieval/types.js.map +1 -1
  108. package/src/retrieval/vector-search.d.ts.map +1 -1
  109. package/src/retrieval/vector-search.js +24 -0
  110. package/src/retrieval/vector-search.js.map +1 -1
  111. package/src/salience/decay.js +15 -0
  112. package/src/scratchpad/scratchpad.js +107 -0
  113. package/src/sessions/manager.d.ts +11 -0
  114. package/src/sessions/manager.d.ts.map +1 -0
  115. package/src/sessions/manager.js +63 -0
  116. package/src/sessions/manager.js.map +1 -0
@@ -0,0 +1,196 @@
1
+ // =============================================================================
2
+ // PASS 1: FACT EXTRACTION — Simple, focused, one job
3
+ // =============================================================================
4
+ export const FACT_EXTRACTION_PROMPT = `You are a memory extraction engine. Extract facts from text for a personal AI memory system.
5
+
6
+ ## WHO IS "USER"?
7
+
8
+ Messages labeled "user" are from THE PERSON whose memories we are storing.
9
+ Messages labeled "assistant" or any other role are from OTHER people.
10
+
11
+ CRITICAL: Focus on facts FROM "user" messages, but ALSO extract notable facts about other people mentioned in the conversation. If the assistant/conversation partner shares personal information (e.g., "I painted a sunrise last year", "I realized self-care is important"), store it as "User's conversation partner [Name] painted a sunrise in [year]".
12
+
13
+ For identity/trait facts, state them DIRECTLY:
14
+ - "User is a transgender woman" (not just "User went to a transgender conference")
15
+ - "User works at Brightwell Capital" (not just "User had a busy day at work")
16
+ - "User is researching adoption agencies" (not just "User attended a meeting about adoption")
17
+
18
+ ## RULES
19
+
20
+ 1. Extract SELF-CONTAINED atomic facts. Each fact must be understandable on its own, without the original conversation.
21
+
22
+ 2. **DATES ARE CRITICAL** — Resolve ALL temporal references to exact dates:
23
+ - "yesterday" → "on 7 May 2023"
24
+ - "last week" → "around 1 May 2023"
25
+ - "recently" → "in early May 2023"
26
+ - Look for date context like "[This conversation took place on 8 May, 2023]" and resolve ALL relative dates from it.
27
+ - EVERY event/activity fact MUST include WHEN it happened if the date can be inferred.
28
+ - BAD: "User went to an LGBTQ support group"
29
+ - GOOD: "User went to an LGBTQ support group on 7 May 2023"
30
+
31
+ 3. Resolve ALL other references:
32
+ - Pronouns → names: "she said" → "Casey said"
33
+ - Places → full names: "there" → "at Brightwell Capital"
34
+
35
+ 4. Be SPECIFIC, not vague:
36
+ BAD: "User had issues at work"
37
+ GOOD: "User's team at Brightwell Capital rambles too much in meetings"
38
+
39
+ 5. Extract ALL facts, even minor ones. You cannot predict what will be asked later.
40
+
41
+ 6. Write all facts in third person using "User" (e.g., "User prefers dark mode").
42
+
43
+ 7. For conversation partners: ALSO extract their facts with their name.
44
+ - If Melanie says "I painted a sunrise last year" → "Melanie painted a sunrise in 2022"
45
+ - If Melanie says "I ran a charity race" → "Melanie ran a charity race"
46
+
47
+ 8. For EVERY fact, include "ed" (event date) if the fact describes something that happened at a specific time.
48
+ - "User went to the gym on May 7" → ed: "2023-05-07"
49
+ - "User prefers dark mode" → ed: null (timeless preference)
50
+ If the conversation header says "[This conversation took place on 8 May, 2023]", set "dd" to "2023-05-08" for all facts.
51
+
52
+ ## OUTPUT
53
+
54
+ Return ONLY a JSON object:
55
+ {"facts": [{"t": "fact text here", "i": 0.7, "ed": "2023-05-07", "dd": "2023-05-08"}, {"t": "another fact", "i": 0.3, "ed": null, "dd": "2023-05-08"}]}
56
+
57
+ - ed (eventDate): ISO date string of WHEN the event occurred, or null if not temporal
58
+ - dd (documentDate): ISO date string of when the conversation took place, from context header
59
+
60
+ Score importance (i) from 0.0 to 1.0:
61
+ - 0.9-1.0: Identity, health conditions, allergies, life events (birth, marriage, death)
62
+ - 0.7-0.8: Relationships, employment, education, strong preferences, plans
63
+ - 0.4-0.6: Activities, opinions, moderate preferences, daily events
64
+ - 0.1-0.3: Casual mentions, weather, trivial observations
65
+
66
+ Nothing else. No explanation, no markdown.`;
67
+ // =============================================================================
68
+ // PASS 2: GRAPH EXTRACTION — Entities + relationships from extracted facts
69
+ // =============================================================================
70
+ export const GRAPH_EXTRACTION_PROMPT = `You are a knowledge graph builder. Given a list of facts about a person, extract entities and relationships.
71
+
72
+ ## ENTITIES
73
+
74
+ Extract only IMPORTANT named entities — proper nouns and specific things worth remembering. Aim for 3-8 entities total, not 40.
75
+
76
+ DO extract: People (Casey, Jamie), Organizations (Brightwell Capital), Places (Harbor Point), Products/Projects (LifePath, AirPods Max), Named activities (Catan, D&D)
77
+ DO NOT extract: Generic nouns (team, boss, meeting, work, food, gym), abstract concepts (motivation, stress), common objects (pizza, chair, phone)
78
+
79
+ ## RELATIONSHIPS
80
+
81
+ Extract relationships between entities. Be SMART — infer from context:
82
+ - "User works at Google" → user works_at google
83
+ - "User loves Casey, plans to propose" → user partner_of casey
84
+ - "User's friend Jamie came over" → user friend_of jamie
85
+
86
+ Use snake_case relation names: works_at, partner_of, friend_of, lives_in, uses, studies, prefers, etc.
87
+
88
+ ## OUTPUT
89
+
90
+ Return ONLY a JSON object:
91
+ {
92
+ "entities": [
93
+ {"name": "Casey", "entity_type": "person"},
94
+ {"name": "Brightwell Capital", "entity_type": "organization"}
95
+ ],
96
+ "edges": [
97
+ {"source": "user", "target": "casey", "relation": "partner_of"},
98
+ {"source": "user", "target": "brightwell capital", "relation": "works_at"}
99
+ ]
100
+ }
101
+
102
+ entity_type must be one of: {ENTITY_TYPES}.
103
+ Entity names must be clean: no punctuation, no articles, no sentence fragments.
104
+ Return ONLY valid JSON.`;
105
+ export const DEFAULT_ENTITY_TYPES = ['person', 'organization', 'location', 'technology', 'concept', 'event'];
106
+ // =============================================================================
107
+ // DEDUP PROMPT — Classify new facts against existing ones
108
+ // =============================================================================
109
+ export const DEDUP_PROMPT = `You are a memory deduplication engine. Given NEW facts and EXISTING facts in a memory store, classify each new fact.
110
+
111
+ For each new fact, decide:
112
+ - ADD: entirely new information, not covered by existing facts
113
+ - UPDATE: replaces or refines an existing fact (provide the index). The new fact SUPERSEDES the old one.
114
+ - EXTEND: adds new detail to an existing fact WITHOUT contradicting it (provide the index)
115
+ - NOOP: already covered by an existing fact — skip it
116
+ - CONTRADICT: conflicts with an existing fact (provide the index)
117
+
118
+ Return a JSON array:
119
+ [
120
+ {"fact": "...", "operation": "ADD"},
121
+ {"fact": "...", "operation": "UPDATE", "existing_index": 3},
122
+ {"fact": "...", "operation": "EXTEND", "existing_index": 5},
123
+ {"fact": "...", "operation": "NOOP"},
124
+ {"fact": "...", "operation": "CONTRADICT", "existing_index": 7}
125
+ ]
126
+
127
+ Be conservative: prefer NOOP over ADD if the information is substantially similar.
128
+ Prefer EXTEND over UPDATE when the new fact adds detail without changing the core meaning.`;
129
+ /**
130
+ * Build the fact extraction prompt (Pass 1).
131
+ * Simple: extract facts as strings.
132
+ */
133
+ export function buildFactExtractionPrompt(input) {
134
+ return [
135
+ { role: 'system', content: FACT_EXTRACTION_PROMPT },
136
+ { role: 'user', content: `Extract facts from this text:\n\n${input}` },
137
+ ];
138
+ }
139
+ /**
140
+ * Build the graph extraction prompt (Pass 2).
141
+ * Takes extracted facts and produces entities + edges.
142
+ */
143
+ export function buildGraphExtractionPrompt(facts, entityTypes, domainEntityTypes) {
144
+ const factsList = facts.map((f, i) => `${i + 1}. ${f}`).join('\n');
145
+ // Build entity type list — merge default names with domain-specific types
146
+ const defaultTypes = entityTypes ?? DEFAULT_ENTITY_TYPES;
147
+ const domainTypeNames = domainEntityTypes?.map(t => t.name.toLowerCase()) ?? [];
148
+ const allTypeNames = [...new Set([...defaultTypes, ...domainTypeNames])];
149
+ let prompt = GRAPH_EXTRACTION_PROMPT.replace('{ENTITY_TYPES}', allTypeNames.join(', '));
150
+ // If domain entity types have field definitions, add them to the prompt
151
+ if (domainEntityTypes && domainEntityTypes.length > 0) {
152
+ const typeDefinitions = domainEntityTypes.map(t => {
153
+ const fieldsStr = t.fields.length > 0
154
+ ? '\n Fields: ' + t.fields.map(f => `${f.name} (${f.type}${f.required ? ', required' : ''}: ${f.description})`).join(', ')
155
+ : '';
156
+ return `- ${t.name}: "${t.description}"${fieldsStr}`;
157
+ }).join('\n');
158
+ prompt += `\n\nCustom entity type definitions:\n${typeDefinitions}\n\nWhen you identify an entity matching a custom type, include its fields in a "properties" object:\n{"name": "Acme Corp", "entity_type": "lead", "properties": {"company_size": "enterprise", "budget_range": "$50k-100k"}}`;
159
+ }
160
+ return [
161
+ { role: 'system', content: prompt },
162
+ { role: 'user', content: `Extract entities and relationships from these facts:\n\n${factsList}` },
163
+ ];
164
+ }
165
+ /**
166
+ * Build the dedup prompt.
167
+ * Compares new facts against existing facts.
168
+ */
169
+ export function buildDedupPrompt(newFacts, existingFacts) {
170
+ const newList = newFacts.map((f, i) => `NEW[${i}]: ${f}`).join('\n');
171
+ const existingList = existingFacts.map((f, i) => `EXISTING[${i}] (lineage: ${f.lineage_id}): ${f.content}`).join('\n');
172
+ return [
173
+ { role: 'system', content: DEDUP_PROMPT },
174
+ { role: 'user', content: `--- NEW FACTS ---\n${newList}\n\n--- EXISTING FACTS ---\n${existingList}` },
175
+ ];
176
+ }
177
+ // =============================================================================
178
+ // LEGACY — Keep old function signature for backward compat during migration
179
+ // =============================================================================
180
+ export const EXTRACTION_SYSTEM_PROMPT = FACT_EXTRACTION_PROMPT;
181
+ export function buildExtractionPrompt(input, existingFacts) {
182
+ // Legacy: still used by the current pipeline
183
+ // Will be replaced by buildFactExtractionPrompt + buildGraphExtractionPrompt
184
+ let userContent = `Extract facts from this text:\n\n${input}`;
185
+ if (existingFacts && existingFacts.length > 0) {
186
+ const factsBlock = existingFacts
187
+ .map((f) => `- [lineage_id: ${f.lineage_id}] ${f.content}`)
188
+ .join('\n');
189
+ userContent += `\n\n--- EXISTING FACTS (for deduplication) ---\n${factsBlock}`;
190
+ }
191
+ return [
192
+ { role: 'system', content: FACT_EXTRACTION_PROMPT },
193
+ { role: 'user', content: userContent },
194
+ ];
195
+ }
196
+ //# sourceMappingURL=prompts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["prompts.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,qDAAqD;AACrD,gFAAgF;AAEhF,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAsDK,CAAC;AAE5C,gFAAgF;AAChF,2EAA2E;AAC3E,gFAAgF;AAEhF,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAkCf,CAAC;AAEzB,gFAAgF;AAChF,0DAA0D;AAC1D,gFAAgF;AAEhF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;mFAgBuD,CAAC;AAWpF;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,OAAO;QACL,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,sBAAsB,EAAE;QACnD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oCAAoC,KAAK,EAAE,EAAE;KACvE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAe;IACxD,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,OAAO;QACL,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,uBAAuB,EAAE;QACpD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,2DAA2D,SAAS,EAAE,EAAE;KAClG,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkB,EAAE,aAA6B;IAChF,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,UAAU,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvH,OAAO;QACL,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;QACzC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,OAAO,+BAA+B,YAAY,EAAE,EAAE;KACtG,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAEhF,MAAM,CAAC,MAAM,wBAAwB,GAAG,sBAAsB,CAAC;AAE/D,MAAM,UAAU,qBAAqB,CACnC,KAAa,EACb,aAA8B;IAE9B,6CAA6C;IAC7C,6EAA6E;IAC7E,IAAI,WAAW,GAAG,oCAAoC,KAAK,EAAE,CAAC;IAC9D,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,UAAU,GAAG,aAAa;aAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC1D,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,WAAW,IAAI,mDAAmD,UAAU,EAAE,CAAC;IACjF,CAAC;IACD,OAAO;QACL,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,sBAAsB,EAAE;QACnD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE;KACvC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["prompts.ts"],"names":[],"mappings":"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"}
@@ -51,12 +51,20 @@ For identity/trait facts, state them DIRECTLY:
51
51
  8. For EVERY fact, include "ed" (event date) if the fact describes something that happened at a specific time.
52
52
  - "User went to the gym on May 7" → ed: "2023-05-07"
53
53
  - "User prefers dark mode" → ed: null (timeless preference)
54
- If the conversation header says "[This conversation took place on 8 May, 2023]", set "dd" to "2023-05-08" for all facts.
54
+ - If only a month is mentioned (e.g., "in March 2026") and today IS in that month, use TODAY's date from the header.
55
+ - If only a month is mentioned and it's a PAST month, use the 15th as midpoint.
56
+ Set "dd" (document date) to today's date from the header for ALL facts — this is when the conversation happened.
57
+
58
+ 9. PATTERN DETECTION: If the text reveals a recurring behavior, preference, routine, or coping strategy, extract it as a pattern fact with higher importance (0.7-0.9).
59
+ - "I always procrastinate on Mondays" → {"t": "User tends to procrastinate on Mondays", "i": 0.8, "ed": null, "dd": "...", "p": true}
60
+ - "Coffee helps me focus" → {"t": "User finds that coffee helps with focus", "i": 0.7, "ed": null, "dd": "...", "p": true}
61
+ Patterns are behavioral insights — routines, coping strategies, energy patterns, recurring struggles.
62
+ Mark patterns with "p": true in the output. Regular facts use "p": false.
55
63
 
56
64
  ## OUTPUT
57
65
 
58
66
  Return ONLY a JSON object:
59
- {"facts": [{"t": "fact text here", "i": 0.7, "ed": "2023-05-07", "dd": "2023-05-08"}, {"t": "another fact", "i": 0.3, "ed": null, "dd": "2023-05-08"}]}
67
+ {"facts": [{"t": "fact text here", "i": 0.7, "ed": "2023-05-07", "dd": "2023-05-08", "p": false}, {"t": "another fact", "i": 0.3, "ed": null, "dd": "2023-05-08", "p": false}]}
60
68
 
61
69
  - ed (eventDate): ISO date string of WHEN the event occurred, or null if not temporal
62
70
  - dd (documentDate): ISO date string of when the conversation took place, from context header
@@ -150,9 +158,10 @@ export interface ExistingFact {
150
158
  * Simple: extract facts as strings.
151
159
  */
152
160
  export function buildFactExtractionPrompt(input: string): LLMMessage[] {
161
+ const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
153
162
  return [
154
163
  { role: 'system', content: FACT_EXTRACTION_PROMPT },
155
- { role: 'user', content: `Extract facts from this text:\n\n${input}` },
164
+ { role: 'user', content: `[Today's date: ${today}]\n\nExtract facts from this text:\n\n${input}` },
156
165
  ];
157
166
  }
158
167
 
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Sliding Window Inference Pipeline — like Hydra DB's context-enriched chunking.
3
+ *
4
+ * Splits long text into segments with overlapping context windows.
5
+ * Each segment gets surrounding context (lookback + lookahead) so the LLM can:
6
+ * - Resolve pronouns ("he" → "John")
7
+ * - Resolve references ("that framework" → "React")
8
+ * - Understand temporal context from earlier messages
9
+ *
10
+ * This prevents the "Orphaned Pronoun Paradox" where isolated chunks lose meaning.
11
+ */
12
+ const DEFAULT_CONFIG = {
13
+ segmentSize: 800,
14
+ hPrev: 2,
15
+ hNext: 1,
16
+ minInputLength: 3500, // Window multi-turn conversations for pronoun/temporal resolution
17
+ maxSegments: 6, // Allow more segments for better coverage
18
+ };
19
+ /**
20
+ * Split text into segments at sentence boundaries.
21
+ */
22
+ function splitIntoSegments(text, segmentSize) {
23
+ const segments = [];
24
+ let current = '';
25
+ // Split by sentences (period/exclamation/question followed by space or newline)
26
+ const sentences = text.split(/(?<=[.!?])\s+/);
27
+ for (const sentence of sentences) {
28
+ if (current.length + sentence.length > segmentSize && current.length > 0) {
29
+ segments.push(current.trim());
30
+ current = sentence;
31
+ }
32
+ else {
33
+ current += (current ? ' ' : '') + sentence;
34
+ }
35
+ }
36
+ if (current.trim())
37
+ segments.push(current.trim());
38
+ return segments;
39
+ }
40
+ /**
41
+ * Create enriched segments with sliding window context.
42
+ *
43
+ * For each segment, includes surrounding context so the LLM can resolve
44
+ * references and understand temporal relationships.
45
+ */
46
+ export function createEnrichedSegments(text, config) {
47
+ const c = { ...DEFAULT_CONFIG, ...config };
48
+ // Short inputs don't need windowing
49
+ if (text.length < c.minInputLength) {
50
+ return [{
51
+ segment: text,
52
+ contextWindow: text,
53
+ index: 0,
54
+ total: 1,
55
+ }];
56
+ }
57
+ const segments = splitIntoSegments(text, c.segmentSize);
58
+ // Cap segments to avoid excessive LLM calls
59
+ const effectiveSegments = segments.length > c.maxSegments
60
+ ? segments.slice(0, c.maxSegments)
61
+ : segments;
62
+ return effectiveSegments.map((seg, i) => {
63
+ // Build context window with lookback and lookahead
64
+ const prevStart = Math.max(0, i - c.hPrev);
65
+ const nextEnd = Math.min(segments.length - 1, i + c.hNext);
66
+ const contextBefore = segments.slice(prevStart, i).join('\n');
67
+ const contextAfter = segments.slice(i + 1, nextEnd + 1).join('\n');
68
+ let contextWindow = '';
69
+ if (contextBefore) {
70
+ contextWindow += `[PRECEDING CONTEXT — use this to resolve pronouns, references, and temporal expressions in the current segment]\n${contextBefore}\n\n`;
71
+ }
72
+ contextWindow += `[CURRENT SEGMENT — extract facts from this]\n${seg}`;
73
+ if (contextAfter) {
74
+ contextWindow += `\n\n[FOLLOWING CONTEXT]\n${contextAfter}`;
75
+ }
76
+ return {
77
+ segment: seg,
78
+ contextWindow,
79
+ index: i,
80
+ total: effectiveSegments.length,
81
+ };
82
+ });
83
+ }
84
+ //# sourceMappingURL=sliding-window.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sliding-window.js","sourceRoot":"","sources":["sliding-window.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sliding-window.js","sourceRoot":"","sources":["sliding-window.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,18 @@ export interface ExtractedFact {
23
23
  contradictsFactId?: string;
24
24
  /** Canonical names of entities mentioned in THIS fact (for precise fact-entity linking) */
25
25
  entityCanonicalNames?: string[];
26
+ /** The conversation segment this fact was extracted from */
27
+ sourceChunk?: string;
28
+ /** When the event described in the fact actually occurred */
29
+ eventDate?: Date;
30
+ /** When the conversation/document was authored */
31
+ documentDate?: Date;
32
+ /** If this fact relates to an existing one: 'updates' | 'extends' | 'derives' */
33
+ relationType?: 'updates' | 'extends' | 'derives';
34
+ /** The ID of the existing fact this relates to (set by dedup) */
35
+ relatedFactId?: string;
36
+ /** Context-enriched version of content used for embedding (not shown to users) */
37
+ contextualContent?: string;
26
38
  }
27
39
  export interface ExtractedEntity {
28
40
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE1F,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,YAAY,CAAC;IACpE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,cAAc,GAAG,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACzF,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACjG;AAED,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,cAAc,GAAG,YAAY,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE1F,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,YAAY,CAAC;IACpE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,kDAAkD;IAClD,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,iFAAiF;IACjF,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACjD,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,cAAc,GAAG,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACzF,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACjG;AAED,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,cAAc,GAAG,YAAY,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,90 @@
1
+ import { calculateDecayScore } from '../salience/decay.js';
2
+ /**
3
+ * Record memory accesses after a search.
4
+ * Also updates last_accessed and frequency on accessed facts.
5
+ * This is called fire-and-forget from the search orchestrator.
6
+ */
7
+ export async function recordAccesses(storage, tenantId, query, results, config) {
8
+ const halfLifeDays = config?.halfLifeDays ?? 30;
9
+ const normalizationK = config?.normalizationK ?? 50;
10
+ const decayUpdates = [];
11
+ for (let i = 0; i < results.length; i++) {
12
+ const result = results[i];
13
+ const id = crypto.randomUUID();
14
+ // Create access record
15
+ await storage.createMemoryAccess({
16
+ id,
17
+ tenantId,
18
+ factId: result.fact.id,
19
+ query,
20
+ retrievalMethod: result.triggeredBy ? 'trigger' : 'fusion',
21
+ similarityScore: result.signals.vectorScore > 0 ? result.signals.vectorScore : undefined,
22
+ rankPosition: i + 1,
23
+ triggerId: result.triggeredBy,
24
+ });
25
+ // Recalculate decay score with updated access time and frequency
26
+ const newFrequency = result.fact.frequency + 1;
27
+ const newDecayScore = calculateDecayScore({
28
+ importance: result.fact.importance,
29
+ frequency: newFrequency,
30
+ lastAccessed: new Date(),
31
+ halfLifeDays,
32
+ normalizationK,
33
+ });
34
+ decayUpdates.push({
35
+ id: result.fact.id,
36
+ decayScore: newDecayScore,
37
+ lastAccessed: new Date(),
38
+ frequency: newFrequency,
39
+ });
40
+ }
41
+ // Batch update decay scores (also updates last_accessed and frequency on the fact)
42
+ if (decayUpdates.length > 0) {
43
+ await storage.updateDecayScores(tenantId, decayUpdates);
44
+ }
45
+ }
46
+ /**
47
+ * Submit user feedback for a retrieved memory.
48
+ * Adjusts the fact's importance score based on feedback.
49
+ */
50
+ export async function submitFeedback(storage, tenantId, factId, feedback, config) {
51
+ // 1. Update the memory access record with feedback
52
+ await storage.updateFeedback(tenantId, factId, {
53
+ wasUseful: feedback.wasUseful,
54
+ feedbackType: feedback.feedbackType,
55
+ feedbackDetail: feedback.feedbackDetail,
56
+ wasCorrected: feedback.feedbackType === 'correction',
57
+ });
58
+ // 2. Adjust fact importance based on feedback
59
+ const fact = await storage.getFact(tenantId, factId);
60
+ if (!fact)
61
+ return;
62
+ let newImportance = fact.importance;
63
+ switch (feedback.feedbackType) {
64
+ case 'explicit_positive':
65
+ case 'implicit_positive':
66
+ newImportance = Math.min(1.0, fact.importance + 0.05);
67
+ break;
68
+ case 'explicit_negative':
69
+ case 'implicit_negative':
70
+ newImportance = Math.max(0.1, fact.importance - 0.05);
71
+ break;
72
+ case 'correction':
73
+ newImportance = Math.max(0.1, fact.importance - 0.1);
74
+ break;
75
+ }
76
+ // 3. Update the fact's importance and recalculate decay score
77
+ if (newImportance !== fact.importance) {
78
+ const newDecayScore = calculateDecayScore({
79
+ importance: newImportance,
80
+ frequency: fact.frequency,
81
+ lastAccessed: fact.lastAccessed ?? new Date(),
82
+ halfLifeDays: config?.halfLifeDays ?? 30,
83
+ normalizationK: config?.normalizationK ?? 50,
84
+ });
85
+ await storage.updateDecayScores(tenantId, [
86
+ { id: factId, decayScore: newDecayScore, importance: newImportance },
87
+ ]);
88
+ }
89
+ }
90
+ //# sourceMappingURL=tracker.js.map
@@ -12,8 +12,8 @@ export declare const ApiKeySchema: z.ZodObject<{
12
12
  createdAt: z.ZodDate;
13
13
  }, "strip", z.ZodTypeAny, {
14
14
  name: string;
15
- id: string;
16
15
  active: boolean;
16
+ id: string;
17
17
  tenantId: string;
18
18
  createdAt: Date;
19
19
  keyHash: string;
@@ -23,8 +23,8 @@ export declare const ApiKeySchema: z.ZodObject<{
23
23
  lastUsedAt: Date | null;
24
24
  }, {
25
25
  name: string;
26
- id: string;
27
26
  active: boolean;
27
+ id: string;
28
28
  tenantId: string;
29
29
  createdAt: Date;
30
30
  keyHash: string;
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import { API_KEY_SCOPES } from '../config.js';
3
+ export const ApiKeySchema = z.object({
4
+ id: z.string().uuid(),
5
+ tenantId: z.string().uuid(),
6
+ keyHash: z.string(),
7
+ keyPrefix: z.string(),
8
+ name: z.string().max(100),
9
+ scopes: z.array(z.enum(API_KEY_SCOPES)),
10
+ expiresAt: z.coerce.date().nullable(),
11
+ lastUsedAt: z.coerce.date().nullable(),
12
+ active: z.boolean(),
13
+ createdAt: z.coerce.date(),
14
+ });
15
+ export const CreateApiKeySchema = z.object({
16
+ tenantId: z.string().uuid(),
17
+ name: z.string().max(100).default('Default'),
18
+ scopes: z.array(z.enum(API_KEY_SCOPES)).default(['read', 'write']),
19
+ expiresAt: z.coerce.date().optional(),
20
+ });
21
+ //# sourceMappingURL=api-key.js.map
@@ -5,7 +5,7 @@ export declare const EdgeSchema: z.ZodObject<{
5
5
  sourceId: z.ZodString;
6
6
  targetId: z.ZodString;
7
7
  relation: z.ZodString;
8
- edgeType: z.ZodEnum<["associative", "causal", "temporal", "contradictory", "hierarchical"]>;
8
+ edgeType: z.ZodEnum<["associative", "causal", "temporal", "contradictory", "hierarchical", "updates", "extends", "derives"]>;
9
9
  weight: z.ZodDefault<z.ZodNumber>;
10
10
  validFrom: z.ZodDate;
11
11
  validUntil: z.ZodNullable<z.ZodDate>;
@@ -24,7 +24,7 @@ export declare const EdgeSchema: z.ZodObject<{
24
24
  sourceId: string;
25
25
  targetId: string;
26
26
  relation: string;
27
- edgeType: "associative" | "causal" | "temporal" | "contradictory" | "hierarchical";
27
+ edgeType: "temporal" | "associative" | "causal" | "contradictory" | "hierarchical" | "updates" | "extends" | "derives";
28
28
  weight: number;
29
29
  factId: string | null;
30
30
  }, {
@@ -38,7 +38,7 @@ export declare const EdgeSchema: z.ZodObject<{
38
38
  sourceId: string;
39
39
  targetId: string;
40
40
  relation: string;
41
- edgeType: "associative" | "causal" | "temporal" | "contradictory" | "hierarchical";
41
+ edgeType: "temporal" | "associative" | "causal" | "contradictory" | "hierarchical" | "updates" | "extends" | "derives";
42
42
  factId: string | null;
43
43
  weight?: number | undefined;
44
44
  }>;
@@ -48,7 +48,7 @@ export declare const CreateEdgeSchema: z.ZodObject<{
48
48
  sourceId: z.ZodString;
49
49
  targetId: z.ZodString;
50
50
  relation: z.ZodString;
51
- edgeType: z.ZodEnum<["associative", "causal", "temporal", "contradictory", "hierarchical"]>;
51
+ edgeType: z.ZodEnum<["associative", "causal", "temporal", "contradictory", "hierarchical", "updates", "extends", "derives"]>;
52
52
  weight: z.ZodDefault<z.ZodNumber>;
53
53
  factId: z.ZodOptional<z.ZodString>;
54
54
  confidence: z.ZodDefault<z.ZodNumber>;
@@ -60,7 +60,7 @@ export declare const CreateEdgeSchema: z.ZodObject<{
60
60
  sourceId: string;
61
61
  targetId: string;
62
62
  relation: string;
63
- edgeType: "associative" | "causal" | "temporal" | "contradictory" | "hierarchical";
63
+ edgeType: "temporal" | "associative" | "causal" | "contradictory" | "hierarchical" | "updates" | "extends" | "derives";
64
64
  weight: number;
65
65
  factId?: string | undefined;
66
66
  }, {
@@ -68,7 +68,7 @@ export declare const CreateEdgeSchema: z.ZodObject<{
68
68
  sourceId: string;
69
69
  targetId: string;
70
70
  relation: string;
71
- edgeType: "associative" | "causal" | "temporal" | "contradictory" | "hierarchical";
71
+ edgeType: "temporal" | "associative" | "causal" | "contradictory" | "hierarchical" | "updates" | "extends" | "derives";
72
72
  confidence?: number | undefined;
73
73
  metadata?: Record<string, unknown> | undefined;
74
74
  weight?: number | undefined;
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import { unitFloat, EDGE_TYPES } from '../config.js';
3
+ export const EdgeSchema = z.object({
4
+ id: z.string().uuid(),
5
+ tenantId: z.string().uuid(),
6
+ sourceId: z.string().uuid(),
7
+ targetId: z.string().uuid(),
8
+ relation: z.string(),
9
+ edgeType: z.enum(EDGE_TYPES),
10
+ weight: z.number().min(0).default(1.0),
11
+ validFrom: z.coerce.date(),
12
+ validUntil: z.coerce.date().nullable(),
13
+ factId: z.string().uuid().nullable(),
14
+ confidence: unitFloat,
15
+ metadata: z.record(z.string(), z.unknown()),
16
+ createdAt: z.coerce.date(),
17
+ });
18
+ export const CreateEdgeSchema = z.object({
19
+ tenantId: z.string().uuid(),
20
+ sourceId: z.string().uuid(),
21
+ targetId: z.string().uuid(),
22
+ relation: z.string(),
23
+ edgeType: z.enum(EDGE_TYPES),
24
+ weight: z.number().min(0).default(1.0),
25
+ factId: z.string().uuid().optional(),
26
+ confidence: unitFloat.default(0.8),
27
+ metadata: z.record(z.string(), z.unknown()).default({}),
28
+ });
29
+ //# sourceMappingURL=edge.js.map
@@ -13,9 +13,9 @@ export declare const EntitySchema: z.ZodObject<{
13
13
  updatedAt: z.ZodDate;
14
14
  }, "strip", z.ZodTypeAny, {
15
15
  name: string;
16
- id: string;
17
16
  embeddingModel: string | null;
18
17
  embeddingDim: number | null;
18
+ id: string;
19
19
  tenantId: string;
20
20
  createdAt: Date;
21
21
  entityType: string;
@@ -25,9 +25,9 @@ export declare const EntitySchema: z.ZodObject<{
25
25
  updatedAt: Date;
26
26
  }, {
27
27
  name: string;
28
- id: string;
29
28
  embeddingModel: string | null;
30
29
  embeddingDim: number | null;
30
+ id: string;
31
31
  tenantId: string;
32
32
  createdAt: Date;
33
33
  entityType: string;
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ export const EntitySchema = z.object({
3
+ id: z.string().uuid(),
4
+ tenantId: z.string().uuid(),
5
+ name: z.string().min(1).max(500),
6
+ entityType: z.string().min(1),
7
+ canonicalName: z.string().min(1),
8
+ properties: z.record(z.string(), z.unknown()),
9
+ embeddingModel: z.string().nullable(),
10
+ embeddingDim: z.number().int().positive().nullable(),
11
+ mergeTargetId: z.string().uuid().nullable(),
12
+ createdAt: z.coerce.date(),
13
+ updatedAt: z.coerce.date(),
14
+ });
15
+ export const CreateEntitySchema = z.object({
16
+ tenantId: z.string().uuid(),
17
+ name: z.string().min(1).max(500),
18
+ entityType: z.string().min(1),
19
+ canonicalName: z.string().min(1),
20
+ properties: z.record(z.string(), z.unknown()).default({}),
21
+ });
22
+ //# sourceMappingURL=entity.js.map