@thesashadev/girl-agent 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/cli.js +676 -180
  3. package/package.json +1 -1
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
+ """
2398
+
2399
+ \u041E\u043D\u0430 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430:
2400
+ """
2401
+ ${reply ?? ""}
2402
+ """
2153
2403
 
2154
- JSON:
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 {
@@ -8145,7 +8629,7 @@ var ASSISTANT_SYSTEM = `\u0422\u044B \u2014 \u0432\u0441\u0442\u0440\u043E\u0435
8145
8629
  - 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
8630
  - 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
8631
  - 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.
8632
+ \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
8633
  - 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
8634
  - 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
8635
  - 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.
@@ -8275,7 +8759,19 @@ var ALLOWED_FIELDS = /* @__PURE__ */ new Set([
8275
8759
  "communication.initiative",
8276
8760
  "communication.lifeSharing"
8277
8761
  ]);
8278
- var ALLOWED_MEMORY = /* @__PURE__ */ new Set(["persona.md", "speech.md", "boundaries.md", "communication.md", "long-term.md"]);
8762
+ var ALLOWED_MEMORY = /* @__PURE__ */ new Set([
8763
+ "persona.md",
8764
+ "speech.md",
8765
+ "boundaries.md",
8766
+ "communication.md",
8767
+ "long-term.md",
8768
+ "memory/long-term.md",
8769
+ "memory/facts.md",
8770
+ "memory/uncertain.md",
8771
+ "time/promises.md",
8772
+ "time/open-loops.md",
8773
+ "relationship/timeline.md"
8774
+ ]);
8279
8775
  async function applyTool(cfg, call) {
8280
8776
  switch (call.tool) {
8281
8777
  case "set_field": {
@@ -8437,8 +8933,8 @@ ${preset.description}
8437
8933
  return { changed: false, message: `unknown tool: ${call.tool}` };
8438
8934
  }
8439
8935
  }
8440
- function setNested(obj, path12, value) {
8441
- const parts = path12.split(".");
8936
+ function setNested(obj, path13, value) {
8937
+ const parts = path13.split(".");
8442
8938
  let cur = obj;
8443
8939
  for (let i = 0; i < parts.length - 1; i++) {
8444
8940
  const p = parts[i];
@@ -8457,14 +8953,14 @@ var DEFAULT_PROXY = "https://tgproxy.girl-agent.com";
8457
8953
  function proxyUrl() {
8458
8954
  return process.env.GIRL_AGENT_AUTH_PROXY ?? DEFAULT_PROXY;
8459
8955
  }
8460
- async function post(path12, body) {
8461
- const res = await fetch(`${proxyUrl()}${path12}`, {
8956
+ async function post(path13, body) {
8957
+ const res = await fetch(`${proxyUrl()}${path13}`, {
8462
8958
  method: "POST",
8463
8959
  headers: { "Content-Type": "application/json" },
8464
8960
  body: JSON.stringify(body)
8465
8961
  });
8466
8962
  const data = await res.json();
8467
- if (!res.ok) throw new Error(data.error ?? `proxy ${path12} failed (${res.status})`);
8963
+ if (!res.ok) throw new Error(data.error ?? `proxy ${path13} failed (${res.status})`);
8468
8964
  return data;
8469
8965
  }
8470
8966
  function remoteSendCode(phone) {
@@ -8851,8 +9347,8 @@ init_esm_shims();
8851
9347
  init_llm2();
8852
9348
  init_stages();
8853
9349
  init_communication();
8854
- import fs10 from "fs/promises";
8855
- import path11 from "path";
9350
+ import fs11 from "fs/promises";
9351
+ import path12 from "path";
8856
9352
  import os6 from "os";
8857
9353
  init_md();
8858
9354
  init_runtime();
@@ -8935,7 +9431,7 @@ async function runServer(rawArgv) {
8935
9431
  }
8936
9432
  if (!args.yes) {
8937
9433
  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)}
9434
+ \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0435\u043D\u043E: ${path12.join(DATA_ROOT, args.profile)}
8939
9435
  `);
8940
9436
  process.exit(1);
8941
9437
  }
@@ -8997,7 +9493,7 @@ data dir: ${DATA_ROOT}
8997
9493
  }
8998
9494
  async function persistAndMaybeStart(cfg, args) {
8999
9495
  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)}
9496
+ 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
9497
  `);
9002
9498
  if (cfg.llm.apiKey || findPreset(cfg.llm.presetId)?.apiKeyRequired === false) {
9003
9499
  try {
@@ -9114,10 +9610,10 @@ function configFromEnv() {
9114
9610
  };
9115
9611
  }
9116
9612
  async function loadConfigFile(file) {
9117
- const abs = path11.isAbsolute(file) ? file : path11.join(process.cwd(), file);
9613
+ const abs = path12.isAbsolute(file) ? file : path12.join(process.cwd(), file);
9118
9614
  let raw;
9119
9615
  try {
9120
- raw = await fs10.readFile(abs, "utf-8");
9616
+ raw = await fs11.readFile(abs, "utf-8");
9121
9617
  } catch (e) {
9122
9618
  process.stderr.write(`[server] \u043D\u0435 \u043C\u043E\u0433\u0443 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C ${abs}: ${e?.message ?? e}
9123
9619
  `);