luckytri 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/SECURITY.md +4 -4
- package/README.en.md +252 -250
- package/README.md +252 -250
- package/bin/luckytri.js +5 -1
- package/docs/en/CHANGELOG.md +8 -0
- package/docs/en/connect.md +18 -18
- package/docs/en/guide.md +202 -216
- package/docs/en/npm-release.md +25 -25
- package/docs/zh/CHANGELOG.md +8 -0
- package/docs/zh/connect.md +17 -17
- package/docs/zh/guide.md +202 -216
- package/docs/zh/npm-release.md +33 -33
- package/package.json +110 -108
- package/plugins/sdk/index.d.ts +155 -155
- package/plugins/sdk/testing.js +58 -58
- package/plugins/weather/index.js +82 -82
- package/plugins/weather/luckytri-plugin.json +29 -29
- package/public/app/assets/ChatsPage-BhlJHseG.js +3 -0
- package/public/app/assets/Empty-DP6WMGHi.js +2 -0
- package/public/app/assets/Field-CtbdqaiB.js +1 -0
- package/public/app/assets/HeartPage-32BeTPn2.js +1 -0
- package/public/app/assets/LifePage-CxSm0XKI.js +1 -0
- package/public/app/assets/MemoryPage-BeKBcPCY.js +1 -0
- package/public/app/assets/NaturePage-NEPYl2HS.js +3 -0
- package/public/app/assets/PeoplePage-DxtpekwM.js +1 -0
- package/public/app/assets/PluginsPage-CVVQwEBI.css +1 -0
- package/public/app/assets/PluginsPage-DksEZbUU.js +2 -0
- package/public/app/assets/{SystemPage-CAMPoYqM.css → SystemPage-BkRu0Rvx.css} +1 -1
- package/public/app/assets/SystemPage-BvzJlHuY.js +1 -0
- package/public/app/assets/TimePage-7-awH5XL.js +3 -0
- package/public/app/assets/index-4rItIkD-.js +4 -0
- package/public/app/assets/{index-YDz1ree1.css → index-DY4e15Kh.css} +1 -1
- package/public/app/assets/{knowledge-DG0e0uKw.js → knowledge-C6_HvoyB.js} +1 -1
- package/public/app/assets/media-SMfTT-yc.js +1 -0
- package/public/app/index.html +3 -3
- package/public/guide.en.html +72 -99
- package/public/guide.html +44 -86
- package/public/tutorial.en.md +25 -39
- package/public/tutorial.md +25 -39
- package/scripts/backup.js +311 -311
- package/scripts/browser.js +41 -41
- package/scripts/import-data.js +128 -0
- package/scripts/launch.js +101 -94
- package/scripts/plugin.js +150 -150
- package/scripts/reset-password.js +22 -0
- package/scripts/runtime.js +25 -25
- package/scripts/service.js +56 -47
- package/scripts/setup.js +50 -50
- package/scripts/start-server.js +41 -41
- package/scripts/stop.js +36 -36
- package/server/app.js +112 -96
- package/server/auth.js +217 -0
- package/server/channels/contract.js +57 -57
- package/server/channels/hub.js +165 -165
- package/server/channels/index.js +28 -25
- package/server/channels/onebot/index.js +335 -336
- package/server/channels/onebot/token.js +22 -3
- package/server/core/capabilities.js +208 -208
- package/server/core/events.js +64 -60
- package/server/core/image-preprocessor.js +208 -208
- package/server/core/model-manager.js +1213 -1213
- package/server/core/net-guard.js +49 -49
- package/server/core/orchestrator.js +569 -569
- package/server/core/perceivers.js +91 -91
- package/server/core/persona-manager.js +243 -243
- package/server/core/repository.js +293 -293
- package/server/core/response-validator.js +508 -508
- package/server/core/turn-processor.js +630 -630
- package/server/core/turn.js +236 -236
- package/server/core/vision-manager.js +633 -633
- package/server/i18n/errors.js +548 -497
- package/server/i18n/labels.js +649 -653
- package/server/index.js +151 -102
- package/server/knowledge/manager.js +386 -386
- package/server/knowledge/schema.js +12 -8
- package/server/local-access.js +32 -0
- package/server/mind/anticipations.js +507 -507
- package/server/mind/api.js +703 -703
- package/server/mind/budget.js +152 -152
- package/server/mind/context-selection.js +61 -61
- package/server/mind/day-planner.js +324 -324
- package/server/mind/evidence.js +148 -148
- package/server/mind/index.js +445 -445
- package/server/mind/life/diary.js +786 -786
- package/server/mind/life/index.js +1049 -1049
- package/server/mind/observations.js +84 -84
- package/server/mind/own-day.js +324 -324
- package/server/mind/own-voice.js +318 -318
- package/server/mind/senses.js +53 -53
- package/server/mind/time/activity-clock.js +196 -196
- package/server/mind/time/availability.js +69 -69
- package/server/mind/time/commitment-review.js +198 -198
- package/server/mind/time/executor.js +512 -512
- package/server/mind/time/experiences.js +128 -128
- package/server/mind/time/games.js +562 -562
- package/server/mind/time/index.js +739 -739
- package/server/mind/time/intent.js +166 -166
- package/server/mind/time/kinds.js +171 -171
- package/server/mind/time/plugin-activity.js +207 -207
- package/server/mind/time/tasks.js +587 -587
- package/server/mind/util.js +110 -110
- package/server/mind/view.js +273 -273
- package/server/paths.js +102 -102
- package/server/plugins/api.js +204 -204
- package/server/plugins/host.js +967 -967
- package/server/plugins/install.js +295 -295
- package/server/plugins/manifest.js +201 -201
- package/server/plugins/permissions.js +42 -42
- package/server/plugins/runner/guard.mjs +36 -36
- package/server/plugins/runner/worker.mjs +250 -250
- package/server/plugins/schema.js +91 -91
- package/server/plugins/zip.js +112 -112
- package/server/startup-lock.js +118 -118
- package/server/storage/archive.js +242 -241
- package/server/storage/backup-cleanup-worker.js +8 -8
- package/server/storage/backup-cleanup.js +415 -414
- package/server/storage/backup-lock.js +76 -76
- package/server/storage/backup-scheduler.js +131 -131
- package/server/storage/data-package.js +298 -0
- package/server/storage/data-transfer-api.js +50 -0
- package/server/storage/data-transfer-worker.js +18 -0
- package/server/storage/data-transfer.js +415 -0
- package/server/storage/store.js +202 -199
- package/server/studio/readiness.js +181 -190
- package/server/studio/settings.js +236 -236
- package/public/app/assets/ChatsPage-C-FcRapR.js +0 -3
- package/public/app/assets/Empty-CpP5kQHH.js +0 -2
- package/public/app/assets/Field-D4gY-INU.js +0 -1
- package/public/app/assets/HeartPage-CPy_thQd.js +0 -1
- package/public/app/assets/LifePage-CxWvwmSf.js +0 -1
- package/public/app/assets/MemoryPage-DC4rhQ68.js +0 -1
- package/public/app/assets/NaturePage-IvYw04Zq.js +0 -3
- package/public/app/assets/PeoplePage-Cqefm9-q.js +0 -1
- package/public/app/assets/PluginsPage-DKSaKvXb.js +0 -2
- package/public/app/assets/PluginsPage-HyBe5D8w.css +0 -1
- package/public/app/assets/SystemPage-CJmtJkrK.js +0 -1
- package/public/app/assets/TimePage-Cw44inM4.js +0 -3
- package/public/app/assets/index-BheRW-1t.js +0 -4
- package/public/app/assets/media-BzPDBm7e.js +0 -1
package/server/mind/view.js
CHANGED
|
@@ -1,273 +1,273 @@
|
|
|
1
|
-
import { summarizeGroupStyle } from "../core/group-style.js";
|
|
2
|
-
import { relevantContext, boundMindContext } from "./context-selection.js";
|
|
3
|
-
import { interestTerms } from "./attention.js";
|
|
4
|
-
import { agoLabel, elapsedLabel } from "./clock.js";
|
|
5
|
-
import { describeFace } from "./faces.js";
|
|
6
|
-
import { SELF_KINDS } from "./self.js";
|
|
7
|
-
import { DAY, text } from "./util.js";
|
|
8
|
-
|
|
9
|
-
const FORGOTTEN = "(很久没想起了)";
|
|
10
|
-
|
|
11
|
-
// One person's own words. Two people cannot be added together, and a line
|
|
12
|
-
// she already said does not count. A bare string is one utterance.
|
|
13
|
-
function speakerCues(cue) {
|
|
14
|
-
const groups = new Map();
|
|
15
|
-
let anon = 0;
|
|
16
|
-
for (const item of cue || []) {
|
|
17
|
-
if (item && typeof item === "object") {
|
|
18
|
-
if (item.role === "assistant") continue;
|
|
19
|
-
const id = String(item.userId || item.speaker || "");
|
|
20
|
-
if (!id) continue;
|
|
21
|
-
const texts = groups.get(id) || [];
|
|
22
|
-
texts.push(String(item.text || ""));
|
|
23
|
-
groups.set(id, texts);
|
|
24
|
-
} else groups.set(`\0${anon++}`, [String(item ?? "")]);
|
|
25
|
-
}
|
|
26
|
-
return [...groups.values()]
|
|
27
|
-
.map((texts) => interestTerms(texts))
|
|
28
|
-
.filter((terms) => terms.size);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const FEEDBACK = {
|
|
32
|
-
too_long: "太长了",
|
|
33
|
-
too_formal: "太端着",
|
|
34
|
-
too_meme: "梗太多",
|
|
35
|
-
natural: "挺自然",
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// The way a room talks is read as of `now`, not as of the wall clock: a replay
|
|
39
|
-
// or a test that moves the clock must see the room as it was at that moment.
|
|
40
|
-
function groupStyle(mind, session, now) {
|
|
41
|
-
try {
|
|
42
|
-
const rows = mind.db
|
|
43
|
-
.prepare(
|
|
44
|
-
"SELECT * FROM (SELECT * FROM messages WHERE session_id=? AND is_demo=0 AND time<=? ORDER BY id DESC LIMIT 200) ORDER BY id",
|
|
45
|
-
)
|
|
46
|
-
.all(session, now + 60000);
|
|
47
|
-
const ids = rows.map((row) => row.event_id).filter(Boolean);
|
|
48
|
-
const away = ids.length
|
|
49
|
-
? new Set(
|
|
50
|
-
mind.db
|
|
51
|
-
.prepare(
|
|
52
|
-
`SELECT e.event_id FROM core_events e JOIN mind_unlived u ON u.seq=e.seq WHERE e.event_id IN (${ids.map(() => "?").join(",")})`,
|
|
53
|
-
)
|
|
54
|
-
.all(...ids)
|
|
55
|
-
.map((row) => row.event_id),
|
|
56
|
-
)
|
|
57
|
-
: new Set();
|
|
58
|
-
const profile = summarizeGroupStyle(
|
|
59
|
-
away.size ? rows.filter((row) => !away.has(row.event_id)) : rows,
|
|
60
|
-
now,
|
|
61
|
-
);
|
|
62
|
-
return profile.ready ? profile.summary : "";
|
|
63
|
-
} catch {
|
|
64
|
-
return "";
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function feedback(mind, session, now) {
|
|
69
|
-
return mind.db
|
|
70
|
-
.prepare(
|
|
71
|
-
"SELECT f.tag,d.reply FROM reply_feedback f JOIN decisions d ON d.id=f.decision_id WHERE d.session_id=? AND d.is_demo=0 AND f.time>? AND f.time<=? ORDER BY f.time DESC LIMIT 2",
|
|
72
|
-
)
|
|
73
|
-
.all(session, now - 3 * DAY, now)
|
|
74
|
-
.filter((row) => FEEDBACK[row.tag] && row.tag !== "natural")
|
|
75
|
-
.map(
|
|
76
|
-
(row) => `有人觉得我说的「${text(row.reply, 30)}」${FEEDBACK[row.tag]}`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Who she is right now, in a few hundred tokens. `self` changes a few times a
|
|
81
|
-
// day and sits in the cached head of the prompt; `inner` changes every turn,
|
|
82
|
-
// so anything this batch of messages brings back to mind belongs there.
|
|
83
|
-
export function innerView(
|
|
84
|
-
mind,
|
|
85
|
-
{ session, kind = "group", people = [], cue = [], now = Date.now() },
|
|
86
|
-
) {
|
|
87
|
-
const nature = mind.nature.current(now);
|
|
88
|
-
const affect = mind.affect.state(now, { nature, room: session });
|
|
89
|
-
const face = mind.faces.current(session, now);
|
|
90
|
-
const living = mind.self.living({ before: now, now, room: session });
|
|
91
|
-
const cues = speakerCues(cue);
|
|
92
|
-
const carries = (thread) => mind.meetings.stays(thread, session, now);
|
|
93
|
-
const visibleThreads = relevantContext(
|
|
94
|
-
mind.self
|
|
95
|
-
.annotated({ before: now, now })
|
|
96
|
-
.filter((t) => !t.faded && carries(t)),
|
|
97
|
-
{ cues, limit: 6, pinned: (t) => t.core || t.thread === living?.thread },
|
|
98
|
-
);
|
|
99
|
-
const shownLiving = living && carries(living) ? living : null;
|
|
100
|
-
const reminded = [
|
|
101
|
-
...mind.self
|
|
102
|
-
.reminded({ before: now, now, cues, limit: 2 })
|
|
103
|
-
.filter(carries)
|
|
104
|
-
.map((t) => `${SELF_KINDS[t.kind]}:${text(t.content, 80)}${FORGOTTEN}`),
|
|
105
|
-
...mind.thoughts
|
|
106
|
-
.reminded({ now, cues, session, limit: 1 })
|
|
107
|
-
.filter((t) => mind.meetings.sayable(t.content, session))
|
|
108
|
-
.map(
|
|
109
|
-
(t) =>
|
|
110
|
-
`${elapsedLabel(t.created, now, mind.timeZone())}想到:${text(t.content, 110)}${FORGOTTEN}`,
|
|
111
|
-
),
|
|
112
|
-
...mind.meetings
|
|
113
|
-
.reminded({ session, now, cues, limit: 1 })
|
|
114
|
-
.filter((line) => mind.meetings.sayable(line, session))
|
|
115
|
-
.map((line) => `${line}${FORGOTTEN}`),
|
|
116
|
-
];
|
|
117
|
-
const diary = mind.db
|
|
118
|
-
.prepare(
|
|
119
|
-
"SELECT day,content FROM mind_diary WHERE created<=? ORDER BY created DESC LIMIT 1",
|
|
120
|
-
)
|
|
121
|
-
.get(now);
|
|
122
|
-
const diaryHere =
|
|
123
|
-
diary &&
|
|
124
|
-
!mind.meetings.privateBeyond(diary.day, session, now) &&
|
|
125
|
-
mind.meetings.sayable(diary.content, session)
|
|
126
|
-
? `${diary.day}:${text(diary.content, 110)}`
|
|
127
|
-
: "";
|
|
128
|
-
const read = mind.reading
|
|
129
|
-
.recent({ now, limit: 2 })
|
|
130
|
-
.filter((r) => r.created > now - 7 * DAY);
|
|
131
|
-
const room = kind === "group" ? groupStyle(mind, session, now) : "";
|
|
132
|
-
const spoken = (value) =>
|
|
133
|
-
value && mind.meetings.sayable(value, session) ? value : "";
|
|
134
|
-
const self = {
|
|
135
|
-
...(face
|
|
136
|
-
? {
|
|
137
|
-
here: describeFace({
|
|
138
|
-
...face,
|
|
139
|
-
role: spoken(face.role),
|
|
140
|
-
tone: spoken(face.tone),
|
|
141
|
-
aspiration: spoken(face.aspiration),
|
|
142
|
-
content: spoken(face.content),
|
|
143
|
-
}),
|
|
144
|
-
}
|
|
145
|
-
: {}),
|
|
146
|
-
...(visibleThreads.length
|
|
147
|
-
? {
|
|
148
|
-
threads: visibleThreads.map(
|
|
149
|
-
(t) =>
|
|
150
|
-
`${SELF_KINDS[t.kind]}:${text(t.content, 80)}${t.status === "emerging" ? "(刚开始有这种感觉)" : ""}`,
|
|
151
|
-
),
|
|
152
|
-
}
|
|
153
|
-
: {}),
|
|
154
|
-
...(shownLiving ? { livingFor: text(shownLiving.content, 80) } : {}),
|
|
155
|
-
...(diaryHere ? { lastDiary: diaryHere } : {}),
|
|
156
|
-
...(read.length
|
|
157
|
-
? {
|
|
158
|
-
readLately: read.map((r) => {
|
|
159
|
-
const note =
|
|
160
|
-
r.note && mind.meetings.sayable(r.note, session)
|
|
161
|
-
? text(r.note, 60)
|
|
162
|
-
: "";
|
|
163
|
-
return `读过《${r.title}》第 ${r.ordinal + 1} 段${note ? `:${note}` : ""}`;
|
|
164
|
-
}),
|
|
165
|
-
}
|
|
166
|
-
: {}),
|
|
167
|
-
};
|
|
168
|
-
if (!self.here) delete self.here;
|
|
169
|
-
const persons = [];
|
|
170
|
-
for (const id of [...new Set(people.map(String))].slice(0, 6)) {
|
|
171
|
-
const bond = mind.bonds.person(id, now, { room: session });
|
|
172
|
-
// A recorded feeling counts even before anyone has spoken. Merely being
|
|
173
|
-
// seen does not.
|
|
174
|
-
if (
|
|
175
|
-
!bond ||
|
|
176
|
-
(!bond.interactions &&
|
|
177
|
-
!bond.impression &&
|
|
178
|
-
!bond.lastChange &&
|
|
179
|
-
bond.tension < 0.15)
|
|
180
|
-
)
|
|
181
|
-
continue;
|
|
182
|
-
persons.push({
|
|
183
|
-
id,
|
|
184
|
-
name: bond.name,
|
|
185
|
-
feel: bond.feel,
|
|
186
|
-
...(bond.impression && mind.meetings.sayable(bond.impression, session)
|
|
187
|
-
? { impression: bond.impression }
|
|
188
|
-
: {}),
|
|
189
|
-
// Someone coming back after days away, or someone who has been around
|
|
190
|
-
// without talking with her for a while.
|
|
191
|
-
...(bond.awayDays >= 3
|
|
192
|
-
? { away: `上次见到是 ${agoLabel(now - bond.seenAt)}` }
|
|
193
|
-
: bond.absentDays >= 7
|
|
194
|
-
? { lastTalked: `上次说上话是 ${agoLabel(now - bond.lastTalkedAt)}` }
|
|
195
|
-
: {}),
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
const group = kind === "group" ? mind.bonds.group(session, now) : null;
|
|
199
|
-
const thoughts = relevantContext(
|
|
200
|
-
mind.thoughts
|
|
201
|
-
.open({ now, limit: 400, session })
|
|
202
|
-
.filter((t) => mind.meetings.sayable(t.content, session)),
|
|
203
|
-
{ cues, limit: 3 },
|
|
204
|
-
);
|
|
205
|
-
const heard = feedback(mind, session, now);
|
|
206
|
-
const continuity = mind.continuity.recall({ session, people, cue, now });
|
|
207
|
-
const expecting = mind.anticipations
|
|
208
|
-
.upcoming({ now, people, session, limit: 3 })
|
|
209
|
-
.map((a) => a.text);
|
|
210
|
-
const withWhom = mind.meetings.recall({
|
|
211
|
-
session,
|
|
212
|
-
people,
|
|
213
|
-
cue,
|
|
214
|
-
now,
|
|
215
|
-
limit: 2,
|
|
216
|
-
});
|
|
217
|
-
const wills = mind.meetings.traces({ session, before: now, limit: 2 });
|
|
218
|
-
const will = wills
|
|
219
|
-
.map((row) =>
|
|
220
|
-
row.living ? row.text : `${text(row.content, 28)}:${row.text}`,
|
|
221
|
-
)
|
|
222
|
-
.join(";");
|
|
223
|
-
const stood = mind.meetings.stood({
|
|
224
|
-
session,
|
|
225
|
-
people,
|
|
226
|
-
cue,
|
|
227
|
-
now,
|
|
228
|
-
limit: 2,
|
|
229
|
-
});
|
|
230
|
-
const cause =
|
|
231
|
-
affect.cause && mind.meetings.sayable(affect.cause, session)
|
|
232
|
-
? text(affect.cause, 40)
|
|
233
|
-
: "";
|
|
234
|
-
const withLines = withWhom.filter((line) =>
|
|
235
|
-
mind.meetings.sayable(line, session),
|
|
236
|
-
);
|
|
237
|
-
const expectingLines = expecting.filter((line) =>
|
|
238
|
-
mind.meetings.sayable(line, session),
|
|
239
|
-
);
|
|
240
|
-
const senses = mind.senses?.lines({ kind, now }) || [];
|
|
241
|
-
const body = (mind.body?.({ kind, now }) || [])
|
|
242
|
-
.map((line) => text(line, 80))
|
|
243
|
-
.filter(Boolean)
|
|
244
|
-
.slice(0, 8);
|
|
245
|
-
return boundMindContext({
|
|
246
|
-
self,
|
|
247
|
-
affect: cause ? affect : { ...affect, cause: "" },
|
|
248
|
-
inner: {
|
|
249
|
-
currentLife: mind.time.view({ session, now, cue }),
|
|
250
|
-
state: `${affect.phaseLabel},精力${affect.energyLabel},心情${affect.mood}${cause ? `(${cause})` : ""}${affect.lately ? `,${affect.lately}` : ""}`,
|
|
251
|
-
...(persons.length ? { people: persons } : {}),
|
|
252
|
-
...(continuity ? { continuity } : {}),
|
|
253
|
-
...(group ? { thisGroup: group.feel } : {}),
|
|
254
|
-
...(thoughts.length
|
|
255
|
-
? {
|
|
256
|
-
onMind: thoughts.map(
|
|
257
|
-
(t) =>
|
|
258
|
-
`${elapsedLabel(t.created, now, mind.timeZone())}想到:${text(t.content, 110)}`,
|
|
259
|
-
),
|
|
260
|
-
}
|
|
261
|
-
: {}),
|
|
262
|
-
...(expectingLines.length ? { expecting: expectingLines } : {}),
|
|
263
|
-
...(withLines.length ? { with: withLines } : {}),
|
|
264
|
-
...(will ? { will } : {}),
|
|
265
|
-
...(stood.length ? { stood } : {}),
|
|
266
|
-
...(reminded.length ? { reminded } : {}),
|
|
267
|
-
...(room ? { room } : {}),
|
|
268
|
-
...(heard.length ? { heard } : {}),
|
|
269
|
-
...(senses.length ? { senses } : {}),
|
|
270
|
-
...(body.length ? { body } : {}),
|
|
271
|
-
},
|
|
272
|
-
});
|
|
273
|
-
}
|
|
1
|
+
import { summarizeGroupStyle } from "../core/group-style.js";
|
|
2
|
+
import { relevantContext, boundMindContext } from "./context-selection.js";
|
|
3
|
+
import { interestTerms } from "./attention.js";
|
|
4
|
+
import { agoLabel, elapsedLabel } from "./clock.js";
|
|
5
|
+
import { describeFace } from "./faces.js";
|
|
6
|
+
import { SELF_KINDS } from "./self.js";
|
|
7
|
+
import { DAY, text } from "./util.js";
|
|
8
|
+
|
|
9
|
+
const FORGOTTEN = "(很久没想起了)";
|
|
10
|
+
|
|
11
|
+
// One person's own words. Two people cannot be added together, and a line
|
|
12
|
+
// she already said does not count. A bare string is one utterance.
|
|
13
|
+
function speakerCues(cue) {
|
|
14
|
+
const groups = new Map();
|
|
15
|
+
let anon = 0;
|
|
16
|
+
for (const item of cue || []) {
|
|
17
|
+
if (item && typeof item === "object") {
|
|
18
|
+
if (item.role === "assistant") continue;
|
|
19
|
+
const id = String(item.userId || item.speaker || "");
|
|
20
|
+
if (!id) continue;
|
|
21
|
+
const texts = groups.get(id) || [];
|
|
22
|
+
texts.push(String(item.text || ""));
|
|
23
|
+
groups.set(id, texts);
|
|
24
|
+
} else groups.set(`\0${anon++}`, [String(item ?? "")]);
|
|
25
|
+
}
|
|
26
|
+
return [...groups.values()]
|
|
27
|
+
.map((texts) => interestTerms(texts))
|
|
28
|
+
.filter((terms) => terms.size);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const FEEDBACK = {
|
|
32
|
+
too_long: "太长了",
|
|
33
|
+
too_formal: "太端着",
|
|
34
|
+
too_meme: "梗太多",
|
|
35
|
+
natural: "挺自然",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// The way a room talks is read as of `now`, not as of the wall clock: a replay
|
|
39
|
+
// or a test that moves the clock must see the room as it was at that moment.
|
|
40
|
+
function groupStyle(mind, session, now) {
|
|
41
|
+
try {
|
|
42
|
+
const rows = mind.db
|
|
43
|
+
.prepare(
|
|
44
|
+
"SELECT * FROM (SELECT * FROM messages WHERE session_id=? AND is_demo=0 AND time<=? ORDER BY id DESC LIMIT 200) ORDER BY id",
|
|
45
|
+
)
|
|
46
|
+
.all(session, now + 60000);
|
|
47
|
+
const ids = rows.map((row) => row.event_id).filter(Boolean);
|
|
48
|
+
const away = ids.length
|
|
49
|
+
? new Set(
|
|
50
|
+
mind.db
|
|
51
|
+
.prepare(
|
|
52
|
+
`SELECT e.event_id FROM core_events e JOIN mind_unlived u ON u.seq=e.seq WHERE e.event_id IN (${ids.map(() => "?").join(",")})`,
|
|
53
|
+
)
|
|
54
|
+
.all(...ids)
|
|
55
|
+
.map((row) => row.event_id),
|
|
56
|
+
)
|
|
57
|
+
: new Set();
|
|
58
|
+
const profile = summarizeGroupStyle(
|
|
59
|
+
away.size ? rows.filter((row) => !away.has(row.event_id)) : rows,
|
|
60
|
+
now,
|
|
61
|
+
);
|
|
62
|
+
return profile.ready ? profile.summary : "";
|
|
63
|
+
} catch {
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function feedback(mind, session, now) {
|
|
69
|
+
return mind.db
|
|
70
|
+
.prepare(
|
|
71
|
+
"SELECT f.tag,d.reply FROM reply_feedback f JOIN decisions d ON d.id=f.decision_id WHERE d.session_id=? AND d.is_demo=0 AND f.time>? AND f.time<=? ORDER BY f.time DESC LIMIT 2",
|
|
72
|
+
)
|
|
73
|
+
.all(session, now - 3 * DAY, now)
|
|
74
|
+
.filter((row) => FEEDBACK[row.tag] && row.tag !== "natural")
|
|
75
|
+
.map(
|
|
76
|
+
(row) => `有人觉得我说的「${text(row.reply, 30)}」${FEEDBACK[row.tag]}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Who she is right now, in a few hundred tokens. `self` changes a few times a
|
|
81
|
+
// day and sits in the cached head of the prompt; `inner` changes every turn,
|
|
82
|
+
// so anything this batch of messages brings back to mind belongs there.
|
|
83
|
+
export function innerView(
|
|
84
|
+
mind,
|
|
85
|
+
{ session, kind = "group", people = [], cue = [], now = Date.now() },
|
|
86
|
+
) {
|
|
87
|
+
const nature = mind.nature.current(now);
|
|
88
|
+
const affect = mind.affect.state(now, { nature, room: session });
|
|
89
|
+
const face = mind.faces.current(session, now);
|
|
90
|
+
const living = mind.self.living({ before: now, now, room: session });
|
|
91
|
+
const cues = speakerCues(cue);
|
|
92
|
+
const carries = (thread) => mind.meetings.stays(thread, session, now);
|
|
93
|
+
const visibleThreads = relevantContext(
|
|
94
|
+
mind.self
|
|
95
|
+
.annotated({ before: now, now })
|
|
96
|
+
.filter((t) => !t.faded && carries(t)),
|
|
97
|
+
{ cues, limit: 6, pinned: (t) => t.core || t.thread === living?.thread },
|
|
98
|
+
);
|
|
99
|
+
const shownLiving = living && carries(living) ? living : null;
|
|
100
|
+
const reminded = [
|
|
101
|
+
...mind.self
|
|
102
|
+
.reminded({ before: now, now, cues, limit: 2 })
|
|
103
|
+
.filter(carries)
|
|
104
|
+
.map((t) => `${SELF_KINDS[t.kind]}:${text(t.content, 80)}${FORGOTTEN}`),
|
|
105
|
+
...mind.thoughts
|
|
106
|
+
.reminded({ now, cues, session, limit: 1 })
|
|
107
|
+
.filter((t) => mind.meetings.sayable(t.content, session))
|
|
108
|
+
.map(
|
|
109
|
+
(t) =>
|
|
110
|
+
`${elapsedLabel(t.created, now, mind.timeZone())}想到:${text(t.content, 110)}${FORGOTTEN}`,
|
|
111
|
+
),
|
|
112
|
+
...mind.meetings
|
|
113
|
+
.reminded({ session, now, cues, limit: 1 })
|
|
114
|
+
.filter((line) => mind.meetings.sayable(line, session))
|
|
115
|
+
.map((line) => `${line}${FORGOTTEN}`),
|
|
116
|
+
];
|
|
117
|
+
const diary = mind.db
|
|
118
|
+
.prepare(
|
|
119
|
+
"SELECT day,content FROM mind_diary WHERE created<=? ORDER BY created DESC LIMIT 1",
|
|
120
|
+
)
|
|
121
|
+
.get(now);
|
|
122
|
+
const diaryHere =
|
|
123
|
+
diary &&
|
|
124
|
+
!mind.meetings.privateBeyond(diary.day, session, now) &&
|
|
125
|
+
mind.meetings.sayable(diary.content, session)
|
|
126
|
+
? `${diary.day}:${text(diary.content, 110)}`
|
|
127
|
+
: "";
|
|
128
|
+
const read = mind.reading
|
|
129
|
+
.recent({ now, limit: 2 })
|
|
130
|
+
.filter((r) => r.created > now - 7 * DAY);
|
|
131
|
+
const room = kind === "group" ? groupStyle(mind, session, now) : "";
|
|
132
|
+
const spoken = (value) =>
|
|
133
|
+
value && mind.meetings.sayable(value, session) ? value : "";
|
|
134
|
+
const self = {
|
|
135
|
+
...(face
|
|
136
|
+
? {
|
|
137
|
+
here: describeFace({
|
|
138
|
+
...face,
|
|
139
|
+
role: spoken(face.role),
|
|
140
|
+
tone: spoken(face.tone),
|
|
141
|
+
aspiration: spoken(face.aspiration),
|
|
142
|
+
content: spoken(face.content),
|
|
143
|
+
}),
|
|
144
|
+
}
|
|
145
|
+
: {}),
|
|
146
|
+
...(visibleThreads.length
|
|
147
|
+
? {
|
|
148
|
+
threads: visibleThreads.map(
|
|
149
|
+
(t) =>
|
|
150
|
+
`${SELF_KINDS[t.kind]}:${text(t.content, 80)}${t.status === "emerging" ? "(刚开始有这种感觉)" : ""}`,
|
|
151
|
+
),
|
|
152
|
+
}
|
|
153
|
+
: {}),
|
|
154
|
+
...(shownLiving ? { livingFor: text(shownLiving.content, 80) } : {}),
|
|
155
|
+
...(diaryHere ? { lastDiary: diaryHere } : {}),
|
|
156
|
+
...(read.length
|
|
157
|
+
? {
|
|
158
|
+
readLately: read.map((r) => {
|
|
159
|
+
const note =
|
|
160
|
+
r.note && mind.meetings.sayable(r.note, session)
|
|
161
|
+
? text(r.note, 60)
|
|
162
|
+
: "";
|
|
163
|
+
return `读过《${r.title}》第 ${r.ordinal + 1} 段${note ? `:${note}` : ""}`;
|
|
164
|
+
}),
|
|
165
|
+
}
|
|
166
|
+
: {}),
|
|
167
|
+
};
|
|
168
|
+
if (!self.here) delete self.here;
|
|
169
|
+
const persons = [];
|
|
170
|
+
for (const id of [...new Set(people.map(String))].slice(0, 6)) {
|
|
171
|
+
const bond = mind.bonds.person(id, now, { room: session });
|
|
172
|
+
// A recorded feeling counts even before anyone has spoken. Merely being
|
|
173
|
+
// seen does not.
|
|
174
|
+
if (
|
|
175
|
+
!bond ||
|
|
176
|
+
(!bond.interactions &&
|
|
177
|
+
!bond.impression &&
|
|
178
|
+
!bond.lastChange &&
|
|
179
|
+
bond.tension < 0.15)
|
|
180
|
+
)
|
|
181
|
+
continue;
|
|
182
|
+
persons.push({
|
|
183
|
+
id,
|
|
184
|
+
name: bond.name,
|
|
185
|
+
feel: bond.feel,
|
|
186
|
+
...(bond.impression && mind.meetings.sayable(bond.impression, session)
|
|
187
|
+
? { impression: bond.impression }
|
|
188
|
+
: {}),
|
|
189
|
+
// Someone coming back after days away, or someone who has been around
|
|
190
|
+
// without talking with her for a while.
|
|
191
|
+
...(bond.awayDays >= 3
|
|
192
|
+
? { away: `上次见到是 ${agoLabel(now - bond.seenAt)}` }
|
|
193
|
+
: bond.absentDays >= 7
|
|
194
|
+
? { lastTalked: `上次说上话是 ${agoLabel(now - bond.lastTalkedAt)}` }
|
|
195
|
+
: {}),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const group = kind === "group" ? mind.bonds.group(session, now) : null;
|
|
199
|
+
const thoughts = relevantContext(
|
|
200
|
+
mind.thoughts
|
|
201
|
+
.open({ now, limit: 400, session })
|
|
202
|
+
.filter((t) => mind.meetings.sayable(t.content, session)),
|
|
203
|
+
{ cues, limit: 3 },
|
|
204
|
+
);
|
|
205
|
+
const heard = feedback(mind, session, now);
|
|
206
|
+
const continuity = mind.continuity.recall({ session, people, cue, now });
|
|
207
|
+
const expecting = mind.anticipations
|
|
208
|
+
.upcoming({ now, people, session, limit: 3 })
|
|
209
|
+
.map((a) => a.text);
|
|
210
|
+
const withWhom = mind.meetings.recall({
|
|
211
|
+
session,
|
|
212
|
+
people,
|
|
213
|
+
cue,
|
|
214
|
+
now,
|
|
215
|
+
limit: 2,
|
|
216
|
+
});
|
|
217
|
+
const wills = mind.meetings.traces({ session, before: now, limit: 2 });
|
|
218
|
+
const will = wills
|
|
219
|
+
.map((row) =>
|
|
220
|
+
row.living ? row.text : `${text(row.content, 28)}:${row.text}`,
|
|
221
|
+
)
|
|
222
|
+
.join(";");
|
|
223
|
+
const stood = mind.meetings.stood({
|
|
224
|
+
session,
|
|
225
|
+
people,
|
|
226
|
+
cue,
|
|
227
|
+
now,
|
|
228
|
+
limit: 2,
|
|
229
|
+
});
|
|
230
|
+
const cause =
|
|
231
|
+
affect.cause && mind.meetings.sayable(affect.cause, session)
|
|
232
|
+
? text(affect.cause, 40)
|
|
233
|
+
: "";
|
|
234
|
+
const withLines = withWhom.filter((line) =>
|
|
235
|
+
mind.meetings.sayable(line, session),
|
|
236
|
+
);
|
|
237
|
+
const expectingLines = expecting.filter((line) =>
|
|
238
|
+
mind.meetings.sayable(line, session),
|
|
239
|
+
);
|
|
240
|
+
const senses = mind.senses?.lines({ kind, now }) || [];
|
|
241
|
+
const body = (mind.body?.({ kind, now }) || [])
|
|
242
|
+
.map((line) => text(line, 80))
|
|
243
|
+
.filter(Boolean)
|
|
244
|
+
.slice(0, 8);
|
|
245
|
+
return boundMindContext({
|
|
246
|
+
self,
|
|
247
|
+
affect: cause ? affect : { ...affect, cause: "" },
|
|
248
|
+
inner: {
|
|
249
|
+
currentLife: mind.time.view({ session, now, cue }),
|
|
250
|
+
state: `${affect.phaseLabel},精力${affect.energyLabel},心情${affect.mood}${cause ? `(${cause})` : ""}${affect.lately ? `,${affect.lately}` : ""}`,
|
|
251
|
+
...(persons.length ? { people: persons } : {}),
|
|
252
|
+
...(continuity ? { continuity } : {}),
|
|
253
|
+
...(group ? { thisGroup: group.feel } : {}),
|
|
254
|
+
...(thoughts.length
|
|
255
|
+
? {
|
|
256
|
+
onMind: thoughts.map(
|
|
257
|
+
(t) =>
|
|
258
|
+
`${elapsedLabel(t.created, now, mind.timeZone())}想到:${text(t.content, 110)}`,
|
|
259
|
+
),
|
|
260
|
+
}
|
|
261
|
+
: {}),
|
|
262
|
+
...(expectingLines.length ? { expecting: expectingLines } : {}),
|
|
263
|
+
...(withLines.length ? { with: withLines } : {}),
|
|
264
|
+
...(will ? { will } : {}),
|
|
265
|
+
...(stood.length ? { stood } : {}),
|
|
266
|
+
...(reminded.length ? { reminded } : {}),
|
|
267
|
+
...(room ? { room } : {}),
|
|
268
|
+
...(heard.length ? { heard } : {}),
|
|
269
|
+
...(senses.length ? { senses } : {}),
|
|
270
|
+
...(body.length ? { body } : {}),
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
}
|