@thesashadev/girl-agent 0.1.15 → 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 +16 -0
- package/dist/cli.js +401 -210
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
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
|
+
|
|
12
|
+
## 0.1.16
|
|
13
|
+
|
|
14
|
+
Дата: 2026-05-08
|
|
15
|
+
|
|
16
|
+
- Merge pull request #63 from TheSashaDev/devin/1778244236-serialize-llm-requests
|
|
17
|
+
- Serialize LLM provider requests
|
|
18
|
+
|
|
3
19
|
## 0.1.15
|
|
4
20
|
|
|
5
21
|
Дата: 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 });
|
|
@@ -1229,6 +1271,31 @@ function openBrowser(url) {
|
|
|
1229
1271
|
// src/llm/index.ts
|
|
1230
1272
|
var LLM_TIMEOUT_MS = 12e4;
|
|
1231
1273
|
var LLM_MAX_RETRIES = 1;
|
|
1274
|
+
var llmQueueTail = Promise.resolve();
|
|
1275
|
+
var SerializedLLMClient = class {
|
|
1276
|
+
constructor(inner) {
|
|
1277
|
+
this.inner = inner;
|
|
1278
|
+
}
|
|
1279
|
+
inner;
|
|
1280
|
+
chat(messages, opts = {}) {
|
|
1281
|
+
return runExclusiveLLM(() => this.inner.chat(messages, opts));
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
async function runExclusiveLLM(task) {
|
|
1285
|
+
const previous = llmQueueTail.catch(() => void 0);
|
|
1286
|
+
let release = () => {
|
|
1287
|
+
};
|
|
1288
|
+
const current = new Promise((resolve) => {
|
|
1289
|
+
release = resolve;
|
|
1290
|
+
});
|
|
1291
|
+
llmQueueTail = previous.then(() => current);
|
|
1292
|
+
await previous;
|
|
1293
|
+
try {
|
|
1294
|
+
return await task();
|
|
1295
|
+
} finally {
|
|
1296
|
+
release();
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1232
1299
|
var OpenAILike = class {
|
|
1233
1300
|
constructor(cfg) {
|
|
1234
1301
|
this.cfg = cfg;
|
|
@@ -1533,11 +1600,49 @@ function errorMessage(error) {
|
|
|
1533
1600
|
return error instanceof Error ? error.message : String(error ?? "");
|
|
1534
1601
|
}
|
|
1535
1602
|
function makeLLM(cfg) {
|
|
1536
|
-
|
|
1603
|
+
const inner = cfg.proto === "anthropic" ? new AnthropicLike(cfg) : new OpenAILike(cfg);
|
|
1604
|
+
return new SerializedLLMClient(inner);
|
|
1537
1605
|
}
|
|
1538
1606
|
|
|
1539
1607
|
// src/engine/persona-gen.ts
|
|
1540
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
|
|
1541
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}}.`;
|
|
1542
1647
|
var WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
1543
1648
|
var BUSY_SCHEDULE_SCHEMA = {
|
|
@@ -1678,11 +1783,11 @@ ${personaNotes.trim()}
|
|
|
1678
1783
|
- days \u0442\u043E\u043B\u044C\u043A\u043E \u0438\u0437: mon, tue, wed, thu, fri, sat, sun.
|
|
1679
1784
|
- \u0411\u0435\u0437 markdown, \u0442\u043E\u043B\u044C\u043A\u043E JSON.`;
|
|
1680
1785
|
onProgress?.(5, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C persona.md\u2026");
|
|
1681
|
-
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 }));
|
|
1682
1787
|
onProgress?.(35, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C speech.md\u2026");
|
|
1683
|
-
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 }));
|
|
1684
1789
|
onProgress?.(65, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C communication.md\u2026");
|
|
1685
|
-
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 }));
|
|
1686
1791
|
onProgress?.(85, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C busy schedule\u2026");
|
|
1687
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 });
|
|
1688
1793
|
const busySchedule = parseBusySchedule(routineRaw, name, age);
|
|
@@ -1691,6 +1796,10 @@ ${personaNotes.trim()}
|
|
|
1691
1796
|
await writeMd(slug, "communication.md", boundaries);
|
|
1692
1797
|
return { persona, speech, boundaries, busySchedule };
|
|
1693
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
|
+
}
|
|
1694
1803
|
function parseBusySchedule(raw, name, age) {
|
|
1695
1804
|
try {
|
|
1696
1805
|
const start = raw.indexOf("{");
|
|
@@ -2028,6 +2137,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2028
2137
|
const [sleepToStr, setSleepToStr] = useState("8");
|
|
2029
2138
|
const [nightWakeStr, setNightWakeStr] = useState("5");
|
|
2030
2139
|
const [communicationProfile, setCommunicationProfile] = useState(normalizeCommunicationProfile(initial));
|
|
2140
|
+
const [ignoreTendencyStr, setIgnoreTendencyStr] = useState(String(normalizeIgnoreTendency(initial?.ignoreTendency)));
|
|
2031
2141
|
const [privacy, setPrivacy] = useState(initial?.privacy ?? "owner-only");
|
|
2032
2142
|
const [stage, setStage] = useState(initial?.stage ?? "tg-given-cold");
|
|
2033
2143
|
const [pickedMcp, setPickedMcp] = useState(initial?.mcp?.map((m) => m.id) ?? []);
|
|
@@ -2555,7 +2665,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2555
2665
|
}
|
|
2556
2666
|
const preset = findCommunicationPreset(String(it.value));
|
|
2557
2667
|
if (preset) setCommunicationProfile(preset.profile);
|
|
2558
|
-
setStep("
|
|
2668
|
+
setStep("ignore-tendency");
|
|
2559
2669
|
}
|
|
2560
2670
|
}
|
|
2561
2671
|
)));
|
|
@@ -2620,13 +2730,25 @@ function Wizard({ initial, onDone }) {
|
|
|
2620
2730
|
],
|
|
2621
2731
|
onSelect: (it) => {
|
|
2622
2732
|
setCommunicationProfile((p) => ({ ...p, lifeSharing: it.value }));
|
|
2623
|
-
setStep("
|
|
2733
|
+
setStep("ignore-tendency");
|
|
2624
2734
|
}
|
|
2625
2735
|
}
|
|
2626
2736
|
)));
|
|
2627
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
|
+
}
|
|
2628
2750
|
if (step === "privacy") {
|
|
2629
|
-
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(
|
|
2630
2752
|
SelectInput,
|
|
2631
2753
|
{
|
|
2632
2754
|
items: [
|
|
@@ -2642,7 +2764,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2642
2764
|
}
|
|
2643
2765
|
if (step === "tz") {
|
|
2644
2766
|
const matches = findTzByQuery(tzQuery, 8);
|
|
2645
|
-
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: () => {
|
|
2646
2768
|
if (matches[0]) {
|
|
2647
2769
|
setTz(matches[0].iana);
|
|
2648
2770
|
setStep("persona-notes");
|
|
@@ -2650,7 +2772,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2650
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")));
|
|
2651
2773
|
}
|
|
2652
2774
|
if (step === "persona-notes") {
|
|
2653
|
-
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.")));
|
|
2654
2776
|
}
|
|
2655
2777
|
if (step === "generating") {
|
|
2656
2778
|
const barWidth = 30;
|
|
@@ -2776,6 +2898,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2776
2898
|
sleepFrom: Number(sleepFromStr),
|
|
2777
2899
|
sleepTo: Number(sleepToStr),
|
|
2778
2900
|
nightWakeChance: Number(nightWakeStr) / 100,
|
|
2901
|
+
ignoreTendency: normalizeIgnoreTendency(ignoreTendencyStr),
|
|
2779
2902
|
vibe: deriveLegacyVibe(communicationProfile),
|
|
2780
2903
|
communication: communicationProfile,
|
|
2781
2904
|
personaNotes: personaNotes.trim() || void 0,
|
|
@@ -2876,27 +2999,6 @@ function Dashboard({ runtime }) {
|
|
|
2876
2999
|
case "amnesia":
|
|
2877
3000
|
append(await runtime.cmdAmnesia(rest[0], rest[1]));
|
|
2878
3001
|
break;
|
|
2879
|
-
case "block":
|
|
2880
|
-
append(await runtime.cmdBlock(rest[0]));
|
|
2881
|
-
break;
|
|
2882
|
-
case "unblock":
|
|
2883
|
-
append(await runtime.cmdUnblock(rest[0]));
|
|
2884
|
-
break;
|
|
2885
|
-
case "read":
|
|
2886
|
-
append(await runtime.cmdRead(rest[0]));
|
|
2887
|
-
break;
|
|
2888
|
-
case "clear-chat":
|
|
2889
|
-
append(await runtime.cmdClearChat(rest.find((x) => !x.startsWith("--")), rest.includes("--revoke")));
|
|
2890
|
-
break;
|
|
2891
|
-
case "report-spam":
|
|
2892
|
-
append(await runtime.cmdReportSpam(rest[0]));
|
|
2893
|
-
break;
|
|
2894
|
-
case "delete-last":
|
|
2895
|
-
append(await runtime.cmdDeleteLast(rest.find((x) => !x.startsWith("--")), !rest.includes("--local")));
|
|
2896
|
-
break;
|
|
2897
|
-
case "edit-last":
|
|
2898
|
-
append(await runtime.cmdEditLast(rest.join(" ")));
|
|
2899
|
-
break;
|
|
2900
3002
|
case "sticker":
|
|
2901
3003
|
append(await runtime.cmdSticker(rest[0]));
|
|
2902
3004
|
break;
|
|
@@ -2933,7 +3035,7 @@ function Dashboard({ runtime }) {
|
|
|
2933
3035
|
break;
|
|
2934
3036
|
}
|
|
2935
3037
|
case "help":
|
|
2936
|
-
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");
|
|
2937
3039
|
break;
|
|
2938
3040
|
case "quit":
|
|
2939
3041
|
case "exit":
|
|
@@ -2951,7 +3053,7 @@ function Dashboard({ runtime }) {
|
|
|
2951
3053
|
const line = cmd.trim();
|
|
2952
3054
|
setCmd("");
|
|
2953
3055
|
if (line) await execute(line);
|
|
2954
|
-
} })), /* @__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"));
|
|
2955
3057
|
}
|
|
2956
3058
|
|
|
2957
3059
|
// src/engine/runtime.ts
|
|
@@ -3130,6 +3232,11 @@ function isHourInRange(h, from, to) {
|
|
|
3130
3232
|
if (from < to) return h >= from && h < to;
|
|
3131
3233
|
return h >= from || h < to;
|
|
3132
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
|
+
}
|
|
3133
3240
|
var WEEKDAYS2 = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
3134
3241
|
function localParts(tz) {
|
|
3135
3242
|
try {
|
|
@@ -3257,9 +3364,10 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
|
|
|
3257
3364
|
nextCheckSec = Math.floor(hoursToWake * 3600) + Math.floor(Math.random() * 1800);
|
|
3258
3365
|
} else if (busySlot && !forcedWake) {
|
|
3259
3366
|
const busyMul = communication.notifications === "priority" ? 0.45 : communication.notifications === "muted" ? 1.25 : 1;
|
|
3367
|
+
const activeDialogMul = inActiveDialog ? 0.35 : 1;
|
|
3260
3368
|
const [rawMinCheck, rawMaxCheck] = busySlot.slot.checkAfterMin ?? [5, 15];
|
|
3261
|
-
const minCheck = Math.max(1, Math.round(rawMinCheck * busyMul));
|
|
3262
|
-
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));
|
|
3263
3371
|
if (maxCheck <= 5) {
|
|
3264
3372
|
const cycleMin = Math.max(1, Math.round((minCheck + maxCheck) / 2));
|
|
3265
3373
|
const minuteOfCycle = minuteOfDay % Math.max(1, cycleMin);
|
|
@@ -3272,7 +3380,9 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
|
|
|
3272
3380
|
} else {
|
|
3273
3381
|
online = false;
|
|
3274
3382
|
const checkAfterMin = minCheck + Math.floor(Math.random() * (maxCheck - minCheck + 1));
|
|
3275
|
-
|
|
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;
|
|
3276
3386
|
busy = { label: busySlot.slot.label, until: busySlot.until, checkAfterMin };
|
|
3277
3387
|
}
|
|
3278
3388
|
} else if (forcedWake) {
|
|
@@ -3286,10 +3396,14 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
|
|
|
3286
3396
|
const isNightOwl = localHour2 >= 22 || localHour2 < 8;
|
|
3287
3397
|
if (profile.pattern === "evening-only" && !isEvening) {
|
|
3288
3398
|
online = false;
|
|
3289
|
-
|
|
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);
|
|
3290
3402
|
} else if (profile.pattern === "phone-attached-night" && !isNightOwl) {
|
|
3291
3403
|
online = false;
|
|
3292
|
-
|
|
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);
|
|
3293
3407
|
} else {
|
|
3294
3408
|
const onlineProb = profile.onlineWindowMin / (profile.onlineWindowMin + profile.checkEveryMin);
|
|
3295
3409
|
online = Math.random() < onlineProb;
|
|
@@ -3869,6 +3983,7 @@ var ANTI_AI_RULES = `# \u041A\u0420\u0418\u0422\u0418\u0427\u0415\u0421\u041A\u0
|
|
|
3869
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
|
|
3870
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"
|
|
3871
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.
|
|
3872
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.
|
|
3873
3988
|
|
|
3874
3989
|
# \u0421\u041C\u0415\u0425 (\u0432\u0430\u0436\u043D\u0430\u044F \u043C\u0438\u043A\u0440\u043E-\u043C\u0435\u0445\u0430\u043D\u0438\u043A\u0430)
|
|
@@ -4009,16 +4124,13 @@ ${hits.map((h) => `- ${h.day}: ${h.excerpt}`).join("\n")}
|
|
|
4009
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.`}
|
|
4010
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.`}` : "";
|
|
4011
4126
|
const communicationFragment = communicationPromptFragment(communication);
|
|
4127
|
+
const ignoreTendency = ignoreTendencyPrompt(cfg.ignoreTendency);
|
|
4012
4128
|
const userbotTools = cfg.mode === "userbot" ? `# \u0414\u041E\u0421\u0422\u0423\u041F\u041D\u042B\u0415 \u0414\u0415\u0419\u0421\u0422\u0412\u0418\u042F (userbot)
|
|
4013
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:
|
|
4014
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
|
|
4015
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
|
|
4016
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)
|
|
4017
|
-
- [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
|
|
4018
|
-
- [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
|
|
4019
4133
|
- [REPORT] \u2014 \u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043D\u0430 \u0441\u043F\u0430\u043C
|
|
4020
|
-
- [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
|
|
4021
|
-
- [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
|
|
4022
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)
|
|
4023
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.
|
|
4024
4136
|
\u041F\u0440\u0438\u043C\u0435\u0440: "[READ]
|
|
@@ -4026,7 +4138,9 @@ ${ctx.romanticApproach ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435
|
|
|
4026
4138
|
\u0442\u044B \u0437\u0430\u0434\u043E\u043B\u0431\u0430\u043B"` : "";
|
|
4027
4139
|
return [
|
|
4028
4140
|
ANTI_AI_RULES,
|
|
4029
|
-
communicationFragment
|
|
4141
|
+
`${communicationFragment}
|
|
4142
|
+
|
|
4143
|
+
${ignoreTendency}`,
|
|
4030
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"}`,
|
|
4031
4145
|
ctx.tgUsername || ctx.tgDisplayName ? `# \u0422\u0432\u043E\u0439 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0432 \u0422\u0413${ctx.tgUsername ? `
|
|
4032
4146
|
\u0422\u0432\u043E\u0439 \u044E\u0437\u0435\u0440\u043D\u0435\u0439\u043C \u0432 \u0442\u0433: @${ctx.tgUsername.replace(/^@/, "")}` : ""}${ctx.tgDisplayName ? `
|
|
@@ -4135,6 +4249,7 @@ ${reactionsHint}
|
|
|
4135
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.
|
|
4136
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.
|
|
4137
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 ")".
|
|
4138
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.
|
|
4139
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.
|
|
4140
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 +.
|
|
@@ -4147,11 +4262,13 @@ async function behaviorTick(llm, cfg, recentHistory, incoming, ctx = {}) {
|
|
|
4147
4262
|
const stage = findStage(cfg.stage);
|
|
4148
4263
|
const rel = await readRelationship(cfg.slug);
|
|
4149
4264
|
const communication = normalizeCommunicationProfile(cfg);
|
|
4265
|
+
const ignoreTendency = normalizeIgnoreTendency(cfg.ignoreTendency);
|
|
4150
4266
|
const state = `stage=${cfg.stage} (${stage.label})
|
|
4151
4267
|
score=${JSON.stringify(rel.score)}
|
|
4152
4268
|
base_ignore=${stage.defaults.ignoreChance}
|
|
4153
4269
|
base_delay=${stage.defaults.replyDelaySec.join("..")}s
|
|
4154
|
-
${communicationDecisionState(communication)}
|
|
4270
|
+
${communicationDecisionState(communication)}
|
|
4271
|
+
${ignoreTendencyPrompt(ignoreTendency)}`;
|
|
4155
4272
|
const reactionsHint = reactionMenu(cfg.stage, rel.score);
|
|
4156
4273
|
const history = recentHistory.slice(-8).map((m) => `${m.role === "user" ? "\u043E\u043D" : "\u043E\u043D\u0430"}: ${m.content}`).join("\n");
|
|
4157
4274
|
if (ctx.activeDialog && !ctx.conflictColdActive) {
|
|
@@ -4179,7 +4296,7 @@ ${communicationDecisionState(communication)}`;
|
|
|
4179
4296
|
intent: "ignore"
|
|
4180
4297
|
};
|
|
4181
4298
|
}
|
|
4182
|
-
const ignoreMul = ignoreMultiplier(communication);
|
|
4299
|
+
const ignoreMul = ignoreMultiplier(communication, ignoreTendency);
|
|
4183
4300
|
const sleepIgnoreMul = communication.notifications === "priority" ? 0.8 : communication.notifications === "muted" ? 1 : 0.9;
|
|
4184
4301
|
if (ctx.presence?.asleep && !ctx.presence.nightAwake && Math.random() < 0.85 * sleepIgnoreMul) {
|
|
4185
4302
|
return {
|
|
@@ -4237,7 +4354,7 @@ ${communicationDecisionState(communication)}`;
|
|
|
4237
4354
|
let shouldReply = !!parsed.shouldReply && intent !== "ignore" && intent !== "left-on-read" && intent !== "reaction-only";
|
|
4238
4355
|
let delaySec = parsed.delaySec ?? 30;
|
|
4239
4356
|
let bubbles = parsed.bubbles ?? sampleBubbles(communication, false);
|
|
4240
|
-
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)) {
|
|
4241
4358
|
shouldReply = true;
|
|
4242
4359
|
intent = communication.messageStyle === "one-liners" ? "short" : "reply";
|
|
4243
4360
|
delaySec = recoverDelay(communication, ctx);
|
|
@@ -4283,11 +4400,12 @@ function sanitizeReaction(emoji, stage, score) {
|
|
|
4283
4400
|
}
|
|
4284
4401
|
return emoji;
|
|
4285
4402
|
}
|
|
4286
|
-
function ignoreMultiplier(profile) {
|
|
4403
|
+
function ignoreMultiplier(profile, ignoreTendency) {
|
|
4287
4404
|
let mul = profile.notifications === "priority" ? 0.3 : profile.notifications === "muted" ? 1.15 : 0.75;
|
|
4288
4405
|
if (profile.initiative === "high") mul *= 0.75;
|
|
4289
4406
|
if (profile.lifeSharing === "high") mul *= 0.85;
|
|
4290
4407
|
if (profile.messageStyle === "one-liners" && profile.initiative === "low") mul *= 1.15;
|
|
4408
|
+
mul *= 0.35 + normalizeIgnoreTendency(ignoreTendency) / 35;
|
|
4291
4409
|
return mul;
|
|
4292
4410
|
}
|
|
4293
4411
|
function activeDialogDelay(profile) {
|
|
@@ -4317,11 +4435,12 @@ function canRecoverReply(stage, score, ctx) {
|
|
|
4317
4435
|
if (stage === "tg-given-cold" && score.interest < 20 && score.attraction < 20) return false;
|
|
4318
4436
|
return true;
|
|
4319
4437
|
}
|
|
4320
|
-
function recoverReplyChance(profile, score) {
|
|
4438
|
+
function recoverReplyChance(profile, score, ignoreTendency) {
|
|
4321
4439
|
let chance = profile.notifications === "priority" ? 0.72 : profile.notifications === "muted" ? 0.16 : 0.38;
|
|
4322
4440
|
if (profile.initiative === "high") chance += 0.16;
|
|
4323
4441
|
if (profile.initiative === "low") chance -= 0.1;
|
|
4324
4442
|
if (profile.lifeSharing === "high") chance += 0.08;
|
|
4443
|
+
chance -= (normalizeIgnoreTendency(ignoreTendency) - 35) / 100;
|
|
4325
4444
|
if (score.interest > 40) chance += 0.12;
|
|
4326
4445
|
if (score.attraction > 50) chance += 0.1;
|
|
4327
4446
|
if (score.annoyance > 30) chance -= 0.2;
|
|
@@ -4534,9 +4653,9 @@ function localParts2(tz, when) {
|
|
|
4534
4653
|
}
|
|
4535
4654
|
}
|
|
4536
4655
|
function maxAutonomousItems(stage, initiative) {
|
|
4537
|
-
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;
|
|
4538
4657
|
if (initiative === "low") base = Math.max(0, base - 1);
|
|
4539
|
-
if (initiative === "high") base +=
|
|
4658
|
+
if (initiative === "high") base += 1;
|
|
4540
4659
|
return base;
|
|
4541
4660
|
}
|
|
4542
4661
|
function isDuringSleep(cfg, when) {
|
|
@@ -4631,7 +4750,7 @@ ${currentAgenda.length ? JSON.stringify(currentAgenda.filter((a) => a.state ===
|
|
|
4631
4750
|
async function extractAgendaUpdates(llm, cfg, history, incoming, chatId) {
|
|
4632
4751
|
const stage = findStage(cfg.stage);
|
|
4633
4752
|
const communication = normalizeCommunicationProfile(cfg);
|
|
4634
|
-
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") {
|
|
4635
4754
|
return { created: 0, updated: 0, cancelled: 0 };
|
|
4636
4755
|
}
|
|
4637
4756
|
const persona = (await readMd(cfg.slug, "persona.md")).slice(0, 800);
|
|
@@ -4940,29 +5059,6 @@ async function closeCurrentSession(llm, cfg) {
|
|
|
4940
5059
|
return !!r;
|
|
4941
5060
|
}
|
|
4942
5061
|
|
|
4943
|
-
// src/engine/security.ts
|
|
4944
|
-
init_esm_shims();
|
|
4945
|
-
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;
|
|
4946
|
-
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;
|
|
4947
|
-
function looksLikeJailbreak(text) {
|
|
4948
|
-
return JAILBREAK_RE.test(text);
|
|
4949
|
-
}
|
|
4950
|
-
function sanitizeModelReply(reply) {
|
|
4951
|
-
const cleaned = reply.replace(/```[\s\S]*?```/g, "").replace(/\b(system|developer|assistant|user)\s*:/gi, "").replace(/как (?:искусственный интеллект|ии|ai)[^\n.]*/gi, "").trim();
|
|
4952
|
-
if (!cleaned || TECHNICAL_ERROR_RE.test(cleaned)) return "";
|
|
4953
|
-
if (looksLikeJailbreak(cleaned) && cleaned.length > 80) return "";
|
|
4954
|
-
return cleaned;
|
|
4955
|
-
}
|
|
4956
|
-
function isTechnicalError(e) {
|
|
4957
|
-
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
4958
|
-
return TECHNICAL_ERROR_RE.test(msg);
|
|
4959
|
-
}
|
|
4960
|
-
function silentErrorLabel(e) {
|
|
4961
|
-
const msg = e instanceof Error ? e.message : String(e ?? "unknown");
|
|
4962
|
-
if (isTechnicalError(e)) return "llm/provider unavailable";
|
|
4963
|
-
return msg.slice(0, 160);
|
|
4964
|
-
}
|
|
4965
|
-
|
|
4966
5062
|
// src/engine/stickers.ts
|
|
4967
5063
|
init_esm_shims();
|
|
4968
5064
|
import { promises as fs3 } from "fs";
|
|
@@ -5006,6 +5102,7 @@ var Runtime = class extends EventEmitter {
|
|
|
5006
5102
|
constructor(cfg) {
|
|
5007
5103
|
super();
|
|
5008
5104
|
this.cfg = cfg;
|
|
5105
|
+
this.cfg.ownerId = normalizeOwnerId(cfg.ownerId ?? process.env.GIRL_AGENT_OWNER_ID);
|
|
5009
5106
|
this.llm = makeLLM(cfg.llm);
|
|
5010
5107
|
}
|
|
5011
5108
|
cfg;
|
|
@@ -5033,6 +5130,8 @@ var Runtime = class extends EventEmitter {
|
|
|
5033
5130
|
pendingReplyTimers = /* @__PURE__ */ new Map();
|
|
5034
5131
|
pendingReplySeq = /* @__PURE__ */ new Map();
|
|
5035
5132
|
pendingReplyIncoming = /* @__PURE__ */ new Map();
|
|
5133
|
+
pendingReplyDueAt = /* @__PURE__ */ new Map();
|
|
5134
|
+
lastDecision = /* @__PURE__ */ new Map();
|
|
5036
5135
|
incomingSeq = /* @__PURE__ */ new Map();
|
|
5037
5136
|
tgSelf = {};
|
|
5038
5137
|
async start() {
|
|
@@ -5049,15 +5148,20 @@ var Runtime = class extends EventEmitter {
|
|
|
5049
5148
|
this.agendaTimer = setInterval(() => this.tickAgenda().catch(
|
|
5050
5149
|
(e) => this.emit("event", { type: "error", text: "agenda tick: " + e.message })
|
|
5051
5150
|
), 6e4);
|
|
5151
|
+
this.agendaTimer.unref?.();
|
|
5052
5152
|
this.dailyTimer = setInterval(() => this.dailyMaintenance().catch(
|
|
5053
5153
|
(e) => this.emit("event", { type: "error", text: "daily maintenance: " + e.message })
|
|
5054
5154
|
), 30 * 6e4);
|
|
5155
|
+
this.dailyTimer.unref?.();
|
|
5055
5156
|
}
|
|
5056
5157
|
async stop() {
|
|
5057
5158
|
if (this.agendaTimer) clearInterval(this.agendaTimer);
|
|
5058
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();
|
|
5059
5163
|
try {
|
|
5060
|
-
const made = await closeCurrentSession(this.llm, this.cfg);
|
|
5164
|
+
const made = await withTimeout2(closeCurrentSession(this.llm, this.cfg), 3500);
|
|
5061
5165
|
if (made) this.emit("event", { type: "info", text: "daily summary \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430" });
|
|
5062
5166
|
} catch (e) {
|
|
5063
5167
|
this.emit("event", { type: "error", text: "daily summary: " + e.message });
|
|
@@ -5080,21 +5184,46 @@ var Runtime = class extends EventEmitter {
|
|
|
5080
5184
|
scheduleReply(key, chatId, hist, tick, scope, romanticApproach, incoming, presenceHint, delaySec) {
|
|
5081
5185
|
const existing = this.pendingReplyTimers.get(key);
|
|
5082
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");
|
|
5083
5188
|
const seq = (this.pendingReplySeq.get(key) ?? 0) + 1;
|
|
5084
5189
|
this.pendingReplySeq.set(key, seq);
|
|
5085
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
|
+
});
|
|
5086
5207
|
const timer = setTimeout(() => {
|
|
5087
5208
|
if (this.pendingReplySeq.get(key) !== seq) return;
|
|
5088
5209
|
this.pendingReplyTimers.delete(key);
|
|
5210
|
+
this.pendingReplyDueAt.delete(key);
|
|
5089
5211
|
const latestIncoming = this.pendingReplyIncoming.get(key) ?? incoming;
|
|
5090
5212
|
this.pendingReplyIncoming.delete(key);
|
|
5091
5213
|
const latestHist = this.histories.get(key) ?? hist;
|
|
5214
|
+
this.setDecisionStatus(key, "sending");
|
|
5092
5215
|
this.generateAndSend(chatId, latestHist, tick, scope, romanticApproach, latestIncoming, presenceHint).catch(
|
|
5093
5216
|
(e) => this.emit("event", { type: "error", text: silentErrorLabel(e) })
|
|
5094
5217
|
);
|
|
5095
5218
|
}, delaySec * 1e3);
|
|
5219
|
+
timer.unref?.();
|
|
5096
5220
|
this.pendingReplyTimers.set(key, timer);
|
|
5097
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
|
+
}
|
|
5098
5227
|
isPrimaryFrom(fromId) {
|
|
5099
5228
|
return this.cfg.ownerId === fromId;
|
|
5100
5229
|
}
|
|
@@ -5105,7 +5234,12 @@ var Runtime = class extends EventEmitter {
|
|
|
5105
5234
|
return ["dating-early", "dating-stable", "long-term"].includes(this.cfg.stage);
|
|
5106
5235
|
}
|
|
5107
5236
|
async ensureOwner(fromId) {
|
|
5108
|
-
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
|
+
}
|
|
5109
5243
|
this.cfg.ownerId = fromId;
|
|
5110
5244
|
await writeConfig(this.cfg);
|
|
5111
5245
|
this.emit("event", { type: "info", text: `primary owner \u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D: ${fromId}` });
|
|
@@ -5197,6 +5331,10 @@ ${m.text}` : media;
|
|
|
5197
5331
|
}
|
|
5198
5332
|
for (let i = 0; i < bubbles.length; i++) {
|
|
5199
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
|
+
}
|
|
5200
5338
|
if (typing) {
|
|
5201
5339
|
await this.tg.setTyping(chatId, true).catch(() => {
|
|
5202
5340
|
});
|
|
@@ -5229,6 +5367,7 @@ ${m.text}` : media;
|
|
|
5229
5367
|
async sendSafeFallback(chatId, hist, scope) {
|
|
5230
5368
|
if (this.userbotActionAvailable("readHistory")) await this.tg.readHistory?.(chatId).catch(() => {
|
|
5231
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");
|
|
5232
5371
|
this.emit("event", { type: "ignored", text: hist[hist.length - 1]?.content ?? "", reason: "silent-fallback" });
|
|
5233
5372
|
if (scope === "primary") await appendSessionLog(this.cfg.slug, this.cfg.tz, " -> ignored (silent-fallback)");
|
|
5234
5373
|
}
|
|
@@ -5435,6 +5574,24 @@ ${m.text}` : media;
|
|
|
5435
5574
|
activeDialog
|
|
5436
5575
|
});
|
|
5437
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
|
+
};
|
|
5438
5595
|
if (tick.moodDelta) {
|
|
5439
5596
|
const rel = await readRelationship(this.cfg.slug);
|
|
5440
5597
|
const newScore = applyMoodDelta(rel.score, tick.moodDelta);
|
|
@@ -5483,9 +5640,10 @@ ${m.text}` : media;
|
|
|
5483
5640
|
this.emit("event", { type: "info", text: `\u0440\u0435\u0430\u043A\u0446\u0438\u044F ${tick.reaction} \u043D\u0430 "${incomingText.slice(0, 40)}"` });
|
|
5484
5641
|
appendSessionLog(this.cfg.slug, this.cfg.tz, ` -> reaction ${tick.reaction}`).catch(() => {
|
|
5485
5642
|
});
|
|
5486
|
-
}, reactDelay);
|
|
5643
|
+
}, reactDelay).unref?.();
|
|
5487
5644
|
}
|
|
5488
5645
|
if (!tick.shouldReply) {
|
|
5646
|
+
this.lastDecision.set(key, baseDecision);
|
|
5489
5647
|
if (tick.shouldRead && this.userbotActionAvailable("readHistory")) {
|
|
5490
5648
|
await this.tg.readHistory?.(m.chatId).catch(() => {
|
|
5491
5649
|
});
|
|
@@ -5499,6 +5657,7 @@ ${m.text}` : media;
|
|
|
5499
5657
|
delaySec = Math.max(delaySec, presence.nextCheckSec);
|
|
5500
5658
|
}
|
|
5501
5659
|
delaySec = Math.min(delaySec, presence.busy ? 24 * 3600 : 3600);
|
|
5660
|
+
this.lastDecision.set(key, { ...baseDecision, delaySec, dueAt: Date.now() + delaySec * 1e3 });
|
|
5502
5661
|
this.scheduleReply(key, m.chatId, hist, tick, "primary", false, m, presence.hint, delaySec);
|
|
5503
5662
|
} catch (e) {
|
|
5504
5663
|
this.emit("event", { type: "error", text: `handleIncoming: ${silentErrorLabel(e)}` });
|
|
@@ -5559,8 +5718,9 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
|
|
|
5559
5718
|
for (const action of actions) {
|
|
5560
5719
|
await this.executeToolAction(action, chatId);
|
|
5561
5720
|
}
|
|
5562
|
-
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));
|
|
5563
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");
|
|
5564
5724
|
if (scope === "primary") {
|
|
5565
5725
|
recordInteractionMemory(this.llm, this.cfg, lastUser ?? "", sent.join(" / ")).catch(() => {
|
|
5566
5726
|
});
|
|
@@ -5618,7 +5778,11 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
|
|
|
5618
5778
|
await markAgendaFired(this.cfg.slug, item.id);
|
|
5619
5779
|
return;
|
|
5620
5780
|
}
|
|
5621
|
-
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
|
+
}
|
|
5622
5786
|
for (let i = 0; i < bubbles.length; i++) {
|
|
5623
5787
|
const piece = bubbles[i];
|
|
5624
5788
|
if (i > 0) {
|
|
@@ -5728,9 +5892,17 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5728
5892
|
}
|
|
5729
5893
|
async cmdWake(chatId) {
|
|
5730
5894
|
const now = Date.now();
|
|
5731
|
-
this.
|
|
5732
|
-
|
|
5733
|
-
|
|
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`;
|
|
5734
5906
|
}
|
|
5735
5907
|
async cmdBlock(chatId) {
|
|
5736
5908
|
this.requireUserbotAction("blockContact");
|
|
@@ -5750,12 +5922,6 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5750
5922
|
await this.tg.readHistory?.(target);
|
|
5751
5923
|
return `userbot: marked read ${target}`;
|
|
5752
5924
|
}
|
|
5753
|
-
async cmdClearChat(chatId, revoke = false) {
|
|
5754
|
-
this.requireUserbotAction("deleteDialogHistory");
|
|
5755
|
-
const target = this.resolveChatRef(chatId);
|
|
5756
|
-
await this.tg.deleteDialogHistory?.(target, revoke);
|
|
5757
|
-
return `userbot: cleared dialog ${target}${revoke ? " with revoke" : ""}`;
|
|
5758
|
-
}
|
|
5759
5925
|
async cmdReportSpam(chatId) {
|
|
5760
5926
|
this.requireUserbotAction("reportSpam");
|
|
5761
5927
|
const target = this.resolveChatRef(chatId);
|
|
@@ -5770,15 +5936,6 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5770
5936
|
await this.tg.deleteMessages?.(target, [lastId], revoke);
|
|
5771
5937
|
return `deleted last message ${lastId} in ${target}`;
|
|
5772
5938
|
}
|
|
5773
|
-
async cmdEditLast(text, chatId) {
|
|
5774
|
-
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");
|
|
5775
|
-
if (!text.trim()) throw new Error("\u0442\u0435\u043A\u0441\u0442 \u0434\u043B\u044F edit \u043F\u0443\u0441\u0442\u043E\u0439");
|
|
5776
|
-
const target = this.resolveChatRef(chatId);
|
|
5777
|
-
const lastId = this.lastSentByChat.get(this.histKey(target));
|
|
5778
|
-
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");
|
|
5779
|
-
await this.tg.editLastMessage?.(target, lastId, text.trim());
|
|
5780
|
-
return `edited last message ${lastId} in ${target}`;
|
|
5781
|
-
}
|
|
5782
5939
|
async cmdSticker(chatId) {
|
|
5783
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");
|
|
5784
5941
|
const target = this.resolveChatRef(chatId);
|
|
@@ -5821,12 +5978,13 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5821
5978
|
}
|
|
5822
5979
|
async cmdWhy(chatId) {
|
|
5823
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";
|
|
5824
|
-
const
|
|
5981
|
+
const target = chatId ? this.resolveChatRef(chatId) : this.cfg.ownerId;
|
|
5982
|
+
const key = target !== void 0 ? this.histKey(target) : this.histKey("default");
|
|
5825
5983
|
const rel = await readRelationship(this.cfg.slug);
|
|
5826
5984
|
const stage = findStage(this.cfg.stage);
|
|
5827
5985
|
const conflict = await readConflict(this.cfg.slug);
|
|
5828
5986
|
const { coldActive } = activeConflict(conflict);
|
|
5829
|
-
const forcedWake = Date.now() < this.forcedWakeUntil;
|
|
5987
|
+
const forcedWake = Date.now() < this.forcedWakeUntil && (!this.forcedWakeChatId || this.forcedWakeChatId === key);
|
|
5830
5988
|
const presence = computePresenceState(
|
|
5831
5989
|
this.cfg,
|
|
5832
5990
|
this.presenceProfile,
|
|
@@ -5838,13 +5996,38 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5838
5996
|
);
|
|
5839
5997
|
const block = this.dailyLife ? currentBlock(this.dailyLife, this.cfg.tz) : void 0;
|
|
5840
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
|
+
}
|
|
5841
6024
|
if (forcedWake) {
|
|
5842
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`);
|
|
5843
6026
|
}
|
|
5844
|
-
if (presence.asleep) {
|
|
5845
|
-
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)`);
|
|
5846
6029
|
} else if (!presence.online) {
|
|
5847
|
-
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`);
|
|
5848
6031
|
}
|
|
5849
6032
|
if (coldActive) {
|
|
5850
6033
|
const hoursLeft = Math.ceil((new Date(conflict.coldUntil).getTime() - Date.now()) / 36e5);
|
|
@@ -5864,15 +6047,11 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
5864
6047
|
if (rel.score.annoyance > 30) {
|
|
5865
6048
|
reasons.push(`\u{1F620} \u041E\u043D\u0430 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0435\u043D\u0430 (annoyance=${rel.score.annoyance})`);
|
|
5866
6049
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
- stage: ${stage.label}
|
|
5873
|
-
\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).`;
|
|
5874
|
-
}
|
|
5875
|
-
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");
|
|
5876
6055
|
}
|
|
5877
6056
|
async cmdAmnesia(minutesStr, chatId) {
|
|
5878
6057
|
const minutes = Number(minutesStr);
|
|
@@ -6014,42 +6193,12 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
6014
6193
|
this.emit("event", { type: "info", text: `AI tool: marked read ${chatId}`, chatId });
|
|
6015
6194
|
}
|
|
6016
6195
|
break;
|
|
6017
|
-
case "CLEAR":
|
|
6018
|
-
if (this.userbotActionAvailable("deleteDialogHistory")) {
|
|
6019
|
-
await this.tg.deleteDialogHistory?.(chatId, false);
|
|
6020
|
-
this.emit("event", { type: "info", text: `AI tool: cleared dialog ${chatId}`, chatId });
|
|
6021
|
-
}
|
|
6022
|
-
break;
|
|
6023
|
-
case "CLEAR_REVOKE":
|
|
6024
|
-
if (this.userbotActionAvailable("deleteDialogHistory")) {
|
|
6025
|
-
await this.tg.deleteDialogHistory?.(chatId, true);
|
|
6026
|
-
this.emit("event", { type: "info", text: `AI tool: cleared dialog ${chatId} (revoke)`, chatId });
|
|
6027
|
-
}
|
|
6028
|
-
break;
|
|
6029
6196
|
case "REPORT":
|
|
6030
6197
|
if (this.userbotActionAvailable("reportSpam")) {
|
|
6031
6198
|
await this.tg.reportSpam?.(chatId);
|
|
6032
6199
|
this.emit("event", { type: "info", text: `AI tool: reported spam ${chatId}`, chatId });
|
|
6033
6200
|
}
|
|
6034
6201
|
break;
|
|
6035
|
-
case "DELETE_LAST":
|
|
6036
|
-
if (this.actionAvailable("deleteMessages")) {
|
|
6037
|
-
const lastId = this.lastSentByChat.get(this.histKey(chatId));
|
|
6038
|
-
if (lastId) {
|
|
6039
|
-
await this.tg.deleteMessages?.(chatId, [lastId], true);
|
|
6040
|
-
this.emit("event", { type: "info", text: `AI tool: deleted last ${chatId}`, chatId });
|
|
6041
|
-
}
|
|
6042
|
-
}
|
|
6043
|
-
break;
|
|
6044
|
-
case "EDIT_LAST":
|
|
6045
|
-
if (this.actionAvailable("editLastMessage") && arg) {
|
|
6046
|
-
const lastId = this.lastSentByChat.get(this.histKey(chatId));
|
|
6047
|
-
if (lastId) {
|
|
6048
|
-
await this.tg.editLastMessage?.(chatId, lastId, arg);
|
|
6049
|
-
this.emit("event", { type: "info", text: `AI tool: edited last ${chatId}`, chatId });
|
|
6050
|
-
}
|
|
6051
|
-
}
|
|
6052
|
-
break;
|
|
6053
6202
|
case "STICKER":
|
|
6054
6203
|
if (this.actionAvailable("sendSticker")) {
|
|
6055
6204
|
const sticker = await pickSticker(this.cfg);
|
|
@@ -6067,9 +6216,38 @@ ${herLastMessages.map((m) => `- "${m}"`).join("\n")}
|
|
|
6067
6216
|
}
|
|
6068
6217
|
}
|
|
6069
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
|
+
}
|
|
6070
6236
|
function sleep(ms) {
|
|
6071
6237
|
return new Promise((r) => setTimeout(r, ms));
|
|
6072
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
|
+
}
|
|
6073
6251
|
|
|
6074
6252
|
// src/headless.ts
|
|
6075
6253
|
init_esm_shims();
|
|
@@ -6121,27 +6299,6 @@ async function runHeadlessJsonEvents(rt) {
|
|
|
6121
6299
|
case "amnesia":
|
|
6122
6300
|
text = await rt.cmdAmnesia(rest[0], rest[1]);
|
|
6123
6301
|
break;
|
|
6124
|
-
case "block":
|
|
6125
|
-
text = await rt.cmdBlock(rest[0]);
|
|
6126
|
-
break;
|
|
6127
|
-
case "unblock":
|
|
6128
|
-
text = await rt.cmdUnblock(rest[0]);
|
|
6129
|
-
break;
|
|
6130
|
-
case "read":
|
|
6131
|
-
text = await rt.cmdRead(rest[0]);
|
|
6132
|
-
break;
|
|
6133
|
-
case "clear-chat":
|
|
6134
|
-
text = await rt.cmdClearChat(rest.find((x) => !x.startsWith("--")), rest.includes("--revoke"));
|
|
6135
|
-
break;
|
|
6136
|
-
case "report-spam":
|
|
6137
|
-
text = await rt.cmdReportSpam(rest[0]);
|
|
6138
|
-
break;
|
|
6139
|
-
case "delete-last":
|
|
6140
|
-
text = await rt.cmdDeleteLast(rest.find((x) => !x.startsWith("--")), !rest.includes("--local"));
|
|
6141
|
-
break;
|
|
6142
|
-
case "edit-last":
|
|
6143
|
-
text = await rt.cmdEditLast(rest.join(" "));
|
|
6144
|
-
break;
|
|
6145
6302
|
case "sticker":
|
|
6146
6303
|
text = await rt.cmdSticker(rest[0]);
|
|
6147
6304
|
break;
|
|
@@ -6190,7 +6347,7 @@ async function runHeadlessJsonEvents(rt) {
|
|
|
6190
6347
|
return;
|
|
6191
6348
|
}
|
|
6192
6349
|
case "help":
|
|
6193
|
-
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";
|
|
6194
6351
|
break;
|
|
6195
6352
|
case "quit":
|
|
6196
6353
|
case "exit":
|
|
@@ -6236,7 +6393,7 @@ function profileSummary(cfg) {
|
|
|
6236
6393
|
init_esm_shims();
|
|
6237
6394
|
import fs5 from "fs/promises";
|
|
6238
6395
|
import path6 from "path";
|
|
6239
|
-
import
|
|
6396
|
+
import os2 from "os";
|
|
6240
6397
|
|
|
6241
6398
|
// src/migrations/index.ts
|
|
6242
6399
|
init_esm_shims();
|
|
@@ -6449,7 +6606,7 @@ env-vars (\u0434\u043B\u044F CI / docker secrets / k8s):
|
|
|
6449
6606
|
GIRL_AGENT_TOKEN telegram bot token
|
|
6450
6607
|
GIRL_AGENT_API_PRESET openai|anthropic|claudehub|...
|
|
6451
6608
|
GIRL_AGENT_API_KEY \u043A\u043B\u044E\u0447 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430
|
|
6452
|
-
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
|
|
6453
6610
|
|
|
6454
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
|
|
6455
6612
|
\u043E\u0442\u043A\u0440\u043E\u0435\u0442\u0441\u044F ink-\u0432\u0438\u0437\u0430\u0440\u0434.
|
|
@@ -6622,14 +6779,17 @@ function configFromEnv() {
|
|
|
6622
6779
|
telegram: mode === "bot" ? { botToken: e.GIRL_AGENT_TOKEN ?? "" } : {
|
|
6623
6780
|
apiId: Number(e.GIRL_AGENT_TG_API_ID ?? 0),
|
|
6624
6781
|
apiHash: e.GIRL_AGENT_TG_API_HASH ?? "",
|
|
6625
|
-
phone: e.GIRL_AGENT_TG_PHONE ?? ""
|
|
6782
|
+
phone: e.GIRL_AGENT_TG_PHONE ?? "",
|
|
6783
|
+
proxy: parseTelegramProxy(e.GIRL_AGENT_TG_PROXY)
|
|
6626
6784
|
},
|
|
6627
6785
|
mcp: [],
|
|
6786
|
+
ownerId: normalizeOwnerId(e.GIRL_AGENT_OWNER_ID),
|
|
6628
6787
|
privacy: "owner-only",
|
|
6629
6788
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6630
6789
|
sleepFrom: Number(e.GIRL_AGENT_SLEEP_FROM ?? 23),
|
|
6631
6790
|
sleepTo: Number(e.GIRL_AGENT_SLEEP_TO ?? 8),
|
|
6632
6791
|
nightWakeChance: Number(e.GIRL_AGENT_NIGHT_WAKE ?? 0.05),
|
|
6792
|
+
ignoreTendency: Number(e.GIRL_AGENT_IGNORE_TENDENCY ?? 35),
|
|
6633
6793
|
communication: commPreset.profile,
|
|
6634
6794
|
vibe: commPreset.profile.messageStyle === "one-liners" ? "short" : "warm",
|
|
6635
6795
|
busySchedule: []
|
|
@@ -6691,11 +6851,13 @@ function validateConfig(raw) {
|
|
|
6691
6851
|
},
|
|
6692
6852
|
telegram: c.telegram ?? {},
|
|
6693
6853
|
mcp: c.mcp ?? [],
|
|
6854
|
+
ownerId: normalizeOwnerId(c.ownerId ?? process.env.GIRL_AGENT_OWNER_ID),
|
|
6694
6855
|
privacy: c.privacy ?? "owner-only",
|
|
6695
6856
|
createdAt: c.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6696
6857
|
sleepFrom: c.sleepFrom ?? 23,
|
|
6697
6858
|
sleepTo: c.sleepTo ?? 8,
|
|
6698
6859
|
nightWakeChance: c.nightWakeChance ?? 0.05,
|
|
6860
|
+
ignoreTendency: c.ignoreTendency ?? 35,
|
|
6699
6861
|
communication: c.communication ?? COMMUNICATION_PRESETS[0].profile,
|
|
6700
6862
|
vibe: c.vibe,
|
|
6701
6863
|
personaNotes: c.personaNotes,
|
|
@@ -6703,6 +6865,27 @@ function validateConfig(raw) {
|
|
|
6703
6865
|
};
|
|
6704
6866
|
return filled;
|
|
6705
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
|
+
}
|
|
6706
6889
|
function buildConfigTemplate() {
|
|
6707
6890
|
const sample = {
|
|
6708
6891
|
slug: "anya",
|
|
@@ -6721,11 +6904,13 @@ function buildConfigTemplate() {
|
|
|
6721
6904
|
},
|
|
6722
6905
|
telegram: { botToken: "REPLACE_ME" },
|
|
6723
6906
|
mcp: [],
|
|
6907
|
+
ownerId: void 0,
|
|
6724
6908
|
privacy: "owner-only",
|
|
6725
6909
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6726
6910
|
sleepFrom: 23,
|
|
6727
6911
|
sleepTo: 8,
|
|
6728
6912
|
nightWakeChance: 0.05,
|
|
6913
|
+
ignoreTendency: 35,
|
|
6729
6914
|
communication: COMMUNICATION_PRESETS[0].profile,
|
|
6730
6915
|
vibe: "warm",
|
|
6731
6916
|
busySchedule: []
|
|
@@ -6733,7 +6918,7 @@ function buildConfigTemplate() {
|
|
|
6733
6918
|
return JSON.stringify(sample, null, 2) + "\n";
|
|
6734
6919
|
}
|
|
6735
6920
|
function buildSystemdUnit() {
|
|
6736
|
-
const home =
|
|
6921
|
+
const home = os2.homedir();
|
|
6737
6922
|
return `# /etc/systemd/system/girl-agent.service
|
|
6738
6923
|
# install: sudo cp this.service /etc/systemd/system/girl-agent.service
|
|
6739
6924
|
# sudo systemctl daemon-reload
|
|
@@ -6846,6 +7031,8 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
|
|
|
6846
7031
|
--message-style=<style> one-liners|balanced|bursty|longform
|
|
6847
7032
|
--initiative=<level> low|medium|high
|
|
6848
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)
|
|
6849
7036
|
--privacy=<mode> owner-only|allow-strangers (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E owner-only)
|
|
6850
7037
|
--nationality=RU|UA (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E RU)
|
|
6851
7038
|
--tz=<value> IANA "Europe/Moscow" / "GMT+3" / "+3" / "\u041A\u0438\u0435\u0432" \u2014 \u043F\u043E\u0438\u0441\u043A
|
|
@@ -6859,7 +7046,7 @@ update:
|
|
|
6859
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
|
|
6860
7047
|
npx girl-agent update --verbose # \u0441 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u044B\u043C \u0432\u044B\u0432\u043E\u0434\u043E\u043C
|
|
6861
7048
|
|
|
6862
|
-
\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
|
|
6863
7050
|
`;
|
|
6864
7051
|
async function main() {
|
|
6865
7052
|
const argv = mri(process.argv.slice(2), {
|
|
@@ -6887,6 +7074,8 @@ async function main() {
|
|
|
6887
7074
|
"message-style",
|
|
6888
7075
|
"initiative",
|
|
6889
7076
|
"life-sharing",
|
|
7077
|
+
"ignore-tendency",
|
|
7078
|
+
"owner-id",
|
|
6890
7079
|
"privacy",
|
|
6891
7080
|
"config"
|
|
6892
7081
|
],
|
|
@@ -7071,10 +7260,12 @@ async function buildConfigFromFlags(argv) {
|
|
|
7071
7260
|
},
|
|
7072
7261
|
mcp: mcps,
|
|
7073
7262
|
privacy,
|
|
7263
|
+
ownerId: normalizeOwnerId(argv["owner-id"] ?? process.env.GIRL_AGENT_OWNER_ID),
|
|
7074
7264
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7075
7265
|
sleepFrom: 23,
|
|
7076
7266
|
sleepTo: 8,
|
|
7077
7267
|
nightWakeChance: 0.05,
|
|
7268
|
+
ignoreTendency: Number(argv["ignore-tendency"] ?? 35),
|
|
7078
7269
|
vibe: deriveLegacyVibe(communication),
|
|
7079
7270
|
communication,
|
|
7080
7271
|
personaNotes: argv["persona-notes"] ? String(argv["persona-notes"]) : void 0,
|