luckytri 1.0.8 → 1.0.9
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/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/en/CHANGELOG.md +35 -24
- package/docs/en/guide.md +3 -1
- package/docs/zh/CHANGELOG.md +35 -24
- package/docs/zh/guide.md +3 -1
- package/package.json +1 -1
- package/public/guide.en.html +13 -5
- package/public/guide.html +12 -5
- package/public/tutorial.en.md +3 -1
- package/public/tutorial.md +3 -1
- package/server/core/context-builder.js +22 -3
- package/server/core/contribution-check.js +50 -0
- package/server/core/conversation-cues.js +0 -8
- package/server/core/conversation-grounding.js +247 -4
- package/server/core/dialogue-context.js +16 -13
- package/server/core/dialogue-contract.js +33 -9
- package/server/core/initiative-context.js +36 -16
- package/server/core/model-json.js +36 -3
- package/server/core/model-manager.js +2 -2
- package/server/core/model-presets.js +169 -24
- package/server/core/persona-manager.js +8 -3
- package/server/core/reader-check.js +130 -26
- package/server/core/reply-delivery.js +152 -11
- package/server/core/repository.js +33 -0
- package/server/core/response-generator.js +12 -3
- package/server/core/response-validator.js +18 -3
- package/server/core/turn-processor.js +26 -4
- package/server/core/turn.js +258 -44
- package/server/i18n/decisions.js +16 -0
- package/server/i18n/errors.js +2 -0
- package/server/i18n/models.js +11 -2
- package/server/mind/expression-grounding.js +5 -2
- package/server/mind/time/activity-recall.js +65 -0
- package/server/mind/time/index.js +6 -2
- package/server/mind/time/tasks.js +8 -10
- package/server/mind/time/works.js +3 -2
- package/server/mind/view.js +9 -2
|
@@ -9,10 +9,25 @@ import {
|
|
|
9
9
|
} from "./response-validator.js";
|
|
10
10
|
import { deliver, sleep } from "./message-scheduler.js";
|
|
11
11
|
import { leaks } from "../mind/guard.js";
|
|
12
|
-
import {
|
|
12
|
+
import { interestTerms } from "../mind/attention.js";
|
|
13
|
+
import {
|
|
14
|
+
bubblePreference,
|
|
15
|
+
conversationGrounding,
|
|
16
|
+
} from "./conversation-grounding.js";
|
|
13
17
|
import { initiativeAudience } from "../mind/conversation-origin.js";
|
|
14
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
readerQuestions,
|
|
20
|
+
readerCheckPrompt,
|
|
21
|
+
INTENT_CHECK,
|
|
22
|
+
} from "./reader-check.js";
|
|
15
23
|
import { currentExchange } from "./dialogue-context.js";
|
|
24
|
+
import {
|
|
25
|
+
claimsLivedAction,
|
|
26
|
+
EXPRESSION_GROUNDING,
|
|
27
|
+
} from "../mind/expression-grounding.js";
|
|
28
|
+
|
|
29
|
+
const replyFormatFeedback =
|
|
30
|
+
/(?:看着|看起来|读起来).{0,8}(?:累|太长|太多)|(?:你|你们).{0,12}(?:可以|能不能|别|不要).{0,12}(?:一段|一条|一句|合在一起|拆|少发|少说)|(?:一小节|一段|一条|一句).{0,12}(?:发|说|回|拆|累)|(?:太抽象|太人机|太机械|复读|看不懂|说人话|别拆|不要拆)/;
|
|
16
31
|
function isFormatError(error) {
|
|
17
32
|
return (
|
|
18
33
|
error instanceof SyntaxError ||
|
|
@@ -22,6 +37,48 @@ function isFormatError(error) {
|
|
|
22
37
|
);
|
|
23
38
|
}
|
|
24
39
|
|
|
40
|
+
export function filterTranscriptContradictedIssues(issues, snapshot) {
|
|
41
|
+
const messages = (snapshot.messages || []).filter(
|
|
42
|
+
(message) => message.role === "user",
|
|
43
|
+
);
|
|
44
|
+
const transcriptTerms = interestTerms(
|
|
45
|
+
messages.map((message) => message.text),
|
|
46
|
+
);
|
|
47
|
+
const absentEvidence =
|
|
48
|
+
/(?:当前|现有|此前|前文|对话|上下文|原话|原文|消息|记录).{0,20}(?:没有|未|并没有|不在)|(?:没有|并没有|未|无人).{0,24}(?:这句话|这句|提到|说过|出现|提供|给出|片段|原文|上下文|记录)/u;
|
|
49
|
+
const quotedPhrases = (issue) => {
|
|
50
|
+
const phrases = [];
|
|
51
|
+
for (const [open, close] of [
|
|
52
|
+
["“", "”"],
|
|
53
|
+
["‘", "’"],
|
|
54
|
+
["「", "」"],
|
|
55
|
+
["『", "』"],
|
|
56
|
+
['"', '"'],
|
|
57
|
+
]) {
|
|
58
|
+
let from = 0;
|
|
59
|
+
while (from < issue.length) {
|
|
60
|
+
const start = issue.indexOf(open, from);
|
|
61
|
+
if (start < 0) break;
|
|
62
|
+
const end = issue.indexOf(close, start + open.length);
|
|
63
|
+
if (end < 0) break;
|
|
64
|
+
const phrase = issue.slice(start + open.length, end).trim();
|
|
65
|
+
if (phrase.length >= 3) phrases.push(phrase);
|
|
66
|
+
from = start + open.length;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return phrases;
|
|
70
|
+
};
|
|
71
|
+
return (issues || []).filter((issue) => {
|
|
72
|
+
if (!absentEvidence.test(issue)) return true;
|
|
73
|
+
return !quotedPhrases(issue).some((phrase) => {
|
|
74
|
+
if (messages.some((message) => (message.text || "").includes(phrase)))
|
|
75
|
+
return true;
|
|
76
|
+
const terms = interestTerms([phrase]);
|
|
77
|
+
return [...terms].filter((term) => transcriptTerms.has(term)).length >= 2;
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
25
82
|
// Owns this part of the lifecycle; the facade keeps the shared runtime state.
|
|
26
83
|
export class ReplyDelivery {
|
|
27
84
|
constructor(owner) {
|
|
@@ -55,7 +112,10 @@ export class ReplyDelivery {
|
|
|
55
112
|
(m) =>
|
|
56
113
|
c.privateChat ||
|
|
57
114
|
targetUsers.has(m.userId) ||
|
|
58
|
-
|
|
115
|
+
m.relation === "direct" ||
|
|
116
|
+
(m.replyId &&
|
|
117
|
+
snapshot.batch.some((b) => b.platformId === m.replyId)) ||
|
|
118
|
+
replyFormatFeedback.test(m.text || ""),
|
|
59
119
|
);
|
|
60
120
|
const audienceCurrent = () =>
|
|
61
121
|
!snapshot.initiative ||
|
|
@@ -135,7 +195,15 @@ export class ReplyDelivery {
|
|
|
135
195
|
issues,
|
|
136
196
|
{ plain },
|
|
137
197
|
);
|
|
138
|
-
return normalizeResponse(
|
|
198
|
+
return normalizeResponse(
|
|
199
|
+
raw,
|
|
200
|
+
{
|
|
201
|
+
...turn,
|
|
202
|
+
maxBubbles:
|
|
203
|
+
bubblePreference(snapshot) === "single" ? 1 : turn.maxBubbles,
|
|
204
|
+
},
|
|
205
|
+
fallbackText,
|
|
206
|
+
);
|
|
139
207
|
} catch (error) {
|
|
140
208
|
if (!isFormatError(error)) throw error;
|
|
141
209
|
if (snapshot.initiative) throw error;
|
|
@@ -183,7 +251,22 @@ export class ReplyDelivery {
|
|
|
183
251
|
);
|
|
184
252
|
const needsDeepCheck = (response) =>
|
|
185
253
|
readerQuestions(snapshot, turn).length > 0 ||
|
|
254
|
+
(turn.contribution?.kind === "question" &&
|
|
255
|
+
snapshot.messages
|
|
256
|
+
.filter((m) => m.role === "assistant")
|
|
257
|
+
.slice(-3)
|
|
258
|
+
.some((m) => /[??]/.test(m.text || ""))) ||
|
|
186
259
|
currentExchange(snapshot).thirdPartyClaims.length > 0 ||
|
|
260
|
+
response.bubbles.some((line) =>
|
|
261
|
+
/你(?:的|在|给|付|掏).{0,12}(?:账单|费用|钱|token)|(?:烧|花|扣|付).{0,6}你.{0,6}(?:钱|token)|账单.{0,8}你/i.test(
|
|
262
|
+
line,
|
|
263
|
+
),
|
|
264
|
+
) ||
|
|
265
|
+
response.bubbles.some((line) =>
|
|
266
|
+
/我.{0,20}(?:只(?:能|有)|才(?:能|有)).{0,15}(?:回|输入|消息|开口)|(?:没人|没有人).{0,12}(?:递话|说话|叫我).{0,12}(?:我.{0,5})?(?:不会|不|才)/.test(
|
|
267
|
+
line,
|
|
268
|
+
),
|
|
269
|
+
) ||
|
|
187
270
|
response.bubbles.some((line) =>
|
|
188
271
|
/(?:我|刚才|刚刚|刚还|今天|昨天|之前).{0,25}(?:读过|读了|读完|翻开|在翻|写完|完成了|看完了)/.test(
|
|
189
272
|
line,
|
|
@@ -250,6 +333,35 @@ export class ReplyDelivery {
|
|
|
250
333
|
snapshot.batch.some((m) => m.relation === "unresolved")))));
|
|
251
334
|
const deepCheck = async (response) => {
|
|
252
335
|
try {
|
|
336
|
+
// An old expression can already contain a fictional action. Check
|
|
337
|
+
// the actual words again against activity records, without giving
|
|
338
|
+
// that draft or its own earlier explanations the role of evidence.
|
|
339
|
+
if (
|
|
340
|
+
snapshot.initiative &&
|
|
341
|
+
claimsLivedAction(response.bubbles.join("\n")) &&
|
|
342
|
+
!(
|
|
343
|
+
trace.experienceReview?.ok === true &&
|
|
344
|
+
trace.experienceReview.words.join("\n") ===
|
|
345
|
+
response.bubbles.join("\n")
|
|
346
|
+
)
|
|
347
|
+
) {
|
|
348
|
+
const lived = await models.call(
|
|
349
|
+
model,
|
|
350
|
+
"validation",
|
|
351
|
+
EXPRESSION_GROUNDING,
|
|
352
|
+
{
|
|
353
|
+
candidate: { words: response.bubbles },
|
|
354
|
+
currentLife: snapshot.inner?.currentLife || {},
|
|
355
|
+
notes: [],
|
|
356
|
+
self: [],
|
|
357
|
+
},
|
|
358
|
+
trace,
|
|
359
|
+
);
|
|
360
|
+
if (lived.ok !== true)
|
|
361
|
+
return [
|
|
362
|
+
`主动消息声称的新行动尚无实际活动记录支持:${lived.reason || "没有核实该行动"}`,
|
|
363
|
+
];
|
|
364
|
+
}
|
|
253
365
|
const checked = await models.call(
|
|
254
366
|
model,
|
|
255
367
|
"validation",
|
|
@@ -263,7 +375,7 @@ export class ReplyDelivery {
|
|
|
263
375
|
},
|
|
264
376
|
response,
|
|
265
377
|
experienceEvidence:
|
|
266
|
-
"
|
|
378
|
+
"自己的旧回复与expression草稿都可能误报经历,不单独证明实际读过、做完或正在做。sourceMaterial里的earlier_thought只证明当时想过。声称具体活动须核对currentLife的真实作品、阅读与动作证据;待办和愿望不能证明执行。自己此刻的想法与感受不需要外部动作证明。普通问候不用补答已经换了话题的旧任务。",
|
|
267
379
|
replyFocus: replyFocus(snapshot, turn),
|
|
268
380
|
thirdPartyClaims: currentExchange(snapshot).thirdPartyClaims,
|
|
269
381
|
imageEvidence: c.generationImages.length
|
|
@@ -320,9 +432,10 @@ export class ReplyDelivery {
|
|
|
320
432
|
const reader = await models.call(
|
|
321
433
|
model,
|
|
322
434
|
"validation",
|
|
323
|
-
|
|
435
|
+
readerCheckPrompt(turn),
|
|
324
436
|
{
|
|
325
437
|
questions: readerQuestions(snapshot, turn),
|
|
438
|
+
choice: turn.choice,
|
|
326
439
|
reply: response.bubbles,
|
|
327
440
|
},
|
|
328
441
|
trace,
|
|
@@ -332,10 +445,14 @@ export class ReplyDelivery {
|
|
|
332
445
|
(issue) => `给提问者的解释仍没说清:${issue}`,
|
|
333
446
|
);
|
|
334
447
|
}
|
|
335
|
-
|
|
448
|
+
const issues = filterTranscriptContradictedIssues(
|
|
449
|
+
checked.issues,
|
|
450
|
+
snapshot,
|
|
451
|
+
);
|
|
452
|
+
return checked.ok || (checked.issues.length > 0 && !issues.length)
|
|
336
453
|
? []
|
|
337
|
-
:
|
|
338
|
-
?
|
|
454
|
+
: issues.length
|
|
455
|
+
? issues
|
|
339
456
|
: ["与天性或语境不一致"];
|
|
340
457
|
} catch (error) {
|
|
341
458
|
trace.steps.push(
|
|
@@ -359,16 +476,18 @@ export class ReplyDelivery {
|
|
|
359
476
|
if (outdated()) return staleExit();
|
|
360
477
|
issues = await deepCheck(response);
|
|
361
478
|
}
|
|
362
|
-
const rewriteLimit =
|
|
479
|
+
const rewriteLimit = c.pressure >= 0.85 ? 1 : 2;
|
|
480
|
+
const repairConstraints = new Set();
|
|
363
481
|
for (let attempt = 0; issues.length && attempt < rewriteLimit; attempt++) {
|
|
364
482
|
(trace.revisions ||= []).push({
|
|
365
483
|
draft: response.bubbles,
|
|
366
484
|
issues: [...issues],
|
|
367
485
|
});
|
|
368
486
|
trace.validation = issues;
|
|
487
|
+
for (const issue of issues) repairConstraints.add(issue);
|
|
369
488
|
if (outdated()) return staleExit();
|
|
370
489
|
response = await makeResponse(
|
|
371
|
-
|
|
490
|
+
[...repairConstraints],
|
|
372
491
|
response,
|
|
373
492
|
!snapshot.initiative && attempt === rewriteLimit - 1,
|
|
374
493
|
);
|
|
@@ -378,6 +497,28 @@ export class ReplyDelivery {
|
|
|
378
497
|
issues = await deepCheck(response);
|
|
379
498
|
}
|
|
380
499
|
}
|
|
500
|
+
// Deleting an unnecessary final bubble can retain a valid answer. It
|
|
501
|
+
// never bypasses factual, privacy or comprehension checks, and crisis
|
|
502
|
+
// responses keep their complete support rather than being shortened.
|
|
503
|
+
if (issues.length && response.bubbles.length > 1 && !turn.crisis?.clear) {
|
|
504
|
+
for (let count = response.bubbles.length - 1; count > 0; count--) {
|
|
505
|
+
const prefix = {
|
|
506
|
+
...response,
|
|
507
|
+
bubbles: response.bubbles.slice(0, count),
|
|
508
|
+
};
|
|
509
|
+
const remaining = check(prefix);
|
|
510
|
+
if (remaining.length) continue;
|
|
511
|
+
if (outdated()) return staleExit();
|
|
512
|
+
const reviewed = needsDeepCheck(prefix) ? await deepCheck(prefix) : [];
|
|
513
|
+
if (reviewed.length) continue;
|
|
514
|
+
response = prefix;
|
|
515
|
+
issues = [];
|
|
516
|
+
trace.steps.push(
|
|
517
|
+
"删去未通过检查的多余末尾气泡,保留已通过全部校验的原答复",
|
|
518
|
+
);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
381
522
|
// Removing a rejected suffix is a mechanical repair with no new factual
|
|
382
523
|
// claims. Preserve the answer, then run every check again.
|
|
383
524
|
if (issues.some((issue) => issue.startsWith("不要把hh反复当句尾装饰"))) {
|
|
@@ -30,6 +30,39 @@ export function migrateCore(store) {
|
|
|
30
30
|
CREATE TABLE IF NOT EXISTS core_context_summaries (id TEXT PRIMARY KEY, session_id TEXT NOT NULL, level INTEGER NOT NULL, first_seq INTEGER NOT NULL, last_seq INTEGER NOT NULL, first_time INTEGER, last_time INTEGER, created INTEGER NOT NULL, data TEXT NOT NULL);
|
|
31
31
|
CREATE INDEX IF NOT EXISTS core_context_summaries_session ON core_context_summaries(session_id,first_seq);
|
|
32
32
|
`);
|
|
33
|
+
const savedModelsRow = db
|
|
34
|
+
.prepare("SELECT value FROM core_config WHERE id=?")
|
|
35
|
+
.get("models");
|
|
36
|
+
if (savedModelsRow) {
|
|
37
|
+
let savedModels;
|
|
38
|
+
try {
|
|
39
|
+
savedModels = JSON.parse(savedModelsRow.value);
|
|
40
|
+
} catch {
|
|
41
|
+
savedModels = null;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(savedModels)) {
|
|
44
|
+
const unavailablePresetIds = new Set([
|
|
45
|
+
"opencode-zen-space-bunny-free",
|
|
46
|
+
"opencode-zen-step-5-preview-free",
|
|
47
|
+
]);
|
|
48
|
+
const unavailableZenModels = new Set([
|
|
49
|
+
"space-bunny-free",
|
|
50
|
+
"step-5-preview-free",
|
|
51
|
+
]);
|
|
52
|
+
const availableModels = savedModels.filter(
|
|
53
|
+
(model) =>
|
|
54
|
+
!(
|
|
55
|
+
unavailablePresetIds.has(model?.id) ||
|
|
56
|
+
(model?.provider === "opencode-zen" &&
|
|
57
|
+
unavailableZenModels.has(model?.model))
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
if (availableModels.length !== savedModels.length)
|
|
61
|
+
db.prepare(
|
|
62
|
+
"UPDATE core_config SET value=?,version=version+1 WHERE id=?",
|
|
63
|
+
).run(JSON.stringify(availableModels), "models");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
33
66
|
// A saved persona or prompt may intentionally use an older name. Leave it
|
|
34
67
|
// intact; fresh workspaces receive the LuckyTri defaults from the store.
|
|
35
68
|
if (!db.prepare("SELECT id FROM core_config WHERE id='migration-v1'").get()) {
|
|
@@ -2,6 +2,7 @@ import { replyFocus } from "./conversation-cues.js";
|
|
|
2
2
|
import { wordingNotes } from "./turn.js";
|
|
3
3
|
import { initiativeContext } from "./initiative-context.js";
|
|
4
4
|
import { currentExchange, dialogueContext } from "./dialogue-context.js";
|
|
5
|
+
import { bubblePreference } from "./conversation-grounding.js";
|
|
5
6
|
|
|
6
7
|
// Puts an already chosen answer into words again: used when the turn came
|
|
7
8
|
// back without words or the first draft failed a check.
|
|
@@ -76,14 +77,22 @@ export async function generate(
|
|
|
76
77
|
issues,
|
|
77
78
|
...(explain
|
|
78
79
|
? {
|
|
79
|
-
explanationTask:
|
|
80
|
-
|
|
80
|
+
explanationTask: ["feeling", "greeting"].includes(
|
|
81
|
+
decision.understanding?.kind,
|
|
82
|
+
)
|
|
83
|
+
? "重新读current。这轮是在表达感受或亲近,用自己此刻真实想说的一两句回应这个人;不分析他的措辞,不宣布接收了什么,不催他交代旧进度,也不用新的问题填空。可以轻松,也可以保留自己的态度,不要求迎合或表演亲密。"
|
|
84
|
+
: decision.understanding?.kind === "proposal"
|
|
85
|
+
? "重新读current的新提议。表达自己的真实兴趣、顾虑或一个具体点子;没有说要改编旧作,就不要替对方定成改编或维护旧稿排期。愿不愿意由自己选择,不需要承诺交付或让对方先定方案。"
|
|
86
|
+
: "重新读current和相关原话,依据确实提供的记录,用最直接的一两句回答当前问题或表达自己的具体反应。不维护旧解释,不加入无关旧事,不把修正写成认错流程或‘没理清’的状态报告;确实缺事实时说清缺的是哪部分。解释故事不把作者本人当小说人物。",
|
|
81
87
|
}
|
|
82
88
|
: {}),
|
|
83
89
|
imageGuide,
|
|
84
90
|
replyFocus: replyFocus(snapshot, decision),
|
|
85
91
|
guidance: wordingNotes(decision, snapshot),
|
|
86
|
-
maxBubbles:
|
|
92
|
+
maxBubbles:
|
|
93
|
+
bubblePreference(snapshot) === "single"
|
|
94
|
+
? 1
|
|
95
|
+
: (decision.maxBubbles ?? 3),
|
|
87
96
|
...(!snapshot.initiative ? { exchange: currentExchange(snapshot) } : {}),
|
|
88
97
|
},
|
|
89
98
|
trace,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
recentOwnMessages,
|
|
14
14
|
routineSocialReply,
|
|
15
15
|
} from "./conversation-grounding.js";
|
|
16
|
+
import { joinBubbleParts } from "./turn.js";
|
|
16
17
|
const norm = (s) => s.toLowerCase().replace(/[\s\p{P}\p{S}]/gu, "");
|
|
17
18
|
export function wrongIdentityClaim(text, snapshot) {
|
|
18
19
|
const name = snapshot.persona?.name;
|
|
@@ -118,7 +119,17 @@ export function attributesOwnWordsToThem(text, snapshot) {
|
|
|
118
119
|
// The reviewer judges one reply; it needs the turn and its recent lead-in,
|
|
119
120
|
// not the summaries, recall or the rest of the transcript.
|
|
120
121
|
export function reviewContext(snapshot, decision = {}, response = {}) {
|
|
121
|
-
if (snapshot.initiative)
|
|
122
|
+
if (snapshot.initiative) {
|
|
123
|
+
const { self: _self, inner, ...context } = initiativeContext(snapshot);
|
|
124
|
+
// Wishes, moods and impressions explain why she wants to make contact;
|
|
125
|
+
// they cannot establish what this recipient actually said or did.
|
|
126
|
+
return {
|
|
127
|
+
...context,
|
|
128
|
+
relationships: inner?.relationships,
|
|
129
|
+
continuity: inner?.continuity,
|
|
130
|
+
currentLife: inner?.currentLife,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
122
133
|
const {
|
|
123
134
|
persona: _persona,
|
|
124
135
|
sourceRows: _sourceRows,
|
|
@@ -187,8 +198,12 @@ export function normalizeResponse(result, decision, fallback = "嗯") {
|
|
|
187
198
|
bubbles = bubbles
|
|
188
199
|
.filter((x) => typeof x === "string")
|
|
189
200
|
.map((x) => x.trim())
|
|
190
|
-
.filter(Boolean)
|
|
191
|
-
|
|
201
|
+
.filter(Boolean);
|
|
202
|
+
if (bubbles.length > maxBubbles)
|
|
203
|
+
bubbles =
|
|
204
|
+
maxBubbles === 1
|
|
205
|
+
? [joinBubbleParts(bubbles)]
|
|
206
|
+
: bubbles.slice(0, maxBubbles);
|
|
192
207
|
if (!bubbles.length) bubbles = [fallback];
|
|
193
208
|
return {
|
|
194
209
|
...(result && typeof result === "object" ? result : {}),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseSessionKey } from "../channels/session-key.js";
|
|
2
2
|
import { initiativeContext, initiativeSnapshot } from "./initiative-context.js";
|
|
3
3
|
import { withFallback } from "./model-manager.js";
|
|
4
|
+
import { activityRecallCue } from "../mind/time/activity-recall.js";
|
|
4
5
|
import { replyPrompt, prompts } from "./persona-manager.js";
|
|
5
6
|
import { buildContext, perceive } from "./context-builder.js";
|
|
6
7
|
import {
|
|
@@ -195,15 +196,23 @@ export class TurnProcessor {
|
|
|
195
196
|
this.owner.models,
|
|
196
197
|
this.owner.fallbackFor(policy, model, trace),
|
|
197
198
|
);
|
|
198
|
-
resolved
|
|
199
|
+
resolved = perceive(
|
|
199
200
|
this.owner.repo,
|
|
200
201
|
session,
|
|
201
202
|
watermark,
|
|
202
203
|
eventMode,
|
|
203
204
|
nature.name,
|
|
205
|
+
{ resolved, sentBefore: live ? now : 0 },
|
|
204
206
|
);
|
|
205
207
|
const batchIds = batch.map((m) => m.seq);
|
|
206
208
|
const window = resolved.filter((m) => !m.referenceOnly).slice(-60);
|
|
209
|
+
const cues = batch.map((m) => ({
|
|
210
|
+
id: m.seq,
|
|
211
|
+
role: m.role,
|
|
212
|
+
speaker: m.userId,
|
|
213
|
+
userId: m.userId,
|
|
214
|
+
text: m.text || "",
|
|
215
|
+
}));
|
|
207
216
|
// The speakers in this batch are the people whose history matters now.
|
|
208
217
|
// Pulling everyone from a long room window made unrelated plans compete
|
|
209
218
|
// with the exchange actually taking place.
|
|
@@ -266,14 +275,27 @@ export class TurnProcessor {
|
|
|
266
275
|
kind: privateChat ? "private" : "group",
|
|
267
276
|
people,
|
|
268
277
|
cue: batch.map((m) => ({
|
|
269
|
-
|
|
270
|
-
userId: m.userId,
|
|
271
|
-
text: m.text || "",
|
|
278
|
+
...cues.find((row) => row.id === m.seq),
|
|
272
279
|
relation: resolved.find((line) => line.id === m.seq)?.relation,
|
|
273
280
|
mentioned: m.mentioned === true,
|
|
274
281
|
})),
|
|
275
282
|
now,
|
|
276
283
|
});
|
|
284
|
+
const botSpeakers = new Set(
|
|
285
|
+
(view.inner?.relationships?.known || [])
|
|
286
|
+
.filter((relationship) => relationship.kind === "bot")
|
|
287
|
+
.map((relationship) => String(relationship.subjectId)),
|
|
288
|
+
);
|
|
289
|
+
const activityCue = activityRecallCue(cues, window, {
|
|
290
|
+
excludedSpeakers: botSpeakers,
|
|
291
|
+
});
|
|
292
|
+
if (activityCue !== cues)
|
|
293
|
+
view.inner.currentLife = this.owner.mind.time.view({
|
|
294
|
+
session: preview?.viewSession || session,
|
|
295
|
+
now,
|
|
296
|
+
cue: cues,
|
|
297
|
+
activityCue,
|
|
298
|
+
});
|
|
277
299
|
const snapshot = ownInitiative
|
|
278
300
|
? initiativeSnapshot(session, window, nature, now, policy.timeZone)
|
|
279
301
|
: buildContext(
|