@pella-labs/pinakes 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 (174) hide show
  1. package/README.md +208 -0
  2. package/dist/cli/audit.d.ts +30 -0
  3. package/dist/cli/audit.d.ts.map +1 -0
  4. package/dist/cli/audit.js +49 -0
  5. package/dist/cli/audit.js.map +1 -0
  6. package/dist/cli/export.d.ts +32 -0
  7. package/dist/cli/export.d.ts.map +1 -0
  8. package/dist/cli/export.js +73 -0
  9. package/dist/cli/export.js.map +1 -0
  10. package/dist/cli/import.d.ts +24 -0
  11. package/dist/cli/import.d.ts.map +1 -0
  12. package/dist/cli/import.js +96 -0
  13. package/dist/cli/import.js.map +1 -0
  14. package/dist/cli/index.d.ts +3 -0
  15. package/dist/cli/index.d.ts.map +1 -0
  16. package/dist/cli/index.js +172 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/cli/purge.d.ts +23 -0
  19. package/dist/cli/purge.d.ts.map +1 -0
  20. package/dist/cli/purge.js +57 -0
  21. package/dist/cli/purge.js.map +1 -0
  22. package/dist/cli/rebuild.d.ts +54 -0
  23. package/dist/cli/rebuild.d.ts.map +1 -0
  24. package/dist/cli/rebuild.js +113 -0
  25. package/dist/cli/rebuild.js.map +1 -0
  26. package/dist/cli/serve.d.ts +49 -0
  27. package/dist/cli/serve.d.ts.map +1 -0
  28. package/dist/cli/serve.js +296 -0
  29. package/dist/cli/serve.js.map +1 -0
  30. package/dist/cli/status.d.ts +39 -0
  31. package/dist/cli/status.d.ts.map +1 -0
  32. package/dist/cli/status.js +108 -0
  33. package/dist/cli/status.js.map +1 -0
  34. package/dist/db/client.d.ts +109 -0
  35. package/dist/db/client.d.ts.map +1 -0
  36. package/dist/db/client.js +175 -0
  37. package/dist/db/client.js.map +1 -0
  38. package/dist/db/repository.d.ts +82 -0
  39. package/dist/db/repository.d.ts.map +1 -0
  40. package/dist/db/repository.js +173 -0
  41. package/dist/db/repository.js.map +1 -0
  42. package/dist/db/schema.d.ts +990 -0
  43. package/dist/db/schema.d.ts.map +1 -0
  44. package/dist/db/schema.js +259 -0
  45. package/dist/db/schema.js.map +1 -0
  46. package/dist/db/types.d.ts +28 -0
  47. package/dist/db/types.d.ts.map +1 -0
  48. package/dist/db/types.js +11 -0
  49. package/dist/db/types.js.map +1 -0
  50. package/dist/gaps/detector.d.ts +67 -0
  51. package/dist/gaps/detector.d.ts.map +1 -0
  52. package/dist/gaps/detector.js +160 -0
  53. package/dist/gaps/detector.js.map +1 -0
  54. package/dist/gate/budget.d.ts +90 -0
  55. package/dist/gate/budget.d.ts.map +1 -0
  56. package/dist/gate/budget.js +145 -0
  57. package/dist/gate/budget.js.map +1 -0
  58. package/dist/ingest/chokidar.d.ts +33 -0
  59. package/dist/ingest/chokidar.d.ts.map +1 -0
  60. package/dist/ingest/chokidar.js +152 -0
  61. package/dist/ingest/chokidar.js.map +1 -0
  62. package/dist/ingest/ingester.d.ts +117 -0
  63. package/dist/ingest/ingester.d.ts.map +1 -0
  64. package/dist/ingest/ingester.js +312 -0
  65. package/dist/ingest/ingester.js.map +1 -0
  66. package/dist/ingest/manifest.d.ts +87 -0
  67. package/dist/ingest/manifest.d.ts.map +1 -0
  68. package/dist/ingest/manifest.js +223 -0
  69. package/dist/ingest/manifest.js.map +1 -0
  70. package/dist/ingest/memory-store.d.ts +55 -0
  71. package/dist/ingest/memory-store.d.ts.map +1 -0
  72. package/dist/ingest/memory-store.js +94 -0
  73. package/dist/ingest/memory-store.js.map +1 -0
  74. package/dist/ingest/parse/chunk.d.ts +15 -0
  75. package/dist/ingest/parse/chunk.d.ts.map +1 -0
  76. package/dist/ingest/parse/chunk.js +88 -0
  77. package/dist/ingest/parse/chunk.js.map +1 -0
  78. package/dist/ingest/parse/markdown.d.ts +64 -0
  79. package/dist/ingest/parse/markdown.d.ts.map +1 -0
  80. package/dist/ingest/parse/markdown.js +152 -0
  81. package/dist/ingest/parse/markdown.js.map +1 -0
  82. package/dist/ingest/queue.d.ts +21 -0
  83. package/dist/ingest/queue.d.ts.map +1 -0
  84. package/dist/ingest/queue.js +24 -0
  85. package/dist/ingest/queue.js.map +1 -0
  86. package/dist/ingest/source.d.ts +42 -0
  87. package/dist/ingest/source.d.ts.map +1 -0
  88. package/dist/ingest/source.js +19 -0
  89. package/dist/ingest/source.js.map +1 -0
  90. package/dist/mcp/envelope.d.ts +73 -0
  91. package/dist/mcp/envelope.d.ts.map +1 -0
  92. package/dist/mcp/envelope.js +46 -0
  93. package/dist/mcp/envelope.js.map +1 -0
  94. package/dist/mcp/tools/execute.d.ts +55 -0
  95. package/dist/mcp/tools/execute.d.ts.map +1 -0
  96. package/dist/mcp/tools/execute.js +232 -0
  97. package/dist/mcp/tools/execute.js.map +1 -0
  98. package/dist/mcp/tools/search.d.ts +53 -0
  99. package/dist/mcp/tools/search.d.ts.map +1 -0
  100. package/dist/mcp/tools/search.js +114 -0
  101. package/dist/mcp/tools/search.js.map +1 -0
  102. package/dist/observability/audit.d.ts +25 -0
  103. package/dist/observability/audit.d.ts.map +1 -0
  104. package/dist/observability/audit.js +38 -0
  105. package/dist/observability/audit.js.map +1 -0
  106. package/dist/observability/logger.d.ts +4 -0
  107. package/dist/observability/logger.d.ts.map +1 -0
  108. package/dist/observability/logger.js +56 -0
  109. package/dist/observability/logger.js.map +1 -0
  110. package/dist/observability/metrics.d.ts +38 -0
  111. package/dist/observability/metrics.d.ts.map +1 -0
  112. package/dist/observability/metrics.js +64 -0
  113. package/dist/observability/metrics.js.map +1 -0
  114. package/dist/retrieval/embedder.d.ts +130 -0
  115. package/dist/retrieval/embedder.d.ts.map +1 -0
  116. package/dist/retrieval/embedder.js +278 -0
  117. package/dist/retrieval/embedder.js.map +1 -0
  118. package/dist/retrieval/fts.d.ts +42 -0
  119. package/dist/retrieval/fts.d.ts.map +1 -0
  120. package/dist/retrieval/fts.js +46 -0
  121. package/dist/retrieval/fts.js.map +1 -0
  122. package/dist/retrieval/hybrid.d.ts +43 -0
  123. package/dist/retrieval/hybrid.d.ts.map +1 -0
  124. package/dist/retrieval/hybrid.js +120 -0
  125. package/dist/retrieval/hybrid.js.map +1 -0
  126. package/dist/retrieval/vec.d.ts +39 -0
  127. package/dist/retrieval/vec.d.ts.map +1 -0
  128. package/dist/retrieval/vec.js +50 -0
  129. package/dist/retrieval/vec.js.map +1 -0
  130. package/dist/sandbox/bindings/budget.d.ts +10 -0
  131. package/dist/sandbox/bindings/budget.d.ts.map +1 -0
  132. package/dist/sandbox/bindings/budget.js +44 -0
  133. package/dist/sandbox/bindings/budget.js.map +1 -0
  134. package/dist/sandbox/bindings/install.d.ts +23 -0
  135. package/dist/sandbox/bindings/install.d.ts.map +1 -0
  136. package/dist/sandbox/bindings/install.js +15 -0
  137. package/dist/sandbox/bindings/install.js.map +1 -0
  138. package/dist/sandbox/bindings/kg.d.ts +29 -0
  139. package/dist/sandbox/bindings/kg.d.ts.map +1 -0
  140. package/dist/sandbox/bindings/kg.js +323 -0
  141. package/dist/sandbox/bindings/kg.js.map +1 -0
  142. package/dist/sandbox/bindings/logger.d.ts +11 -0
  143. package/dist/sandbox/bindings/logger.d.ts.map +1 -0
  144. package/dist/sandbox/bindings/logger.js +33 -0
  145. package/dist/sandbox/bindings/logger.js.map +1 -0
  146. package/dist/sandbox/bindings/write.d.ts +34 -0
  147. package/dist/sandbox/bindings/write.d.ts.map +1 -0
  148. package/dist/sandbox/bindings/write.js +195 -0
  149. package/dist/sandbox/bindings/write.js.map +1 -0
  150. package/dist/sandbox/executor.d.ts +68 -0
  151. package/dist/sandbox/executor.d.ts.map +1 -0
  152. package/dist/sandbox/executor.js +280 -0
  153. package/dist/sandbox/executor.js.map +1 -0
  154. package/dist/sandbox/helpers.d.ts +26 -0
  155. package/dist/sandbox/helpers.d.ts.map +1 -0
  156. package/dist/sandbox/helpers.js +131 -0
  157. package/dist/sandbox/helpers.js.map +1 -0
  158. package/dist/sandbox/pool.d.ts +63 -0
  159. package/dist/sandbox/pool.d.ts.map +1 -0
  160. package/dist/sandbox/pool.js +98 -0
  161. package/dist/sandbox/pool.js.map +1 -0
  162. package/dist/sandbox/vendored-codemode.d.ts +99 -0
  163. package/dist/sandbox/vendored-codemode.d.ts.map +1 -0
  164. package/dist/sandbox/vendored-codemode.js +471 -0
  165. package/dist/sandbox/vendored-codemode.js.map +1 -0
  166. package/dist/server.d.ts +3 -0
  167. package/dist/server.d.ts.map +1 -0
  168. package/dist/server.js +74 -0
  169. package/dist/server.js.map +1 -0
  170. package/dist/spike.d.ts +15 -0
  171. package/dist/spike.d.ts.map +1 -0
  172. package/dist/spike.js +90 -0
  173. package/dist/spike.js.map +1 -0
  174. package/package.json +60 -0
@@ -0,0 +1,42 @@
1
+ import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
2
+ /**
3
+ * FTS5 full-text search with BM25 ranking and optional snippet extraction.
4
+ *
5
+ * Extracted from `src/sandbox/bindings/kg.ts` so both the sandbox bindings
6
+ * and the `kg_search` MCP tool can share the same query logic. The binding
7
+ * delegates here; the MCP tool calls `hybridSearch` which calls here.
8
+ *
9
+ * **Tokenizer**: `unicode61 remove_diacritics 2` (set in the migration).
10
+ * **NOT trigram** — it triples the DB size (presearch.md §Loop 0 gotcha).
11
+ *
12
+ * **Query escaping**: each whitespace-separated token is wrapped in double
13
+ * quotes so user/LLM-provided input can't trigger FTS5 syntax errors.
14
+ */
15
+ export interface FtsResult {
16
+ id: string;
17
+ text: string;
18
+ snippet: string;
19
+ source_uri: string;
20
+ node_id: string;
21
+ rank: number;
22
+ confidence: string;
23
+ title: string | null;
24
+ section_path: string;
25
+ }
26
+ /**
27
+ * Run an FTS5 MATCH query with BM25 ranking and snippet extraction.
28
+ *
29
+ * @param reader A read-only `better-sqlite3` connection (from the read pool).
30
+ * @param scope `'project'` or `'personal'` — filters by `kg_nodes.scope`.
31
+ * @param query Raw user/LLM query string (will be escaped).
32
+ * @param limit Max results (clamped to 1..100 by callers).
33
+ * @returns Results ranked by BM25 (lower rank = better match).
34
+ */
35
+ export declare function ftsQuery(reader: BetterSqliteDatabase, scope: string, query: string, limit: number): FtsResult[];
36
+ /**
37
+ * Escape an FTS5 query by wrapping each token in double quotes. This prevents
38
+ * FTS5 syntax errors when the query contains special characters (*, +, -, etc).
39
+ * Returns null if no valid tokens remain.
40
+ */
41
+ export declare function escapeFts5Query(query: string): string | null;
42
+ //# sourceMappingURL=fts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../src/retrieval/fts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,SAAS,EAAE,CA2Bb;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO5D"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Run an FTS5 MATCH query with BM25 ranking and snippet extraction.
3
+ *
4
+ * @param reader A read-only `better-sqlite3` connection (from the read pool).
5
+ * @param scope `'project'` or `'personal'` — filters by `kg_nodes.scope`.
6
+ * @param query Raw user/LLM query string (will be escaped).
7
+ * @param limit Max results (clamped to 1..100 by callers).
8
+ * @returns Results ranked by BM25 (lower rank = better match).
9
+ */
10
+ export function ftsQuery(reader, scope, query, limit) {
11
+ const escaped = escapeFts5Query(query);
12
+ if (!escaped)
13
+ return [];
14
+ const rows = reader
15
+ .prepare(`SELECT c.id AS id, c.text AS text,
16
+ snippet(kg_chunks_fts, 0, '', '', '…', 64) AS snippet,
17
+ n.source_uri AS source_uri,
18
+ n.id AS node_id, bm25(kg_chunks_fts) AS rank,
19
+ n.confidence AS confidence,
20
+ n.title AS title,
21
+ n.section_path AS section_path
22
+ FROM kg_chunks_fts f
23
+ JOIN kg_chunks c ON c.rowid = f.rowid
24
+ JOIN kg_nodes n ON c.node_id = n.id
25
+ WHERE n.scope = ?
26
+ AND kg_chunks_fts MATCH ?
27
+ ORDER BY bm25(kg_chunks_fts)
28
+ LIMIT ?`)
29
+ .all(scope, escaped, limit);
30
+ return rows;
31
+ }
32
+ /**
33
+ * Escape an FTS5 query by wrapping each token in double quotes. This prevents
34
+ * FTS5 syntax errors when the query contains special characters (*, +, -, etc).
35
+ * Returns null if no valid tokens remain.
36
+ */
37
+ export function escapeFts5Query(query) {
38
+ const tokens = query
39
+ .split(/\s+/)
40
+ .map((t) => t.trim())
41
+ .filter((t) => t.length > 0);
42
+ if (tokens.length === 0)
43
+ return null;
44
+ return tokens.map((t) => `"${t.replace(/"/g, '""')}"`).join(' ');
45
+ }
46
+ //# sourceMappingURL=fts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts.js","sourceRoot":"","sources":["../../src/retrieval/fts.ts"],"names":[],"mappings":"AA4BA;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CACtB,MAA4B,EAC5B,KAAa,EACb,KAAa,EACb,KAAa;IAEb,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,MAAM,IAAI,GAAG,MAAM;SAChB,OAAO,CAIN;;;;;;;;;;;;;gBAaU,CACX;SACA,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAE9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,MAAM,GAAG,KAAK;SACjB,KAAK,CAAC,KAAK,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
2
+ import type { Embedder } from './embedder.js';
3
+ import { type FtsResult } from './fts.js';
4
+ import { type VecResult } from './vec.js';
5
+ export interface HybridResult {
6
+ id: string;
7
+ text: string;
8
+ source_uri: string;
9
+ node_id: string;
10
+ score: number;
11
+ snippet?: string;
12
+ confidence: string;
13
+ title: string | null;
14
+ section_path: string;
15
+ }
16
+ export interface HybridSearchOpts {
17
+ limit?: number;
18
+ rrf_k?: number;
19
+ }
20
+ /**
21
+ * Run a hybrid FTS5 + vector search with RRF fusion.
22
+ *
23
+ * Both sub-queries run with an over-fetch factor (2x limit) so that RRF
24
+ * has enough candidates to fuse meaningfully.
25
+ *
26
+ * @param reader Read-only DB connection.
27
+ * @param scope `'project'` or `'personal'`.
28
+ * @param query Raw query text.
29
+ * @param embedder Embedder for the vector path.
30
+ * @param opts `limit` (default 20), `rrf_k` (default 60).
31
+ * @returns Results sorted by descending RRF score.
32
+ */
33
+ export declare function hybridSearch(reader: BetterSqliteDatabase, scope: string, query: string, embedder: Embedder, opts?: HybridSearchOpts): Promise<HybridResult[]>;
34
+ /**
35
+ * Fuse FTS and vec results via equal-weight Reciprocal Rank Fusion.
36
+ *
37
+ * Phase 7.5: equal weighting — both FTS and vec contribute `1/(rrfK+rank)`.
38
+ * No adaptive weighting or BM25 filtering. The LLM is the precision layer.
39
+ *
40
+ * Exported for direct testing — `hybridSearch` is the normal entry point.
41
+ */
42
+ export declare function rrfFuse(ftsResults: FtsResult[], vecResults: VecResult[], rrfK: number, limit: number): HybridResult[];
43
+ //# sourceMappingURL=hybrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAuBrD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC,CAczB;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CACrB,UAAU,EAAE,SAAS,EAAE,EACvB,UAAU,EAAE,SAAS,EAAE,EACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,YAAY,EAAE,CAiEhB"}
@@ -0,0 +1,120 @@
1
+ import { ftsQuery } from './fts.js';
2
+ import { vecSearch } from './vec.js';
3
+ /**
4
+ * Hybrid retrieval: FTS5 + sqlite-vec fused via Reciprocal Rank Fusion (RRF).
5
+ *
6
+ * This is the canonical Alex Garcia pattern from presearch.md:
7
+ * RRF_score(d) = Σ 1 / (rrf_k + rank_i(d))
8
+ *
9
+ * where rank_i(d) is the 1-based rank of document d in result list i.
10
+ *
11
+ * Items appearing in only one list get a single-side RRF score (the other
12
+ * term is zero). Items in both lists get both terms summed.
13
+ *
14
+ * Phase 7.5: equal-weight RRF (no adaptive weighting). The LLM is the
15
+ * precision layer; we optimize for recall by letting both FTS and vec
16
+ * contribute equally. FTS catches keyword matches vec might miss; vec
17
+ * catches semantic matches FTS misses.
18
+ *
19
+ * Default `rrf_k = 60` per the PRD.
20
+ */
21
+ const DEFAULT_RRF_K = 60;
22
+ /**
23
+ * Run a hybrid FTS5 + vector search with RRF fusion.
24
+ *
25
+ * Both sub-queries run with an over-fetch factor (2x limit) so that RRF
26
+ * has enough candidates to fuse meaningfully.
27
+ *
28
+ * @param reader Read-only DB connection.
29
+ * @param scope `'project'` or `'personal'`.
30
+ * @param query Raw query text.
31
+ * @param embedder Embedder for the vector path.
32
+ * @param opts `limit` (default 20), `rrf_k` (default 60).
33
+ * @returns Results sorted by descending RRF score.
34
+ */
35
+ export async function hybridSearch(reader, scope, query, embedder, opts) {
36
+ const limit = Math.min(Math.max(opts?.limit ?? 20, 1), 100);
37
+ const rrfK = opts?.rrf_k ?? DEFAULT_RRF_K;
38
+ // Over-fetch from each source so RRF has good coverage.
39
+ const fetchLimit = Math.min(limit * 2, 100);
40
+ // Run both queries. Vec is async (embedding), FTS is sync.
41
+ const [ftsResults, vecResults] = await Promise.all([
42
+ Promise.resolve(ftsQuery(reader, scope, query, fetchLimit)),
43
+ vecSearch(reader, scope, query, embedder, fetchLimit),
44
+ ]);
45
+ return rrfFuse(ftsResults, vecResults, rrfK, limit);
46
+ }
47
+ /**
48
+ * Fuse FTS and vec results via equal-weight Reciprocal Rank Fusion.
49
+ *
50
+ * Phase 7.5: equal weighting — both FTS and vec contribute `1/(rrfK+rank)`.
51
+ * No adaptive weighting or BM25 filtering. The LLM is the precision layer.
52
+ *
53
+ * Exported for direct testing — `hybridSearch` is the normal entry point.
54
+ */
55
+ export function rrfFuse(ftsResults, vecResults, rrfK, limit) {
56
+ // Build a map keyed by chunk id. Each entry accumulates its RRF score.
57
+ const merged = new Map();
58
+ // FTS: rank is 1-based position in the result list.
59
+ for (let i = 0; i < ftsResults.length; i++) {
60
+ const r = ftsResults[i];
61
+ const rrfScore = 1 / (rrfK + (i + 1));
62
+ const existing = merged.get(r.id);
63
+ if (existing) {
64
+ existing._rrfScore += rrfScore;
65
+ // Prefer FTS snippet over vec (which has none).
66
+ if (r.snippet)
67
+ existing.snippet = r.snippet;
68
+ }
69
+ else {
70
+ merged.set(r.id, {
71
+ id: r.id,
72
+ text: r.text,
73
+ source_uri: r.source_uri,
74
+ node_id: r.node_id,
75
+ score: 0, // filled after fusion
76
+ snippet: r.snippet || undefined,
77
+ confidence: r.confidence,
78
+ title: r.title ?? null,
79
+ section_path: r.section_path ?? '',
80
+ _rrfScore: rrfScore,
81
+ });
82
+ }
83
+ }
84
+ // Vec: rank is 1-based position in the vec result list.
85
+ for (let i = 0; i < vecResults.length; i++) {
86
+ const r = vecResults[i];
87
+ const rrfScore = 1 / (rrfK + (i + 1));
88
+ const existing = merged.get(r.id);
89
+ if (existing) {
90
+ existing._rrfScore += rrfScore;
91
+ }
92
+ else {
93
+ merged.set(r.id, {
94
+ id: r.id,
95
+ text: r.text,
96
+ source_uri: r.source_uri,
97
+ node_id: r.node_id,
98
+ score: 0,
99
+ confidence: r.confidence,
100
+ title: r.title ?? null,
101
+ section_path: r.section_path ?? '',
102
+ _rrfScore: rrfScore,
103
+ });
104
+ }
105
+ }
106
+ // Sort by descending RRF score, assign final score, trim to limit.
107
+ const sorted = [...merged.values()].sort((a, b) => b._rrfScore - a._rrfScore);
108
+ return sorted.slice(0, limit).map((r) => ({
109
+ id: r.id,
110
+ text: r.text,
111
+ source_uri: r.source_uri,
112
+ node_id: r.node_id,
113
+ score: r._rrfScore,
114
+ snippet: r.snippet,
115
+ confidence: r.confidence,
116
+ title: r.title,
117
+ section_path: r.section_path,
118
+ }));
119
+ }
120
+ //# sourceMappingURL=hybrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid.js","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
2
+ import type { Embedder } from './embedder.js';
3
+ /**
4
+ * Vector similarity search via sqlite-vec's `vec0` virtual table.
5
+ *
6
+ * The query embedding is computed on the fly by the caller-provided embedder.
7
+ * The sqlite-vec `MATCH` operator performs an approximate nearest-neighbor
8
+ * search against `kg_chunks_vec`, returning rowids + cosine distances.
9
+ * We join back to `kg_chunks` + `kg_nodes` for the full result shape.
10
+ *
11
+ * **Graceful degradation**: if the vec table is empty or the embedder fails,
12
+ * returns an empty array (never throws on query path).
13
+ */
14
+ export interface VecResult {
15
+ id: string;
16
+ text: string;
17
+ source_uri: string;
18
+ node_id: string;
19
+ distance: number;
20
+ confidence: string;
21
+ title: string | null;
22
+ section_path: string;
23
+ }
24
+ /**
25
+ * Run a vector similarity search.
26
+ *
27
+ * @param reader A read-only `better-sqlite3` connection.
28
+ * @param scope `'project'` or `'personal'`.
29
+ * @param embedding Pre-computed query embedding (Float32Array of length `dim`).
30
+ * @param limit Max results (clamped by callers).
31
+ * @returns Results sorted by ascending distance (closest first).
32
+ */
33
+ export declare function vecQuery(reader: BetterSqliteDatabase, scope: string, embedding: Float32Array, limit: number): VecResult[];
34
+ /**
35
+ * Convenience wrapper: embed the query text, then run vecQuery.
36
+ * Returns [] if the embedder fails (non-fatal on query path).
37
+ */
38
+ export declare function vecSearch(reader: BetterSqliteDatabase, scope: string, query: string, embedder: Embedder, limit: number): Promise<VecResult[]>;
39
+ //# sourceMappingURL=vec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vec.d.ts","sourceRoot":"","sources":["../../src/retrieval/vec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,YAAY,EACvB,KAAK,EAAE,MAAM,GACZ,SAAS,EAAE,CAgCb;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,EAAE,CAAC,CAOtB"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Run a vector similarity search.
3
+ *
4
+ * @param reader A read-only `better-sqlite3` connection.
5
+ * @param scope `'project'` or `'personal'`.
6
+ * @param embedding Pre-computed query embedding (Float32Array of length `dim`).
7
+ * @param limit Max results (clamped by callers).
8
+ * @returns Results sorted by ascending distance (closest first).
9
+ */
10
+ export function vecQuery(reader, scope, embedding, limit) {
11
+ try {
12
+ // sqlite-vec expects the embedding as a raw buffer of float32 values.
13
+ const buf = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
14
+ const rows = reader
15
+ .prepare(`SELECT c.id AS id, c.text AS text,
16
+ n.source_uri AS source_uri,
17
+ n.id AS node_id,
18
+ v.distance AS distance,
19
+ n.confidence AS confidence,
20
+ n.title AS title,
21
+ n.section_path AS section_path
22
+ FROM kg_chunks_vec v
23
+ JOIN kg_chunks c ON c.rowid = v.rowid
24
+ JOIN kg_nodes n ON c.node_id = n.id
25
+ WHERE v.embedding MATCH ?
26
+ AND k = ?
27
+ AND n.scope = ?
28
+ ORDER BY v.distance`)
29
+ .all(buf, limit, scope);
30
+ return rows;
31
+ }
32
+ catch {
33
+ // Graceful degradation: empty vec table, extension not loaded, etc.
34
+ return [];
35
+ }
36
+ }
37
+ /**
38
+ * Convenience wrapper: embed the query text, then run vecQuery.
39
+ * Returns [] if the embedder fails (non-fatal on query path).
40
+ */
41
+ export async function vecSearch(reader, scope, query, embedder, limit) {
42
+ try {
43
+ const embedding = await embedder.embed(query);
44
+ return vecQuery(reader, scope, embedding, limit);
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ //# sourceMappingURL=vec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vec.js","sourceRoot":"","sources":["../../src/retrieval/vec.ts"],"names":[],"mappings":"AA2BA;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CACtB,MAA4B,EAC5B,KAAa,EACb,SAAuB,EACvB,KAAa;IAEb,IAAI,CAAC;QACH,sEAAsE;QACtE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QAEtF,MAAM,IAAI,GAAG,MAAM;aAChB,OAAO,CAIN;;;;;;;;;;;;;8BAasB,CACvB;aACA,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAE1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;QACpE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAA4B,EAC5B,KAAa,EACb,KAAa,EACb,QAAkB,EAClB,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { QuickJSContext } from 'quickjs-emscripten';
2
+ /**
3
+ * Install the `budget` global with a `fit(items, maxTokens)` method.
4
+ *
5
+ * `budget.fit()` is a host-backed function: guest code passes an array of
6
+ * items and a token budget, the host runs the real `fitResults()` with
7
+ * js-tiktoken, and the truncated array is returned to the guest.
8
+ */
9
+ export declare function installBudgetBindings(context: QuickJSContext, maxTokens: number): void;
10
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../../src/sandbox/bindings/budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,oBAAoB,CAAC;AAMxE;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAyCtF"}
@@ -0,0 +1,44 @@
1
+ import { Scope } from 'quickjs-emscripten';
2
+ import { fitResults } from '../../gate/budget.js';
3
+ import { errorMessage, marshalJsValue } from '../helpers.js';
4
+ /**
5
+ * Install the `budget` global with a `fit(items, maxTokens)` method.
6
+ *
7
+ * `budget.fit()` is a host-backed function: guest code passes an array of
8
+ * items and a token budget, the host runs the real `fitResults()` with
9
+ * js-tiktoken, and the truncated array is returned to the guest.
10
+ */
11
+ export function installBudgetBindings(context, maxTokens) {
12
+ Scope.withScope((scope) => {
13
+ const budgetObj = scope.manage(context.newObject());
14
+ const fitFn = context.newFunction('fit', (...handles) => {
15
+ try {
16
+ const items = handles[0] ? context.dump(handles[0]) : [];
17
+ if (!Array.isArray(items)) {
18
+ return context.newError('budget.fit: first argument must be an array');
19
+ }
20
+ const budget = typeof handles[1] !== 'undefined'
21
+ ? context.dump(handles[1])
22
+ : maxTokens;
23
+ let idx = 0;
24
+ const result = fitResults(items, budget, (item) => JSON.stringify(item), (item) => {
25
+ const obj = item;
26
+ if (obj && typeof obj.id === 'string')
27
+ return obj.id;
28
+ return `[${idx++}]`;
29
+ }, (item) => {
30
+ const obj = item;
31
+ return obj && typeof obj.source_uri === 'string' ? obj.source_uri : '';
32
+ });
33
+ return marshalJsValue(context, result.kept);
34
+ }
35
+ catch (e) {
36
+ return context.newError(errorMessage(e));
37
+ }
38
+ });
39
+ context.setProp(budgetObj, 'fit', fitFn);
40
+ fitFn.dispose();
41
+ context.setProp(context.global, 'budget', budgetObj);
42
+ });
43
+ }
44
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../../src/sandbox/bindings/budget.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAuB,EAAE,SAAiB;IAC9E,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QACxB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QAEpD,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,OAAwB,EAAE,EAAE;YACvE,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,OAAO,OAAO,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAAC;gBACzE,CAAC;gBACD,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW;oBAC9C,CAAC,CAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAY;oBACtC,CAAC,CAAC,SAAS,CAAC;gBAEd,IAAI,GAAG,GAAG,CAAC,CAAC;gBACZ,MAAM,MAAM,GAAG,UAAU,CACvB,KAAK,EACL,MAAM,EACN,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAC9B,CAAC,IAAI,EAAE,EAAE;oBACP,MAAM,GAAG,GAAG,IAA+B,CAAC;oBAC5C,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;wBAAE,OAAO,GAAG,CAAC,EAAE,CAAC;oBACrD,OAAO,IAAI,GAAG,EAAE,GAAG,CAAC;gBACtB,CAAC,EACD,CAAC,IAAI,EAAE,EAAE;oBACP,MAAM,GAAG,GAAG,IAAuC,CAAC;oBACpD,OAAO,GAAG,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzE,CAAC,CACF,CAAC;gBAEF,OAAO,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACzC,KAAK,CAAC,OAAO,EAAE,CAAC;QAEhB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { QuickJSContext } from 'quickjs-emscripten';
2
+ import { type KgBindingDeps } from './kg.js';
3
+ /**
4
+ * All deps needed by the binding surface. Supports single-scope
5
+ * (project-only or personal-only) and dual-scope (both).
6
+ *
7
+ * Privacy invariant: if `personal` is undefined, `kg.personal`
8
+ * does NOT exist in the sandbox — not undefined, not null, absent.
9
+ */
10
+ export interface BindingDeps {
11
+ project?: KgBindingDeps;
12
+ personal?: KgBindingDeps;
13
+ maxTokens: number;
14
+ logs: string[];
15
+ }
16
+ /**
17
+ * Install all sandbox bindings on a fresh QuickJS context:
18
+ * 1. `logger.log(...)` capture
19
+ * 2. `kg.project.*` and/or `kg.personal.*` (scope-dependent)
20
+ * 3. `budget.fit()`
21
+ */
22
+ export declare function installBindings(context: QuickJSContext, deps: BindingDeps): void;
23
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/sandbox/bindings/install.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAGhE;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAIhF"}
@@ -0,0 +1,15 @@
1
+ import { installLogger } from './logger.js';
2
+ import { installKgBindings } from './kg.js';
3
+ import { installBudgetBindings } from './budget.js';
4
+ /**
5
+ * Install all sandbox bindings on a fresh QuickJS context:
6
+ * 1. `logger.log(...)` capture
7
+ * 2. `kg.project.*` and/or `kg.personal.*` (scope-dependent)
8
+ * 3. `budget.fit()`
9
+ */
10
+ export function installBindings(context, deps) {
11
+ installLogger(context, deps.logs);
12
+ installKgBindings(context, deps.project, deps.personal);
13
+ installBudgetBindings(context, deps.maxTokens);
14
+ }
15
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/sandbox/bindings/install.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAsB,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAgBpD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAuB,EAAE,IAAiB;IACxE,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type QuickJSContext } from 'quickjs-emscripten';
2
+ import type { Repository } from '../../db/repository.js';
3
+ import { type DbBundle } from '../../db/client.js';
4
+ import type { Embedder } from '../../retrieval/embedder.js';
5
+ import { type WriteCounter } from './write.js';
6
+ /**
7
+ * Dependencies for installing one scope's bindings in the sandbox.
8
+ */
9
+ export interface KgBindingDeps {
10
+ repository: Repository;
11
+ bundle: DbBundle;
12
+ scope: 'project' | 'personal';
13
+ embedder: Embedder;
14
+ wikiRoot?: string;
15
+ writeCounter?: WriteCounter;
16
+ queryEmbeddings?: Map<string, Float32Array>;
17
+ }
18
+ /**
19
+ * Install the `kg` global with `kg.project.*` and/or `kg.personal.*`
20
+ * bindings plus backward-compat `kg.search()` / `kg.get()` aliases
21
+ * and `kg.describe()`.
22
+ *
23
+ * **Privacy invariant**: if `personalDeps` is undefined, `kg.personal`
24
+ * does NOT exist in the sandbox — not undefined, not null, simply absent.
25
+ * This is enforced at the dispatcher level (tool handler decides which
26
+ * deps to pass). The 15-test adversarial suite verifies this.
27
+ */
28
+ export declare function installKgBindings(context: QuickJSContext, projectDeps?: KgBindingDeps, personalDeps?: KgBindingDeps): void;
29
+ //# sourceMappingURL=kg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kg.d.ts","sourceRoot":"","sources":["../../../src/sandbox/bindings/kg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,cAAc,EAAsB,MAAM,oBAAoB,CAAC;AAGpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,KAAK,QAAQ,EAAc,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAI5D,OAAO,EAAgD,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI7F;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,SAAS,GAAG,UAAU,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,WAAW,CAAC,EAAE,aAAa,EAC3B,YAAY,CAAC,EAAE,aAAa,GAC3B,IAAI,CAmCN"}