luckytri 1.0.0

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 (235) hide show
  1. package/.github/SECURITY.md +71 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +250 -0
  4. package/README.md +250 -0
  5. package/bin/luckytri.js +107 -0
  6. package/docs/brand/banner.png +0 -0
  7. package/docs/brand/palette.png +0 -0
  8. package/docs/brand/portrait.png +0 -0
  9. package/docs/en/CHANGELOG.md +113 -0
  10. package/docs/en/architecture.md +152 -0
  11. package/docs/en/chat-style.md +62 -0
  12. package/docs/en/connect.md +63 -0
  13. package/docs/en/guide.md +216 -0
  14. package/docs/en/her-life.md +189 -0
  15. package/docs/en/npm-release.md +25 -0
  16. package/docs/en/plugins.md +82 -0
  17. package/docs/en/recovery.md +28 -0
  18. package/docs/en/time.md +71 -0
  19. package/docs/en/webui.md +73 -0
  20. package/docs/zh/CHANGELOG.md +113 -0
  21. package/docs/zh/architecture.md +152 -0
  22. package/docs/zh/chat-style.md +62 -0
  23. package/docs/zh/connect.md +63 -0
  24. package/docs/zh/guide.md +216 -0
  25. package/docs/zh/her-life.md +189 -0
  26. package/docs/zh/npm-release.md +33 -0
  27. package/docs/zh/plugins.md +82 -0
  28. package/docs/zh/recovery.md +28 -0
  29. package/docs/zh/time.md +71 -0
  30. package/docs/zh/webui.md +73 -0
  31. package/package.json +108 -0
  32. package/plugins/sdk/index.d.ts +155 -0
  33. package/plugins/sdk/testing.js +58 -0
  34. package/plugins/weather/index.js +82 -0
  35. package/plugins/weather/luckytri-plugin.json +29 -0
  36. package/public/app/assets/ChatsPage-C-FcRapR.js +3 -0
  37. package/public/app/assets/ChatsPage-DfyPCS9N.css +1 -0
  38. package/public/app/assets/Empty-CpP5kQHH.js +2 -0
  39. package/public/app/assets/Empty-DmUB2-Bk.css +1 -0
  40. package/public/app/assets/Field-D4gY-INU.js +1 -0
  41. package/public/app/assets/HeartPage-Bft9wZp5.css +1 -0
  42. package/public/app/assets/HeartPage-CPy_thQd.js +1 -0
  43. package/public/app/assets/LifePage-BUi5euBF.css +1 -0
  44. package/public/app/assets/LifePage-CxWvwmSf.js +1 -0
  45. package/public/app/assets/MemoryPage-DC4rhQ68.js +1 -0
  46. package/public/app/assets/MemoryPage-cZvXfkXb.css +1 -0
  47. package/public/app/assets/NaturePage-CTXHCE2D.css +1 -0
  48. package/public/app/assets/NaturePage-IvYw04Zq.js +3 -0
  49. package/public/app/assets/PeoplePage-BmUxkrOU.css +1 -0
  50. package/public/app/assets/PeoplePage-Cqefm9-q.js +1 -0
  51. package/public/app/assets/PluginsPage-DKSaKvXb.js +2 -0
  52. package/public/app/assets/PluginsPage-HyBe5D8w.css +1 -0
  53. package/public/app/assets/SystemPage-CAMPoYqM.css +1 -0
  54. package/public/app/assets/SystemPage-CJmtJkrK.js +1 -0
  55. package/public/app/assets/TimePage-BOms_n4I.css +1 -0
  56. package/public/app/assets/TimePage-Cw44inM4.js +3 -0
  57. package/public/app/assets/formatters-ZmW_xFHw.js +1 -0
  58. package/public/app/assets/index-BheRW-1t.js +4 -0
  59. package/public/app/assets/index-YDz1ree1.css +1 -0
  60. package/public/app/assets/knowledge-DG0e0uKw.js +1 -0
  61. package/public/app/assets/liquid-garden-IMoHBwzc.png +0 -0
  62. package/public/app/assets/luckytri-home-95QKeMtc.png +0 -0
  63. package/public/app/assets/media-BzPDBm7e.js +1 -0
  64. package/public/app/index.html +27 -0
  65. package/public/guide.en.html +1053 -0
  66. package/public/guide.html +893 -0
  67. package/public/plugin-kit/v1/bridge.js +30 -0
  68. package/public/plugin-kit/v1/kit.css +14 -0
  69. package/public/tutorial.en.md +214 -0
  70. package/public/tutorial.md +214 -0
  71. package/scripts/backup.js +311 -0
  72. package/scripts/browser.js +41 -0
  73. package/scripts/launch.js +94 -0
  74. package/scripts/plugin.js +150 -0
  75. package/scripts/proxy-config.js +167 -0
  76. package/scripts/recover.js +26 -0
  77. package/scripts/runtime.js +25 -0
  78. package/scripts/service.js +47 -0
  79. package/scripts/setup.js +50 -0
  80. package/scripts/start-server.js +41 -0
  81. package/scripts/stop.js +36 -0
  82. package/server/app.js +96 -0
  83. package/server/channels/adapters.js +27 -0
  84. package/server/channels/contract.js +57 -0
  85. package/server/channels/hub.js +165 -0
  86. package/server/channels/index.js +25 -0
  87. package/server/channels/onebot/adapter.js +288 -0
  88. package/server/channels/onebot/index.js +336 -0
  89. package/server/channels/onebot/token.js +3 -0
  90. package/server/channels/qqbot/adapter.js +253 -0
  91. package/server/channels/qqbot/api.js +199 -0
  92. package/server/channels/qqbot/credentials.js +11 -0
  93. package/server/channels/qqbot/gateway.js +274 -0
  94. package/server/channels/qqbot/index.js +389 -0
  95. package/server/channels/segments.js +40 -0
  96. package/server/channels/session-key.js +153 -0
  97. package/server/channels/state.js +42 -0
  98. package/server/core/api.js +564 -0
  99. package/server/core/capabilities.js +208 -0
  100. package/server/core/context-builder.js +288 -0
  101. package/server/core/context-compactor.js +454 -0
  102. package/server/core/conversation-cues.js +325 -0
  103. package/server/core/conversation-manager.js +87 -0
  104. package/server/core/events.js +60 -0
  105. package/server/core/feedback.js +8 -0
  106. package/server/core/group-style.js +72 -0
  107. package/server/core/image-preprocessor.js +208 -0
  108. package/server/core/initiative-context.js +66 -0
  109. package/server/core/input-budget.js +32 -0
  110. package/server/core/local-demo.js +98 -0
  111. package/server/core/message-manager.js +118 -0
  112. package/server/core/message-scheduler.js +78 -0
  113. package/server/core/model-json.js +190 -0
  114. package/server/core/model-manager.js +1213 -0
  115. package/server/core/model-presets.js +900 -0
  116. package/server/core/name-call.js +29 -0
  117. package/server/core/net-guard.js +49 -0
  118. package/server/core/network.js +157 -0
  119. package/server/core/orchestrator.js +569 -0
  120. package/server/core/perceivers.js +91 -0
  121. package/server/core/person-name.js +22 -0
  122. package/server/core/persona-manager.js +243 -0
  123. package/server/core/reply-delivery.js +393 -0
  124. package/server/core/reply-target-resolver.js +50 -0
  125. package/server/core/repository.js +293 -0
  126. package/server/core/response-generator.js +50 -0
  127. package/server/core/response-validator.js +508 -0
  128. package/server/core/retired-prompts.js +51 -0
  129. package/server/core/sessions.js +109 -0
  130. package/server/core/speaker-names.js +92 -0
  131. package/server/core/topic-tracker.js +65 -0
  132. package/server/core/turn-processor.js +630 -0
  133. package/server/core/turn.js +236 -0
  134. package/server/core/vision-manager.js +633 -0
  135. package/server/http.js +18 -0
  136. package/server/i18n/decisions.js +206 -0
  137. package/server/i18n/errors.js +497 -0
  138. package/server/i18n/index.js +144 -0
  139. package/server/i18n/labels.js +653 -0
  140. package/server/i18n/models.js +111 -0
  141. package/server/index.js +102 -0
  142. package/server/knowledge/api.js +342 -0
  143. package/server/knowledge/embedding-profile.js +114 -0
  144. package/server/knowledge/index.js +13 -0
  145. package/server/knowledge/manager.js +386 -0
  146. package/server/knowledge/retrieval.js +93 -0
  147. package/server/knowledge/schema.js +154 -0
  148. package/server/knowledge/vector-index.js +90 -0
  149. package/server/mind/affect.js +265 -0
  150. package/server/mind/anticipations.js +507 -0
  151. package/server/mind/api.js +703 -0
  152. package/server/mind/attention.js +200 -0
  153. package/server/mind/bonds.js +558 -0
  154. package/server/mind/budget.js +152 -0
  155. package/server/mind/clock.js +83 -0
  156. package/server/mind/context-selection.js +61 -0
  157. package/server/mind/continuity.js +562 -0
  158. package/server/mind/conversation-origin.js +175 -0
  159. package/server/mind/day-planner.js +324 -0
  160. package/server/mind/days.js +210 -0
  161. package/server/mind/evidence-read.js +45 -0
  162. package/server/mind/evidence.js +148 -0
  163. package/server/mind/faces.js +138 -0
  164. package/server/mind/guard.js +60 -0
  165. package/server/mind/index.js +445 -0
  166. package/server/mind/initiative.js +333 -0
  167. package/server/mind/life/activities.js +21 -0
  168. package/server/mind/life/context.js +309 -0
  169. package/server/mind/life/diary.js +786 -0
  170. package/server/mind/life/growth.js +252 -0
  171. package/server/mind/life/index.js +1049 -0
  172. package/server/mind/life/presence.js +305 -0
  173. package/server/mind/meetings.js +1069 -0
  174. package/server/mind/memory-policy.js +48 -0
  175. package/server/mind/memory.js +888 -0
  176. package/server/mind/nature.js +262 -0
  177. package/server/mind/novelty.js +19 -0
  178. package/server/mind/observations.js +84 -0
  179. package/server/mind/own-day.js +324 -0
  180. package/server/mind/own-voice.js +318 -0
  181. package/server/mind/periods.js +118 -0
  182. package/server/mind/reading.js +120 -0
  183. package/server/mind/salience.js +201 -0
  184. package/server/mind/schema.js +562 -0
  185. package/server/mind/self.js +484 -0
  186. package/server/mind/senses.js +53 -0
  187. package/server/mind/thoughts.js +274 -0
  188. package/server/mind/time/activity-clock.js +196 -0
  189. package/server/mind/time/agenda.js +242 -0
  190. package/server/mind/time/api.js +200 -0
  191. package/server/mind/time/attention.js +34 -0
  192. package/server/mind/time/availability.js +69 -0
  193. package/server/mind/time/commitment-review.js +198 -0
  194. package/server/mind/time/delivery-links.js +130 -0
  195. package/server/mind/time/executor.js +512 -0
  196. package/server/mind/time/experiences.js +128 -0
  197. package/server/mind/time/games.js +562 -0
  198. package/server/mind/time/index.js +739 -0
  199. package/server/mind/time/intent.js +166 -0
  200. package/server/mind/time/kinds.js +171 -0
  201. package/server/mind/time/model-material.js +77 -0
  202. package/server/mind/time/plugin-activity.js +207 -0
  203. package/server/mind/time/presentation.js +51 -0
  204. package/server/mind/time/schedule.js +83 -0
  205. package/server/mind/time/schema.js +84 -0
  206. package/server/mind/time/search.js +383 -0
  207. package/server/mind/time/sharing.js +472 -0
  208. package/server/mind/time/task-links.js +311 -0
  209. package/server/mind/time/tasks.js +587 -0
  210. package/server/mind/time/works.js +432 -0
  211. package/server/mind/traits.js +191 -0
  212. package/server/mind/util.js +110 -0
  213. package/server/mind/view.js +273 -0
  214. package/server/paths.js +102 -0
  215. package/server/plugins/api.js +204 -0
  216. package/server/plugins/host.js +967 -0
  217. package/server/plugins/install.js +295 -0
  218. package/server/plugins/manifest.js +201 -0
  219. package/server/plugins/permissions.js +42 -0
  220. package/server/plugins/runner/guard.mjs +36 -0
  221. package/server/plugins/runner/worker.mjs +250 -0
  222. package/server/plugins/schema.js +91 -0
  223. package/server/plugins/zip.js +112 -0
  224. package/server/startup-lock.js +118 -0
  225. package/server/storage/archive.js +241 -0
  226. package/server/storage/backup-cleanup-worker.js +8 -0
  227. package/server/storage/backup-cleanup.js +414 -0
  228. package/server/storage/backup-lock.js +76 -0
  229. package/server/storage/backup-scheduler.js +131 -0
  230. package/server/storage/store.js +199 -0
  231. package/server/studio/display-names.js +138 -0
  232. package/server/studio/management.js +102 -0
  233. package/server/studio/readiness.js +190 -0
  234. package/server/studio/settings.js +236 -0
  235. package/server/version.js +10 -0
@@ -0,0 +1,393 @@
1
+ import { replyFocus } from "./conversation-cues.js";
2
+ import { replyPrompt } from "./persona-manager.js";
3
+ import { generate } from "./response-generator.js";
4
+ import {
5
+ contradictoryOwnWords,
6
+ normalizeResponse,
7
+ reviewContext,
8
+ validateResponse,
9
+ } from "./response-validator.js";
10
+ import { deliver, sleep } from "./message-scheduler.js";
11
+ import { leaks } from "../mind/guard.js";
12
+ import { initiativeAudience } from "../mind/conversation-origin.js";
13
+ function isFormatError(error) {
14
+ return (
15
+ error instanceof SyntaxError ||
16
+ /JSON|Unexpected token|格式|气泡|bubbles|choice|输出被截断/i.test(
17
+ error?.message || "",
18
+ )
19
+ );
20
+ }
21
+
22
+ // Owns this part of the lifecycle; the facade keeps the shared runtime state.
23
+ export class ReplyDelivery {
24
+ constructor(owner) {
25
+ this.owner = owner;
26
+ }
27
+ async speak(c) {
28
+ const {
29
+ session,
30
+ batch,
31
+ turn,
32
+ snapshot,
33
+ trace,
34
+ finish,
35
+ models,
36
+ model,
37
+ nature,
38
+ prompt,
39
+ policy,
40
+ } = c;
41
+ const targetUsers = new Set(
42
+ snapshot.messages
43
+ .filter((m) => turn.targetMessageIds.includes(m.id))
44
+ .map((m) => m.speaker),
45
+ );
46
+ const newerUserMessages = () =>
47
+ this.owner.repo
48
+ .eventsAfter(session, c.watermark, { simulated: c.simulatedTurn })
49
+ .filter((m) => m.role === "user");
50
+ const hasRelevantUpdate = () =>
51
+ newerUserMessages().some(
52
+ (m) =>
53
+ c.privateChat ||
54
+ targetUsers.has(m.userId) ||
55
+ (m.replyId && snapshot.batch.some((b) => b.platformId === m.replyId)),
56
+ );
57
+ const audienceCurrent = () =>
58
+ !snapshot.initiative ||
59
+ initiativeAudience(
60
+ this.owner.mind,
61
+ {
62
+ sources: snapshot.initiative.expression?.sources || [],
63
+ created: snapshot.initiative.expression?.formedAt,
64
+ },
65
+ session,
66
+ this.owner.now(),
67
+ ).allowed;
68
+ const isCurrent = () =>
69
+ !this.owner.queue.closed &&
70
+ (c.preview ||
71
+ this.owner.enabled(session, { simulated: c.simulatedTurn })) &&
72
+ this.owner.sessionState(session) === c.state &&
73
+ !hasRelevantUpdate() &&
74
+ audienceCurrent();
75
+ const staleExit = () => {
76
+ if (!audienceCurrent())
77
+ trace.steps.push("话题来源或参与者已改变,这份旧稿未继续发送");
78
+ if (newerUserMessages().length)
79
+ trace.steps.push("新消息已进入下一批,取消旧稿");
80
+ if (
81
+ hasRelevantUpdate() &&
82
+ c.clearEpoch === (this.owner.clearEpoch.get(session) || 0)
83
+ )
84
+ this.owner.queue.retain(session, batch);
85
+ return finish("stale", "生成期间语境已更新,旧稿未发送");
86
+ };
87
+ const outdated = () => !c.replay && !c.preview && !isCurrent();
88
+ const focus = replyFocus(snapshot, turn).kind;
89
+ const fallbackOptions = [
90
+ ...(focus === "promise_check"
91
+ ? ["你问的约定我得认真核对,刚才我没接住。", "我先认真核对一下。"]
92
+ : []),
93
+ "嗯",
94
+ "好",
95
+ "行",
96
+ "收到",
97
+ ];
98
+ const fallbackText = turn.crisis?.clear
99
+ ? "你现在还好吗?身边有人能陪着你吗?"
100
+ : snapshot.initiative
101
+ ? ""
102
+ : fallbackOptions.find(
103
+ (text) =>
104
+ !(nature.forbidden || []).some((word) => text.includes(word)) &&
105
+ !snapshot.messages
106
+ .filter((m) => m.role === "assistant")
107
+ .slice(-12)
108
+ .some((m) => m.text === text),
109
+ ) || "嗯";
110
+ const generationPrompt = replyPrompt(
111
+ nature,
112
+ prompt,
113
+ snapshot.initiative ? "initiative" : "generation",
114
+ );
115
+ const makeResponse = async (issues = []) => {
116
+ try {
117
+ const raw = await generate(
118
+ models,
119
+ model,
120
+ generationPrompt,
121
+ snapshot,
122
+ turn,
123
+ trace,
124
+ c.generationImages,
125
+ issues,
126
+ );
127
+ return normalizeResponse(raw, turn, fallbackText);
128
+ } catch (error) {
129
+ if (!isFormatError(error)) throw error;
130
+ if (snapshot.initiative) throw error;
131
+ trace.steps.push("模型回复格式异常,已使用本地短句兜底");
132
+ return { bubbles: [fallbackText], reason: "本地短句兜底" };
133
+ }
134
+ };
135
+ const secrets = this.owner.mind.memory.secretsOutside(session);
136
+ const privateFacts = this.owner.mind.meetings.privateSayings(session);
137
+ const check = (response) => {
138
+ const issues = validateResponse(
139
+ response,
140
+ snapshot,
141
+ turn,
142
+ policy.maxReply,
143
+ );
144
+ if (leaks(response.bubbles, secrets).length)
145
+ issues.push("这句话说出了别人要求保密的事,不能在这里说");
146
+ if (leaks(response.bubbles, privateFacts).length)
147
+ issues.push("这句话把私下知道的事说出来了,不能在这里说");
148
+ return issues;
149
+ };
150
+ const recentPrivateContinuity =
151
+ c.direct &&
152
+ !c.privateChat &&
153
+ snapshot.inner?.continuity?.people?.some(
154
+ (person) =>
155
+ person.recentShared?.length || person.myPrivateIntentions?.length,
156
+ );
157
+ const needsDeepCheck = (response) =>
158
+ !!snapshot.initiative ||
159
+ !!snapshot.inner?.continuity?.requested ||
160
+ focus === "clarify_claim" ||
161
+ focus === "basis_check" ||
162
+ !!(
163
+ c.direct &&
164
+ snapshot.inner?.continuity?.people?.some(
165
+ (person) => person.myElsewhereWords?.length,
166
+ )
167
+ ) ||
168
+ !!recentPrivateContinuity ||
169
+ (policy.deepCheck &&
170
+ c.pressure < 0.85 &&
171
+ (turn.crisis?.clear ||
172
+ [
173
+ "feeling",
174
+ "vent",
175
+ "repair",
176
+ "promise_check",
177
+ "clarify_claim",
178
+ "basis_check",
179
+ ].includes(focus) ||
180
+ (!c.direct &&
181
+ (response.bubbles.join("").length > 60 ||
182
+ snapshot.batch.some((m) => m.relation === "unresolved")))));
183
+ const deepCheck = async (response) => {
184
+ try {
185
+ const checked = await models.call(
186
+ model,
187
+ "validation",
188
+ replyPrompt(nature, prompt, "validation"),
189
+ {
190
+ context: reviewContext(snapshot, turn),
191
+ decision: {
192
+ choice: turn.choice,
193
+ reason: turn.reason,
194
+ targetMessageIds: turn.targetMessageIds,
195
+ },
196
+ response,
197
+ replyFocus: replyFocus(snapshot, turn),
198
+ imageEvidence: c.generationImages.length
199
+ ? "复审已附上回合模型看到的同一张画面;据图核对具体描述,不凭空判定为编造。"
200
+ : snapshot.vision
201
+ ? "本轮有图片观察结果,回复可以依据 context.vision,不要当成编造。"
202
+ : snapshot.unavailableImages?.length
203
+ ? "本轮图片没有读取成功,回复不应包含具体画面细节。"
204
+ : undefined,
205
+ ...(snapshot.initiative
206
+ ? {
207
+ task: "本轮没有收到新消息,是自己先形成念头再分享。逐项检查:有没有捏造对方刚说过或发过消息;有没有把旧消息当成当前提问而补答;有没有把自己的念头换成另一件事;有没有捏造亲历;有没有把其他会话的参与者、项目、约定误认成当前对象或咱们的事。依据 expression.audience.origin 核对真实来源与参与者。未参与者也可以聊:她认为有分享价值并自然给背景,不应仅因shared:false或话题来自别处而否决。公开可知不代表当前对象参与过、知道上下文或有相同需求。expression.words 也可能误认对象,不可因它是已有草稿而免检;可以改成向当前对象介绍新想法。只有具体错误才给 issues。",
208
+ }
209
+ : {}),
210
+ },
211
+ trace,
212
+ c.generationImages,
213
+ );
214
+ if (typeof checked.ok !== "boolean" || !Array.isArray(checked.issues)) {
215
+ trace.steps.push("回复复审结果格式异常,已按本地校验继续");
216
+ return snapshot.initiative
217
+ ? ["主动消息的语境核对结果无效,草稿保留"]
218
+ : [];
219
+ }
220
+ return checked.ok
221
+ ? []
222
+ : checked.issues.length
223
+ ? checked.issues
224
+ : ["与天性或语境不一致"];
225
+ } catch (error) {
226
+ trace.steps.push(
227
+ `回复复审暂不可用,已按本地校验继续:${error.message}`,
228
+ );
229
+ return snapshot.initiative
230
+ ? ["主动消息的事实与内容核对暂未完成,草稿保留,稍后重新决定"]
231
+ : [];
232
+ }
233
+ };
234
+ if (outdated()) return staleExit();
235
+ let response = turn.bubbles.length
236
+ ? normalizeResponse(
237
+ { bubbles: turn.bubbles, reason: turn.reason },
238
+ turn,
239
+ fallbackText,
240
+ )
241
+ : await makeResponse();
242
+ let issues = check(response);
243
+ if (!issues.length && needsDeepCheck(response)) {
244
+ if (outdated()) return staleExit();
245
+ issues = await deepCheck(response);
246
+ }
247
+ const rewriteLimit = snapshot.inner?.continuity?.people?.some(
248
+ (person) => person.myElsewhereWords?.length,
249
+ )
250
+ ? 2
251
+ : 1;
252
+ for (let attempt = 0; issues.length && attempt < rewriteLimit; attempt++) {
253
+ trace.validation = issues;
254
+ if (outdated()) return staleExit();
255
+ response = await makeResponse(issues);
256
+ issues = check(response);
257
+ if (!issues.length && needsDeepCheck(response)) {
258
+ if (outdated()) return staleExit();
259
+ issues = await deepCheck(response);
260
+ }
261
+ }
262
+ if (issues.length && contradictoryOwnWords(snapshot, turn)) {
263
+ const honest = {
264
+ bubbles: ["我前面确实说过,后来解释得前后不一致,是我说乱了。"],
265
+ reason: "已发出的原话互相矛盾,先承认自己说乱了",
266
+ };
267
+ const checked = check(honest);
268
+ if (!checked.length) {
269
+ trace.steps.push("本人旧话互相矛盾,使用核实后的简短更正");
270
+ response = honest;
271
+ issues = [];
272
+ }
273
+ }
274
+ if (
275
+ issues.length &&
276
+ focus === "clarify_claim" &&
277
+ issues.some((issue) =>
278
+ /第三人的主人|倒置时间|追问者承担误解责任/.test(issue),
279
+ )
280
+ ) {
281
+ const honest = {
282
+ bubbles: [
283
+ "那句‘你主人’是我回他时说错了,我没有依据说他有主人。后来又解释乱了,是我的问题。",
284
+ ],
285
+ reason: "旧话错误且原回复对象明确,承认没有依据",
286
+ };
287
+ if (!check(honest).length) {
288
+ trace.steps.push("旧话将第三人关系说错,使用核实后的简短更正");
289
+ response = honest;
290
+ issues = [];
291
+ }
292
+ }
293
+ if (
294
+ issues.length &&
295
+ focus === "basis_check" &&
296
+ issues.some((issue) => /自己先开口的/.test(issue))
297
+ ) {
298
+ const honest = {
299
+ bubbles: [
300
+ "那句是我自己先开口的,你之前没发消息。我只是猜的,没有别的依据。",
301
+ ],
302
+ reason: "自己先开口的话没有他发来的消息可依,承认是猜的",
303
+ };
304
+ if (!check(honest).length) {
305
+ trace.steps.push("先开口的话被追问依据,使用核实后的简短更正");
306
+ response = honest;
307
+ issues = [];
308
+ }
309
+ }
310
+ trace.response = response;
311
+ if (issues.length) {
312
+ if (snapshot.initiative) {
313
+ trace.validation = issues;
314
+ return finish("error", "想说的话还没整理好,愿望保留,稍后重新决定");
315
+ }
316
+ trace.validation = issues;
317
+ // Nobody called her here: a filler word says nothing to the room, and
318
+ // often repeats the last one. Not saying it is the plain answer.
319
+ if (
320
+ !c.direct &&
321
+ !c.privateChat &&
322
+ !turn.crisis?.clear &&
323
+ !c.replay &&
324
+ !c.preview
325
+ ) {
326
+ trace.steps.push(
327
+ "没有人在叫她,回复两次仍未通过校验,不用空话顶替,不说了",
328
+ );
329
+ return finish("silent", "没被叫到,话没整理好,就不说了");
330
+ }
331
+ // Checks protect the words; when someone did call her, they must not
332
+ // erase the decision to answer.
333
+ trace.steps.push("回复两次生成仍未通过校验,使用本地安全短句");
334
+ response = { bubbles: [fallbackText], reason: "本地安全短句" };
335
+ trace.response = response;
336
+ }
337
+ if (c.replay) return finish("replayed", "隔离回放完成,未发送或写入记忆");
338
+ if (c.preview) return finish("previewed", turn.reason);
339
+ // Give a sender who is adding one last line a moment to finish. The
340
+ // queue already aggregates incoming messages; this catches the tail of
341
+ // a model call before its first bubble is committed to the platform.
342
+ if (this.owner.queue.lanes.has(session) && !turn.crisis?.clear)
343
+ await sleep(250);
344
+ if (!isCurrent()) return staleExit();
345
+ response = this.owner.mind.time.reconcile(
346
+ response,
347
+ session,
348
+ this.owner.now(),
349
+ snapshot.inner?.currentLife?.current,
350
+ );
351
+ trace.response = response;
352
+ trace.sent = await deliver(
353
+ this.owner.repo,
354
+ batch.at(-1) || c.anchor,
355
+ response.bubbles,
356
+ trace,
357
+ this.owner.send,
358
+ isCurrent,
359
+ {
360
+ now: this.owner.now,
361
+ prepareBubble: (line) =>
362
+ this.owner.mind.time.reconcile(
363
+ { bubbles: [line] },
364
+ session,
365
+ this.owner.now(),
366
+ snapshot.inner?.currentLife?.current,
367
+ ).bubbles[0],
368
+ },
369
+ );
370
+ if (!trace.sent.length && hasRelevantUpdate()) return staleExit();
371
+ if (trace.sent.length && !c.simulatedTurn) {
372
+ if (turn.share?.workId) {
373
+ try {
374
+ this.owner.mind.time.sharing.choose(
375
+ turn.share.workId,
376
+ session,
377
+ turn.share,
378
+ this.owner.now(),
379
+ );
380
+ } catch (error) {
381
+ trace.steps.push("分享选择未执行:" + error.message);
382
+ }
383
+ }
384
+ this.owner.mind.time.tasks.capture(trace, turn, this.owner.now());
385
+ this.owner.mind.time.interaction(
386
+ session,
387
+ turn.targetMessageIds,
388
+ this.owner.now(),
389
+ );
390
+ }
391
+ return finish(trace.sent.length ? "sent" : "cancelled", turn.reason);
392
+ }
393
+ }
@@ -0,0 +1,50 @@
1
+ import { isNameCall } from "./name-call.js";
2
+ export function resolveTargets(rows, name, aliases = "") {
3
+ const byPlatform = new Map(
4
+ rows
5
+ .filter((x) => x.platformId)
6
+ .map((x) => [`${x.accountId}:${x.platformId}`, x]),
7
+ );
8
+ return rows.map((m) => {
9
+ const parent = m.replyId
10
+ ? byPlatform.get(`${m.accountId}:${m.replyId}`)
11
+ : null;
12
+ const botMention =
13
+ m.mentioned === true ||
14
+ m.mentions?.includes(m.accountId) ||
15
+ m.raw?.mentioned === true;
16
+ const explicit =
17
+ m.kind === "private" ||
18
+ botMention ||
19
+ parent?.role === "assistant" ||
20
+ m.replyToBot ||
21
+ isNameCall(m.text, [name, ...aliases.split(/[,,]/)]);
22
+ const others = (m.mentions || []).filter((id) => id !== m.accountId);
23
+ const relation = explicit
24
+ ? "direct"
25
+ : parent
26
+ ? "other"
27
+ : m.replyId
28
+ ? "unresolved"
29
+ : others.length
30
+ ? "other"
31
+ : "unknown";
32
+ const chain = [];
33
+ let p = parent;
34
+ const seen = new Set([m.seq]);
35
+ while (p && !seen.has(p.seq) && chain.length < 12) {
36
+ chain.push(p.seq);
37
+ seen.add(p.seq);
38
+ p = byPlatform.get(`${p.accountId}:${p.replyId}`);
39
+ }
40
+ return {
41
+ ...m,
42
+ relation,
43
+ replyTo: parent
44
+ ? { seq: parent.seq, userId: parent.userId, role: parent.role }
45
+ : null,
46
+ replyChain: chain,
47
+ targetCandidates: parent ? [parent.userId] : others,
48
+ };
49
+ });
50
+ }