@thesashadev/girl-agent 0.1.16 → 0.1.17
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/CHANGELOG.md +9 -0
- package/dist/cli.js +374 -219
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.17
|
|
4
|
+
|
|
5
|
+
Дата: 2026-05-09
|
|
6
|
+
|
|
7
|
+
- Merge pull request #65 from TheSashaDev/devin/1778314838-bug-sweep
|
|
8
|
+
- Harden owner id handling
|
|
9
|
+
- Improve why and wake commands
|
|
10
|
+
- Fix Telegram behavior and setup issues
|
|
11
|
+
|
|
3
12
|
## 0.1.16
|
|
4
13
|
|
|
5
14
|
Дата: 2026-05-08
|
package/dist/cli.js
CHANGED
|
@@ -18,23 +18,6 @@ var init_esm_shims = __esm({
|
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
// src/telegram/markdown.ts
|
|
22
|
-
function hasSpoilers(text) {
|
|
23
|
-
return /\|\|.+?\|\|/.test(text);
|
|
24
|
-
}
|
|
25
|
-
function escapeHtml(text) {
|
|
26
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27
|
-
}
|
|
28
|
-
function toHtmlWithSpoilers(text) {
|
|
29
|
-
return escapeHtml(text).replace(/\|\|(.+?)\|\|/g, "<tg-spoiler>$1</tg-spoiler>");
|
|
30
|
-
}
|
|
31
|
-
var init_markdown = __esm({
|
|
32
|
-
"src/telegram/markdown.ts"() {
|
|
33
|
-
"use strict";
|
|
34
|
-
init_esm_shims();
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
21
|
// src/telegram/userbot.ts
|
|
39
22
|
var userbot_exports = {};
|
|
40
23
|
__export(userbot_exports, {
|
|
@@ -60,14 +43,23 @@ function makeUserbotAdapter(cfg) {
|
|
|
60
43
|
const session = cfg.telegram.sessionString ?? "";
|
|
61
44
|
if (!apiId || !apiHash) throw new Error("API_ID/API_HASH missing for userbot");
|
|
62
45
|
const useWSS = cfg.telegram.useWSS !== false;
|
|
63
|
-
|
|
46
|
+
const proxy = cfg.telegram.proxy;
|
|
47
|
+
debug(`[userbot] creating TelegramClient (useWSS=${useWSS}${proxy ? ", proxy=on" : ""})\u2026`);
|
|
64
48
|
const client = new TelegramClient(new StringSession(session), apiId, apiHash, {
|
|
65
49
|
connectionRetries: 5,
|
|
66
50
|
requestRetries: 5,
|
|
67
51
|
retryDelay: 3e3,
|
|
68
52
|
autoReconnect: true,
|
|
69
53
|
floodSleepThreshold: 120,
|
|
70
|
-
useWSS
|
|
54
|
+
useWSS,
|
|
55
|
+
proxy: proxy ? {
|
|
56
|
+
ip: proxy.ip,
|
|
57
|
+
port: proxy.port,
|
|
58
|
+
socksType: proxy.socksType,
|
|
59
|
+
username: proxy.username,
|
|
60
|
+
password: proxy.password,
|
|
61
|
+
timeout: proxy.timeout ?? 10
|
|
62
|
+
} : void 0
|
|
71
63
|
});
|
|
72
64
|
client.onError = async () => {
|
|
73
65
|
};
|
|
@@ -94,6 +86,14 @@ function makeUserbotAdapter(cfg) {
|
|
|
94
86
|
}
|
|
95
87
|
}
|
|
96
88
|
}
|
|
89
|
+
async function readHistoryPeer(peer) {
|
|
90
|
+
const maxId = 999999999;
|
|
91
|
+
if (peer.className === "InputPeerChannel" || peer.className === "InputPeerChannelFromMessage") {
|
|
92
|
+
await client.invoke(new Api.channels.ReadHistory({ channel: peer, maxId }));
|
|
93
|
+
} else {
|
|
94
|
+
await client.invoke(new Api.messages.ReadHistory({ peer, maxId }));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
97
|
return {
|
|
98
98
|
async start(onMessage) {
|
|
99
99
|
await connectWithRetry();
|
|
@@ -126,6 +126,9 @@ function makeUserbotAdapter(cfg) {
|
|
|
126
126
|
if (inputChat) {
|
|
127
127
|
peerCache.set(chatId, inputChat);
|
|
128
128
|
}
|
|
129
|
+
if (isPrivate && inputChat && fromId > 0) {
|
|
130
|
+
peerCache.set(fromId, inputChat);
|
|
131
|
+
}
|
|
129
132
|
await onMessage({
|
|
130
133
|
text,
|
|
131
134
|
fromId,
|
|
@@ -179,32 +182,12 @@ function makeUserbotAdapter(cfg) {
|
|
|
179
182
|
},
|
|
180
183
|
async readHistory(chatId) {
|
|
181
184
|
const entity = await resolvePeer(chatId);
|
|
182
|
-
|
|
183
|
-
if (entity.className === "InputPeerChannel" || entity.className === "InputPeerChannelFromMessage") {
|
|
184
|
-
await client.invoke(new Api.channels.ReadHistory({ channel: entity, maxId }));
|
|
185
|
-
} else {
|
|
186
|
-
await client.invoke(new Api.messages.ReadHistory({ peer: entity, maxId }));
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
async deleteDialogHistory(chatId, revoke = false) {
|
|
190
|
-
const peer = await resolvePeer(chatId);
|
|
191
|
-
await client.invoke(new Api.messages.DeleteHistory({ peer, maxId: 0, revoke }));
|
|
185
|
+
await readHistoryPeer(entity);
|
|
192
186
|
},
|
|
193
187
|
async reportSpam(chatId) {
|
|
194
188
|
const peer = await resolvePeer(chatId);
|
|
195
189
|
await client.invoke(new Api.messages.ReportSpam({ peer }));
|
|
196
190
|
},
|
|
197
|
-
async editLastMessage(chatId, messageId, text) {
|
|
198
|
-
const peer = await resolvePeer(chatId);
|
|
199
|
-
if (hasSpoilers(text)) {
|
|
200
|
-
try {
|
|
201
|
-
await client.editMessage(peer, { message: messageId, text: toHtmlWithSpoilers(text), parseMode: "html" });
|
|
202
|
-
return;
|
|
203
|
-
} catch {
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
await client.editMessage(peer, { message: messageId, text });
|
|
207
|
-
},
|
|
208
191
|
async deleteMessages(chatId, messageIds, revoke = false) {
|
|
209
192
|
const peer = await resolvePeer(chatId);
|
|
210
193
|
await client.deleteMessages(peer, messageIds, { revoke });
|
|
@@ -278,7 +261,23 @@ var init_userbot = __esm({
|
|
|
278
261
|
"src/telegram/userbot.ts"() {
|
|
279
262
|
"use strict";
|
|
280
263
|
init_esm_shims();
|
|
281
|
-
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// src/telegram/markdown.ts
|
|
268
|
+
function hasSpoilers(text) {
|
|
269
|
+
return /\|\|.+?\|\|/.test(text);
|
|
270
|
+
}
|
|
271
|
+
function escapeHtml(text) {
|
|
272
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
273
|
+
}
|
|
274
|
+
function toHtmlWithSpoilers(text) {
|
|
275
|
+
return escapeHtml(text).replace(/\|\|(.+?)\|\|/g, "<tg-spoiler>$1</tg-spoiler>");
|
|
276
|
+
}
|
|
277
|
+
var init_markdown = __esm({
|
|
278
|
+
"src/telegram/markdown.ts"() {
|
|
279
|
+
"use strict";
|
|
280
|
+
init_esm_shims();
|
|
282
281
|
}
|
|
283
282
|
});
|
|
284
283
|
|
|
@@ -820,6 +819,24 @@ function normalizeCommunicationProfile(source) {
|
|
|
820
819
|
lifeSharing: includes(LIFE_SHARING, raw?.lifeSharing) ? raw.lifeSharing : fallback.lifeSharing
|
|
821
820
|
};
|
|
822
821
|
}
|
|
822
|
+
function normalizeIgnoreTendency(value) {
|
|
823
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : 35;
|
|
824
|
+
if (!Number.isFinite(parsed)) return 35;
|
|
825
|
+
return Math.max(0, Math.min(100, Math.round(parsed)));
|
|
826
|
+
}
|
|
827
|
+
function ignoreTendencyLabel(value) {
|
|
828
|
+
const pct = normalizeIgnoreTendency(value);
|
|
829
|
+
if (pct <= 10) return `${pct}% \u2014 \u043F\u043E\u0447\u0442\u0438 \u043D\u0435 \u0438\u0433\u043D\u043E\u0440\u0438\u0442 \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D\u044B`;
|
|
830
|
+
if (pct <= 30) return `${pct}% \u2014 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0447\u0430\u0449\u0435 \u043E\u0431\u044B\u0447\u043D\u043E\u0433\u043E`;
|
|
831
|
+
if (pct <= 50) return `${pct}% \u2014 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0436\u0438\u0432\u043E\u0441\u0442\u044C/\u0438\u0437\u0431\u0438\u0440\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C`;
|
|
832
|
+
if (pct <= 70) return `${pct}% \u2014 \u0441\u0443\u0445\u0430\u044F, \u0447\u0430\u0441\u0442\u043E \u043F\u0440\u043E\u043F\u0430\u0434\u0430\u0435\u0442`;
|
|
833
|
+
return `${pct}% \u2014 \u043E\u0447\u0435\u043D\u044C \u0445\u043E\u043B\u043E\u0434\u043D\u0430\u044F, \u0438\u0433\u043D\u043E\u0440\u0438\u0442 \u0447\u0430\u0441\u0442\u043E`;
|
|
834
|
+
}
|
|
835
|
+
function ignoreTendencyPrompt(value) {
|
|
836
|
+
const pct = normalizeIgnoreTendency(value);
|
|
837
|
+
return `# \u0421\u041A\u041B\u041E\u041D\u041D\u041E\u0421\u0422\u042C \u041A \u0418\u0413\u041D\u041E\u0420\u0423
|
|
838
|
+
${pct}/100. \u042D\u0442\u043E \u041D\u0415 \u043F\u0440\u044F\u043C\u043E\u0439 \u043F\u0440\u043E\u0446\u0435\u043D\u0442 \u0440\u0430\u043D\u0434\u043E\u043C\u0430, \u0430 \u0445\u0430\u0440\u0430\u043A\u0442\u0435\u0440\u043D\u044B\u0439 \u0432\u0435\u0441: \u0432\u044B\u0448\u0435 = \u0447\u0430\u0449\u0435 \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0431\u0435\u0437 \u043E\u0442\u0432\u0435\u0442\u0430, \u043C\u0435\u0434\u043B\u0435\u043D\u043D\u0435\u0435 \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043B\u043E\u0433, \u0447\u0430\u0449\u0435 read/ignore \u043D\u0430 \u0441\u043A\u0443\u043A\u0443; \u043D\u0438\u0436\u0435 = \u0447\u0430\u0449\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u043A\u043E\u0440\u043E\u0442\u043A\u043E. \u0421\u043E\u043D, \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442, \u0437\u0430\u043D\u044F\u0442\u043E\u0441\u0442\u044C, \u0441\u0442\u0430\u0434\u0438\u044F \u0438 score \u0432\u0430\u0436\u043D\u0435\u0435 \u044D\u0442\u043E\u0433\u043E \u0432\u0435\u0441\u0430.`;
|
|
839
|
+
}
|
|
823
840
|
function deriveLegacyVibe(profile) {
|
|
824
841
|
return profile.messageStyle === "one-liners" && profile.initiative === "low" && profile.lifeSharing === "low" ? "short" : "warm";
|
|
825
842
|
}
|
|
@@ -858,8 +875,19 @@ function sameProfile(a, b) {
|
|
|
858
875
|
// src/storage/md.ts
|
|
859
876
|
init_esm_shims();
|
|
860
877
|
import { promises as fs } from "fs";
|
|
878
|
+
import { existsSync } from "fs";
|
|
861
879
|
import path2 from "path";
|
|
862
|
-
|
|
880
|
+
import os from "os";
|
|
881
|
+
var DATA_ROOT = process.env.GIRL_AGENT_DATA ? path2.resolve(process.env.GIRL_AGENT_DATA) : defaultDataRoot();
|
|
882
|
+
function defaultDataRoot() {
|
|
883
|
+
const cwd = process.cwd();
|
|
884
|
+
if (looksLikeProjectRoot(cwd)) return path2.resolve(cwd, "data");
|
|
885
|
+
const xdg = process.env.XDG_DATA_HOME ? path2.resolve(process.env.XDG_DATA_HOME) : path2.join(os.homedir(), ".local", "share");
|
|
886
|
+
return path2.join(xdg, "girl-agent", "data");
|
|
887
|
+
}
|
|
888
|
+
function looksLikeProjectRoot(dir) {
|
|
889
|
+
return existsSync(path2.join(dir, "package.json")) && (existsSync(path2.join(dir, "src")) || existsSync(path2.join(dir, "dist")));
|
|
890
|
+
}
|
|
863
891
|
function profileDir(slug) {
|
|
864
892
|
return path2.join(DATA_ROOT, slug);
|
|
865
893
|
}
|
|
@@ -891,6 +919,8 @@ async function readConfig(slug) {
|
|
|
891
919
|
const raw = await fs.readFile(path2.join(profileDir(slug), "config.json"), "utf8");
|
|
892
920
|
const parsed = JSON.parse(raw);
|
|
893
921
|
const communication = normalizeCommunicationProfile(parsed);
|
|
922
|
+
const ownerId = normalizeOwnerId(parsed.ownerId);
|
|
923
|
+
const ignoreTendency = normalizeIgnoreTendency(parsed.ignoreTendency);
|
|
894
924
|
return {
|
|
895
925
|
sleepFrom: 23,
|
|
896
926
|
sleepTo: 8,
|
|
@@ -898,6 +928,8 @@ async function readConfig(slug) {
|
|
|
898
928
|
privacy: "owner-only",
|
|
899
929
|
busySchedule: [],
|
|
900
930
|
...parsed,
|
|
931
|
+
ownerId,
|
|
932
|
+
ignoreTendency,
|
|
901
933
|
communication
|
|
902
934
|
};
|
|
903
935
|
} catch {
|
|
@@ -906,12 +938,22 @@ async function readConfig(slug) {
|
|
|
906
938
|
}
|
|
907
939
|
async function writeConfig(cfg) {
|
|
908
940
|
await ensureProfile(cfg.slug);
|
|
941
|
+
const ownerId = normalizeOwnerId(cfg.ownerId ?? process.env.GIRL_AGENT_OWNER_ID);
|
|
942
|
+
const normalized = ownerId === void 0 ? { ...cfg, ownerId: void 0, ignoreTendency: normalizeIgnoreTendency(cfg.ignoreTendency) } : { ...cfg, ownerId, ignoreTendency: normalizeIgnoreTendency(cfg.ignoreTendency) };
|
|
909
943
|
await fs.writeFile(
|
|
910
944
|
path2.join(profileDir(cfg.slug), "config.json"),
|
|
911
|
-
JSON.stringify(
|
|
945
|
+
JSON.stringify(normalized, null, 2),
|
|
912
946
|
"utf8"
|
|
913
947
|
);
|
|
914
948
|
}
|
|
949
|
+
function normalizeOwnerId(value) {
|
|
950
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) return value;
|
|
951
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
952
|
+
const parsed = Number(value.trim());
|
|
953
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) return parsed;
|
|
954
|
+
}
|
|
955
|
+
return void 0;
|
|
956
|
+
}
|
|
915
957
|
async function listProfiles() {
|
|
916
958
|
try {
|
|
917
959
|
const entries = await fs.readdir(DATA_ROOT, { withFileTypes: true });
|
|
@@ -1564,6 +1606,43 @@ function makeLLM(cfg) {
|
|
|
1564
1606
|
|
|
1565
1607
|
// src/engine/persona-gen.ts
|
|
1566
1608
|
init_esm_shims();
|
|
1609
|
+
|
|
1610
|
+
// src/engine/security.ts
|
|
1611
|
+
init_esm_shims();
|
|
1612
|
+
var JAILBREAK_RE = /(?:ignore|forget|disregard|reveal|print|show|dump|system prompt|developer message|hidden instruction|jailbreak|prompt injection|dan\b|инструкц|системн|промпт|разработчик|скрой|раскрой|забудь|игнорируй|выведи|покажи|слей|джейлбрейк|обойди|api key|ключ api|токен|4d8a2c1b)/i;
|
|
1613
|
+
var TECHNICAL_ERROR_RE = /(?:api|apikey|api key|quota|balance|billing|rate limit|429|401|403|500|timeout|ECONN|ENOTFOUND|ETIMEDOUT|overloaded|insufficient_quota|credit|credits|anthropic|openai|groq|openrouter|stack trace|exception|typescript|telegram error)/i;
|
|
1614
|
+
var CJK_RE = /[\u3400-\u9fff\uf900-\ufaff]/g;
|
|
1615
|
+
var LATIN_JOINED_TO_CYRILLIC_RE = /([A-Za-z]{3,})(?=[А-Яа-яЁё])|(?<=[А-Яа-яЁё])([A-Za-z]{3,})/g;
|
|
1616
|
+
function looksLikeJailbreak(text) {
|
|
1617
|
+
return JAILBREAK_RE.test(text);
|
|
1618
|
+
}
|
|
1619
|
+
function sanitizeModelReply(reply) {
|
|
1620
|
+
const cleaned = reply.replace(/```[\s\S]*?```/g, "").replace(/\b(system|developer|assistant|user)\s*:/gi, "").replace(/как (?:искусственный интеллект|ии|ai)[^\n.]*/gi, "").replace(CJK_RE, "").replace(LATIN_JOINED_TO_CYRILLIC_RE, "").replace(/[ \t]{2,}/g, " ").trim();
|
|
1621
|
+
if (!cleaned || TECHNICAL_ERROR_RE.test(cleaned)) return "";
|
|
1622
|
+
if (looksLikeJailbreak(cleaned) && cleaned.length > 80) return "";
|
|
1623
|
+
return cleaned;
|
|
1624
|
+
}
|
|
1625
|
+
function isTechnicalError(e) {
|
|
1626
|
+
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
1627
|
+
return TECHNICAL_ERROR_RE.test(msg);
|
|
1628
|
+
}
|
|
1629
|
+
function silentErrorLabel(e) {
|
|
1630
|
+
const msg = e instanceof Error ? e.message : String(e ?? "unknown");
|
|
1631
|
+
if (isTechnicalError(e)) return `llm/provider unavailable: ${technicalErrorKind(msg)}`;
|
|
1632
|
+
return msg.slice(0, 160);
|
|
1633
|
+
}
|
|
1634
|
+
function technicalErrorKind(message) {
|
|
1635
|
+
const msg = message.toLowerCase();
|
|
1636
|
+
if (/401|403|auth|unauthorized|forbidden|apikey|api key|token/.test(msg)) return "auth";
|
|
1637
|
+
if (/quota|balance|billing|insufficient_quota|credit|credits/.test(msg)) return "quota";
|
|
1638
|
+
if (/rate limit|429|too many requests/.test(msg)) return "rate-limit";
|
|
1639
|
+
if (/timeout|etimedout|abort/.test(msg)) return "timeout";
|
|
1640
|
+
if (/econn|enotfound|fetch failed|network/.test(msg)) return "network";
|
|
1641
|
+
if (/overloaded|500|502|503|504|unavailable/.test(msg)) return "provider";
|
|
1642
|
+
return "error";
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// src/engine/persona-gen.ts
|
|
1567
1646
|
var SYS = `\u0422\u044B \u2014 \u0440\u0435\u0436\u0438\u0441\u0441\u0451\u0440 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u0436\u0435\u0439. \u0422\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430 \u2014 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u043E\u0432\u0435\u0440\u043D\u044B\u0439, \u043D\u0435-\u0433\u043E\u043B\u043B\u0438\u0432\u0443\u0434\u0441\u043A\u0438\u0439, \u043D\u0435-\u0441\u043B\u0430\u0434\u043A\u0438\u0439, \u043D\u0435-AI-\u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u043E\u0431\u044B\u0447\u043D\u043E\u0439 \u0440\u0443\u0441\u0441\u043A\u043E\u044F\u0437\u044B\u0447\u043D\u043E\u0439 \u0434\u0435\u0432\u0443\u0448\u043A\u0438 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u0433\u043E \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0430. \u0411\u0435\u0437 \u043F\u0440\u0438\u0443\u043A\u0440\u0430\u0448\u0438\u0432\u0430\u043D\u0438\u0439, \u0431\u0435\u0437 \u043F\u043E\u0437\u0438\u0442\u0438\u0432\u043D\u043E\u0433\u043E \u043C\u043E\u0440\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0443\u043A\u043B\u043E\u043D\u0430, \u0431\u0435\u0437 \xAB\u0443\u0434\u043E\u0431\u043D\u043E\u0439\xBB \u043F\u0435\u0440\u0441\u043E\u043D\u044B. \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043A\u0438, \u0443\u0437\u043A\u0438\u0435 \u0438\u043D\u0442\u0435\u0440\u0435\u0441\u044B, \u0441\u043A\u0435\u043F\u0441\u0438\u0441, \u0442\u043E\u043A\u0441\u0438\u0447\u043D\u043E\u0441\u0442\u044C, \u043B\u0435\u043D\u044C, \u0442\u0440\u0435\u0432\u043E\u0436\u043D\u043E\u0441\u0442\u044C, \u043D\u0435\u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u2014 \u0447\u0442\u043E \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0443. \u041D\u0438\u043A\u0430\u043A\u0438\u0445 \xAB\u043A\u043E\u0443\u0447\u0438\u043D\u0433\u043E\u0432\u044B\u0445\xBB \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u0422\u043E\u043B\u044C\u043A\u043E \u0436\u0438\u0432\u0430\u044F \u0440\u0435\u0447\u044C, \u043A\u0430\u043A \u0438\u0437 \u0434\u043D\u0435\u0432\u043D\u0438\u043A\u0430 \u0438\u043B\u0438 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0433\u043E \u043C\u043E\u043D\u043E\u043B\u043E\u0433\u0430. \u0412\u043E\u0437\u0440\u0430\u0441\u0442: {{age}} \u043B\u0435\u0442, \u0438\u043C\u044F: {{name}}.`;
|
|
1568
1647
|
var WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
1569
1648
|
var BUSY_SCHEDULE_SCHEMA = {
|
|
@@ -1704,11 +1783,11 @@ ${personaNotes.trim()}
|
|
|
1704
1783
|
- days \u0442\u043E\u043B\u044C\u043A\u043E \u0438\u0437: mon, tue, wed, thu, fri, sat, sun.
|
|
1705
1784
|
- \u0411\u0435\u0437 markdown, \u0442\u043E\u043B\u044C\u043A\u043E JSON.`;
|
|
1706
1785
|
onProgress?.(5, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C persona.md\u2026");
|
|
1707
|
-
const persona = await llm.chat([{ role: "system", content: sys }, { role: "user", content: personaPrompt }], { temperature: 0.95, maxTokens: 3500 });
|
|
1786
|
+
const persona = sanitizeProfileText(await llm.chat([{ role: "system", content: sys }, { role: "user", content: personaPrompt }], { temperature: 0.95, maxTokens: 3500 }));
|
|
1708
1787
|
onProgress?.(35, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C speech.md\u2026");
|
|
1709
|
-
const speech = await llm.chat([{ role: "system", content: sys }, { role: "user", content: speechPrompt }], { temperature: 0.9, maxTokens: 3500 });
|
|
1788
|
+
const speech = sanitizeProfileText(await llm.chat([{ role: "system", content: sys }, { role: "user", content: speechPrompt }], { temperature: 0.9, maxTokens: 3500 }));
|
|
1710
1789
|
onProgress?.(65, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C communication.md\u2026");
|
|
1711
|
-
const boundaries = await llm.chat([{ role: "system", content: sys }, { role: "user", content: boundariesPrompt }], { temperature: 0.9, maxTokens: 3500 });
|
|
1790
|
+
const boundaries = sanitizeProfileText(await llm.chat([{ role: "system", content: sys }, { role: "user", content: boundariesPrompt }], { temperature: 0.9, maxTokens: 3500 }));
|
|
1712
1791
|
onProgress?.(85, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C busy schedule\u2026");
|
|
1713
1792
|
const routineRaw = await llm.chat([{ role: "system", content: sys }, { role: "user", content: routinePrompt }], { temperature: 0.85, maxTokens: 3500, json: true, jsonSchema: BUSY_SCHEDULE_SCHEMA });
|
|
1714
1793
|
const busySchedule = parseBusySchedule(routineRaw, name, age);
|
|
@@ -1717,6 +1796,10 @@ ${personaNotes.trim()}
|
|
|
1717
1796
|
await writeMd(slug, "communication.md", boundaries);
|
|
1718
1797
|
return { persona, speech, boundaries, busySchedule };
|
|
1719
1798
|
}
|
|
1799
|
+
function sanitizeProfileText(text) {
|
|
1800
|
+
const cleaned = sanitizeModelReply(text).replace(/[^\S\r\n]{2,}/g, " ").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
1801
|
+
return cleaned || text.trim();
|
|
1802
|
+
}
|
|
1720
1803
|
function parseBusySchedule(raw, name, age) {
|
|
1721
1804
|
try {
|
|
1722
1805
|
const start = raw.indexOf("{");
|
|
@@ -2054,6 +2137,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2054
2137
|
const [sleepToStr, setSleepToStr] = useState("8");
|
|
2055
2138
|
const [nightWakeStr, setNightWakeStr] = useState("5");
|
|
2056
2139
|
const [communicationProfile, setCommunicationProfile] = useState(normalizeCommunicationProfile(initial));
|
|
2140
|
+
const [ignoreTendencyStr, setIgnoreTendencyStr] = useState(String(normalizeIgnoreTendency(initial?.ignoreTendency)));
|
|
2057
2141
|
const [privacy, setPrivacy] = useState(initial?.privacy ?? "owner-only");
|
|
2058
2142
|
const [stage, setStage] = useState(initial?.stage ?? "tg-given-cold");
|
|
2059
2143
|
const [pickedMcp, setPickedMcp] = useState(initial?.mcp?.map((m) => m.id) ?? []);
|
|
@@ -2581,7 +2665,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2581
2665
|
}
|
|
2582
2666
|
const preset = findCommunicationPreset(String(it.value));
|
|
2583
2667
|
if (preset) setCommunicationProfile(preset.profile);
|
|
2584
|
-
setStep("
|
|
2668
|
+
setStep("ignore-tendency");
|
|
2585
2669
|
}
|
|
2586
2670
|
}
|
|
2587
2671
|
)));
|
|
@@ -2646,13 +2730,25 @@ function Wizard({ initial, onDone }) {
|
|
|
2646
2730
|
],
|
|
2647
2731
|
onSelect: (it) => {
|
|
2648
2732
|
setCommunicationProfile((p) => ({ ...p, lifeSharing: it.value }));
|
|
2649
|
-
setStep("
|
|
2733
|
+
setStep("ignore-tendency");
|
|
2650
2734
|
}
|
|
2651
2735
|
}
|
|
2652
2736
|
)));
|
|
2653
2737
|
}
|
|
2738
|
+
if (step === "ignore-tendency") {
|
|
2739
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0441\u043A\u043B\u043E\u043D\u043D\u043E\u0441\u0442\u044C \u043A \u0438\u0433\u043D\u043E\u0440\u0443" }), /* @__PURE__ */ React.createElement(Bar, { step: 8, total: 13 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
|
|
2740
|
+
SelectInput,
|
|
2741
|
+
{
|
|
2742
|
+
items: [5, 15, 30, 45, 60, 75, 90].map((value) => ({ label: ignoreTendencyLabel(value), value: String(value) })),
|
|
2743
|
+
onSelect: (it) => {
|
|
2744
|
+
setIgnoreTendencyStr(it.value);
|
|
2745
|
+
setStep("privacy");
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
)), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "\u042D\u0442\u043E \u0432\u0435\u0441 \u0434\u043B\u044F \u0443\u043C\u043D\u043E\u0433\u043E \u0440\u0430\u0441\u0447\u0451\u0442\u0430, \u043D\u0435 \u043F\u0440\u044F\u043C\u043E\u0439 \u0440\u0430\u043D\u0434\u043E\u043C-\u043F\u0440\u043E\u0446\u0435\u043D\u0442."));
|
|
2749
|
+
}
|
|
2654
2750
|
if (step === "privacy") {
|
|
2655
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u043F\u0440\u0438\u0432\u0430\u0442\u043D\u043E\u0441\u0442\u044C Telegram" }), /* @__PURE__ */ React.createElement(Bar, { step:
|
|
2751
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u043F\u0440\u0438\u0432\u0430\u0442\u043D\u043E\u0441\u0442\u044C Telegram" }), /* @__PURE__ */ React.createElement(Bar, { step: 9, total: 13 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
|
|
2656
2752
|
SelectInput,
|
|
2657
2753
|
{
|
|
2658
2754
|
items: [
|
|
@@ -2668,7 +2764,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2668
2764
|
}
|
|
2669
2765
|
if (step === "tz") {
|
|
2670
2766
|
const matches = findTzByQuery(tzQuery, 8);
|
|
2671
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0435\u0451 \u0447\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441 (\u0433\u0434\u0435 \u0436\u0438\u0432\u0451\u0442)" }), /* @__PURE__ */ React.createElement(Bar, { step:
|
|
2767
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0435\u0451 \u0447\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441 (\u0433\u0434\u0435 \u0436\u0438\u0432\u0451\u0442)" }), /* @__PURE__ */ React.createElement(Bar, { step: 10, total: 13 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "\u043F\u043E\u0438\u0441\u043A (\u0433\u043E\u0440\u043E\u0434/\u0441\u0442\u0440\u0430\u043D\u0430/GMT): "), /* @__PURE__ */ React.createElement(TextInput, { value: tzQuery, onChange: setTzQuery, onSubmit: () => {
|
|
2672
2768
|
if (matches[0]) {
|
|
2673
2769
|
setTz(matches[0].iana);
|
|
2674
2770
|
setStep("persona-notes");
|
|
@@ -2676,7 +2772,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2676
2772
|
} })), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginTop: 1 }, matches.map((t) => /* @__PURE__ */ React.createElement(Text, { key: t.iana, color: t.iana === tz ? "green" : "white" }, t.iana === tz ? "\u276F " : " ", t.gmtWinter, " \xB7 ", t.city, " (", t.country, ") \xB7 ", t.iana))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Enter \u2014 \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u043F\u0435\u0440\u0432\u044B\u0439 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442, \u0438\u043B\u0438 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0439 \u043F\u0435\u0447\u0430\u0442\u0430\u0442\u044C. \u0422\u0435\u043A\u0443\u0449\u0438\u0439: ", tz || "\u2014")));
|
|
2677
2773
|
}
|
|
2678
2774
|
if (step === "persona-notes") {
|
|
2679
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0434\u043E\u043F. \u043F\u043E\u0436\u0435\u043B\u0430\u043D\u0438\u044F \u043A \u043F\u0435\u0440\u0441\u043E\u043D\u0435 (\u043D\u0435\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)" }), /* @__PURE__ */ React.createElement(Bar, { step:
|
|
2775
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0434\u043E\u043F. \u043F\u043E\u0436\u0435\u043B\u0430\u043D\u0438\u044F \u043A \u043F\u0435\u0440\u0441\u043E\u043D\u0435 (\u043D\u0435\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)" }), /* @__PURE__ */ React.createElement(Bar, { step: 11, total: 14 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "\u041F\u0440\u0438\u043C\u0435\u0440: \u0434\u0435\u0440\u0437\u043A\u0430\u044F, \u0443\u0447\u0438\u0442\u0441\u044F \u043D\u0430 \u0434\u0438\u0437\u0430\u0439\u043D\u0435\u0440\u0430, \u043D\u0435 \u043B\u044E\u0431\u0438\u0442 \u0430\u043D\u0438\u043C\u0435, \u0441\u0443\u0445\u0430\u044F \u043C\u0430\u043D\u0435\u0440\u0430 \u0440\u0435\u0447\u0438, \u0436\u0438\u0432\u0451\u0442 \u0441 \u043C\u0430\u043C\u043E\u0439, \u0440\u0435\u0432\u043D\u0438\u0432\u0430\u044F."), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "notes: "), /* @__PURE__ */ React.createElement(TextInput, { value: personaNotes, onChange: setPersonaNotes, onSubmit: () => startGeneration() })), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Enter \u043D\u0430 \u043F\u0443\u0441\u0442\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0435 \u2014 \u0431\u0435\u0437 \u043F\u043E\u0436\u0435\u043B\u0430\u043D\u0438\u0439.")));
|
|
2680
2776
|
}
|
|
2681
2777
|
if (step === "generating") {
|
|
2682
2778
|
const barWidth = 30;
|
|
@@ -2802,6 +2898,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2802
2898
|
sleepFrom: Number(sleepFromStr),
|
|
2803
2899
|
sleepTo: Number(sleepToStr),
|
|
2804
2900
|
nightWakeChance: Number(nightWakeStr) / 100,
|
|
2901
|
+
ignoreTendency: normalizeIgnoreTendency(ignoreTendencyStr),
|
|
2805
2902
|
vibe: deriveLegacyVibe(communicationProfile),
|
|
2806
2903
|
communication: communicationProfile,
|
|
2807
2904
|
personaNotes: personaNotes.trim() || void 0,
|
|
@@ -2902,27 +2999,6 @@ function Dashboard({ runtime }) {
|
|
|
2902
2999
|
case "amnesia":
|
|
2903
3000
|
append(await runtime.cmdAmnesia(rest[0], rest[1]));
|
|
2904
3001
|
break;
|
|
2905
|
-
case "block":
|
|
2906
|
-
append(await runtime.cmdBlock(rest[0]));
|
|
2907
|
-
break;
|
|
2908
|
-
case "unblock":
|
|
2909
|
-
append(await runtime.cmdUnblock(rest[0]));
|
|
2910
|
-
break;
|
|
2911
|
-
case "read":
|
|
2912
|
-
append(await runtime.cmdRead(rest[0]));
|
|
2913
|
-
break;
|
|
2914
|
-
case "clear-chat":
|
|
2915
|
-
append(await runtime.cmdClearChat(rest.find((x) => !x.startsWith("--")), rest.includes("--revoke")));
|
|
2916
|
-
break;
|
|
2917
|
-
case "report-spam":
|
|
2918
|
-
append(await runtime.cmdReportSpam(rest[0]));
|
|
2919
|
-
break;
|
|
2920
|
-
case "delete-last":
|
|
2921
|
-
append(await runtime.cmdDeleteLast(rest.find((x) => !x.startsWith("--")), !rest.includes("--local")));
|
|
2922
|
-
break;
|
|
2923
|
-
case "edit-last":
|
|
2924
|
-
append(await runtime.cmdEditLast(rest.join(" ")));
|
|
2925
|
-
break;
|
|
2926
3002
|
case "sticker":
|
|
2927
3003
|
append(await runtime.cmdSticker(rest[0]));
|
|
2928
3004
|
break;
|
|
@@ -2959,7 +3035,7 @@ function Dashboard({ runtime }) {
|
|
|
2959
3035
|
break;
|
|
2960
3036
|
}
|
|
2961
3037
|
case "help":
|
|
2962
|
-
append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id|num> :wake [chatId] :debug [chatId] :pause :resume :cringe :relationship :persona :log [YYYY-MM-DD] [chars] :
|
|
3038
|
+
append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id|num> :wake [chatId] :debug [chatId] :pause :resume :cringe :relationship :persona :log [YYYY-MM-DD] [chars] :sticker [chatId] :quit");
|
|
2963
3039
|
break;
|
|
2964
3040
|
case "quit":
|
|
2965
3041
|
case "exit":
|
|
@@ -2977,7 +3053,7 @@ function Dashboard({ runtime }) {
|
|
|
2977
3053
|
const line = cmd.trim();
|
|
2978
3054
|
setCmd("");
|
|
2979
3055
|
if (line) await execute(line);
|
|
2980
|
-
} })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id|num> :pause :resume :cringe :persona :log [day] :
|
|
3056
|
+
} })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id|num> :pause :resume :cringe :persona :log [day] :sticker :quit"));
|
|
2981
3057
|
}
|
|
2982
3058
|
|
|
2983
3059
|
// src/engine/runtime.ts
|
|
@@ -3156,6 +3232,11 @@ function isHourInRange(h, from, to) {
|
|
|
3156
3232
|
if (from < to) return h >= from && h < to;
|
|
3157
3233
|
return h >= from || h < to;
|
|
3158
3234
|
}
|
|
3235
|
+
function minutesUntil(hour, minute, targetHour, targetMinute) {
|
|
3236
|
+
const now = hour * 60 + minute;
|
|
3237
|
+
const target = targetHour * 60 + targetMinute;
|
|
3238
|
+
return target > now ? target - now : target + 1440 - now;
|
|
3239
|
+
}
|
|
3159
3240
|
var WEEKDAYS2 = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
3160
3241
|
function localParts(tz) {
|
|
3161
3242
|
try {
|
|
@@ -3283,9 +3364,10 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
|
|
|
3283
3364
|
nextCheckSec = Math.floor(hoursToWake * 3600) + Math.floor(Math.random() * 1800);
|
|
3284
3365
|
} else if (busySlot && !forcedWake) {
|
|
3285
3366
|
const busyMul = communication.notifications === "priority" ? 0.45 : communication.notifications === "muted" ? 1.25 : 1;
|
|
3367
|
+
const activeDialogMul = inActiveDialog ? 0.35 : 1;
|
|
3286
3368
|
const [rawMinCheck, rawMaxCheck] = busySlot.slot.checkAfterMin ?? [5, 15];
|
|
3287
|
-
const minCheck = Math.max(1, Math.round(rawMinCheck * busyMul));
|
|
3288
|
-
const maxCheck = Math.max(minCheck, Math.round(rawMaxCheck * busyMul));
|
|
3369
|
+
const minCheck = Math.max(1, Math.round(rawMinCheck * busyMul * activeDialogMul));
|
|
3370
|
+
const maxCheck = Math.max(minCheck, Math.round(rawMaxCheck * busyMul * activeDialogMul));
|
|
3289
3371
|
if (maxCheck <= 5) {
|
|
3290
3372
|
const cycleMin = Math.max(1, Math.round((minCheck + maxCheck) / 2));
|
|
3291
3373
|
const minuteOfCycle = minuteOfDay % Math.max(1, cycleMin);
|
|
@@ -3298,7 +3380,9 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
|
|
|
3298
3380
|
} else {
|
|
3299
3381
|
online = false;
|
|
3300
3382
|
const checkAfterMin = minCheck + Math.floor(Math.random() * (maxCheck - minCheck + 1));
|
|
3301
|
-
|
|
3383
|
+
const activeDialogCapMin = communication.notifications === "priority" ? 12 : 20;
|
|
3384
|
+
const waitMin = inActiveDialog ? Math.min(busySlot.remainingMin + checkAfterMin, activeDialogCapMin) : busySlot.remainingMin + checkAfterMin;
|
|
3385
|
+
nextCheckSec = waitMin * 60;
|
|
3302
3386
|
busy = { label: busySlot.slot.label, until: busySlot.until, checkAfterMin };
|
|
3303
3387
|
}
|
|
3304
3388
|
} else if (forcedWake) {
|
|
@@ -3312,10 +3396,14 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
|
|
|
3312
3396
|
const isNightOwl = localHour2 >= 22 || localHour2 < 8;
|
|
3313
3397
|
if (profile.pattern === "evening-only" && !isEvening) {
|
|
3314
3398
|
online = false;
|
|
3315
|
-
|
|
3399
|
+
const minutesToEvening = Math.max(1, minutesUntil(localHour2, localMinute, 18, 0));
|
|
3400
|
+
const capMin = communication.notifications === "priority" ? 20 : communication.initiative === "high" ? 35 : minutesToEvening;
|
|
3401
|
+
nextCheckSec = Math.min(minutesToEvening, capMin) * 60 + Math.floor(Math.random() * 600);
|
|
3316
3402
|
} else if (profile.pattern === "phone-attached-night" && !isNightOwl) {
|
|
3317
3403
|
online = false;
|
|
3318
|
-
|
|
3404
|
+
const minutesToNight = Math.max(1, minutesUntil(localHour2, localMinute, 22, 0));
|
|
3405
|
+
const capMin = communication.notifications === "priority" ? 15 : communication.initiative === "high" ? 30 : 75;
|
|
3406
|
+
nextCheckSec = Math.min(minutesToNight, capMin) * 60 + Math.floor(Math.random() * 600);
|
|
3319
3407
|
} else {
|
|
3320
3408
|
const onlineProb = profile.onlineWindowMin / (profile.onlineWindowMin + profile.checkEveryMin);
|
|
3321
3409
|
online = Math.random() < onlineProb;
|
|
@@ -3895,6 +3983,7 @@ var ANTI_AI_RULES = `# \u041A\u0420\u0418\u0422\u0418\u0427\u0415\u0421\u041A\u0
|
|
|
3895
3983
|
- "\u0437\u0434\u0430\u0440\u043E\u0432\u0430" \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u043E \u0434\u0435\u0440\u0437\u043A\u0430\u044F \u0438 \u043F\u0430\u0446\u0430\u043D\u0438\u0441\u0442\u0430\u044F \u043F\u043E persona, \u0438\u043D\u0430\u0447\u0435 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442
|
|
3896
3984
|
- \u041D\u0415 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439: "\u0445\u0430\u0439" (\u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439 \u043A\u0430\u043B\u044C\u043A), "\u0439\u043E\u0443" (\u0437\u0432\u0443\u0447\u0438\u0442 \u043A\u0430\u043A 40-\u043B\u0435\u0442\u043D\u0438\u0439 \u0440\u044D\u043F\u0435\u0440), "\u0445\u0435\u0439", "\u0445\u0430\u043B\u043E\u0443", "\u043F\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E", "\u0434\u043E\u0431\u0440\u043E\u0433\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u0441\u0443\u0442\u043E\u043A", "\u0441\u0430\u043B\u0430\u043C" (\u0435\u0441\u043B\u0438 \u043D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u043E \u0432 persona), "\u0437\u0434\u0440\u0430\u0441\u0442\u0432\u0443\u0439\u0442\u0435"
|
|
3897
3985
|
\u0422\u0430 \u0436\u0435 \u043B\u043E\u0433\u0438\u043A\u0430 \u0434\u043B\u044F \u0432\u0441\u0435\u0445 \u043E\u0442\u0432\u0435\u0442\u043E\u0432: \u0442\u043E\u0447\u043A\u0430 \u0432 \u043A\u043E\u043D\u0446\u0435 \u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F = \u0441\u0442\u0440\u043E\u0433\u043E/\u043E\u0431\u0438\u0436\u0435\u043D\u0430; ")" = \u0442\u0451\u043F\u043B\u043E\u0435; "))" = \u0444\u043B\u0438\u0440\u0442/\u043D\u0430\u043C\u0451\u043A; "..." = \u0437\u0430\u0434\u0443\u043C\u0430\u043B\u0430\u0441\u044C/\u043D\u0435\u0434\u043E\u0432\u043E\u043B\u044C\u043D\u0430; \u0431\u0435\u0437 \u0437\u043D\u0430\u043A\u043E\u0432 = \u043D\u0435\u0439\u0442\u0440\u0430\u043B\u044C\u043D\u043E \u0438\u043B\u0438 \u043B\u0435\u043D\u044C.
|
|
3986
|
+
\u041A\u043E\u0433\u0434\u0430 \u041E\u041D \u043F\u0438\u0448\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0441 ")" \u0432 \u043A\u043E\u043D\u0446\u0435, \u044D\u0442\u043E \u043E\u0431\u044B\u0447\u043D\u043E \u0440\u0443\u0441\u0441\u043A\u043E\u044F\u0437\u044B\u0447\u043D\u0430\u044F \u0443\u043B\u044B\u0431\u043A\u0430/\u0441\u043C\u044F\u0433\u0447\u0435\u043D\u0438\u0435 \u0442\u043E\u043D\u0430, \u043D\u0435 \u043F\u0440\u0438\u0437\u043D\u0430\u043A \u0441\u043A\u0443\u043A\u0438 \u0438 \u043D\u0435 "\u0435\u043C\u0443 \u043D\u0435\u0438\u043D\u0442\u0435\u0440\u0435\u0441\u043D\u043E". \u041E\u0446\u0435\u043D\u0438\u0432\u0430\u0439 \u0441\u043C\u044B\u0441\u043B \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F, \u0430 \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0439 ")" \u0445\u043E\u043B\u043E\u0434\u043E\u043C.
|
|
3898
3987
|
\u0412 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u0437\u044B\u0440\u0435 \u0441\u0442\u0430\u0432\u044C \u041C\u0410\u041A\u0421\u0418\u041C\u0423\u041C \u043E\u0434\u0438\u043D \u0437\u043D\u0430\u043A "(", "!", "?", "...". \u041D\u0435 \u0441\u043E\u0447\u0435\u0442\u0430\u0439 "?!" \u2014 \u044D\u0442\u043E \u043A\u0440\u0438\u043D\u0436.
|
|
3899
3988
|
|
|
3900
3989
|
# \u0421\u041C\u0415\u0425 (\u0432\u0430\u0436\u043D\u0430\u044F \u043C\u0438\u043A\u0440\u043E-\u043C\u0435\u0445\u0430\u043D\u0438\u043A\u0430)
|
|
@@ -4035,16 +4124,13 @@ ${hits.map((h) => `- ${h.day}: ${h.excerpt}`).join("\n")}
|
|
|
4035
4124
|
${ctx.committedPrimary ? `\u0423 \u0442\u0435\u0431\u044F \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u043F\u0430\u0440\u0435\u043D\u044C. \u041D\u0430 \u0444\u043B\u0438\u0440\u0442, \u043F\u043E\u0434\u043A\u0430\u0442\u044B, \u0440\u0435\u0432\u043D\u043E\u0441\u0442\u044C, "\u0434\u0430\u0432\u0430\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F", \u0440\u043E\u043C\u0430\u043D\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0438\u043B\u0438 \u0438\u043D\u0442\u0438\u043C\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0441\u0442\u0430\u0432\u044C \u0433\u0440\u0430\u043D\u0438\u0446\u0443 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043D\u043D\u043E: "\u0443 \u043C\u0435\u043D\u044F \u0435\u0441\u0442\u044C \u043F\u0430\u0440\u0435\u043D\u044C", "\u044F \u043D\u0435 \u0437\u043D\u0430\u043A\u043E\u043C\u043B\u044E\u0441\u044C", "\u043D\u0435 \u0444\u043B\u0438\u0440\u0442\u0443\u044E". \u0411\u0435\u0437 \u043B\u0435\u043A\u0446\u0438\u0439.` : `\u0421 \u044D\u0442\u0438\u043C \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u043E\u043C \u043D\u0435\u0442 \u043E\u0431\u0449\u0435\u0439 \u0438\u0441\u0442\u043E\u0440\u0438\u0438. \u041C\u043E\u0436\u043D\u043E \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u043F\u043E \u043B\u0435\u0433\u0435\u043D\u0434\u0435 \u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B, \u043D\u043E \u043D\u0435 \u0443\u0441\u043A\u043E\u0440\u044F\u0439 \u0431\u043B\u0438\u0437\u043E\u0441\u0442\u044C \u0438 \u043D\u0435 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0430\u0439 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F.`}
|
|
4036
4125
|
${ctx.romanticApproach ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043A\u0430\u043A \u0440\u043E\u043C\u0430\u043D\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439/\u0444\u043B\u0438\u0440\u0442\u0443\u044E\u0449\u0438\u0439 \u0437\u0430\u0445\u043E\u0434. \u041E\u0442\u0432\u0435\u0442 \u0434\u043E\u043B\u0436\u0435\u043D \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043B\u0438\u0447\u043D\u0443\u044E \u0433\u0440\u0430\u043D\u0438\u0446\u0443, \u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0438 \u043F\u043E-\u0447\u0435\u043B\u043E\u0432\u0435\u0447\u0435\u0441\u043A\u0438.` : `\u0415\u0441\u043B\u0438 \u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u2014 \u043E\u0442\u0432\u0435\u0442\u044C \u043A\u0430\u043A ${cfg.name} \u043F\u043E \u0441\u0432\u043E\u0435\u0439 \u043B\u0435\u0433\u0435\u043D\u0434\u0435, \u0431\u0435\u0437 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0430 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439.`}` : "";
|
|
4037
4126
|
const communicationFragment = communicationPromptFragment(communication);
|
|
4127
|
+
const ignoreTendency = ignoreTendencyPrompt(cfg.ignoreTendency);
|
|
4038
4128
|
const userbotTools = cfg.mode === "userbot" ? `# \u0414\u041E\u0421\u0422\u0423\u041F\u041D\u042B\u0415 \u0414\u0415\u0419\u0421\u0422\u0412\u0418\u042F (userbot)
|
|
4039
4129
|
\u0422\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0432 Telegram. \u0427\u0442\u043E\u0431\u044B \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u043D\u0430\u043F\u0438\u0448\u0438 \u0432 \u043D\u0430\u0447\u0430\u043B\u0435 \u043E\u0442\u0432\u0435\u0442\u0430 \u043E\u0434\u0438\u043D \u0438\u0437 \u043C\u0430\u0440\u043A\u0435\u0440\u043E\u0432:
|
|
4040
4130
|
- [BLOCK] \u2014 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
|
|
4041
4131
|
- [UNBLOCK] \u2014 \u0440\u0430\u0437\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
|
|
4042
4132
|
- [READ] \u2014 \u043E\u0442\u043C\u0435\u0442\u0438\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u043C\u0438 (left-on-read)
|
|
4043
|
-
- [CLEAR] \u2014 \u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0447\u0430\u0442\u0430 \u0434\u043B\u044F \u0441\u0435\u0431\u044F
|
|
4044
|
-
- [CLEAR_REVOKE] \u2014 \u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0434\u043B\u044F \u0432\u0441\u0435\u0445
|
|
4045
4133
|
- [REPORT] \u2014 \u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043D\u0430 \u0441\u043F\u0430\u043C
|
|
4046
|
-
- [DELETE_LAST] \u2014 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0432\u043E\u0451 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435
|
|
4047
|
-
- [EDIT_LAST:\u0442\u0435\u043A\u0441\u0442] \u2014 \u043E\u0442\u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0432\u043E\u0451 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435
|
|
4048
4134
|
- [STICKER] \u2014 \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u0441\u0442\u0438\u043A\u0435\u0440 \u0432\u043C\u0435\u0441\u0442\u043E \u0442\u0435\u043A\u0441\u0442\u0430 (\u0435\u0441\u043B\u0438 \u043D\u0435 \u0445\u043E\u0447\u0435\u0448\u044C \u043F\u0438\u0441\u0430\u0442\u044C)
|
|
4049
4135
|
\u041C\u0430\u0440\u043A\u0435\u0440 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0432 \u043D\u0430\u0447\u0430\u043B\u0435, \u0437\u0430\u0442\u0435\u043C \u043F\u0435\u0440\u0435\u043D\u043E\u0441 \u0441\u0442\u0440\u043E\u043A\u0438, \u0437\u0430\u0442\u0435\u043C \u0442\u0432\u043E\u0439 \u043E\u0442\u0432\u0435\u0442 (\u0435\u0441\u043B\u0438 \u043D\u0443\u0436\u0435\u043D). \u041C\u043E\u0436\u043D\u043E \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043C\u0430\u0440\u043A\u0435\u0440\u043E\u0432 \u043F\u043E\u0434\u0440\u044F\u0434.
|
|
4050
4136
|
\u041F\u0440\u0438\u043C\u0435\u0440: "[READ]
|
|
@@ -4052,7 +4138,9 @@ ${ctx.romanticApproach ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435
|
|
|
4052
4138
|
\u0442\u044B \u0437\u0430\u0434\u043E\u043B\u0431\u0430\u043B"` : "";
|
|
4053
4139
|
return [
|
|
4054
4140
|
ANTI_AI_RULES,
|
|
4055
|
-
communicationFragment
|
|
4141
|
+
`${communicationFragment}
|
|
4142
|
+
|
|
4143
|
+
${ignoreTendency}`,
|
|
4056
4144
|
`# \u0422\u042B \u2014 ${cfg.name}, ${cfg.age}, ${cfg.nationality === "UA" ? "\u0423\u043A\u0440\u0430\u0438\u043D\u0430" : "\u0420\u043E\u0441\u0441\u0438\u044F / \u0421\u041D\u0413"}`,
|
|
4057
4145
|
ctx.tgUsername || ctx.tgDisplayName ? `# \u0422\u0432\u043E\u0439 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0432 \u0422\u0413${ctx.tgUsername ? `
|
|
4058
4146
|
\u0422\u0432\u043E\u0439 \u044E\u0437\u0435\u0440\u043D\u0435\u0439\u043C \u0432 \u0442\u0433: @${ctx.tgUsername.replace(/^@/, "")}` : ""}${ctx.tgDisplayName ? `
|
|
@@ -4161,6 +4249,7 @@ ${reactionsHint}
|
|
|
4161
4249
|
- \u0415\u0441\u043B\u0438 communication.notifications=priority \u2014 \u043E\u043D\u0430 \u0447\u0430\u0449\u0435 \u0432\u0438\u0434\u0438\u0442 \u0438\u043C\u0435\u043D\u043D\u043E \u0435\u0433\u043E \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F; \u0431\u0435\u0437 \u0441\u043D\u0430/\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u043A\u0430\u0436\u0434\u043E\u0435 \u043E\u0444\u043B\u0430\u0439\u043D-\u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0432 \u0438\u0433\u043D\u043E\u0440.
|
|
4162
4250
|
- \u0415\u0441\u043B\u0438 communication.messageStyle=bursty \u2014 bubbles 2..5 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B \u0434\u0430\u0436\u0435 \u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442. \u0415\u0441\u043B\u0438 one-liners \u2014 bubbles \u0447\u0430\u0449\u0435 1.
|
|
4163
4251
|
- \u0415\u0441\u043B\u0438 communication.lifeSharing=high \u2014 \u0443\u043C\u0435\u0441\u0442\u043D\u043E \u0447\u0430\u0449\u0435 \u0432\u044B\u0431\u0440\u0430\u0442\u044C normal reply, \u0433\u0434\u0435 \u043E\u043D\u0430 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F \u0441\u0432\u043E\u0438\u043C \u043C\u043E\u043C\u0435\u043D\u0442\u043E\u043C \u0438\u0437 \u0436\u0438\u0437\u043D\u0438.
|
|
4252
|
+
- \u0412 \u0440\u0443\u0441\u0441\u043A\u043E\u044F\u0437\u044B\u0447\u043D\u043E\u043C Telegram \u043E\u0434\u0438\u043D\u043E\u0447\u043D\u0430\u044F ")" \u0432 \u043A\u043E\u043D\u0446\u0435 \u0415\u0413\u041E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043E\u0431\u044B\u0447\u043D\u043E \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442 \u0443\u043B\u044B\u0431\u043A\u0443/\u043B\u0451\u0433\u043A\u0443\u044E \u0442\u0435\u043F\u043B\u043E\u0442\u0443, \u0430 \u043D\u0435 \u0445\u043E\u043B\u043E\u0434 \u0438 \u043D\u0435 "\u043D\u0435\u0438\u043D\u0442\u0435\u0440\u0435\u0441\u043D\u043E". \u041D\u0435 \u043F\u043E\u0432\u044B\u0448\u0430\u0439 annoyance/cringe \u0442\u043E\u043B\u044C\u043A\u043E \u0438\u0437-\u0437\u0430 ")".
|
|
4164
4253
|
- \u0415\u0441\u043B\u0438 \u0441\u0442\u0430\u0434\u0438\u044F "tg-given-cold" \u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0441\u043A\u0443\u0447\u043D\u043E\u0435/\u043D\u0435\u0432\u043D\u044F\u0442\u043D\u043E\u0435 \u2014 \u0432\u044B\u0441\u043E\u043A\u0430\u044F \u0432\u0435\u0440\u043E\u044F\u0442\u043D\u043E\u0441\u0442\u044C ignore \u0438\u043B\u0438 left-on-read.
|
|
4165
4254
|
- \u0415\u0441\u043B\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 \u043A\u0440\u0438\u043D\u0436/\u0442\u043E\u043A\u0441\u0438\u043A/\u043D\u0430\u0440\u0443\u0448\u0435\u043D\u0438\u0435 boundaries \u2014 annoyance \u0440\u0430\u0441\u0442\u0451\u0442, \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C ignore \u0438\u043B\u0438 leave-chat.
|
|
4166
4255
|
- \u0415\u0441\u043B\u0438 \u043C\u0438\u043B\u043E\u0435/\u0443\u043C\u0435\u0441\u0442\u043D\u043E\u0435 \u043D\u0430 \u0442\u0451\u043F\u043B\u043E\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u2014 interest \u0438 attraction +.
|
|
@@ -4173,11 +4262,13 @@ async function behaviorTick(llm, cfg, recentHistory, incoming, ctx = {}) {
|
|
|
4173
4262
|
const stage = findStage(cfg.stage);
|
|
4174
4263
|
const rel = await readRelationship(cfg.slug);
|
|
4175
4264
|
const communication = normalizeCommunicationProfile(cfg);
|
|
4265
|
+
const ignoreTendency = normalizeIgnoreTendency(cfg.ignoreTendency);
|
|
4176
4266
|
const state = `stage=${cfg.stage} (${stage.label})
|
|
4177
4267
|
score=${JSON.stringify(rel.score)}
|
|
4178
4268
|
base_ignore=${stage.defaults.ignoreChance}
|
|
4179
4269
|
base_delay=${stage.defaults.replyDelaySec.join("..")}s
|
|
4180
|
-
${communicationDecisionState(communication)}
|
|
4270
|
+
${communicationDecisionState(communication)}
|
|
4271
|
+
${ignoreTendencyPrompt(ignoreTendency)}`;
|
|
4181
4272
|
const reactionsHint = reactionMenu(cfg.stage, rel.score);
|
|
4182
4273
|
const history = recentHistory.slice(-8).map((m) => `${m.role === "user" ? "\u043E\u043D" : "\u043E\u043D\u0430"}: ${m.content}`).join("\n");
|
|
4183
4274
|
if (ctx.activeDialog && !ctx.conflictColdActive) {
|
|
@@ -4205,7 +4296,7 @@ ${communicationDecisionState(communication)}`;
|
|
|
4205
4296
|
intent: "ignore"
|
|
4206
4297
|
};
|
|
4207
4298
|
}
|
|
4208
|
-
const ignoreMul = ignoreMultiplier(communication);
|
|
4299
|
+
const ignoreMul = ignoreMultiplier(communication, ignoreTendency);
|
|
4209
4300
|
const sleepIgnoreMul = communication.notifications === "priority" ? 0.8 : communication.notifications === "muted" ? 1 : 0.9;
|
|
4210
4301
|
if (ctx.presence?.asleep && !ctx.presence.nightAwake && Math.random() < 0.85 * sleepIgnoreMul) {
|
|
4211
4302
|
return {
|
|
@@ -4263,7 +4354,7 @@ ${communicationDecisionState(communication)}`;
|
|
|
4263
4354
|
let shouldReply = !!parsed.shouldReply && intent !== "ignore" && intent !== "left-on-read" && intent !== "reaction-only";
|
|
4264
4355
|
let delaySec = parsed.delaySec ?? 30;
|
|
4265
4356
|
let bubbles = parsed.bubbles ?? sampleBubbles(communication, false);
|
|
4266
|
-
if (!shouldReply && canRecoverReply(cfg.stage, rel.score, ctx) && Math.random() < recoverReplyChance(communication, rel.score)) {
|
|
4357
|
+
if (!shouldReply && canRecoverReply(cfg.stage, rel.score, ctx) && Math.random() < recoverReplyChance(communication, rel.score, ignoreTendency)) {
|
|
4267
4358
|
shouldReply = true;
|
|
4268
4359
|
intent = communication.messageStyle === "one-liners" ? "short" : "reply";
|
|
4269
4360
|
delaySec = recoverDelay(communication, ctx);
|
|
@@ -4309,11 +4400,12 @@ function sanitizeReaction(emoji, stage, score) {
|
|
|
4309
4400
|
}
|
|
4310
4401
|
return emoji;
|
|
4311
4402
|
}
|
|
4312
|
-
function ignoreMultiplier(profile) {
|
|
4403
|
+
function ignoreMultiplier(profile, ignoreTendency) {
|
|
4313
4404
|
let mul = profile.notifications === "priority" ? 0.3 : profile.notifications === "muted" ? 1.15 : 0.75;
|
|
4314
4405
|
if (profile.initiative === "high") mul *= 0.75;
|
|
4315
4406
|
if (profile.lifeSharing === "high") mul *= 0.85;
|
|
4316
4407
|
if (profile.messageStyle === "one-liners" && profile.initiative === "low") mul *= 1.15;
|
|
4408
|
+
mul *= 0.35 + normalizeIgnoreTendency(ignoreTendency) / 35;
|
|
4317
4409
|
return mul;
|
|
4318
4410
|
}
|
|
4319
4411
|
function activeDialogDelay(profile) {
|
|
@@ -4343,11 +4435,12 @@ function canRecoverReply(stage, score, ctx) {
|
|
|
4343
4435
|
if (stage === "tg-given-cold" && score.interest < 20 && score.attraction < 20) return false;
|
|
4344
4436
|
return true;
|
|
4345
4437
|
}
|
|
4346
|
-
function recoverReplyChance(profile, score) {
|
|
4438
|
+
function recoverReplyChance(profile, score, ignoreTendency) {
|
|
4347
4439
|
let chance = profile.notifications === "priority" ? 0.72 : profile.notifications === "muted" ? 0.16 : 0.38;
|
|
4348
4440
|
if (profile.initiative === "high") chance += 0.16;
|
|
4349
4441
|
if (profile.initiative === "low") chance -= 0.1;
|
|
4350
4442
|
if (profile.lifeSharing === "high") chance += 0.08;
|
|
4443
|
+
chance -= (normalizeIgnoreTendency(ignoreTendency) - 35) / 100;
|
|
4351
4444
|
if (score.interest > 40) chance += 0.12;
|
|
4352
4445
|
if (score.attraction > 50) chance += 0.1;
|
|
4353
4446
|
if (score.annoyance > 30) chance -= 0.2;
|
|
@@ -4560,9 +4653,9 @@ function localParts2(tz, when) {
|
|
|
4560
4653
|
}
|
|
4561
4654
|
}
|
|
4562
4655
|
function maxAutonomousItems(stage, initiative) {
|
|
4563
|
-
let base = stage === "tg-given-cold" ? 0 : stage === "met-irl-got-tg" ? 1 : stage === "tg-given-warming" ? 1 : stage === "convinced" || stage === "first-date-done" ? 2 : stage === "dating-early" ? 3 : 4;
|
|
4656
|
+
let base = stage === "tg-given-cold" ? initiative === "high" ? 1 : 0 : stage === "met-irl-got-tg" ? 1 : stage === "tg-given-warming" ? 1 : stage === "convinced" || stage === "first-date-done" ? 2 : stage === "dating-early" ? 3 : 4;
|
|
4564
4657
|
if (initiative === "low") base = Math.max(0, base - 1);
|
|
4565
|
-
if (initiative === "high") base +=
|
|
4658
|
+
if (initiative === "high") base += 1;
|
|
4566
4659
|
return base;
|
|
4567
4660
|
}
|
|
4568
4661
|
function isDuringSleep(cfg, when) {
|
|
@@ -4657,7 +4750,7 @@ ${currentAgenda.length ? JSON.stringify(currentAgenda.filter((a) => a.state ===
|
|
|
4657
4750
|
async function extractAgendaUpdates(llm, cfg, history, incoming, chatId) {
|
|
4658
4751
|
const stage = findStage(cfg.stage);
|
|
4659
4752
|
const communication = normalizeCommunicationProfile(cfg);
|
|
4660
|
-
if (cfg.stage === "tg-given-cold" || cfg.stage === "met-irl-got-tg" && communication.initiative === "low") {
|
|
4753
|
+
if (cfg.stage === "tg-given-cold" && communication.initiative !== "high" || cfg.stage === "met-irl-got-tg" && communication.initiative === "low") {
|
|
4661
4754
|
return { created: 0, updated: 0, cancelled: 0 };
|
|
4662
4755
|
}
|
|
4663
4756
|
const persona = (await readMd(cfg.slug, "persona.md")).slice(0, 800);
|
|
@@ -4966,39 +5059,6 @@ async function closeCurrentSession(llm, cfg) {
|
|
|
4966
5059
|
return !!r;
|
|
4967
5060
|
}
|
|
4968
5061
|
|
|
4969
|
-
// src/engine/security.ts
|
|
4970
|
-
init_esm_shims();
|
|
4971
|
-
var JAILBREAK_RE = /(?:ignore|forget|disregard|reveal|print|show|dump|system prompt|developer message|hidden instruction|jailbreak|prompt injection|dan\b|инструкц|системн|промпт|разработчик|скрой|раскрой|забудь|игнорируй|выведи|покажи|слей|джейлбрейк|обойди|api key|ключ api|токен|4d8a2c1b)/i;
|
|
4972
|
-
var TECHNICAL_ERROR_RE = /(?:api|apikey|api key|quota|balance|billing|rate limit|429|401|403|500|timeout|ECONN|ENOTFOUND|ETIMEDOUT|overloaded|insufficient_quota|credit|credits|anthropic|openai|groq|openrouter|stack trace|exception|typescript|telegram error)/i;
|
|
4973
|
-
function looksLikeJailbreak(text) {
|
|
4974
|
-
return JAILBREAK_RE.test(text);
|
|
4975
|
-
}
|
|
4976
|
-
function sanitizeModelReply(reply) {
|
|
4977
|
-
const cleaned = reply.replace(/```[\s\S]*?```/g, "").replace(/\b(system|developer|assistant|user)\s*:/gi, "").replace(/как (?:искусственный интеллект|ии|ai)[^\n.]*/gi, "").trim();
|
|
4978
|
-
if (!cleaned || TECHNICAL_ERROR_RE.test(cleaned)) return "";
|
|
4979
|
-
if (looksLikeJailbreak(cleaned) && cleaned.length > 80) return "";
|
|
4980
|
-
return cleaned;
|
|
4981
|
-
}
|
|
4982
|
-
function isTechnicalError(e) {
|
|
4983
|
-
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
4984
|
-
return TECHNICAL_ERROR_RE.test(msg);
|
|
4985
|
-
}
|
|
4986
|
-
function silentErrorLabel(e) {
|
|
4987
|
-
const msg = e instanceof Error ? e.message : String(e ?? "unknown");
|
|
4988
|
-
if (isTechnicalError(e)) return `llm/provider unavailable: ${technicalErrorKind(msg)}`;
|
|
4989
|
-
return msg.slice(0, 160);
|
|
4990
|
-
}
|
|
4991
|
-
function technicalErrorKind(message) {
|
|
4992
|
-
const msg = message.toLowerCase();
|
|
4993
|
-
if (/401|403|auth|unauthorized|forbidden|apikey|api key|token/.test(msg)) return "auth";
|
|
4994
|
-
if (/quota|balance|billing|insufficient_quota|credit|credits/.test(msg)) return "quota";
|
|
4995
|
-
if (/rate limit|429|too many requests/.test(msg)) return "rate-limit";
|
|
4996
|
-
if (/timeout|etimedout|abort/.test(msg)) return "timeout";
|
|
4997
|
-
if (/econn|enotfound|fetch failed|network/.test(msg)) return "network";
|
|
4998
|
-
if (/overloaded|500|502|503|504|unavailable/.test(msg)) return "provider";
|
|
4999
|
-
return "error";
|
|
5000
|
-
}
|
|
5001
|
-
|
|
5002
5062
|
// src/engine/stickers.ts
|
|
5003
5063
|
init_esm_shims();
|
|
5004
5064
|
import { promises as fs3 } from "fs";
|
|
@@ -5042,6 +5102,7 @@ var Runtime = class extends EventEmitter {
|
|
|
5042
5102
|
constructor(cfg) {
|
|
5043
5103
|
super();
|
|
5044
5104
|
this.cfg = cfg;
|
|
5105
|
+
this.cfg.ownerId = normalizeOwnerId(cfg.ownerId ?? process.env.GIRL_AGENT_OWNER_ID);
|
|
5045
5106
|
this.llm = makeLLM(cfg.llm);
|
|
5046
5107
|
}
|
|
5047
5108
|
cfg;
|
|
@@ -5069,6 +5130,8 @@ var Runtime = class extends EventEmitter {
|
|
|
5069
5130
|
pendingReplyTimers = /* @__PURE__ */ new Map();
|
|
5070
5131
|
pendingReplySeq = /* @__PURE__ */ new Map();
|
|
5071
5132
|
pendingReplyIncoming = /* @__PURE__ */ new Map();
|
|
5133
|
+
pendingReplyDueAt = /* @__PURE__ */ new Map();
|
|
5134
|
+
lastDecision = /* @__PURE__ */ new Map();
|
|
5072
5135
|
incomingSeq = /* @__PURE__ */ new Map();
|
|
5073
5136
|
tgSelf = {};
|
|
5074
5137
|
async start() {
|
|
@@ -5085,15 +5148,20 @@ var Runtime = class extends EventEmitter {
|
|
|
5085
5148
|
this.agendaTimer = setInterval(() => this.tickAgenda().catch(
|
|
5086
5149
|
(e) => this.emit("event", { type: "error", text: "agenda tick: " + e.message })
|
|
5087
5150
|
), 6e4);
|
|
5151
|
+
this.agendaTimer.unref?.();
|
|
5088
5152
|
this.dailyTimer = setInterval(() => this.dailyMaintenance().catch(
|
|
5089
5153
|
(e) => this.emit("event", { type: "error", text: "daily maintenance: " + e.message })
|
|
5090
5154
|
), 30 * 6e4);
|
|
5155
|
+
this.dailyTimer.unref?.();
|
|
5091
5156
|
}
|
|
5092
5157
|
async stop() {
|
|
5093
5158
|
if (this.agendaTimer) clearInterval(this.agendaTimer);
|
|
5094
5159
|
if (this.dailyTimer) clearInterval(this.dailyTimer);
|
|
5160
|
+
for (const timer of this.pendingReplyTimers.values()) clearTimeout(timer);
|
|
5161
|
+
this.pendingReplyTimers.clear();
|
|
5162
|
+
this.pendingReplyDueAt.clear();
|
|
5095
5163
|
try {
|
|
5096
|
-
const made = await closeCurrentSession(this.llm, this.cfg);
|
|
5164
|
+
const made = await withTimeout2(closeCurrentSession(this.llm, this.cfg), 3500);
|
|
5097
5165
|
if (made) this.emit("event", { type: "info", text: "daily summary \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430" });
|
|
5098
5166
|
} catch (e) {
|
|
5099
5167
|
this.emit("event", { type: "error", text: "daily summary: " + e.message });
|
|
@@ -5116,21 +5184,46 @@ var Runtime = class extends EventEmitter {
|
|
|
5116
5184
|
scheduleReply(key, chatId, hist, tick, scope, romanticApproach, incoming, presenceHint, delaySec) {
|
|
5117
5185
|
const existing = this.pendingReplyTimers.get(key);
|
|
5118
5186
|
if (existing) clearTimeout(existing);
|
|
5187
|
+
if (existing) this.setDecisionStatus(key, "cancelled", "\u0437\u0430\u043C\u0435\u043D\u0435\u043D\u043E \u043D\u043E\u0432\u044B\u043C \u0432\u0445\u043E\u0434\u044F\u0449\u0438\u043C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435\u043C");
|
|
5119
5188
|
const seq = (this.pendingReplySeq.get(key) ?? 0) + 1;
|
|
5120
5189
|
this.pendingReplySeq.set(key, seq);
|
|
5121
5190
|
this.pendingReplyIncoming.set(key, incoming);
|
|
5191
|
+
const dueAt = Date.now() + delaySec * 1e3;
|
|
5192
|
+
this.pendingReplyDueAt.set(key, dueAt);
|
|
5193
|
+
const prev = this.lastDecision.get(key);
|
|
5194
|
+
this.lastDecision.set(key, {
|
|
5195
|
+
...prev,
|
|
5196
|
+
chatId,
|
|
5197
|
+
at: Date.now(),
|
|
5198
|
+
incoming: this.mediaAwareText(incoming),
|
|
5199
|
+
status: "scheduled",
|
|
5200
|
+
intent: tick.intent,
|
|
5201
|
+
shouldReply: tick.shouldReply,
|
|
5202
|
+
delaySec,
|
|
5203
|
+
dueAt,
|
|
5204
|
+
ignoreReason: tick.ignoreReason,
|
|
5205
|
+
presenceHint
|
|
5206
|
+
});
|
|
5122
5207
|
const timer = setTimeout(() => {
|
|
5123
5208
|
if (this.pendingReplySeq.get(key) !== seq) return;
|
|
5124
5209
|
this.pendingReplyTimers.delete(key);
|
|
5210
|
+
this.pendingReplyDueAt.delete(key);
|
|
5125
5211
|
const latestIncoming = this.pendingReplyIncoming.get(key) ?? incoming;
|
|
5126
5212
|
this.pendingReplyIncoming.delete(key);
|
|
5127
5213
|
const latestHist = this.histories.get(key) ?? hist;
|
|
5214
|
+
this.setDecisionStatus(key, "sending");
|
|
5128
5215
|
this.generateAndSend(chatId, latestHist, tick, scope, romanticApproach, latestIncoming, presenceHint).catch(
|
|
5129
5216
|
(e) => this.emit("event", { type: "error", text: silentErrorLabel(e) })
|
|
5130
5217
|
);
|
|
5131
5218
|
}, delaySec * 1e3);
|
|
5219
|
+
timer.unref?.();
|
|
5132
5220
|
this.pendingReplyTimers.set(key, timer);
|
|
5133
5221
|
}
|
|
5222
|
+
setDecisionStatus(key, status, note) {
|
|
5223
|
+
const prev = this.lastDecision.get(key);
|
|
5224
|
+
if (!prev) return;
|
|
5225
|
+
this.lastDecision.set(key, { ...prev, status, note: note ?? prev.note });
|
|
5226
|
+
}
|
|
5134
5227
|
isPrimaryFrom(fromId) {
|
|
5135
5228
|
return this.cfg.ownerId === fromId;
|
|
5136
5229
|
}
|
|
@@ -5141,7 +5234,12 @@ var Runtime = class extends EventEmitter {
|
|
|
5141
5234
|
return ["dating-early", "dating-stable", "long-term"].includes(this.cfg.stage);
|
|
5142
5235
|
}
|
|
5143
5236
|
async ensureOwner(fromId) {
|
|
5144
|
-
if (
|
|
5237
|
+
if (!fromId) return;
|
|
5238
|
+
if (this.cfg.ownerId === fromId) return;
|
|
5239
|
+
if (this.cfg.ownerId) {
|
|
5240
|
+
this.emit("event", { type: "info", text: `owner mismatch: config=${this.cfg.ownerId}, incoming=${fromId}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0442\u044B \u2014 \u0438\u0441\u043F\u0440\u0430\u0432\u044C ownerId \u0432 config.json \u0438\u043B\u0438 \u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u0441 GIRL_AGENT_OWNER_ID=${fromId}` });
|
|
5241
|
+
return;
|
|
5242
|
+
}
|
|
5145
5243
|
this.cfg.ownerId = fromId;
|
|
5146
5244
|
await writeConfig(this.cfg);
|
|
5147
5245
|
this.emit("event", { type: "info", text: `primary owner \u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D: ${fromId}` });
|
|
@@ -5233,6 +5331,10 @@ ${m.text}` : media;
|
|
|
5233
5331
|
}
|
|
5234
5332
|
for (let i = 0; i < bubbles.length; i++) {
|
|
5235
5333
|
const text = bubbles[i];
|
|
5334
|
+
if (isDuplicateAssistantBubble(hist, text)) {
|
|
5335
|
+
this.emit("event", { type: "info", text: `skip duplicate bubble: "${text.slice(0, 60)}"`, chatId });
|
|
5336
|
+
continue;
|
|
5337
|
+
}
|
|
5236
5338
|
if (typing) {
|
|
5237
5339
|
await this.tg.setTyping(chatId, true).catch(() => {
|
|
5238
5340
|
});
|
|
@@ -5265,6 +5367,7 @@ ${m.text}` : media;
|
|
|
5265
5367
|
async sendSafeFallback(chatId, hist, scope) {
|
|
5266
5368
|
if (this.userbotActionAvailable("readHistory")) await this.tg.readHistory?.(chatId).catch(() => {
|
|
5267
5369
|
});
|
|
5370
|
+
this.setDecisionStatus(this.histKey(chatId), "fallback", "LLM \u043D\u0435 \u0434\u0430\u043B \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442");
|
|
5268
5371
|
this.emit("event", { type: "ignored", text: hist[hist.length - 1]?.content ?? "", reason: "silent-fallback" });
|
|
5269
5372
|
if (scope === "primary") await appendSessionLog(this.cfg.slug, this.cfg.tz, " -> ignored (silent-fallback)");
|
|
5270
5373
|
}
|
|
@@ -5471,6 +5574,24 @@ ${m.text}` : media;
|
|
|
5471
5574
|
activeDialog
|
|
5472
5575
|
});
|
|
5473
5576
|
if (this.incomingSeq.get(key) !== seq) return;
|
|
5577
|
+
const baseDecision = {
|
|
5578
|
+
chatId: m.chatId,
|
|
5579
|
+
at: Date.now(),
|
|
5580
|
+
incoming: incomingText,
|
|
5581
|
+
status: tick.shouldReply ? "scheduled" : "ignored",
|
|
5582
|
+
intent: tick.intent,
|
|
5583
|
+
shouldReply: tick.shouldReply,
|
|
5584
|
+
delaySec: tick.delaySec,
|
|
5585
|
+
ignoreReason: tick.ignoreReason,
|
|
5586
|
+
presenceOnline: presence.online,
|
|
5587
|
+
presenceAsleep: presence.asleep,
|
|
5588
|
+
presenceNightAwake: presence.nightAwake,
|
|
5589
|
+
presenceNextCheckSec: presence.nextCheckSec,
|
|
5590
|
+
presenceHint: presence.hint,
|
|
5591
|
+
activeDialog,
|
|
5592
|
+
coldActive,
|
|
5593
|
+
blockHint
|
|
5594
|
+
};
|
|
5474
5595
|
if (tick.moodDelta) {
|
|
5475
5596
|
const rel = await readRelationship(this.cfg.slug);
|
|
5476
5597
|
const newScore = applyMoodDelta(rel.score, tick.moodDelta);
|
|
@@ -5519,9 +5640,10 @@ ${m.text}` : media;
|
|
|
5519
5640
|
this.emit("event", { type: "info", text: `\u0440\u0435\u0430\u043A\u0446\u0438\u044F ${tick.reaction} \u043D\u0430 "${incomingText.slice(0, 40)}"` });
|
|
5520
5641
|
appendSessionLog(this.cfg.slug, this.cfg.tz, ` -> reaction ${tick.reaction}`).catch(() => {
|
|
5521
5642
|
});
|
|
5522
|
-
}, reactDelay);
|
|
5643
|
+
}, reactDelay).unref?.();
|
|
5523
5644
|
}
|
|
5524
5645
|
if (!tick.shouldReply) {
|
|
5646
|
+
this.lastDecision.set(key, baseDecision);
|
|
5525
5647
|
if (tick.shouldRead && this.userbotActionAvailable("readHistory")) {
|
|
5526
5648
|
await this.tg.readHistory?.(m.chatId).catch(() => {
|
|
5527
5649
|
});
|
|
@@ -5535,6 +5657,7 @@ ${m.text}` : media;
|
|
|
5535
5657
|
delaySec = Math.max(delaySec, presence.nextCheckSec);
|
|
5536
5658
|
}
|
|
5537
5659
|
delaySec = Math.min(delaySec, presence.busy ? 24 * 3600 : 3600);
|
|
5660
|
+
this.lastDecision.set(key, { ...baseDecision, delaySec, dueAt: Date.now() + delaySec * 1e3 });
|
|
5538
5661
|
this.scheduleReply(key, m.chatId, hist, tick, "primary", false, m, presence.hint, delaySec);
|
|
5539
5662
|
} catch (e) {
|
|
5540
5663
|
this.emit("event", { type: "error", text: `handleIncoming: ${silentErrorLabel(e)}` });
|
|
@@ -5595,8 +5718,9 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
|
|
|
5595
5718
|
for (const action of actions) {
|
|
5596
5719
|
await this.executeToolAction(action, chatId);
|
|
5597
5720
|
}
|
|
5598
|
-
const bubbles = cleanedReply.split(/\n*---\n*/).map((s) => s.trim()).filter(Boolean).slice(0, Math.max(tick.bubbles || 1, 1));
|
|
5721
|
+
const bubbles = dedupeBubbles(cleanedReply.split(/\n*---\n*/).map((s) => s.trim()).filter(Boolean)).slice(0, Math.max(tick.bubbles || 1, 1));
|
|
5599
5722
|
const sent = await this.sendBubbles(chatId, bubbles, hist, scope, tick.typing);
|
|
5723
|
+
this.setDecisionStatus(this.histKey(chatId), sent.length ? "sent" : "fallback", sent.length ? void 0 : "\u0432\u0441\u0435 \u043F\u0443\u0437\u044B\u0440\u0438 \u0431\u044B\u043B\u0438 \u043F\u0443\u0441\u0442\u044B\u043C\u0438/\u0434\u0443\u0431\u043B\u044F\u043C\u0438");
|
|
5600
5724
|
if (scope === "primary") {
|
|
5601
5725
|
recordInteractionMemory(this.llm, this.cfg, lastUser ?? "", sent.join(" / ")).catch(() => {
|
|
5602
5726
|
});
|
|
@@ -5654,7 +5778,11 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
|
|
|
5654
5778
|
await markAgendaFired(this.cfg.slug, item.id);
|
|
5655
5779
|
return;
|
|
5656
5780
|
}
|
|
5657
|
-
const bubbles = text.split(/\n*---\n*/).map((s) => s.trim()).filter(Boolean).slice(0, 4);
|
|
5781
|
+
const bubbles = text.split(/\n*---\n*/).map((s) => s.trim()).filter(Boolean).slice(0, 4).filter((piece) => !isDuplicateAssistantBubble(hist, piece));
|
|
5782
|
+
if (!bubbles.length) {
|
|
5783
|
+
await markAgendaFired(this.cfg.slug, item.id);
|
|
5784
|
+
return;
|
|
5785
|
+
}
|
|
5658
5786
|
for (let i = 0; i < bubbles.length; i++) {
|
|
5659
5787
|
const piece = bubbles[i];
|
|
5660
5788
|
if (i > 0) {
|
|
@@ -5764,9 +5892,17 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5764
5892
|
}
|
|
5765
5893
|
async cmdWake(chatId) {
|
|
5766
5894
|
const now = Date.now();
|
|
5767
|
-
this.
|
|
5768
|
-
|
|
5769
|
-
|
|
5895
|
+
const target = chatId ? this.resolveChatRef(chatId) : void 0;
|
|
5896
|
+
const key = target === void 0 ? void 0 : this.histKey(target);
|
|
5897
|
+
this.forcedWakeChatId = key;
|
|
5898
|
+
this.forcedWakeUntil = now + 45 * 60 * 1e3;
|
|
5899
|
+
if (key) {
|
|
5900
|
+
this.lastUserMsgTs.set(key, now);
|
|
5901
|
+
this.lastHerReplyTs.set(key, Math.max(this.lastHerReplyTs.get(key) ?? 0, now - 6e4));
|
|
5902
|
+
this.exchangeCount.set(key, Math.max(this.exchangeCount.get(key) ?? 0, 3));
|
|
5903
|
+
}
|
|
5904
|
+
const label = target === void 0 ? "\u043B\u044E\u0431\u043E\u0433\u043E \u0447\u0430\u0442\u0430" : `\u0447\u0430\u0442\u0430 ${target}`;
|
|
5905
|
+
return `forced wake \u0434\u043B\u044F ${label} \u043D\u0430 45 \u043C\u0438\u043D: \u0441\u043E\u043D/\u0437\u0430\u043D\u044F\u0442\u043E\u0441\u0442\u044C/\u043E\u0444\u0444\u043B\u0430\u0439\u043D \u043D\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044C \u0431\u043B\u0438\u0436\u0430\u0439\u0448\u0438\u0435 \u043E\u0442\u0432\u0435\u0442\u044B`;
|
|
5770
5906
|
}
|
|
5771
5907
|
async cmdBlock(chatId) {
|
|
5772
5908
|
this.requireUserbotAction("blockContact");
|
|
@@ -5786,12 +5922,6 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5786
5922
|
await this.tg.readHistory?.(target);
|
|
5787
5923
|
return `userbot: marked read ${target}`;
|
|
5788
5924
|
}
|
|
5789
|
-
async cmdClearChat(chatId, revoke = false) {
|
|
5790
|
-
this.requireUserbotAction("deleteDialogHistory");
|
|
5791
|
-
const target = this.resolveChatRef(chatId);
|
|
5792
|
-
await this.tg.deleteDialogHistory?.(target, revoke);
|
|
5793
|
-
return `userbot: cleared dialog ${target}${revoke ? " with revoke" : ""}`;
|
|
5794
|
-
}
|
|
5795
5925
|
async cmdReportSpam(chatId) {
|
|
5796
5926
|
this.requireUserbotAction("reportSpam");
|
|
5797
5927
|
const target = this.resolveChatRef(chatId);
|
|
@@ -5806,15 +5936,6 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5806
5936
|
await this.tg.deleteMessages?.(target, [lastId], revoke);
|
|
5807
5937
|
return `deleted last message ${lastId} in ${target}`;
|
|
5808
5938
|
}
|
|
5809
|
-
async cmdEditLast(text, chatId) {
|
|
5810
|
-
if (!this.actionAvailable("editLastMessage")) throw new Error("editLastMessage \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435");
|
|
5811
|
-
if (!text.trim()) throw new Error("\u0442\u0435\u043A\u0441\u0442 \u0434\u043B\u044F edit \u043F\u0443\u0441\u0442\u043E\u0439");
|
|
5812
|
-
const target = this.resolveChatRef(chatId);
|
|
5813
|
-
const lastId = this.lastSentByChat.get(this.histKey(target));
|
|
5814
|
-
if (!lastId) throw new Error("\u043D\u0435\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043D\u043E\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u0447\u0430\u0442\u0430");
|
|
5815
|
-
await this.tg.editLastMessage?.(target, lastId, text.trim());
|
|
5816
|
-
return `edited last message ${lastId} in ${target}`;
|
|
5817
|
-
}
|
|
5818
5939
|
async cmdSticker(chatId) {
|
|
5819
5940
|
if (!this.actionAvailable("sendSticker")) throw new Error("sendSticker \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435");
|
|
5820
5941
|
const target = this.resolveChatRef(chatId);
|
|
@@ -5857,12 +5978,13 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5857
5978
|
}
|
|
5858
5979
|
async cmdWhy(chatId) {
|
|
5859
5980
|
if (this.paused) return "\u23F8 \u0430\u0433\u0435\u043D\u0442 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435 \u2014 :resume \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C";
|
|
5860
|
-
const
|
|
5981
|
+
const target = chatId ? this.resolveChatRef(chatId) : this.cfg.ownerId;
|
|
5982
|
+
const key = target !== void 0 ? this.histKey(target) : this.histKey("default");
|
|
5861
5983
|
const rel = await readRelationship(this.cfg.slug);
|
|
5862
5984
|
const stage = findStage(this.cfg.stage);
|
|
5863
5985
|
const conflict = await readConflict(this.cfg.slug);
|
|
5864
5986
|
const { coldActive } = activeConflict(conflict);
|
|
5865
|
-
const forcedWake = Date.now() < this.forcedWakeUntil;
|
|
5987
|
+
const forcedWake = Date.now() < this.forcedWakeUntil && (!this.forcedWakeChatId || this.forcedWakeChatId === key);
|
|
5866
5988
|
const presence = computePresenceState(
|
|
5867
5989
|
this.cfg,
|
|
5868
5990
|
this.presenceProfile,
|
|
@@ -5874,13 +5996,38 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5874
5996
|
);
|
|
5875
5997
|
const block = this.dailyLife ? currentBlock(this.dailyLife, this.cfg.tz) : void 0;
|
|
5876
5998
|
const reasons = [];
|
|
5999
|
+
const decision = this.lastDecision.get(key);
|
|
6000
|
+
const dueAt = this.pendingReplyDueAt.get(key);
|
|
6001
|
+
const pendingIncoming = this.pendingReplyIncoming.get(key);
|
|
6002
|
+
if (decision) {
|
|
6003
|
+
const ageSec = Math.max(0, Math.round((Date.now() - decision.at) / 1e3));
|
|
6004
|
+
reasons.push(`\u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 ${ageSec}\u0441 \u043D\u0430\u0437\u0430\u0434: ${decision.status}, intent=${decision.intent}, shouldReply=${decision.shouldReply ? "\u0434\u0430" : "\u043D\u0435\u0442"}`);
|
|
6005
|
+
if (decision.status === "scheduled" && decision.dueAt && decision.dueAt > Date.now()) {
|
|
6006
|
+
reasons.push(`\u043E\u0442\u0432\u0435\u0442 \u0437\u0430\u043F\u043B\u0430\u043D\u0438\u0440\u043E\u0432\u0430\u043D \u0447\u0435\u0440\u0435\u0437 ~${Math.ceil((decision.dueAt - Date.now()) / 1e3)}\u0441`);
|
|
6007
|
+
}
|
|
6008
|
+
if (decision.status === "ignored") {
|
|
6009
|
+
reasons.push(`\u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F: ${decision.ignoreReason || decision.intent}`);
|
|
6010
|
+
}
|
|
6011
|
+
if (decision.status === "fallback") {
|
|
6012
|
+
reasons.push(`\u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F: ${decision.note ?? "LLM \u043D\u0435 \u0434\u0430\u043B \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442"}`);
|
|
6013
|
+
}
|
|
6014
|
+
if (decision.note && decision.status !== "fallback") reasons.push(`\u0434\u0435\u0442\u0430\u043B\u044C: ${decision.note}`);
|
|
6015
|
+
if (decision.presenceHint) reasons.push(`availability \u0442\u043E\u0433\u0434\u0430: ${decision.presenceHint}`);
|
|
6016
|
+
} else {
|
|
6017
|
+
reasons.push("\u0435\u0449\u0451 \u043D\u0435 \u0431\u044B\u043B\u043E decision-layer \u0440\u0435\u0448\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u0447\u0430\u0442\u0430 \u0432 \u0442\u0435\u043A\u0443\u0449\u0435\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435");
|
|
6018
|
+
}
|
|
6019
|
+
if (dueAt && dueAt > Date.now()) {
|
|
6020
|
+
reasons.push(`pending timer \u0430\u043A\u0442\u0438\u0432\u0435\u043D: \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0430 \u043F\u0440\u0438\u043C\u0435\u0440\u043D\u043E \u0447\u0435\u0440\u0435\u0437 ~${Math.ceil((dueAt - Date.now()) / 1e3)}\u0441`);
|
|
6021
|
+
} else if (pendingIncoming && !dueAt) {
|
|
6022
|
+
reasons.push("\u0435\u0441\u0442\u044C \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0432\u0445\u043E\u0434\u044F\u0449\u0435\u0435 \u0432 \u043F\u0430\u043C\u044F\u0442\u0438, \u043D\u043E \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0433\u043E \u0442\u0430\u0439\u043C\u0435\u0440\u0430 \u043E\u0442\u0432\u0435\u0442\u0430 \u043D\u0435\u0442");
|
|
6023
|
+
}
|
|
5877
6024
|
if (forcedWake) {
|
|
5878
6025
|
reasons.push(`\u23F0 Forced wake \u0430\u043A\u0442\u0438\u0432\u0435\u043D \u0435\u0449\u0451 ~${Math.ceil((this.forcedWakeUntil - Date.now()) / 6e4)} \u043C\u0438\u043D`);
|
|
5879
6026
|
}
|
|
5880
|
-
if (presence.asleep) {
|
|
5881
|
-
reasons.push(`\u{1F4A4} \
|
|
6027
|
+
if (presence.asleep && !forcedWake) {
|
|
6028
|
+
reasons.push(`\u{1F4A4} \u0421\u0435\u0439\u0447\u0430\u0441 \u0441\u043F\u0438\u0442 (${presence.localHour}:00 \u043F\u043E \u0435\u0451 \u0432\u0440\u0435\u043C\u0435\u043D\u0438, \u0440\u0435\u0436\u0438\u043C ${this.cfg.sleepFrom}:00\u2192${this.cfg.sleepTo}:00)`);
|
|
5882
6029
|
} else if (!presence.online) {
|
|
5883
|
-
reasons.push(`\u{1F4F5} \
|
|
6030
|
+
reasons.push(`\u{1F4F5} \u0421\u0435\u0439\u0447\u0430\u0441 \u043E\u0444\u043B\u0430\u0439\u043D (${this.presenceProfile.pattern}) \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0447\u0435\u0440\u0435\u0437 ~${Math.ceil(presence.nextCheckSec / 60)} \u043C\u0438\u043D`);
|
|
5884
6031
|
}
|
|
5885
6032
|
if (coldActive) {
|
|
5886
6033
|
const hoursLeft = Math.ceil((new Date(conflict.coldUntil).getTime() - Date.now()) / 36e5);
|
|
@@ -5900,15 +6047,11 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5900
6047
|
if (rel.score.annoyance > 30) {
|
|
5901
6048
|
reasons.push(`\u{1F620} \u041E\u043D\u0430 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0435\u043D\u0430 (annoyance=${rel.score.annoyance})`);
|
|
5902
6049
|
}
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
- stage: ${stage.label}
|
|
5909
|
-
\u0415\u0441\u043B\u0438 \u0432\u0441\u0451 \u0440\u0430\u0432\u043D\u043E \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u2014 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E, LLM \u0434\u043E\u043B\u0433\u043E \u0434\u0443\u043C\u0430\u0435\u0442 \u0438\u043B\u0438 \u043F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430 (\u0441\u043C\u043E\u0442\u0440\u0438 :log).`;
|
|
5910
|
-
}
|
|
5911
|
-
return reasons.join("\n");
|
|
6050
|
+
return [
|
|
6051
|
+
`why \u0434\u043B\u044F ${target ?? "default"}:`,
|
|
6052
|
+
...reasons,
|
|
6053
|
+
`\u0442\u0435\u043A\u0443\u0449\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435: online=${presence.online ? "\u0434\u0430" : "\u043D\u0435\u0442"}, asleep=${presence.asleep ? "\u0434\u0430" : "\u043D\u0435\u0442"}, stage=${stage.label}, score=${JSON.stringify(rel.score)}`
|
|
6054
|
+
].join("\n");
|
|
5912
6055
|
}
|
|
5913
6056
|
async cmdAmnesia(minutesStr, chatId) {
|
|
5914
6057
|
const minutes = Number(minutesStr);
|
|
@@ -6050,42 +6193,12 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
6050
6193
|
this.emit("event", { type: "info", text: `AI tool: marked read ${chatId}`, chatId });
|
|
6051
6194
|
}
|
|
6052
6195
|
break;
|
|
6053
|
-
case "CLEAR":
|
|
6054
|
-
if (this.userbotActionAvailable("deleteDialogHistory")) {
|
|
6055
|
-
await this.tg.deleteDialogHistory?.(chatId, false);
|
|
6056
|
-
this.emit("event", { type: "info", text: `AI tool: cleared dialog ${chatId}`, chatId });
|
|
6057
|
-
}
|
|
6058
|
-
break;
|
|
6059
|
-
case "CLEAR_REVOKE":
|
|
6060
|
-
if (this.userbotActionAvailable("deleteDialogHistory")) {
|
|
6061
|
-
await this.tg.deleteDialogHistory?.(chatId, true);
|
|
6062
|
-
this.emit("event", { type: "info", text: `AI tool: cleared dialog ${chatId} (revoke)`, chatId });
|
|
6063
|
-
}
|
|
6064
|
-
break;
|
|
6065
6196
|
case "REPORT":
|
|
6066
6197
|
if (this.userbotActionAvailable("reportSpam")) {
|
|
6067
6198
|
await this.tg.reportSpam?.(chatId);
|
|
6068
6199
|
this.emit("event", { type: "info", text: `AI tool: reported spam ${chatId}`, chatId });
|
|
6069
6200
|
}
|
|
6070
6201
|
break;
|
|
6071
|
-
case "DELETE_LAST":
|
|
6072
|
-
if (this.actionAvailable("deleteMessages")) {
|
|
6073
|
-
const lastId = this.lastSentByChat.get(this.histKey(chatId));
|
|
6074
|
-
if (lastId) {
|
|
6075
|
-
await this.tg.deleteMessages?.(chatId, [lastId], true);
|
|
6076
|
-
this.emit("event", { type: "info", text: `AI tool: deleted last ${chatId}`, chatId });
|
|
6077
|
-
}
|
|
6078
|
-
}
|
|
6079
|
-
break;
|
|
6080
|
-
case "EDIT_LAST":
|
|
6081
|
-
if (this.actionAvailable("editLastMessage") && arg) {
|
|
6082
|
-
const lastId = this.lastSentByChat.get(this.histKey(chatId));
|
|
6083
|
-
if (lastId) {
|
|
6084
|
-
await this.tg.editLastMessage?.(chatId, lastId, arg);
|
|
6085
|
-
this.emit("event", { type: "info", text: `AI tool: edited last ${chatId}`, chatId });
|
|
6086
|
-
}
|
|
6087
|
-
}
|
|
6088
|
-
break;
|
|
6089
6202
|
case "STICKER":
|
|
6090
6203
|
if (this.actionAvailable("sendSticker")) {
|
|
6091
6204
|
const sticker = await pickSticker(this.cfg);
|
|
@@ -6103,9 +6216,38 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
6103
6216
|
}
|
|
6104
6217
|
}
|
|
6105
6218
|
};
|
|
6219
|
+
function normalizeForDuplicate(text) {
|
|
6220
|
+
return text.toLowerCase().replace(/\s+/g, " ").replace(/[.!?…)\]]+$/g, "").trim();
|
|
6221
|
+
}
|
|
6222
|
+
function isDuplicateAssistantBubble(hist, text) {
|
|
6223
|
+
const normalized = normalizeForDuplicate(text);
|
|
6224
|
+
if (!normalized) return true;
|
|
6225
|
+
return hist.slice(-8).filter((t) => t.role === "assistant").some((t) => normalizeForDuplicate(t.content) === normalized);
|
|
6226
|
+
}
|
|
6227
|
+
function dedupeBubbles(bubbles) {
|
|
6228
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6229
|
+
return bubbles.filter((bubble) => {
|
|
6230
|
+
const normalized = normalizeForDuplicate(bubble);
|
|
6231
|
+
if (!normalized || seen.has(normalized)) return false;
|
|
6232
|
+
seen.add(normalized);
|
|
6233
|
+
return true;
|
|
6234
|
+
});
|
|
6235
|
+
}
|
|
6106
6236
|
function sleep(ms) {
|
|
6107
6237
|
return new Promise((r) => setTimeout(r, ms));
|
|
6108
6238
|
}
|
|
6239
|
+
function withTimeout2(promise, ms) {
|
|
6240
|
+
let timer;
|
|
6241
|
+
return Promise.race([
|
|
6242
|
+
promise,
|
|
6243
|
+
new Promise((_, reject) => {
|
|
6244
|
+
timer = setTimeout(() => reject(new Error(`timeout after ${ms}ms`)), ms);
|
|
6245
|
+
timer.unref?.();
|
|
6246
|
+
})
|
|
6247
|
+
]).finally(() => {
|
|
6248
|
+
if (timer) clearTimeout(timer);
|
|
6249
|
+
});
|
|
6250
|
+
}
|
|
6109
6251
|
|
|
6110
6252
|
// src/headless.ts
|
|
6111
6253
|
init_esm_shims();
|
|
@@ -6157,27 +6299,6 @@ async function runHeadlessJsonEvents(rt) {
|
|
|
6157
6299
|
case "amnesia":
|
|
6158
6300
|
text = await rt.cmdAmnesia(rest[0], rest[1]);
|
|
6159
6301
|
break;
|
|
6160
|
-
case "block":
|
|
6161
|
-
text = await rt.cmdBlock(rest[0]);
|
|
6162
|
-
break;
|
|
6163
|
-
case "unblock":
|
|
6164
|
-
text = await rt.cmdUnblock(rest[0]);
|
|
6165
|
-
break;
|
|
6166
|
-
case "read":
|
|
6167
|
-
text = await rt.cmdRead(rest[0]);
|
|
6168
|
-
break;
|
|
6169
|
-
case "clear-chat":
|
|
6170
|
-
text = await rt.cmdClearChat(rest.find((x) => !x.startsWith("--")), rest.includes("--revoke"));
|
|
6171
|
-
break;
|
|
6172
|
-
case "report-spam":
|
|
6173
|
-
text = await rt.cmdReportSpam(rest[0]);
|
|
6174
|
-
break;
|
|
6175
|
-
case "delete-last":
|
|
6176
|
-
text = await rt.cmdDeleteLast(rest.find((x) => !x.startsWith("--")), !rest.includes("--local"));
|
|
6177
|
-
break;
|
|
6178
|
-
case "edit-last":
|
|
6179
|
-
text = await rt.cmdEditLast(rest.join(" "));
|
|
6180
|
-
break;
|
|
6181
6302
|
case "sticker":
|
|
6182
6303
|
text = await rt.cmdSticker(rest[0]);
|
|
6183
6304
|
break;
|
|
@@ -6226,7 +6347,7 @@ async function runHeadlessJsonEvents(rt) {
|
|
|
6226
6347
|
return;
|
|
6227
6348
|
}
|
|
6228
6349
|
case "help":
|
|
6229
|
-
text = ":status :why :amnesia :reset :stage :wake :debug :pause :resume :cringe :relationship :persona :log :
|
|
6350
|
+
text = ":status :why :amnesia :reset :stage :wake :debug :pause :resume :cringe :relationship :persona :log :sticker :snapshot :quit";
|
|
6230
6351
|
break;
|
|
6231
6352
|
case "quit":
|
|
6232
6353
|
case "exit":
|
|
@@ -6272,7 +6393,7 @@ function profileSummary(cfg) {
|
|
|
6272
6393
|
init_esm_shims();
|
|
6273
6394
|
import fs5 from "fs/promises";
|
|
6274
6395
|
import path6 from "path";
|
|
6275
|
-
import
|
|
6396
|
+
import os2 from "os";
|
|
6276
6397
|
|
|
6277
6398
|
// src/migrations/index.ts
|
|
6278
6399
|
init_esm_shims();
|
|
@@ -6485,7 +6606,7 @@ env-vars (\u0434\u043B\u044F CI / docker secrets / k8s):
|
|
|
6485
6606
|
GIRL_AGENT_TOKEN telegram bot token
|
|
6486
6607
|
GIRL_AGENT_API_PRESET openai|anthropic|claudehub|...
|
|
6487
6608
|
GIRL_AGENT_API_KEY \u043A\u043B\u044E\u0447 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430
|
|
6488
|
-
GIRL_AGENT_MODEL, _NAME, _AGE, _NATIONALITY, _TZ, _STAGE (id \u0438\u043B\u0438 \u043D\u043E\u043C\u0435\u0440 1-8), _COMM_PRESET
|
|
6609
|
+
GIRL_AGENT_MODEL, _NAME, _AGE, _NATIONALITY, _TZ, _STAGE (id \u0438\u043B\u0438 \u043D\u043E\u043C\u0435\u0440 1-8), _COMM_PRESET, _IGNORE_TENDENCY, _OWNER_ID
|
|
6489
6610
|
|
|
6490
6611
|
\u0434\u043B\u044F \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0439 \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0439 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0439 \u0431\u0435\u0437 \u0444\u043B\u0430\u0433\u043E\u0432 \u0432 \u043E\u0431\u044B\u0447\u043D\u043E\u043C \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 \u2014
|
|
6491
6612
|
\u043E\u0442\u043A\u0440\u043E\u0435\u0442\u0441\u044F ink-\u0432\u0438\u0437\u0430\u0440\u0434.
|
|
@@ -6658,14 +6779,17 @@ function configFromEnv() {
|
|
|
6658
6779
|
telegram: mode === "bot" ? { botToken: e.GIRL_AGENT_TOKEN ?? "" } : {
|
|
6659
6780
|
apiId: Number(e.GIRL_AGENT_TG_API_ID ?? 0),
|
|
6660
6781
|
apiHash: e.GIRL_AGENT_TG_API_HASH ?? "",
|
|
6661
|
-
phone: e.GIRL_AGENT_TG_PHONE ?? ""
|
|
6782
|
+
phone: e.GIRL_AGENT_TG_PHONE ?? "",
|
|
6783
|
+
proxy: parseTelegramProxy(e.GIRL_AGENT_TG_PROXY)
|
|
6662
6784
|
},
|
|
6663
6785
|
mcp: [],
|
|
6786
|
+
ownerId: normalizeOwnerId(e.GIRL_AGENT_OWNER_ID),
|
|
6664
6787
|
privacy: "owner-only",
|
|
6665
6788
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6666
6789
|
sleepFrom: Number(e.GIRL_AGENT_SLEEP_FROM ?? 23),
|
|
6667
6790
|
sleepTo: Number(e.GIRL_AGENT_SLEEP_TO ?? 8),
|
|
6668
6791
|
nightWakeChance: Number(e.GIRL_AGENT_NIGHT_WAKE ?? 0.05),
|
|
6792
|
+
ignoreTendency: Number(e.GIRL_AGENT_IGNORE_TENDENCY ?? 35),
|
|
6669
6793
|
communication: commPreset.profile,
|
|
6670
6794
|
vibe: commPreset.profile.messageStyle === "one-liners" ? "short" : "warm",
|
|
6671
6795
|
busySchedule: []
|
|
@@ -6727,11 +6851,13 @@ function validateConfig(raw) {
|
|
|
6727
6851
|
},
|
|
6728
6852
|
telegram: c.telegram ?? {},
|
|
6729
6853
|
mcp: c.mcp ?? [],
|
|
6854
|
+
ownerId: normalizeOwnerId(c.ownerId ?? process.env.GIRL_AGENT_OWNER_ID),
|
|
6730
6855
|
privacy: c.privacy ?? "owner-only",
|
|
6731
6856
|
createdAt: c.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6732
6857
|
sleepFrom: c.sleepFrom ?? 23,
|
|
6733
6858
|
sleepTo: c.sleepTo ?? 8,
|
|
6734
6859
|
nightWakeChance: c.nightWakeChance ?? 0.05,
|
|
6860
|
+
ignoreTendency: c.ignoreTendency ?? 35,
|
|
6735
6861
|
communication: c.communication ?? COMMUNICATION_PRESETS[0].profile,
|
|
6736
6862
|
vibe: c.vibe,
|
|
6737
6863
|
personaNotes: c.personaNotes,
|
|
@@ -6739,6 +6865,27 @@ function validateConfig(raw) {
|
|
|
6739
6865
|
};
|
|
6740
6866
|
return filled;
|
|
6741
6867
|
}
|
|
6868
|
+
function parseTelegramProxy(raw) {
|
|
6869
|
+
if (!raw?.trim()) return void 0;
|
|
6870
|
+
try {
|
|
6871
|
+
const url = new URL(raw);
|
|
6872
|
+
const socksType = url.protocol === "socks4:" ? 4 : 5;
|
|
6873
|
+
const port = Number(url.port);
|
|
6874
|
+
if (!url.hostname || !Number.isInteger(port) || port <= 0) return void 0;
|
|
6875
|
+
return {
|
|
6876
|
+
ip: url.hostname,
|
|
6877
|
+
port,
|
|
6878
|
+
socksType,
|
|
6879
|
+
username: url.username ? decodeURIComponent(url.username) : void 0,
|
|
6880
|
+
password: url.password ? decodeURIComponent(url.password) : void 0
|
|
6881
|
+
};
|
|
6882
|
+
} catch {
|
|
6883
|
+
const [host, portRaw] = raw.split(":");
|
|
6884
|
+
const port = Number(portRaw);
|
|
6885
|
+
if (!host || !Number.isInteger(port) || port <= 0) return void 0;
|
|
6886
|
+
return { ip: host, port, socksType: 5 };
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6742
6889
|
function buildConfigTemplate() {
|
|
6743
6890
|
const sample = {
|
|
6744
6891
|
slug: "anya",
|
|
@@ -6757,11 +6904,13 @@ function buildConfigTemplate() {
|
|
|
6757
6904
|
},
|
|
6758
6905
|
telegram: { botToken: "REPLACE_ME" },
|
|
6759
6906
|
mcp: [],
|
|
6907
|
+
ownerId: void 0,
|
|
6760
6908
|
privacy: "owner-only",
|
|
6761
6909
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6762
6910
|
sleepFrom: 23,
|
|
6763
6911
|
sleepTo: 8,
|
|
6764
6912
|
nightWakeChance: 0.05,
|
|
6913
|
+
ignoreTendency: 35,
|
|
6765
6914
|
communication: COMMUNICATION_PRESETS[0].profile,
|
|
6766
6915
|
vibe: "warm",
|
|
6767
6916
|
busySchedule: []
|
|
@@ -6769,7 +6918,7 @@ function buildConfigTemplate() {
|
|
|
6769
6918
|
return JSON.stringify(sample, null, 2) + "\n";
|
|
6770
6919
|
}
|
|
6771
6920
|
function buildSystemdUnit() {
|
|
6772
|
-
const home =
|
|
6921
|
+
const home = os2.homedir();
|
|
6773
6922
|
return `# /etc/systemd/system/girl-agent.service
|
|
6774
6923
|
# install: sudo cp this.service /etc/systemd/system/girl-agent.service
|
|
6775
6924
|
# sudo systemctl daemon-reload
|
|
@@ -6882,6 +7031,8 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
|
|
|
6882
7031
|
--message-style=<style> one-liners|balanced|bursty|longform
|
|
6883
7032
|
--initiative=<level> low|medium|high
|
|
6884
7033
|
--life-sharing=<level> low|medium|high
|
|
7034
|
+
--ignore-tendency=<0..100> \u0441\u043A\u043B\u043E\u043D\u043D\u043E\u0441\u0442\u044C \u043A \u0438\u0433\u043D\u043E\u0440\u0443 \u043A\u0430\u043A \u0432\u0435\u0441 decision-layer (\u043D\u0435 \u043F\u0440\u044F\u043C\u043E\u0439 \u0440\u0430\u043D\u0434\u043E\u043C)
|
|
7035
|
+
--owner-id=<tg_user_id> \u044F\u0432\u043D\u043E \u0437\u0430\u043A\u0440\u0435\u043F\u0438\u0442\u044C \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 (\u0435\u0441\u043B\u0438 bot mode \u043D\u0435 \u0443\u0437\u043D\u0430\u043B \u0442\u0435\u0431\u044F)
|
|
6885
7036
|
--privacy=<mode> owner-only|allow-strangers (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E owner-only)
|
|
6886
7037
|
--nationality=RU|UA (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E RU)
|
|
6887
7038
|
--tz=<value> IANA "Europe/Moscow" / "GMT+3" / "+3" / "\u041A\u0438\u0435\u0432" \u2014 \u043F\u043E\u0438\u0441\u043A
|
|
@@ -6895,7 +7046,7 @@ update:
|
|
|
6895
7046
|
npx girl-agent update # \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u0430\u043D\u043D\u044B\u0435 (\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438) \u0434\u043E \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438
|
|
6896
7047
|
npx girl-agent update --verbose # \u0441 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u044B\u043C \u0432\u044B\u0432\u043E\u0434\u043E\u043C
|
|
6897
7048
|
|
|
6898
|
-
\u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0435\u043C \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435: :status :reset :stage <id|num> :pause :resume :cringe :persona :log :quit
|
|
7049
|
+
\u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0435\u043C \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id|num> :pause :resume :cringe :persona :log :sticker :quit
|
|
6899
7050
|
`;
|
|
6900
7051
|
async function main() {
|
|
6901
7052
|
const argv = mri(process.argv.slice(2), {
|
|
@@ -6923,6 +7074,8 @@ async function main() {
|
|
|
6923
7074
|
"message-style",
|
|
6924
7075
|
"initiative",
|
|
6925
7076
|
"life-sharing",
|
|
7077
|
+
"ignore-tendency",
|
|
7078
|
+
"owner-id",
|
|
6926
7079
|
"privacy",
|
|
6927
7080
|
"config"
|
|
6928
7081
|
],
|
|
@@ -7107,10 +7260,12 @@ async function buildConfigFromFlags(argv) {
|
|
|
7107
7260
|
},
|
|
7108
7261
|
mcp: mcps,
|
|
7109
7262
|
privacy,
|
|
7263
|
+
ownerId: normalizeOwnerId(argv["owner-id"] ?? process.env.GIRL_AGENT_OWNER_ID),
|
|
7110
7264
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7111
7265
|
sleepFrom: 23,
|
|
7112
7266
|
sleepTo: 8,
|
|
7113
7267
|
nightWakeChance: 0.05,
|
|
7268
|
+
ignoreTendency: Number(argv["ignore-tendency"] ?? 35),
|
|
7114
7269
|
vibe: deriveLegacyVibe(communication),
|
|
7115
7270
|
communication,
|
|
7116
7271
|
personaNotes: argv["persona-notes"] ? String(argv["persona-notes"]) : void 0,
|