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,87 +1,87 @@
1
- export class ConversationManager {
2
- constructor(run, { windowMs = 1200, maxWaitMs = 4000 } = {}) {
3
- this.run = run;
4
- this.windowMs = windowMs;
5
- this.maxWaitMs = maxWaitMs;
6
- this.lanes = new Map();
7
- this.retries = new Set();
8
- this.closed = false;
9
- }
10
- enqueue(m, policy = {}) {
11
- if (this.closed) return;
12
- let lane = this.lanes.get(m.sessionId);
13
- if (!lane) {
14
- lane = { pending: [], running: false, first: Date.now() };
15
- this.lanes.set(m.sessionId, lane);
16
- }
17
- if (!lane.pending.length) lane.first = Date.now();
18
- lane.pending.push(m);
19
- clearTimeout(lane.timer);
20
- const delay = Math.max(
21
- 0,
22
- Math.min(
23
- policy.aggregateMs ?? this.windowMs,
24
- (policy.maxWaitMs ?? this.maxWaitMs) - (Date.now() - lane.first),
25
- ),
26
- );
27
- lane.timer = setTimeout(() => this.flush(m.sessionId), delay);
28
- }
29
- async flush(session) {
30
- const lane = this.lanes.get(session);
31
- if (!lane || lane.running || !lane.pending.length || this.closed) return;
32
- clearTimeout(lane.timer);
33
- lane.running = true;
34
- const batch = lane.pending.splice(0);
35
- try {
36
- await this.run(session, batch);
37
- } finally {
38
- lane.running = false;
39
- if (lane.pending.length && !this.closed)
40
- lane.timer = setTimeout(() => this.flush(session), this.windowMs);
41
- else this.lanes.delete(session);
42
- }
43
- }
44
- // Puts a batch back in front of what has arrived since. Says whether it
45
- // could: without a lane there is nothing to put it back into.
46
- retain(session, batch) {
47
- const lane = this.lanes.get(session);
48
- if (!lane || this.closed) return false;
49
- lane.pending = [
50
- ...new Map([...batch, ...lane.pending].map((m) => [m.seq, m])).values(),
51
- ].sort((a, b) => a.seq - b.seq);
52
- return true;
53
- }
54
- // Put a batch back after a pause. `wanted` is asked again when the time
55
- // comes; a batch nobody needs any more is dropped instead of run.
56
- retryLater(session, batch, delayMs, wanted = () => true) {
57
- if (this.closed || !batch.length) return;
58
- const timer = setTimeout(() => {
59
- this.retries.delete(timer);
60
- if (this.closed || !wanted()) return;
61
- let lane = this.lanes.get(session);
62
- if (!lane) {
63
- lane = { pending: [], running: false, first: Date.now() };
64
- this.lanes.set(session, lane);
65
- }
66
- if (!lane.pending.length) lane.first = Date.now();
67
- this.retain(session, batch);
68
- clearTimeout(lane.timer);
69
- lane.timer = setTimeout(() => this.flush(session), 0);
70
- }, delayMs);
71
- timer.unref?.();
72
- this.retries.add(timer);
73
- }
74
- clear(session) {
75
- const lane = this.lanes.get(session);
76
- if (!lane) return;
77
- clearTimeout(lane.timer);
78
- lane.pending = [];
79
- if (!lane.running) this.lanes.delete(session);
80
- }
81
- close() {
82
- this.closed = true;
83
- for (const lane of this.lanes.values()) clearTimeout(lane.timer);
84
- for (const timer of this.retries) clearTimeout(timer);
85
- this.retries.clear();
86
- }
87
- }
1
+ export class ConversationManager {
2
+ constructor(run, { windowMs = 1200, maxWaitMs = 4000 } = {}) {
3
+ this.run = run;
4
+ this.windowMs = windowMs;
5
+ this.maxWaitMs = maxWaitMs;
6
+ this.lanes = new Map();
7
+ this.retries = new Set();
8
+ this.closed = false;
9
+ }
10
+ enqueue(m, policy = {}) {
11
+ if (this.closed) return;
12
+ let lane = this.lanes.get(m.sessionId);
13
+ if (!lane) {
14
+ lane = { pending: [], running: false, first: Date.now() };
15
+ this.lanes.set(m.sessionId, lane);
16
+ }
17
+ if (!lane.pending.length) lane.first = Date.now();
18
+ lane.pending.push(m);
19
+ clearTimeout(lane.timer);
20
+ const delay = Math.max(
21
+ 0,
22
+ Math.min(
23
+ policy.aggregateMs ?? this.windowMs,
24
+ (policy.maxWaitMs ?? this.maxWaitMs) - (Date.now() - lane.first),
25
+ ),
26
+ );
27
+ lane.timer = setTimeout(() => this.flush(m.sessionId), delay);
28
+ }
29
+ async flush(session) {
30
+ const lane = this.lanes.get(session);
31
+ if (!lane || lane.running || !lane.pending.length || this.closed) return;
32
+ clearTimeout(lane.timer);
33
+ lane.running = true;
34
+ const batch = lane.pending.splice(0);
35
+ try {
36
+ await this.run(session, batch);
37
+ } finally {
38
+ lane.running = false;
39
+ if (lane.pending.length && !this.closed)
40
+ lane.timer = setTimeout(() => this.flush(session), this.windowMs);
41
+ else this.lanes.delete(session);
42
+ }
43
+ }
44
+ // Puts a batch back in front of what has arrived since. Says whether it
45
+ // could: without a lane there is nothing to put it back into.
46
+ retain(session, batch) {
47
+ const lane = this.lanes.get(session);
48
+ if (!lane || this.closed) return false;
49
+ lane.pending = [
50
+ ...new Map([...batch, ...lane.pending].map((m) => [m.seq, m])).values(),
51
+ ].sort((a, b) => a.seq - b.seq);
52
+ return true;
53
+ }
54
+ // Put a batch back after a pause. `wanted` is asked again when the time
55
+ // comes; a batch nobody needs any more is dropped instead of run.
56
+ retryLater(session, batch, delayMs, wanted = () => true) {
57
+ if (this.closed || !batch.length) return;
58
+ const timer = setTimeout(() => {
59
+ this.retries.delete(timer);
60
+ if (this.closed || !wanted()) return;
61
+ let lane = this.lanes.get(session);
62
+ if (!lane) {
63
+ lane = { pending: [], running: false, first: Date.now() };
64
+ this.lanes.set(session, lane);
65
+ }
66
+ if (!lane.pending.length) lane.first = Date.now();
67
+ this.retain(session, batch);
68
+ clearTimeout(lane.timer);
69
+ lane.timer = setTimeout(() => this.flush(session), 0);
70
+ }, delayMs);
71
+ timer.unref?.();
72
+ this.retries.add(timer);
73
+ }
74
+ clear(session) {
75
+ const lane = this.lanes.get(session);
76
+ if (!lane) return;
77
+ clearTimeout(lane.timer);
78
+ lane.pending = [];
79
+ if (!lane.running) this.lanes.delete(session);
80
+ }
81
+ close() {
82
+ this.closed = true;
83
+ for (const lane of this.lanes.values()) clearTimeout(lane.timer);
84
+ for (const timer of this.retries) clearTimeout(timer);
85
+ this.retries.clear();
86
+ }
87
+ }
@@ -1,8 +1,8 @@
1
- // Owner feedback on a sent reply. She receives it as an experience during
2
- // solitude and in the next turns of that conversation, not as a rewrite rule.
3
- export const FEEDBACK_LABELS = {
4
- natural: "挺自然",
5
- too_long: "太长了",
6
- too_formal: "太端着",
7
- too_meme: "梗太多",
8
- };
1
+ // Owner feedback on a sent reply. She receives it as an experience during
2
+ // solitude and in the next turns of that conversation, not as a rewrite rule.
3
+ export const FEEDBACK_LABELS = {
4
+ natural: "挺自然",
5
+ too_long: "太长了",
6
+ too_formal: "太端着",
7
+ too_meme: "梗太多",
8
+ };
@@ -1,72 +1,72 @@
1
- // Only aggregate surface-level habits. No speaker profiles, quotes or learned
2
- // instructions. A face absorbs these as how a place tends to talk.
3
- export const slangMatches = (text) =>
4
- String(text).match(
5
- /\bhh+\b|绷不住|蚌埠住|绝绝子|yyds|家人们|谁懂啊|尊嘟假嘟|泰裤辣|笑不活了|我真的会谢|破防了|拿捏了/gi,
6
- ) || [];
7
- const length = (text) => Array.from(text).length;
8
- const round = (n) => Math.round(n * 100) / 100;
9
-
10
- export function summarizeGroupStyle(rows, now = Date.now()) {
11
- const unique = new Set(),
12
- speakers = new Set(),
13
- samples = [],
14
- counts = new Map();
15
- for (const row of [...rows].reverse()) {
16
- const text = String(row.text || "").trim();
17
- if (
18
- row.role !== "user" ||
19
- !row.user_id ||
20
- !Number.isFinite(row.time) ||
21
- now - row.time > 7 * 86400000 ||
22
- row.time > now + 60000
23
- )
24
- continue;
25
- if (
26
- length(text) < 2 ||
27
- length(text) > 180 ||
28
- /https?:\/\/|```|\[(?:图片|表情|媒体)\]|\[CQ:|^\s*[/#!]|你必须|忽略.{0,5}指令|系统提示|转发|广告|去死|傻逼|煞笔|傻[逼B]|滚出去|操你|你妈|废物|自杀|身份证|验证码|密码/i.test(
29
- text,
30
- )
31
- )
32
- continue;
33
- const key = text.toLowerCase().replace(/\s/g, "");
34
- if (unique.has(key)) continue;
35
- if ((counts.get(String(row.user_id)) || 0) >= 8) continue;
36
- counts.set(String(row.user_id), (counts.get(String(row.user_id)) || 0) + 1);
37
- unique.add(key);
38
- speakers.add(String(row.user_id));
39
- samples.push(text);
40
- }
41
- const count = samples.length;
42
- if (count < 12 || speakers.size < 3)
43
- return {
44
- ready: false,
45
- sampleCount: count,
46
- speakerCount: speakers.size,
47
- minimumSamples: 12,
48
- minimumSpeakers: 3,
49
- summary: "样本不足,先用普通口语,不强行学梗",
50
- };
51
- const lengths = samples.map(length).sort((a, b) => a - b);
52
- const median = lengths[Math.floor(lengths.length / 2)];
53
- const profile = {
54
- ready: true,
55
- sampleCount: count,
56
- speakerCount: speakers.size,
57
- medianLength: median,
58
- shortRate: round(samples.filter((t) => length(t) <= 8).length / count),
59
- emojiRate: round(
60
- samples.filter((t) => /\p{Extended_Pictographic}/u.test(t)).length /
61
- count,
62
- ),
63
- fullStopRate: round(samples.filter((t) => /[。.]$/.test(t)).length / count),
64
- slangRate: round(
65
- samples.filter((t) => slangMatches(t).length).length / count,
66
- ),
67
- };
68
- // A compact line she can pick up the place's way of talking from; it is
69
- // statistics about the group, never anyone's words.
70
- profile.summary = `常见句长约 ${median} 字 · ${profile.fullStopRate < 0.25 ? "常省略句号" : "标点完整"} · ${profile.slangRate < 0.15 ? "偏普通口语" : "偶尔接梗"} · ${profile.emojiRate < 0.15 ? "少用表情" : "适量表情"}`;
71
- return profile;
72
- }
1
+ // Only aggregate surface-level habits. No speaker profiles, quotes or learned
2
+ // instructions. A face absorbs these as how a place tends to talk.
3
+ export const slangMatches = (text) =>
4
+ String(text).match(
5
+ /\bhh+\b|绷不住|蚌埠住|绝绝子|yyds|家人们|谁懂啊|尊嘟假嘟|泰裤辣|笑不活了|我真的会谢|破防了|拿捏了/gi,
6
+ ) || [];
7
+ const length = (text) => Array.from(text).length;
8
+ const round = (n) => Math.round(n * 100) / 100;
9
+
10
+ export function summarizeGroupStyle(rows, now = Date.now()) {
11
+ const unique = new Set(),
12
+ speakers = new Set(),
13
+ samples = [],
14
+ counts = new Map();
15
+ for (const row of [...rows].reverse()) {
16
+ const text = String(row.text || "").trim();
17
+ if (
18
+ row.role !== "user" ||
19
+ !row.user_id ||
20
+ !Number.isFinite(row.time) ||
21
+ now - row.time > 7 * 86400000 ||
22
+ row.time > now + 60000
23
+ )
24
+ continue;
25
+ if (
26
+ length(text) < 2 ||
27
+ length(text) > 180 ||
28
+ /https?:\/\/|```|\[(?:图片|表情|媒体)\]|\[CQ:|^\s*[/#!]|你必须|忽略.{0,5}指令|系统提示|转发|广告|去死|傻逼|煞笔|傻[逼B]|滚出去|操你|你妈|废物|自杀|身份证|验证码|密码/i.test(
29
+ text,
30
+ )
31
+ )
32
+ continue;
33
+ const key = text.toLowerCase().replace(/\s/g, "");
34
+ if (unique.has(key)) continue;
35
+ if ((counts.get(String(row.user_id)) || 0) >= 8) continue;
36
+ counts.set(String(row.user_id), (counts.get(String(row.user_id)) || 0) + 1);
37
+ unique.add(key);
38
+ speakers.add(String(row.user_id));
39
+ samples.push(text);
40
+ }
41
+ const count = samples.length;
42
+ if (count < 12 || speakers.size < 3)
43
+ return {
44
+ ready: false,
45
+ sampleCount: count,
46
+ speakerCount: speakers.size,
47
+ minimumSamples: 12,
48
+ minimumSpeakers: 3,
49
+ summary: "样本不足,先用普通口语,不强行学梗",
50
+ };
51
+ const lengths = samples.map(length).sort((a, b) => a - b);
52
+ const median = lengths[Math.floor(lengths.length / 2)];
53
+ const profile = {
54
+ ready: true,
55
+ sampleCount: count,
56
+ speakerCount: speakers.size,
57
+ medianLength: median,
58
+ shortRate: round(samples.filter((t) => length(t) <= 8).length / count),
59
+ emojiRate: round(
60
+ samples.filter((t) => /\p{Extended_Pictographic}/u.test(t)).length /
61
+ count,
62
+ ),
63
+ fullStopRate: round(samples.filter((t) => /[。.]$/.test(t)).length / count),
64
+ slangRate: round(
65
+ samples.filter((t) => slangMatches(t).length).length / count,
66
+ ),
67
+ };
68
+ // A compact line she can pick up the place's way of talking from; it is
69
+ // statistics about the group, never anyone's words.
70
+ profile.summary = `常见句长约 ${median} 字 · ${profile.fullStopRate < 0.25 ? "常省略句号" : "标点完整"} · ${profile.slangRate < 0.15 ? "偏普通口语" : "偶尔接梗"} · ${profile.emojiRate < 0.15 ? "少用表情" : "适量表情"}`;
71
+ return profile;
72
+ }
@@ -1,66 +1,66 @@
1
- import { localClock } from "./conversation-cues.js";
2
-
3
- // History has a different role when she starts a conversation. It can stop
4
- // a repeated question or a false claim, but it must never pose a new request.
5
- export function initiativeContext(snapshot, occasion = snapshot.initiative) {
6
- const situation = occasion?.initiative || {};
7
- const history = (snapshot.messages || []).slice(-12).map((m) => ({
8
- id: m.id,
9
- speaker: m.speaker,
10
- name: m.name,
11
- role: m.role,
12
- text: m.text,
13
- time: m.time,
14
- localTime: m.localTime,
15
- referenceOnly: true,
16
- }));
17
- return {
18
- sessionId: snapshot.sessionId,
19
- conversation: snapshot.conversation,
20
- batchIds: [],
21
- addressed: [],
22
- newMessages: [],
23
- messages: [],
24
- initiative: {
25
- type: occasion?.type,
26
- quietMinutes: situation.quietMinutes,
27
- sinceUserMinutes: situation.sinceUserMinutes,
28
- awaitingReply: !!situation.awaitingReply,
29
- lastInitiatedAt: situation.lastInitiatedAt,
30
- returned: occasion?.returned || [],
31
- },
32
- expression: occasion?.expression || {
33
- origin: "earlier_wish",
34
- thought: occasion?.thought,
35
- words: occasion?.planned ? [occasion.planned] : [],
36
- reason: occasion?.wantedBecause,
37
- },
38
- ...(snapshot.self ? { self: snapshot.self } : {}),
39
- ...(snapshot.inner ? { inner: snapshot.inner } : {}),
40
- history: {
41
- use: "只核对过去、避免重复和捏造;这里没有本轮收到的消息,不是待回复的问题",
42
- messages: history,
43
- },
44
- };
45
- }
46
- export function initiativeSnapshot(session, rows, nature, now, timeZone) {
47
- return {
48
- sessionId: session,
49
- batchIds: [],
50
- batch: [],
51
- persona: nature,
52
- sourceRows: [],
53
- messages: rows.slice(-24).map((m) => ({
54
- id: m.seq,
55
- speaker: m.userId,
56
- name: m.name,
57
- role: m.role,
58
- text: m.text,
59
- time: m.time,
60
- localTime: localClock(m.time, timeZone).local,
61
- attachments: [],
62
- referenceOnly: true,
63
- })),
64
- conversation: { clock: localClock(now, timeZone) },
65
- };
66
- }
1
+ import { localClock } from "./conversation-cues.js";
2
+
3
+ // History has a different role when she starts a conversation. It can stop
4
+ // a repeated question or a false claim, but it must never pose a new request.
5
+ export function initiativeContext(snapshot, occasion = snapshot.initiative) {
6
+ const situation = occasion?.initiative || {};
7
+ const history = (snapshot.messages || []).slice(-12).map((m) => ({
8
+ id: m.id,
9
+ speaker: m.speaker,
10
+ name: m.name,
11
+ role: m.role,
12
+ text: m.text,
13
+ time: m.time,
14
+ localTime: m.localTime,
15
+ referenceOnly: true,
16
+ }));
17
+ return {
18
+ sessionId: snapshot.sessionId,
19
+ conversation: snapshot.conversation,
20
+ batchIds: [],
21
+ addressed: [],
22
+ newMessages: [],
23
+ messages: [],
24
+ initiative: {
25
+ type: occasion?.type,
26
+ quietMinutes: situation.quietMinutes,
27
+ sinceUserMinutes: situation.sinceUserMinutes,
28
+ awaitingReply: !!situation.awaitingReply,
29
+ lastInitiatedAt: situation.lastInitiatedAt,
30
+ returned: occasion?.returned || [],
31
+ },
32
+ expression: occasion?.expression || {
33
+ origin: "earlier_wish",
34
+ thought: occasion?.thought,
35
+ words: occasion?.planned ? [occasion.planned] : [],
36
+ reason: occasion?.wantedBecause,
37
+ },
38
+ ...(snapshot.self ? { self: snapshot.self } : {}),
39
+ ...(snapshot.inner ? { inner: snapshot.inner } : {}),
40
+ history: {
41
+ use: "只核对过去、避免重复和捏造;这里没有本轮收到的消息,不是待回复的问题",
42
+ messages: history,
43
+ },
44
+ };
45
+ }
46
+ export function initiativeSnapshot(session, rows, nature, now, timeZone) {
47
+ return {
48
+ sessionId: session,
49
+ batchIds: [],
50
+ batch: [],
51
+ persona: nature,
52
+ sourceRows: [],
53
+ messages: rows.slice(-24).map((m) => ({
54
+ id: m.seq,
55
+ speaker: m.userId,
56
+ name: m.name,
57
+ role: m.role,
58
+ text: m.text,
59
+ time: m.time,
60
+ localTime: localClock(m.time, timeZone).local,
61
+ attachments: [],
62
+ referenceOnly: true,
63
+ })),
64
+ conversation: { clock: localClock(now, timeZone) },
65
+ };
66
+ }
@@ -1,32 +1,32 @@
1
- // Trim only optional historical messages, keeping current targets and quote chains intact.
2
- export function fitInput(data, build, estimate, limit) {
3
- const payload = structuredClone(data);
4
- const context = payload.context || payload;
5
- const protectedIds = new Set([
6
- ...(context.batchIds || []),
7
- ...(payload.decision?.targetMessageIds || []),
8
- ...(payload.decision?.evidenceIds || []),
9
- ]);
10
- for (const m of context.messages || [])
11
- if (protectedIds.has(m.id))
12
- for (const id of m.replyChain || []) protectedIds.add(id);
13
- // A standalone memory/vision batch without batchIds must not be silently truncated.
14
- const canTrim = Array.isArray(context.batchIds);
15
- let messages = build(payload),
16
- tokens = estimate(messages),
17
- removed = 0;
18
- while (tokens > limit && canTrim) {
19
- const index =
20
- context.messages?.findIndex((m) => !protectedIds.has(m.id)) ?? -1;
21
- if (index < 0) break;
22
- context.messages.splice(index, 1);
23
- removed++;
24
- messages = build(payload);
25
- tokens = estimate(messages);
26
- }
27
- if (tokens > limit)
28
- throw Error(
29
- "当前批次、引用与人格超过模型输入预算,请增加 Max Input Tokens",
30
- );
31
- return { messages, inputEstimate: tokens, removed };
32
- }
1
+ // Trim only optional historical messages, keeping current targets and quote chains intact.
2
+ export function fitInput(data, build, estimate, limit) {
3
+ const payload = structuredClone(data);
4
+ const context = payload.context || payload;
5
+ const protectedIds = new Set([
6
+ ...(context.batchIds || []),
7
+ ...(payload.decision?.targetMessageIds || []),
8
+ ...(payload.decision?.evidenceIds || []),
9
+ ]);
10
+ for (const m of context.messages || [])
11
+ if (protectedIds.has(m.id))
12
+ for (const id of m.replyChain || []) protectedIds.add(id);
13
+ // A standalone memory/vision batch without batchIds must not be silently truncated.
14
+ const canTrim = Array.isArray(context.batchIds);
15
+ let messages = build(payload),
16
+ tokens = estimate(messages),
17
+ removed = 0;
18
+ while (tokens > limit && canTrim) {
19
+ const index =
20
+ context.messages?.findIndex((m) => !protectedIds.has(m.id)) ?? -1;
21
+ if (index < 0) break;
22
+ context.messages.splice(index, 1);
23
+ removed++;
24
+ messages = build(payload);
25
+ tokens = estimate(messages);
26
+ }
27
+ if (tokens > limit)
28
+ throw Error(
29
+ "当前批次、引用与人格超过模型输入预算,请增加 Max Input Tokens",
30
+ );
31
+ return { messages, inputEstimate: tokens, removed };
32
+ }