luckytri 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +21 -21
  2. package/README.en.md +2 -0
  3. package/README.md +2 -0
  4. package/docs/en/CHANGELOG.md +20 -13
  5. package/docs/en/guide.md +7 -5
  6. package/docs/en/her-life.md +2 -2
  7. package/docs/en/webui.md +1 -1
  8. package/docs/zh/CHANGELOG.md +20 -13
  9. package/docs/zh/guide.md +7 -5
  10. package/docs/zh/her-life.md +2 -2
  11. package/docs/zh/webui.md +1 -1
  12. package/package.json +2 -1
  13. package/public/app/assets/ChatsPage-CFMVozoG.css +1 -0
  14. package/public/app/assets/{ChatsPage-CshdtTzV.js → ChatsPage-DCYMkJ91.js} +2 -2
  15. package/public/app/assets/{Empty-C7yzOBzy.js → Empty-CLyZkxR5.js} +1 -1
  16. package/public/app/assets/{Empty-DmUB2-Bk.css → Empty-DIobYG3x.css} +1 -1
  17. package/public/app/assets/{Field-BjFv4Rfn.js → Field-BpaRwLI_.js} +1 -1
  18. package/public/app/assets/{HeartPage-eqo6ru16.js → HeartPage-BIOMTiwr.js} +1 -1
  19. package/public/app/assets/HeartPage-C-2jvgzF.css +1 -0
  20. package/public/app/assets/{LifePage-CN8GE5NK.js → LifePage-BCgM4JkR.js} +1 -1
  21. package/public/app/assets/LifePage-CG1OHbRK.css +1 -0
  22. package/public/app/assets/MemoryPage-C_Bu4rfb.css +1 -0
  23. package/public/app/assets/{MemoryPage-CCpMN4Wg.js → MemoryPage-vShvRqMt.js} +1 -1
  24. package/public/app/assets/NaturePage-CPZlImT0.css +1 -0
  25. package/public/app/assets/{NaturePage-C0JqdBTR.js → NaturePage-Cy9ptr4G.js} +2 -2
  26. package/public/app/assets/PeoplePage-3RRUITDz.css +1 -0
  27. package/public/app/assets/{PeoplePage-BXGBbO4u.js → PeoplePage-CUZekB2E.js} +1 -1
  28. package/public/app/assets/{PluginsPage-Dksp5AV5.js → PluginsPage-1RM4rmJP.js} +1 -1
  29. package/public/app/assets/{SystemPage-BenIyTvk.css → SystemPage-BI2CB5Ig.css} +1 -1
  30. package/public/app/assets/{SystemPage-Cd7aa8eE.js → SystemPage-BeT5TmIV.js} +1 -1
  31. package/public/app/assets/TimePage-C-guwPk2.css +1 -0
  32. package/public/app/assets/{TimePage-Bs_zImb4.js → TimePage-taTmPMM8.js} +2 -2
  33. package/public/app/assets/{index-BHZcHUMh.css → index-DY5AM0Hz.css} +1 -1
  34. package/public/app/assets/{index-CyRtYxP3.js → index-DdYwC5ZU.js} +3 -3
  35. package/public/app/assets/{knowledge-BJOIyuJo.js → knowledge-DJmfu2KN.js} +1 -1
  36. package/public/app/assets/{media-Bwtx8blz.js → media-BEtnE0Gv.js} +1 -1
  37. package/public/app/assets/{mind-DtTVrW6Z.js → mind-Dtiw0-QK.js} +1 -1
  38. package/public/app/index.html +6 -5
  39. package/public/guide.en.html +11 -3
  40. package/public/guide.html +6 -3
  41. package/public/tutorial.en.md +2 -0
  42. package/public/tutorial.md +2 -0
  43. package/scripts/import-data.js +128 -128
  44. package/scripts/proxy-config.js +167 -167
  45. package/scripts/service-supervisor.js +50 -0
  46. package/scripts/start-server.js +71 -16
  47. package/scripts/update-instance.js +201 -201
  48. package/server/channels/adapters.js +27 -27
  49. package/server/channels/onebot/adapter.js +288 -288
  50. package/server/channels/qqbot/adapter.js +253 -253
  51. package/server/channels/qqbot/api.js +199 -199
  52. package/server/channels/qqbot/credentials.js +11 -11
  53. package/server/channels/qqbot/gateway.js +274 -274
  54. package/server/channels/qqbot/index.js +389 -389
  55. package/server/channels/segments.js +40 -40
  56. package/server/channels/session-key.js +153 -153
  57. package/server/channels/state.js +42 -42
  58. package/server/core/api.js +564 -564
  59. package/server/core/context-builder.js +288 -288
  60. package/server/core/context-compactor.js +454 -454
  61. package/server/core/conversation-cues.js +325 -325
  62. package/server/core/conversation-manager.js +87 -87
  63. package/server/core/feedback.js +8 -8
  64. package/server/core/group-style.js +72 -72
  65. package/server/core/initiative-context.js +66 -66
  66. package/server/core/input-budget.js +32 -32
  67. package/server/core/local-demo.js +98 -98
  68. package/server/core/message-manager.js +118 -118
  69. package/server/core/message-scheduler.js +78 -78
  70. package/server/core/model-json.js +190 -190
  71. package/server/core/model-presets.js +900 -900
  72. package/server/core/name-call.js +29 -29
  73. package/server/core/network.js +157 -157
  74. package/server/core/person-name.js +22 -22
  75. package/server/core/persona-manager.js +2 -1
  76. package/server/core/reply-delivery.js +404 -403
  77. package/server/core/reply-target-resolver.js +50 -50
  78. package/server/core/response-generator.js +50 -50
  79. package/server/core/response-validator.js +14 -2
  80. package/server/core/retired-prompts.js +51 -51
  81. package/server/core/sessions.js +109 -109
  82. package/server/core/speaker-names.js +92 -92
  83. package/server/core/topic-tracker.js +65 -65
  84. package/server/http.js +18 -18
  85. package/server/i18n/decisions.js +209 -206
  86. package/server/i18n/errors.js +4 -0
  87. package/server/i18n/index.js +144 -144
  88. package/server/i18n/models.js +111 -111
  89. package/server/knowledge/api.js +342 -342
  90. package/server/knowledge/embedding-profile.js +114 -114
  91. package/server/knowledge/index.js +13 -13
  92. package/server/knowledge/retrieval.js +93 -93
  93. package/server/knowledge/schema.js +158 -158
  94. package/server/knowledge/vector-index.js +90 -90
  95. package/server/mind/affect.js +265 -265
  96. package/server/mind/attention.js +200 -200
  97. package/server/mind/bonds.js +565 -565
  98. package/server/mind/clock.js +83 -83
  99. package/server/mind/continuity.js +562 -562
  100. package/server/mind/conversation-origin.js +175 -175
  101. package/server/mind/days.js +210 -210
  102. package/server/mind/evidence-read.js +45 -45
  103. package/server/mind/faces.js +373 -373
  104. package/server/mind/guard.js +60 -60
  105. package/server/mind/initiative.js +338 -338
  106. package/server/mind/life/activities.js +21 -21
  107. package/server/mind/life/context.js +330 -323
  108. package/server/mind/life/diary.js +20 -0
  109. package/server/mind/life/growth.js +261 -254
  110. package/server/mind/life/index.js +10 -2
  111. package/server/mind/life/presence.js +305 -305
  112. package/server/mind/meetings.js +1069 -1069
  113. package/server/mind/memory-policy.js +48 -48
  114. package/server/mind/memory.js +888 -888
  115. package/server/mind/nature.js +358 -358
  116. package/server/mind/novelty.js +19 -19
  117. package/server/mind/periods.js +118 -118
  118. package/server/mind/reading.js +120 -120
  119. package/server/mind/relationship-context.js +71 -0
  120. package/server/mind/relationships.js +390 -279
  121. package/server/mind/salience.js +201 -201
  122. package/server/mind/schema.js +567 -567
  123. package/server/mind/self.js +484 -484
  124. package/server/mind/thoughts.js +274 -274
  125. package/server/mind/time/agenda.js +242 -242
  126. package/server/mind/time/api.js +200 -200
  127. package/server/mind/time/attention.js +34 -34
  128. package/server/mind/time/delivery-links.js +130 -130
  129. package/server/mind/time/model-material.js +77 -77
  130. package/server/mind/time/presentation.js +51 -51
  131. package/server/mind/time/schedule.js +83 -83
  132. package/server/mind/time/schema.js +84 -84
  133. package/server/mind/time/search.js +383 -383
  134. package/server/mind/time/sharing.js +472 -472
  135. package/server/mind/time/task-links.js +311 -311
  136. package/server/mind/time/works.js +432 -432
  137. package/server/mind/traits.js +191 -191
  138. package/server/mind/view.js +9 -1
  139. package/server/runtime/installation.js +116 -116
  140. package/server/runtime/update-execution.js +2 -1
  141. package/server/runtime/update-manager.js +140 -140
  142. package/server/storage/data-package.js +298 -298
  143. package/server/storage/data-transfer-api.js +50 -50
  144. package/server/storage/data-transfer-worker.js +18 -18
  145. package/server/storage/data-transfer.js +416 -416
  146. package/server/studio/display-names.js +139 -139
  147. package/server/studio/management.js +102 -102
  148. package/server/studio/updates.js +144 -144
  149. package/server/version.js +10 -10
  150. package/public/app/assets/ChatsPage-DfyPCS9N.css +0 -1
  151. package/public/app/assets/HeartPage-Bft9wZp5.css +0 -1
  152. package/public/app/assets/LifePage-BUi5euBF.css +0 -1
  153. package/public/app/assets/MemoryPage-cZvXfkXb.css +0 -1
  154. package/public/app/assets/NaturePage-CHkpb5NK.css +0 -1
  155. package/public/app/assets/PeoplePage-BTU0H1pM.css +0 -1
  156. package/public/app/assets/TimePage-BOms_n4I.css +0 -1
@@ -1,201 +1,201 @@
1
- import { interestTerms, namesProject } from "./attention.js";
2
- import { similar } from "./util.js";
3
-
4
- // How long each kind of thing stays with her without being lived again, as
5
- // a half-life in days she actually lived. Nothing here is deleted: a faded
6
- // thread is only out of view until something touches it again.
7
- export const THREAD_HALF_LIFE = {
8
- trait: 180,
9
- view: 90,
10
- habit: 90,
11
- interest: 60,
12
- care: 30,
13
- intention: 30,
14
- curiosity: 21,
15
- };
16
- export const THOUGHT_HALF_LIFE = {
17
- unfinished: 21,
18
- revision: 14,
19
- reflection: 10,
20
- reconnection: 10,
21
- };
22
- const THREAD_GRACE = 3;
23
- export const THREAD_FADED = 0.12;
24
- export const THREAD_FADING = 0.2;
25
- export const THOUGHT_FADED = 0.1;
26
- // A meeting's meaning stays about as long as an unfinished thought. It is
27
- // not deleted; it only leaves what she is looking at now.
28
- export const MEETING_HALF_LIFE = 21;
29
- export const MEETING_FADED = 0.12;
30
- // A memory nobody has touched in this many lived days needs a strong cue.
31
- export const MEMORY_IDLE_DAYS = 45;
32
- // The strongest threads stay with her however quiet it gets.
33
- export const CORE_THREADS = 2;
34
-
35
- const round = (value) => Math.round(value * 1000) / 1000;
36
-
37
- // An opinion the nightly memory step picked out of one chat is not yet who she
38
- // is. It stays only as long as it keeps coming back: each new piece of
39
- // evidence renews it, and one that never recurs fades like a passing thought.
40
- export const PASSING_VIEW_HALF_LIFE = 21;
41
-
42
- export function threadSalience(row, lived) {
43
- const idle = lived.since(row.created);
44
- const settled = row.kind === "trait" && (row.days?.length || 0) >= 5 ? 2 : 1;
45
- const passing = row.kind === "view" && row.origin === "memory";
46
- const half = passing
47
- ? PASSING_VIEW_HALF_LIFE
48
- : (THREAD_HALF_LIFE[row.kind] || 60) * settled;
49
- return round(
50
- Number(row.strength || 0) *
51
- 0.5 ** (Math.max(0, idle - THREAD_GRACE) / half),
52
- );
53
- }
54
-
55
- // A revisit that has come due brings a thought back for a while.
56
- export function thoughtSalience(thought, lived, now) {
57
- const due = !!(thought.revisit_at && thought.revisit_at <= now);
58
- const from = due
59
- ? Math.max(thought.created, thought.revisit_at)
60
- : thought.created;
61
- const base = due
62
- ? Math.max(0.6, Number(thought.importance || 0))
63
- : Number(thought.importance || 0);
64
- return round(
65
- base * 0.5 ** (lived.since(from) / (THOUGHT_HALF_LIFE[thought.kind] || 14)),
66
- );
67
- }
68
-
69
- export function meetingSalience(created, lived) {
70
- return round(0.5 ** (lived.since(created) / MEETING_HALF_LIFE));
71
- }
72
-
73
- // Whether what is being said touches this content: two shared word pairs,
74
- // so one common pair is not enough to bring something back.
75
- export function touches(content, cue, need = 2) {
76
- if (!cue?.size) return false;
77
- let shared = 0;
78
- for (const term of interestTerms([content]))
79
- if (cue.has(term) && ++shared >= need) return true;
80
- return false;
81
- }
82
-
83
- // Whether this wording is still her sentence: two distinctive pairs in common
84
- // with what was said, or one when the wording only has one.
85
- export function echoes(content, texts) {
86
- const said = interestTerms(texts);
87
- if (!said.size) return false;
88
- const need = interestTerms([content]).size < 2 ? 1 : 2;
89
- return touches(content, said, need);
90
- }
91
-
92
- // A restatement may add words, as in "搬到上海" remembered as "住在上海".
93
- // The last distinctive word of what is written still has to be one that was
94
- // said. Swapping that word ("辞职" written as "结婚") does not count.
95
- export function grounded(content, texts) {
96
- const said = interestTerms(texts);
97
- const terms = [...interestTerms([content])];
98
- if (!terms.length || !said.size) return false;
99
- return said.has(terms.at(-1));
100
- }
101
-
102
- // One set, or one set per speaker. An empty list means nothing was said.
103
- export function cueList(cue, cues) {
104
- if (Array.isArray(cues) && cues.length)
105
- return cues.filter((set) => set?.size);
106
- if (cue?.size) return [cue];
107
- return [];
108
- }
109
-
110
- export function anyTouches(content, cues, need = 2) {
111
- return cueList(null, cues).some((cue) => touches(content, cue, need));
112
- }
113
-
114
- // Two wordings of the same self-thread. A longer sentence that still
115
- // carries the shorter wish's distinctive pairs is the same thread.
116
- // Sharing only a frame ("我想把…完") is not enough.
117
- export function sameSelf(a, b) {
118
- if (similar(a, b, 0.75)) return true;
119
- const left = interestTerms([a]);
120
- const right = interestTerms([b]);
121
- if (!left.size || !right.size) return false;
122
- let shared = 0;
123
- for (const term of left) if (right.has(term)) shared++;
124
- const shorter = Math.min(left.size, right.size);
125
- const longer = Math.max(left.size, right.size);
126
- return shared >= 2 && shared / shorter >= 0.6 && shared / longer >= 0.22;
127
- }
128
-
129
- // Whether someone's own words met this wish. Two distinctive pairs, or
130
- // one when the wish only has one; naming a title or work in it is enough.
131
- // A single Chinese pair is still not a meeting.
132
- export function meetsLife(content, cue) {
133
- const wish = interestTerms([content]);
134
- if (!wish.size || !cue?.size) return false;
135
- if (touches(content, cue, wish.size < 2 ? 1 : 2)) return true;
136
- return namesProject(wish, cue);
137
- }
138
-
139
- // A later note that puts this wish down. The same sentence must name
140
- // the wish, not just share a leftover pair like "子里"; "封面" or
141
- // walking up to the button is not putting it down.
142
- const LIFE_FRAME = new Set(
143
- "游戏 决定 完全 不看 评分 讨论 一下 开始 起来 出来 一点 开新 新游 看看 试试 打算 想要".split(
144
- " ",
145
- ),
146
- );
147
-
148
- export function leftLife(wish, thought) {
149
- const want = String(wish || "");
150
- const note = String(thought || "");
151
- if (!want || !note) return false;
152
- const marks = [...interestTerms([want])].filter(
153
- (term) =>
154
- !LIFE_FRAME.has(term) &&
155
- (/^[a-z0-9]{3,}$/.test(term) ||
156
- (/[\u4e00-\u9fff]{2}/.test(term) &&
157
- !/[里的了着吗呢去上来]$/.test(term))),
158
- );
159
- if (!marks.length) return false;
160
- return note.split(/(?<=[。!?\n])/).some((sentence) => {
161
- if (
162
- !/(?:封了|放下了|到此为止|不想再(?:过|想|开|玩|打|写)|不再过|不继续|先放下)/.test(
163
- sentence,
164
- )
165
- )
166
- return false;
167
- const cue = interestTerms([sentence]);
168
- return marks.some((term) => cue.has(term) || sentence.includes(term));
169
- });
170
- }
171
-
172
- // A later note that is still this wish: the same wording, a title in it,
173
- // two shared pairs, or one pair that is not just a frame like "游戏".
174
- export function aboutLife(wish, thought) {
175
- const want = String(wish || "");
176
- const note = String(thought || "");
177
- if (!want || !note) return false;
178
- if (sameSelf(want, note)) return true;
179
- const cue = interestTerms([note]);
180
- if (meetsLife(want, cue)) return true;
181
- const terms = interestTerms([want]);
182
- let shared = 0;
183
- for (const term of terms) if (cue.has(term)) shared++;
184
- if (shared >= 2) return true;
185
- for (const term of terms)
186
- if (cue.has(term) && !LIFE_FRAME.has(term)) return true;
187
- return false;
188
- }
189
-
190
- // A wish she is living for herself: something she wants to do or become.
191
- // Advice, a promise about how she will treat someone, and watching over
192
- // another person's state stay as threads; they do not take livingFor.
193
-
194
- export function ownLife(content) {
195
- const text = String(content || "");
196
- if (/^(?:我)?(?:会|愿意|想)?(?:建议|劝|提醒|鼓励|答应)/.test(text))
197
- return false;
198
- return /(?:希望成为|想.{0,6}(?:看|学|玩|打|去|读|写|找|把|自己)|打算(?:开|玩|看|学|去)|决定(?:开|玩|看|学)|更想)/.test(
199
- text,
200
- );
201
- }
1
+ import { interestTerms, namesProject } from "./attention.js";
2
+ import { similar } from "./util.js";
3
+
4
+ // How long each kind of thing stays with her without being lived again, as
5
+ // a half-life in days she actually lived. Nothing here is deleted: a faded
6
+ // thread is only out of view until something touches it again.
7
+ export const THREAD_HALF_LIFE = {
8
+ trait: 180,
9
+ view: 90,
10
+ habit: 90,
11
+ interest: 60,
12
+ care: 30,
13
+ intention: 30,
14
+ curiosity: 21,
15
+ };
16
+ export const THOUGHT_HALF_LIFE = {
17
+ unfinished: 21,
18
+ revision: 14,
19
+ reflection: 10,
20
+ reconnection: 10,
21
+ };
22
+ const THREAD_GRACE = 3;
23
+ export const THREAD_FADED = 0.12;
24
+ export const THREAD_FADING = 0.2;
25
+ export const THOUGHT_FADED = 0.1;
26
+ // A meeting's meaning stays about as long as an unfinished thought. It is
27
+ // not deleted; it only leaves what she is looking at now.
28
+ export const MEETING_HALF_LIFE = 21;
29
+ export const MEETING_FADED = 0.12;
30
+ // A memory nobody has touched in this many lived days needs a strong cue.
31
+ export const MEMORY_IDLE_DAYS = 45;
32
+ // The strongest threads stay with her however quiet it gets.
33
+ export const CORE_THREADS = 2;
34
+
35
+ const round = (value) => Math.round(value * 1000) / 1000;
36
+
37
+ // An opinion the nightly memory step picked out of one chat is not yet who she
38
+ // is. It stays only as long as it keeps coming back: each new piece of
39
+ // evidence renews it, and one that never recurs fades like a passing thought.
40
+ export const PASSING_VIEW_HALF_LIFE = 21;
41
+
42
+ export function threadSalience(row, lived) {
43
+ const idle = lived.since(row.created);
44
+ const settled = row.kind === "trait" && (row.days?.length || 0) >= 5 ? 2 : 1;
45
+ const passing = row.kind === "view" && row.origin === "memory";
46
+ const half = passing
47
+ ? PASSING_VIEW_HALF_LIFE
48
+ : (THREAD_HALF_LIFE[row.kind] || 60) * settled;
49
+ return round(
50
+ Number(row.strength || 0) *
51
+ 0.5 ** (Math.max(0, idle - THREAD_GRACE) / half),
52
+ );
53
+ }
54
+
55
+ // A revisit that has come due brings a thought back for a while.
56
+ export function thoughtSalience(thought, lived, now) {
57
+ const due = !!(thought.revisit_at && thought.revisit_at <= now);
58
+ const from = due
59
+ ? Math.max(thought.created, thought.revisit_at)
60
+ : thought.created;
61
+ const base = due
62
+ ? Math.max(0.6, Number(thought.importance || 0))
63
+ : Number(thought.importance || 0);
64
+ return round(
65
+ base * 0.5 ** (lived.since(from) / (THOUGHT_HALF_LIFE[thought.kind] || 14)),
66
+ );
67
+ }
68
+
69
+ export function meetingSalience(created, lived) {
70
+ return round(0.5 ** (lived.since(created) / MEETING_HALF_LIFE));
71
+ }
72
+
73
+ // Whether what is being said touches this content: two shared word pairs,
74
+ // so one common pair is not enough to bring something back.
75
+ export function touches(content, cue, need = 2) {
76
+ if (!cue?.size) return false;
77
+ let shared = 0;
78
+ for (const term of interestTerms([content]))
79
+ if (cue.has(term) && ++shared >= need) return true;
80
+ return false;
81
+ }
82
+
83
+ // Whether this wording is still her sentence: two distinctive pairs in common
84
+ // with what was said, or one when the wording only has one.
85
+ export function echoes(content, texts) {
86
+ const said = interestTerms(texts);
87
+ if (!said.size) return false;
88
+ const need = interestTerms([content]).size < 2 ? 1 : 2;
89
+ return touches(content, said, need);
90
+ }
91
+
92
+ // A restatement may add words, as in "搬到上海" remembered as "住在上海".
93
+ // The last distinctive word of what is written still has to be one that was
94
+ // said. Swapping that word ("辞职" written as "结婚") does not count.
95
+ export function grounded(content, texts) {
96
+ const said = interestTerms(texts);
97
+ const terms = [...interestTerms([content])];
98
+ if (!terms.length || !said.size) return false;
99
+ return said.has(terms.at(-1));
100
+ }
101
+
102
+ // One set, or one set per speaker. An empty list means nothing was said.
103
+ export function cueList(cue, cues) {
104
+ if (Array.isArray(cues) && cues.length)
105
+ return cues.filter((set) => set?.size);
106
+ if (cue?.size) return [cue];
107
+ return [];
108
+ }
109
+
110
+ export function anyTouches(content, cues, need = 2) {
111
+ return cueList(null, cues).some((cue) => touches(content, cue, need));
112
+ }
113
+
114
+ // Two wordings of the same self-thread. A longer sentence that still
115
+ // carries the shorter wish's distinctive pairs is the same thread.
116
+ // Sharing only a frame ("我想把…完") is not enough.
117
+ export function sameSelf(a, b) {
118
+ if (similar(a, b, 0.75)) return true;
119
+ const left = interestTerms([a]);
120
+ const right = interestTerms([b]);
121
+ if (!left.size || !right.size) return false;
122
+ let shared = 0;
123
+ for (const term of left) if (right.has(term)) shared++;
124
+ const shorter = Math.min(left.size, right.size);
125
+ const longer = Math.max(left.size, right.size);
126
+ return shared >= 2 && shared / shorter >= 0.6 && shared / longer >= 0.22;
127
+ }
128
+
129
+ // Whether someone's own words met this wish. Two distinctive pairs, or
130
+ // one when the wish only has one; naming a title or work in it is enough.
131
+ // A single Chinese pair is still not a meeting.
132
+ export function meetsLife(content, cue) {
133
+ const wish = interestTerms([content]);
134
+ if (!wish.size || !cue?.size) return false;
135
+ if (touches(content, cue, wish.size < 2 ? 1 : 2)) return true;
136
+ return namesProject(wish, cue);
137
+ }
138
+
139
+ // A later note that puts this wish down. The same sentence must name
140
+ // the wish, not just share a leftover pair like "子里"; "封面" or
141
+ // walking up to the button is not putting it down.
142
+ const LIFE_FRAME = new Set(
143
+ "游戏 决定 完全 不看 评分 讨论 一下 开始 起来 出来 一点 开新 新游 看看 试试 打算 想要".split(
144
+ " ",
145
+ ),
146
+ );
147
+
148
+ export function leftLife(wish, thought) {
149
+ const want = String(wish || "");
150
+ const note = String(thought || "");
151
+ if (!want || !note) return false;
152
+ const marks = [...interestTerms([want])].filter(
153
+ (term) =>
154
+ !LIFE_FRAME.has(term) &&
155
+ (/^[a-z0-9]{3,}$/.test(term) ||
156
+ (/[\u4e00-\u9fff]{2}/.test(term) &&
157
+ !/[里的了着吗呢去上来]$/.test(term))),
158
+ );
159
+ if (!marks.length) return false;
160
+ return note.split(/(?<=[。!?\n])/).some((sentence) => {
161
+ if (
162
+ !/(?:封了|放下了|到此为止|不想再(?:过|想|开|玩|打|写)|不再过|不继续|先放下)/.test(
163
+ sentence,
164
+ )
165
+ )
166
+ return false;
167
+ const cue = interestTerms([sentence]);
168
+ return marks.some((term) => cue.has(term) || sentence.includes(term));
169
+ });
170
+ }
171
+
172
+ // A later note that is still this wish: the same wording, a title in it,
173
+ // two shared pairs, or one pair that is not just a frame like "游戏".
174
+ export function aboutLife(wish, thought) {
175
+ const want = String(wish || "");
176
+ const note = String(thought || "");
177
+ if (!want || !note) return false;
178
+ if (sameSelf(want, note)) return true;
179
+ const cue = interestTerms([note]);
180
+ if (meetsLife(want, cue)) return true;
181
+ const terms = interestTerms([want]);
182
+ let shared = 0;
183
+ for (const term of terms) if (cue.has(term)) shared++;
184
+ if (shared >= 2) return true;
185
+ for (const term of terms)
186
+ if (cue.has(term) && !LIFE_FRAME.has(term)) return true;
187
+ return false;
188
+ }
189
+
190
+ // A wish she is living for herself: something she wants to do or become.
191
+ // Advice, a promise about how she will treat someone, and watching over
192
+ // another person's state stay as threads; they do not take livingFor.
193
+
194
+ export function ownLife(content) {
195
+ const text = String(content || "");
196
+ if (/^(?:我)?(?:会|愿意|想)?(?:建议|劝|提醒|鼓励|答应)/.test(text))
197
+ return false;
198
+ return /(?:希望成为|想.{0,6}(?:看|学|玩|打|去|读|写|找|把|自己)|打算(?:开|玩|看|学|去)|决定(?:开|玩|看|学)|更想)/.test(
199
+ text,
200
+ );
201
+ }