luckytri 1.0.6 → 1.0.8
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/LICENSE +21 -21
- package/README.en.md +7 -3
- package/README.md +7 -3
- package/docs/en/CHANGELOG.md +35 -16
- package/docs/en/chat-style.md +28 -9
- package/docs/en/guide.md +5 -5
- package/docs/en/her-life.md +2 -2
- package/docs/en/webui.md +1 -1
- package/docs/zh/CHANGELOG.md +35 -16
- package/docs/zh/chat-style.md +30 -9
- package/docs/zh/guide.md +5 -5
- package/docs/zh/her-life.md +2 -2
- package/docs/zh/webui.md +1 -1
- package/package.json +1 -1
- package/public/app/assets/{ChatsPage-DCYMkJ91.js → ChatsPage-CshdtTzV.js} +2 -2
- package/public/app/assets/ChatsPage-DfyPCS9N.css +1 -0
- package/public/app/assets/{Empty-CLyZkxR5.js → Empty-C7yzOBzy.js} +1 -1
- package/public/app/assets/{Empty-DIobYG3x.css → Empty-DmUB2-Bk.css} +1 -1
- package/public/app/assets/{Field-BpaRwLI_.js → Field-BjFv4Rfn.js} +1 -1
- package/public/app/assets/HeartPage-Bft9wZp5.css +1 -0
- package/public/app/assets/{HeartPage-BIOMTiwr.js → HeartPage-eqo6ru16.js} +1 -1
- package/public/app/assets/LifePage-BUi5euBF.css +1 -0
- package/public/app/assets/{LifePage-BCgM4JkR.js → LifePage-CN8GE5NK.js} +1 -1
- package/public/app/assets/{MemoryPage-vShvRqMt.js → MemoryPage-CCpMN4Wg.js} +1 -1
- package/public/app/assets/MemoryPage-cZvXfkXb.css +1 -0
- package/public/app/assets/{NaturePage-Cy9ptr4G.js → NaturePage-C0JqdBTR.js} +2 -2
- package/public/app/assets/NaturePage-CHkpb5NK.css +1 -0
- package/public/app/assets/PeoplePage-BTU0H1pM.css +1 -0
- package/public/app/assets/{PeoplePage-CUZekB2E.js → PeoplePage-BXGBbO4u.js} +1 -1
- package/public/app/assets/{PluginsPage-1RM4rmJP.js → PluginsPage-Dksp5AV5.js} +1 -1
- package/public/app/assets/{SystemPage-BI2CB5Ig.css → SystemPage-BenIyTvk.css} +1 -1
- package/public/app/assets/{SystemPage-BeT5TmIV.js → SystemPage-Cd7aa8eE.js} +1 -1
- package/public/app/assets/TimePage-BOms_n4I.css +1 -0
- package/public/app/assets/{TimePage-taTmPMM8.js → TimePage-Bs_zImb4.js} +2 -2
- package/public/app/assets/{index-DY5AM0Hz.css → index-BHZcHUMh.css} +1 -1
- package/public/app/assets/{index-DdYwC5ZU.js → index-CyRtYxP3.js} +3 -3
- package/public/app/assets/{knowledge-DJmfu2KN.js → knowledge-BJOIyuJo.js} +1 -1
- package/public/app/assets/{media-BEtnE0Gv.js → media-Bwtx8blz.js} +1 -1
- package/public/app/assets/{mind-Dtiw0-QK.js → mind-DtTVrW6Z.js} +1 -1
- package/public/app/index.html +5 -6
- package/public/guide.en.html +3 -3
- package/public/guide.html +3 -3
- package/scripts/import-data.js +128 -128
- package/scripts/proxy-config.js +167 -167
- package/scripts/update-instance.js +201 -201
- package/server/channels/adapters.js +27 -27
- package/server/channels/onebot/adapter.js +288 -288
- package/server/channels/qqbot/adapter.js +253 -253
- package/server/channels/qqbot/api.js +199 -199
- package/server/channels/qqbot/credentials.js +11 -11
- package/server/channels/qqbot/gateway.js +274 -274
- package/server/channels/qqbot/index.js +389 -389
- package/server/channels/segments.js +40 -40
- package/server/channels/session-key.js +153 -153
- package/server/channels/state.js +42 -42
- package/server/core/api.js +564 -564
- package/server/core/context-builder.js +288 -288
- package/server/core/context-compactor.js +454 -454
- package/server/core/conversation-cues.js +385 -325
- package/server/core/conversation-grounding.js +304 -0
- package/server/core/conversation-manager.js +87 -87
- package/server/core/dialogue-context.js +244 -0
- package/server/core/dialogue-contract.js +35 -0
- package/server/core/feedback.js +8 -8
- package/server/core/group-style.js +72 -72
- package/server/core/initiative-context.js +68 -66
- package/server/core/input-budget.js +32 -32
- package/server/core/local-demo.js +98 -98
- package/server/core/message-manager.js +118 -118
- package/server/core/message-scheduler.js +78 -78
- package/server/core/model-json.js +190 -190
- package/server/core/model-manager.js +5 -8
- package/server/core/model-presets.js +900 -900
- package/server/core/name-call.js +29 -29
- package/server/core/network.js +157 -157
- package/server/core/person-name.js +22 -22
- package/server/core/persona-manager.js +359 -269
- package/server/core/reader-check.js +62 -0
- package/server/core/reply-delivery.js +537 -404
- package/server/core/reply-target-resolver.js +50 -50
- package/server/core/response-generator.js +92 -50
- package/server/core/response-validator.js +53 -32
- package/server/core/retired-prompts.js +51 -51
- package/server/core/sessions.js +109 -109
- package/server/core/speaker-names.js +92 -92
- package/server/core/topic-tracker.js +65 -65
- package/server/core/turn.js +82 -15
- package/server/http.js +18 -18
- package/server/i18n/decisions.js +209 -209
- package/server/i18n/errors.js +2 -0
- package/server/i18n/index.js +144 -144
- package/server/i18n/labels.js +6 -10
- package/server/i18n/models.js +111 -111
- package/server/knowledge/api.js +342 -342
- package/server/knowledge/embedding-profile.js +114 -114
- package/server/knowledge/index.js +13 -13
- package/server/knowledge/retrieval.js +93 -93
- package/server/knowledge/schema.js +158 -158
- package/server/knowledge/vector-index.js +90 -90
- package/server/mind/affect.js +265 -265
- package/server/mind/attention.js +200 -200
- package/server/mind/bonds.js +565 -565
- package/server/mind/clock.js +83 -83
- package/server/mind/context-selection.js +16 -4
- package/server/mind/continuity.js +562 -562
- package/server/mind/conversation-origin.js +175 -175
- package/server/mind/days.js +210 -210
- package/server/mind/evidence-read.js +45 -45
- package/server/mind/expression-grounding.js +21 -0
- package/server/mind/faces.js +373 -373
- package/server/mind/guard.js +68 -60
- package/server/mind/initiative.js +338 -338
- package/server/mind/life/activities.js +21 -21
- package/server/mind/life/context.js +330 -330
- package/server/mind/life/growth.js +261 -261
- package/server/mind/life/presence.js +305 -305
- package/server/mind/meetings.js +1069 -1069
- package/server/mind/memory-policy.js +48 -48
- package/server/mind/memory.js +888 -888
- package/server/mind/nature.js +358 -358
- package/server/mind/novelty.js +19 -19
- package/server/mind/own-voice.js +35 -0
- package/server/mind/periods.js +118 -118
- package/server/mind/reading.js +132 -120
- package/server/mind/relationship-context.js +71 -71
- package/server/mind/relationships.js +390 -390
- package/server/mind/salience.js +201 -201
- package/server/mind/schema.js +567 -567
- package/server/mind/self.js +496 -484
- package/server/mind/thoughts.js +274 -274
- package/server/mind/time/agenda.js +242 -242
- package/server/mind/time/api.js +200 -200
- package/server/mind/time/attention.js +34 -34
- package/server/mind/time/availability.js +23 -1
- package/server/mind/time/executor.js +4 -2
- package/server/mind/time/games.js +20 -11
- package/server/mind/time/index.js +9 -2
- package/server/mind/time/kinds.js +29 -2
- package/server/mind/time/material-relevance.js +19 -0
- package/server/mind/time/model-material.js +77 -77
- package/server/mind/time/presentation.js +35 -51
- package/server/mind/time/reading-input.js +31 -0
- package/server/mind/time/schedule.js +83 -83
- package/server/mind/time/schema.js +84 -84
- package/server/mind/time/search.js +383 -383
- package/server/mind/time/sharing.js +482 -472
- package/server/mind/time/task-links.js +311 -311
- package/server/mind/time/tasks.js +41 -3
- package/server/mind/time/works.js +527 -432
- package/server/mind/traits.js +191 -191
- package/server/mind/view.js +62 -12
- package/server/runtime/installation.js +116 -116
- package/server/runtime/update-manager.js +140 -140
- package/server/storage/data-package.js +298 -298
- package/server/storage/data-transfer-api.js +50 -50
- package/server/storage/data-transfer-worker.js +18 -18
- package/server/storage/data-transfer.js +416 -416
- package/server/studio/display-names.js +139 -139
- package/server/studio/management.js +102 -102
- package/server/studio/updates.js +144 -144
- package/server/version.js +10 -10
- package/public/app/assets/ChatsPage-CFMVozoG.css +0 -1
- package/public/app/assets/HeartPage-C-2jvgzF.css +0 -1
- package/public/app/assets/LifePage-CG1OHbRK.css +0 -1
- package/public/app/assets/MemoryPage-C_Bu4rfb.css +0 -1
- package/public/app/assets/NaturePage-CPZlImT0.css +0 -1
- package/public/app/assets/PeoplePage-3RRUITDz.css +0 -1
- package/public/app/assets/TimePage-C-guwPk2.css +0 -1
package/server/mind/traits.js
CHANGED
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { evidenceRoots, externalEvidence } from "./evidence.js";
|
|
3
|
-
import { lifeDayStart } from "./nature.js";
|
|
4
|
-
import { evidence, hasCredential, similar, text } from "./util.js";
|
|
5
|
-
|
|
6
|
-
export const LIVED_TRAITS = [
|
|
7
|
-
"warmth",
|
|
8
|
-
"sarcasm",
|
|
9
|
-
"humor",
|
|
10
|
-
"activity",
|
|
11
|
-
"initiative",
|
|
12
|
-
];
|
|
13
|
-
const TRAIT_HALF_LIFE = 180;
|
|
14
|
-
const DAILY_SHIFT_CAP = 4;
|
|
15
|
-
export function personaNeedsRephrase(content, mind) {
|
|
16
|
-
const value = String(content || "");
|
|
17
|
-
return (
|
|
18
|
-
value.length > 100 ||
|
|
19
|
-
/今天|昨天|明天|今晚|这几天|刚才|比如|下次|打完/.test(value) ||
|
|
20
|
-
!mind.meetings.sayable(value, "")
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Nature is the starting point. These small, cited changes are what she has
|
|
25
|
-
// actually learned about how she wants to show up; they belong to one self.
|
|
26
|
-
export class Traits {
|
|
27
|
-
constructor(mind) {
|
|
28
|
-
this.mind = mind;
|
|
29
|
-
this.db = mind.db;
|
|
30
|
-
}
|
|
31
|
-
current(nature = this.mind.nature.current(), before = Date.now()) {
|
|
32
|
-
const values = Object.fromEntries(
|
|
33
|
-
LIVED_TRAITS.map((key) => [key, Number(nature[key])]),
|
|
34
|
-
);
|
|
35
|
-
const rows = this.db
|
|
36
|
-
.prepare(
|
|
37
|
-
"SELECT trait,delta,created FROM mind_trait_changes WHERE nature_version=? AND created<=? ORDER BY created,rowid",
|
|
38
|
-
)
|
|
39
|
-
.all(nature.version, before);
|
|
40
|
-
const lived = this.mind.days.lived(before);
|
|
41
|
-
for (const row of rows) {
|
|
42
|
-
if (!LIVED_TRAITS.includes(row.trait)) continue;
|
|
43
|
-
values[row.trait] +=
|
|
44
|
-
row.delta * 0.5 ** (lived.since(row.created) / TRAIT_HALF_LIFE);
|
|
45
|
-
}
|
|
46
|
-
for (const trait of LIVED_TRAITS)
|
|
47
|
-
values[trait] = Math.max(0, Math.min(100, Math.round(values[trait])));
|
|
48
|
-
return values;
|
|
49
|
-
}
|
|
50
|
-
effective(nature = this.mind.nature.current(), before = Date.now()) {
|
|
51
|
-
const persona = this.persona(nature, before);
|
|
52
|
-
return {
|
|
53
|
-
...nature,
|
|
54
|
-
...this.current(nature, before),
|
|
55
|
-
...(persona ? { livedPersona: persona.content } : {}),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
persona(nature = this.mind.nature.current(), before = Date.now()) {
|
|
59
|
-
return (
|
|
60
|
-
this.db
|
|
61
|
-
.prepare(
|
|
62
|
-
"SELECT * FROM mind_persona_growth WHERE nature_version=? AND created<=? ORDER BY created DESC,rowid DESC LIMIT 1",
|
|
63
|
-
)
|
|
64
|
-
.get(nature.version, before) || null
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
personaHistory(limit = 15) {
|
|
68
|
-
return this.db
|
|
69
|
-
.prepare(
|
|
70
|
-
"SELECT * FROM mind_persona_growth ORDER BY created DESC,rowid DESC LIMIT ?",
|
|
71
|
-
)
|
|
72
|
-
.all(limit)
|
|
73
|
-
.map((row) => ({ ...row, sources: JSON.parse(row.sources) }));
|
|
74
|
-
}
|
|
75
|
-
proposePersona(
|
|
76
|
-
input,
|
|
77
|
-
{ valid, origin = "solitude", time = Date.now() } = {},
|
|
78
|
-
) {
|
|
79
|
-
const content = text(input?.content, 240);
|
|
80
|
-
if (
|
|
81
|
-
content.length < 12 ||
|
|
82
|
-
hasCredential(content) ||
|
|
83
|
-
personaNeedsRephrase(content, this.mind)
|
|
84
|
-
)
|
|
85
|
-
return { rejected: "不适合长期自述或含私下内容" };
|
|
86
|
-
const sources = evidence(input?.sources).filter(
|
|
87
|
-
(s) => !valid || valid.has(s),
|
|
88
|
-
);
|
|
89
|
-
if (!sources.length) return { rejected: "缺少亲历来源" };
|
|
90
|
-
const nature = this.mind.nature.current(time);
|
|
91
|
-
const previous = this.persona(nature, time);
|
|
92
|
-
const correcting =
|
|
93
|
-
previous && personaNeedsRephrase(previous.content, this.mind);
|
|
94
|
-
const grounded = externalEvidence(this.db, sources, time);
|
|
95
|
-
if (!correcting && !grounded.length)
|
|
96
|
-
return { rejected: "自述需要模型之外的新经历" };
|
|
97
|
-
if (previous && similar(content, previous.content, 0.85))
|
|
98
|
-
return { rejected: "没有实质变化" };
|
|
99
|
-
const spent = new Set(
|
|
100
|
-
this.db
|
|
101
|
-
.prepare(
|
|
102
|
-
"SELECT sources FROM mind_persona_growth WHERE nature_version=? AND created<=?",
|
|
103
|
-
)
|
|
104
|
-
.all(nature.version, time)
|
|
105
|
-
.flatMap((row) =>
|
|
106
|
-
evidenceRoots(this.db, JSON.parse(row.sources), time),
|
|
107
|
-
),
|
|
108
|
-
);
|
|
109
|
-
if (
|
|
110
|
-
previous &&
|
|
111
|
-
!personaNeedsRephrase(previous.content, this.mind) &&
|
|
112
|
-
!grounded.some((root) => !spent.has(root))
|
|
113
|
-
)
|
|
114
|
-
return { rejected: "没有新的亲历来源" };
|
|
115
|
-
const id = randomUUID();
|
|
116
|
-
this.db
|
|
117
|
-
.prepare(
|
|
118
|
-
"INSERT INTO mind_persona_growth(id,created,content,sources,origin,nature_version) VALUES (?,?,?,?,?,?)",
|
|
119
|
-
)
|
|
120
|
-
.run(id, time, content, JSON.stringify(sources), origin, nature.version);
|
|
121
|
-
return { id };
|
|
122
|
-
}
|
|
123
|
-
history(limit = 30) {
|
|
124
|
-
return this.db
|
|
125
|
-
.prepare(
|
|
126
|
-
"SELECT id,created,trait,value,delta,reason,sources,origin,nature_version FROM mind_trait_changes ORDER BY created DESC,rowid DESC LIMIT ?",
|
|
127
|
-
)
|
|
128
|
-
.all(limit)
|
|
129
|
-
.map((row) => ({ ...row, sources: JSON.parse(row.sources) }));
|
|
130
|
-
}
|
|
131
|
-
propose(input, { valid, origin = "solitude", time = Date.now() } = {}) {
|
|
132
|
-
const trait = String(input?.trait || "");
|
|
133
|
-
if (!LIVED_TRAITS.includes(trait)) return { rejected: "未知性格维度" };
|
|
134
|
-
const direction = Number(input?.direction);
|
|
135
|
-
if (!Number.isFinite(direction) || direction === 0)
|
|
136
|
-
return { rejected: "没有方向" };
|
|
137
|
-
const reason = text(input?.why, 180);
|
|
138
|
-
if (!reason || hasCredential(reason)) return { rejected: "缺少可用的理由" };
|
|
139
|
-
const sources = evidence(input?.sources).filter(
|
|
140
|
-
(s) => !valid || valid.has(s),
|
|
141
|
-
);
|
|
142
|
-
if (!sources.length) return { rejected: "缺少亲历来源" };
|
|
143
|
-
const nature = this.mind.nature.current(time);
|
|
144
|
-
const grounded = externalEvidence(this.db, sources, time);
|
|
145
|
-
if (!grounded.length) return { rejected: "性格变化需要模型之外的经历" };
|
|
146
|
-
const spent = new Set(
|
|
147
|
-
this.db
|
|
148
|
-
.prepare(
|
|
149
|
-
"SELECT sources FROM mind_trait_changes WHERE trait=? AND nature_version=?",
|
|
150
|
-
)
|
|
151
|
-
.all(trait, nature.version)
|
|
152
|
-
.flatMap((row) =>
|
|
153
|
-
evidenceRoots(this.db, JSON.parse(row.sources), time),
|
|
154
|
-
),
|
|
155
|
-
);
|
|
156
|
-
if (!grounded.some((source) => !spent.has(source)))
|
|
157
|
-
return { rejected: "这段经历已经改变过它" };
|
|
158
|
-
const previous = this.current(nature, time)[trait];
|
|
159
|
-
const start = lifeDayStart(nature, time, this.mind.timeZone());
|
|
160
|
-
const used = this.db
|
|
161
|
-
.prepare(
|
|
162
|
-
"SELECT COALESCE(SUM(ABS(delta)),0) n FROM mind_trait_changes WHERE trait=? AND nature_version=? AND created>=? AND created<=?",
|
|
163
|
-
)
|
|
164
|
-
.get(trait, nature.version, start, time).n;
|
|
165
|
-
const allowance = Math.max(0, DAILY_SHIFT_CAP - used);
|
|
166
|
-
if (!allowance) return { rejected: "今天这项性格已经改变过,留些时间检验" };
|
|
167
|
-
// A single reflection may nudge a tendency, never replace her overnight.
|
|
168
|
-
const delta =
|
|
169
|
-
Math.sign(direction) *
|
|
170
|
-
Math.min(allowance, Math.max(1, Math.round(Math.abs(direction))));
|
|
171
|
-
const value = Math.max(0, Math.min(100, previous + delta));
|
|
172
|
-
if (value === previous) return { rejected: "已到边界" };
|
|
173
|
-
const id = randomUUID();
|
|
174
|
-
this.db
|
|
175
|
-
.prepare(
|
|
176
|
-
"INSERT INTO mind_trait_changes(id,created,trait,value,delta,reason,sources,origin,nature_version) VALUES (?,?,?,?,?,?,?,?,?)",
|
|
177
|
-
)
|
|
178
|
-
.run(
|
|
179
|
-
id,
|
|
180
|
-
time,
|
|
181
|
-
trait,
|
|
182
|
-
value,
|
|
183
|
-
value - previous,
|
|
184
|
-
reason,
|
|
185
|
-
JSON.stringify(sources),
|
|
186
|
-
origin,
|
|
187
|
-
nature.version,
|
|
188
|
-
);
|
|
189
|
-
return { id, value };
|
|
190
|
-
}
|
|
191
|
-
}
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { evidenceRoots, externalEvidence } from "./evidence.js";
|
|
3
|
+
import { lifeDayStart } from "./nature.js";
|
|
4
|
+
import { evidence, hasCredential, similar, text } from "./util.js";
|
|
5
|
+
|
|
6
|
+
export const LIVED_TRAITS = [
|
|
7
|
+
"warmth",
|
|
8
|
+
"sarcasm",
|
|
9
|
+
"humor",
|
|
10
|
+
"activity",
|
|
11
|
+
"initiative",
|
|
12
|
+
];
|
|
13
|
+
const TRAIT_HALF_LIFE = 180;
|
|
14
|
+
const DAILY_SHIFT_CAP = 4;
|
|
15
|
+
export function personaNeedsRephrase(content, mind) {
|
|
16
|
+
const value = String(content || "");
|
|
17
|
+
return (
|
|
18
|
+
value.length > 100 ||
|
|
19
|
+
/今天|昨天|明天|今晚|这几天|刚才|比如|下次|打完/.test(value) ||
|
|
20
|
+
!mind.meetings.sayable(value, "")
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Nature is the starting point. These small, cited changes are what she has
|
|
25
|
+
// actually learned about how she wants to show up; they belong to one self.
|
|
26
|
+
export class Traits {
|
|
27
|
+
constructor(mind) {
|
|
28
|
+
this.mind = mind;
|
|
29
|
+
this.db = mind.db;
|
|
30
|
+
}
|
|
31
|
+
current(nature = this.mind.nature.current(), before = Date.now()) {
|
|
32
|
+
const values = Object.fromEntries(
|
|
33
|
+
LIVED_TRAITS.map((key) => [key, Number(nature[key])]),
|
|
34
|
+
);
|
|
35
|
+
const rows = this.db
|
|
36
|
+
.prepare(
|
|
37
|
+
"SELECT trait,delta,created FROM mind_trait_changes WHERE nature_version=? AND created<=? ORDER BY created,rowid",
|
|
38
|
+
)
|
|
39
|
+
.all(nature.version, before);
|
|
40
|
+
const lived = this.mind.days.lived(before);
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
if (!LIVED_TRAITS.includes(row.trait)) continue;
|
|
43
|
+
values[row.trait] +=
|
|
44
|
+
row.delta * 0.5 ** (lived.since(row.created) / TRAIT_HALF_LIFE);
|
|
45
|
+
}
|
|
46
|
+
for (const trait of LIVED_TRAITS)
|
|
47
|
+
values[trait] = Math.max(0, Math.min(100, Math.round(values[trait])));
|
|
48
|
+
return values;
|
|
49
|
+
}
|
|
50
|
+
effective(nature = this.mind.nature.current(), before = Date.now()) {
|
|
51
|
+
const persona = this.persona(nature, before);
|
|
52
|
+
return {
|
|
53
|
+
...nature,
|
|
54
|
+
...this.current(nature, before),
|
|
55
|
+
...(persona ? { livedPersona: persona.content } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
persona(nature = this.mind.nature.current(), before = Date.now()) {
|
|
59
|
+
return (
|
|
60
|
+
this.db
|
|
61
|
+
.prepare(
|
|
62
|
+
"SELECT * FROM mind_persona_growth WHERE nature_version=? AND created<=? ORDER BY created DESC,rowid DESC LIMIT 1",
|
|
63
|
+
)
|
|
64
|
+
.get(nature.version, before) || null
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
personaHistory(limit = 15) {
|
|
68
|
+
return this.db
|
|
69
|
+
.prepare(
|
|
70
|
+
"SELECT * FROM mind_persona_growth ORDER BY created DESC,rowid DESC LIMIT ?",
|
|
71
|
+
)
|
|
72
|
+
.all(limit)
|
|
73
|
+
.map((row) => ({ ...row, sources: JSON.parse(row.sources) }));
|
|
74
|
+
}
|
|
75
|
+
proposePersona(
|
|
76
|
+
input,
|
|
77
|
+
{ valid, origin = "solitude", time = Date.now() } = {},
|
|
78
|
+
) {
|
|
79
|
+
const content = text(input?.content, 240);
|
|
80
|
+
if (
|
|
81
|
+
content.length < 12 ||
|
|
82
|
+
hasCredential(content) ||
|
|
83
|
+
personaNeedsRephrase(content, this.mind)
|
|
84
|
+
)
|
|
85
|
+
return { rejected: "不适合长期自述或含私下内容" };
|
|
86
|
+
const sources = evidence(input?.sources).filter(
|
|
87
|
+
(s) => !valid || valid.has(s),
|
|
88
|
+
);
|
|
89
|
+
if (!sources.length) return { rejected: "缺少亲历来源" };
|
|
90
|
+
const nature = this.mind.nature.current(time);
|
|
91
|
+
const previous = this.persona(nature, time);
|
|
92
|
+
const correcting =
|
|
93
|
+
previous && personaNeedsRephrase(previous.content, this.mind);
|
|
94
|
+
const grounded = externalEvidence(this.db, sources, time);
|
|
95
|
+
if (!correcting && !grounded.length)
|
|
96
|
+
return { rejected: "自述需要模型之外的新经历" };
|
|
97
|
+
if (previous && similar(content, previous.content, 0.85))
|
|
98
|
+
return { rejected: "没有实质变化" };
|
|
99
|
+
const spent = new Set(
|
|
100
|
+
this.db
|
|
101
|
+
.prepare(
|
|
102
|
+
"SELECT sources FROM mind_persona_growth WHERE nature_version=? AND created<=?",
|
|
103
|
+
)
|
|
104
|
+
.all(nature.version, time)
|
|
105
|
+
.flatMap((row) =>
|
|
106
|
+
evidenceRoots(this.db, JSON.parse(row.sources), time),
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
if (
|
|
110
|
+
previous &&
|
|
111
|
+
!personaNeedsRephrase(previous.content, this.mind) &&
|
|
112
|
+
!grounded.some((root) => !spent.has(root))
|
|
113
|
+
)
|
|
114
|
+
return { rejected: "没有新的亲历来源" };
|
|
115
|
+
const id = randomUUID();
|
|
116
|
+
this.db
|
|
117
|
+
.prepare(
|
|
118
|
+
"INSERT INTO mind_persona_growth(id,created,content,sources,origin,nature_version) VALUES (?,?,?,?,?,?)",
|
|
119
|
+
)
|
|
120
|
+
.run(id, time, content, JSON.stringify(sources), origin, nature.version);
|
|
121
|
+
return { id };
|
|
122
|
+
}
|
|
123
|
+
history(limit = 30) {
|
|
124
|
+
return this.db
|
|
125
|
+
.prepare(
|
|
126
|
+
"SELECT id,created,trait,value,delta,reason,sources,origin,nature_version FROM mind_trait_changes ORDER BY created DESC,rowid DESC LIMIT ?",
|
|
127
|
+
)
|
|
128
|
+
.all(limit)
|
|
129
|
+
.map((row) => ({ ...row, sources: JSON.parse(row.sources) }));
|
|
130
|
+
}
|
|
131
|
+
propose(input, { valid, origin = "solitude", time = Date.now() } = {}) {
|
|
132
|
+
const trait = String(input?.trait || "");
|
|
133
|
+
if (!LIVED_TRAITS.includes(trait)) return { rejected: "未知性格维度" };
|
|
134
|
+
const direction = Number(input?.direction);
|
|
135
|
+
if (!Number.isFinite(direction) || direction === 0)
|
|
136
|
+
return { rejected: "没有方向" };
|
|
137
|
+
const reason = text(input?.why, 180);
|
|
138
|
+
if (!reason || hasCredential(reason)) return { rejected: "缺少可用的理由" };
|
|
139
|
+
const sources = evidence(input?.sources).filter(
|
|
140
|
+
(s) => !valid || valid.has(s),
|
|
141
|
+
);
|
|
142
|
+
if (!sources.length) return { rejected: "缺少亲历来源" };
|
|
143
|
+
const nature = this.mind.nature.current(time);
|
|
144
|
+
const grounded = externalEvidence(this.db, sources, time);
|
|
145
|
+
if (!grounded.length) return { rejected: "性格变化需要模型之外的经历" };
|
|
146
|
+
const spent = new Set(
|
|
147
|
+
this.db
|
|
148
|
+
.prepare(
|
|
149
|
+
"SELECT sources FROM mind_trait_changes WHERE trait=? AND nature_version=?",
|
|
150
|
+
)
|
|
151
|
+
.all(trait, nature.version)
|
|
152
|
+
.flatMap((row) =>
|
|
153
|
+
evidenceRoots(this.db, JSON.parse(row.sources), time),
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
if (!grounded.some((source) => !spent.has(source)))
|
|
157
|
+
return { rejected: "这段经历已经改变过它" };
|
|
158
|
+
const previous = this.current(nature, time)[trait];
|
|
159
|
+
const start = lifeDayStart(nature, time, this.mind.timeZone());
|
|
160
|
+
const used = this.db
|
|
161
|
+
.prepare(
|
|
162
|
+
"SELECT COALESCE(SUM(ABS(delta)),0) n FROM mind_trait_changes WHERE trait=? AND nature_version=? AND created>=? AND created<=?",
|
|
163
|
+
)
|
|
164
|
+
.get(trait, nature.version, start, time).n;
|
|
165
|
+
const allowance = Math.max(0, DAILY_SHIFT_CAP - used);
|
|
166
|
+
if (!allowance) return { rejected: "今天这项性格已经改变过,留些时间检验" };
|
|
167
|
+
// A single reflection may nudge a tendency, never replace her overnight.
|
|
168
|
+
const delta =
|
|
169
|
+
Math.sign(direction) *
|
|
170
|
+
Math.min(allowance, Math.max(1, Math.round(Math.abs(direction))));
|
|
171
|
+
const value = Math.max(0, Math.min(100, previous + delta));
|
|
172
|
+
if (value === previous) return { rejected: "已到边界" };
|
|
173
|
+
const id = randomUUID();
|
|
174
|
+
this.db
|
|
175
|
+
.prepare(
|
|
176
|
+
"INSERT INTO mind_trait_changes(id,created,trait,value,delta,reason,sources,origin,nature_version) VALUES (?,?,?,?,?,?,?,?,?)",
|
|
177
|
+
)
|
|
178
|
+
.run(
|
|
179
|
+
id,
|
|
180
|
+
time,
|
|
181
|
+
trait,
|
|
182
|
+
value,
|
|
183
|
+
value - previous,
|
|
184
|
+
reason,
|
|
185
|
+
JSON.stringify(sources),
|
|
186
|
+
origin,
|
|
187
|
+
nature.version,
|
|
188
|
+
);
|
|
189
|
+
return { id, value };
|
|
190
|
+
}
|
|
191
|
+
}
|
package/server/mind/view.js
CHANGED
|
@@ -6,6 +6,10 @@ import { describeFace } from "./faces.js";
|
|
|
6
6
|
import { SELF_KINDS } from "./self.js";
|
|
7
7
|
import { DAY, text } from "./util.js";
|
|
8
8
|
import { relationshipHistory } from "./relationship-context.js";
|
|
9
|
+
import {
|
|
10
|
+
fixedReplyHabit,
|
|
11
|
+
conversationalMemory,
|
|
12
|
+
} from "../core/conversation-grounding.js";
|
|
9
13
|
|
|
10
14
|
const FORGOTTEN = "(很久没想起了)";
|
|
11
15
|
|
|
@@ -46,6 +50,12 @@ function groupStyle(mind, session, now) {
|
|
|
46
50
|
)
|
|
47
51
|
.all(session, now + 60000);
|
|
48
52
|
const ids = rows.map((row) => row.event_id).filter(Boolean);
|
|
53
|
+
const bots = new Set(
|
|
54
|
+
mind.relationships
|
|
55
|
+
.context({ room: session, now, limit: 100 })
|
|
56
|
+
.known.filter((p) => p.kind === "bot")
|
|
57
|
+
.map((p) => String(p.subjectId)),
|
|
58
|
+
);
|
|
49
59
|
const away = ids.length
|
|
50
60
|
? new Set(
|
|
51
61
|
mind.db
|
|
@@ -57,7 +67,9 @@ function groupStyle(mind, session, now) {
|
|
|
57
67
|
)
|
|
58
68
|
: new Set();
|
|
59
69
|
const profile = summarizeGroupStyle(
|
|
60
|
-
|
|
70
|
+
rows.filter(
|
|
71
|
+
(row) => !away.has(row.event_id) && !bots.has(String(row.user_id)),
|
|
72
|
+
),
|
|
61
73
|
now,
|
|
62
74
|
);
|
|
63
75
|
return profile.ready ? profile.summary : "";
|
|
@@ -97,11 +109,31 @@ export function innerView(
|
|
|
97
109
|
now,
|
|
98
110
|
});
|
|
99
111
|
const carries = (thread) => mind.meetings.stays(thread, session, now);
|
|
112
|
+
const availableThreads = mind.self
|
|
113
|
+
.annotated({ before: now, now })
|
|
114
|
+
.filter(
|
|
115
|
+
(t) =>
|
|
116
|
+
!t.faded &&
|
|
117
|
+
carries(t) &&
|
|
118
|
+
!fixedReplyHabit(t.content) &&
|
|
119
|
+
!(
|
|
120
|
+
t.kind === "habit" &&
|
|
121
|
+
t.origin === "memory" &&
|
|
122
|
+
new Set(t.days).size < 3
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
// Her cached self stays the same across cues. Topic-specific recollections
|
|
126
|
+
// belong in inner, so a new question does not swap her personality.
|
|
100
127
|
const visibleThreads = relevantContext(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
availableThreads.filter((t) => t.core || t.thread === living?.thread),
|
|
129
|
+
{
|
|
130
|
+
limit: 6,
|
|
131
|
+
pinned: (t) => t.core || t.thread === living?.thread,
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
const relatedSelf = relevantContext(
|
|
135
|
+
availableThreads.filter((t) => !visibleThreads.includes(t)),
|
|
136
|
+
{ cues, limit: 2, requireOverlap: true },
|
|
105
137
|
);
|
|
106
138
|
const shownLiving = living && carries(living) ? living : null;
|
|
107
139
|
const reminded = [
|
|
@@ -143,9 +175,14 @@ export function innerView(
|
|
|
143
175
|
? {
|
|
144
176
|
here: describeFace({
|
|
145
177
|
...face,
|
|
146
|
-
notes: face.notes
|
|
147
|
-
|
|
148
|
-
|
|
178
|
+
notes: face.notes
|
|
179
|
+
.map((note) => ({
|
|
180
|
+
...note,
|
|
181
|
+
content: conversationalMemory(note.content),
|
|
182
|
+
}))
|
|
183
|
+
.filter(
|
|
184
|
+
(note) => note.content && spoken(`${note.content} ${note.why}`),
|
|
185
|
+
),
|
|
149
186
|
role: spoken(face.role),
|
|
150
187
|
tone: spoken(face.tone),
|
|
151
188
|
aspiration: spoken(face.aspiration),
|
|
@@ -211,8 +248,12 @@ export function innerView(
|
|
|
211
248
|
const thoughts = relevantContext(
|
|
212
249
|
mind.thoughts
|
|
213
250
|
.open({ now, limit: 400, session })
|
|
214
|
-
.filter(
|
|
215
|
-
|
|
251
|
+
.filter(
|
|
252
|
+
(t) =>
|
|
253
|
+
!fixedReplyHabit(t.content) &&
|
|
254
|
+
mind.meetings.sayable(t.content, session),
|
|
255
|
+
),
|
|
256
|
+
{ cues, limit: 3, requireOverlap: cue.length > 0 },
|
|
216
257
|
);
|
|
217
258
|
const heard = feedback(mind, session, now);
|
|
218
259
|
const continuity = mind.continuity.recall({ session, people, cue, now });
|
|
@@ -228,6 +269,7 @@ export function innerView(
|
|
|
228
269
|
});
|
|
229
270
|
const wills = mind.meetings.traces({ session, before: now, limit: 2 });
|
|
230
271
|
const will = wills
|
|
272
|
+
.filter((row) => !fixedReplyHabit(row.text))
|
|
231
273
|
.map((row) =>
|
|
232
274
|
row.living ? row.text : `${text(row.content, 28)}:${row.text}`,
|
|
233
275
|
)
|
|
@@ -243,8 +285,8 @@ export function innerView(
|
|
|
243
285
|
affect.cause && mind.meetings.sayable(affect.cause, session)
|
|
244
286
|
? text(affect.cause, 40)
|
|
245
287
|
: "";
|
|
246
|
-
const withLines = withWhom.filter(
|
|
247
|
-
mind.meetings.sayable(line, session),
|
|
288
|
+
const withLines = withWhom.filter(
|
|
289
|
+
(line) => !fixedReplyHabit(line) && mind.meetings.sayable(line, session),
|
|
248
290
|
);
|
|
249
291
|
const expectingLines = expecting.filter((line) =>
|
|
250
292
|
mind.meetings.sayable(line, session),
|
|
@@ -272,6 +314,14 @@ export function innerView(
|
|
|
272
314
|
),
|
|
273
315
|
}
|
|
274
316
|
: {}),
|
|
317
|
+
...(relatedSelf.length
|
|
318
|
+
? {
|
|
319
|
+
relatedSelf: relatedSelf.map(
|
|
320
|
+
(t) =>
|
|
321
|
+
`${SELF_KINDS[t.kind]}:${relationshipHistory(text(t.content, 80), t.created, relationships)}`,
|
|
322
|
+
),
|
|
323
|
+
}
|
|
324
|
+
: {}),
|
|
275
325
|
...(expectingLines.length ? { expecting: expectingLines } : {}),
|
|
276
326
|
...(withLines.length ? { with: withLines } : {}),
|
|
277
327
|
...(will ? { will } : {}),
|