pi-ghost-autocomplete 0.3.1 → 0.3.2
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 +101 -6
- package/dist/bench-cli.js +81 -5
- package/dist/bench-cli.js.map +1 -1
- package/dist/bench.d.ts +130 -2
- package/dist/bench.d.ts.map +1 -1
- package/dist/bench.js +285 -4
- package/dist/bench.js.map +1 -1
- package/dist/cache.d.ts +124 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +173 -0
- package/dist/cache.js.map +1 -0
- package/dist/candidate-rerank.d.ts +57 -0
- package/dist/candidate-rerank.d.ts.map +1 -0
- package/dist/candidate-rerank.js +78 -0
- package/dist/candidate-rerank.js.map +1 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +148 -18
- package/dist/config.js.map +1 -1
- package/dist/context-aggregator.d.ts +14 -5
- package/dist/context-aggregator.d.ts.map +1 -1
- package/dist/context-aggregator.js +38 -9
- package/dist/context-aggregator.js.map +1 -1
- package/dist/context-selector.d.ts +128 -0
- package/dist/context-selector.d.ts.map +1 -0
- package/dist/context-selector.js +300 -0
- package/dist/context-selector.js.map +1 -0
- package/dist/cycle-manager.d.ts +102 -0
- package/dist/cycle-manager.d.ts.map +1 -0
- package/dist/cycle-manager.js +168 -0
- package/dist/cycle-manager.js.map +1 -0
- package/dist/edit-history-ring.d.ts +148 -0
- package/dist/edit-history-ring.d.ts.map +1 -0
- package/dist/edit-history-ring.js +262 -0
- package/dist/edit-history-ring.js.map +1 -0
- package/dist/entropy-stop.d.ts +102 -0
- package/dist/entropy-stop.d.ts.map +1 -0
- package/dist/entropy-stop.js +141 -0
- package/dist/entropy-stop.js.map +1 -0
- package/dist/eval-dataset.d.ts +66 -0
- package/dist/eval-dataset.d.ts.map +1 -0
- package/dist/eval-dataset.js +127 -0
- package/dist/eval-dataset.js.map +1 -0
- package/dist/fast-path-engine.d.ts +286 -0
- package/dist/fast-path-engine.d.ts.map +1 -0
- package/dist/fast-path-engine.js +341 -0
- package/dist/fast-path-engine.js.map +1 -0
- package/dist/fuzzy-matcher.d.ts +8 -0
- package/dist/fuzzy-matcher.d.ts.map +1 -1
- package/dist/fuzzy-matcher.js +5 -3
- package/dist/fuzzy-matcher.js.map +1 -1
- package/dist/ghost-editor.d.ts +321 -4
- package/dist/ghost-editor.d.ts.map +1 -1
- package/dist/ghost-editor.js +1190 -144
- package/dist/ghost-editor.js.map +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +81 -2
- package/dist/index.js.map +1 -1
- package/dist/keystroke-timer.d.ts +59 -0
- package/dist/keystroke-timer.d.ts.map +1 -0
- package/dist/keystroke-timer.js +94 -0
- package/dist/keystroke-timer.js.map +1 -0
- package/dist/metrics-builder.d.ts +71 -0
- package/dist/metrics-builder.d.ts.map +1 -0
- package/dist/metrics-builder.js +71 -0
- package/dist/metrics-builder.js.map +1 -0
- package/dist/metrics.d.ts +99 -2
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +131 -17
- package/dist/metrics.js.map +1 -1
- package/dist/path-matcher.d.ts +9 -0
- package/dist/path-matcher.d.ts.map +1 -1
- package/dist/path-matcher.js +5 -2
- package/dist/path-matcher.js.map +1 -1
- package/dist/profile-store.d.ts +82 -0
- package/dist/profile-store.d.ts.map +1 -0
- package/dist/profile-store.js +199 -0
- package/dist/profile-store.js.map +1 -0
- package/dist/profile.d.ts +185 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +434 -0
- package/dist/profile.js.map +1 -0
- package/dist/providers/cloud-provider.d.ts +10 -1
- package/dist/providers/cloud-provider.d.ts.map +1 -1
- package/dist/providers/cloud-provider.js +71 -14
- package/dist/providers/cloud-provider.js.map +1 -1
- package/dist/providers/local-provider.d.ts +8 -1
- package/dist/providers/local-provider.d.ts.map +1 -1
- package/dist/providers/local-provider.js +15 -3
- package/dist/providers/local-provider.js.map +1 -1
- package/dist/providers/openai-compat-provider.d.ts +11 -1
- package/dist/providers/openai-compat-provider.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.js +93 -6
- package/dist/providers/openai-compat-provider.js.map +1 -1
- package/dist/providers/openai-completions-runner.d.ts +25 -1
- package/dist/providers/openai-completions-runner.d.ts.map +1 -1
- package/dist/providers/openai-completions-runner.js +109 -14
- package/dist/providers/openai-completions-runner.js.map +1 -1
- package/dist/providers/race.d.ts +58 -1
- package/dist/providers/race.d.ts.map +1 -1
- package/dist/providers/race.js +317 -4
- package/dist/providers/race.js.map +1 -1
- package/dist/providers/truncate.d.ts.map +1 -1
- package/dist/providers/truncate.js +20 -2
- package/dist/providers/truncate.js.map +1 -1
- package/dist/providers/types.d.ts +96 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +59 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/provisional-manager.d.ts +93 -0
- package/dist/provisional-manager.d.ts.map +1 -0
- package/dist/provisional-manager.js +98 -0
- package/dist/provisional-manager.js.map +1 -0
- package/dist/provisional.d.ts +150 -0
- package/dist/provisional.d.ts.map +1 -0
- package/dist/provisional.js +238 -0
- package/dist/provisional.js.map +1 -0
- package/dist/regret-tracker.d.ts +84 -0
- package/dist/regret-tracker.d.ts.map +1 -0
- package/dist/regret-tracker.js +145 -0
- package/dist/regret-tracker.js.map +1 -0
- package/dist/replay-cli.d.ts +13 -0
- package/dist/replay-cli.d.ts.map +1 -0
- package/dist/replay-cli.js +167 -0
- package/dist/replay-cli.js.map +1 -0
- package/dist/replay.d.ts +107 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +225 -0
- package/dist/replay.js.map +1 -0
- package/dist/reranker.d.ts +181 -0
- package/dist/reranker.d.ts.map +1 -0
- package/dist/reranker.js +324 -0
- package/dist/reranker.js.map +1 -0
- package/dist/session-trie.d.ts +103 -0
- package/dist/session-trie.d.ts.map +1 -0
- package/dist/session-trie.js +280 -0
- package/dist/session-trie.js.map +1 -0
- package/dist/speculative.d.ts +74 -0
- package/dist/speculative.d.ts.map +1 -0
- package/dist/speculative.js +107 -0
- package/dist/speculative.js.map +1 -0
- package/dist/tool-summarize.d.ts +40 -0
- package/dist/tool-summarize.d.ts.map +1 -0
- package/dist/tool-summarize.js +245 -0
- package/dist/tool-summarize.js.map +1 -0
- package/dist/trie-sanitize.d.ts +22 -0
- package/dist/trie-sanitize.d.ts.map +1 -0
- package/dist/trie-sanitize.js +142 -0
- package/dist/trie-sanitize.js.map +1 -0
- package/dist/versions.d.ts +23 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +31 -0
- package/dist/versions.js.map +1 -0
- package/package.json +3 -2
package/dist/profile.js
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User profile — long-lived, cross-session frequency store. PRD §M4.
|
|
3
|
+
*
|
|
4
|
+
* Storage: `.pi/ghost-autocomplete/profile.jsonl` (append-only). One JSON
|
|
5
|
+
* record per line, written via the in-process writer.
|
|
6
|
+
*
|
|
7
|
+
* The profile feeds three local-only signals:
|
|
8
|
+
* - ngram trigrams → reranker `profileBias` (PRD §M3c hook)
|
|
9
|
+
* - path frequency → bounded boost to deterministic path/fuzzy ranking
|
|
10
|
+
* - slash-command frequency → editor fast-path for `/cmd…` prefixes
|
|
11
|
+
*
|
|
12
|
+
* Privacy: raw full prefixes and full completions are NEVER persisted. We
|
|
13
|
+
* store sanitized n-grams (≤4), absolute paths, slash commands, and SHA-256
|
|
14
|
+
* hashes of prefix keys (`prefixHash`) for exact-prefix lookup without
|
|
15
|
+
* writing prompt text. The sanitizer is shared with `trie-sanitize.ts` so
|
|
16
|
+
* the entropy + key-prefix rules drop the same tokens.
|
|
17
|
+
*
|
|
18
|
+
* The on-disk format is intentionally append-only + line-delimited so a
|
|
19
|
+
* crash mid-write never corrupts the existing index; partial trailing lines
|
|
20
|
+
* are skipped on load. Rotation compacts when the file exceeds 2 MB.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
23
|
+
import { appendFile, mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
|
+
import { containsSensitive } from "./trie-sanitize.js";
|
|
26
|
+
export const PROFILE_REL = ".pi/ghost-autocomplete/profile.jsonl";
|
|
27
|
+
export const DEFAULT_MAX_BYTES = 2 * 1024 * 1024;
|
|
28
|
+
export const DEFAULT_DECAY_DAYS = 90;
|
|
29
|
+
export const DROP_BELOW_WEIGHT = 0.1;
|
|
30
|
+
export const MAX_NGRAM_N = 4;
|
|
31
|
+
export const TRIGRAM_N = 3;
|
|
32
|
+
const PATH_RE = /(?:^|[\s"'`(])((?:[\w.-]+\/)+[\w.-]+\.[a-zA-Z]{1,6})\b/g;
|
|
33
|
+
const SLASH_RE = /(?:^|\s)(\/[a-z][a-z0-9_-]{0,30})\b/g;
|
|
34
|
+
const TOKEN_RE = /\b[\w][\w-]*\b/g;
|
|
35
|
+
export function emptyIndex() {
|
|
36
|
+
return {
|
|
37
|
+
ngram: new Map(),
|
|
38
|
+
path: new Map(),
|
|
39
|
+
slash: new Map(),
|
|
40
|
+
acceptByPrefix: new Map(),
|
|
41
|
+
totalAcceptRecords: 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Tokenize a string into the same word-token form the profile stores. Drops
|
|
46
|
+
* the empty string, deduplicates whitespace, and lowercases nothing — code
|
|
47
|
+
* identifiers are case-sensitive.
|
|
48
|
+
*/
|
|
49
|
+
export function tokenize(text) {
|
|
50
|
+
if (!text)
|
|
51
|
+
return [];
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const m of text.matchAll(TOKEN_RE)) {
|
|
54
|
+
if (m[0])
|
|
55
|
+
out.push(m[0]);
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Generate n-grams of size `n` from `tokens`. Returns empty when there are
|
|
61
|
+
* fewer than `n` tokens.
|
|
62
|
+
*/
|
|
63
|
+
export function ngrams(tokens, n) {
|
|
64
|
+
if (n <= 0 || tokens.length < n)
|
|
65
|
+
return [];
|
|
66
|
+
const out = [];
|
|
67
|
+
for (let i = 0; i + n <= tokens.length; i++) {
|
|
68
|
+
out.push(tokens.slice(i, i + n));
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* SHA-256 hex of a prefix string. The hash is the disk-storable surrogate
|
|
74
|
+
* for the prefix — same prefix always maps to the same key, but the raw
|
|
75
|
+
* prefix never leaves the process.
|
|
76
|
+
*/
|
|
77
|
+
export function prefixHash(prefix) {
|
|
78
|
+
return `sha256:${createHash("sha256").update(prefix).digest("hex")}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extract candidate paths from free text. Same shape used by `recent-paths.ts`
|
|
82
|
+
* but kept independent so changes to recent-paths semantics don't drift
|
|
83
|
+
* the profile schema. Matches `foo/bar.ts`, `src/a/b.tsx`, etc.
|
|
84
|
+
*/
|
|
85
|
+
export function extractPaths(text) {
|
|
86
|
+
if (!text)
|
|
87
|
+
return [];
|
|
88
|
+
const out = [];
|
|
89
|
+
PATH_RE.lastIndex = 0;
|
|
90
|
+
for (const m of text.matchAll(PATH_RE)) {
|
|
91
|
+
if (m[1])
|
|
92
|
+
out.push(m[1]);
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract `/slash-command` tokens from free text. Limited to a-z prefix
|
|
98
|
+
* plus alphanumerics/hyphen/underscore to avoid false matches on URLs
|
|
99
|
+
* (`https://…`) or expressions (`x/y`).
|
|
100
|
+
*/
|
|
101
|
+
export function extractSlash(text) {
|
|
102
|
+
if (!text)
|
|
103
|
+
return [];
|
|
104
|
+
const out = [];
|
|
105
|
+
SLASH_RE.lastIndex = 0;
|
|
106
|
+
for (const m of text.matchAll(SLASH_RE)) {
|
|
107
|
+
if (m[1])
|
|
108
|
+
out.push(m[1]);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build profile records from a committed user message. Capture path +
|
|
114
|
+
* slash + trigram ngrams. Sanitize check runs FIRST: messages with
|
|
115
|
+
* sensitive content are dropped entirely (consistent with session trie).
|
|
116
|
+
*/
|
|
117
|
+
export function recordsFromCommit(text, ts = Date.now()) {
|
|
118
|
+
if (!text)
|
|
119
|
+
return [];
|
|
120
|
+
if (containsSensitive(text))
|
|
121
|
+
return [];
|
|
122
|
+
const out = [];
|
|
123
|
+
for (const p of extractPaths(text)) {
|
|
124
|
+
out.push({ ts, kind: "path", value: p, weight: 1 });
|
|
125
|
+
}
|
|
126
|
+
for (const s of extractSlash(text)) {
|
|
127
|
+
out.push({ ts, kind: "slash", value: s, weight: 1 });
|
|
128
|
+
}
|
|
129
|
+
const toks = tokenize(text);
|
|
130
|
+
for (const g of ngrams(toks, TRIGRAM_N)) {
|
|
131
|
+
out.push({ ts, kind: "ngram", n: TRIGRAM_N, tokens: g, weight: 1 });
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build an `accept` record for a full or partial acceptance. `prefix` is
|
|
137
|
+
* hashed; `completion` is tokenized. The raw prefix never appears in the
|
|
138
|
+
* returned record. Drops the whole record if either side fails sanitize.
|
|
139
|
+
*/
|
|
140
|
+
export function recordFromAccept(prefix, completion, weight = 3, ts = Date.now()) {
|
|
141
|
+
if (!prefix || !completion)
|
|
142
|
+
return null;
|
|
143
|
+
if (containsSensitive(prefix) || containsSensitive(completion))
|
|
144
|
+
return null;
|
|
145
|
+
const prefixTailTokens = tokenize(prefix).slice(-MAX_NGRAM_N);
|
|
146
|
+
const completionTokens = tokenize(completion).slice(0, 8);
|
|
147
|
+
if (completionTokens.length === 0)
|
|
148
|
+
return null;
|
|
149
|
+
return {
|
|
150
|
+
ts,
|
|
151
|
+
kind: "accept",
|
|
152
|
+
weight,
|
|
153
|
+
prefixHash: prefixHash(prefix),
|
|
154
|
+
prefixTailTokens,
|
|
155
|
+
completionTokens,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Append one or more records to the JSONL file. Returns silently on write
|
|
160
|
+
* failure — profile logging must never break ghost completion.
|
|
161
|
+
*/
|
|
162
|
+
export async function appendRecords(cwd, recs) {
|
|
163
|
+
if (recs.length === 0)
|
|
164
|
+
return;
|
|
165
|
+
const path = join(cwd, PROFILE_REL);
|
|
166
|
+
try {
|
|
167
|
+
await mkdir(dirname(path), { recursive: true });
|
|
168
|
+
const body = recs.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
169
|
+
await appendFile(path, body, "utf8");
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// best-effort
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Parse the JSONL file into an array of records. Silently skips malformed
|
|
177
|
+
* lines (partial trailing write) and records missing required fields.
|
|
178
|
+
* Returns an empty array when the file does not exist.
|
|
179
|
+
*/
|
|
180
|
+
export async function loadRecords(cwd) {
|
|
181
|
+
const path = join(cwd, PROFILE_REL);
|
|
182
|
+
let body;
|
|
183
|
+
try {
|
|
184
|
+
body = await readFile(path, "utf8");
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
return parseRecords(body);
|
|
190
|
+
}
|
|
191
|
+
export function parseRecords(body) {
|
|
192
|
+
const out = [];
|
|
193
|
+
for (const line of body.split("\n")) {
|
|
194
|
+
if (!line)
|
|
195
|
+
continue;
|
|
196
|
+
try {
|
|
197
|
+
const r = JSON.parse(line);
|
|
198
|
+
if (typeof r.ts !== "number")
|
|
199
|
+
continue;
|
|
200
|
+
if (typeof r.weight !== "number")
|
|
201
|
+
continue;
|
|
202
|
+
if (r.kind !== "ngram" && r.kind !== "path" && r.kind !== "slash" && r.kind !== "accept")
|
|
203
|
+
continue;
|
|
204
|
+
out.push(r);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// partial trailing write — ignore
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Build the in-memory aggregated index from a record list. Decays records
|
|
214
|
+
* older than `decayDays` by halving their weight; drops weights below
|
|
215
|
+
* `DROP_BELOW_WEIGHT` (0.1). `now` defaults to `Date.now()`; expose for
|
|
216
|
+
* deterministic tests.
|
|
217
|
+
*/
|
|
218
|
+
export function buildIndex(records, opts = {}) {
|
|
219
|
+
const now = opts.now ?? Date.now();
|
|
220
|
+
const decayDays = opts.decayDays ?? DEFAULT_DECAY_DAYS;
|
|
221
|
+
const decayCutoff = now - decayDays * 24 * 60 * 60 * 1000;
|
|
222
|
+
const idx = emptyIndex();
|
|
223
|
+
for (const r of records) {
|
|
224
|
+
let w = r.weight;
|
|
225
|
+
if (r.ts < decayCutoff)
|
|
226
|
+
w *= 0.5;
|
|
227
|
+
if (w < DROP_BELOW_WEIGHT)
|
|
228
|
+
continue;
|
|
229
|
+
switch (r.kind) {
|
|
230
|
+
case "ngram":
|
|
231
|
+
if (r.tokens && r.tokens.length > 0) {
|
|
232
|
+
const key = r.tokens.join(" ");
|
|
233
|
+
idx.ngram.set(key, (idx.ngram.get(key) ?? 0) + w);
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
case "path":
|
|
237
|
+
if (r.value)
|
|
238
|
+
idx.path.set(r.value, (idx.path.get(r.value) ?? 0) + w);
|
|
239
|
+
break;
|
|
240
|
+
case "slash":
|
|
241
|
+
if (r.value)
|
|
242
|
+
idx.slash.set(r.value, (idx.slash.get(r.value) ?? 0) + w);
|
|
243
|
+
break;
|
|
244
|
+
case "accept":
|
|
245
|
+
if (r.prefixHash && r.completionTokens && r.completionTokens.length > 0) {
|
|
246
|
+
const existing = idx.acceptByPrefix.get(r.prefixHash);
|
|
247
|
+
if (existing) {
|
|
248
|
+
existing.weight += w;
|
|
249
|
+
// Newer completion tokens win if more frequent; keep the
|
|
250
|
+
// most-recent tokens as the surrogate (last-write-wins is
|
|
251
|
+
// fine here — accept records carry the actual user choice).
|
|
252
|
+
existing.completionTokens = r.completionTokens;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
idx.acceptByPrefix.set(r.prefixHash, {
|
|
256
|
+
completionTokens: r.completionTokens,
|
|
257
|
+
weight: w,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
idx.totalAcceptRecords += 1;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return idx;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Look up the weight of a trigram (already tokenized + joined). Returns 0
|
|
269
|
+
* when missing. Callers should also tokenize the candidate text the same
|
|
270
|
+
* way the writer did.
|
|
271
|
+
*/
|
|
272
|
+
export function ngramWeight(idx, tokens) {
|
|
273
|
+
if (tokens.length === 0)
|
|
274
|
+
return 0;
|
|
275
|
+
let total = 0;
|
|
276
|
+
for (const g of ngrams(tokens, TRIGRAM_N)) {
|
|
277
|
+
total += idx.ngram.get(g.join(" ")) ?? 0;
|
|
278
|
+
}
|
|
279
|
+
return total;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Profile-bias score for a candidate completion. PRD §M4
|
|
283
|
+
* "Ranking-from-prior": `α·log(1 + ngramWeight) + β·pathWeight + γ·slashWeight`.
|
|
284
|
+
* Returns 0 when the index is empty or the candidate has no signal —
|
|
285
|
+
* neutral input to the reranker.
|
|
286
|
+
*/
|
|
287
|
+
export function profileBias(idx, text, weights = {}) {
|
|
288
|
+
if (idx.totalAcceptRecords === 0 && idx.ngram.size === 0 && idx.path.size === 0 && idx.slash.size === 0) {
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
const alpha = weights.alpha ?? 0.3;
|
|
292
|
+
const beta = weights.beta ?? 0.5;
|
|
293
|
+
const gamma = weights.gamma ?? 0.5;
|
|
294
|
+
const toks = tokenize(text);
|
|
295
|
+
const ng = ngramWeight(idx, toks);
|
|
296
|
+
let pathW = 0;
|
|
297
|
+
for (const p of extractPaths(text))
|
|
298
|
+
pathW += idx.path.get(p) ?? 0;
|
|
299
|
+
let slashW = 0;
|
|
300
|
+
for (const s of extractSlash(text))
|
|
301
|
+
slashW += idx.slash.get(s) ?? 0;
|
|
302
|
+
return alpha * Math.log(1 + ng) + beta * pathW + gamma * slashW;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Bounded boost for deterministic path matches. Capped so persistent
|
|
306
|
+
* frequency cannot beat a fresh exact-prefix path-prefix match (PRD §M4
|
|
307
|
+
* "Deterministic path/fuzzy ranking"). The cap is a constant 0.2 — tune
|
|
308
|
+
* once we have replay data.
|
|
309
|
+
*/
|
|
310
|
+
export const MAX_PATH_BOOST = 0.2;
|
|
311
|
+
export function profilePathBoost(idx, path) {
|
|
312
|
+
const w = idx.path.get(path) ?? 0;
|
|
313
|
+
if (w === 0)
|
|
314
|
+
return 0;
|
|
315
|
+
// Log + clamp so a path with 1000 accepts isn't 1000× a path with 1.
|
|
316
|
+
const raw = Math.log(1 + w) / 10;
|
|
317
|
+
return Math.min(MAX_PATH_BOOST, raw);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Slash-command frequency score. Used by the editor's slash fast-path:
|
|
321
|
+
* given a `/cmd-prefix`, return the best-matching full command by weight.
|
|
322
|
+
* Returns null when nothing in the index extends `prefix`.
|
|
323
|
+
*/
|
|
324
|
+
export function bestSlashMatch(idx, prefix) {
|
|
325
|
+
if (!prefix.startsWith("/"))
|
|
326
|
+
return null;
|
|
327
|
+
let best = null;
|
|
328
|
+
for (const [cmd, w] of idx.slash) {
|
|
329
|
+
if (!cmd.startsWith(prefix))
|
|
330
|
+
continue;
|
|
331
|
+
if (cmd === prefix)
|
|
332
|
+
continue;
|
|
333
|
+
if (!best || w > best.weight)
|
|
334
|
+
best = { cmd, weight: w };
|
|
335
|
+
}
|
|
336
|
+
return best;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Rotate + compact when the file exceeds `maxBytes`. Strategy: load all
|
|
340
|
+
* records, decay them, sum weights per (kind, key), drop sub-threshold
|
|
341
|
+
* entries, write the compacted file back. Returns true when a rotation
|
|
342
|
+
* happened. Best-effort — swallows errors.
|
|
343
|
+
*/
|
|
344
|
+
export async function compactIfNeeded(cwd, opts = {}) {
|
|
345
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
346
|
+
const path = join(cwd, PROFILE_REL);
|
|
347
|
+
let size;
|
|
348
|
+
try {
|
|
349
|
+
const s = await stat(path);
|
|
350
|
+
size = s.size;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
if (size < maxBytes)
|
|
356
|
+
return false;
|
|
357
|
+
const records = await loadRecords(cwd);
|
|
358
|
+
const compacted = compactRecords(records, {
|
|
359
|
+
decayDays: opts.decayDays ?? DEFAULT_DECAY_DAYS,
|
|
360
|
+
now: opts.now ?? Date.now(),
|
|
361
|
+
});
|
|
362
|
+
try {
|
|
363
|
+
// Move the original aside first so a crash mid-write keeps the old
|
|
364
|
+
// data recoverable as `profile.jsonl.1`.
|
|
365
|
+
await rename(path, `${path}.1`);
|
|
366
|
+
const body = compacted.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
367
|
+
await writeFile(path, body, "utf8");
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Compact a record list in memory: decay aged weights, sum per (kind,key),
|
|
376
|
+
* drop sub-threshold. Pure function — exposed for unit tests.
|
|
377
|
+
*/
|
|
378
|
+
export function compactRecords(records, opts = {}) {
|
|
379
|
+
const now = opts.now ?? Date.now();
|
|
380
|
+
const decayDays = opts.decayDays ?? DEFAULT_DECAY_DAYS;
|
|
381
|
+
const decayCutoff = now - decayDays * 24 * 60 * 60 * 1000;
|
|
382
|
+
// Map<groupKey, { rec template, summedWeight, latestTs }>
|
|
383
|
+
const groups = new Map();
|
|
384
|
+
for (const r of records) {
|
|
385
|
+
let w = r.weight;
|
|
386
|
+
if (r.ts < decayCutoff)
|
|
387
|
+
w *= 0.5;
|
|
388
|
+
if (w < DROP_BELOW_WEIGHT)
|
|
389
|
+
continue;
|
|
390
|
+
const key = groupKey(r);
|
|
391
|
+
if (!key)
|
|
392
|
+
continue;
|
|
393
|
+
const existing = groups.get(key);
|
|
394
|
+
if (existing) {
|
|
395
|
+
existing.weight += w;
|
|
396
|
+
if (r.ts > existing.ts)
|
|
397
|
+
existing.ts = r.ts;
|
|
398
|
+
// For `accept`, keep the latest completion tokens.
|
|
399
|
+
if (r.kind === "accept" && r.completionTokens) {
|
|
400
|
+
existing.template.completionTokens = r.completionTokens;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
groups.set(key, { template: { ...r }, weight: w, ts: r.ts });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return [...groups.values()].map(({ template, weight, ts }) => ({
|
|
408
|
+
...template,
|
|
409
|
+
weight,
|
|
410
|
+
ts,
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
function groupKey(r) {
|
|
414
|
+
switch (r.kind) {
|
|
415
|
+
case "ngram":
|
|
416
|
+
return r.tokens ? `ngram:${r.tokens.join(" ")}` : null;
|
|
417
|
+
case "path":
|
|
418
|
+
return r.value ? `path:${r.value}` : null;
|
|
419
|
+
case "slash":
|
|
420
|
+
return r.value ? `slash:${r.value}` : null;
|
|
421
|
+
case "accept":
|
|
422
|
+
return r.prefixHash ? `accept:${r.prefixHash}` : null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/** Parse `PI_GHOST_PROFILE_DECAY_DAYS`. Falls back to default when invalid. */
|
|
426
|
+
export function parseDecayDaysEnv(raw) {
|
|
427
|
+
if (raw === undefined)
|
|
428
|
+
return DEFAULT_DECAY_DAYS;
|
|
429
|
+
const v = Number(raw);
|
|
430
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
431
|
+
return DEFAULT_DECAY_DAYS;
|
|
432
|
+
return Math.floor(v);
|
|
433
|
+
}
|
|
434
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { CloudProviderConfig } from "../config.js";
|
|
2
|
-
import type { GhostProvider, GhostRequest } from "./types.js";
|
|
2
|
+
import type { GhostProvider, GhostRequest, StreamDelta } from "./types.js";
|
|
3
3
|
export declare class CloudProvider implements GhostProvider {
|
|
4
4
|
private readonly config;
|
|
5
5
|
readonly name = "ghost-cloud";
|
|
6
6
|
constructor(config: CloudProviderConfig);
|
|
7
7
|
get debounceMs(): number;
|
|
8
8
|
predict(req: GhostRequest): Promise<string | null>;
|
|
9
|
+
/**
|
|
10
|
+
* PRD §M3.5a — stream tokens from the upstream provider via pi-ai's
|
|
11
|
+
* `stream()`. Yields `text_delta` events as `StreamDelta` chunks
|
|
12
|
+
* including a running `cumulative` to spare the editor from tracking
|
|
13
|
+
* concatenation state. `sanitizeCompletion` runs once on the final
|
|
14
|
+
* assembled text after the stream ends — running it per-chunk would
|
|
15
|
+
* truncate mid-token and break ghost rendering.
|
|
16
|
+
*/
|
|
17
|
+
predictStream(req: GhostRequest): AsyncIterable<StreamDelta>;
|
|
9
18
|
}
|
|
10
19
|
//# sourceMappingURL=cloud-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-provider.d.ts","sourceRoot":"","sources":["../../src/providers/cloud-provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloud-provider.d.ts","sourceRoot":"","sources":["../../src/providers/cloud-provider.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK3E,qBAAa,aAAc,YAAW,aAAa;IAGtC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,QAAQ,CAAC,IAAI,iBAAiB;gBAED,MAAM,EAAE,mBAAmB;IAExD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEK,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuCxD;;;;;;;OAOG;IACI,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,aAAa,CAAC,WAAW,CAAC;CA8BnE"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { complete, getEnvApiKey, getModels, } from "@earendil-works/pi-ai";
|
|
1
|
+
import { complete, getEnvApiKey, getModels, stream, } from "@earendil-works/pi-ai";
|
|
2
|
+
import { streamWithSanitize } from "./openai-completions-runner.js";
|
|
2
3
|
import { buildPromptMessages, sanitizeCompletion } from "./truncate.js";
|
|
4
|
+
/** SEC-3: absolute upper bound for a single cloud completion call. */
|
|
5
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
3
6
|
export class CloudProvider {
|
|
4
7
|
config;
|
|
5
8
|
name = "ghost-cloud";
|
|
@@ -19,19 +22,73 @@ export class CloudProvider {
|
|
|
19
22
|
const { systemPrompt, user } = buildPromptMessages(req, this.config.maxRecentMessages);
|
|
20
23
|
const messages = [{ role: "user", content: user, timestamp: Date.now() }];
|
|
21
24
|
const ctx = { systemPrompt, messages };
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
// SEC-3: cap the call at REQUEST_TIMEOUT_MS in addition to the
|
|
26
|
+
// user's keystroke-driven abort.
|
|
27
|
+
const localCtrl = new AbortController();
|
|
28
|
+
const onUserAbort = () => localCtrl.abort();
|
|
29
|
+
if (req.signal.aborted)
|
|
30
|
+
localCtrl.abort();
|
|
31
|
+
else
|
|
32
|
+
req.signal.addEventListener("abort", onUserAbort, { once: true });
|
|
33
|
+
const timeoutHandle = setTimeout(() => localCtrl.abort(), REQUEST_TIMEOUT_MS);
|
|
34
|
+
try {
|
|
35
|
+
const result = await complete(model, ctx, {
|
|
36
|
+
signal: localCtrl.signal,
|
|
37
|
+
temperature: this.config.temperature,
|
|
38
|
+
maxTokens: this.config.maxTokens,
|
|
39
|
+
apiKey,
|
|
40
|
+
});
|
|
41
|
+
if (result.stopReason === "error" || result.stopReason === "aborted")
|
|
42
|
+
return null;
|
|
43
|
+
const text = result.content
|
|
44
|
+
.filter((c) => c.type === "text")
|
|
45
|
+
.map((c) => c.text)
|
|
46
|
+
.join("");
|
|
47
|
+
return sanitizeCompletion(text, req.editorTextBeforeCursor, 200);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
clearTimeout(timeoutHandle);
|
|
51
|
+
req.signal.removeEventListener("abort", onUserAbort);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* PRD §M3.5a — stream tokens from the upstream provider via pi-ai's
|
|
56
|
+
* `stream()`. Yields `text_delta` events as `StreamDelta` chunks
|
|
57
|
+
* including a running `cumulative` to spare the editor from tracking
|
|
58
|
+
* concatenation state. `sanitizeCompletion` runs once on the final
|
|
59
|
+
* assembled text after the stream ends — running it per-chunk would
|
|
60
|
+
* truncate mid-token and break ghost rendering.
|
|
61
|
+
*/
|
|
62
|
+
async *predictStream(req) {
|
|
63
|
+
const apiKey = this.config.apiKey ?? getEnvApiKey(this.config.provider);
|
|
64
|
+
if (!apiKey)
|
|
65
|
+
return;
|
|
66
|
+
const model = resolveModel(this.config.provider, this.config.modelId);
|
|
67
|
+
if (!model)
|
|
68
|
+
return;
|
|
69
|
+
const { systemPrompt, user } = buildPromptMessages(req, this.config.maxRecentMessages);
|
|
70
|
+
const messages = [{ role: "user", content: user, timestamp: Date.now() }];
|
|
71
|
+
const ctx = { systemPrompt, messages };
|
|
72
|
+
const localCtrl = new AbortController();
|
|
73
|
+
const onUserAbort = () => localCtrl.abort();
|
|
74
|
+
if (req.signal.aborted)
|
|
75
|
+
localCtrl.abort();
|
|
76
|
+
else
|
|
77
|
+
req.signal.addEventListener("abort", onUserAbort, { once: true });
|
|
78
|
+
const timeoutHandle = setTimeout(() => localCtrl.abort(), REQUEST_TIMEOUT_MS);
|
|
79
|
+
try {
|
|
80
|
+
const events = stream(model, ctx, {
|
|
81
|
+
signal: localCtrl.signal,
|
|
82
|
+
temperature: this.config.temperature,
|
|
83
|
+
maxTokens: this.config.maxTokens,
|
|
84
|
+
apiKey,
|
|
85
|
+
});
|
|
86
|
+
yield* streamWithSanitize(events, req.editorTextBeforeCursor);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
clearTimeout(timeoutHandle);
|
|
90
|
+
req.signal.removeEventListener("abort", onUserAbort);
|
|
91
|
+
}
|
|
35
92
|
}
|
|
36
93
|
}
|
|
37
94
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-provider.js","sourceRoot":"","sources":["../../src/providers/cloud-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,QAAQ,EACR,YAAY,EACZ,SAAS,
|
|
1
|
+
{"version":3,"file":"cloud-provider.js","sourceRoot":"","sources":["../../src/providers/cloud-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LocalProviderConfig } from "../config.js";
|
|
2
|
-
import type { GhostProvider, GhostRequest } from "./types.js";
|
|
2
|
+
import type { GhostProvider, GhostRequest, StreamDelta } from "./types.js";
|
|
3
3
|
export declare class LocalProvider implements GhostProvider {
|
|
4
4
|
private readonly config;
|
|
5
5
|
readonly name = "ghost-local";
|
|
@@ -7,5 +7,12 @@ export declare class LocalProvider implements GhostProvider {
|
|
|
7
7
|
get debounceMs(): number;
|
|
8
8
|
preflight(): Promise<boolean>;
|
|
9
9
|
predict(req: GhostRequest): Promise<string | null>;
|
|
10
|
+
/**
|
|
11
|
+
* PRD §M3.5a — Ollama/vLLM streaming via the OpenAI-completions stream
|
|
12
|
+
* endpoint. Same opts as `predict`, just driven by the runner's
|
|
13
|
+
* streaming variant.
|
|
14
|
+
*/
|
|
15
|
+
predictStream(req: GhostRequest): AsyncIterable<StreamDelta>;
|
|
16
|
+
private callOpts;
|
|
10
17
|
}
|
|
11
18
|
//# sourceMappingURL=local-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-provider.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"local-provider.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAMxD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI3E,qBAAa,aAAc,YAAW,aAAa;IAGtC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,QAAQ,CAAC,IAAI,iBAAiB;gBAED,MAAM,EAAE,mBAAmB;IAExD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEK,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAY7B,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxD;;;;OAIG;IACH,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,aAAa,CAAC,WAAW,CAAC;IAI5D,OAAO,CAAC,QAAQ;CAYhB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runOpenAICompletion, trimSlash } from "./openai-completions-runner.js";
|
|
1
|
+
import { runOpenAICompletion, runOpenAICompletionStream, trimSlash, } from "./openai-completions-runner.js";
|
|
2
2
|
const DEFAULT_CONTEXT_WINDOW = 8192;
|
|
3
3
|
export class LocalProvider {
|
|
4
4
|
config;
|
|
@@ -22,7 +22,18 @@ export class LocalProvider {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
async predict(req) {
|
|
25
|
-
return runOpenAICompletion(req,
|
|
25
|
+
return runOpenAICompletion(req, this.callOpts());
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* PRD §M3.5a — Ollama/vLLM streaming via the OpenAI-completions stream
|
|
29
|
+
* endpoint. Same opts as `predict`, just driven by the runner's
|
|
30
|
+
* streaming variant.
|
|
31
|
+
*/
|
|
32
|
+
predictStream(req) {
|
|
33
|
+
return runOpenAICompletionStream(req, this.callOpts());
|
|
34
|
+
}
|
|
35
|
+
callOpts() {
|
|
36
|
+
return {
|
|
26
37
|
baseUrl: `${trimSlash(this.config.baseUrl)}/v1`,
|
|
27
38
|
apiKey: "ollama",
|
|
28
39
|
modelId: this.config.modelId,
|
|
@@ -30,7 +41,8 @@ export class LocalProvider {
|
|
|
30
41
|
maxTokens: this.config.maxTokens,
|
|
31
42
|
maxRecentMessages: this.config.maxRecentMessages,
|
|
32
43
|
contextWindow: this.config.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
33
|
-
|
|
44
|
+
temperature: this.config.temperature,
|
|
45
|
+
};
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
48
|
//# sourceMappingURL=local-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-provider.js","sourceRoot":"","sources":["../../src/providers/local-provider.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"local-provider.js","sourceRoot":"","sources":["../../src/providers/local-provider.ts"],"names":[],"mappings":"AACA,OAAO,EACN,mBAAmB,EACnB,yBAAyB,EACzB,SAAS,GACT,MAAM,gCAAgC,CAAC;AAGxC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC,MAAM,OAAO,aAAa;IAGI;IAFpB,IAAI,GAAG,aAAa,CAAC;IAE9B,YAA6B,MAA2B;QAA3B,WAAM,GAAN,MAAM,CAAqB;IAAG,CAAC;IAE5D,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,SAAS;QACd,IAAI,CAAC;YACJ,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAC;YACpD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAC/F,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,GAAG,CAAC,EAAE,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAiB;QAC9B,OAAO,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,GAAiB;QAC9B,OAAO,yBAAyB,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxD,CAAC;IAEO,QAAQ;QACf,OAAO;YACN,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK;YAC/C,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,WAAW,EAAE,QAAQ;YACrB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;YAChC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;YAChD,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,IAAI,sBAAsB;YAClE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;SACpC,CAAC;IACH,CAAC;CACD"}
|