@thesashadev/girl-agent 0.2.2 → 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/dist/cli.js CHANGED
@@ -1353,7 +1353,8 @@ function parseSessionLogTurns(raw, fromId, limit = 30) {
1353
1353
  }
1354
1354
  async function readRecentSessionTurns(slug, tz, fromId, limit = 30) {
1355
1355
  const day = sessionDate(tz);
1356
- const raw = await readSessionLog(slug, day);
1356
+ const days = [.../* @__PURE__ */ new Set([...await listSessionDays(slug), day])].filter((d) => d <= day).sort().slice(-4);
1357
+ const raw = (await Promise.all(days.map((d) => readSessionLog(slug, d)))).join("\n");
1357
1358
  return parseSessionLogTurns(raw, fromId, limit);
1358
1359
  }
1359
1360
  async function readAgenda(slug) {
@@ -1836,8 +1837,8 @@ phoneAvailable=false \u043A\u043E\u0433\u0434\u0430: \u0441\u043F\u0438\u0442, \
1836
1837
  }
1837
1838
  async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(), conflict = null) {
1838
1839
  const dateLocal = localDateStr(cfg.tz, now);
1839
- const path12 = `daily-life/${dateLocal}.md`;
1840
- const existing = await readMd(cfg.slug, path12);
1840
+ const path13 = `daily-life/${dateLocal}.md`;
1841
+ const existing = await readMd(cfg.slug, path13);
1841
1842
  if (existing) {
1842
1843
  try {
1843
1844
  const m = existing.match(/<!--daily:(.+?)-->/s);
@@ -1869,7 +1870,7 @@ async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(
1869
1870
  dl = { dateLocal, vibe: "\u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0434\u0435\u043D\u044C", blocks: [], events: [], wants: [] };
1870
1871
  }
1871
1872
  const human = renderDailyLifeHuman(dl);
1872
- await writeMd(cfg.slug, path12, `${human}
1873
+ await writeMd(cfg.slug, path13, `${human}
1873
1874
 
1874
1875
  <!--daily:${JSON.stringify(dl)}-->
1875
1876
  `);
@@ -2070,15 +2071,131 @@ var init_conflict = __esm({
2070
2071
  }
2071
2072
  });
2072
2073
 
2073
- // src/engine/realism.ts
2074
+ // src/engine/memory-palace.ts
2075
+ import { promises as fs4 } from "fs";
2076
+ import { createHash } from "crypto";
2077
+ import path5 from "path";
2078
+ function wordsFrom(text) {
2079
+ return [...text.toLowerCase().matchAll(/[a-zа-яё0-9]{3,}/gi)].map((match) => match[0]).filter((token) => !STOP_WORDS.has(token));
2080
+ }
2081
+ function normalizedQuote(value) {
2082
+ return value.toLowerCase().replace(/\s+/g, " ").trim();
2083
+ }
2074
2084
  function today(tz) {
2075
2085
  return sessionDate(tz);
2076
2086
  }
2087
+ function nowStamp() {
2088
+ return (/* @__PURE__ */ new Date()).toISOString();
2089
+ }
2090
+ function wingFor(cfg) {
2091
+ return `primary-${cfg.ownerId ?? "unknown"}`;
2092
+ }
2093
+ function normalizeRoom(value) {
2094
+ const source = value.trim().toLowerCase() || "general";
2095
+ const slug = source.replace(/ё/g, "\u0435").replace(/[^a-zа-я0-9]+/gi, "-").replace(/^-+|-+$/g, "").slice(0, 60);
2096
+ return slug || "general";
2097
+ }
2098
+ function normalizeHall(value) {
2099
+ return HALLS.includes(value) ? value : "hall_facts";
2100
+ }
2101
+ function normalizeKeywords(value, quote, room) {
2102
+ const raw = Array.isArray(value) ? value.filter((x) => typeof x === "string") : [];
2103
+ const fallback = [...quote.toLowerCase().matchAll(/[a-zа-яё0-9]{3,}/gi)].map((match) => match[0]).filter((word) => !STOP_WORDS.has(word)).slice(0, 10);
2104
+ return [...new Set([...raw, room, ...fallback].map((x) => x.trim().toLowerCase()).filter(Boolean))].slice(0, 16);
2105
+ }
2106
+ function scoreClamp(value) {
2107
+ if (typeof value !== "number" || !Number.isFinite(value)) return 5;
2108
+ return Math.max(1, Math.min(10, Math.round(value)));
2109
+ }
2110
+ function safeId() {
2111
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
2112
+ }
2113
+ function stableId(source, quote) {
2114
+ return createHash("sha1").update(`${source}
2115
+ ${quote}`).digest("hex").slice(0, 16);
2116
+ }
2117
+ function asRecord(value) {
2118
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2119
+ }
2120
+ function parseDrawer(raw) {
2121
+ const metaMatch = raw.match(/^<!--drawer:(.+?)-->\n/);
2122
+ if (!metaMatch) return null;
2123
+ try {
2124
+ const meta = JSON.parse(metaMatch[1] ?? "");
2125
+ const quote = raw.slice(metaMatch[0].length);
2126
+ if (!meta.id || !meta.ts || !meta.wing || !meta.room || !meta.hall || !meta.source || !quote.trim()) return null;
2127
+ return {
2128
+ id: meta.id,
2129
+ ts: meta.ts,
2130
+ wing: meta.wing,
2131
+ room: meta.room,
2132
+ hall: normalizeHall(meta.hall),
2133
+ source: meta.source,
2134
+ quote: quote.trim(),
2135
+ keywords: normalizeKeywords(meta.keywords, quote, meta.room),
2136
+ salience: scoreClamp(meta.salience)
2137
+ };
2138
+ } catch {
2139
+ return null;
2140
+ }
2141
+ }
2142
+ function renderDrawer(drawer) {
2143
+ const meta = {
2144
+ id: drawer.id,
2145
+ ts: drawer.ts,
2146
+ wing: drawer.wing,
2147
+ room: drawer.room,
2148
+ hall: drawer.hall,
2149
+ source: drawer.source,
2150
+ keywords: drawer.keywords,
2151
+ salience: drawer.salience
2152
+ };
2153
+ return `<!--drawer:${JSON.stringify(meta)}-->
2154
+ ${drawer.quote.trim()}
2155
+ `;
2156
+ }
2157
+ function drawerPath(drawer) {
2158
+ return `memory/palace/${drawer.wing}/${drawer.hall}/${drawer.room}/${drawer.id}.md`;
2159
+ }
2160
+ function parseJsonObject(raw) {
2161
+ try {
2162
+ const parsed = JSON.parse(raw);
2163
+ return asRecord(parsed);
2164
+ } catch {
2165
+ const match = raw.match(/\{[\s\S]*\}/);
2166
+ if (!match) return null;
2167
+ try {
2168
+ const parsed = JSON.parse(match[0]);
2169
+ return asRecord(parsed);
2170
+ } catch {
2171
+ return null;
2172
+ }
2173
+ }
2174
+ }
2175
+ function parsedDrawers(value) {
2176
+ if (!Array.isArray(value)) return [];
2177
+ const out = [];
2178
+ for (const item of value) {
2179
+ const rec = asRecord(item);
2180
+ if (!rec) continue;
2181
+ const quote = typeof rec.quote === "string" ? rec.quote.trim() : "";
2182
+ if (!quote) continue;
2183
+ const room = normalizeRoom(typeof rec.room === "string" ? rec.room : "");
2184
+ out.push({
2185
+ room,
2186
+ hall: normalizeHall(typeof rec.hall === "string" ? rec.hall : ""),
2187
+ quote,
2188
+ keywords: normalizeKeywords(rec.keywords, quote, room),
2189
+ salience: scoreClamp(rec.salience)
2190
+ });
2191
+ }
2192
+ return out;
2193
+ }
2077
2194
  async function ensureDefaults(cfg) {
2078
2195
  const defaults = [
2079
2196
  ["memory/facts.md", DEFAULT_FACTS],
2080
2197
  ["relationship/timeline.md", `# relationship timeline
2081
- - ${(/* @__PURE__ */ new Date()).toISOString()}: \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0437\u0434\u0430\u043D, \u0441\u0442\u0430\u0434\u0438\u044F ${cfg.stage}`],
2198
+ - ${nowStamp()}: \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0437\u0434\u0430\u043D, \u0441\u0442\u0430\u0434\u0438\u044F ${cfg.stage}`],
2082
2199
  ["life/week-plan.md", DEFAULT_WEEKLY],
2083
2200
  ["life/contacts.md", DEFAULT_SOCIAL],
2084
2201
  ["life/habits.md", DEFAULT_HABITS],
@@ -2087,14 +2204,85 @@ async function ensureDefaults(cfg) {
2087
2204
  ["time/promises.md", "# promises\n"],
2088
2205
  ["memory/uncertain.md", "# uncertain\n"]
2089
2206
  ];
2090
- await Promise.all(defaults.map(async ([path12, content]) => {
2091
- const current = await readMd(cfg.slug, path12);
2092
- if (!current.trim()) await writeMd(cfg.slug, path12, content + "\n");
2207
+ await Promise.all(defaults.map(async ([path13, content]) => {
2208
+ const current = await readMd(cfg.slug, path13);
2209
+ if (!current.trim()) await writeMd(cfg.slug, path13, content + "\n");
2093
2210
  }));
2094
2211
  }
2095
- async function loadRealismContext(cfg, incoming) {
2212
+ function scoreDrawer(drawer, tokens, query) {
2213
+ if (!tokens.length) return drawer.salience;
2214
+ const haystack = [
2215
+ drawer.quote,
2216
+ drawer.room,
2217
+ drawer.hall,
2218
+ drawer.keywords.join(" ")
2219
+ ].join("\n").toLowerCase();
2220
+ let score = drawer.salience;
2221
+ for (const token of tokens) {
2222
+ if (haystack.includes(token)) score += drawer.keywords.includes(token) ? 4 : 2;
2223
+ }
2224
+ if (drawer.quote.toLowerCase().includes(query)) score += 4;
2225
+ return score;
2226
+ }
2227
+ async function listPalaceDrawers(cfg) {
2228
+ const root = `memory/palace/${wingFor(cfg)}`;
2229
+ const halls = await listChildDirs(cfg.slug, root);
2230
+ const drawers = [];
2231
+ for (const hall of halls) {
2232
+ const rooms = await listChildDirs(cfg.slug, `${root}/${hall}`);
2233
+ for (const room of rooms) {
2234
+ const files = await listChildFiles(cfg.slug, `${root}/${hall}/${room}`);
2235
+ for (const file of files) {
2236
+ if (!file.endsWith(".md")) continue;
2237
+ const drawer = parseDrawer(await readMd(cfg.slug, `${root}/${hall}/${room}/${file}`));
2238
+ if (drawer) drawers.push(drawer);
2239
+ }
2240
+ }
2241
+ }
2242
+ return drawers;
2243
+ }
2244
+ async function listChildDirs(slug, rel) {
2245
+ try {
2246
+ const entries = await fs4.readdir(path5.join(profileDir(slug), rel), { withFileTypes: true });
2247
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
2248
+ } catch {
2249
+ return [];
2250
+ }
2251
+ }
2252
+ async function listChildFiles(slug, rel) {
2253
+ try {
2254
+ const entries = await fs4.readdir(path5.join(profileDir(slug), rel), { withFileTypes: true });
2255
+ return entries.filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
2256
+ } catch {
2257
+ return [];
2258
+ }
2259
+ }
2260
+ function renderPalaceRecall(drawers) {
2261
+ if (!drawers.length) return "";
2262
+ const grouped = /* @__PURE__ */ new Map();
2263
+ for (const drawer of drawers) {
2264
+ const key = `${drawer.hall}/${drawer.room}`;
2265
+ grouped.set(key, [...grouped.get(key) ?? [], drawer]);
2266
+ }
2267
+ const lines = ["## Memory Palace: \u0442\u043E\u0447\u043D\u044B\u0435 \u044F\u0449\u0438\u043A\u0438 \u043F\u043E \u0442\u0435\u043C\u0435"];
2268
+ for (const [key, group] of grouped) {
2269
+ lines.push(`### ${key}`);
2270
+ for (const drawer of group.slice(0, 4)) {
2271
+ lines.push(`- [${drawer.ts.slice(0, 10)}] ${drawer.quote}`);
2272
+ }
2273
+ }
2274
+ lines.push("\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u044D\u0442\u0438 \u044F\u0449\u0438\u043A\u0438 \u043A\u0430\u043A \u0442\u043E\u0447\u043D\u0443\u044E \u043F\u0430\u043C\u044F\u0442\u044C. \u041D\u0435 \u0446\u0438\u0442\u0438\u0440\u0443\u0439 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0435 hall/room/source, \u043D\u0435 \u0433\u043E\u0432\u043E\u0440\u0438 \u0447\u0442\u043E \u044D\u0442\u043E \u0444\u0430\u0439\u043B \u0438\u043B\u0438 \u043F\u0430\u043C\u044F\u0442\u044C.");
2275
+ return lines.join("\n");
2276
+ }
2277
+ async function searchPalaceDrawers(cfg, query, limit = 8) {
2278
+ const normalized = query.toLowerCase();
2279
+ const tokens = wordsFrom(normalized);
2280
+ const scored = (await listPalaceDrawers(cfg)).map((drawer) => ({ drawer, score: scoreDrawer(drawer, tokens, normalized) })).filter((item) => item.score > item.drawer.salience || item.drawer.salience >= 8).sort((a, b) => b.score - a.score || b.drawer.ts.localeCompare(a.drawer.ts));
2281
+ return scored.slice(0, limit).map((item) => item.drawer);
2282
+ }
2283
+ async function loadMemoryPalaceContext(cfg, incoming) {
2096
2284
  await ensureDefaults(cfg);
2097
- const [facts, episodesRaw, timeline, attachment, openLoops, promises, weeklyLife, socialGraph, habits] = await Promise.all([
2285
+ const [facts, episodesRaw, timeline, attachment, openLoops, promises, weeklyLife, socialGraph, habits, palaceHits] = await Promise.all([
2098
2286
  readMd(cfg.slug, "memory/facts.md"),
2099
2287
  readMd(cfg.slug, `memory/episodes/${today(cfg.tz)}.md`),
2100
2288
  readMd(cfg.slug, "relationship/timeline.md"),
@@ -2103,28 +2291,32 @@ async function loadRealismContext(cfg, incoming) {
2103
2291
  readMd(cfg.slug, "time/promises.md"),
2104
2292
  readMd(cfg.slug, "life/week-plan.md"),
2105
2293
  readMd(cfg.slug, "life/contacts.md"),
2106
- readMd(cfg.slug, "life/habits.md")
2294
+ readMd(cfg.slug, "life/habits.md"),
2295
+ incoming && incoming.trim().length > 3 ? searchPalaceDrawers(cfg, incoming, 10) : Promise.resolve([])
2107
2296
  ]);
2108
2297
  const query = incoming?.toLowerCase() ?? "";
2109
2298
  const factLines = facts.split("\n").filter((l) => l.trim());
2110
- const relevantFacts = query.length > 3 ? factLines.filter((l) => query.split(/\s+/).some((t) => t.length > 3 && l.toLowerCase().includes(t))).slice(-12).join("\n") || facts.slice(-1600) : facts.slice(-1600);
2299
+ const tokens = wordsFrom(query);
2300
+ const relevantFacts = tokens.length ? factLines.filter((l) => tokens.some((t) => l.toLowerCase().includes(t))).slice(-18).join("\n") || facts.slice(-1800) : facts.slice(-1800);
2111
2301
  return {
2112
2302
  facts: relevantFacts,
2113
- episodes: episodesRaw.slice(-1800),
2114
- relationshipTimeline: timeline.slice(-2e3),
2303
+ episodes: episodesRaw.slice(-2200),
2304
+ relationshipTimeline: timeline.slice(-2200),
2115
2305
  attachment: attachment.slice(-1200),
2116
- time: [openLoops.slice(-1200), promises.slice(-1200)].filter(Boolean).join("\n\n"),
2306
+ time: [openLoops.slice(-1400), promises.slice(-1400)].filter(Boolean).join("\n\n"),
2117
2307
  weeklyLife: weeklyLife.slice(-1200),
2118
2308
  socialGraph: socialGraph.slice(-1200),
2119
2309
  habits: habits.slice(-1200),
2120
- openLoops: openLoops.slice(-1200)
2310
+ openLoops: openLoops.slice(-1400),
2311
+ palace: renderPalaceRecall(palaceHits)
2121
2312
  };
2122
2313
  }
2123
- function realismPromptFragment(ctx) {
2314
+ function memoryPalacePromptFragment(ctx) {
2124
2315
  return [
2125
2316
  "# \u0420\u0435\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u0430\u044F \u043D\u0435\u043F\u0440\u0435\u0440\u044B\u0432\u043D\u043E\u0441\u0442\u044C",
2126
2317
  "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u044D\u0442\u0438 \u0434\u0430\u043D\u043D\u044B\u0435 \u043A\u0430\u043A \u0444\u043E\u043D, \u0430 \u043D\u0435 \u043A\u0430\u043A \u043E\u0442\u0447\u0451\u0442. \u041D\u0435 \u0433\u043E\u0432\u043E\u0440\u0438, \u0447\u0442\u043E \u0443 \u0442\u0435\u0431\u044F \u0435\u0441\u0442\u044C \u043F\u0430\u043C\u044F\u0442\u044C, \u0444\u0430\u0439\u043B\u044B, \u0444\u0430\u043A\u0442\u044B \u0438\u043B\u0438 \u0441\u0438\u0441\u0442\u0435\u043C\u0430.",
2127
2318
  "\u0415\u0441\u043B\u0438 \u0442\u043E\u0447\u043D\u043E\u0433\u043E \u0444\u0430\u043A\u0442\u0430 \u043D\u0435\u0442 \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E; \u043E\u0442\u0432\u0435\u0447\u0430\u0439 \u0443\u043A\u043B\u043E\u043D\u0447\u0438\u0432\u043E \u0438\u043B\u0438 \u0443\u0442\u043E\u0447\u043D\u044F\u0439 \u043F\u043E-\u0447\u0435\u043B\u043E\u0432\u0435\u0447\u0435\u0441\u043A\u0438.",
2319
+ ctx.palace,
2128
2320
  "## \u0424\u0430\u043A\u0442\u044B \u043E \u043D\u0451\u043C",
2129
2321
  ctx.facts,
2130
2322
  "## \u042D\u043F\u0438\u0437\u043E\u0434\u044B \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0434\u043D\u044F",
@@ -2143,53 +2335,221 @@ function realismPromptFragment(ctx) {
2143
2335
  ctx.habits
2144
2336
  ].filter(Boolean).join("\n\n");
2145
2337
  }
2338
+ async function appendDrawer(cfg, source, parsed) {
2339
+ const stamp = nowStamp();
2340
+ const quoteKey = normalizedQuote(parsed.quote);
2341
+ const duplicate = (await listPalaceDrawers(cfg)).find(
2342
+ (existing2) => normalizedQuote(existing2.quote) === quoteKey || existing2.room === parsed.room && existing2.hall === parsed.hall && normalizedQuote(existing2.quote).includes(quoteKey) || existing2.room === parsed.room && existing2.hall === parsed.hall && quoteKey.includes(normalizedQuote(existing2.quote))
2343
+ );
2344
+ if (duplicate && duplicate.salience >= parsed.salience) return;
2345
+ const drawer = {
2346
+ id: stableId(source, parsed.quote),
2347
+ ts: stamp,
2348
+ wing: wingFor(cfg),
2349
+ room: parsed.room,
2350
+ hall: parsed.hall,
2351
+ source,
2352
+ quote: parsed.quote,
2353
+ keywords: parsed.keywords,
2354
+ salience: parsed.salience
2355
+ };
2356
+ const existing = await readMd(cfg.slug, drawerPath(drawer));
2357
+ if (existing.trim()) return;
2358
+ await writeMd(cfg.slug, drawerPath(drawer), renderDrawer(drawer));
2359
+ await appendCompatibilityMemory(cfg, drawer);
2360
+ }
2361
+ async function appendCompatibilityMemory(cfg, drawer) {
2362
+ const stamp = drawer.ts;
2363
+ const line = `- ${stamp}: ${drawer.quote}
2364
+ `;
2365
+ if (drawer.hall === "hall_facts" || drawer.hall === "hall_preferences" || drawer.hall === "hall_discoveries") {
2366
+ await appendMd(cfg.slug, "memory/facts.md", line);
2367
+ await appendMd(cfg.slug, "memory/long-term.md", `
2368
+ ## ${stamp.slice(0, 16)}
2369
+ - ${drawer.quote}`);
2370
+ } else if (drawer.hall === "hall_events" || drawer.hall === "hall_feelings") {
2371
+ await appendMd(cfg.slug, `memory/episodes/${today(cfg.tz)}.md`, line);
2372
+ } else if (drawer.hall === "hall_promises") {
2373
+ await appendMd(cfg.slug, "time/promises.md", line);
2374
+ } else if (drawer.hall === "hall_open_loops") {
2375
+ await appendMd(cfg.slug, "time/open-loops.md", line);
2376
+ } else if (drawer.hall === "hall_uncertain") {
2377
+ await appendMd(cfg.slug, "memory/uncertain.md", line);
2378
+ }
2379
+ if (drawer.hall === "hall_events" && drawer.salience >= 8) {
2380
+ await appendMd(cfg.slug, "relationship/timeline.md", line);
2381
+ }
2382
+ }
2146
2383
  async function recordInteractionMemory(llm, cfg, incoming, reply) {
2147
2384
  if (!incoming.trim()) return;
2385
+ await ensureDefaults(cfg);
2148
2386
  const raw = await llm.chat([
2149
- { role: "system", content: "\u0422\u044B \u0438\u0437\u0432\u043B\u0435\u043A\u0430\u0435\u0448\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0443\u0441\u0442\u043E\u0439\u0447\u0438\u0432\u044B\u0435 \u0444\u0430\u043A\u0442\u044B, \u044D\u043F\u0438\u0437\u043E\u0434\u044B, \u043E\u0431\u0435\u0449\u0430\u043D\u0438\u044F \u0438 \u044D\u043C\u043E\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0435 \u0441\u043B\u0435\u0434\u044B \u0438\u0437 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438. \u0412\u0435\u0440\u043D\u0438 \u0441\u0442\u0440\u043E\u0433\u0438\u0439 JSON. \u041D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439." },
2150
- { role: "user", content: `\u041F\u0440\u043E\u0444\u0438\u043B\u044C: ${cfg.name}, \u0441\u0442\u0430\u0434\u0438\u044F ${cfg.stage}.
2151
- \u041E\u043D \u043D\u0430\u043F\u0438\u0441\u0430\u043B: ${incoming}
2152
- \u041E\u043D\u0430 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430: ${reply ?? ""}
2387
+ {
2388
+ role: "system",
2389
+ content: `\u0422\u044B \u0438\u0437\u0432\u043B\u0435\u043A\u0430\u0435\u0448\u044C \u043F\u0430\u043C\u044F\u0442\u044C \u0434\u043B\u044F Telegram-\u043F\u0435\u0440\u0441\u043E\u043D\u044B. \u041F\u0440\u0438\u043D\u0446\u0438\u043F MemPalace: \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0442\u044C \u043E\u0440\u0438\u0433\u0438\u043D\u0430\u043B\u044C\u043D\u044B\u0435 \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043A\u0438 \u0434\u043E\u0441\u043B\u043E\u0432\u043D\u043E, \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u0438 \u043D\u0435 \u0441\u0436\u0438\u043C\u0430\u0442\u044C. \u041D\u0443\u0436\u043D\u044B \u0442\u043E\u043B\u044C\u043A\u043E \u044F\u0432\u043D\u044B\u0435 \u0444\u0430\u043A\u0442\u044B, \u043F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F, \u043E\u0431\u0435\u0449\u0430\u043D\u0438\u044F, \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u043F\u0435\u0442\u043B\u0438, \u044D\u043C\u043E\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0435 \u044D\u043F\u0438\u0437\u043E\u0434\u044B \u0438 \u0441\u043E\u043C\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u0444\u0430\u043A\u0442\u044B. \u0415\u0441\u043B\u0438 \u0432\u0430\u0436\u043D\u0430 \u0446\u0435\u043B\u0430\u044F \u0444\u0440\u0430\u0437\u0430 \u2014 \u0441\u043E\u0445\u0440\u0430\u043D\u0438 \u0435\u0451 \u0446\u0435\u043B\u0438\u043A\u043E\u043C \u0431\u0435\u0437 \u043E\u0431\u0440\u0435\u0437\u043A\u0438 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0441\u043B\u043E\u0432\u0430. \u041D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439.`
2390
+ },
2391
+ {
2392
+ role: "user",
2393
+ content: `\u041F\u0440\u043E\u0444\u0438\u043B\u044C: ${cfg.name}, \u0441\u0442\u0430\u0434\u0438\u044F ${cfg.stage}.
2394
+ \u041E\u043D \u043D\u0430\u043F\u0438\u0441\u0430\u043B:
2395
+ """
2396
+ ${incoming}
2397
+ """
2153
2398
 
2154
- JSON:
2399
+ \u041E\u043D\u0430 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430:
2400
+ """
2401
+ ${reply ?? ""}
2402
+ """
2403
+
2404
+ \u0412\u0435\u0440\u043D\u0438 STRICT JSON:
2155
2405
  {
2156
- "facts": ["\u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0435 \u0444\u0430\u043A\u0442\u044B \u043E \u043D\u0451\u043C \u0438\u043B\u0438 \u043E \u043D\u0435\u0439, \u0435\u0441\u043B\u0438 \u044F\u0432\u043D\u043E \u0441\u043A\u0430\u0437\u0430\u043D\u043E"],
2157
- "episode": "\u043E\u0434\u043D\u043E \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435, \u0435\u0441\u043B\u0438 \u0431\u044B\u043B \u044D\u043C\u043E\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0439/\u0432\u0430\u0436\u043D\u044B\u0439 \u043C\u043E\u043C\u0435\u043D\u0442, \u0438\u043D\u0430\u0447\u0435 \u043F\u0443\u0441\u0442\u043E",
2158
- "promise": "\u043E\u0431\u0435\u0449\u0430\u043D\u0438\u0435/\u043F\u043B\u0430\u043D \u043D\u0430 \u0431\u0443\u0434\u0443\u0449\u0435\u0435, \u0438\u043D\u0430\u0447\u0435 \u043F\u0443\u0441\u0442\u043E",
2159
- "openLoop": "\u043D\u0435\u0437\u0430\u043A\u0440\u044B\u0442\u0430\u044F \u0442\u0435\u043C\u0430, \u0438\u043D\u0430\u0447\u0435 \u043F\u0443\u0441\u0442\u043E",
2160
- "timeline": "milestone \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439, \u0435\u0441\u043B\u0438 \u0431\u044B\u043B, \u0438\u043D\u0430\u0447\u0435 \u043F\u0443\u0441\u0442\u043E",
2161
- "uncertain": ["\u0441\u043E\u043C\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u0444\u0430\u043A\u0442\u044B"]
2162
- }` }
2163
- ], { temperature: 0.2, maxTokens: 3500, json: true });
2164
- let parsed;
2165
- try {
2166
- parsed = JSON.parse(raw);
2167
- } catch {
2168
- return;
2406
+ "drawers": [
2407
+ {
2408
+ "room": "\u043A\u043E\u0440\u043E\u0442\u043A\u0430\u044F \u0442\u0435\u043C\u0430 \u043D\u0430 \u0440\u0443\u0441\u0441\u043A\u043E\u043C \u0438\u043B\u0438 latin-slug",
2409
+ "hall": "hall_facts | hall_events | hall_discoveries | hall_preferences | hall_advice | hall_promises | hall_open_loops | hall_feelings | hall_uncertain",
2410
+ "quote": "\u0434\u043E\u0441\u043B\u043E\u0432\u043D\u0430\u044F \u0444\u0440\u0430\u0437\u0430/\u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442 \u0438\u0437 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438, \u0431\u0435\u0437 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437\u0430 \u0438 \u0431\u0435\u0437 \u043E\u0431\u0440\u0435\u0437\u043A\u0438 \u0432\u0430\u0436\u043D\u044B\u0445 \u0441\u043B\u043E\u0432",
2411
+ "keywords": ["\u0441\u043B\u043E\u0432\u0430 \u0434\u043B\u044F \u043F\u043E\u0438\u0441\u043A\u0430"],
2412
+ "salience": 1-10
2413
+ }
2414
+ ]
2415
+ }`
2416
+ }
2417
+ ], { temperature: 0.1, maxTokens: 3500, json: true });
2418
+ const parsed = parseJsonObject(raw);
2419
+ const drawers = parsedDrawers(parsed?.drawers).slice(0, 12);
2420
+ for (const drawer of drawers) {
2421
+ await appendDrawer(cfg, "interaction", drawer);
2169
2422
  }
2170
- const stamp = (/* @__PURE__ */ new Date()).toISOString();
2171
- const facts = Array.isArray(parsed.facts) ? parsed.facts.filter((x) => typeof x === "string" && x.trim()).slice(0, 8) : [];
2172
- if (facts.length) await appendMd(cfg.slug, "memory/facts.md", facts.map((f) => `- ${stamp}: ${f}`).join("\n") + "\n");
2173
- if (typeof parsed.episode === "string" && parsed.episode.trim()) await appendMd(cfg.slug, `memory/episodes/${today(cfg.tz)}.md`, `- ${stamp}: ${parsed.episode.trim()}
2174
- `);
2175
- if (typeof parsed.promise === "string" && parsed.promise.trim()) await appendMd(cfg.slug, "time/promises.md", `- ${stamp}: ${parsed.promise.trim()}
2176
- `);
2177
- if (typeof parsed.openLoop === "string" && parsed.openLoop.trim()) await appendMd(cfg.slug, "time/open-loops.md", `- ${stamp}: ${parsed.openLoop.trim()}
2178
- `);
2179
- if (typeof parsed.timeline === "string" && parsed.timeline.trim()) await appendMd(cfg.slug, "relationship/timeline.md", `- ${stamp}: ${parsed.timeline.trim()}
2180
- `);
2181
- const uncertain = Array.isArray(parsed.uncertain) ? parsed.uncertain.filter((x) => typeof x === "string" && x.trim()).slice(0, 6) : [];
2182
- if (uncertain.length) await appendMd(cfg.slug, "memory/uncertain.md", uncertain.map((f) => `- ${stamp}: ${f}`).join("\n") + "\n");
2183
2423
  }
2184
- async function maybeAdvanceRelationshipTimeline(cfg, previousStage, nextStage) {
2185
- if (previousStage === nextStage) return;
2424
+ async function mineDailyLogToPalace(llm, cfg, day) {
2186
2425
  await ensureDefaults(cfg);
2187
- await appendMd(cfg.slug, "relationship/timeline.md", `- ${(/* @__PURE__ */ new Date()).toISOString()}: \u0441\u0442\u0430\u0434\u0438\u044F \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0430\u0441\u044C ${previousStage} \u2192 ${nextStage}
2188
- `);
2426
+ const log = await readSessionLog(cfg.slug, day);
2427
+ if (!log.trim()) return 0;
2428
+ const chunks = splitTextByChars(log, 12e3);
2429
+ let total = 0;
2430
+ for (let i = 0; i < chunks.length; i++) {
2431
+ const raw = await llm.chat([
2432
+ {
2433
+ role: "system",
2434
+ content: `\u0422\u044B \u043C\u0430\u0439\u043D\u0438\u0448\u044C \u0434\u043D\u0435\u0432\u043D\u043E\u0439 \u043B\u043E\u0433 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438 \u0432 Memory Palace. \u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0439 \u043C\u0430\u043A\u0441\u0438\u043C\u0443\u043C \u0444\u0430\u043A\u0442\u043E\u0432 \u0438 \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A \u0434\u043E\u0441\u043B\u043E\u0432\u043D\u043E. \u041D\u0435 \u0441\u0443\u043C\u043C\u0430\u0440\u0438\u0437\u0438\u0440\u0443\u0439 \u0432\u043C\u0435\u0441\u0442\u043E quote: quote \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0440\u0438\u0433\u0438\u043D\u0430\u043B\u044C\u043D\u044B\u043C \u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442\u043E\u043C \u043B\u043E\u0433\u0430. \u041C\u043E\u0436\u043D\u043E \u043C\u043D\u043E\u0433\u043E drawers, \u043D\u043E \u043A\u0430\u0436\u0434\u044B\u0439 \u2014 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u0430\u0442\u043E\u043C \u043F\u0430\u043C\u044F\u0442\u0438.`
2435
+ },
2436
+ {
2437
+ role: "user",
2438
+ content: `\u0414\u0435\u043D\u044C: ${day}. \u041F\u0440\u043E\u0444\u0438\u043B\u044C: ${cfg.name}, \u0441\u0442\u0430\u0434\u0438\u044F ${cfg.stage}.
2439
+ \u0427\u0430\u0441\u0442\u044C ${i + 1}/${chunks.length}. \u041B\u043E\u0433:
2440
+ """
2441
+ ${chunks[i] ?? ""}
2442
+ """
2443
+
2444
+ \u0412\u0435\u0440\u043D\u0438 STRICT JSON:
2445
+ {
2446
+ "drawers": [
2447
+ {
2448
+ "room": "\u0442\u0435\u043C\u0430",
2449
+ "hall": "hall_facts | hall_events | hall_discoveries | hall_preferences | hall_advice | hall_promises | hall_open_loops | hall_feelings | hall_uncertain",
2450
+ "quote": "\u0434\u043E\u0441\u043B\u043E\u0432\u043D\u044B\u0439 \u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442 \u043B\u043E\u0433\u0430 \u0431\u0435\u0437 \u043E\u0431\u0440\u0435\u0437\u043A\u0438 \u0432\u0430\u0436\u043D\u044B\u0445 \u0441\u043B\u043E\u0432",
2451
+ "keywords": ["\u043F\u043E\u0438\u0441\u043A"],
2452
+ "salience": 1-10
2453
+ }
2454
+ ]
2455
+ }`
2456
+ }
2457
+ ], { temperature: 0.1, maxTokens: 8e3, json: true });
2458
+ const parsed = parseJsonObject(raw);
2459
+ const drawers = parsedDrawers(parsed?.drawers).slice(0, 80);
2460
+ for (const drawer of drawers) {
2461
+ await appendDrawer(cfg, `log/${day}`, drawer);
2462
+ }
2463
+ total += drawers.length;
2464
+ }
2465
+ if (total) {
2466
+ await writeMd(cfg.slug, `memory/palace/.mined/${day}.txt`, nowStamp() + "\n");
2467
+ }
2468
+ return total;
2189
2469
  }
2190
- var DEFAULT_FACTS, DEFAULT_ATTACHMENT, DEFAULT_WEEKLY, DEFAULT_SOCIAL, DEFAULT_HABITS;
2191
- var init_realism = __esm({
2192
- "src/engine/realism.ts"() {
2470
+ function splitTextByChars(text, maxChars) {
2471
+ if (text.length <= maxChars) return [text];
2472
+ const chunks = [];
2473
+ let current = "";
2474
+ for (const line of text.split(/\r?\n/)) {
2475
+ if (current.length + line.length + 1 > maxChars && current) {
2476
+ chunks.push(current);
2477
+ current = "";
2478
+ }
2479
+ current += current ? `
2480
+ ${line}` : line;
2481
+ }
2482
+ if (current) chunks.push(current);
2483
+ return chunks;
2484
+ }
2485
+ async function mineUnminedDailyLogs(llm, cfg, maxDays = 3) {
2486
+ const todayDay = today(cfg.tz);
2487
+ const days = (await listSessionDays(cfg.slug)).filter((day) => day !== todayDay).reverse();
2488
+ let mined = 0;
2489
+ let checked = 0;
2490
+ for (const day of days) {
2491
+ if (checked >= maxDays) break;
2492
+ const mark = await readMd(cfg.slug, `memory/palace/.mined/${day}.txt`);
2493
+ if (mark.trim()) continue;
2494
+ checked++;
2495
+ mined += await mineDailyLogToPalace(llm, cfg, day);
2496
+ }
2497
+ return mined;
2498
+ }
2499
+ async function migrateExistingMemoryToPalace(cfg) {
2500
+ await ensureDefaults(cfg);
2501
+ const existing = await listPalaceDrawers(cfg);
2502
+ if (existing.length) return 0;
2503
+ let made = 0;
2504
+ const wing = wingFor(cfg);
2505
+ const migrateLines = async (source, content, hall, room) => {
2506
+ const lines = content.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("- "));
2507
+ for (const line of lines) {
2508
+ const quote = line.replace(/^-\s*/, "").trim();
2509
+ if (!quote) continue;
2510
+ const drawer = {
2511
+ id: safeId(),
2512
+ ts: nowStamp(),
2513
+ wing,
2514
+ room: normalizeRoom(room),
2515
+ hall,
2516
+ source,
2517
+ quote,
2518
+ keywords: normalizeKeywords([], quote, room),
2519
+ salience: hall === "hall_uncertain" ? 4 : 6
2520
+ };
2521
+ await writeMd(cfg.slug, drawerPath(drawer), renderDrawer(drawer));
2522
+ made++;
2523
+ }
2524
+ };
2525
+ await migrateLines("memory/facts.md", await readMd(cfg.slug, "memory/facts.md"), "hall_facts", "facts");
2526
+ await migrateLines("memory/uncertain.md", await readMd(cfg.slug, "memory/uncertain.md"), "hall_uncertain", "uncertain");
2527
+ await migrateLines("time/promises.md", await readMd(cfg.slug, "time/promises.md"), "hall_promises", "promises");
2528
+ await migrateLines("time/open-loops.md", await readMd(cfg.slug, "time/open-loops.md"), "hall_open_loops", "open-loops");
2529
+ const dailyDays = await listDailySummaries(cfg.slug);
2530
+ for (const day of dailyDays.slice(-30)) {
2531
+ const summary = await readDailySummary(cfg.slug, day);
2532
+ if (summary.trim()) {
2533
+ const drawer = {
2534
+ id: safeId(),
2535
+ ts: nowStamp(),
2536
+ wing,
2537
+ room: normalizeRoom(day),
2538
+ hall: "hall_events",
2539
+ source: `memory/daily/${day}.md`,
2540
+ quote: summary.trim(),
2541
+ keywords: normalizeKeywords([day], summary, day),
2542
+ salience: 5
2543
+ };
2544
+ await writeMd(cfg.slug, drawerPath(drawer), renderDrawer(drawer));
2545
+ made++;
2546
+ }
2547
+ }
2548
+ return made;
2549
+ }
2550
+ var DEFAULT_FACTS, DEFAULT_ATTACHMENT, DEFAULT_WEEKLY, DEFAULT_SOCIAL, DEFAULT_HABITS, HALLS, STOP_WORDS;
2551
+ var init_memory_palace = __esm({
2552
+ "src/engine/memory-palace.ts"() {
2193
2553
  "use strict";
2194
2554
  init_esm_shims();
2195
2555
  init_md();
@@ -2220,6 +2580,74 @@ apologyStyle: awkward_short`;
2220
2580
  - \u043A\u043E\u0433\u0434\u0430 \u0443\u0441\u0442\u0430\u043B\u0430, \u0447\u0438\u0442\u0430\u0435\u0442 \u0438 \u043E\u0442\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0435\u0442 \u043E\u0442\u0432\u0435\u0442
2221
2581
  - \u0440\u0435\u0434\u043A\u043E \u043E\u0431\u044A\u044F\u0441\u043D\u044F\u0435\u0442 \u043F\u043E\u0447\u0435\u043C\u0443 \u043F\u0440\u043E\u043F\u0430\u043B\u0430
2222
2582
  - \u043C\u043E\u0436\u0435\u0442 \u0432\u0441\u043F\u043E\u043C\u043D\u0438\u0442\u044C \u043C\u0435\u043B\u043A\u0443\u044E \u0434\u0435\u0442\u0430\u043B\u044C \u0447\u0435\u0440\u0435\u0437 \u0434\u0435\u043D\u044C, \u0435\u0441\u043B\u0438 \u043E\u043D\u0430 \u044D\u043C\u043E\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E \u0437\u0430\u0446\u0435\u043F\u0438\u043B\u0430`;
2583
+ HALLS = [
2584
+ "hall_facts",
2585
+ "hall_events",
2586
+ "hall_discoveries",
2587
+ "hall_preferences",
2588
+ "hall_advice",
2589
+ "hall_promises",
2590
+ "hall_open_loops",
2591
+ "hall_feelings",
2592
+ "hall_uncertain"
2593
+ ];
2594
+ STOP_WORDS = /* @__PURE__ */ new Set([
2595
+ "\u044D\u0442\u043E",
2596
+ "\u043A\u0430\u043A",
2597
+ "\u0447\u0442\u043E",
2598
+ "\u0438\u043B\u0438",
2599
+ "\u0435\u0441\u043B\u0438",
2600
+ "\u043E\u043D\u0430",
2601
+ "\u043E\u043D\u0438",
2602
+ "\u0435\u0433\u043E",
2603
+ "\u0435\u043C\u0443",
2604
+ "\u0442\u0435\u0431\u044F",
2605
+ "\u0442\u0435\u0431\u0435",
2606
+ "\u043C\u0435\u043D\u044F",
2607
+ "\u043C\u043D\u0435",
2608
+ "\u0434\u043B\u044F",
2609
+ "\u043F\u0440\u043E",
2610
+ "\u043D\u0430\u0434",
2611
+ "\u043F\u043E\u0434",
2612
+ "\u043F\u0440\u0438",
2613
+ "\u0431\u0435\u0437",
2614
+ "\u0435\u0449\u0435",
2615
+ "\u0435\u0449\u0451",
2616
+ "\u0443\u0436\u0435",
2617
+ "\u0442\u0430\u043C",
2618
+ "\u0442\u0443\u0442",
2619
+ "\u0442\u043E\u0436\u0435",
2620
+ "\u043F\u043E\u0441\u043B\u0435",
2621
+ "\u0441\u0435\u0439\u0447\u0430\u0441",
2622
+ "\u0449\u0430\u0441",
2623
+ "\u043A\u043E\u0433\u0434\u0430",
2624
+ "\u043F\u043E\u0447\u0435\u043C\u0443",
2625
+ "\u043F\u043E\u0442\u043E\u043C\u0443",
2626
+ "\u043E\u0447\u0435\u043D\u044C",
2627
+ "\u043F\u0440\u043E\u0441\u0442\u043E",
2628
+ "\u0432\u043E\u043E\u0431\u0449\u0435",
2629
+ "\u043A\u043E\u0440\u043E\u0447\u0435",
2630
+ "\u0442\u0438\u043F\u0430"
2631
+ ]);
2632
+ }
2633
+ });
2634
+
2635
+ // src/engine/realism.ts
2636
+ async function maybeAdvanceRelationshipTimeline(cfg, previousStage, nextStage) {
2637
+ if (previousStage === nextStage) return;
2638
+ await migrateExistingMemoryToPalace(cfg);
2639
+ await appendMd(cfg.slug, "relationship/timeline.md", `- ${(/* @__PURE__ */ new Date()).toISOString()}: \u0441\u0442\u0430\u0434\u0438\u044F \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0430\u0441\u044C ${previousStage} \u2192 ${nextStage}
2640
+ `);
2641
+ }
2642
+ var loadRealismContext, realismPromptFragment;
2643
+ var init_realism = __esm({
2644
+ "src/engine/realism.ts"() {
2645
+ "use strict";
2646
+ init_esm_shims();
2647
+ init_md();
2648
+ init_memory_palace();
2649
+ loadRealismContext = loadMemoryPalaceContext;
2650
+ realismPromptFragment = memoryPalacePromptFragment;
2223
2651
  }
2224
2652
  });
2225
2653
 
@@ -2509,8 +2937,8 @@ ${ignoreTendency}`,
2509
2937
  `\u0421\u0442\u0430\u0434\u0438\u044F: ${stage.label}`,
2510
2938
  `\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u0441\u0442\u0430\u0434\u0438\u0438: ${stage.description}`,
2511
2939
  `Score: ${JSON.stringify(rel.score)}`,
2512
- longTerm ? `## long-term memory \u043E \u044E\u0437\u0435\u0440\u0435
2513
- ${longTerm}` : "",
2940
+ longTerm.trim() ? `## legacy long-term memory \u043E \u044E\u0437\u0435\u0440\u0435
2941
+ ${longTerm.slice(-2200)}` : "",
2514
2942
  recall
2515
2943
  ].filter(Boolean).join("\n\n");
2516
2944
  }
@@ -3539,7 +3967,8 @@ autonomous:${dateKey} created=0`.trim() + "\n");
3539
3967
  const rel = await readRelationship(cfg.slug);
3540
3968
  const persona = (await readMd(cfg.slug, "persona.md")).slice(0, 900);
3541
3969
  const speech = (await readMd(cfg.slug, "speech.md")).slice(0, 600);
3542
- const longTerm = (await readMd(cfg.slug, "memory/long-term.md")).slice(0, 1200);
3970
+ const palace = await searchPalaceDrawers(cfg, history.slice(-8).map((m) => m.content).join("\n"), 8);
3971
+ const longTerm = palace.length ? palace.map((d) => `- [${d.ts.slice(0, 10)} ${d.hall}/${d.room}] ${d.quote}`).join("\n") : (await readMd(cfg.slug, "memory/long-term.md")).slice(-1200);
3543
3972
  const histStr = history.slice(-16).map((m) => `${m.role === "user" ? "\u043E\u043D" : "\u043E\u043D\u0430"}: ${m.content}`).join("\n");
3544
3973
  const stateBlock = [
3545
3974
  `# \u0421\u0442\u0430\u0434\u0438\u044F: ${stage.label} (${cfg.stage})`,
@@ -3674,6 +4103,7 @@ var init_agenda = __esm({
3674
4103
  init_md();
3675
4104
  init_stages();
3676
4105
  init_communication();
4106
+ init_memory_palace();
3677
4107
  SYS_EXTRACT = `\u0422\u044B \u2014 \u043C\u043E\u0434\u0443\u043B\u044C \u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043D\u0442\u0430-\u0434\u0435\u0432\u0443\u0448\u043A\u0438. \u0422\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430: \u043F\u043E\u0441\u043B\u0435 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043F\u0430\u0440\u043D\u044F \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C, \u043D\u0435 \u0443\u043F\u043E\u043C\u044F\u043D\u0443\u043B \u043B\u0438 \u043E\u043D \u0447\u0442\u043E-\u0442\u043E \u043F\u0440\u043E \u0441\u0432\u043E\u0451 \u0431\u0443\u0434\u0443\u0449\u0435\u0435 (\u043F\u043B\u0430\u043D\u044B, \u0441\u043E\u0431\u044B\u0442\u0438\u044F, \u0434\u0435\u043B\u0430, \u0434\u0435\u0434\u043B\u0430\u0439\u043D\u044B, \u0440\u0430\u0431\u043E\u0442\u0443, \u0441\u043E\u0440\u0435\u0432\u043D\u043E\u0432\u0430\u043D\u0438\u044F, \u044D\u043A\u0437\u0430\u043C\u0435\u043D\u044B, \u043F\u043E\u0435\u0437\u0434\u043A\u0438), \u043D\u0430 \u0447\u0442\u043E \u041E\u041D\u0410 \u0431\u044B \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u043B\u0430 / \u0445\u043E\u0442\u0435\u043B\u0430 \u0443\u0437\u043D\u0430\u0442\u044C / \u043E\u0442\u043C\u0435\u0442\u0438\u043B\u0430 \u043F\u0440\u043E \u0441\u0435\u0431\u044F \u2014 \u043A\u0430\u043A \u0436\u0438\u0432\u043E\u0439 \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u043D\u0435 \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C. \u0422\u0430\u043A\u0436\u0435 \u043F\u043E\u043D\u044F\u0442\u044C, \u0435\u0441\u0442\u044C \u043B\u0438 \u0443 \u041D\u0415\u0401 \u043F\u043E\u0432\u043E\u0434 \u043F\u0440\u043E\u0430\u043A\u0442\u0438\u0432\u043D\u043E \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0435\u043C\u0443 \u043F\u043E\u0437\u0436\u0435 (\u0431\u0435\u0437 \u044F\u0432\u043D\u043E\u0433\u043E \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u0430 \u043E\u0442 \u043D\u0435\u0433\u043E).
3678
4108
 
3679
4109
  \u0412\u0410\u0416\u041D\u041E:
@@ -3832,6 +4262,7 @@ async function buildDailySummary(llm, cfg, day) {
3832
4262
  const log = await readSessionLog(cfg.slug, day);
3833
4263
  if (!log || log.length < 50) return null;
3834
4264
  try {
4265
+ await mineDailyLogToPalace(llm, cfg, day).catch(() => 0);
3835
4266
  const raw = await llm.chat([
3836
4267
  { role: "system", content: SYS3 },
3837
4268
  {
@@ -3840,7 +4271,7 @@ async function buildDailySummary(llm, cfg, day) {
3840
4271
 
3841
4272
  \u041B\u043E\u0433 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438 \u0437\u0430 \u0434\u0435\u043D\u044C:
3842
4273
  """
3843
- ${log.slice(0, 8e3)}
4274
+ ${log.slice(-8e3)}
3844
4275
  """
3845
4276
 
3846
4277
  \u0412\u0435\u0440\u043D\u0438 STRICT JSON:
@@ -3907,18 +4338,19 @@ var init_daily_summarizer = __esm({
3907
4338
  "use strict";
3908
4339
  init_esm_shims();
3909
4340
  init_md();
4341
+ init_memory_palace();
3910
4342
  SYS3 = `\u0422\u044B \u2014 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0439 \u0434\u043D\u0435\u0432\u043D\u0438\u043A \u0434\u0435\u0432\u0443\u0448\u043A\u0438. \u041F\u043E \u0441\u044B\u0440\u043E\u043C\u0443 \u043B\u043E\u0433\u0443 \u0435\u0451 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438 \u0437\u0430 \u0434\u0435\u043D\u044C \u043D\u0430\u043F\u0438\u0448\u0438 \u041A\u0420\u0410\u0422\u041A\u0423\u042E \u0441\u0432\u043E\u0434\u043A\u0443 \u0434\u043B\u044F \u0434\u043E\u043B\u0433\u043E\u0441\u0440\u043E\u0447\u043D\u043E\u0439 \u043F\u0430\u043C\u044F\u0442\u0438. \u041E\u0442 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043B\u0438\u0446\u0430, \u0432 \u0435\u0451 \u043C\u0430\u043D\u0435\u0440\u0435 (lowercase, \u0431\u0435\u0437 markdown). \u0427\u0442\u043E \u0431\u044B\u043B\u043E, \u0447\u0442\u043E \u043E\u0431\u0441\u0443\u0436\u0434\u0430\u043B\u0438, \u043A\u0430\u043A \u043E\u043D\u0430 \u0432 \u0438\u0442\u043E\u0433\u0435 \u0432\u043E\u0441\u043F\u0440\u0438\u043D\u044F\u043B\u0430 \u0435\u0433\u043E, \u043A\u0430\u043A\u0438\u0435 \u043F\u043E\u044F\u0432\u0438\u043B\u0438\u0441\u044C \u043D\u043E\u0432\u044B\u0435 \u0444\u0430\u043A\u0442\u044B \u043E \u043D\u0451\u043C, \u0431\u0435\u0441\u0438\u043B\u043E \u043B\u0438 \u0447\u0442\u043E-\u0442\u043E.`;
3911
4343
  }
3912
4344
  });
3913
4345
 
3914
4346
  // src/engine/stickers.ts
3915
- import { promises as fs4 } from "fs";
3916
- import path5 from "path";
4347
+ import { promises as fs5 } from "fs";
4348
+ import path6 from "path";
3917
4349
  async function libraryPath(cfg) {
3918
4350
  const rel = "stickers/library.md";
3919
4351
  const current = await readMd(cfg.slug, rel);
3920
4352
  if (!current.trim()) await writeMd(cfg.slug, rel, DEFAULT_LIBRARY);
3921
- return path5.join(profileDir(cfg.slug), rel);
4353
+ return path6.join(profileDir(cfg.slug), rel);
3922
4354
  }
3923
4355
  async function listStickers(cfg) {
3924
4356
  await libraryPath(cfg);
@@ -3938,8 +4370,8 @@ async function pickSticker(cfg, mood = "") {
3938
4370
  }
3939
4371
  async function addStickerToLibrary(cfg, fileId, emoji = "", tags = []) {
3940
4372
  await libraryPath(cfg);
3941
- const p = path5.join(profileDir(cfg.slug), "stickers/library.md");
3942
- await fs4.appendFile(p, `${fileId} | ${emoji} | ${tags.join(",")}
4373
+ const p = path6.join(profileDir(cfg.slug), "stickers/library.md");
4374
+ await fs5.appendFile(p, `${fileId} | ${emoji} | ${tags.join(",")}
3943
4375
  `, "utf8");
3944
4376
  }
3945
4377
  var DEFAULT_LIBRARY;
@@ -4642,6 +5074,7 @@ var init_runtime = __esm({
4642
5074
  init_conflict();
4643
5075
  init_daily_summarizer();
4644
5076
  init_realism();
5077
+ init_memory_palace();
4645
5078
  init_media();
4646
5079
  init_security();
4647
5080
  init_stickers();
@@ -4839,7 +5272,7 @@ var init_runtime = __esm({
4839
5272
  async historyFor(key, fromId, restore = false) {
4840
5273
  const existing = this.histories.get(key);
4841
5274
  if (existing) return existing;
4842
- const restored = restore ? await readRecentSessionTurns(this.cfg.slug, this.cfg.tz, fromId, 30) : [];
5275
+ const restored = restore ? await readRecentSessionTurns(this.cfg.slug, this.cfg.tz, fromId, 80) : [];
4843
5276
  const hist = restored.map((t) => ({ role: t.role, content: t.content, ts: t.ts }));
4844
5277
  this.histories.set(key, hist);
4845
5278
  this.hydratePresenceTrackers(key, hist);
@@ -5097,6 +5530,8 @@ ${m.text}` : media;
5097
5530
  }
5098
5531
  const made = await closeStaleSessions(this.llm, this.cfg);
5099
5532
  if (made > 0) this.emit("event", { type: "info", text: `daily summaries: +${made}` });
5533
+ const mined = await mineUnminedDailyLogs(this.llm, this.cfg, 2).catch(() => 0);
5534
+ if (mined > 0) this.emit("event", { type: "info", text: `memory palace drawers: +${mined}` });
5100
5535
  }
5101
5536
  /**
5102
5537
  * Issue #81 — периодически выставляет статус «онлайн» в Telegram
@@ -5343,6 +5778,8 @@ ${m.text}` : media;
5343
5778
  }
5344
5779
  this.emit("event", { type: "ignored", text: incomingText, reason: tick.ignoreReason ?? tick.intent });
5345
5780
  await appendSessionLog(this.cfg.slug, this.cfg.tz, ` -> ignored (${tick.intent}: ${tick.ignoreReason ?? ""})`);
5781
+ recordInteractionMemory(this.llm, this.cfg, incomingText).catch(() => {
5782
+ });
5346
5783
  return;
5347
5784
  }
5348
5785
  let delaySec = tick.delaySec;
@@ -5382,7 +5819,7 @@ intent=${tick.intent}
5382
5819
  \u043A\u043E\u043B-\u0432\u043E \u043F\u0443\u0437\u044B\u0440\u0435\u0439: ${tick.bubbles}${presenceHint ? `
5383
5820
  \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C: ${presenceHint}` : ""}
5384
5821
  ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u0434\u043D\u043E\u0441\u043B\u043E\u0436\u043D\u043E: '\u043E\u043A', '\u044F\u0441\u043D\u043E', '\u0438?', '\u043D\u0443 \u043E\u043A'. \u0411\u0435\u0437 \u043E\u0431\u044A\u044F\u0441\u043D\u0435\u043D\u0438\u0439." : tick.bubbles > 1 ? "\u0420\u0430\u0437\u0431\u0435\u0439 \u043E\u0442\u0432\u0435\u0442 \u043D\u0430 \u043F\u0443\u0437\u044B\u0440\u0438 \u0421\u0422\u0420\u041E\u0413\u041E \u0441\u0442\u0440\u043E\u043A\u043E\u0439 '---' (\u0442\u0440\u0438 \u0434\u0435\u0444\u0438\u0441\u0430 \u043D\u0430 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0435) \u043C\u0435\u0436\u0434\u0443 \u043D\u0438\u043C\u0438. \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u0437\u044B\u0440\u044C \u2014 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u0442\u0433. \u0417\u0410\u041F\u0420\u0415\u0429\u0415\u041D\u041E \u0440\u0430\u0441\u043A\u0438\u0434\u044B\u0432\u0430\u0442\u044C \u043E\u0434\u043D\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043D\u0430 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0442\u0440\u043E\u043A \u0447\u0435\u0440\u0435\u0437 \u043F\u0435\u0440\u0435\u043D\u043E\u0441 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u0435\u0437 '---' \u2014 \u0432 \u0442\u0433 \u044D\u0442\u043E \u0432\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043A\u0430\u043A \u043E\u0434\u043D\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u0441\u0442\u043E\u043B\u0431\u0438\u043A, \u0447\u0442\u043E \u043F\u0430\u043B\u0438\u0442 \u0418\u0418. \u041F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E:\\n\\n\u043F\u0440\u0438\u0432\u0435\u0442\\n---\\n\u043A\u0430\u043A \u0441\u0430\u043C\\n\\n\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E:\\n\\n\u043F\u0440\u0438\u0432\u0435\u0442\\n\u043A\u0430\u043A \u0441\u0430\u043C" : "\u041E\u0434\u0438\u043D \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u043E\u0442\u0432\u0435\u0442, \u0431\u0435\u0437 '---'."}${scopeHint}` },
5385
- ...hist.slice(-30).map((t) => ({ role: t.role, content: t.content }))
5822
+ ...hist.slice(-60).map((t) => ({ role: t.role, content: t.content }))
5386
5823
  ];
5387
5824
  const image = imagePartFromMedia(incoming?.media);
5388
5825
  if (image) {
@@ -6250,22 +6687,41 @@ ${persona}`;
6250
6687
  }
6251
6688
  });
6252
6689
 
6690
+ // src/migrations/0114-memory-palace.ts
6691
+ var migration0114;
6692
+ var init_memory_palace2 = __esm({
6693
+ "src/migrations/0114-memory-palace.ts"() {
6694
+ "use strict";
6695
+ init_esm_shims();
6696
+ init_memory_palace();
6697
+ migration0114 = {
6698
+ id: "0114-memory-palace",
6699
+ description: "\u041F\u0435\u0440\u0435\u043D\u0435\u0441\u0442\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u0444\u0430\u0439\u043B\u044B \u043F\u0430\u043C\u044F\u0442\u0438 \u0432 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 Memory Palace",
6700
+ async migrate(ctx) {
6701
+ const made = await migrateExistingMemoryToPalace(ctx.config);
6702
+ if (made > 0) ctx.log(`memory palace drawers: +${made}`);
6703
+ return ctx.config;
6704
+ }
6705
+ };
6706
+ }
6707
+ });
6708
+
6253
6709
  // src/migrations/index.ts
6254
- import { promises as fs5 } from "fs";
6255
- import path6 from "path";
6710
+ import { promises as fs6 } from "fs";
6711
+ import path7 from "path";
6256
6712
  import { readFileSync } from "fs";
6257
6713
  import { fileURLToPath as fileURLToPath3 } from "url";
6258
6714
  async function readMigrationState() {
6259
6715
  try {
6260
- const raw = await fs5.readFile(MIGRATIONS_FILE(), "utf8");
6716
+ const raw = await fs6.readFile(MIGRATIONS_FILE(), "utf8");
6261
6717
  return JSON.parse(raw);
6262
6718
  } catch {
6263
6719
  return { appliedMigrations: [], lastRunVersion: "0.0.0", lastRunAt: "" };
6264
6720
  }
6265
6721
  }
6266
6722
  async function writeMigrationState(state) {
6267
- await fs5.mkdir(DATA_ROOT, { recursive: true });
6268
- await fs5.writeFile(MIGRATIONS_FILE(), JSON.stringify(state, null, 2), "utf8");
6723
+ await fs6.mkdir(DATA_ROOT, { recursive: true });
6724
+ await fs6.writeFile(MIGRATIONS_FILE(), JSON.stringify(state, null, 2), "utf8");
6269
6725
  }
6270
6726
  async function pendingMigrations() {
6271
6727
  const state = await readMigrationState();
@@ -6358,15 +6814,15 @@ function formatUpdateWarnings(warnings) {
6358
6814
  function currentVersion() {
6359
6815
  try {
6360
6816
  const here = fileURLToPath3(import.meta.url);
6361
- let dir = path6.dirname(here);
6817
+ let dir = path7.dirname(here);
6362
6818
  for (let i = 0; i < 5; i++) {
6363
- const candidate = path6.join(dir, "package.json");
6819
+ const candidate = path7.join(dir, "package.json");
6364
6820
  try {
6365
6821
  const pkg = JSON.parse(readFileSync(candidate, "utf8"));
6366
6822
  if (pkg.name === "@thesashadev/girl-agent" && pkg.version) return pkg.version;
6367
6823
  } catch {
6368
6824
  }
6369
- dir = path6.dirname(dir);
6825
+ dir = path7.dirname(dir);
6370
6826
  }
6371
6827
  return "unknown";
6372
6828
  } catch {
@@ -6381,10 +6837,12 @@ var init_migrations = __esm({
6381
6837
  init_md();
6382
6838
  init_add_use_wss_default();
6383
6839
  init_ensure_communication_md();
6384
- MIGRATIONS_FILE = () => path6.join(DATA_ROOT, ".migrations.json");
6840
+ init_memory_palace2();
6841
+ MIGRATIONS_FILE = () => path7.join(DATA_ROOT, ".migrations.json");
6385
6842
  ALL_MIGRATIONS = [
6386
6843
  migration0112,
6387
- migration0113
6844
+ migration0113,
6845
+ migration0114
6388
6846
  ];
6389
6847
  }
6390
6848
  });
@@ -6542,23 +7000,23 @@ __export(addons_exports, {
6542
7000
  updateSettings: () => updateSettings,
6543
7001
  validateManifest: () => validateManifest
6544
7002
  });
6545
- import { promises as fs8 } from "fs";
6546
- import path9 from "path";
7003
+ import { promises as fs9 } from "fs";
7004
+ import path10 from "path";
6547
7005
  import os3 from "os";
6548
7006
  import { execFile } from "child_process";
6549
7007
  import { promisify } from "util";
6550
7008
  function addonsDir() {
6551
- const root = process.env.GIRL_AGENT_DATA ? path9.resolve(process.env.GIRL_AGENT_DATA, "..") : path9.join(os3.homedir(), ".local", "share", "girl-agent");
6552
- return path9.join(root, "addons");
7009
+ const root = process.env.GIRL_AGENT_DATA ? path10.resolve(process.env.GIRL_AGENT_DATA, "..") : path10.join(os3.homedir(), ".local", "share", "girl-agent");
7010
+ return path10.join(root, "addons");
6553
7011
  }
6554
7012
  async function ensureDir() {
6555
7013
  const dir = addonsDir();
6556
- await fs8.mkdir(dir, { recursive: true });
7014
+ await fs9.mkdir(dir, { recursive: true });
6557
7015
  return dir;
6558
7016
  }
6559
7017
  async function readJsonOrEmpty(p, fallback) {
6560
7018
  try {
6561
- const raw = await fs8.readFile(p, "utf8");
7019
+ const raw = await fs9.readFile(p, "utf8");
6562
7020
  return JSON.parse(raw);
6563
7021
  } catch {
6564
7022
  return fallback;
@@ -6566,12 +7024,12 @@ async function readJsonOrEmpty(p, fallback) {
6566
7024
  }
6567
7025
  async function listInstalled() {
6568
7026
  const dir = await ensureDir();
6569
- const indexPath = path9.join(dir, "installed.json");
7027
+ const indexPath = path10.join(dir, "installed.json");
6570
7028
  return await readJsonOrEmpty(indexPath, []);
6571
7029
  }
6572
7030
  async function writeInstalled(list) {
6573
7031
  const dir = await ensureDir();
6574
- await fs8.writeFile(path9.join(dir, "installed.json"), JSON.stringify(list, null, 2), "utf8");
7032
+ await fs9.writeFile(path10.join(dir, "installed.json"), JSON.stringify(list, null, 2), "utf8");
6575
7033
  }
6576
7034
  async function fetchRegistry() {
6577
7035
  try {
@@ -6585,17 +7043,17 @@ async function fetchRegistry() {
6585
7043
  }
6586
7044
  }
6587
7045
  async function unpackGaa(gaaPath) {
6588
- const tmpDir = path9.join(os3.tmpdir(), `gaa-${Date.now()}-${Math.random().toString(36).slice(2)}`);
6589
- await fs8.mkdir(tmpDir, { recursive: true });
7046
+ const tmpDir = path10.join(os3.tmpdir(), `gaa-${Date.now()}-${Math.random().toString(36).slice(2)}`);
7047
+ await fs9.mkdir(tmpDir, { recursive: true });
6590
7048
  await execFileAsync("unzip", ["-o", "-q", gaaPath, "-d", tmpDir]);
6591
- const entries = await fs8.readdir(tmpDir);
7049
+ const entries = await fs9.readdir(tmpDir);
6592
7050
  if (entries.length === 1) {
6593
- const sub = path9.join(tmpDir, entries[0]);
6594
- const st = await fs8.stat(sub);
7051
+ const sub = path10.join(tmpDir, entries[0]);
7052
+ const st = await fs9.stat(sub);
6595
7053
  if (st.isDirectory()) {
6596
- const innerManifest = path9.join(sub, "manifest.json");
7054
+ const innerManifest = path10.join(sub, "manifest.json");
6597
7055
  try {
6598
- await fs8.access(innerManifest);
7056
+ await fs9.access(innerManifest);
6599
7057
  return sub;
6600
7058
  } catch {
6601
7059
  }
@@ -6604,34 +7062,34 @@ async function unpackGaa(gaaPath) {
6604
7062
  return tmpDir;
6605
7063
  }
6606
7064
  async function packGaa(addonDir, outputPath) {
6607
- const manifestPath = path9.join(addonDir, "manifest.json");
6608
- const manifestRaw = await fs8.readFile(manifestPath, "utf8");
7065
+ const manifestPath = path10.join(addonDir, "manifest.json");
7066
+ const manifestRaw = await fs9.readFile(manifestPath, "utf8");
6609
7067
  const manifest = JSON.parse(manifestRaw);
6610
7068
  validateManifest(manifest);
6611
- const out = outputPath ?? path9.join(process.cwd(), `${manifest.id}.gaa`);
7069
+ const out = outputPath ?? path10.join(process.cwd(), `${manifest.id}.gaa`);
6612
7070
  try {
6613
- await fs8.unlink(out);
7071
+ await fs9.unlink(out);
6614
7072
  } catch {
6615
7073
  }
6616
- const dirName = path9.basename(addonDir);
6617
- const parentDir = path9.dirname(addonDir);
7074
+ const dirName = path10.basename(addonDir);
7075
+ const parentDir = path10.dirname(addonDir);
6618
7076
  await execFileAsync("zip", ["-r", "-q", out, dirName], { cwd: parentDir });
6619
7077
  return out;
6620
7078
  }
6621
7079
  async function installFromDir(addonDir, profileSlug, source = "local") {
6622
- const manifestPath = path9.join(addonDir, "manifest.json");
6623
- const manifestRaw = await fs8.readFile(manifestPath, "utf8");
7080
+ const manifestPath = path10.join(addonDir, "manifest.json");
7081
+ const manifestRaw = await fs9.readFile(manifestPath, "utf8");
6624
7082
  const manifest = JSON.parse(manifestRaw);
6625
7083
  validateManifest(manifest);
6626
7084
  const applied = [];
6627
7085
  const installedFiles = [];
6628
- const filesDir = path9.join(addonDir, "files");
7086
+ const filesDir = path10.join(addonDir, "files");
6629
7087
  try {
6630
- const fileStat = await fs8.stat(filesDir);
7088
+ const fileStat = await fs9.stat(filesDir);
6631
7089
  if (fileStat.isDirectory() && profileSlug) {
6632
7090
  const fileEntries = await walkDir(filesDir);
6633
7091
  for (const relPath of fileEntries) {
6634
- const content = await fs8.readFile(path9.join(filesDir, relPath), "utf8");
7092
+ const content = await fs9.readFile(path10.join(filesDir, relPath), "utf8");
6635
7093
  await writeMd(profileSlug, relPath, content);
6636
7094
  installedFiles.push(relPath);
6637
7095
  }
@@ -6639,9 +7097,9 @@ async function installFromDir(addonDir, profileSlug, source = "local") {
6639
7097
  }
6640
7098
  } catch {
6641
7099
  }
6642
- const patchPath = path9.join(addonDir, "config.patch.json");
7100
+ const patchPath = path10.join(addonDir, "config.patch.json");
6643
7101
  try {
6644
- const patchRaw = await fs8.readFile(patchPath, "utf8");
7102
+ const patchRaw = await fs9.readFile(patchPath, "utf8");
6645
7103
  const patch = JSON.parse(patchRaw);
6646
7104
  if (profileSlug) {
6647
7105
  const cfg = await readConfig(profileSlug);
@@ -6653,11 +7111,11 @@ async function installFromDir(addonDir, profileSlug, source = "local") {
6653
7111
  }
6654
7112
  } catch {
6655
7113
  }
6656
- const codePatchPath = path9.join(addonDir, "code.patch");
7114
+ const codePatchPath = path10.join(addonDir, "code.patch");
6657
7115
  try {
6658
- const patchContent = await fs8.readFile(codePatchPath, "utf8");
7116
+ const patchContent = await fs9.readFile(codePatchPath, "utf8");
6659
7117
  if (patchContent.trim()) {
6660
- const projectRoot = path9.resolve(import.meta.url.replace("file://", ""), "../../../");
7118
+ const projectRoot = path10.resolve(import.meta.url.replace("file://", ""), "../../../");
6661
7119
  try {
6662
7120
  await execFileAsync("git", ["apply", "--check", codePatchPath], { cwd: projectRoot });
6663
7121
  await execFileAsync("git", ["apply", codePatchPath], { cwd: projectRoot });
@@ -6668,25 +7126,25 @@ async function installFromDir(addonDir, profileSlug, source = "local") {
6668
7126
  }
6669
7127
  } catch {
6670
7128
  }
6671
- const themePath = path9.join(addonDir, "theme.css");
7129
+ const themePath = path10.join(addonDir, "theme.css");
6672
7130
  try {
6673
- const css = await fs8.readFile(themePath, "utf8");
7131
+ const css = await fs9.readFile(themePath, "utf8");
6674
7132
  const dir2 = await ensureDir();
6675
- await fs8.writeFile(path9.join(dir2, `theme-${manifest.id}.css`), css, "utf8");
7133
+ await fs9.writeFile(path10.join(dir2, `theme-${manifest.id}.css`), css, "utf8");
6676
7134
  applied.push("\u0442\u0435\u043C\u0430 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430");
6677
7135
  } catch {
6678
7136
  }
6679
7137
  const dir = await ensureDir();
6680
- const addonStorePath = path9.join(dir, manifest.id);
6681
- await fs8.mkdir(addonStorePath, { recursive: true });
6682
- await fs8.copyFile(manifestPath, path9.join(addonStorePath, "manifest.json"));
7138
+ const addonStorePath = path10.join(dir, manifest.id);
7139
+ await fs9.mkdir(addonStorePath, { recursive: true });
7140
+ await fs9.copyFile(manifestPath, path10.join(addonStorePath, "manifest.json"));
6683
7141
  const allFiles = await walkDir(addonDir);
6684
7142
  for (const f of allFiles) {
6685
7143
  if (f === "manifest.json") continue;
6686
- const src = path9.join(addonDir, f);
6687
- const dst = path9.join(addonStorePath, f);
6688
- await fs8.mkdir(path9.dirname(dst), { recursive: true });
6689
- await fs8.copyFile(src, dst);
7144
+ const src = path10.join(addonDir, f);
7145
+ const dst = path10.join(addonStorePath, f);
7146
+ await fs9.mkdir(path10.dirname(dst), { recursive: true });
7147
+ await fs9.copyFile(src, dst);
6690
7148
  }
6691
7149
  const list = await listInstalled();
6692
7150
  const item = {
@@ -6707,7 +7165,7 @@ async function installFromGaa(gaaPath, profileSlug) {
6707
7165
  try {
6708
7166
  return await installFromDir(dir, profileSlug, "file");
6709
7167
  } finally {
6710
- await fs8.rm(dir, { recursive: true, force: true }).catch(() => {
7168
+ await fs9.rm(dir, { recursive: true, force: true }).catch(() => {
6711
7169
  });
6712
7170
  }
6713
7171
  }
@@ -6730,12 +7188,12 @@ async function installFromRegistry(id, registryManifest, profileSlug) {
6730
7188
  const res = await fetch(url, { signal: AbortSignal.timeout(3e4) });
6731
7189
  if (!res.ok) throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u0430\u0434\u0434\u043E\u043D: HTTP ${res.status}`);
6732
7190
  const buf = Buffer.from(await res.arrayBuffer());
6733
- const tmpGaa = path9.join(os3.tmpdir(), `${id}-${Date.now()}.gaa`);
6734
- await fs8.writeFile(tmpGaa, buf);
7191
+ const tmpGaa = path10.join(os3.tmpdir(), `${id}-${Date.now()}.gaa`);
7192
+ await fs9.writeFile(tmpGaa, buf);
6735
7193
  try {
6736
7194
  return await installFromGaa(tmpGaa, profileSlug);
6737
7195
  } finally {
6738
- await fs8.unlink(tmpGaa).catch(() => {
7196
+ await fs9.unlink(tmpGaa).catch(() => {
6739
7197
  });
6740
7198
  }
6741
7199
  }
@@ -6744,11 +7202,11 @@ async function uninstall(id) {
6744
7202
  const next = list.filter((a) => a.manifest.id !== id);
6745
7203
  if (next.length === list.length) return false;
6746
7204
  const dir = addonsDir();
6747
- const addonStore = path9.join(dir, id);
6748
- await fs8.rm(addonStore, { recursive: true, force: true }).catch(() => {
7205
+ const addonStore = path10.join(dir, id);
7206
+ await fs9.rm(addonStore, { recursive: true, force: true }).catch(() => {
6749
7207
  });
6750
- const themePath = path9.join(dir, `theme-${id}.css`);
6751
- await fs8.unlink(themePath).catch(() => {
7208
+ const themePath = path10.join(dir, `theme-${id}.css`);
7209
+ await fs9.unlink(themePath).catch(() => {
6752
7210
  });
6753
7211
  await writeInstalled(next);
6754
7212
  return true;
@@ -6779,11 +7237,11 @@ function validateManifest(m) {
6779
7237
  }
6780
7238
  async function walkDir(dir, prefix = "") {
6781
7239
  const result = [];
6782
- const entries = await fs8.readdir(dir, { withFileTypes: true });
7240
+ const entries = await fs9.readdir(dir, { withFileTypes: true });
6783
7241
  for (const e of entries) {
6784
7242
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
6785
7243
  if (e.isDirectory()) {
6786
- result.push(...await walkDir(path9.join(dir, e.name), rel));
7244
+ result.push(...await walkDir(path10.join(dir, e.name), rel));
6787
7245
  } else {
6788
7246
  result.push(rel);
6789
7247
  }
@@ -6803,16 +7261,16 @@ function deepMerge(target, source) {
6803
7261
  }
6804
7262
  async function getAddonReadme(id) {
6805
7263
  const dir = addonsDir();
6806
- const readmePath = path9.join(dir, id, "README.md");
7264
+ const readmePath = path10.join(dir, id, "README.md");
6807
7265
  try {
6808
- return await fs8.readFile(readmePath, "utf8");
7266
+ return await fs9.readFile(readmePath, "utf8");
6809
7267
  } catch {
6810
7268
  return null;
6811
7269
  }
6812
7270
  }
6813
7271
  async function getAddonFiles(id) {
6814
7272
  const dir = addonsDir();
6815
- const addonDir = path9.join(dir, id);
7273
+ const addonDir = path10.join(dir, id);
6816
7274
  try {
6817
7275
  return await walkDir(addonDir);
6818
7276
  } catch {
@@ -6855,9 +7313,9 @@ var HttpError = class extends Error {
6855
7313
  };
6856
7314
  var Router = class {
6857
7315
  routes = [];
6858
- add(method, path12, handler) {
7316
+ add(method, path13, handler) {
6859
7317
  const paramNames = [];
6860
- const parts = path12.split("/").map((part) => {
7318
+ const parts = path13.split("/").map((part) => {
6861
7319
  if (part.startsWith(":")) {
6862
7320
  paramNames.push(part.slice(1));
6863
7321
  return "([^/]+)";
@@ -6872,20 +7330,20 @@ var Router = class {
6872
7330
  handler
6873
7331
  });
6874
7332
  }
6875
- get(path12, h) {
6876
- this.add("GET", path12, h);
7333
+ get(path13, h) {
7334
+ this.add("GET", path13, h);
6877
7335
  }
6878
- post(path12, h) {
6879
- this.add("POST", path12, h);
7336
+ post(path13, h) {
7337
+ this.add("POST", path13, h);
6880
7338
  }
6881
- put(path12, h) {
6882
- this.add("PUT", path12, h);
7339
+ put(path13, h) {
7340
+ this.add("PUT", path13, h);
6883
7341
  }
6884
- delete(path12, h) {
6885
- this.add("DELETE", path12, h);
7342
+ delete(path13, h) {
7343
+ this.add("DELETE", path13, h);
6886
7344
  }
6887
- patch(path12, h) {
6888
- this.add("PATCH", path12, h);
7345
+ patch(path13, h) {
7346
+ this.add("PATCH", path13, h);
6889
7347
  }
6890
7348
  match(method, pathname) {
6891
7349
  for (const r of this.routes) {
@@ -7377,25 +7835,31 @@ function fallbackBusySchedule(name, age) {
7377
7835
  init_llm();
7378
7836
  init_llm_update();
7379
7837
  init_llm2();
7380
- import { promises as fs6 } from "fs";
7381
- import path7 from "path";
7838
+ import { promises as fs7 } from "fs";
7839
+ import path8 from "path";
7382
7840
  var MEMORY_FILES = [
7383
7841
  "persona.md",
7384
7842
  "speech.md",
7385
7843
  "boundaries.md",
7386
7844
  "communication.md",
7387
7845
  "long-term.md",
7388
- "memory/long-term.md"
7846
+ "memory/long-term.md",
7847
+ "memory/facts.md",
7848
+ "memory/uncertain.md",
7849
+ "relationship/timeline.md",
7850
+ "time/open-loops.md",
7851
+ "time/promises.md"
7389
7852
  ];
7390
7853
  function isAllowedMemoryPath(p) {
7391
7854
  if (!p || typeof p !== "string") return false;
7392
7855
  if (p.includes("..")) return false;
7393
- if (path7.isAbsolute(p)) return false;
7856
+ if (path8.isAbsolute(p)) return false;
7394
7857
  if (p.startsWith("config.json")) return false;
7395
7858
  if (p.startsWith("agenda.json")) return false;
7396
7859
  if (MEMORY_FILES.includes(p)) return true;
7397
7860
  if (/^memory\/daily\/\d{4}-\d{2}-\d{2}\.md$/.test(p)) return true;
7398
7861
  if (/^memory\/episodes\/[\w\-]{1,80}\.md$/.test(p)) return true;
7862
+ if (/^memory\/palace\/[\w\-]{1,80}\/[\w\-]{1,80}\/[\w\-]{1,80}\/[\w\-]{1,120}\.md$/.test(p)) return true;
7399
7863
  if (/^log\/\d{4}-\d{2}-\d{2}\.md$/.test(p)) return true;
7400
7864
  return false;
7401
7865
  }
@@ -7565,20 +8029,40 @@ function registerProfileRoutes(r) {
7565
8029
  const entries = [];
7566
8030
  for (const f of MEMORY_FILES) entries.push({ rel: f });
7567
8031
  try {
7568
- const dailyDir = path7.join(dir, "memory", "daily");
7569
- const list = await fs6.readdir(dailyDir);
8032
+ const dailyDir = path8.join(dir, "memory", "daily");
8033
+ const list = await fs7.readdir(dailyDir);
7570
8034
  for (const f of list) if (/^\d{4}-\d{2}-\d{2}\.md$/.test(f)) entries.push({ rel: `memory/daily/${f}` });
7571
8035
  } catch {
7572
8036
  }
7573
8037
  try {
7574
- const epDir = path7.join(dir, "memory", "episodes");
7575
- const list = await fs6.readdir(epDir);
8038
+ const epDir = path8.join(dir, "memory", "episodes");
8039
+ const list = await fs7.readdir(epDir);
7576
8040
  for (const f of list) if (/^[\w\-]{1,80}\.md$/.test(f)) entries.push({ rel: `memory/episodes/${f}` });
7577
8041
  } catch {
7578
8042
  }
8043
+ try {
8044
+ const palaceDir = path8.join(dir, "memory", "palace");
8045
+ const wings = await fs7.readdir(palaceDir, { withFileTypes: true });
8046
+ for (const wing of wings) {
8047
+ if (!wing.isDirectory() || !/^[\w\-]{1,80}$/.test(wing.name)) continue;
8048
+ const halls = await fs7.readdir(path8.join(palaceDir, wing.name), { withFileTypes: true });
8049
+ for (const hall of halls) {
8050
+ if (!hall.isDirectory() || !/^[\w\-]{1,80}$/.test(hall.name)) continue;
8051
+ const rooms = await fs7.readdir(path8.join(palaceDir, wing.name, hall.name), { withFileTypes: true });
8052
+ for (const room of rooms) {
8053
+ if (!room.isDirectory() || !/^[\w\-]{1,80}$/.test(room.name)) continue;
8054
+ const drawers = await fs7.readdir(path8.join(palaceDir, wing.name, hall.name, room.name));
8055
+ for (const drawer of drawers) {
8056
+ if (/^[\w\-]{1,120}\.md$/.test(drawer)) entries.push({ rel: `memory/palace/${wing.name}/${hall.name}/${room.name}/${drawer}` });
8057
+ }
8058
+ }
8059
+ }
8060
+ }
8061
+ } catch {
8062
+ }
7579
8063
  for (const e of entries) {
7580
8064
  try {
7581
- const stat = await fs6.stat(path7.join(dir, e.rel));
8065
+ const stat = await fs7.stat(path8.join(dir, e.rel));
7582
8066
  items.push({ path: e.rel, size: stat.size, mtime: stat.mtimeMs });
7583
8067
  } catch {
7584
8068
  }
@@ -7947,9 +8431,9 @@ function registerPresetRoutes(r) {
7947
8431
  // src/webui/routes/system.ts
7948
8432
  init_esm_shims();
7949
8433
  init_md();
7950
- import { promises as fs7 } from "fs";
8434
+ import { promises as fs8 } from "fs";
7951
8435
  import { fileURLToPath as fileURLToPath4 } from "url";
7952
- import path8 from "path";
8436
+ import path9 from "path";
7953
8437
  import os2 from "os";
7954
8438
  var cachedVersion = null;
7955
8439
  async function readPackageVersion() {
@@ -7957,15 +8441,15 @@ async function readPackageVersion() {
7957
8441
  const candidates = [];
7958
8442
  try {
7959
8443
  const here = fileURLToPath4(import.meta.url);
7960
- candidates.push(path8.resolve(path8.dirname(here), "..", "package.json"));
7961
- candidates.push(path8.resolve(path8.dirname(here), "..", "..", "package.json"));
7962
- candidates.push(path8.resolve(path8.dirname(here), "..", "..", "..", "package.json"));
8444
+ candidates.push(path9.resolve(path9.dirname(here), "..", "package.json"));
8445
+ candidates.push(path9.resolve(path9.dirname(here), "..", "..", "package.json"));
8446
+ candidates.push(path9.resolve(path9.dirname(here), "..", "..", "..", "package.json"));
7963
8447
  } catch {
7964
8448
  }
7965
- candidates.push(path8.resolve(process.cwd(), "package.json"));
8449
+ candidates.push(path9.resolve(process.cwd(), "package.json"));
7966
8450
  for (const c of candidates) {
7967
8451
  try {
7968
- const raw = await fs7.readFile(c, "utf8");
8452
+ const raw = await fs8.readFile(c, "utf8");
7969
8453
  const parsed = JSON.parse(raw);
7970
8454
  if (parsed.name === "@thesashadev/girl-agent" && parsed.version) {
7971
8455
  cachedVersion = parsed.version;
@@ -8016,8 +8500,8 @@ function registerSystemRoutes(r) {
8016
8500
  // src/webui/routes/addons.ts
8017
8501
  init_esm_shims();
8018
8502
  init_addons();
8019
- import { promises as fs9 } from "fs";
8020
- import path10 from "path";
8503
+ import { promises as fs10 } from "fs";
8504
+ import path11 from "path";
8021
8505
  import os4 from "os";
8022
8506
  function registerAddonRoutes(r) {
8023
8507
  r.get("/api/addons", async () => {
@@ -8044,13 +8528,13 @@ function registerAddonRoutes(r) {
8044
8528
  const data = body;
8045
8529
  if (!data?.gaaBase64) throw new HttpError(400, "gaaBase64 required");
8046
8530
  const buf = Buffer.from(data.gaaBase64, "base64");
8047
- const tmpPath = path10.join(os4.tmpdir(), `upload-${Date.now()}.gaa`);
8048
- await fs9.writeFile(tmpPath, buf);
8531
+ const tmpPath = path11.join(os4.tmpdir(), `upload-${Date.now()}.gaa`);
8532
+ await fs10.writeFile(tmpPath, buf);
8049
8533
  try {
8050
8534
  const result = await installFromGaa(tmpPath, data.profileSlug);
8051
8535
  return { ok: true, installed: result.addon, applied: result.applied };
8052
8536
  } finally {
8053
- await fs9.unlink(tmpPath).catch(() => {
8537
+ await fs10.unlink(tmpPath).catch(() => {
8054
8538
  });
8055
8539
  }
8056
8540
  });
@@ -8062,13 +8546,13 @@ function registerAddonRoutes(r) {
8062
8546
  const res = await fetch(url, { signal: AbortSignal.timeout(3e4) });
8063
8547
  if (!res.ok) throw new HttpError(502, `fetch failed: HTTP ${res.status}`);
8064
8548
  const buf = Buffer.from(await res.arrayBuffer());
8065
- const tmpPath = path10.join(os4.tmpdir(), `url-${Date.now()}.gaa`);
8066
- await fs9.writeFile(tmpPath, buf);
8549
+ const tmpPath = path11.join(os4.tmpdir(), `url-${Date.now()}.gaa`);
8550
+ await fs10.writeFile(tmpPath, buf);
8067
8551
  try {
8068
8552
  const result = await installFromGaa(tmpPath, data.profileSlug);
8069
8553
  return { ok: true, installed: result.addon, applied: result.applied };
8070
8554
  } finally {
8071
- await fs9.unlink(tmpPath).catch(() => {
8555
+ await fs10.unlink(tmpPath).catch(() => {
8072
8556
  });
8073
8557
  }
8074
8558
  } else {
@@ -8115,7 +8599,561 @@ init_llm();
8115
8599
  init_stages();
8116
8600
  init_communication();
8117
8601
  init_llm2();
8602
+ init_realism();
8118
8603
  init_runtime_bus();
8604
+
8605
+ // src/webui/assistant-knowledge.ts
8606
+ init_esm_shims();
8607
+ init_communication();
8608
+ init_llm2();
8609
+ init_mcp();
8610
+ init_stages();
8611
+ var CORE_KNOWLEDGE_BASE = [
8612
+ {
8613
+ category: "overview",
8614
+ subcategory: "product",
8615
+ title: "\u0427\u0442\u043E \u0442\u0430\u043A\u043E\u0435 girl-agent",
8616
+ keywords: ["\u043F\u0440\u043E\u0435\u043A\u0442", "girl-agent", "\u0431\u043E\u0442", "\u0447\u0442\u043E \u044D\u0442\u043E", "\u043A\u043E\u043D\u0446\u0435\u043F\u0446\u0438\u044F", "\u0430\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430", "\u0438\u0434\u0435\u044F"],
8617
+ body: "girl-agent \u2014 \u0434\u0432\u0438\u0436\u043E\u043A Telegram-\u043F\u0435\u0440\u0441\u043E\u043D\u044B, \u0430 \u043D\u0435 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0447\u0430\u0442-\u0431\u043E\u0442. \u041E\u043D \u0441\u0438\u043C\u0443\u043B\u0438\u0440\u0443\u0435\u0442 \u0436\u0438\u0432\u043E\u0435 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u0434\u0435\u0432\u0443\u0448\u043A\u0438: \u043E\u043D\u043B\u0430\u0439\u043D/\u043E\u0444\u043B\u0430\u0439\u043D, \u0441\u043E\u043D, \u0437\u0430\u043D\u044F\u0442\u043E\u0441\u0442\u044C, \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D\u0438\u0435, \u043F\u0430\u043C\u044F\u0442\u044C, \u0441\u0442\u0430\u0434\u0438\u0438 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439, \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u044B, \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0438, \u0440\u0435\u0430\u043A\u0446\u0438\u0438, \u0441\u0442\u0438\u043A\u0435\u0440\u044B, \u043E\u043F\u0435\u0447\u0430\u0442\u043A\u0438 \u0438 \u043F\u0440\u043E\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F. README \u043F\u0440\u044F\u043C\u043E \u043F\u043E\u0434\u0447\u0451\u0440\u043A\u0438\u0432\u0430\u0435\u0442: \u043E\u043D\u0430 \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043D\u0430 \u043A\u0430\u0436\u0434\u043E\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435, \u0438\u043D\u043E\u0433\u0434\u0430 \u0447\u0438\u0442\u0430\u0435\u0442 \u0438 \u043C\u043E\u043B\u0447\u0438\u0442 \u2014 \u044D\u0442\u043E \u0437\u0430\u0434\u0443\u043C\u0430\u043D\u043E."
8618
+ },
8619
+ {
8620
+ category: "overview",
8621
+ subcategory: "layers",
8622
+ title: "\u0421\u043B\u043E\u0438\u0441\u0442\u0430\u044F \u0430\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430",
8623
+ keywords: ["\u0441\u043B\u043E\u0438", "\u0430\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430", "runtime", "prompt", "behavior", "presence", "memory"],
8624
+ body: "\u041F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0438\u0437 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u0438\u0445 \u0441\u043B\u043E\u0451\u0432: Telegram adapter \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u0441\u043E\u0431\u044B\u0442\u0438\u044F; Runtime \u043E\u0440\u043A\u0435\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 state; presence \u0440\u0435\u0448\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C; behavior-tick \u0432\u044B\u0431\u0438\u0440\u0430\u0435\u0442 intent/delay/reaction; prompt \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442 persona/speech/boundaries/relationship/memory; LLM \u043F\u0438\u0448\u0435\u0442 \u0442\u0435\u043A\u0441\u0442; storage \u0444\u0438\u043A\u0441\u0438\u0440\u0443\u0435\u0442 \u043B\u043E\u0433\u0438, score, memory, agenda. \u041F\u043E\u044D\u0442\u043E\u043C\u0443 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u043E\u0431\u044A\u044F\u0441\u043D\u044F\u0442\u044C \u043E\u0434\u043D\u0438\u043C system prompt \u2014 \u043F\u043E\u0447\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u043D\u0430\u0434\u043E \u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0441\u043B\u043E\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0441\u0438\u043C\u043F\u0442\u043E\u043C."
8625
+ },
8626
+ {
8627
+ category: "overview",
8628
+ subcategory: "tech-stack",
8629
+ title: "\u0422\u0435\u0445\u043D\u043E\u043B\u043E\u0433\u0438\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430",
8630
+ keywords: ["\u0441\u0442\u0435\u043A", "typescript", "node", "react", "vite", "grammy", "gramjs", "tsup", "rust", "desktop"],
8631
+ body: "Runtime: Node.js >=20, TypeScript strict, ESM. Build: tsup \u0432 dist/cli.js. WebUI: React + Vite. Telegram: grammY \u0434\u043B\u044F bot mode \u0438 GramJS/telegram \u0434\u043B\u044F userbot. LLM: OpenAI-compatible \u0438 Anthropic SDK. Desktop: Rust/iced \u0432 desktop-rs. \u0418\u043C\u043F\u043E\u0440\u0442\u044B TypeScript \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0442 .js extension."
8632
+ },
8633
+ {
8634
+ category: "overview",
8635
+ subcategory: "project-structure",
8636
+ title: "\u041A\u0430\u0440\u0442\u0430 \u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0438\u0439",
8637
+ keywords: ["\u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0438\u0438", "\u0444\u0430\u0439\u043B\u044B", "\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430", "src", "engine", "webui", "telegram", "storage"],
8638
+ body: "src/engine \u2014 \u044F\u0434\u0440\u043E \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u044F: runtime, presence, behavior-tick, prompt, memory-palace, conflict, agenda, daily-life. src/telegram \u2014 bot/userbot adapters. src/llm \u2014 \u043A\u043B\u0438\u0435\u043D\u0442\u044B \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u0432. src/storage/md.ts \u2014 \u0444\u0430\u0439\u043B\u043E\u0432\u043E\u0435 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439. src/webui \u2014 HTTP API, runtime bus, routes. webui/src \u2014 React \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B. src/presets \u2014 stages, llm, communication, mcp."
8639
+ },
8640
+ {
8641
+ category: "overview",
8642
+ subcategory: "commands",
8643
+ title: "\u041A\u043E\u043C\u0430\u043D\u0434\u044B \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u043A\u0438 \u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430",
8644
+ keywords: ["\u043A\u043E\u043C\u0430\u043D\u0434\u044B", "npm", "build", "typecheck", "dev", "start", "server", "update", "addon"],
8645
+ body: "\u041E\u0441\u043D\u043E\u0432\u043D\u044B\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u044B: npm install, npm run dev, npm run build, npm run typecheck, npm run start. CLI: npx girl-agent \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442 WebUI; --profile \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442 \u043F\u0440\u043E\u0444\u0438\u043B\u044C; server --print-config/--config/--headless \u0434\u043B\u044F \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u0432; update \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442 data-\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438; addon init/pack \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0441 .gaa \u0430\u0434\u0434\u043E\u043D\u0430\u043C\u0438."
8646
+ },
8647
+ {
8648
+ category: "storage",
8649
+ subcategory: "data-root",
8650
+ title: "\u0413\u0434\u0435 \u043B\u0435\u0436\u0430\u0442 \u0434\u0430\u043D\u043D\u044B\u0435",
8651
+ keywords: ["data", "GIRL_AGENT_DATA", "\u043F\u0430\u043F\u043A\u0430", "\u043F\u0440\u043E\u0444\u0438\u043B\u0438", "windows", "macos", "linux", "\u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435"],
8652
+ body: "\u041A\u043E\u0440\u0435\u043D\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439 \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 GIRL_AGENT_DATA, \u0438\u043D\u0430\u0447\u0435 \u0432 \u0438\u0441\u0445\u043E\u0434\u043D\u0438\u043A\u0430\u0445 \u044D\u0442\u043E ./data, \u0432 npm/global \u0437\u0430\u043F\u0443\u0441\u043A\u0435 \u2014 XDG data dir, \u043D\u0430 Windows %APPDATA%/girl-agent/data, \u043D\u0430 macOS ~/Library/Application Support/girl-agent/data. \u041A\u0430\u0436\u0434\u044B\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0436\u0438\u0432\u0451\u0442 \u0432 data/<slug>/."
8653
+ },
8654
+ {
8655
+ category: "storage",
8656
+ subcategory: "profile-files",
8657
+ title: "\u0424\u0430\u0439\u043B\u044B \u043F\u0440\u043E\u0444\u0438\u043B\u044F",
8658
+ keywords: ["config.json", "persona.md", "speech.md", "boundaries.md", "communication.md", "relationship.md", "agenda.json"],
8659
+ body: "config.json \u0445\u0440\u0430\u043D\u0438\u0442 ProfileConfig. persona.md \u2014 \u043B\u0438\u0447\u043D\u043E\u0441\u0442\u044C, speech.md \u2014 \u0440\u0435\u0447\u044C, boundaries.md \u2014 \u0433\u0440\u0430\u043D\u0438\u0446\u044B, communication.md \u2014 \u0441\u0442\u0438\u043B\u044C \u043E\u0431\u0449\u0435\u043D\u0438\u044F. relationship.md \u0445\u0440\u0430\u043D\u0438\u0442 stage \u0438 score. agenda.json \u2014 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u043F\u0440\u043E\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u043F\u0438\u043D\u0433\u0438. conflict.json \u2014 \u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0438 coldUntil."
8660
+ },
8661
+ {
8662
+ category: "storage",
8663
+ subcategory: "memory-files",
8664
+ title: "\u0424\u0430\u0439\u043B\u044B \u043F\u0430\u043C\u044F\u0442\u0438",
8665
+ keywords: ["memory", "long-term", "facts", "uncertain", "timeline", "promises", "open-loops"],
8666
+ body: "\u0413\u043B\u0430\u0432\u043D\u044B\u0435 memory-\u0444\u0430\u0439\u043B\u044B: memory/long-term.md, memory/facts.md, memory/uncertain.md, relationship/timeline.md, time/open-loops.md, time/promises.md. \u0415\u0441\u0442\u044C legacy long-term.md. MemoryPage \u0438 assistant \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u044E\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 whitelist \u043F\u0443\u0442\u0435\u0439, \u043F\u043B\u044E\u0441 memory/daily/YYYY-MM-DD.md, memory/episodes/*.md, memory/palace/*."
8667
+ },
8668
+ {
8669
+ category: "storage",
8670
+ subcategory: "logs-and-days",
8671
+ title: "\u041B\u043E\u0433\u0438 \u0438 \u0434\u043D\u0435\u0432\u043D\u044B\u0435 summary",
8672
+ keywords: ["log", "daily", "summary", "session", "\u0434\u043D\u0435\u0432\u043D\u0438\u043A", "\u0434\u0430\u0442\u0430", "05:00"],
8673
+ body: "log/YYYY-MM-DD.md \u2014 \u0441\u0435\u0441\u0441\u0438\u043E\u043D\u043D\u044B\u0435 \u043B\u043E\u0433\u0438. sessionDate \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u0434\u0435\u043D\u044C \u043F\u043E timezone \u043F\u0440\u043E\u0444\u0438\u043B\u044F; \u0434\u043E 05:00 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u043E\u0442\u043D\u043E\u0441\u044F\u0442\u0441\u044F \u043A \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u043C\u0443 \u0434\u043D\u044E. memory/daily/YYYY-MM-DD.md \u2014 \u0434\u043D\u0435\u0432\u043D\u044B\u0435 summary \u0434\u043B\u044F \u0434\u043E\u043B\u0433\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430 \u0438 \u043F\u043E\u0438\u0441\u043A\u0430 \u043F\u043E \u043F\u0440\u043E\u0448\u043B\u044B\u043C \u0434\u043D\u044F\u043C."
8674
+ },
8675
+ {
8676
+ category: "config",
8677
+ subcategory: "profile-config",
8678
+ title: "ProfileConfig",
8679
+ keywords: ["ProfileConfig", "config", "slug", "name", "age", "nationality", "tz", "mode"],
8680
+ body: "ProfileConfig \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u0442 slug, name, age, nationality RU/UA, timezone, mode bot/userbot, stage, llm, telegram, mcp, ownerId, privacy, sleepFrom/sleepTo, nightWakeChance, ignoreTendency, vibe, communication, personaNotes \u0438 busySchedule. \u041F\u0440\u0438 \u0447\u0442\u0435\u043D\u0438\u0438 storage \u043D\u043E\u0440\u043C\u0430\u043B\u0438\u0437\u0443\u0435\u0442 ownerId, communication \u0438 ignoreTendency."
8681
+ },
8682
+ {
8683
+ category: "config",
8684
+ subcategory: "sleep-and-schedule",
8685
+ title: "\u0421\u043E\u043D \u0438 \u0437\u0430\u043D\u044F\u0442\u043E\u0441\u0442\u044C",
8686
+ keywords: ["sleepFrom", "sleepTo", "nightWakeChance", "busySchedule", "\u0441\u043E\u043D", "\u0440\u0430\u0441\u043F\u0438\u0441\u0430\u043D\u0438\u0435", "\u0437\u0430\u043D\u044F\u0442\u043E\u0441\u0442\u044C"],
8687
+ body: "sleepFrom/sleepTo \u2014 \u0447\u0430\u0441\u044B \u0441\u043D\u0430 0..23, \u043C\u043E\u0433\u0443\u0442 \u043F\u0435\u0440\u0435\u0441\u0435\u043A\u0430\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0447\u044C. nightWakeChance \u2014 \u0448\u0430\u043D\u0441 \u043F\u0440\u043E\u0441\u043D\u0443\u0442\u044C\u0441\u044F \u043D\u043E\u0447\u044C\u044E \u0431\u0435\u0437 :wake. busySchedule \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 label, days, from/to \u0438 checkAfterMin; daily-life \u0438 presence \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0442 \u0435\u0433\u043E, \u0447\u0442\u043E\u0431\u044B \u043E\u0431\u044A\u044F\u0441\u043D\u044F\u0442\u044C \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0438 \u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C \u0442\u0435\u043B\u0435\u0444\u043E\u043D\u0430."
8688
+ },
8689
+ {
8690
+ category: "telegram",
8691
+ subcategory: "bot-mode",
8692
+ title: "Bot mode",
8693
+ keywords: ["bot", "grammy", "Bot API", "\u0442\u043E\u043A\u0435\u043D", "message_reaction", "\u0431\u043E\u0442"],
8694
+ body: "bot mode \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442 grammY \u0438 telegram.botToken. \u041F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 message \u0438 message_reaction, \u0443\u043C\u0435\u0435\u0442 sendMessage, typing action, setMessageReaction, editMessageText \u0438 sendSticker. \u041F\u0440\u043E\u0449\u0435 \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0435, \u043D\u043E \u0432\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043A\u0430\u043A \u0431\u043E\u0442 \u0438 \u0438\u043C\u0435\u0435\u0442 \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F Bot API."
8695
+ },
8696
+ {
8697
+ category: "telegram",
8698
+ subcategory: "userbot-mode",
8699
+ title: "Userbot mode",
8700
+ keywords: ["userbot", "gramjs", "mtproto", "apiId", "apiHash", "sessionString", "\u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0439 \u0430\u043A\u043A\u0430\u0443\u043D\u0442"],
8701
+ body: "userbot mode \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442 GramJS/MTProto \u043A\u0430\u043A \u043E\u0431\u044B\u0447\u043D\u044B\u0439 Telegram \u0430\u043A\u043A\u0430\u0443\u043D\u0442. \u041D\u0443\u0436\u043D\u044B apiId/apiHash \u0438 sessionString, \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044B\u0439 \u0447\u0435\u0440\u0435\u0437 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044E. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 readHistory, typing, reactions, stickers, block/unblock/reportSpam \u0438 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0443 \u0443\u0434\u0430\u043B\u0451\u043D\u043D\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 raw updates."
8702
+ },
8703
+ {
8704
+ category: "telegram",
8705
+ subcategory: "wss-and-proxy",
8706
+ title: "WSS \u0438 proxy",
8707
+ keywords: ["wss", "useWSS", "proxy", "socks", "\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438", "443"],
8708
+ body: "telegram.useWSS \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E true \u0438 \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u0442 WebSocket \u0447\u0435\u0440\u0435\u0437 443 \u0432\u043C\u0435\u0441\u0442\u043E TCP 80 \u2014 \u044D\u0442\u043E \u043F\u043E\u043C\u043E\u0433\u0430\u0435\u0442 \u043F\u0440\u0438 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0430\u0445 Telegram. \u0414\u043B\u044F userbot \u043C\u043E\u0436\u043D\u043E \u0437\u0430\u0434\u0430\u0442\u044C SOCKS proxy \u0432 config \u0438\u043B\u0438 \u0447\u0435\u0440\u0435\u0437 GIRL_AGENT_TG_PROXY."
8709
+ },
8710
+ {
8711
+ category: "telegram",
8712
+ subcategory: "privacy",
8713
+ title: "Privacy \u0438 owner",
8714
+ keywords: ["privacy", "owner", "ownerId", "allow-strangers", "strangers", "\u0447\u0443\u0436\u0438\u0435", "primary"],
8715
+ body: "privacy=owner-only \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E ownerId/primary owner. allow-strangers \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0441\u0442\u043E\u0440\u043E\u043D\u043D\u0438\u0435 \u043B\u0438\u0447\u043D\u044B\u0435 \u0447\u0430\u0442\u044B, \u043D\u043E \u0441 relationshipScope=acquaintance: \u0431\u0435\u0437 \u043F\u0430\u043C\u044F\u0442\u0438 \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0433\u043E \u043F\u0430\u0440\u043D\u044F, \u0431\u0435\u0437 \u0440\u043E\u043C\u0430\u043D\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0439 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u0438 \u0441 \u0433\u0440\u0430\u043D\u0438\u0446\u0430\u043C\u0438, \u0435\u0441\u043B\u0438 \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 relationship \u0443\u0436\u0435 committed."
8716
+ },
8717
+ {
8718
+ category: "runtime",
8719
+ subcategory: "runtime-bus",
8720
+ title: "RuntimeBus \u0432 WebUI",
8721
+ keywords: ["runtimebus", "runtime", "start", "stop", "pause", "resume", "restart", "logs"],
8722
+ body: "RuntimeBus \u0434\u0435\u0440\u0436\u0438\u0442 Runtime \u043D\u0430 \u043A\u0430\u0436\u0434\u044B\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C, \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F running/paused/stopped/error \u0438 ring-buffer \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0445 500 \u0441\u043E\u0431\u044B\u0442\u0438\u0439. WebUI \u0447\u0435\u0440\u0435\u0437 \u043D\u0435\u0433\u043E \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442/\u043E\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442 \u043F\u0440\u043E\u0444\u0438\u043B\u0438, \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 status \u0438 recentLogs, \u0430 WebSocket \u0440\u0430\u0437\u0434\u0430\u0451\u0442 \u0441\u043E\u0431\u044B\u0442\u0438\u044F UI."
8723
+ },
8724
+ {
8725
+ category: "runtime",
8726
+ subcategory: "message-flow",
8727
+ title: "\u041F\u0443\u0442\u044C \u0432\u0445\u043E\u0434\u044F\u0449\u0435\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F",
8728
+ keywords: ["handleIncoming", "incoming", "message flow", "\u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435", "\u043E\u0442\u0432\u0435\u0442"],
8729
+ body: "Telegram adapter \u0441\u043E\u0437\u0434\u0430\u0451\u0442 IncomingMessage. Runtime \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u043E\u0441\u0442\u044C/owner, media, deletion/reaction, \u0438\u0441\u0442\u043E\u0440\u0438\u044E, presence, conflict, active dialog \u0438 behavior-tick. \u0415\u0441\u043B\u0438 shouldReply=false \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0435\u0442 ignored/read. \u0415\u0441\u043B\u0438 reply \u2014 scheduleReply \u0441 delay, \u043F\u043E\u0442\u043E\u043C generateAndSend \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442 prompt, \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442 LLM, \u0441\u0430\u043D\u0438\u0442\u0430\u0439\u0437\u0438\u0442 \u043E\u0442\u0432\u0435\u0442, \u0440\u0435\u0436\u0435\u0442 \u043D\u0430 bubbles \u0438 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u0442."
8730
+ },
8731
+ {
8732
+ category: "runtime",
8733
+ subcategory: "behavior-tick",
8734
+ title: "Behavior tick",
8735
+ keywords: ["behavior", "intent", "reply", "ignore", "short", "left-on-read", "reaction-only", "delay", "moodDelta"],
8736
+ body: "behavior-tick \u2014 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0439 decision layer. \u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 JSON: intent, shouldReply, shouldRead, delaySec, bubbles, typing, reaction, reactionTargetMessageId, ignoreReason \u0438 moodDelta. \u041E\u043D \u0443\u0447\u0438\u0442\u044B\u0432\u0430\u0435\u0442 stage defaults, score, ignoreTendency, presence, conflict, active dialog \u0438 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F."
8737
+ },
8738
+ {
8739
+ category: "runtime",
8740
+ subcategory: "presence",
8741
+ title: "Presence simulation",
8742
+ keywords: ["presence", "online", "offline", "phone-attached", "burst-checker", "rare-checker", "evening-only", "night"],
8743
+ body: "PresenceProfile \u0434\u0435\u0442\u0435\u0440\u043C\u0438\u043D\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E \u0432\u044B\u0431\u0438\u0440\u0430\u0435\u0442 pattern: phone-attached, burst-checker, rare-checker, evening-only \u0438\u043B\u0438 phone-attached-night. \u041E\u043D \u0437\u0430\u0434\u0430\u0451\u0442 checkEveryMin, onlineWindowMin, offlineReplyChance \u0438 nightWakeChance. Communication notifications \u0438 stage \u043C\u043E\u0433\u0443\u0442 \u0443\u0441\u043A\u043E\u0440\u044F\u0442\u044C/\u0437\u0430\u043C\u0435\u0434\u043B\u044F\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C."
8744
+ },
8745
+ {
8746
+ category: "runtime",
8747
+ subcategory: "ignore-tendency",
8748
+ title: "ignoreTendency",
8749
+ keywords: ["ignoreTendency", "\u0438\u0433\u043D\u043E\u0440", "\u043C\u043E\u043B\u0447\u0438\u0442", "\u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442", "read", "left-on-read"],
8750
+ body: "ignoreTendency 0..100 \u2014 \u0445\u0430\u0440\u0430\u043A\u0442\u0435\u0440\u043D\u044B\u0439 \u0432\u0435\u0441, \u0430 \u043D\u0435 \u043F\u0440\u044F\u043C\u043E\u0439 \u043F\u0440\u043E\u0446\u0435\u043D\u0442. 0 \u043F\u043E\u0447\u0442\u0438 \u043D\u0435 \u0438\u0433\u043D\u043E\u0440\u0438\u0442 \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D\u044B, 35 \u0434\u0435\u0444\u043E\u043B\u0442, 70+ \u0441\u0443\u0445\u0430\u044F \u0438 \u0447\u0430\u0441\u0442\u043E \u043F\u0440\u043E\u043F\u0430\u0434\u0430\u0435\u0442. \u0421\u043E\u043D, busy, \u0441\u0442\u0430\u0434\u0438\u044F, \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0438 score \u0441\u0438\u043B\u044C\u043D\u0435\u0435. \u041F\u0440\u0438 \u0436\u0430\u043B\u043E\u0431\u0435 '\u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442' \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0439 runtime state, recent logs, sleep/busy/conflict/stage/score."
8751
+ },
8752
+ {
8753
+ category: "runtime",
8754
+ subcategory: "active-dialog",
8755
+ title: "\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433",
8756
+ keywords: ["\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433", "activeDialog", "\u0431\u0443\u0440\u0441\u0442", "\u0431\u044B\u0441\u0442\u0440\u044B\u0439 \u043E\u0442\u0432\u0435\u0442", "\u043D\u0435 \u043F\u0440\u043E\u043F\u0430\u0434\u0430\u0435\u0442"],
8757
+ body: "\u0415\u0441\u043B\u0438 \u043E\u043D\u0430 \u0443\u0436\u0435 \u043D\u0435\u0434\u0430\u0432\u043D\u043E \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430, \u0430 \u043E\u043D \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u0432 \u0442\u0435\u0447\u0435\u043D\u0438\u0435 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u0438\u0445 \u043C\u0438\u043D\u0443\u0442, Runtime \u043F\u043E\u043C\u0435\u0447\u0430\u0435\u0442 activeDialog. Behavior-layer \u0434\u043E\u043B\u0436\u0435\u043D \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0443 \u0438 \u043D\u0435 \u0443\u0445\u043E\u0434\u0438\u0442\u044C \u0432 \u0441\u043B\u0443\u0447\u0430\u0439\u043D\u044B\u0439 \u0438\u0433\u043D\u043E\u0440 \u0431\u0435\u0437 \u0432\u0435\u0441\u043A\u043E\u0439 \u043F\u0440\u0438\u0447\u0438\u043D\u044B. \u042D\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0442 \u0434\u0438\u0430\u043B\u043E\u0433 \u043F\u043E\u0445\u043E\u0436\u0438\u043C \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u0443\u044E \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0443, \u0430 \u043D\u0435 \u043D\u0430 \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u044B\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u044B."
8758
+ },
8759
+ {
8760
+ category: "runtime",
8761
+ subcategory: "bubbles",
8762
+ title: "\u041F\u0443\u0437\u044B\u0440\u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
8763
+ keywords: ["bubbles", "\u043F\u0443\u0437\u044B\u0440\u044C", "split", "---", "\u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F", "\u0434\u0440\u043E\u0431\u0438\u0442\u044C"],
8764
+ body: "LLM \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 \u0443\u043A\u0430\u0437\u0430\u043D\u0438\u0435: \u0435\u0441\u043B\u0438 bubbles > 1, \u0440\u0430\u0437\u0434\u0435\u043B\u044F\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0441\u0442\u0440\u043E\u043A\u043E\u0439 '---'. smartSplitBubbles \u0438 dedupeBubbles \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044E\u0442 \u043E\u0442\u0432\u0435\u0442 \u0432 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0435 TG-\u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F. \u041F\u0435\u0440\u0435\u043D\u043E\u0441\u044B \u0441\u0442\u0440\u043E\u043A \u0431\u0435\u0437 '---' \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u044B, \u043F\u043E\u0442\u043E\u043C\u0443 \u0447\u0442\u043E \u0432 Telegram \u044D\u0442\u043E \u043E\u0434\u043D\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0441\u0442\u043E\u043B\u0431\u0438\u043A\u043E\u043C \u0438 \u043F\u0430\u043B\u0438\u0442 \u0418\u0418."
8765
+ },
8766
+ {
8767
+ category: "runtime",
8768
+ subcategory: "typing-and-delays",
8769
+ title: "Typing \u0438 \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0438",
8770
+ keywords: ["typing", "delay", "\u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0430", "\u043F\u0435\u0447\u0430\u0442\u0430\u0435\u0442", "scheduleReply", "sendBubbles"],
8771
+ body: "delaySec \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0438\u0437 behavior-tick \u0438 \u043C\u043E\u0436\u0435\u0442 \u0443\u0432\u0435\u043B\u0438\u0447\u0438\u0432\u0430\u0442\u044C\u0441\u044F \u0438\u0437-\u0437\u0430 offline/busy. scheduleReply \u0441\u0442\u0430\u0432\u0438\u0442 \u0442\u0430\u0439\u043C\u0435\u0440. sendBubbles \u0438\u043C\u0438\u0442\u0438\u0440\u0443\u0435\u0442 typing: \u043F\u0435\u0440\u0435\u0434 \u043F\u0435\u0440\u0432\u044B\u043C \u043F\u0443\u0437\u044B\u0440\u0451\u043C \u043A\u043E\u0440\u043E\u0442\u043A\u0430\u044F \u043F\u0430\u0443\u0437\u0430, \u043C\u0435\u0436\u0434\u0443 \u043F\u0443\u0437\u044B\u0440\u044F\u043C\u0438 \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0430 \u043F\u043E \u0434\u043B\u0438\u043D\u0435 \u0442\u0435\u043A\u0441\u0442\u0430 \u0438 WPM. \u0415\u0441\u043B\u0438 userbot \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D, \u043F\u0435\u0440\u0435\u0434 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u043E\u0439 \u043C\u043E\u0436\u0435\u0442 readHistory."
8772
+ },
8773
+ {
8774
+ category: "runtime",
8775
+ subcategory: "anti-ai",
8776
+ title: "Anti-AI \u0438 \u0441\u0430\u043D\u0438\u0442\u0430\u0439\u0437\u0438\u043D\u0433",
8777
+ keywords: ["anti-ai", "sanitize", "markdown", "jailbreak", "system prompt", "chatgpt", "\u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430"],
8778
+ body: "ANTI_AI_RULES \u0437\u0430\u043F\u0440\u0435\u0449\u0430\u044E\u0442 ChatGPT-\u043F\u043E\u0432\u0430\u0434\u043A\u0438, markdown \u0438 \u043C\u0435\u0442\u0430-\u0444\u0440\u0430\u0437\u044B. security.ts \u0432\u044B\u0447\u0438\u0449\u0430\u0435\u0442 code fences, action leak narration, system/developer labels, CJK \u043C\u0443\u0441\u043E\u0440 \u0438 technical error replies. Jailbreak-\u043F\u043E\u0434\u043E\u0431\u043D\u044B\u0435 \u0434\u043B\u0438\u043D\u043D\u044B\u0435 \u043E\u0442\u0432\u0435\u0442\u044B \u043E\u0442\u0431\u0440\u0430\u0441\u044B\u0432\u0430\u044E\u0442\u0441\u044F; \u043F\u0440\u0438 \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u043E\u0448\u0438\u0431\u043A\u0430\u0445 Runtime \u0443\u0445\u043E\u0434\u0438\u0442 \u0432 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 fallback/ignored."
8779
+ },
8780
+ {
8781
+ category: "runtime",
8782
+ subcategory: "media",
8783
+ title: "\u041C\u0435\u0434\u0438\u0430",
8784
+ keywords: ["media", "photo", "voice", "video", "video_note", "sticker", "document", "\u0444\u043E\u0442\u043E", "\u0433\u043E\u043B\u043E\u0441\u043E\u0432\u043E\u0435"],
8785
+ body: "IncomingMedia \u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442 \u0444\u043E\u0442\u043E, \u0432\u0438\u0434\u0435\u043E, \u0433\u043E\u043B\u043E\u0441\u043E\u0432\u043E\u0435, \u043A\u0440\u0443\u0436\u043E\u043A, \u0441\u0442\u0438\u043A\u0435\u0440 \u0438\u043B\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442. \u0424\u043E\u0442\u043E \u0441 base64 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044F LLM \u043A\u0430\u043A image part, \u0435\u0441\u043B\u0438 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442. \u041D\u0430 \u0433\u043E\u043B\u043E\u0441\u043E\u0432\u044B\u0435 \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043E\u0432\u043A\u0438 \u043F\u0435\u0440\u0441\u043E\u043D\u0430 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0442\u0435\u043A\u0441\u0442\u043E\u043C. \u041D\u0430 \u0438\u0441\u0445\u043E\u0434\u044F\u0449\u0438\u0435 \u043F\u0440\u043E\u0441\u044C\u0431\u044B \u0444\u043E\u0442\u043E/\u0432\u0438\u0434\u0435\u043E/voice Runtime \u0447\u0430\u0449\u0435 \u0434\u0430\u0451\u0442 \u043E\u0442\u043A\u0430\u0437 \u0432\u0440\u043E\u0434\u0435 '\u043D\u0435 \u0445\u043E\u0447\u0443 \u0444\u043E\u0442\u043A\u0430\u0442\u044C\u0441\u044F \u0449\u0430'."
8786
+ },
8787
+ {
8788
+ category: "runtime",
8789
+ subcategory: "deleted-messages",
8790
+ title: "\u0423\u0434\u0430\u043B\u0451\u043D\u043D\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F",
8791
+ keywords: ["\u0443\u0434\u0430\u043B\u0438\u043B", "deleted", "delete", "saw-and-read", "saw-not-read", "missed"],
8792
+ body: "deletion-handler \u043A\u043B\u0430\u0441\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u0435: saw-and-read \u2014 \u043E\u043D\u0430 \u0443\u0436\u0435 \u043F\u0440\u043E\u0447\u043B\u0430 \u0438 \u043C\u043E\u0436\u0435\u0442 \u0441\u043A\u0430\u0437\u0430\u0442\u044C '\u043F\u043E\u0437\u0434\u043D\u043E, \u044F \u0432\u0438\u0434\u0435\u043B\u0430'; saw-not-read \u2014 \u0432\u0438\u0434\u0435\u043B\u0430 \u0444\u0430\u043A\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F, \u043D\u043E \u043D\u0435 \u043F\u0440\u043E\u0447\u043B\u0430 \u0438 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C; missed \u2014 \u043D\u0435 \u0437\u0430\u043C\u0435\u0442\u0438\u043B\u0430 \u0438 \u043C\u043E\u043B\u0447\u0438\u0442. Userbot \u043A\u044D\u0448\u0438\u0440\u0443\u0435\u0442 \u0432\u0445\u043E\u0434\u044F\u0449\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0442\u0435\u043A\u0441\u0442 \u043F\u043E raw update."
8793
+ },
8794
+ {
8795
+ category: "runtime",
8796
+ subcategory: "emoji-reactions",
8797
+ title: "\u0420\u0435\u0430\u043A\u0446\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F",
8798
+ keywords: ["emoji", "reaction", "\u0440\u0435\u0430\u043A\u0446\u0438\u044F", "\u0442\u043E\u043A\u0441\u0438\u0447\u043D\u0430\u044F", "positive", "react-back", "silent-mood"],
8799
+ body: "emoji-reaction-handler \u0434\u0435\u043B\u0438\u0442 \u0440\u0435\u0430\u043A\u0446\u0438\u0438 \u043D\u0430 toxic, positive, funny, sad, neutral. \u0422\u043E\u043A\u0441\u0438\u0447\u043D\u044B\u0435 \u044D\u043C\u043E\u0434\u0437\u0438 \u043D\u0430 \u0435\u0451 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044B\u0447\u043D\u043E \u043C\u043E\u043B\u0447\u0430 \u0443\u0445\u0443\u0434\u0448\u0430\u044E\u0442 mood; \u043D\u043E \u0435\u0441\u043B\u0438 \u044D\u043C\u043E\u0434\u0437\u0438 \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0441\u044F \u043A \u0432\u043D\u0435\u0448\u043D\u0435\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u0438\u0437 \u0435\u0451 \u0442\u0435\u043A\u0441\u0442\u0430, annoyance \u043D\u0435 \u0440\u0430\u0441\u0442\u0451\u0442. Positive \u0438\u043D\u043E\u0433\u0434\u0430 react-back/\u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u0442\u0435\u043A\u0441\u0442, \u043D\u043E \u0447\u0430\u0449\u0435 \u043C\u043E\u043B\u0447\u0430. \u0421\u043D\u044F\u0442\u0438\u0435 \u0440\u0435\u0430\u043A\u0446\u0438\u0438 \u043E\u0431\u044B\u0447\u043D\u043E \u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044F."
8800
+ },
8801
+ {
8802
+ category: "runtime",
8803
+ subcategory: "typos",
8804
+ title: "\u0420\u0435\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u044B\u0435 \u043E\u043F\u0435\u0447\u0430\u0442\u043A\u0438",
8805
+ keywords: ["typos", "\u043E\u043F\u0435\u0447\u0430\u0442\u043A\u0438", "\u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430", "qwerty", "\u0439\u0446\u0443\u043A\u0435\u043D", "\u043E\u0448\u0438\u0431\u043A\u0438"],
8806
+ body: "typos.ts \u0432\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u043E\u043F\u0435\u0447\u0430\u0442\u043A\u0438 \u043F\u043E\u0441\u043B\u0435 LLM, \u0447\u0442\u043E\u0431\u044B \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043B\u043E\u0442\u043D\u043E\u0441\u0442\u044C. \u0422\u0438\u043F\u044B: \u0441\u043E\u0441\u0435\u0434\u043D\u044F\u044F \u043A\u043B\u0430\u0432\u0438\u0448\u0430, \u043F\u0440\u043E\u043F\u0443\u0441\u043A, \u0434\u0443\u0431\u043B\u044C, \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0431\u0443\u043A\u0432, \u0440\u0435\u0434\u043A\u0430\u044F \u043D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0430\u044F \u0440\u0430\u0441\u043A\u043B\u0430\u0434\u043A\u0430 RU/EN. \u041D\u0435 \u043B\u043E\u043C\u0430\u0435\u0442 \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0435 \u0441\u043B\u043E\u0432\u0430, \u0441\u0441\u044B\u043B\u043A\u0438, \u0441\u043C\u0430\u0439\u043B\u044B \u0438 \u043F\u0443\u043D\u043A\u0442\u0443\u0430\u0446\u0438\u044E. \u0418\u043D\u0442\u0435\u043D\u0441\u0438\u0432\u043D\u043E\u0441\u0442\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043E\u0442 communication messageStyle \u0438 vibe."
8807
+ },
8808
+ {
8809
+ category: "relationship",
8810
+ subcategory: "stages",
8811
+ title: "\u0421\u0442\u0430\u0434\u0438\u0438 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439",
8812
+ keywords: ["stage", "\u0441\u0442\u0430\u0434\u0438\u044F", "\u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F", "\u043C\u0435\u0442", "cold", "warming", "dating", "long-term", "dumped"],
8813
+ body: "\u0421\u0442\u0430\u0434\u0438\u044F \u0437\u0430\u0434\u0430\u0451\u0442 \u0431\u043B\u0438\u0437\u043E\u0441\u0442\u044C, \u0442\u043E\u043D, \u0448\u0430\u043D\u0441 \u0438\u0433\u043D\u043E\u0440\u0430 \u0438 \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0438. \u041E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u043F\u043E\u0440\u044F\u0434\u043E\u043A: met-irl-got-tg \u2192 tg-given-cold \u2192 tg-given-warming \u2192 convinced \u2192 first-date-done \u2192 dating-early \u2192 dating-stable \u2192 long-term. dumped \u2014 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u0430\u044F \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u044C\u043D\u0430\u044F \u0441\u0442\u0430\u0434\u0438\u044F \u0441 \u043F\u043E\u043B\u043D\u044B\u043C \u0438\u0433\u043D\u043E\u0440\u043E\u043C."
8814
+ },
8815
+ {
8816
+ category: "relationship",
8817
+ subcategory: "score",
8818
+ title: "Score \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439",
8819
+ keywords: ["score", "interest", "trust", "attraction", "annoyance", "cringe", "\u043C\u0435\u0442\u0440\u0438\u043A\u0438"],
8820
+ body: "RelationshipScore: interest \u2014 \u0438\u043D\u0442\u0435\u0440\u0435\u0441, trust \u2014 \u0434\u043E\u0432\u0435\u0440\u0438\u0435, attraction \u2014 \u0440\u043E\u043C\u0430\u043D\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0435/\u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043A\u043E\u0435 \u043F\u0440\u0438\u0442\u044F\u0436\u0435\u043D\u0438\u0435, annoyance \u2014 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0435\u043D\u0438\u0435, cringe \u2014 \u043D\u0430\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043E\u043D \u043A\u0440\u0438\u043D\u0436\u0443\u0435\u0442/\u0434\u0430\u0432\u0438\u0442. Score \u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 moodDelta \u0438 reflection, \u0432\u043B\u0438\u044F\u0435\u0442 \u043D\u0430 conflict, stage transitions, stressLoad \u0433\u043E\u0440\u043C\u043E\u043D\u043E\u0432, ignore \u0438 \u0442\u043E\u043D."
8821
+ },
8822
+ {
8823
+ category: "relationship",
8824
+ subcategory: "stage-transitions",
8825
+ title: "\u0410\u0432\u0442\u043E\u0441\u043C\u0435\u043D\u0430 \u0441\u0442\u0430\u0434\u0438\u0439",
8826
+ keywords: ["stage transition", "\u0430\u0432\u0442\u043E\u0441\u043C\u0435\u043D\u0430", "\u043F\u043E\u0432\u044B\u0441\u0438\u0442\u044C", "\u043F\u043E\u043D\u0438\u0437\u0438\u0442\u044C", "\u0440\u0435\u0433\u0440\u0435\u0441\u0441", "upgrade", "downgrade"],
8827
+ body: "decideStageTransition \u043D\u0435 \u0440\u0430\u043D\u0434\u043E\u043C\u043D\u044B\u0439: \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 downgrade, \u043F\u043E\u0442\u043E\u043C upgrade. \u0414\u043B\u044F upgrade \u043D\u0443\u0436\u043D\u043E \u043C\u0438\u043D\u0438\u043C\u0443\u043C 6 \u0435\u0451 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0432 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0434\u0438\u0438, \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0449\u0438\u0435 score \u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0433\u043E \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430. Downgrade \u0441\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0435\u0442 \u043F\u0440\u0438 \u0432\u044B\u0441\u043E\u043A\u043E\u043C annoyance, \u043D\u0438\u0437\u043A\u0438\u0445 interest/trust \u0438\u043B\u0438 \u0431\u043E\u043B\u044C\u0448\u043E\u043C \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u0438\u0433\u043D\u043E\u0440\u043E\u0432 \u043D\u0430 \u0442\u0451\u043F\u043B\u043E\u0439 \u0441\u0442\u0430\u0434\u0438\u0438. dumped \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u043D\u0435 \u043F\u043E\u0432\u044B\u0448\u0430\u0435\u0442\u0441\u044F."
8828
+ },
8829
+ {
8830
+ category: "relationship",
8831
+ subcategory: "dumped",
8832
+ title: "dumped",
8833
+ keywords: ["dumped", "\u043E\u0442\u0448\u0438\u043B\u0430", "\u0440\u0430\u0437\u0440\u044B\u0432", "\u0441\u0431\u0440\u043E\u0441", "reset", "\u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442"],
8834
+ body: "dumped \u2014 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u0430\u044F \u0441\u0442\u0430\u0434\u0438\u044F '\u043E\u0442\u0448\u0438\u043B\u0430': ignoreChance=1.0 \u0438 \u043E\u0433\u0440\u043E\u043C\u043D\u044B\u0435 delays. Runtime \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C dumped \u043F\u0440\u0438 annoyance > 80 \u0438 interest < -30. \u0412\u044B\u0445\u043E\u0434 \u2014 :reset \u0438\u043B\u0438 \u0440\u0443\u0447\u043D\u043E\u0439 set_stage. reset \u0447\u0438\u0441\u0442\u0438\u0442 score, long-term memory, conflict \u0438 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0438\u0437 dumped \u0432 tg-given-cold."
8835
+ },
8836
+ {
8837
+ category: "relationship",
8838
+ subcategory: "timeline",
8839
+ title: "relationship/timeline.md",
8840
+ keywords: ["timeline", "\u0438\u0441\u0442\u043E\u0440\u0438\u044F \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439", "relationship", "\u0441\u0442\u0430\u0434\u0438\u044F \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0430\u0441\u044C"],
8841
+ body: "maybeAdvanceRelationshipTimeline \u043F\u0440\u0438 \u0441\u043C\u0435\u043D\u0435 \u0441\u0442\u0430\u0434\u0438\u0438 \u043C\u0438\u0433\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0443\u044E \u043F\u0430\u043C\u044F\u0442\u044C \u0432 Memory Palace \u0438 \u043F\u0438\u0448\u0435\u0442 \u0432 relationship/timeline.md \u0441\u0442\u0440\u043E\u043A\u0443 \u0432\u0438\u0434\u0430 '\u0441\u0442\u0430\u0434\u0438\u044F \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0430\u0441\u044C previous \u2192 next'. \u042D\u0442\u043E \u0432\u0430\u0436\u043D\u043E \u0434\u043B\u044F \u043E\u0442\u0432\u0435\u0442\u0430 \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A\u0430 \u043E \u0442\u043E\u043C, \u043A\u0430\u043A \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u043B\u0438\u0441\u044C \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F."
8842
+ },
8843
+ {
8844
+ category: "conflict",
8845
+ subcategory: "levels",
8846
+ title: "\u0423\u0440\u043E\u0432\u043D\u0438 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430",
8847
+ keywords: ["conflict", "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442", "coldUntil", "\u043E\u0431\u0438\u0434\u0430", "level", "\u043C\u043E\u043B\u0447\u0438\u0442"],
8848
+ body: "conflict.json \u0445\u0440\u0430\u043D\u0438\u0442 level 0..4, reason, since, coldUntil \u0438 history. level 1 \u2014 \u043B\u0451\u0433\u043A\u0430\u044F \u043E\u0431\u0438\u0434\u0430 \u043D\u0430 \u0447\u0430\u0441, 2 \u2014 \u043E\u0431\u0438\u0436\u0435\u043D\u0430 \u043D\u0430 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0447\u0430\u0441\u043E\u0432/\u0441\u0443\u0442\u043A\u0438, 3 \u2014 \u0441\u0435\u0440\u044C\u0451\u0437\u043D\u044B\u0439 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u043D\u0430 \u0434\u043D\u0438, 4 \u2014 \u043D\u0430 \u0433\u0440\u0430\u043D\u0438 \u0440\u0430\u0437\u0440\u044B\u0432\u0430. \u041F\u043E\u043A\u0430 coldUntil \u0430\u043A\u0442\u0438\u0432\u0435\u043D, behavior \u043F\u043E\u0447\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 ignore \u0438\u043B\u0438 \u0441\u0443\u0445\u043E\u0439 short."
8849
+ },
8850
+ {
8851
+ category: "conflict",
8852
+ subcategory: "escalation",
8853
+ title: "\u042D\u0441\u043A\u0430\u043B\u0430\u0446\u0438\u044F \u0438 \u043F\u0440\u0438\u043C\u0438\u0440\u0435\u043D\u0438\u0435",
8854
+ keywords: ["escalate", "soften", "annoyance", "cringe", "interestDrop", "\u043F\u043E\u043C\u0438\u0440\u0438\u0442\u044C\u0441\u044F"],
8855
+ body: "escalateFromMood \u0441\u043C\u043E\u0442\u0440\u0438\u0442 \u043D\u0430 delta annoyance/cringe/interestDrop \u0438 \u0442\u0435\u043A\u0443\u0449\u0438\u0439 score. trigger >= 8 \u0434\u0430\u0451\u0442 \u043B\u0451\u0433\u043A\u0443\u044E \u043E\u0431\u0438\u0434\u0443, >=15 \u2014 \u0441\u0435\u0440\u044C\u0451\u0437\u043D\u0435\u0435, >=25 \u0438\u043B\u0438 annoyance >70 \u2014 level 3. annoyance >85 + cringe >70 + interest < -30 \u0432\u0435\u0434\u0451\u0442 \u043A level 4. softenFromMood \u0441\u043D\u0438\u0436\u0430\u0435\u0442 level, \u0435\u0441\u043B\u0438 positive delta attraction+trust+interest >=12."
8856
+ },
8857
+ {
8858
+ category: "memory",
8859
+ subcategory: "memory-palace",
8860
+ title: "Memory Palace",
8861
+ keywords: ["memory palace", "mempalace", "palace", "hall", "drawer", "\u043F\u0430\u043C\u044F\u0442\u044C", "\u0434\u0432\u043E\u0440\u0435\u0446"],
8862
+ body: "Memory Palace \u2014 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F \u043F\u0430\u043C\u044F\u0442\u044C \u0432 memory/palace. \u041E\u043D\u0430 \u0440\u0430\u0437\u0431\u0438\u0442\u0430 \u043D\u0430 halls \u0438 drawers, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0436\u043D\u043E \u0438\u0441\u043A\u0430\u0442\u044C \u043F\u043E \u0432\u0445\u043E\u0434\u044F\u0449\u0435\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E \u0438 \u043F\u043E\u0434\u043C\u0435\u0448\u0438\u0432\u0430\u0442\u044C \u0432 prompt. \u042D\u0442\u043E \u043D\u0435 \u0432\u0435\u043A\u0442\u043E\u0440\u043D\u0430\u044F \u0411\u0414: \u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u0444\u0430\u0439\u043B\u043E\u0432\u043E\u0435, \u0432\u044B\u0431\u043E\u0440 \u0440\u0435\u043B\u0435\u0432\u0430\u043D\u0442\u043D\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430 \u043B\u0451\u0433\u043A\u0438\u0439 \u0438 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439."
8863
+ },
8864
+ {
8865
+ category: "memory",
8866
+ subcategory: "halls",
8867
+ title: "\u0417\u0430\u043B\u044B Memory Palace",
8868
+ keywords: ["hall_facts", "hall_events", "hall_discoveries", "hall_preferences", "hall_advice", "hall_promises", "hall_open_loops", "hall_feelings", "hall_uncertain"],
8869
+ body: "\u0417\u0430\u043B\u044B: hall_facts \u2014 \u0444\u0430\u043A\u0442\u044B; hall_events \u2014 \u0441\u043E\u0431\u044B\u0442\u0438\u044F; hall_discoveries \u2014 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u044F \u043E \u043D\u0451\u043C/\u043D\u0435\u0439; hall_preferences \u2014 \u043F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F; hall_advice \u2014 \u0441\u043E\u0432\u0435\u0442\u044B; hall_promises \u2014 \u043E\u0431\u0435\u0449\u0430\u043D\u0438\u044F; hall_open_loops \u2014 \u043D\u0435\u0437\u0430\u043A\u0440\u044B\u0442\u044B\u0435 \u0442\u0435\u043C\u044B; hall_feelings \u2014 \u0447\u0443\u0432\u0441\u0442\u0432\u0430; hall_uncertain \u2014 \u0441\u043E\u043C\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0435/\u043D\u0435\u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0451\u043D\u043D\u043E\u0435."
8870
+ },
8871
+ {
8872
+ category: "memory",
8873
+ subcategory: "recording",
8874
+ title: "\u0417\u0430\u043F\u0438\u0441\u044C \u043F\u0430\u043C\u044F\u0442\u0438",
8875
+ keywords: ["recordInteractionMemory", "maybeReflect", "memory", "reflect", "interaction"],
8876
+ body: "\u041F\u043E\u0441\u043B\u0435 primary interaction Runtime \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442 recordInteractionMemory. \u041A\u0430\u0436\u0434\u044B\u0435 ~6 turns maybeReflect \u043C\u043E\u0436\u0435\u0442 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u0438\u0435 \u0432 \u0434\u043E\u043B\u0433\u0443\u044E \u043F\u0430\u043C\u044F\u0442\u044C \u0441 \u0443\u0447\u0451\u0442\u043E\u043C conflict. \u0414\u043B\u044F agenda \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0442\u0441\u044F \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0438\u0437 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439; \u0434\u043B\u044F daily summaries \u2014 \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 stale/current sessions."
8877
+ },
8878
+ {
8879
+ category: "memory",
8880
+ subcategory: "retrieval",
8881
+ title: "\u0414\u043E\u0441\u0442\u0430\u0432\u0430\u043D\u0438\u0435 \u043F\u0430\u043C\u044F\u0442\u0438 \u0432 prompt",
8882
+ keywords: ["retrieval", "searchDailySummaries", "loadMemoryPalaceContext", "recall", "prompt"],
8883
+ body: "buildSystemPrompt \u0447\u0438\u0442\u0430\u0435\u0442 persona/speech/communication/relationship, legacy memory \u0438 \u043F\u0440\u0438 incoming \u0438\u0449\u0435\u0442 daily summaries. Runtime \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 loadRealismContext=loadMemoryPalaceContext, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442 \u0440\u0435\u043B\u0435\u0432\u0430\u043D\u0442\u043D\u044B\u0435 palace drawers. \u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043E\u043B\u0436\u043D\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043F\u0430\u043C\u044F\u0442\u044C \u043A\u0430\u043A \u0444\u043E\u043D, \u043D\u0435 \u0446\u0438\u0442\u0438\u0440\u0443\u044F '\u0432 \u043B\u043E\u0433\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E'."
8884
+ },
8885
+ {
8886
+ category: "life",
8887
+ subcategory: "daily-life",
8888
+ title: "Daily-life",
8889
+ keywords: ["daily-life", "\u0436\u0438\u0437\u043D\u044C", "\u0434\u0435\u043D\u044C", "blocks", "events", "wants", "weather", "\u0440\u0430\u0441\u043F\u0438\u0441\u0430\u043D\u0438\u0435"],
8890
+ body: "daily-life \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043E\u0434\u0438\u043D \u0434\u0435\u043D\u044C \u0436\u0438\u0437\u043D\u0438: weather, vibe, blocks \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u0438, events \u0438 wants. \u041A\u044D\u0448 \u0445\u0440\u0430\u043D\u0438\u0442\u0441\u044F \u0432 data/<slug>/daily-life/YYYY-MM-DD.md. \u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0443\u0447\u0438\u0442\u044B\u0432\u0430\u0435\u0442 persona, \u0432\u043E\u0437\u0440\u0430\u0441\u0442, stage, timezone, sleep \u0438 busySchedule, \u0430 conflict \u0434\u0435\u043B\u0430\u0435\u0442 \u0434\u0435\u043D\u044C \u0442\u044F\u0436\u0435\u043B\u0435\u0435."
8891
+ },
8892
+ {
8893
+ category: "life",
8894
+ subcategory: "age-context",
8895
+ title: "\u0412\u043E\u0437\u0440\u0430\u0441\u0442 \u0438 \u0443\u0447\u0451\u0431\u0430",
8896
+ keywords: ["\u0432\u043E\u0437\u0440\u0430\u0441\u0442", "\u0448\u043A\u043E\u043B\u0430", "\u0443\u043D\u0438\u0432\u0435\u0440", "\u043A\u043E\u043B\u043B\u0435\u0434\u0436", "\u043F\u0430\u0440\u044B", "\u0443\u0440\u043E\u043A\u0438"],
8897
+ body: "prompt.ts \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0438\u0440\u0443\u0435\u0442 \u0443\u0447\u0435\u0431\u043D\u044B\u0439 \u044F\u0437\u044B\u043A. \u0414\u043E 17 \u043B\u0435\u0442 \u043E\u043D\u0430 \u0448\u043A\u043E\u043B\u044C\u043D\u0438\u0446\u0430: '\u0443\u0440\u043E\u043A', '\u0448\u043A\u043E\u043B\u0430', '\u043F\u0435\u0440\u0435\u043C\u0435\u043D\u0430', '\u0434\u043E\u043C\u0430\u0448\u043A\u0430', \u043D\u0435 '\u043F\u0430\u0440\u044B/\u0443\u043D\u0438\u0432\u0435\u0440/\u043B\u0435\u043A\u0446\u0438\u044F'. \u0412 17-22 \u043C\u043E\u0436\u043D\u043E \u043A\u043E\u043B\u043B\u0435\u0434\u0436/\u0443\u043D\u0438\u0432\u0435\u0440, \u0435\u0441\u043B\u0438 persona \u044D\u0442\u043E \u0434\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442. Daily-life \u0442\u043E\u0436\u0435 \u0443\u0447\u0438\u0442\u044B\u0432\u0430\u0435\u0442 \u044D\u0442\u043E \u0432 blocks."
8898
+ },
8899
+ {
8900
+ category: "life",
8901
+ subcategory: "agenda",
8902
+ title: "\u041F\u0440\u043E\u0430\u043A\u0442\u0438\u0432\u043D\u0430\u044F agenda",
8903
+ keywords: ["agenda", "\u043F\u0440\u043E\u0430\u043A\u0442\u0438\u0432", "\u0441\u0430\u043C\u0430 \u043F\u0438\u0448\u0435\u0442", "\u043F\u0438\u043D\u0433", "\u043D\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u044C", "\u0441\u043E\u0431\u044B\u0442\u0438\u0435"],
8904
+ body: "Agenda engine \u0434\u0435\u043B\u0430\u0435\u0442 mental notes \u043E \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u0441\u043E\u0431\u044B\u0442\u0438\u044F\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0438 \u043F\u043E\u0432\u043E\u0434\u0430\u0445 \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0441\u0430\u043C\u043E\u0439. extractAgendaUpdates \u0441\u043E\u0437\u0434\u0430\u0451\u0442/update/cancel items \u043F\u043E\u0441\u043B\u0435 \u0435\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439; tickAgenda \u043F\u0440\u0438\u043C\u0435\u0440\u043D\u043E \u0440\u0430\u0437 \u0432 \u043C\u0438\u043D\u0443\u0442\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 due items; handleResponseToProactive \u043F\u043E\u043D\u0438\u043C\u0430\u0435\u0442 \u0435\u0433\u043E \u0440\u0435\u0430\u043A\u0446\u0438\u044E. \u041D\u0430 cold \u0441\u0442\u0430\u0434\u0438\u044F\u0445 agenda \u043F\u043E\u0447\u0442\u0438 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0451\u0442."
8905
+ },
8906
+ {
8907
+ category: "life",
8908
+ subcategory: "autonomous-agenda",
8909
+ title: "\u0410\u0432\u0442\u043E\u043D\u043E\u043C\u043D\u044B\u0435 \u043F\u0438\u043D\u0433\u0438",
8910
+ keywords: ["autonomous", "\u0441\u0430\u043C\u043E\u0441\u0442\u043E\u044F\u0442\u0435\u043B\u044C\u043D\u043E", "\u043F\u0435\u0440\u0432\u043E\u0439", "initiative", "lifeSharing", "\u043F\u0438\u0448\u0435\u0442 \u0441\u0430\u043C\u0430"],
8911
+ body: "ensureAutonomousAgenda \u043C\u043E\u0436\u0435\u0442 \u0441\u043E\u0437\u0434\u0430\u0432\u0430\u0442\u044C \u0441\u0430\u043C\u043E\u0441\u0442\u043E\u044F\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u043E\u0432\u043E\u0434\u044B \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C, \u043D\u0435 \u043F\u0440\u0438\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u043A \u0435\u0433\u043E \u044F\u0432\u043D\u043E\u043C\u0443 \u0431\u0443\u0434\u0443\u0449\u0435\u043C\u0443 \u0441\u043E\u0431\u044B\u0442\u0438\u044E. \u042D\u0442\u043E \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043E\u0442 stage, communication initiative/lifeSharing, conflict \u0438 \u0434\u043D\u0435\u0432\u043D\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430. Runtime \u043D\u0435 \u0441\u043F\u0430\u043C\u0438\u0442: \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443 due item \u0437\u0430 \u0442\u0438\u043A \u0438 \u043D\u0435 \u043B\u0435\u0437\u0435\u0442, \u0435\u0441\u043B\u0438 \u043D\u0435\u0434\u0430\u0432\u043D\u043E \u0431\u044B\u043B\u0430 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u044C."
8912
+ },
8913
+ {
8914
+ category: "persona",
8915
+ subcategory: "prompt-files",
8916
+ title: "persona/speech/boundaries/communication",
8917
+ keywords: ["persona", "speech", "boundaries", "communication.md", "\u043F\u0435\u0440\u0441\u043E\u043D\u0430", "\u0440\u0435\u0447\u044C", "\u0433\u0440\u0430\u043D\u0438\u0446\u044B"],
8918
+ body: "persona.md \u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442 \u043B\u0438\u0447\u043D\u043E\u0441\u0442\u044C \u0438 \u0431\u0438\u043E\u0433\u0440\u0430\u0444\u0438\u044E, speech.md \u2014 \u043C\u0430\u043D\u0435\u0440\u0443 \u0440\u0435\u0447\u0438 \u0438 \u0441\u043B\u043E\u0432\u0435\u0447\u043A\u0438, boundaries.md \u2014 \u0437\u0430\u043F\u0440\u0435\u0442\u044B \u0438 \u043B\u0438\u0447\u043D\u044B\u0435 \u0433\u0440\u0430\u043D\u0438\u0446\u044B, communication.md \u2014 \u0441\u0442\u0438\u043B\u044C \u043A\u043E\u043C\u043C\u0443\u043D\u0438\u043A\u0430\u0446\u0438\u0438. buildSystemPrompt \u0432\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u044D\u0442\u0438 \u0444\u0430\u0439\u043B\u044B \u043A\u0430\u043A \u043E\u0441\u043D\u043E\u0432\u043D\u044B\u0435 \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0438, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043F\u0440\u0430\u0432\u0438\u0442\u044C \u0438\u0445 \u043B\u0443\u0447\u0448\u0435 \u0447\u0435\u0440\u0435\u0437 MemoryPage \u0438\u043B\u0438 assistant tools, \u0430 \u043D\u0435 \u043C\u0435\u043D\u044F\u0442\u044C \u043A\u043E\u0434."
8919
+ },
8920
+ {
8921
+ category: "persona",
8922
+ subcategory: "generation",
8923
+ title: "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u043F\u0435\u0440\u0441\u043E\u043D\u044B",
8924
+ keywords: ["generatePersonaPack", "generate_persona", "\u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043F\u0435\u0440\u0441\u043E\u043D\u0443", "setup", "personaNotes"],
8925
+ body: "generatePersonaPack \u0447\u0435\u0440\u0435\u0437 LLM \u0441\u043E\u0437\u0434\u0430\u0451\u0442 persona.md, speech.md, boundaries.md \u0438 communication.md. Assistant tool generate_persona \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442 \u0442\u0435\u043A\u0443\u0449\u0438\u0435 name/age/nationality/personaNotes. SetupFlow \u0438 CLI \u0442\u043E\u0436\u0435 \u043C\u043E\u0433\u0443\u0442 \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C."
8926
+ },
8927
+ {
8928
+ category: "communication",
8929
+ subcategory: "fields",
8930
+ title: "CommunicationProfile",
8931
+ keywords: ["communication", "notifications", "messageStyle", "initiative", "lifeSharing"],
8932
+ body: "CommunicationProfile \u0441\u043E\u0441\u0442\u043E\u0438\u0442 \u0438\u0437 notifications muted/normal/priority, messageStyle one-liners/balanced/bursty/longform, initiative low/medium/high, lifeSharing low/medium/high. \u041E\u043D \u0432\u043B\u0438\u044F\u0435\u0442 \u043D\u0430 presence, agenda, typo density, prompt \u0438 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u0432 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u043C \u0434\u0438\u0430\u043B\u043E\u0433\u0435."
8933
+ },
8934
+ {
8935
+ category: "communication",
8936
+ subcategory: "legacy-vibe",
8937
+ title: "legacy vibe",
8938
+ keywords: ["vibe", "short", "warm", "legacy", "deriveLegacyVibe"],
8939
+ body: "\u0421\u0442\u0430\u0440\u043E\u0435 \u043F\u043E\u043B\u0435 vibe \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0434\u043B\u044F \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u043E\u0441\u0442\u0438. vibe=warm \u043C\u0430\u043F\u0438\u0442\u0441\u044F \u043F\u0440\u0438\u043C\u0435\u0440\u043D\u043E \u0432 cute/\u0442\u0451\u043F\u043B\u044B\u0439 \u0441\u0442\u0438\u043B\u044C, vibe=short \u2014 \u0432 alt/one-liners/low initiative. \u041D\u043E\u0432\u0430\u044F \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u2014 communication, \u0430 vibe \u0442\u043E\u043B\u044C\u043A\u043E legacy."
8940
+ },
8941
+ {
8942
+ category: "llm",
8943
+ subcategory: "client",
8944
+ title: "LLM client",
8945
+ keywords: ["llm", "openai", "anthropic", "client", "timeout", "retries", "json"],
8946
+ body: "src/llm/index.ts \u0434\u0430\u0451\u0442 \u0435\u0434\u0438\u043D\u044B\u0439 LLMClient.chat \u0434\u043B\u044F OpenAI-compatible \u0438 Anthropic. \u0415\u0441\u0442\u044C timeout 120s, maxRetries=1 \u0438 \u0441\u0435\u0440\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u0432\u044B\u0437\u043E\u0432\u043E\u0432 \u0447\u0435\u0440\u0435\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u044C, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u044B\u043C\u0438 \u0437\u0430\u043F\u0440\u043E\u0441\u0430\u043C\u0438. \u041E\u043F\u0446\u0438\u0438: temperature, maxTokens, json/jsonSchema."
8947
+ },
8948
+ {
8949
+ category: "llm",
8950
+ subcategory: "providers",
8951
+ title: "\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u044B LLM",
8952
+ keywords: ["provider", "preset", "claudehub", "openrouter", "groq", "deepseek", "mistral", "gemini", "ollama"],
8953
+ body: "LLM presets \u0432\u043A\u043B\u044E\u0447\u0430\u044E\u0442 claudehub, openai, lmstudio, ollama, anthropic, openrouter, groq, deepseek, mistral, google, xai, together, fireworks, perplexity, cerebras \u0438 \u0434\u0440. \u0423 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0435\u0441\u0442\u044C proto openai/anthropic, baseURL, defaultModel, models, apiKeyRequired \u0438 hints. GirlAI \u0441\u0435\u0439\u0447\u0430\u0441 disabled."
8954
+ },
8955
+ {
8956
+ category: "llm",
8957
+ subcategory: "oauth",
8958
+ title: "OAuth GirlAI",
8959
+ keywords: ["oauth", "girlai", "refresh token", "access token", "expires"],
8960
+ body: "LLM config \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 oauthRefreshToken \u0438 oauthExpiresAt. OpenAILike \u043F\u0435\u0440\u0435\u0434 \u0432\u044B\u0437\u043E\u0432\u043E\u043C \u0434\u0435\u043B\u0430\u0435\u0442 ensureFreshToken: \u0435\u0441\u043B\u0438 \u0442\u043E\u043A\u0435\u043D \u0438\u0441\u0442\u0451\u043A, refreshAccessToken \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442 access/refresh tokens. \u041F\u0440\u0438 \u043E\u0448\u0438\u0431\u043A\u0435 refresh \u043F\u043E\u043B\u044F oauth \u043E\u0447\u0438\u0449\u0430\u044E\u0442\u0441\u044F, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u043B\u043E\u043C\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438."
8961
+ },
8962
+ {
8963
+ category: "diagnostics",
8964
+ subcategory: "runtime-commands",
8965
+ title: "Runtime \u043A\u043E\u043C\u0430\u043D\u0434\u044B",
8966
+ keywords: ["status", "why", "wake", "debug", "reset", "stage", "sticker", "amnesia", "\u043A\u043E\u043C\u0430\u043D\u0434\u044B"],
8967
+ body: "send_command \u0432 assistant \u043C\u043E\u0436\u0435\u0442 \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C runtime-\u043A\u043E\u043C\u0430\u043D\u0434\u044B: status, why, wake, debug, reset. Runtime \u0442\u0430\u043A\u0436\u0435 \u0438\u043C\u0435\u0435\u0442 :stage, :sticker, :amnesia \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 CLI/chat commands. status \u2014 \u043E\u0431\u0449\u0438\u0439 \u0441\u043D\u0438\u043C\u043E\u043A, why \u2014 \u043F\u0440\u0438\u0447\u0438\u043D\u044B \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u0440\u0435\u0448\u0435\u043D\u0438\u044F, debug \u2014 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u043D\u044B\u0439 presence/stage/conflict/score/communication, wake \u2014 \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u0431\u0443\u0434\u0438\u0442, reset \u2014 \u0447\u0438\u0441\u0442\u0438\u0442 score/memory/conflict."
8968
+ },
8969
+ {
8970
+ category: "diagnostics",
8971
+ subcategory: "not-replying",
8972
+ title: "\u0415\u0441\u043B\u0438 \u043E\u043D\u0430 \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442",
8973
+ keywords: ["\u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442", "\u043C\u043E\u043B\u0447\u0438\u0442", "ignore", "ignored", "\u043F\u043E\u0447\u0435\u043C\u0443", "why", "read_logs"],
8974
+ body: "\u0410\u043B\u0433\u043E\u0440\u0438\u0442\u043C: \u043F\u0440\u043E\u0432\u0435\u0440\u044C RuntimeBus state \u0438 lastError; read_logs \u043D\u0430 ignored/error; \u043A\u043E\u043C\u0430\u043D\u0434\u0443 why/debug; sleep/busy/presence; conflict coldUntil; stage defaults ignoreChance; ignoreTendency; score annoyance/cringe/interest; LLM/provider errors; Telegram mode/token/session. \u041D\u0435 \u043D\u0430\u0434\u043E \u0441\u0440\u0430\u0437\u0443 \u0441\u043E\u0432\u0435\u0442\u043E\u0432\u0430\u0442\u044C \u043C\u0435\u043D\u044F\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u044C, \u0435\u0441\u043B\u0438 \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u0432 stage/sleep/conflict."
8975
+ },
8976
+ {
8977
+ category: "diagnostics",
8978
+ subcategory: "llm-errors",
8979
+ title: "\u041E\u0448\u0438\u0431\u043A\u0438 LLM",
8980
+ keywords: ["401", "403", "429", "quota", "billing", "timeout", "api key", "baseURL", "model", "\u043E\u0448\u0438\u0431\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0438"],
8981
+ body: "silentErrorLabel \u043A\u043B\u0430\u0441\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u043E\u0448\u0438\u0431\u043A\u0438 \u043A\u0430\u043A auth/quota/rate-limit/network/provider. \u041F\u0440\u0438 LLM \u043E\u0448\u0438\u0431\u043A\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0439 llm.presetId, proto, baseURL, apiKey, model, \u0431\u0430\u043B\u0430\u043D\u0441/\u043A\u0432\u043E\u0442\u044B \u0438 \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u043E\u0441\u0442\u044C max_tokens/max_completion_tokens. \u041B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0435 LM Studio/Ollama \u043C\u043E\u0433\u0443\u0442 \u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C \u0431\u0435\u0437 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u0433\u043E \u043A\u043B\u044E\u0447\u0430."
8982
+ },
8983
+ {
8984
+ category: "diagnostics",
8985
+ subcategory: "telegram-errors",
8986
+ title: "\u041E\u0448\u0438\u0431\u043A\u0438 Telegram",
8987
+ keywords: ["telegram error", "BOT_TOKEN", "API_ID", "API_HASH", "session", "connect", "timeout"],
8988
+ body: "bot mode \u0442\u0440\u0435\u0431\u0443\u0435\u0442 telegram.botToken. userbot \u0442\u0440\u0435\u0431\u0443\u0435\u0442 apiId/apiHash/sessionString \u0438 \u043C\u043E\u0436\u0435\u0442 \u043F\u0430\u0434\u0430\u0442\u044C \u043D\u0430 connect/getMe. \u0414\u043B\u044F \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043E\u043A \u0432\u043A\u043B\u044E\u0447\u0430\u0439 useWSS \u0438\u043B\u0438 proxy. GIRL_AGENT_DEBUG=1 \u043F\u0435\u0447\u0430\u0442\u0430\u0435\u0442 debug userbot connect/getMe/handlers."
8989
+ },
8990
+ {
8991
+ category: "webui",
8992
+ subcategory: "pages",
8993
+ title: "\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u044B WebUI",
8994
+ keywords: ["webui", "\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B", "assistant", "configuration", "diagnostics", "logs", "memory", "relationship", "addons"],
8995
+ body: "WebUI React \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B: SetupFlow \u2014 \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u0430\u044F \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430; ConfigurationPage \u2014 config; AssistantPage \u2014 \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A \u0441 tool blocks \u0438 question buttons; DiagnosticsPage \u2014 runtime/system diagnostics; LogsPage \u2014 \u0441\u043E\u0431\u044B\u0442\u0438\u044F; MemoryPage \u2014 editing/preview memory; RelationshipPage \u2014 stage/score/timeline; AddonsPage \u2014 marketplace/installed."
8996
+ },
8997
+ {
8998
+ category: "webui",
8999
+ subcategory: "assistant",
9000
+ title: "WebUI assistant",
9001
+ keywords: ["assistant", "\u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A", "tool", "question", "\u043A\u043D\u043E\u043F\u043A\u0438", "assistant page"],
9002
+ body: "AssistantPage \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u0442 /api/assistant/chat \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439. \u041E\u0442\u0432\u0435\u0442 \u043C\u043E\u0436\u0435\u0442 \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C <tool> JSON-\u0431\u043B\u043E\u043A\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 UI \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043A\u0430\u043A \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u043C\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F, \u0438 <question> \u0441 options, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0440\u0435\u043D\u0434\u0435\u0440\u044F\u0442\u0441\u044F \u043A\u043D\u043E\u043F\u043A\u0430\u043C\u0438. \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442 tool calls, \u043E\u043D\u0438 \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438."
9003
+ },
9004
+ {
9005
+ category: "webui",
9006
+ subcategory: "assistant-tools",
9007
+ title: "\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A\u0430",
9008
+ keywords: ["set_field", "set_stage", "write_memory", "append_memory", "runtime_action", "read_logs", "read_memory", "list_presets"],
9009
+ body: "Backend assistant tools: set_field, set_stage, set_communication_preset, write_memory, append_memory, generate_persona, runtime_action, send_command, list_presets, read_logs, read_memory. ALLOWED_FIELDS \u0438 ALLOWED_MEMORY \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0438\u0432\u0430\u044E\u0442, \u0447\u0442\u043E \u043C\u043E\u0436\u043D\u043E \u043C\u0435\u043D\u044F\u0442\u044C, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A \u043D\u0435 \u043F\u0438\u0441\u0430\u043B \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u043B\u044C\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B."
9010
+ },
9011
+ {
9012
+ category: "webui",
9013
+ subcategory: "theme-and-css",
9014
+ title: "\u0422\u0435\u043C\u044B \u0438 \u043A\u043D\u043E\u043F\u043A\u0438 WebUI",
9015
+ keywords: ["theme", "dark", "light", "css", "\u043A\u043D\u043E\u043F\u043A\u0438", "\u0446\u0432\u0435\u0442", "bone", "ink"],
9016
+ body: "styles.css \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442 bone/ink \u043F\u0430\u043B\u0438\u0442\u0440\u0443 \u0438 CSS variables. \u0412 light/dark \u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F --bone \u0438 --ink, \u0430 app colors \u0438\u0434\u0443\u0442 \u0447\u0435\u0440\u0435\u0437 --ga-text, --ga-bg-* \u0438 --ga-border. Button text \u0434\u043E\u043B\u0436\u0435\u043D \u044F\u0432\u043D\u043E \u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u044C/inherit \u0438\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C --ga-text, \u0438\u043D\u0430\u0447\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043D\u044B\u0439 default \u043C\u043E\u0436\u0435\u0442 \u0434\u0430\u0442\u044C \u043F\u043B\u043E\u0445\u043E\u0439 \u043A\u043E\u043D\u0442\u0440\u0430\u0441\u0442."
9017
+ },
9018
+ {
9019
+ category: "webui",
9020
+ subcategory: "api",
9021
+ title: "WebUI API",
9022
+ keywords: ["api", "routes", "profiles", "presets", "system", "tg-auth", "addons", "websocket"],
9023
+ body: "src/webui/routes \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 profiles, presets, assistant, addons, system, tg-auth. runtime-bus \u0434\u0430\u0451\u0442 runtime state/logs, websocket \u043E\u0442\u0434\u0430\u0451\u0442 live events. static.ts \u0440\u0430\u0437\u0434\u0430\u0451\u0442 webui/dist. system diagnostics \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 platform, arch, node, dataRoot, ipv4 \u0438 memory."
9024
+ },
9025
+ {
9026
+ category: "addons",
9027
+ subcategory: "format",
9028
+ title: "\u0424\u043E\u0440\u043C\u0430\u0442 .gaa \u0430\u0434\u0434\u043E\u043D\u043E\u0432",
9029
+ keywords: ["addon", "gaa", "manifest", "files", "config.patch", "theme.css", "install.sh"],
9030
+ body: ".gaa \u2014 zip-\u0430\u0440\u0445\u0438\u0432 \u043F\u0430\u043F\u043A\u0438 \u0430\u0434\u0434\u043E\u043D\u0430. manifest.json \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D. files/ \u043A\u043E\u043F\u0438\u0440\u0443\u044E\u0442\u0441\u044F \u0432 data/<slug>/, config.patch.json deep-merge \u0432 config, code.patch \u043C\u043E\u0436\u0435\u0442 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0442\u044C\u0441\u044F git apply, theme.css \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442 CSS, install.sh \u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u0435\u043D. Marketplace \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 GIRL_AGENT_ADDON_REGISTRY \u0438\u043B\u0438 GitHub raw registry."
9031
+ },
9032
+ {
9033
+ category: "addons",
9034
+ subcategory: "manifest",
9035
+ title: "manifest.json \u0430\u0434\u0434\u043E\u043D\u0430",
9036
+ keywords: ["manifest", "addon", "id", "version", "compatibility", "settings"],
9037
+ body: "Manifest \u0442\u0440\u0435\u0431\u0443\u0435\u0442 id/name/description/version. \u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E: author, compatibility semver range, tags, dependencies, settings, icon, homepage. Settings \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442 string/number/boolean/select \u0441 default/options/required."
9038
+ },
9039
+ {
9040
+ category: "mcp",
9041
+ subcategory: "client",
9042
+ title: "MCP servers",
9043
+ keywords: ["mcp", "tools", "stdio", "exa", "spotify", "instagram", "weather", "calendar"],
9044
+ body: "startMcpServers \u0447\u0438\u0442\u0430\u0435\u0442 cfg.mcp, \u043D\u0430\u0445\u043E\u0434\u0438\u0442 preset, \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442 stdio transport, \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 MCP Client \u0438 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 listTools. \u0421\u0435\u0439\u0447\u0430\u0441 \u0433\u043E\u0442\u043E\u0432 Exa Search \u0447\u0435\u0440\u0435\u0437 npx -y exa-mcp-server \u0438 EXA_API_KEY; spotify/instagram/weather/calendar \u043F\u043E\u043C\u0435\u0447\u0435\u043D\u044B soon."
9045
+ },
9046
+ {
9047
+ category: "migrations",
9048
+ subcategory: "data-migrations",
9049
+ title: "\u041C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445",
9050
+ keywords: ["migration", "update", "0112", "0113", "0114", "\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438"],
9051
+ body: "src/migrations \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 versioned data migrations. RuntimeBus \u043F\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043E\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 pending migrations \u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442 runMigrations. CLI update [--verbose] \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0432\u0440\u0443\u0447\u043D\u0443\u044E. \u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0435: 0112 useWSS default, 0113 communication.md, 0114 Memory Palace."
9052
+ },
9053
+ {
9054
+ category: "desktop",
9055
+ subcategory: "wrapper",
9056
+ title: "desktop-rs",
9057
+ keywords: ["desktop", "rust", "iced", "windows", "installer", "json-events"],
9058
+ body: "README \u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442 desktop-rs: \u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 Rust/iced \u043A\u043B\u0438\u0435\u043D\u0442 \u0438 installer wizard \u0434\u043B\u044F Windows. \u041E\u043D \u0441\u0442\u0430\u0432\u0438\u0442 Node package, \u0441\u043E\u0437\u0434\u0430\u0451\u0442 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0438 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0435\u0442 WebUI \u043D\u0430 127.0.0.1:7777. CLI \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 --json-events/--headless \u0434\u043B\u044F desktop wrapper."
9059
+ },
9060
+ {
9061
+ category: "release",
9062
+ subcategory: "install",
9063
+ title: "\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C",
9064
+ keywords: ["install", "\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430", "curl", "docker", "npx", "node", "server", "systemd"],
9065
+ body: "\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430: curl install.sh \u0441\u0442\u0430\u0432\u0438\u0442 docker wrapper \u0438\u043B\u0438 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 Node 22 LTS \u0432 ~/.local/share/girl-agent/runtime, \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 sudo. \u0410\u043B\u044C\u0442\u0435\u0440\u043D\u0430\u0442\u0438\u0432\u044B: npx @thesashadev/girl-agent, docker run \u0441 volume /data, server --print-config/--print-systemd/--print-docker."
9066
+ }
9067
+ ];
9068
+ function generatedKnowledge() {
9069
+ return [
9070
+ ...STAGE_PRESETS.map((stage) => ({
9071
+ category: "relationship",
9072
+ subcategory: `stage-${stage.num}`,
9073
+ title: `${stage.num}. ${stage.id}`,
9074
+ keywords: ["stage", "\u0441\u0442\u0430\u0434\u0438\u044F", stage.id, stage.label, String(stage.num)],
9075
+ body: `${stage.label}: ${stage.description}. Defaults: interest=${stage.defaults.interest}, trust=${stage.defaults.trust}, attraction=${stage.defaults.attraction}, annoyance=${stage.defaults.annoyance}, cringeTolerance=${stage.defaults.cringeTolerance}, ignoreChance=${stage.defaults.ignoreChance}, replyDelaySec=${stage.defaults.replyDelaySec[0]}-${stage.defaults.replyDelaySec[1]}.`
9076
+ })),
9077
+ ...COMMUNICATION_PRESETS.map((preset) => ({
9078
+ category: "communication",
9079
+ subcategory: `preset-${preset.id}`,
9080
+ title: `Communication preset ${preset.id}`,
9081
+ keywords: ["communication", "preset", "\u043E\u0431\u0449\u0435\u043D\u0438\u0435", preset.id, preset.label],
9082
+ body: `${preset.label}: ${preset.description}. notifications=${preset.profile.notifications}, messageStyle=${preset.profile.messageStyle}, initiative=${preset.profile.initiative}, lifeSharing=${preset.profile.lifeSharing}.`
9083
+ })),
9084
+ {
9085
+ category: "llm",
9086
+ subcategory: "preset-index",
9087
+ title: "\u0418\u043D\u0434\u0435\u043A\u0441 LLM presets",
9088
+ keywords: ["llm", "preset", "\u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u044B", ...LLM_PRESETS.map((p) => p.id)],
9089
+ body: LLM_PRESETS.map((p) => `${p.id}: ${p.name}, proto=${p.proto}, default=${p.defaultModel || "custom"}${p.baseURL ? `, baseURL=${p.baseURL}` : ""}${p.disabled ? `, disabled=${p.disabledReason ?? "yes"}` : ""}${p.hint ? `, hint=${p.hint}` : ""}`).join("\n")
9090
+ },
9091
+ {
9092
+ category: "mcp",
9093
+ subcategory: "preset-index",
9094
+ title: "\u0418\u043D\u0434\u0435\u043A\u0441 MCP presets",
9095
+ keywords: ["mcp", "preset", "\u0438\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438", ...MCP_PRESETS.map((p) => p.id)],
9096
+ body: MCP_PRESETS.map((p) => `${p.id}: ${p.name}, ready=${p.ready ? "yes" : "soon"} \u2014 ${p.description}`).join("\n")
9097
+ }
9098
+ ];
9099
+ }
9100
+ function knowledgeBase() {
9101
+ return [...CORE_KNOWLEDGE_BASE, ...generatedKnowledge()];
9102
+ }
9103
+ function renderRelevantKnowledge(query, limit = 8) {
9104
+ const articles = selectKnowledgeArticles(query, limit);
9105
+ return [
9106
+ "\u0411\u0430\u0437\u0430 \u0437\u043D\u0430\u043D\u0438\u0439 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 girl-agent:",
9107
+ renderKnowledgeIndex(),
9108
+ "\u0412\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u0435 \u0440\u0435\u043B\u0435\u0432\u0430\u043D\u0442\u043D\u044B\u0435 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438/\u043F\u043E\u0434\u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438:",
9109
+ ...articles.map((a) => `## ${a.category} / ${a.subcategory}: ${a.title}
9110
+ ${a.body}`),
9111
+ "\u0415\u0441\u043B\u0438 \u0432\u043E\u043F\u0440\u043E\u0441 \u043D\u0435 \u043F\u043E\u043A\u0440\u044B\u0442 \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u043C\u0438 \u0441\u0442\u0430\u0442\u044C\u044F\u043C\u0438 \u2014 \u0441\u043A\u0430\u0436\u0438 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u043E \u0438 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0439 \u043A\u043E\u043D\u0444\u0438\u0433, \u043F\u0430\u043C\u044F\u0442\u044C \u0438\u043B\u0438 runtime logs."
9112
+ ].join("\n\n");
9113
+ }
9114
+ function renderKnowledgeIndex() {
9115
+ const grouped = /* @__PURE__ */ new Map();
9116
+ for (const article of knowledgeBase()) {
9117
+ const list = grouped.get(article.category) ?? [];
9118
+ if (!list.includes(article.subcategory)) list.push(article.subcategory);
9119
+ grouped.set(article.category, list);
9120
+ }
9121
+ return [...grouped.entries()].map(([category, subcategories]) => `- ${category}: ${subcategories.join(", ")}`).join("\n");
9122
+ }
9123
+ function selectKnowledgeArticles(query, limit) {
9124
+ const normalized = normalizeSearchText(query);
9125
+ const terms = searchTerms(normalized);
9126
+ const scored = knowledgeBase().map((article) => ({ article, score: knowledgeScore(article, normalized, terms) })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((item) => item.article);
9127
+ if (scored.length) return scored;
9128
+ return knowledgeBase().filter((article) => article.category === "overview" || article.category === "diagnostics").slice(0, limit);
9129
+ }
9130
+ function knowledgeScore(article, normalizedQuery, terms) {
9131
+ const haystack = normalizeSearchText([
9132
+ article.category,
9133
+ article.subcategory,
9134
+ article.title,
9135
+ article.keywords.join(" "),
9136
+ article.body
9137
+ ].join(" "));
9138
+ let score = 0;
9139
+ for (const keyword of article.keywords) {
9140
+ const normalizedKeyword = normalizeSearchText(keyword);
9141
+ if (normalizedKeyword && normalizedQuery.includes(normalizedKeyword)) score += 6;
9142
+ }
9143
+ for (const term of terms) {
9144
+ if (article.category.includes(term) || article.subcategory.includes(term)) score += 3;
9145
+ if (haystack.includes(term)) score += 1;
9146
+ }
9147
+ return score;
9148
+ }
9149
+ function searchTerms(text) {
9150
+ return [...new Set(text.split(/[^a-zа-яё0-9]+/i).filter((t) => t.length >= 3))];
9151
+ }
9152
+ function normalizeSearchText(text) {
9153
+ return text.toLowerCase().replace(/ё/g, "\u0435");
9154
+ }
9155
+
9156
+ // src/webui/routes/assistant.ts
8119
9157
  var ASSISTANT_SYSTEM = `\u0422\u044B \u2014 \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0439 \u0418\u0418-\u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A \u043F\u043E \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0435 girl-agent (\u0440\u0430\u043D\u0442\u0430\u0439\u043C \u0434\u043B\u044F Telegram-\u0434\u0435\u0432\u0443\u0448\u043A\u0438 \u0441 \u0447\u0435\u043B\u043E\u0432\u0435\u0447\u043D\u044B\u043C \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435\u043C). \u0422\u0435\u0431\u044F \u0437\u043E\u0432\u0443\u0442 "\u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A", \u043D\u0435 "\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043D\u0442".
8120
9158
 
8121
9159
  \u0422\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430:
@@ -8123,6 +9161,7 @@ var ASSISTANT_SYSTEM = `\u0422\u044B \u2014 \u0432\u0441\u0442\u0440\u043E\u0435
8123
9161
  - \u041C\u0435\u043D\u044F\u0442\u044C \u043A\u043E\u043D\u0444\u0438\u0433 \u043F\u0440\u043E\u0444\u0438\u043B\u044F \u0438 \u0444\u0430\u0439\u043B\u044B \u043F\u0430\u043C\u044F\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B (\u0441\u043C. \u043D\u0438\u0436\u0435).
8124
9162
  - \u041F\u043E\u043C\u043E\u0433\u0430\u0442\u044C \u0441 \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0439 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u043E\u0439 \u0438 \u0434\u0438\u0430\u0433\u043D\u043E\u0441\u0442\u0438\u043A\u043E\u0439 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F.
8125
9163
  - \u041E\u0431\u044A\u044F\u0441\u043D\u044F\u0442\u044C \u043E\u0448\u0438\u0431\u043A\u0438 \u0438\u0437 \u043B\u043E\u0433\u043E\u0432 \u0438 \u043F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0442\u044C \u043F\u043E\u0447\u0438\u043D\u043A\u0443.
9164
+ - \u0414\u0430\u0432\u0430\u0442\u044C \u043E\u0442\u0432\u0435\u0442\u044B, \u043E\u043F\u0438\u0440\u0430\u044F\u0441\u044C \u043D\u0430 \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u0435 \u0441\u0442\u0430\u0442\u044C\u0438 \u0438\u0437 \u0431\u0430\u0437\u044B \u0437\u043D\u0430\u043D\u0438\u0439 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u0430 \u043D\u0435 \u043D\u0430 \u0434\u043E\u0433\u0430\u0434\u043A\u0438.
8126
9165
 
8127
9166
  \u041F\u0440\u0430\u0432\u0438\u043B\u0430 \u043E\u0442\u0432\u0435\u0442\u0430:
8128
9167
  - \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0442\u043A\u043E (2-5 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0439), \u043D\u0430 \u0440\u0443\u0441\u0441\u043A\u043E\u043C.
@@ -8145,7 +9184,7 @@ var ASSISTANT_SYSTEM = `\u0422\u044B \u2014 \u0432\u0441\u0442\u0440\u043E\u0435
8145
9184
  - set_stage { stage: string } \u2014 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0442\u0430\u0434\u0438\u044E \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439 (id \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430).
8146
9185
  - set_communication_preset { id: string } \u2014 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0440\u0435\u0441\u0435\u0442 \u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0438 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C communication.md.
8147
9186
  - write_memory { file: string, content: string } \u2014 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u0442\u044C \u0444\u0430\u0439\u043B \u043F\u0430\u043C\u044F\u0442\u0438.
8148
- \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0435 \u0444\u0430\u0439\u043B\u044B: persona.md, speech.md, boundaries.md, communication.md, long-term.md.
9187
+ \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0435 \u0444\u0430\u0439\u043B\u044B: persona.md, speech.md, boundaries.md, communication.md, long-term.md, memory/long-term.md, memory/facts.md, memory/uncertain.md, time/promises.md, time/open-loops.md.
8149
9188
  - append_memory { file: string, content: string } \u2014 \u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443 \u0432 \u0444\u0430\u0439\u043B \u043F\u0430\u043C\u044F\u0442\u0438.
8150
9189
  - generate_persona { name?: string, age?: number, nationality?: string, notes?: string } \u2014 LLM-\u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F persona.md/speech.md/communication.md (\u044D\u0442\u043E \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442 ~30s).
8151
9190
  - runtime_action { action: "start"|"stop"|"pause"|"resume"|"restart" } \u2014 \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0440\u0430\u043D\u0442\u0430\u0439\u043C\u043E\u043C.
@@ -8168,7 +9207,7 @@ var ASSISTANT_SYSTEM = `\u0422\u044B \u2014 \u0432\u0441\u0442\u0440\u043E\u0435
8168
9207
 
8169
9208
  \u0412\u0430\u0436\u043D\u044B\u0435 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438:
8170
9209
  - ignoreTendency: 0 \u2014 \u0432\u0441\u0435\u0433\u0434\u0430 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442; 100 \u2014 \u043F\u043E\u0447\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u0438\u0433\u043D\u043E\u0440\u0438\u0442. \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 35.
8171
- - \u0415\u0441\u043B\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0436\u0430\u043B\u0443\u0435\u0442\u0441\u044F \u0447\u0442\u043E "\u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442" \u2192 \u043F\u0440\u043E\u0432\u0435\u0440\u044C runtime-action stat\u0435 \u0438 read_logs.
9210
+ - \u0415\u0441\u043B\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0436\u0430\u043B\u0443\u0435\u0442\u0441\u044F \u0447\u0442\u043E "\u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442" \u2192 \u043F\u0440\u043E\u0432\u0435\u0440\u044C runtime state \u0438 read_logs.
8172
9211
  - \u0415\u0441\u043B\u0438 LLM \u043E\u0448\u0438\u0431\u043A\u0438 \u2192 \u043F\u0440\u043E\u0432\u0435\u0440\u044C llm.apiKey, llm.baseURL, llm.model.
8173
9212
  - \u0415\u0441\u043B\u0438 \u0441\u043C\u0435\u043D\u0438\u043B\u0438 telegram.mode \u2014 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043D\u0443\u0436\u0435\u043D restart.`;
8174
9213
  function registerAssistantRoutes(r) {
@@ -8191,22 +9230,69 @@ function registerAssistantRoutes(r) {
8191
9230
  return { reply: reply2, toolCalls: [] };
8192
9231
  }
8193
9232
  const stage = findStage(cfg.stage);
9233
+ const status = bus.status(cfg.slug);
9234
+ const userQuestion = body.messages.slice().reverse().find((m) => m.role === "user")?.content ?? "";
9235
+ const relevantKnowledge = renderRelevantKnowledge(userQuestion);
8194
9236
  let scoreLine = "";
9237
+ let memoryContext = "";
8195
9238
  let recentLogs = "";
8196
9239
  try {
8197
9240
  const rel = await readRelationship(cfg.slug);
8198
9241
  scoreLine = ` score=${JSON.stringify(rel.score)}`;
8199
9242
  } catch {
8200
9243
  }
9244
+ try {
9245
+ const [persona, speech, communication, boundaries, longTerm, facts, uncertain, timeline, openLoops, promises] = await Promise.all([
9246
+ readMd(cfg.slug, "persona.md"),
9247
+ readMd(cfg.slug, "speech.md"),
9248
+ readMd(cfg.slug, "communication.md"),
9249
+ readMd(cfg.slug, "boundaries.md"),
9250
+ readMd(cfg.slug, "memory/long-term.md"),
9251
+ readMd(cfg.slug, "memory/facts.md"),
9252
+ readMd(cfg.slug, "memory/uncertain.md"),
9253
+ readMd(cfg.slug, "relationship/timeline.md"),
9254
+ readMd(cfg.slug, "time/open-loops.md"),
9255
+ readMd(cfg.slug, "time/promises.md")
9256
+ ]);
9257
+ memoryContext = renderAssistantMemoryContext({
9258
+ persona,
9259
+ speech,
9260
+ communication,
9261
+ boundaries,
9262
+ longTerm,
9263
+ facts,
9264
+ uncertain,
9265
+ timeline,
9266
+ openLoops,
9267
+ promises
9268
+ });
9269
+ } catch {
9270
+ }
8201
9271
  try {
8202
9272
  const buf = bus.recentLogs(cfg.slug, 25);
8203
9273
  recentLogs = buf.map((e) => `[${e.type}] ${e.text ?? ""}`).join("\n");
8204
9274
  } catch {
8205
9275
  }
8206
- const ctxPrompt = `\u0422\u0435\u043A\u0443\u0449\u0438\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C: ${cfg.name}, ${cfg.age}, \u0441\u0442\u0430\u0434\u0438\u044F "${stage.label}" (${cfg.stage}), privacy=${cfg.privacy ?? "owner-only"}, ignoreTendency=${cfg.ignoreTendency ?? 35}, llm=${cfg.llm.presetId}/${cfg.llm.model}, telegram=${cfg.mode ?? "bot"}.${scoreLine}` + (recentLogs ? `
8207
-
8208
- \u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F runtime'\u0430 (\u0434\u043B\u044F \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430):
8209
- ${recentLogs.slice(-1500)}` : "");
9276
+ const runtimeContext = [
9277
+ `\u0422\u0435\u043A\u0443\u0449\u0438\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C: ${cfg.name}, ${cfg.age}, ${cfg.nationality}, tz=${cfg.tz}`,
9278
+ `slug=${cfg.slug}, runtime=${status.state}${status.lastError ? `, lastError=${status.lastError}` : ""}`,
9279
+ `\u0441\u0442\u0430\u0434\u0438\u044F "${stage.label}" (${cfg.stage}), ${stage.description}`,
9280
+ `stage defaults: ignoreChance=${stage.defaults.ignoreChance}, replyDelaySec=${stage.defaults.replyDelaySec[0]}-${stage.defaults.replyDelaySec[1]}`,
9281
+ `privacy=${cfg.privacy ?? "owner-only"}, ownerId=${cfg.ownerId ?? "\u2014"}, ignoreTendency=${cfg.ignoreTendency ?? 35}`,
9282
+ `sleep=${cfg.sleepFrom}:00-${cfg.sleepTo}:00, nightWakeChance=${cfg.nightWakeChance}`,
9283
+ `communication=${cfg.communication ? JSON.stringify(cfg.communication) : "default"}, vibe=${cfg.vibe ?? "\u2014"}`,
9284
+ `llm=${cfg.llm.presetId}/${cfg.llm.model} (${cfg.llm.proto}), telegram=${cfg.mode ?? "bot"}, useWSS=${cfg.telegram.useWSS ?? true}`,
9285
+ `busySchedule=${cfg.busySchedule?.length ? JSON.stringify(cfg.busySchedule).slice(0, 1e3) : "[]"}`,
9286
+ scoreLine.trim()
9287
+ ].filter(Boolean).join("\n");
9288
+ const ctxPrompt = [
9289
+ relevantKnowledge,
9290
+ `\u041A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0444\u0438\u043B\u044F:
9291
+ ${runtimeContext}`,
9292
+ memoryContext,
9293
+ recentLogs ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F runtime'\u0430:
9294
+ ${recentLogs.slice(-2500)}` : ""
9295
+ ].filter(Boolean).join("\n\n");
8210
9296
  const llm = makeLLM(cfg.llm);
8211
9297
  const messages = [
8212
9298
  { role: "system", content: ASSISTANT_SYSTEM },
@@ -8275,7 +9361,19 @@ var ALLOWED_FIELDS = /* @__PURE__ */ new Set([
8275
9361
  "communication.initiative",
8276
9362
  "communication.lifeSharing"
8277
9363
  ]);
8278
- var ALLOWED_MEMORY = /* @__PURE__ */ new Set(["persona.md", "speech.md", "boundaries.md", "communication.md", "long-term.md"]);
9364
+ var ALLOWED_MEMORY = /* @__PURE__ */ new Set([
9365
+ "persona.md",
9366
+ "speech.md",
9367
+ "boundaries.md",
9368
+ "communication.md",
9369
+ "long-term.md",
9370
+ "memory/long-term.md",
9371
+ "memory/facts.md",
9372
+ "memory/uncertain.md",
9373
+ "time/promises.md",
9374
+ "time/open-loops.md",
9375
+ "relationship/timeline.md"
9376
+ ]);
8279
9377
  async function applyTool(cfg, call) {
8280
9378
  switch (call.tool) {
8281
9379
  case "set_field": {
@@ -8290,12 +9388,14 @@ async function applyTool(cfg, call) {
8290
9388
  const stage = String(call.args?.stage ?? "");
8291
9389
  const found = STAGE_PRESETS.find((s) => s.id === stage);
8292
9390
  if (!found) return { changed: false, message: `unknown stage: ${stage}. \u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435: ${STAGE_PRESETS.map((s) => s.id).join(", ")}` };
9391
+ const prevStage = cfg.stage;
8293
9392
  cfg.stage = stage;
8294
9393
  try {
8295
9394
  const rel = await readRelationship(cfg.slug);
8296
9395
  await writeRelationship(cfg.slug, { ...rel, stage });
8297
9396
  } catch {
8298
9397
  }
9398
+ await maybeAdvanceRelationshipTimeline(cfg, prevStage, stage);
8299
9399
  return { changed: true, message: `stage = ${stage} (${found.label})` };
8300
9400
  }
8301
9401
  case "set_communication_preset": {
@@ -8437,8 +9537,34 @@ ${preset.description}
8437
9537
  return { changed: false, message: `unknown tool: ${call.tool}` };
8438
9538
  }
8439
9539
  }
8440
- function setNested(obj, path12, value) {
8441
- const parts = path12.split(".");
9540
+ function renderAssistantMemoryContext(parts) {
9541
+ const sections = [
9542
+ ["persona.md", parts.persona],
9543
+ ["speech.md", parts.speech],
9544
+ ["communication.md", parts.communication],
9545
+ ["boundaries.md", parts.boundaries],
9546
+ ["memory/facts.md", parts.facts],
9547
+ ["memory/uncertain.md", parts.uncertain],
9548
+ ["memory/long-term.md", parts.longTerm],
9549
+ ["relationship/timeline.md", parts.timeline],
9550
+ ["time/open-loops.md", parts.openLoops],
9551
+ ["time/promises.md", parts.promises]
9552
+ ].map(([name, text]) => renderContextSection(name, text)).filter(Boolean);
9553
+ return sections.length ? `\u041F\u0430\u043C\u044F\u0442\u044C \u0438 \u0444\u0430\u0439\u043B\u044B \u043F\u0440\u043E\u0444\u0438\u043B\u044F:
9554
+ ${sections.join("\n\n")}` : "";
9555
+ }
9556
+ function renderContextSection(name, text) {
9557
+ const clean = text.trim();
9558
+ if (!clean) return "";
9559
+ return `## ${name}
9560
+ ${tail(clean, 1400)}`;
9561
+ }
9562
+ function tail(text, limit) {
9563
+ if (text.length <= limit) return text;
9564
+ return text.slice(-limit);
9565
+ }
9566
+ function setNested(obj, path13, value) {
9567
+ const parts = path13.split(".");
8442
9568
  let cur = obj;
8443
9569
  for (let i = 0; i < parts.length - 1; i++) {
8444
9570
  const p = parts[i];
@@ -8457,14 +9583,14 @@ var DEFAULT_PROXY = "https://tgproxy.girl-agent.com";
8457
9583
  function proxyUrl() {
8458
9584
  return process.env.GIRL_AGENT_AUTH_PROXY ?? DEFAULT_PROXY;
8459
9585
  }
8460
- async function post(path12, body) {
8461
- const res = await fetch(`${proxyUrl()}${path12}`, {
9586
+ async function post(path13, body) {
9587
+ const res = await fetch(`${proxyUrl()}${path13}`, {
8462
9588
  method: "POST",
8463
9589
  headers: { "Content-Type": "application/json" },
8464
9590
  body: JSON.stringify(body)
8465
9591
  });
8466
9592
  const data = await res.json();
8467
- if (!res.ok) throw new Error(data.error ?? `proxy ${path12} failed (${res.status})`);
9593
+ if (!res.ok) throw new Error(data.error ?? `proxy ${path13} failed (${res.status})`);
8468
9594
  return data;
8469
9595
  }
8470
9596
  function remoteSendCode(phone) {
@@ -8851,8 +9977,8 @@ init_esm_shims();
8851
9977
  init_llm2();
8852
9978
  init_stages();
8853
9979
  init_communication();
8854
- import fs10 from "fs/promises";
8855
- import path11 from "path";
9980
+ import fs11 from "fs/promises";
9981
+ import path12 from "path";
8856
9982
  import os6 from "os";
8857
9983
  init_md();
8858
9984
  init_runtime();
@@ -8935,7 +10061,7 @@ async function runServer(rawArgv) {
8935
10061
  }
8936
10062
  if (!args.yes) {
8937
10063
  process.stderr.write(`\u043F\u0440\u043E\u0444\u0438\u043B\u044C \u041D\u0415 \u0443\u0434\u0430\u043B\u0451\u043D: \u0434\u043E\u0431\u0430\u0432\u044C --yes \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F.
8938
- \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0435\u043D\u043E: ${path11.join(DATA_ROOT, args.profile)}
10064
+ \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0435\u043D\u043E: ${path12.join(DATA_ROOT, args.profile)}
8939
10065
  `);
8940
10066
  process.exit(1);
8941
10067
  }
@@ -8997,7 +10123,7 @@ data dir: ${DATA_ROOT}
8997
10123
  }
8998
10124
  async function persistAndMaybeStart(cfg, args) {
8999
10125
  await writeConfig(cfg);
9000
- process.stderr.write(`[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D: ${path11.join(DATA_ROOT, cfg.slug)}
10126
+ process.stderr.write(`[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D: ${path12.join(DATA_ROOT, cfg.slug)}
9001
10127
  `);
9002
10128
  if (cfg.llm.apiKey || findPreset(cfg.llm.presetId)?.apiKeyRequired === false) {
9003
10129
  try {
@@ -9114,10 +10240,10 @@ function configFromEnv() {
9114
10240
  };
9115
10241
  }
9116
10242
  async function loadConfigFile(file) {
9117
- const abs = path11.isAbsolute(file) ? file : path11.join(process.cwd(), file);
10243
+ const abs = path12.isAbsolute(file) ? file : path12.join(process.cwd(), file);
9118
10244
  let raw;
9119
10245
  try {
9120
- raw = await fs10.readFile(abs, "utf-8");
10246
+ raw = await fs11.readFile(abs, "utf-8");
9121
10247
  } catch (e) {
9122
10248
  process.stderr.write(`[server] \u043D\u0435 \u043C\u043E\u0433\u0443 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C ${abs}: ${e?.message ?? e}
9123
10249
  `);