@kybernesis/brain-core 0.1.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 (129) hide show
  1. package/dist/claude-call.d.ts +37 -0
  2. package/dist/claude-call.d.ts.map +1 -0
  3. package/dist/claude-call.js +55 -0
  4. package/dist/claude-call.js.map +1 -0
  5. package/dist/entity-graph.d.ts +79 -0
  6. package/dist/entity-graph.d.ts.map +1 -0
  7. package/dist/entity-graph.js +611 -0
  8. package/dist/entity-graph.js.map +1 -0
  9. package/dist/fact-contradiction.d.ts +25 -0
  10. package/dist/fact-contradiction.d.ts.map +1 -0
  11. package/dist/fact-contradiction.js +109 -0
  12. package/dist/fact-contradiction.js.map +1 -0
  13. package/dist/fact-extractor.d.ts +26 -0
  14. package/dist/fact-extractor.d.ts.map +1 -0
  15. package/dist/fact-extractor.js +98 -0
  16. package/dist/fact-extractor.js.map +1 -0
  17. package/dist/fact-retrieval.d.ts +51 -0
  18. package/dist/fact-retrieval.d.ts.map +1 -0
  19. package/dist/fact-retrieval.js +401 -0
  20. package/dist/fact-retrieval.js.map +1 -0
  21. package/dist/fact-store.d.ts +26 -0
  22. package/dist/fact-store.d.ts.map +1 -0
  23. package/dist/fact-store.js +181 -0
  24. package/dist/fact-store.js.map +1 -0
  25. package/dist/fact-temporal.d.ts +20 -0
  26. package/dist/fact-temporal.d.ts.map +1 -0
  27. package/dist/fact-temporal.js +79 -0
  28. package/dist/fact-temporal.js.map +1 -0
  29. package/dist/fts-sanitizer.d.ts +32 -0
  30. package/dist/fts-sanitizer.d.ts.map +1 -0
  31. package/dist/fts-sanitizer.js +61 -0
  32. package/dist/fts-sanitizer.js.map +1 -0
  33. package/dist/hybrid-search.d.ts +44 -0
  34. package/dist/hybrid-search.d.ts.map +1 -0
  35. package/dist/hybrid-search.js +410 -0
  36. package/dist/hybrid-search.js.map +1 -0
  37. package/dist/index.d.ts +33 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +19 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/ops.d.ts +28 -0
  42. package/dist/ops.d.ts.map +1 -0
  43. package/dist/ops.js +307 -0
  44. package/dist/ops.js.map +1 -0
  45. package/dist/queue.d.ts +13 -0
  46. package/dist/queue.d.ts.map +1 -0
  47. package/dist/queue.js +28 -0
  48. package/dist/queue.js.map +1 -0
  49. package/dist/relationship-extractor.d.ts +38 -0
  50. package/dist/relationship-extractor.d.ts.map +1 -0
  51. package/dist/relationship-extractor.js +119 -0
  52. package/dist/relationship-extractor.js.map +1 -0
  53. package/dist/sleep/config.d.ts +47 -0
  54. package/dist/sleep/config.d.ts.map +1 -0
  55. package/dist/sleep/config.js +46 -0
  56. package/dist/sleep/config.js.map +1 -0
  57. package/dist/sleep/db.d.ts +16 -0
  58. package/dist/sleep/db.d.ts.map +1 -0
  59. package/dist/sleep/db.js +114 -0
  60. package/dist/sleep/db.js.map +1 -0
  61. package/dist/sleep/index.d.ts +69 -0
  62. package/dist/sleep/index.d.ts.map +1 -0
  63. package/dist/sleep/index.js +99 -0
  64. package/dist/sleep/index.js.map +1 -0
  65. package/dist/sleep/steps/consolidate.d.ts +19 -0
  66. package/dist/sleep/steps/consolidate.d.ts.map +1 -0
  67. package/dist/sleep/steps/consolidate.js +74 -0
  68. package/dist/sleep/steps/consolidate.js.map +1 -0
  69. package/dist/sleep/steps/decay.d.ts +19 -0
  70. package/dist/sleep/steps/decay.d.ts.map +1 -0
  71. package/dist/sleep/steps/decay.js +121 -0
  72. package/dist/sleep/steps/decay.js.map +1 -0
  73. package/dist/sleep/steps/entity-hygiene.d.ts +29 -0
  74. package/dist/sleep/steps/entity-hygiene.d.ts.map +1 -0
  75. package/dist/sleep/steps/entity-hygiene.js +452 -0
  76. package/dist/sleep/steps/entity-hygiene.js.map +1 -0
  77. package/dist/sleep/steps/link.d.ts +20 -0
  78. package/dist/sleep/steps/link.d.ts.map +1 -0
  79. package/dist/sleep/steps/link.js +216 -0
  80. package/dist/sleep/steps/link.js.map +1 -0
  81. package/dist/sleep/steps/observe.d.ts +17 -0
  82. package/dist/sleep/steps/observe.d.ts.map +1 -0
  83. package/dist/sleep/steps/observe.js +192 -0
  84. package/dist/sleep/steps/observe.js.map +1 -0
  85. package/dist/sleep/steps/profile.d.ts +15 -0
  86. package/dist/sleep/steps/profile.d.ts.map +1 -0
  87. package/dist/sleep/steps/profile.js +26 -0
  88. package/dist/sleep/steps/profile.js.map +1 -0
  89. package/dist/sleep/steps/reasoning.d.ts +19 -0
  90. package/dist/sleep/steps/reasoning.d.ts.map +1 -0
  91. package/dist/sleep/steps/reasoning.js +173 -0
  92. package/dist/sleep/steps/reasoning.js.map +1 -0
  93. package/dist/sleep/steps/summarize.d.ts +21 -0
  94. package/dist/sleep/steps/summarize.d.ts.map +1 -0
  95. package/dist/sleep/steps/summarize.js +206 -0
  96. package/dist/sleep/steps/summarize.js.map +1 -0
  97. package/dist/sleep/steps/tag.d.ts +16 -0
  98. package/dist/sleep/steps/tag.d.ts.map +1 -0
  99. package/dist/sleep/steps/tag.js +84 -0
  100. package/dist/sleep/steps/tag.js.map +1 -0
  101. package/dist/sleep/steps/tier.d.ts +18 -0
  102. package/dist/sleep/steps/tier.d.ts.map +1 -0
  103. package/dist/sleep/steps/tier.js +112 -0
  104. package/dist/sleep/steps/tier.js.map +1 -0
  105. package/dist/sleep/utils/checkpoint.d.ts +11 -0
  106. package/dist/sleep/utils/checkpoint.d.ts.map +1 -0
  107. package/dist/sleep/utils/checkpoint.js +26 -0
  108. package/dist/sleep/utils/checkpoint.js.map +1 -0
  109. package/dist/sleep/utils/jaccard.d.ts +7 -0
  110. package/dist/sleep/utils/jaccard.d.ts.map +1 -0
  111. package/dist/sleep/utils/jaccard.js +19 -0
  112. package/dist/sleep/utils/jaccard.js.map +1 -0
  113. package/dist/store-conversation.d.ts +29 -0
  114. package/dist/store-conversation.d.ts.map +1 -0
  115. package/dist/store-conversation.js +227 -0
  116. package/dist/store-conversation.js.map +1 -0
  117. package/dist/timeline.d.ts +50 -0
  118. package/dist/timeline.d.ts.map +1 -0
  119. package/dist/timeline.js +389 -0
  120. package/dist/timeline.js.map +1 -0
  121. package/dist/user-profile.d.ts +30 -0
  122. package/dist/user-profile.d.ts.map +1 -0
  123. package/dist/user-profile.js +147 -0
  124. package/dist/user-profile.js.map +1 -0
  125. package/dist/vectors.d.ts +56 -0
  126. package/dist/vectors.d.ts.map +1 -0
  127. package/dist/vectors.js +132 -0
  128. package/dist/vectors.js.map +1 -0
  129. package/package.json +53 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Fact temporal expiry detection — port of KAD's fact-temporal.ts.
3
+ *
4
+ * Detects time-bound facts ("I have an exam tomorrow") and computes
5
+ * absolute expiry dates so they automatically disappear from retrieval.
6
+ * Pure heuristic — no LLM needed.
7
+ */
8
+ export interface TemporalResult {
9
+ is_temporal: boolean;
10
+ expires_at: string | null;
11
+ temporal_expression: string | null;
12
+ }
13
+ /**
14
+ * Detect time-bound expressions in a fact and compute an absolute expiry.
15
+ *
16
+ * @param factContent - The extracted fact text
17
+ * @param factTimestamp - ISO 8601 timestamp of the source conversation
18
+ */
19
+ export declare function detectTemporalExpiry(factContent: string, factTimestamp: string): TemporalResult;
20
+ //# sourceMappingURL=fact-temporal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-temporal.d.ts","sourceRoot":"","sources":["../src/fact-temporal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AA+BD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,cAAc,CA2C/F"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Fact temporal expiry detection — port of KAD's fact-temporal.ts.
3
+ *
4
+ * Detects time-bound facts ("I have an exam tomorrow") and computes
5
+ * absolute expiry dates so they automatically disappear from retrieval.
6
+ * Pure heuristic — no LLM needed.
7
+ */
8
+ const DAY_NAMES = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
9
+ function addDays(date, days) {
10
+ const result = new Date(date);
11
+ result.setUTCDate(result.getUTCDate() + days);
12
+ return result;
13
+ }
14
+ function endOfMonth(year, month) {
15
+ return new Date(Date.UTC(year, month, 1));
16
+ }
17
+ function nextWeekday(from, targetDay) {
18
+ const current = from.getUTCDay();
19
+ let diff = targetDay - current;
20
+ if (diff <= 0)
21
+ diff += 7;
22
+ return addDays(from, diff);
23
+ }
24
+ function startOfDayUTC(date) {
25
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
26
+ }
27
+ function result(expiresAt, expression) {
28
+ return { is_temporal: true, expires_at: expiresAt.toISOString(), temporal_expression: expression };
29
+ }
30
+ const NO_MATCH = { is_temporal: false, expires_at: null, temporal_expression: null };
31
+ /**
32
+ * Detect time-bound expressions in a fact and compute an absolute expiry.
33
+ *
34
+ * @param factContent - The extracted fact text
35
+ * @param factTimestamp - ISO 8601 timestamp of the source conversation
36
+ */
37
+ export function detectTemporalExpiry(factContent, factTimestamp) {
38
+ const text = factContent.toLowerCase();
39
+ const base = startOfDayUTC(new Date(factTimestamp));
40
+ if (/\bthis\s+weekend\b/.test(text)) {
41
+ return result(nextWeekday(base, 1), 'this weekend');
42
+ }
43
+ if (/\bnext\s+week\b/.test(text)) {
44
+ return result(addDays(base, 14), 'next week');
45
+ }
46
+ if (/\bthis\s+week\b/.test(text)) {
47
+ return result(addDays(base, 7), 'this week');
48
+ }
49
+ if (/\bnext\s+month\b/.test(text)) {
50
+ const nextMonthIdx = base.getUTCMonth() + 2;
51
+ const year = nextMonthIdx > 12 ? base.getUTCFullYear() + 1 : base.getUTCFullYear();
52
+ const month = nextMonthIdx > 12 ? nextMonthIdx - 12 : nextMonthIdx;
53
+ return result(endOfMonth(year, month), 'next month');
54
+ }
55
+ if (/\bthis\s+month\b/.test(text)) {
56
+ return result(endOfMonth(base.getUTCFullYear(), base.getUTCMonth() + 1), 'this month');
57
+ }
58
+ if (/\btomorrow\b/.test(text)) {
59
+ return result(addDays(base, 2), 'tomorrow');
60
+ }
61
+ if (/\b(today|tonight)\b/.test(text)) {
62
+ return result(addDays(base, 1), text.match(/\b(today|tonight)\b/)[0]);
63
+ }
64
+ for (let i = 0; i < DAY_NAMES.length; i++) {
65
+ const dayName = DAY_NAMES[i];
66
+ const thisPattern = new RegExp(`\\bthis\\s+${dayName}\\b`);
67
+ const nextPattern = new RegExp(`\\bnext\\s+${dayName}\\b`);
68
+ if (thisPattern.test(text) || nextPattern.test(text)) {
69
+ const target = nextWeekday(base, i);
70
+ return result(addDays(target, 1), text.match(thisPattern)?.[0] || text.match(nextPattern)[0]);
71
+ }
72
+ }
73
+ if (/\bupcoming\b/.test(text))
74
+ return result(addDays(base, 30), 'upcoming');
75
+ if (/\bsoon\b/.test(text))
76
+ return result(addDays(base, 14), 'soon');
77
+ return NO_MATCH;
78
+ }
79
+ //# sourceMappingURL=fact-temporal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-temporal.js","sourceRoot":"","sources":["../src/fact-temporal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * FTS5 query sanitizer — Bug Fix #2.
3
+ *
4
+ * KAD's naive sanitizer (`replace(/[?.,!'"]/g, '')`) silently breaks on:
5
+ * - Hyphenated terms: "post-mortem" → FTS5 reads as "post NOT mortem"
6
+ * - Unicode: "café" — the quote-strip mangled accented chars
7
+ * - FTS5 operator tokens: bare AND/OR/NOT/NEAR are taken as operators
8
+ * - Quoted phrases: embedded quotes broke the MATCH string
9
+ *
10
+ * This helper is the single shared sanitizer for all three FTS call sites:
11
+ * 1. `searchFactsFts` in fact-store.ts
12
+ * 2. `searchFactsDirect` in fact-retrieval.ts
13
+ * 3. `queryTimeline` FTS MATCH path in timeline.ts
14
+ */
15
+ /**
16
+ * Convert a natural-language query into a safe FTS5 MATCH expression.
17
+ *
18
+ * Strategy:
19
+ * - Normalize unicode (NFC)
20
+ * - Split on whitespace
21
+ * - Strip leading/trailing punctuation from each token
22
+ * - Replace hyphens with spaces (FTS5 leading `-` = NOT operator)
23
+ * - Drop FTS5 reserved operator words
24
+ * - Drop tokens shorter than 2 chars
25
+ * - Double-quote each token so FTS5 treats it as a literal phrase
26
+ * - Join with OR for broad matching
27
+ *
28
+ * Returns empty string when no usable tokens remain; callers must
29
+ * skip the FTS query entirely on empty string to avoid a syntax error.
30
+ */
31
+ export declare function sanitizeFtsQuery(query: string): string;
32
+ //# sourceMappingURL=fts-sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-sanitizer.d.ts","sourceRoot":"","sources":["../src/fts-sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA2BtD"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * FTS5 query sanitizer — Bug Fix #2.
3
+ *
4
+ * KAD's naive sanitizer (`replace(/[?.,!'"]/g, '')`) silently breaks on:
5
+ * - Hyphenated terms: "post-mortem" → FTS5 reads as "post NOT mortem"
6
+ * - Unicode: "café" — the quote-strip mangled accented chars
7
+ * - FTS5 operator tokens: bare AND/OR/NOT/NEAR are taken as operators
8
+ * - Quoted phrases: embedded quotes broke the MATCH string
9
+ *
10
+ * This helper is the single shared sanitizer for all three FTS call sites:
11
+ * 1. `searchFactsFts` in fact-store.ts
12
+ * 2. `searchFactsDirect` in fact-retrieval.ts
13
+ * 3. `queryTimeline` FTS MATCH path in timeline.ts
14
+ */
15
+ const FTS5_RESERVED = new Set(['NEAR', 'AND', 'OR', 'NOT']);
16
+ /**
17
+ * Convert a natural-language query into a safe FTS5 MATCH expression.
18
+ *
19
+ * Strategy:
20
+ * - Normalize unicode (NFC)
21
+ * - Split on whitespace
22
+ * - Strip leading/trailing punctuation from each token
23
+ * - Replace hyphens with spaces (FTS5 leading `-` = NOT operator)
24
+ * - Drop FTS5 reserved operator words
25
+ * - Drop tokens shorter than 2 chars
26
+ * - Double-quote each token so FTS5 treats it as a literal phrase
27
+ * - Join with OR for broad matching
28
+ *
29
+ * Returns empty string when no usable tokens remain; callers must
30
+ * skip the FTS query entirely on empty string to avoid a syntax error.
31
+ */
32
+ export function sanitizeFtsQuery(query) {
33
+ const normalized = query.normalize('NFC');
34
+ const rawTokens = normalized.split(/\s+/).filter(Boolean);
35
+ const quoted = [];
36
+ for (const raw of rawTokens) {
37
+ // Strip leading/trailing punctuation (preserve inner chars including unicode)
38
+ const stripped = raw.replace(/^[.,!?;:'"()\[\]{}|\\^*]+|[.,!?;:'"()\[\]{}|\\^*]+$/g, '');
39
+ if (!stripped)
40
+ continue;
41
+ // Replace hyphens with spaces — FTS5 treats leading "-" as NOT operator
42
+ const dehyphenated = stripped.replace(/-/g, ' ').trim();
43
+ if (!dehyphenated)
44
+ continue;
45
+ // Split any sub-tokens produced by hyphen removal
46
+ for (const sub of dehyphenated.split(/\s+/)) {
47
+ if (sub.length < 2)
48
+ continue;
49
+ // Skip bare FTS5 reserved words
50
+ if (FTS5_RESERVED.has(sub.toUpperCase()))
51
+ continue;
52
+ // Escape embedded double-quotes and wrap in quotes
53
+ const escaped = sub.replace(/"/g, '');
54
+ if (!escaped)
55
+ continue;
56
+ quoted.push(`"${escaped}"`);
57
+ }
58
+ }
59
+ return quoted.join(' OR ');
60
+ }
61
+ //# sourceMappingURL=fts-sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-sanitizer.js","sourceRoot":"","sources":["../src/fts-sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAE5D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,8EAA8E;QAC9E,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,sDAAsD,EAAE,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,wEAAwE;QACxE,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACxD,IAAI,CAAC,YAAY;YAAE,SAAS;QAE5B,kDAAkD;QAClD,KAAK,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,SAAS;YAC7B,gCAAgC;YAChC,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBAAE,SAAS;YACnD,mDAAmD;YACnD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtC,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,MAAM,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Hybrid search — port of KAD's hybrid-search.ts.
3
+ *
4
+ * Combines semantic search (vectors), keyword search (FTS5), and temporal
5
+ * search. Uses Reciprocal Rank Fusion (RRF k=60) to merge ranked lists.
6
+ *
7
+ * Bug Fix #3: the null `getSleepDb` stub that made sleep-edge enrichment
8
+ * dead code has been deleted. Uses `getDb(t, 'sleep')` directly.
9
+ */
10
+ import type { TenantContext } from '@kybernesis/brain-contracts';
11
+ export interface HybridSearchResult {
12
+ id: string;
13
+ title: string;
14
+ content: string;
15
+ source_path: string;
16
+ timestamp: string;
17
+ type: string;
18
+ tier?: string;
19
+ priority?: number;
20
+ tags?: string[];
21
+ semanticScore: number;
22
+ metadataScore: number;
23
+ hybridScore: number;
24
+ matchType: 'semantic' | 'keyword' | 'both';
25
+ relatedMemories?: string[];
26
+ }
27
+ export interface HybridSearchOptions {
28
+ limit?: number;
29
+ tier?: 'hot' | 'warm' | 'archive' | 'all';
30
+ minPriority?: number;
31
+ includeRelated?: boolean;
32
+ semanticWeight?: number;
33
+ metadataWeight?: number;
34
+ type?: 'conversation' | 'idea' | 'file' | 'transcript' | 'note';
35
+ entity?: string;
36
+ entityMatch?: 'all' | 'any';
37
+ after?: Date;
38
+ before?: Date;
39
+ expandQuery?: boolean;
40
+ factFirst?: boolean;
41
+ rerank?: boolean;
42
+ }
43
+ export declare function hybridSearch(t: TenantContext, query: string, options?: HybridSearchOptions): Promise<HybridSearchResult[]>;
44
+ //# sourceMappingURL=hybrid-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid-search.d.ts","sourceRoot":"","sources":["../src/hybrid-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAKjE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3C,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,MAAM,CAAC;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC5B,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA2QD,wBAAsB,YAAY,CAChC,CAAC,EAAE,aAAa,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CA8K/B"}