kirograph 0.13.1 → 0.14.1
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.
- package/README.md +191 -8
- package/dist/bin/commands/caveman.js +7 -2
- package/dist/bin/commands/caveman.js.map +2 -2
- package/dist/bin/commands/compression.js +109 -0
- package/dist/bin/commands/compression.js.map +7 -0
- package/dist/bin/commands/context.js +31 -24
- package/dist/bin/commands/context.js.map +2 -2
- package/dist/bin/commands/exec.js +107 -0
- package/dist/bin/commands/exec.js.map +7 -0
- package/dist/bin/commands/gain.js +119 -0
- package/dist/bin/commands/gain.js.map +7 -0
- package/dist/bin/commands/help.js +10 -1
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/memory.js +483 -0
- package/dist/bin/commands/memory.js.map +7 -0
- package/dist/bin/commands/query.js +5 -1
- package/dist/bin/commands/query.js.map +2 -2
- package/dist/bin/commands/uninit.js +14 -2
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/utils.js +16 -0
- package/dist/bin/commands/utils.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +9 -2
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/hooks.js +48 -71
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.js +6 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/steering.js +116 -40
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/kiro.js +4 -2
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/kirograph.js +7 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/filters/aws.js +418 -0
- package/dist/compression/filters/aws.js.map +7 -0
- package/dist/compression/filters/docker.js +153 -0
- package/dist/compression/filters/docker.js.map +7 -0
- package/dist/compression/filters/files.js +150 -0
- package/dist/compression/filters/files.js.map +7 -0
- package/dist/compression/filters/generic.js +86 -0
- package/dist/compression/filters/generic.js.map +7 -0
- package/dist/compression/filters/git.js +272 -0
- package/dist/compression/filters/git.js.map +7 -0
- package/dist/compression/filters/github.js +137 -0
- package/dist/compression/filters/github.js.map +7 -0
- package/dist/compression/filters/lint.js +280 -0
- package/dist/compression/filters/lint.js.map +7 -0
- package/dist/compression/filters/misc.js +212 -0
- package/dist/compression/filters/misc.js.map +7 -0
- package/dist/compression/filters/package.js +151 -0
- package/dist/compression/filters/package.js.map +7 -0
- package/dist/compression/filters/test.js +266 -0
- package/dist/compression/filters/test.js.map +7 -0
- package/dist/compression/index.js +144 -0
- package/dist/compression/index.js.map +7 -0
- package/dist/compression/naive-cost.js +94 -0
- package/dist/compression/naive-cost.js.map +7 -0
- package/dist/compression/tracker.js +228 -0
- package/dist/compression/tracker.js.map +7 -0
- package/dist/compression/types.js +17 -0
- package/dist/compression/types.js.map +7 -0
- package/dist/config.js +18 -1
- package/dist/config.js.map +2 -2
- package/dist/db/memory-schema.sql +51 -0
- package/dist/mcp/tool-names.js +3 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +170 -4
- package/dist/mcp/tools.js.map +3 -3
- package/dist/memory/compress.js +142 -0
- package/dist/memory/compress.js.map +7 -0
- package/dist/memory/database.js +375 -0
- package/dist/memory/database.js.map +7 -0
- package/dist/memory/index.js +239 -0
- package/dist/memory/index.js.map +7 -0
- package/dist/memory/symbols.js +54 -0
- package/dist/memory/symbols.js.map +7 -0
- package/dist/memory/types.js +17 -0
- package/dist/memory/types.js.map +7 -0
- package/dist/memory/vectors.js +199 -0
- package/dist/memory/vectors.js.map +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var compress_exports = {};
|
|
20
|
+
__export(compress_exports, {
|
|
21
|
+
compressObservation: () => compressObservation,
|
|
22
|
+
extractIdentifiers: () => extractIdentifiers
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(compress_exports);
|
|
25
|
+
const CODE_BLOCK_RE = /```[\s\S]*?```/g;
|
|
26
|
+
const INLINE_CODE_RE = /`[^`]+`/g;
|
|
27
|
+
const FILE_PATH_RE = /(?:\.?\.?\/)?[\w\-./\\]+\.\w{1,10}/g;
|
|
28
|
+
const URL_RE = /https?:\/\/[^\s)]+/g;
|
|
29
|
+
const VERSION_RE = /\bv?\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?\b/g;
|
|
30
|
+
const IDENTIFIER_RE = /\b(?:[A-Z][a-z]+(?:[A-Z][a-z]+)+|[a-z]+(?:[A-Z][a-z]+)+|[a-z]+(?:_[a-z]+)+|[A-Z]+(?:_[A-Z]+)+)\b/g;
|
|
31
|
+
const FILLER_WORDS = /\b(just|really|basically|simply|actually|very|quite|rather|somewhat|perhaps|maybe)\b/gi;
|
|
32
|
+
const ARTICLES = /\b(a|an|the)\b/gi;
|
|
33
|
+
const HEDGING_PHRASES = [
|
|
34
|
+
/\bI think\b/gi,
|
|
35
|
+
/\bit seems (like |that )?/gi,
|
|
36
|
+
/\byou might want to\b/gi,
|
|
37
|
+
/\byou may want to\b/gi,
|
|
38
|
+
/\bit looks like\b/gi,
|
|
39
|
+
/\bprobably\b/gi,
|
|
40
|
+
/\bI believe\b/gi,
|
|
41
|
+
/\bin my opinion\b/gi
|
|
42
|
+
];
|
|
43
|
+
const PLEASANTRIES = [
|
|
44
|
+
/\b(please|thanks|thank you|sorry|excuse me)\b/gi,
|
|
45
|
+
/^(hi|hello|hey|sure|okay|ok|great|nice|cool|awesome|perfect),?\s*/gim
|
|
46
|
+
];
|
|
47
|
+
const ABBREVIATIONS = [
|
|
48
|
+
[/\bdatabase\b/gi, "DB"],
|
|
49
|
+
[/\bauthentication\b/gi, "auth"],
|
|
50
|
+
[/\bauthenticate\b/gi, "auth"],
|
|
51
|
+
[/\brequest\b/gi, "req"],
|
|
52
|
+
[/\bresponse\b/gi, "res"],
|
|
53
|
+
[/\bfunction\b/gi, "fn"],
|
|
54
|
+
[/\bconfiguration\b/gi, "cfg"],
|
|
55
|
+
[/\bconfigure\b/gi, "cfg"],
|
|
56
|
+
[/\bmessage\b/gi, "msg"],
|
|
57
|
+
[/\berror\b/gi, "err"],
|
|
58
|
+
[/\bimplementation\b/gi, "impl"],
|
|
59
|
+
[/\bimplement\b/gi, "impl"],
|
|
60
|
+
[/\bdependency\b/gi, "dep"],
|
|
61
|
+
[/\bdependencies\b/gi, "deps"],
|
|
62
|
+
[/\brepository\b/gi, "repo"],
|
|
63
|
+
[/\benvironment\b/gi, "env"],
|
|
64
|
+
[/\bapplication\b/gi, "app"],
|
|
65
|
+
[/\bmiddleware\b/gi, "mw"],
|
|
66
|
+
[/\bparameter\b/gi, "param"],
|
|
67
|
+
[/\bparameters\b/gi, "params"]
|
|
68
|
+
];
|
|
69
|
+
function compressObservation(text, mode) {
|
|
70
|
+
const originalLength = text.length;
|
|
71
|
+
if (mode === "off") {
|
|
72
|
+
return {
|
|
73
|
+
compressed: text,
|
|
74
|
+
originalLength,
|
|
75
|
+
compressedLength: text.length,
|
|
76
|
+
detectedSymbols: extractIdentifiers(text)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const preserved = /* @__PURE__ */ new Map();
|
|
80
|
+
let processed = text;
|
|
81
|
+
let placeholderIdx = 0;
|
|
82
|
+
function preserve(re) {
|
|
83
|
+
processed = processed.replace(re, (match) => {
|
|
84
|
+
const key = `\0P${placeholderIdx++}\0`;
|
|
85
|
+
preserved.set(key, match);
|
|
86
|
+
return key;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
preserve(CODE_BLOCK_RE);
|
|
90
|
+
preserve(INLINE_CODE_RE);
|
|
91
|
+
preserve(URL_RE);
|
|
92
|
+
preserve(FILE_PATH_RE);
|
|
93
|
+
preserve(VERSION_RE);
|
|
94
|
+
preserve(IDENTIFIER_RE);
|
|
95
|
+
if (mode === "lite" || mode === "full" || mode === "ultra") {
|
|
96
|
+
processed = processed.replace(FILLER_WORDS, "");
|
|
97
|
+
for (const re of PLEASANTRIES) {
|
|
98
|
+
processed = processed.replace(re, "");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (mode === "full" || mode === "ultra") {
|
|
102
|
+
processed = processed.replace(ARTICLES, "");
|
|
103
|
+
for (const re of HEDGING_PHRASES) {
|
|
104
|
+
processed = processed.replace(re, "");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (mode === "ultra") {
|
|
108
|
+
for (const [re, replacement] of ABBREVIATIONS) {
|
|
109
|
+
processed = processed.replace(re, replacement);
|
|
110
|
+
}
|
|
111
|
+
processed = processed.replace(/\b(because|so|therefore|thus|hence)\b/gi, "\u2192");
|
|
112
|
+
processed = processed.replace(/\band\b/gi, "+");
|
|
113
|
+
}
|
|
114
|
+
processed = processed.split("\n").map((line) => line.replace(/\s{2,}/g, " ").trim()).filter((line) => line.length > 0).join("\n");
|
|
115
|
+
for (const [key, value] of preserved) {
|
|
116
|
+
processed = processed.replace(key, value);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
compressed: processed,
|
|
120
|
+
originalLength,
|
|
121
|
+
compressedLength: processed.length,
|
|
122
|
+
detectedSymbols: extractIdentifiers(text)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function extractIdentifiers(text) {
|
|
126
|
+
const matches = /* @__PURE__ */ new Set();
|
|
127
|
+
const camelPascal = text.match(/\b[A-Z]?[a-z]+(?:[A-Z][a-z]+)+\b/g);
|
|
128
|
+
if (camelPascal) camelPascal.forEach((m) => matches.add(m));
|
|
129
|
+
const pascal = text.match(/\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b/g);
|
|
130
|
+
if (pascal) pascal.forEach((m) => matches.add(m));
|
|
131
|
+
const snake = text.match(/\b[a-z]+(?:_[a-z]+)+\b/g);
|
|
132
|
+
if (snake) snake.forEach((m) => matches.add(m));
|
|
133
|
+
const screaming = text.match(/\b[A-Z]+(?:_[A-Z]+)+\b/g);
|
|
134
|
+
if (screaming) screaming.forEach((m) => matches.add(m));
|
|
135
|
+
return [...matches];
|
|
136
|
+
}
|
|
137
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
138
|
+
0 && (module.exports = {
|
|
139
|
+
compressObservation,
|
|
140
|
+
extractIdentifiers
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=compress.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/memory/compress.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Memory \u2014 Deterministic observation compressor\n *\n * Applies caveman grammar rules programmatically. No LLM involved.\n * Only runs when cavemanMode is not 'off'. Uses the same level the user chose.\n */\n\nimport type { CompressResult } from './types';\n\nexport type CavemanMode = 'off' | 'lite' | 'full' | 'ultra';\n\n// \u2500\u2500 Patterns to preserve (never compress) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Matches code blocks (``` ... ```) */\nconst CODE_BLOCK_RE = /```[\\s\\S]*?```/g;\n\n/** Matches inline code (`...`) */\nconst INLINE_CODE_RE = /`[^`]+`/g;\n\n/** Matches file paths (containing / or \\ with extensions) */\nconst FILE_PATH_RE = /(?:\\.?\\.?\\/)?[\\w\\-./\\\\]+\\.\\w{1,10}/g;\n\n/** Matches URLs */\nconst URL_RE = /https?:\\/\\/[^\\s)]+/g;\n\n/** Matches version numbers (v1.2.3, 1.2.3) */\nconst VERSION_RE = /\\bv?\\d+\\.\\d+(?:\\.\\d+)?(?:-[\\w.]+)?\\b/g;\n\n/** Matches identifiers: camelCase, PascalCase, snake_case, SCREAMING_SNAKE */\nconst IDENTIFIER_RE = /\\b(?:[A-Z][a-z]+(?:[A-Z][a-z]+)+|[a-z]+(?:[A-Z][a-z]+)+|[a-z]+(?:_[a-z]+)+|[A-Z]+(?:_[A-Z]+)+)\\b/g;\n\n// \u2500\u2500 Filler / article / hedging patterns \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst FILLER_WORDS = /\\b(just|really|basically|simply|actually|very|quite|rather|somewhat|perhaps|maybe)\\b/gi;\n\nconst ARTICLES = /\\b(a|an|the)\\b/gi;\n\nconst HEDGING_PHRASES = [\n /\\bI think\\b/gi,\n /\\bit seems (like |that )?/gi,\n /\\byou might want to\\b/gi,\n /\\byou may want to\\b/gi,\n /\\bit looks like\\b/gi,\n /\\bprobably\\b/gi,\n /\\bI believe\\b/gi,\n /\\bin my opinion\\b/gi,\n];\n\nconst PLEASANTRIES = [\n /\\b(please|thanks|thank you|sorry|excuse me)\\b/gi,\n /^(hi|hello|hey|sure|okay|ok|great|nice|cool|awesome|perfect),?\\s*/gim,\n];\n\n// \u2500\u2500 Abbreviations (ultra only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ABBREVIATIONS: [RegExp, string][] = [\n [/\\bdatabase\\b/gi, 'DB'],\n [/\\bauthentication\\b/gi, 'auth'],\n [/\\bauthenticate\\b/gi, 'auth'],\n [/\\brequest\\b/gi, 'req'],\n [/\\bresponse\\b/gi, 'res'],\n [/\\bfunction\\b/gi, 'fn'],\n [/\\bconfiguration\\b/gi, 'cfg'],\n [/\\bconfigure\\b/gi, 'cfg'],\n [/\\bmessage\\b/gi, 'msg'],\n [/\\berror\\b/gi, 'err'],\n [/\\bimplementation\\b/gi, 'impl'],\n [/\\bimplement\\b/gi, 'impl'],\n [/\\bdependency\\b/gi, 'dep'],\n [/\\bdependencies\\b/gi, 'deps'],\n [/\\brepository\\b/gi, 'repo'],\n [/\\benvironment\\b/gi, 'env'],\n [/\\bapplication\\b/gi, 'app'],\n [/\\bmiddleware\\b/gi, 'mw'],\n [/\\bparameter\\b/gi, 'param'],\n [/\\bparameters\\b/gi, 'params'],\n];\n\n// \u2500\u2500 Core compressor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Compress observation text using caveman grammar rules.\n * Returns the text unchanged if mode is 'off'.\n */\nexport function compressObservation(text: string, mode: CavemanMode): CompressResult {\n const originalLength = text.length;\n\n if (mode === 'off') {\n return {\n compressed: text,\n originalLength,\n compressedLength: text.length,\n detectedSymbols: extractIdentifiers(text),\n };\n }\n\n // Extract preserved tokens (code blocks, paths, URLs, versions, identifiers)\n const preserved = new Map<string, string>();\n let processed = text;\n let placeholderIdx = 0;\n\n function preserve(re: RegExp): void {\n processed = processed.replace(re, (match) => {\n const key = `\\x00P${placeholderIdx++}\\x00`;\n preserved.set(key, match);\n return key;\n });\n }\n\n // Order matters: code blocks first (they may contain other patterns)\n preserve(CODE_BLOCK_RE);\n preserve(INLINE_CODE_RE);\n preserve(URL_RE);\n preserve(FILE_PATH_RE);\n preserve(VERSION_RE);\n preserve(IDENTIFIER_RE);\n\n // Apply compression rules based on mode\n if (mode === 'lite' || mode === 'full' || mode === 'ultra') {\n // All modes: drop filler words\n processed = processed.replace(FILLER_WORDS, '');\n\n // All modes: drop pleasantries\n for (const re of PLEASANTRIES) {\n processed = processed.replace(re, '');\n }\n }\n\n if (mode === 'full' || mode === 'ultra') {\n // Drop articles\n processed = processed.replace(ARTICLES, '');\n\n // Drop hedging phrases\n for (const re of HEDGING_PHRASES) {\n processed = processed.replace(re, '');\n }\n }\n\n if (mode === 'ultra') {\n // Apply abbreviations\n for (const [re, replacement] of ABBREVIATIONS) {\n processed = processed.replace(re, replacement);\n }\n\n // Replace \"because\" / \"so\" / \"therefore\" with \u2192\n processed = processed.replace(/\\b(because|so|therefore|thus|hence)\\b/gi, '\u2192');\n\n // Replace \"and\" with +\n processed = processed.replace(/\\band\\b/gi, '+');\n }\n\n // Clean up multiple spaces and leading/trailing whitespace per line\n processed = processed\n .split('\\n')\n .map(line => line.replace(/\\s{2,}/g, ' ').trim())\n .filter(line => line.length > 0)\n .join('\\n');\n\n // Restore preserved tokens\n for (const [key, value] of preserved) {\n processed = processed.replace(key, value);\n }\n\n return {\n compressed: processed,\n originalLength,\n compressedLength: processed.length,\n detectedSymbols: extractIdentifiers(text),\n };\n}\n\n/**\n * Extract candidate identifiers from text (camelCase, PascalCase, snake_case).\n * Used for symbol detection.\n */\nexport function extractIdentifiers(text: string): string[] {\n const matches = new Set<string>();\n\n // camelCase and PascalCase\n const camelPascal = text.match(/\\b[A-Z]?[a-z]+(?:[A-Z][a-z]+)+\\b/g);\n if (camelPascal) camelPascal.forEach(m => matches.add(m));\n\n // PascalCase (starting with uppercase)\n const pascal = text.match(/\\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\\b/g);\n if (pascal) pascal.forEach(m => matches.add(m));\n\n // snake_case (at least one underscore, all lowercase)\n const snake = text.match(/\\b[a-z]+(?:_[a-z]+)+\\b/g);\n if (snake) snake.forEach(m => matches.add(m));\n\n // SCREAMING_SNAKE_CASE\n const screaming = text.match(/\\b[A-Z]+(?:_[A-Z]+)+\\b/g);\n if (screaming) screaming.forEach(m => matches.add(m));\n\n return [...matches];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var database_exports = {};
|
|
30
|
+
__export(database_exports, {
|
|
31
|
+
MemoryDatabase: () => MemoryDatabase
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(database_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
var crypto = __toESM(require("crypto"));
|
|
37
|
+
function generateId() {
|
|
38
|
+
return crypto.randomUUID();
|
|
39
|
+
}
|
|
40
|
+
function hashContent(content) {
|
|
41
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
42
|
+
}
|
|
43
|
+
class MemoryDatabase {
|
|
44
|
+
constructor(db) {
|
|
45
|
+
this.initialized = false;
|
|
46
|
+
this.db = db;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Apply memory schema. Called once when enableMemory is true.
|
|
50
|
+
*/
|
|
51
|
+
initialize() {
|
|
52
|
+
if (this.initialized) return;
|
|
53
|
+
const schemaPath = path.join(__dirname, "../db/memory-schema.sql");
|
|
54
|
+
const sql = fs.readFileSync(schemaPath, "utf8");
|
|
55
|
+
this.db.exec(sql);
|
|
56
|
+
this.initialized = true;
|
|
57
|
+
}
|
|
58
|
+
// ── Sessions ─────────────────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* Get or create an active session. Auto-creates if no active session exists
|
|
61
|
+
* within the timeout window.
|
|
62
|
+
*/
|
|
63
|
+
getOrCreateSession(ide, cwd, timeoutSeconds = 7200) {
|
|
64
|
+
const cutoff = Date.now() - timeoutSeconds * 1e3;
|
|
65
|
+
const existing = this.db.get(
|
|
66
|
+
`SELECT id FROM mem_sessions
|
|
67
|
+
WHERE ended_at IS NULL AND ide = ? AND started_at > ?
|
|
68
|
+
ORDER BY started_at DESC LIMIT 1`,
|
|
69
|
+
[ide, cutoff]
|
|
70
|
+
);
|
|
71
|
+
if (existing) return existing.id;
|
|
72
|
+
this.db.run(
|
|
73
|
+
`UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND ide = ?`,
|
|
74
|
+
[Date.now(), ide]
|
|
75
|
+
);
|
|
76
|
+
const id = generateId();
|
|
77
|
+
this.db.run(
|
|
78
|
+
`INSERT INTO mem_sessions (id, ide, cwd, started_at) VALUES (?, ?, ?, ?)`,
|
|
79
|
+
[id, ide, cwd, Date.now()]
|
|
80
|
+
);
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
83
|
+
endSession(sessionId) {
|
|
84
|
+
this.db.run(
|
|
85
|
+
`UPDATE mem_sessions SET ended_at = ? WHERE id = ? AND ended_at IS NULL`,
|
|
86
|
+
[Date.now(), sessionId]
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
getSession(sessionId) {
|
|
90
|
+
const row = this.db.get("SELECT * FROM mem_sessions WHERE id = ?", [sessionId]);
|
|
91
|
+
return row ? this.rowToSession(row) : null;
|
|
92
|
+
}
|
|
93
|
+
listSessions(limit = 10) {
|
|
94
|
+
return this.db.all(
|
|
95
|
+
"SELECT * FROM mem_sessions ORDER BY started_at DESC LIMIT ?",
|
|
96
|
+
[limit]
|
|
97
|
+
).map(this.rowToSession);
|
|
98
|
+
}
|
|
99
|
+
// ── Observations ─────────────────────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Insert an observation. Returns the ID, or null if a duplicate (same content_hash).
|
|
102
|
+
*/
|
|
103
|
+
insertObservation(content, opts = {}) {
|
|
104
|
+
const id = generateId();
|
|
105
|
+
const contentHash = hashContent(content);
|
|
106
|
+
const kind = opts.kind ?? "note";
|
|
107
|
+
const source = opts.source ?? "manual";
|
|
108
|
+
const tags = opts.tags ? JSON.stringify(opts.tags) : null;
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
try {
|
|
111
|
+
this.db.run(
|
|
112
|
+
`INSERT INTO mem_observations (id, session_id, content, content_raw, content_hash, kind, source, tags, created_at)
|
|
113
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
114
|
+
[id, opts.sessionId ?? null, content, opts.contentRaw ?? null, contentHash, kind, source, tags, now]
|
|
115
|
+
);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (err.message?.includes("UNIQUE constraint failed") || err.message?.includes("unique")) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
this.db.run(
|
|
123
|
+
`INSERT INTO mem_fts (id, content, kind) VALUES (?, ?, ?)`,
|
|
124
|
+
[id, content, kind]
|
|
125
|
+
);
|
|
126
|
+
return id;
|
|
127
|
+
}
|
|
128
|
+
getObservation(id) {
|
|
129
|
+
const row = this.db.get("SELECT * FROM mem_observations WHERE id = ?", [id]);
|
|
130
|
+
return row ? this.rowToObservation(row) : null;
|
|
131
|
+
}
|
|
132
|
+
getObservationsBySession(sessionId, limit = 50) {
|
|
133
|
+
return this.db.all(
|
|
134
|
+
"SELECT * FROM mem_observations WHERE session_id = ? ORDER BY created_at DESC LIMIT ?",
|
|
135
|
+
[sessionId, limit]
|
|
136
|
+
).map(this.rowToObservation);
|
|
137
|
+
}
|
|
138
|
+
getObservationsByTimeRange(from, to, limit = 50) {
|
|
139
|
+
return this.db.all(
|
|
140
|
+
"SELECT * FROM mem_observations WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?",
|
|
141
|
+
[from, to, limit]
|
|
142
|
+
).map(this.rowToObservation);
|
|
143
|
+
}
|
|
144
|
+
// ── FTS Search ───────────────────────────────────────────────────────────
|
|
145
|
+
searchFTS(query, opts = {}) {
|
|
146
|
+
const { limit = 10, kind, sessionId } = opts;
|
|
147
|
+
const safe = query.replace(/\b(AND|OR|NOT)\b/g, " ").replace(/['"*()?\-+^~:{}\\\.\/,]/g, " ").replace(/\s+/g, " ").trim();
|
|
148
|
+
if (!safe) return [];
|
|
149
|
+
const ftsQuery = safe + "*";
|
|
150
|
+
const safeLimit = Math.max(1, Math.floor(Number(limit)));
|
|
151
|
+
const conditions = [];
|
|
152
|
+
const params = [];
|
|
153
|
+
if (kind) {
|
|
154
|
+
conditions.push("o.kind = ?");
|
|
155
|
+
params.push(kind);
|
|
156
|
+
}
|
|
157
|
+
if (sessionId) {
|
|
158
|
+
conditions.push("o.session_id = ?");
|
|
159
|
+
params.push(sessionId);
|
|
160
|
+
}
|
|
161
|
+
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
162
|
+
const rows = this.db.all(
|
|
163
|
+
`SELECT o.*, rank FROM mem_fts f
|
|
164
|
+
JOIN mem_observations o ON o.id = f.id
|
|
165
|
+
WHERE mem_fts MATCH '${ftsQuery}' ${where}
|
|
166
|
+
ORDER BY rank
|
|
167
|
+
LIMIT ${safeLimit}`,
|
|
168
|
+
params
|
|
169
|
+
);
|
|
170
|
+
return rows.map((row) => ({
|
|
171
|
+
observation: this.rowToObservation(row),
|
|
172
|
+
score: Math.abs(row.rank ?? 0),
|
|
173
|
+
scoreSource: "fts"
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
// ── Links ────────────────────────────────────────────────────────────────
|
|
177
|
+
linkToSymbol(observationId, qualifiedName, relevance = 1) {
|
|
178
|
+
this.db.run(
|
|
179
|
+
`INSERT OR IGNORE INTO mem_links (observation_id, qualified_name, relevance)
|
|
180
|
+
VALUES (?, ?, ?)`,
|
|
181
|
+
[observationId, qualifiedName, relevance]
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
linkToSymbols(observationId, qualifiedNames) {
|
|
185
|
+
for (const qn of qualifiedNames) {
|
|
186
|
+
this.linkToSymbol(observationId, qn);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get observations linked to a symbol by qualified_name.
|
|
191
|
+
* Resolves through mem_links — no dependency on node IDs.
|
|
192
|
+
*/
|
|
193
|
+
getLinkedObservations(qualifiedName, limit = 10) {
|
|
194
|
+
const rows = this.db.all(
|
|
195
|
+
`SELECT o.*, l.relevance FROM mem_observations o
|
|
196
|
+
JOIN mem_links l ON l.observation_id = o.id
|
|
197
|
+
WHERE l.qualified_name = ?
|
|
198
|
+
ORDER BY l.relevance DESC, o.created_at DESC
|
|
199
|
+
LIMIT ?`,
|
|
200
|
+
[qualifiedName, limit]
|
|
201
|
+
);
|
|
202
|
+
return rows.map((row) => ({
|
|
203
|
+
observation: this.rowToObservation(row),
|
|
204
|
+
score: row.relevance ?? 1,
|
|
205
|
+
scoreSource: "fts"
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get observations linked to any of the given qualified names.
|
|
210
|
+
* Used by kirograph_context and kirograph_impact to surface memory.
|
|
211
|
+
*/
|
|
212
|
+
getLinkedObservationsForSymbols(qualifiedNames, limit = 5) {
|
|
213
|
+
if (qualifiedNames.length === 0) return [];
|
|
214
|
+
const placeholders = qualifiedNames.map(() => "?").join(",");
|
|
215
|
+
const rows = this.db.all(
|
|
216
|
+
`SELECT DISTINCT o.*, MAX(l.relevance) as relevance FROM mem_observations o
|
|
217
|
+
JOIN mem_links l ON l.observation_id = o.id
|
|
218
|
+
WHERE l.qualified_name IN (${placeholders})
|
|
219
|
+
GROUP BY o.id
|
|
220
|
+
ORDER BY relevance DESC, o.created_at DESC
|
|
221
|
+
LIMIT ?`,
|
|
222
|
+
[...qualifiedNames, limit]
|
|
223
|
+
);
|
|
224
|
+
return rows.map((row) => ({
|
|
225
|
+
observation: this.rowToObservation(row),
|
|
226
|
+
score: row.relevance ?? 1,
|
|
227
|
+
scoreSource: "fts"
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
// ── Vectors ──────────────────────────────────────────────────────────────
|
|
231
|
+
insertVector(observationId, embedding, model) {
|
|
232
|
+
this.db.run(
|
|
233
|
+
`INSERT OR REPLACE INTO mem_vectors (observation_id, embedding, model, created_at)
|
|
234
|
+
VALUES (?, ?, ?, ?)`,
|
|
235
|
+
[observationId, embedding, model, Date.now()]
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
getVector(observationId) {
|
|
239
|
+
const row = this.db.get(
|
|
240
|
+
"SELECT embedding, model FROM mem_vectors WHERE observation_id = ?",
|
|
241
|
+
[observationId]
|
|
242
|
+
);
|
|
243
|
+
return row ? { embedding: row.embedding, model: row.model } : null;
|
|
244
|
+
}
|
|
245
|
+
getVectorCount() {
|
|
246
|
+
const row = this.db.get("SELECT COUNT(*) as count FROM mem_vectors");
|
|
247
|
+
return row?.count ?? 0;
|
|
248
|
+
}
|
|
249
|
+
getVectorModelMismatch(currentModel) {
|
|
250
|
+
const row = this.db.get(
|
|
251
|
+
"SELECT COUNT(*) as count FROM mem_vectors WHERE model != ?",
|
|
252
|
+
[currentModel]
|
|
253
|
+
);
|
|
254
|
+
return row?.count ?? 0;
|
|
255
|
+
}
|
|
256
|
+
getAllVectors(model) {
|
|
257
|
+
const sql = model ? "SELECT observation_id, embedding FROM mem_vectors WHERE model = ?" : "SELECT observation_id, embedding FROM mem_vectors";
|
|
258
|
+
const params = model ? [model] : [];
|
|
259
|
+
return this.db.all(sql, params).map((row) => ({
|
|
260
|
+
observationId: row.observation_id,
|
|
261
|
+
embedding: row.embedding
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
deleteVectors() {
|
|
265
|
+
this.db.run("DELETE FROM mem_vectors");
|
|
266
|
+
}
|
|
267
|
+
// ── Stats ────────────────────────────────────────────────────────────────
|
|
268
|
+
getStats(currentModel) {
|
|
269
|
+
const sessions = this.db.get("SELECT COUNT(*) as c FROM mem_sessions")?.c ?? 0;
|
|
270
|
+
const activeSessions = this.db.get("SELECT COUNT(*) as c FROM mem_sessions WHERE ended_at IS NULL")?.c ?? 0;
|
|
271
|
+
const observations = this.db.get("SELECT COUNT(*) as c FROM mem_observations")?.c ?? 0;
|
|
272
|
+
const links = this.db.get("SELECT COUNT(*) as c FROM mem_links")?.c ?? 0;
|
|
273
|
+
const vectors = this.db.get("SELECT COUNT(*) as c FROM mem_vectors")?.c ?? 0;
|
|
274
|
+
const mismatch = currentModel ? this.getVectorModelMismatch(currentModel) : 0;
|
|
275
|
+
return {
|
|
276
|
+
sessions,
|
|
277
|
+
activeSessions,
|
|
278
|
+
observations,
|
|
279
|
+
links,
|
|
280
|
+
vectors,
|
|
281
|
+
embeddableCount: observations,
|
|
282
|
+
modelMismatch: mismatch > 0,
|
|
283
|
+
currentModel
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// ── Maintenance ──────────────────────────────────────────────────────────
|
|
287
|
+
/**
|
|
288
|
+
* Delete observations older than the given timestamp.
|
|
289
|
+
* Cascades to mem_links and mem_vectors via FK.
|
|
290
|
+
*/
|
|
291
|
+
prune(olderThan) {
|
|
292
|
+
const ids = this.db.all(
|
|
293
|
+
"SELECT id FROM mem_observations WHERE created_at < ?",
|
|
294
|
+
[olderThan]
|
|
295
|
+
).map((r) => r.id);
|
|
296
|
+
if (ids.length === 0) return 0;
|
|
297
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
298
|
+
this.db.run(`DELETE FROM mem_fts WHERE id IN (${placeholders})`, ids);
|
|
299
|
+
this.db.run(`DELETE FROM mem_observations WHERE created_at < ?`, [olderThan]);
|
|
300
|
+
this.db.run(
|
|
301
|
+
`DELETE FROM mem_sessions WHERE id NOT IN (SELECT DISTINCT session_id FROM mem_observations WHERE session_id IS NOT NULL)`
|
|
302
|
+
);
|
|
303
|
+
return ids.length;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Find stale links where qualified_name no longer exists in the nodes table.
|
|
307
|
+
*/
|
|
308
|
+
findStaleLinks() {
|
|
309
|
+
const rows = this.db.all(
|
|
310
|
+
`SELECT l.* FROM mem_links l
|
|
311
|
+
LEFT JOIN nodes n ON n.qualified_name = l.qualified_name
|
|
312
|
+
WHERE n.id IS NULL`
|
|
313
|
+
);
|
|
314
|
+
return rows.map((row) => ({
|
|
315
|
+
observationId: row.observation_id,
|
|
316
|
+
qualifiedName: row.qualified_name,
|
|
317
|
+
relevance: row.relevance
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Remove stale links (where qualified_name no longer resolves).
|
|
322
|
+
*/
|
|
323
|
+
removeStaleLinks() {
|
|
324
|
+
const result = this.db.run(
|
|
325
|
+
`DELETE FROM mem_links WHERE qualified_name NOT IN (SELECT DISTINCT qualified_name FROM nodes)`
|
|
326
|
+
);
|
|
327
|
+
return result?.changes ?? 0;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Close stale sessions (started but never ended, older than timeout).
|
|
331
|
+
*/
|
|
332
|
+
closeStaleSessionsOlderThan(timeoutMs) {
|
|
333
|
+
const cutoff = Date.now() - timeoutMs;
|
|
334
|
+
const result = this.db.run(
|
|
335
|
+
`UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND started_at < ?`,
|
|
336
|
+
[Date.now(), cutoff]
|
|
337
|
+
);
|
|
338
|
+
return result?.changes ?? 0;
|
|
339
|
+
}
|
|
340
|
+
// ── Privacy ──────────────────────────────────────────────────────────────
|
|
341
|
+
/**
|
|
342
|
+
* Strip <private>...</private> blocks from text.
|
|
343
|
+
*/
|
|
344
|
+
static stripPrivate(text) {
|
|
345
|
+
return text.replace(/<private>[\s\S]*?<\/private>/gi, "").trim();
|
|
346
|
+
}
|
|
347
|
+
// ── Row mappers ──────────────────────────────────────────────────────────
|
|
348
|
+
rowToSession(row) {
|
|
349
|
+
return {
|
|
350
|
+
id: row.id,
|
|
351
|
+
ide: row.ide ?? void 0,
|
|
352
|
+
cwd: row.cwd ?? void 0,
|
|
353
|
+
startedAt: row.started_at,
|
|
354
|
+
endedAt: row.ended_at ?? void 0
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
rowToObservation(row) {
|
|
358
|
+
return {
|
|
359
|
+
id: row.id,
|
|
360
|
+
sessionId: row.session_id ?? void 0,
|
|
361
|
+
content: row.content,
|
|
362
|
+
contentRaw: row.content_raw ?? void 0,
|
|
363
|
+
contentHash: row.content_hash,
|
|
364
|
+
kind: row.kind,
|
|
365
|
+
source: row.source,
|
|
366
|
+
tags: row.tags ? JSON.parse(row.tags) : void 0,
|
|
367
|
+
createdAt: row.created_at
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
372
|
+
0 && (module.exports = {
|
|
373
|
+
MemoryDatabase
|
|
374
|
+
});
|
|
375
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/memory/database.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Memory \u2014 Database layer\n *\n * Operates on mem_* tables within the same SQLite database as the code graph.\n * Completely isolated: no FK to core nodes/edges tables.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport type {\n MemSession,\n MemObservation,\n MemObservationInput,\n MemLink,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n} from './types';\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash('sha256').update(content).digest('hex');\n}\n\n// \u2500\u2500 MemoryDatabase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryDatabase {\n private db: any;\n private initialized = false;\n\n constructor(db: any) {\n this.db = db;\n }\n\n /**\n * Apply memory schema. Called once when enableMemory is true.\n */\n initialize(): void {\n if (this.initialized) return;\n const schemaPath = path.join(__dirname, '../db/memory-schema.sql');\n const sql = fs.readFileSync(schemaPath, 'utf8');\n this.db.exec(sql);\n this.initialized = true;\n }\n\n // \u2500\u2500 Sessions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Get or create an active session. Auto-creates if no active session exists\n * within the timeout window.\n */\n getOrCreateSession(ide: string, cwd: string, timeoutSeconds = 7200): string {\n const cutoff = Date.now() - (timeoutSeconds * 1000);\n\n // Look for an active session (no ended_at, same ide, started within timeout)\n const existing = this.db.get(\n `SELECT id FROM mem_sessions\n WHERE ended_at IS NULL AND ide = ? AND started_at > ?\n ORDER BY started_at DESC LIMIT 1`,\n [ide, cutoff]\n );\n\n if (existing) return existing.id;\n\n // Auto-close any stale sessions for this IDE\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND ide = ?`,\n [Date.now(), ide]\n );\n\n // Create new session\n const id = generateId();\n this.db.run(\n `INSERT INTO mem_sessions (id, ide, cwd, started_at) VALUES (?, ?, ?, ?)`,\n [id, ide, cwd, Date.now()]\n );\n return id;\n }\n\n endSession(sessionId: string): void {\n this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE id = ? AND ended_at IS NULL`,\n [Date.now(), sessionId]\n );\n }\n\n getSession(sessionId: string): MemSession | null {\n const row = this.db.get('SELECT * FROM mem_sessions WHERE id = ?', [sessionId]);\n return row ? this.rowToSession(row) : null;\n }\n\n listSessions(limit = 10): MemSession[] {\n return this.db.all(\n 'SELECT * FROM mem_sessions ORDER BY started_at DESC LIMIT ?',\n [limit]\n ).map(this.rowToSession);\n }\n\n // \u2500\u2500 Observations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert an observation. Returns the ID, or null if a duplicate (same content_hash).\n */\n insertObservation(\n content: string,\n opts: {\n contentRaw?: string;\n kind?: string;\n source?: string;\n tags?: string[];\n sessionId?: string;\n } = {}\n ): string | null {\n const id = generateId();\n const contentHash = hashContent(content);\n const kind = opts.kind ?? 'note';\n const source = opts.source ?? 'manual';\n const tags = opts.tags ? JSON.stringify(opts.tags) : null;\n const now = Date.now();\n\n try {\n this.db.run(\n `INSERT INTO mem_observations (id, session_id, content, content_raw, content_hash, kind, source, tags, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, opts.sessionId ?? null, content, opts.contentRaw ?? null, contentHash, kind, source, tags, now]\n );\n } catch (err: any) {\n // Unique constraint on content_hash \u2014 duplicate, skip silently\n if (err.message?.includes('UNIQUE constraint failed') || err.message?.includes('unique')) {\n return null;\n }\n throw err;\n }\n\n // Keep FTS in sync\n this.db.run(\n `INSERT INTO mem_fts (id, content, kind) VALUES (?, ?, ?)`,\n [id, content, kind]\n );\n\n return id;\n }\n\n getObservation(id: string): MemObservation | null {\n const row = this.db.get('SELECT * FROM mem_observations WHERE id = ?', [id]);\n return row ? this.rowToObservation(row) : null;\n }\n\n getObservationsBySession(sessionId: string, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE session_id = ? ORDER BY created_at DESC LIMIT ?',\n [sessionId, limit]\n ).map(this.rowToObservation);\n }\n\n getObservationsByTimeRange(from: number, to: number, limit = 50): MemObservation[] {\n return this.db.all(\n 'SELECT * FROM mem_observations WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?',\n [from, to, limit]\n ).map(this.rowToObservation);\n }\n\n // \u2500\u2500 FTS Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n searchFTS(query: string, opts: MemSearchOptions = {}): ScoredObservation[] {\n const { limit = 10, kind, sessionId } = opts;\n\n // Sanitize for FTS5\n const safe = query\n .replace(/\\b(AND|OR|NOT)\\b/g, ' ')\n .replace(/['\"*()?\\-+^~:{}\\\\\\.\\/,]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n if (!safe) return [];\n\n const ftsQuery = safe + '*';\n const safeLimit = Math.max(1, Math.floor(Number(limit)));\n\n const conditions: string[] = [];\n const params: any[] = [];\n\n if (kind) {\n conditions.push('o.kind = ?');\n params.push(kind);\n }\n if (sessionId) {\n conditions.push('o.session_id = ?');\n params.push(sessionId);\n }\n\n const where = conditions.length > 0 ? `AND ${conditions.join(' AND ')}` : '';\n\n const rows = this.db.all(\n `SELECT o.*, rank FROM mem_fts f\n JOIN mem_observations o ON o.id = f.id\n WHERE mem_fts MATCH '${ftsQuery}' ${where}\n ORDER BY rank\n LIMIT ${safeLimit}`,\n params\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: Math.abs(row.rank ?? 0),\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n linkToSymbol(observationId: string, qualifiedName: string, relevance = 1.0): void {\n this.db.run(\n `INSERT OR IGNORE INTO mem_links (observation_id, qualified_name, relevance)\n VALUES (?, ?, ?)`,\n [observationId, qualifiedName, relevance]\n );\n }\n\n linkToSymbols(observationId: string, qualifiedNames: string[]): void {\n for (const qn of qualifiedNames) {\n this.linkToSymbol(observationId, qn);\n }\n }\n\n /**\n * Get observations linked to a symbol by qualified_name.\n * Resolves through mem_links \u2014 no dependency on node IDs.\n */\n getLinkedObservations(qualifiedName: string, limit = 10): ScoredObservation[] {\n const rows = this.db.all(\n `SELECT o.*, l.relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name = ?\n ORDER BY l.relevance DESC, o.created_at DESC\n LIMIT ?`,\n [qualifiedName, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n /**\n * Get observations linked to any of the given qualified names.\n * Used by kirograph_context and kirograph_impact to surface memory.\n */\n getLinkedObservationsForSymbols(qualifiedNames: string[], limit = 5): ScoredObservation[] {\n if (qualifiedNames.length === 0) return [];\n const placeholders = qualifiedNames.map(() => '?').join(',');\n const rows = this.db.all(\n `SELECT DISTINCT o.*, MAX(l.relevance) as relevance FROM mem_observations o\n JOIN mem_links l ON l.observation_id = o.id\n WHERE l.qualified_name IN (${placeholders})\n GROUP BY o.id\n ORDER BY relevance DESC, o.created_at DESC\n LIMIT ?`,\n [...qualifiedNames, limit]\n );\n\n return rows.map((row: any) => ({\n observation: this.rowToObservation(row),\n score: row.relevance ?? 1.0,\n scoreSource: 'fts' as const,\n }));\n }\n\n // \u2500\u2500 Vectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n insertVector(observationId: string, embedding: Buffer, model: string): void {\n this.db.run(\n `INSERT OR REPLACE INTO mem_vectors (observation_id, embedding, model, created_at)\n VALUES (?, ?, ?, ?)`,\n [observationId, embedding, model, Date.now()]\n );\n }\n\n getVector(observationId: string): { embedding: Buffer; model: string } | null {\n const row = this.db.get(\n 'SELECT embedding, model FROM mem_vectors WHERE observation_id = ?',\n [observationId]\n );\n return row ? { embedding: row.embedding, model: row.model } : null;\n }\n\n getVectorCount(): number {\n const row = this.db.get('SELECT COUNT(*) as count FROM mem_vectors');\n return row?.count ?? 0;\n }\n\n getVectorModelMismatch(currentModel: string): number {\n const row = this.db.get(\n 'SELECT COUNT(*) as count FROM mem_vectors WHERE model != ?',\n [currentModel]\n );\n return row?.count ?? 0;\n }\n\n getAllVectors(model?: string): Array<{ observationId: string; embedding: Buffer }> {\n const sql = model\n ? 'SELECT observation_id, embedding FROM mem_vectors WHERE model = ?'\n : 'SELECT observation_id, embedding FROM mem_vectors';\n const params = model ? [model] : [];\n return this.db.all(sql, params).map((row: any) => ({\n observationId: row.observation_id,\n embedding: row.embedding,\n }));\n }\n\n deleteVectors(): void {\n this.db.run('DELETE FROM mem_vectors');\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(currentModel?: string): MemStats {\n const sessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions')?.c ?? 0;\n const activeSessions = this.db.get('SELECT COUNT(*) as c FROM mem_sessions WHERE ended_at IS NULL')?.c ?? 0;\n const observations = this.db.get('SELECT COUNT(*) as c FROM mem_observations')?.c ?? 0;\n const links = this.db.get('SELECT COUNT(*) as c FROM mem_links')?.c ?? 0;\n const vectors = this.db.get('SELECT COUNT(*) as c FROM mem_vectors')?.c ?? 0;\n const mismatch = currentModel ? this.getVectorModelMismatch(currentModel) : 0;\n\n return {\n sessions,\n activeSessions,\n observations,\n links,\n vectors,\n embeddableCount: observations,\n modelMismatch: mismatch > 0,\n currentModel,\n };\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Delete observations older than the given timestamp.\n * Cascades to mem_links and mem_vectors via FK.\n */\n prune(olderThan: number): number {\n // Get IDs to delete (for FTS cleanup)\n const ids = this.db.all(\n 'SELECT id FROM mem_observations WHERE created_at < ?',\n [olderThan]\n ).map((r: any) => r.id);\n\n if (ids.length === 0) return 0;\n\n const placeholders = ids.map(() => '?').join(',');\n this.db.run(`DELETE FROM mem_fts WHERE id IN (${placeholders})`, ids);\n this.db.run(`DELETE FROM mem_observations WHERE created_at < ?`, [olderThan]);\n\n // Clean up empty sessions\n this.db.run(\n `DELETE FROM mem_sessions WHERE id NOT IN (SELECT DISTINCT session_id FROM mem_observations WHERE session_id IS NOT NULL)`\n );\n\n return ids.length;\n }\n\n /**\n * Find stale links where qualified_name no longer exists in the nodes table.\n */\n findStaleLinks(): MemLink[] {\n const rows = this.db.all(\n `SELECT l.* FROM mem_links l\n LEFT JOIN nodes n ON n.qualified_name = l.qualified_name\n WHERE n.id IS NULL`\n );\n return rows.map((row: any) => ({\n observationId: row.observation_id,\n qualifiedName: row.qualified_name,\n relevance: row.relevance,\n }));\n }\n\n /**\n * Remove stale links (where qualified_name no longer resolves).\n */\n removeStaleLinks(): number {\n const result = this.db.run(\n `DELETE FROM mem_links WHERE qualified_name NOT IN (SELECT DISTINCT qualified_name FROM nodes)`\n );\n return result?.changes ?? 0;\n }\n\n /**\n * Close stale sessions (started but never ended, older than timeout).\n */\n closeStaleSessionsOlderThan(timeoutMs: number): number {\n const cutoff = Date.now() - timeoutMs;\n const result = this.db.run(\n `UPDATE mem_sessions SET ended_at = ? WHERE ended_at IS NULL AND started_at < ?`,\n [Date.now(), cutoff]\n );\n return result?.changes ?? 0;\n }\n\n // \u2500\u2500 Privacy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Strip <private>...</private> blocks from text.\n */\n static stripPrivate(text: string): string {\n return text.replace(/<private>[\\s\\S]*?<\\/private>/gi, '').trim();\n }\n\n // \u2500\u2500 Row mappers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private rowToSession(row: any): MemSession {\n return {\n id: row.id,\n ide: row.ide ?? undefined,\n cwd: row.cwd ?? undefined,\n startedAt: row.started_at,\n endedAt: row.ended_at ?? undefined,\n };\n }\n\n private rowToObservation(row: any): MemObservation {\n return {\n id: row.id,\n sessionId: row.session_id ?? undefined,\n content: row.content,\n contentRaw: row.content_raw ?? undefined,\n contentHash: row.content_hash,\n kind: row.kind,\n source: row.source,\n tags: row.tags ? JSON.parse(row.tags) : undefined,\n createdAt: row.created_at,\n };\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAoB;AACpB,WAAsB;AACtB,aAAwB;AAcxB,SAAS,aAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,YAAY,SAAyB;AAC5C,SAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AACjE;AAIO,MAAM,eAAe;AAAA,EAI1B,YAAY,IAAS;AAFrB,SAAQ,cAAc;AAGpB,SAAK,KAAK;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,aAAmB;AACjB,QAAI,KAAK,YAAa;AACtB,UAAM,aAAa,KAAK,KAAK,WAAW,yBAAyB;AACjE,UAAM,MAAM,GAAG,aAAa,YAAY,MAAM;AAC9C,SAAK,GAAG,KAAK,GAAG;AAChB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBAAmB,KAAa,KAAa,iBAAiB,MAAc;AAC1E,UAAM,SAAS,KAAK,IAAI,IAAK,iBAAiB;AAG9C,UAAM,WAAW,KAAK,GAAG;AAAA,MACvB;AAAA;AAAA;AAAA,MAGA,CAAC,KAAK,MAAM;AAAA,IACd;AAEA,QAAI,SAAU,QAAO,SAAS;AAG9B,SAAK,GAAG;AAAA,MACN;AAAA,MACA,CAAC,KAAK,IAAI,GAAG,GAAG;AAAA,IAClB;AAGA,UAAM,KAAK,WAAW;AACtB,SAAK,GAAG;AAAA,MACN;AAAA,MACA,CAAC,IAAI,KAAK,KAAK,KAAK,IAAI,CAAC;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,WAAyB;AAClC,SAAK,GAAG;AAAA,MACN;AAAA,MACA,CAAC,KAAK,IAAI,GAAG,SAAS;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,WAAW,WAAsC;AAC/C,UAAM,MAAM,KAAK,GAAG,IAAI,2CAA2C,CAAC,SAAS,CAAC;AAC9E,WAAO,MAAM,KAAK,aAAa,GAAG,IAAI;AAAA,EACxC;AAAA,EAEA,aAAa,QAAQ,IAAkB;AACrC,WAAO,KAAK,GAAG;AAAA,MACb;AAAA,MACA,CAAC,KAAK;AAAA,IACR,EAAE,IAAI,KAAK,YAAY;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBACE,SACA,OAMI,CAAC,GACU;AACf,UAAM,KAAK,WAAW;AACtB,UAAM,cAAc,YAAY,OAAO;AACvC,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,OAAO,KAAK,OAAO,KAAK,UAAU,KAAK,IAAI,IAAI;AACrD,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI;AACF,WAAK,GAAG;AAAA,QACN;AAAA;AAAA,QAEA,CAAC,IAAI,KAAK,aAAa,MAAM,SAAS,KAAK,cAAc,MAAM,aAAa,MAAM,QAAQ,MAAM,GAAG;AAAA,MACrG;AAAA,IACF,SAAS,KAAU;AAEjB,UAAI,IAAI,SAAS,SAAS,0BAA0B,KAAK,IAAI,SAAS,SAAS,QAAQ,GAAG;AACxF,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAGA,SAAK,GAAG;AAAA,MACN;AAAA,MACA,CAAC,IAAI,SAAS,IAAI;AAAA,IACpB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,IAAmC;AAChD,UAAM,MAAM,KAAK,GAAG,IAAI,+CAA+C,CAAC,EAAE,CAAC;AAC3E,WAAO,MAAM,KAAK,iBAAiB,GAAG,IAAI;AAAA,EAC5C;AAAA,EAEA,yBAAyB,WAAmB,QAAQ,IAAsB;AACxE,WAAO,KAAK,GAAG;AAAA,MACb;AAAA,MACA,CAAC,WAAW,KAAK;AAAA,IACnB,EAAE,IAAI,KAAK,gBAAgB;AAAA,EAC7B;AAAA,EAEA,2BAA2B,MAAc,IAAY,QAAQ,IAAsB;AACjF,WAAO,KAAK,GAAG;AAAA,MACb;AAAA,MACA,CAAC,MAAM,IAAI,KAAK;AAAA,IAClB,EAAE,IAAI,KAAK,gBAAgB;AAAA,EAC7B;AAAA;AAAA,EAIA,UAAU,OAAe,OAAyB,CAAC,GAAwB;AACzE,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,IAAI;AAGxC,UAAM,OAAO,MACV,QAAQ,qBAAqB,GAAG,EAChC,QAAQ,4BAA4B,GAAG,EACvC,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACR,QAAI,CAAC,KAAM,QAAO,CAAC;AAEnB,UAAM,WAAW,OAAO;AACxB,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,KAAK,CAAC,CAAC;AAEvD,UAAM,aAAuB,CAAC;AAC9B,UAAM,SAAgB,CAAC;AAEvB,QAAI,MAAM;AACR,iBAAW,KAAK,YAAY;AAC5B,aAAO,KAAK,IAAI;AAAA,IAClB;AACA,QAAI,WAAW;AACb,iBAAW,KAAK,kBAAkB;AAClC,aAAO,KAAK,SAAS;AAAA,IACvB;AAEA,UAAM,QAAQ,WAAW,SAAS,IAAI,OAAO,WAAW,KAAK,OAAO,CAAC,KAAK;AAE1E,UAAM,OAAO,KAAK,GAAG;AAAA,MACnB;AAAA;AAAA,8BAEwB,QAAQ,KAAK,KAAK;AAAA;AAAA,eAEjC,SAAS;AAAA,MAClB;AAAA,IACF;AAEA,WAAO,KAAK,IAAI,CAAC,SAAc;AAAA,MAC7B,aAAa,KAAK,iBAAiB,GAAG;AAAA,MACtC,OAAO,KAAK,IAAI,IAAI,QAAQ,CAAC;AAAA,MAC7B,aAAa;AAAA,IACf,EAAE;AAAA,EACJ;AAAA;AAAA,EAIA,aAAa,eAAuB,eAAuB,YAAY,GAAW;AAChF,SAAK,GAAG;AAAA,MACN;AAAA;AAAA,MAEA,CAAC,eAAe,eAAe,SAAS;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,cAAc,eAAuB,gBAAgC;AACnE,eAAW,MAAM,gBAAgB;AAC/B,WAAK,aAAa,eAAe,EAAE;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,eAAuB,QAAQ,IAAyB;AAC5E,UAAM,OAAO,KAAK,GAAG;AAAA,MACnB;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,CAAC,eAAe,KAAK;AAAA,IACvB;AAEA,WAAO,KAAK,IAAI,CAAC,SAAc;AAAA,MAC7B,aAAa,KAAK,iBAAiB,GAAG;AAAA,MACtC,OAAO,IAAI,aAAa;AAAA,MACxB,aAAa;AAAA,IACf,EAAE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gCAAgC,gBAA0B,QAAQ,GAAwB;AACxF,QAAI,eAAe,WAAW,EAAG,QAAO,CAAC;AACzC,UAAM,eAAe,eAAe,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAC3D,UAAM,OAAO,KAAK,GAAG;AAAA,MACnB;AAAA;AAAA,oCAE8B,YAAY;AAAA;AAAA;AAAA;AAAA,MAI1C,CAAC,GAAG,gBAAgB,KAAK;AAAA,IAC3B;AAEA,WAAO,KAAK,IAAI,CAAC,SAAc;AAAA,MAC7B,aAAa,KAAK,iBAAiB,GAAG;AAAA,MACtC,OAAO,IAAI,aAAa;AAAA,MACxB,aAAa;AAAA,IACf,EAAE;AAAA,EACJ;AAAA;AAAA,EAIA,aAAa,eAAuB,WAAmB,OAAqB;AAC1E,SAAK,GAAG;AAAA,MACN;AAAA;AAAA,MAEA,CAAC,eAAe,WAAW,OAAO,KAAK,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,UAAU,eAAoE;AAC5E,UAAM,MAAM,KAAK,GAAG;AAAA,MAClB;AAAA,MACA,CAAC,aAAa;AAAA,IAChB;AACA,WAAO,MAAM,EAAE,WAAW,IAAI,WAAW,OAAO,IAAI,MAAM,IAAI;AAAA,EAChE;AAAA,EAEA,iBAAyB;AACvB,UAAM,MAAM,KAAK,GAAG,IAAI,2CAA2C;AACnE,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,uBAAuB,cAA8B;AACnD,UAAM,MAAM,KAAK,GAAG;AAAA,MAClB;AAAA,MACA,CAAC,YAAY;AAAA,IACf;AACA,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,cAAc,OAAqE;AACjF,UAAM,MAAM,QACR,sEACA;AACJ,UAAM,SAAS,QAAQ,CAAC,KAAK,IAAI,CAAC;AAClC,WAAO,KAAK,GAAG,IAAI,KAAK,MAAM,EAAE,IAAI,CAAC,SAAc;AAAA,MACjD,eAAe,IAAI;AAAA,MACnB,WAAW,IAAI;AAAA,IACjB,EAAE;AAAA,EACJ;AAAA,EAEA,gBAAsB;AACpB,SAAK,GAAG,IAAI,yBAAyB;AAAA,EACvC;AAAA;AAAA,EAIA,SAAS,cAAiC;AACxC,UAAM,WAAW,KAAK,GAAG,IAAI,wCAAwC,GAAG,KAAK;AAC7E,UAAM,iBAAiB,KAAK,GAAG,IAAI,+DAA+D,GAAG,KAAK;AAC1G,UAAM,eAAe,KAAK,GAAG,IAAI,4CAA4C,GAAG,KAAK;AACrF,UAAM,QAAQ,KAAK,GAAG,IAAI,qCAAqC,GAAG,KAAK;AACvE,UAAM,UAAU,KAAK,GAAG,IAAI,uCAAuC,GAAG,KAAK;AAC3E,UAAM,WAAW,eAAe,KAAK,uBAAuB,YAAY,IAAI;AAE5E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,MACjB,eAAe,WAAW;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAA2B;AAE/B,UAAM,MAAM,KAAK,GAAG;AAAA,MAClB;AAAA,MACA,CAAC,SAAS;AAAA,IACZ,EAAE,IAAI,CAAC,MAAW,EAAE,EAAE;AAEtB,QAAI,IAAI,WAAW,EAAG,QAAO;AAE7B,UAAM,eAAe,IAAI,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAChD,SAAK,GAAG,IAAI,oCAAoC,YAAY,KAAK,GAAG;AACpE,SAAK,GAAG,IAAI,qDAAqD,CAAC,SAAS,CAAC;AAG5E,SAAK,GAAG;AAAA,MACN;AAAA,IACF;AAEA,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA4B;AAC1B,UAAM,OAAO,KAAK,GAAG;AAAA,MACnB;AAAA;AAAA;AAAA,IAGF;AACA,WAAO,KAAK,IAAI,CAAC,SAAc;AAAA,MAC7B,eAAe,IAAI;AAAA,MACnB,eAAe,IAAI;AAAA,MACnB,WAAW,IAAI;AAAA,IACjB,EAAE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA2B;AACzB,UAAM,SAAS,KAAK,GAAG;AAAA,MACrB;AAAA,IACF;AACA,WAAO,QAAQ,WAAW;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,4BAA4B,WAA2B;AACrD,UAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,UAAM,SAAS,KAAK,GAAG;AAAA,MACrB;AAAA,MACA,CAAC,KAAK,IAAI,GAAG,MAAM;AAAA,IACrB;AACA,WAAO,QAAQ,WAAW;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,aAAa,MAAsB;AACxC,WAAO,KAAK,QAAQ,kCAAkC,EAAE,EAAE,KAAK;AAAA,EACjE;AAAA;AAAA,EAIQ,aAAa,KAAsB;AACzC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,KAAK,IAAI,OAAO;AAAA,MAChB,KAAK,IAAI,OAAO;AAAA,MAChB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI,YAAY;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,iBAAiB,KAA0B;AACjD,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,WAAW,IAAI,cAAc;AAAA,MAC7B,SAAS,IAAI;AAAA,MACb,YAAY,IAAI,eAAe;AAAA,MAC/B,aAAa,IAAI;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI,IAAI;AAAA,MACxC,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|