luckytri 1.0.7 → 1.0.8
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 +7 -3
- package/README.md +7 -3
- package/docs/en/CHANGELOG.md +10 -0
- package/docs/zh/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/public/guide.en.html +3 -3
- package/public/guide.html +3 -3
- package/server/core/conversation-cues.js +29 -9
- package/server/core/conversation-grounding.js +130 -4
- package/server/core/dialogue-context.js +194 -2
- package/server/core/dialogue-contract.js +35 -0
- package/server/core/initiative-context.js +4 -2
- package/server/core/model-manager.js +5 -8
- package/server/core/persona-manager.js +44 -9
- package/server/core/reader-check.js +62 -0
- package/server/core/reply-delivery.js +68 -3
- package/server/core/response-generator.js +43 -9
- package/server/core/response-validator.js +24 -6
- package/server/core/turn.js +43 -10
- package/server/i18n/errors.js +2 -0
- package/server/i18n/labels.js +6 -5
- package/server/mind/expression-grounding.js +21 -0
- package/server/mind/own-voice.js +35 -0
- package/server/mind/reading.js +15 -3
- package/server/mind/time/availability.js +23 -1
- package/server/mind/time/delivery-links.js +130 -130
- package/server/mind/time/executor.js +4 -2
- package/server/mind/time/games.js +13 -8
- package/server/mind/time/index.js +6 -2
- package/server/mind/time/kinds.js +29 -2
- package/server/mind/time/material-relevance.js +19 -0
- package/server/mind/time/reading-input.js +31 -0
- package/server/mind/time/sharing.js +10 -0
- package/server/mind/time/tasks.js +41 -3
- package/server/mind/time/works.js +104 -15
- package/server/mind/view.js +20 -7
|
@@ -114,11 +114,38 @@ export function pluginActivities() {
|
|
|
114
114
|
return [...registry.values()].filter((def) => def.plugin);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
//
|
|
117
|
+
// Identify her action before matching topic nouns. "She writes a novel and
|
|
118
|
+
// I promised to read it" is reading; the word "novel" is not a writing plan.
|
|
118
119
|
export function classifyActivity(words) {
|
|
119
120
|
const source = String(words || "");
|
|
121
|
+
const own =
|
|
122
|
+
source.match(/我(?:会|要|想|打算|准备|答应|先|来|再)([^]*)/) ||
|
|
123
|
+
source.match(/我(?=读|阅读|看|写|玩|游玩)([^]*)/);
|
|
124
|
+
const action = own?.[1] || source;
|
|
125
|
+
if (/^(?:让|请|希望)(?:你|他|她|对方)/.test(action)) return "unknown";
|
|
126
|
+
const verbs = [
|
|
127
|
+
["write", /写|续写|改写|创作/],
|
|
128
|
+
[
|
|
129
|
+
"read",
|
|
130
|
+
/阅读|读(?:书|稿|正文|完|了|过|一|这|那|你|她|他|它|《)|看.{0,12}(?:书|稿|正文|小说|文章|资料)/,
|
|
131
|
+
],
|
|
132
|
+
["game", /游玩|盲开|玩(?:一|个|下|这|那|《)|通关|打完.{0,10}章/],
|
|
133
|
+
["think", /思考|想一想|想想|整理思路/],
|
|
134
|
+
]
|
|
135
|
+
.map(([kind, pattern]) => ({ kind, at: action.search(pattern) }))
|
|
136
|
+
.filter((item) => item.at >= 0)
|
|
137
|
+
.sort((a, b) => a.at - b.at);
|
|
138
|
+
if (verbs.length) {
|
|
139
|
+
if (
|
|
140
|
+
verbs[0].kind === "read" &&
|
|
141
|
+
registry.get("game").pattern.test(action) &&
|
|
142
|
+
!/稿|正文|小说|故事|书|文章|诗/.test(action)
|
|
143
|
+
)
|
|
144
|
+
return "game";
|
|
145
|
+
return verbs[0].kind;
|
|
146
|
+
}
|
|
120
147
|
for (const kind of ["write", "game", "read", "think"])
|
|
121
|
-
if (registry.get(kind).pattern.test(
|
|
148
|
+
if (registry.get(kind).pattern.test(action)) return kind;
|
|
122
149
|
for (const def of registry.values())
|
|
123
150
|
if (def.plugin && def.enabled && def.pattern?.test(source)) return def.kind;
|
|
124
151
|
return "unknown";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const normalized = (value) =>
|
|
2
|
+
String(value || "")
|
|
3
|
+
.normalize("NFKC")
|
|
4
|
+
.toLowerCase()
|
|
5
|
+
.replace(/[\p{P}\p{Z}\s]/gu, "");
|
|
6
|
+
|
|
7
|
+
// A result rank or internal segment number is never evidence that a web page
|
|
8
|
+
// belongs to this game. Require its actual title in the source, not the query.
|
|
9
|
+
export function belongsToGame(source, topic) {
|
|
10
|
+
const name = normalized(topic);
|
|
11
|
+
if (name.length < 2) return false;
|
|
12
|
+
const title = normalized(source.title);
|
|
13
|
+
const content = normalized(source.content);
|
|
14
|
+
let url = "";
|
|
15
|
+
try {
|
|
16
|
+
url = normalized(decodeURIComponent(source.url || ""));
|
|
17
|
+
} catch {}
|
|
18
|
+
return title.includes(name) || content.includes(name) || url.includes(name);
|
|
19
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function awaitsTextInput(words) {
|
|
2
|
+
const value = String(words || "");
|
|
3
|
+
if (
|
|
4
|
+
/(?:已经|刚才|刚刚)(?:收到|拿到)|正文(?:已|已经)?(?:到手|收到了)/.test(
|
|
5
|
+
value,
|
|
6
|
+
)
|
|
7
|
+
)
|
|
8
|
+
return false;
|
|
9
|
+
return /等(?:你|他|她|对方).{0,35}(?:发来|发过来|递来|递过来|提供)|等.{0,25}(?:稿子|稿件|正文|全文|资料|题目|要求).{0,18}(?:发|递|到|来|齐|提供)/.test(
|
|
10
|
+
value,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function expectedTextTitle(task) {
|
|
15
|
+
return (
|
|
16
|
+
task.checkpoint?.expectedTextTitle ||
|
|
17
|
+
String(task.title || "").match(/[《「]([^》」]+)[》」]/)?.[1] ||
|
|
18
|
+
""
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function nextReadingChunk(mind, task, now) {
|
|
23
|
+
const title = expectedTextTitle(task);
|
|
24
|
+
const pending = task.checkpoint?.pendingStep?.chunk;
|
|
25
|
+
if (pending) return !title || pending.title?.includes(title) ? pending : null;
|
|
26
|
+
if (task.checkpoint?.awaitedText && !title) return null;
|
|
27
|
+
return mind.reading.next(now, {
|
|
28
|
+
title,
|
|
29
|
+
after: task.checkpoint?.awaitedAfter || 0,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -31,6 +31,8 @@ export class Sharing {
|
|
|
31
31
|
const work = this.time.works.get(workId);
|
|
32
32
|
if (!work || work.state !== "complete")
|
|
33
33
|
throw Error("只有真实完成稿可以交付");
|
|
34
|
+
if (!this.time.works.materialSupported(work))
|
|
35
|
+
throw Error("这篇记录引用了与作品无关的资料,不能作为游戏阅读进度分享");
|
|
34
36
|
if (
|
|
35
37
|
leaks([work.content], this.time.mind.meetings.privateSayings(session))
|
|
36
38
|
.length
|
|
@@ -154,6 +156,14 @@ export class Sharing {
|
|
|
154
156
|
if (!current || current.state !== "pending") return null;
|
|
155
157
|
share = current;
|
|
156
158
|
const work = this.time.works.get(share.work_id, share.version);
|
|
159
|
+
if (work && !this.time.works.materialSupported(work)) {
|
|
160
|
+
this.db
|
|
161
|
+
.prepare(
|
|
162
|
+
"UPDATE mind_time_shares SET state='declined',reason=? WHERE id=?",
|
|
163
|
+
)
|
|
164
|
+
.run("引用资料与作品无关,保留记录但停止分享", share.id);
|
|
165
|
+
return { status: "declined", reason: "引用资料与作品无关" };
|
|
166
|
+
}
|
|
157
167
|
if (
|
|
158
168
|
!work ||
|
|
159
169
|
!life.chat.enabled(share.session_id, { simulated: false }) ||
|
|
@@ -8,6 +8,7 @@ import { deadlineAt, taskSchedule, spokenDate } from "./schedule.js";
|
|
|
8
8
|
import { DeliveryLinks } from "./delivery-links.js";
|
|
9
9
|
import { activityPresentation } from "./presentation.js";
|
|
10
10
|
import { activityLabel, classifyActivity, isExecutable } from "./kinds.js";
|
|
11
|
+
import { awaitsTextInput, nextReadingChunk } from "./reading-input.js";
|
|
11
12
|
|
|
12
13
|
export const TASK_STATES = [
|
|
13
14
|
"todo",
|
|
@@ -105,10 +106,13 @@ export class Tasks {
|
|
|
105
106
|
const privateRoots = this.time.mind.meetings.privateRoots(refs, now);
|
|
106
107
|
session = privateRoots[0] || session;
|
|
107
108
|
if (privateRoots.length && discretion !== "secret") discretion = "private";
|
|
109
|
+
const inferred = classifyActivity(title);
|
|
108
110
|
const type =
|
|
109
|
-
|
|
110
|
-
?
|
|
111
|
-
:
|
|
111
|
+
awaitsTextInput(title) && inferred === "read"
|
|
112
|
+
? "read"
|
|
113
|
+
: activity && (ACTIVITY_LABELS[activity] || isExecutable(activity))
|
|
114
|
+
? activity
|
|
115
|
+
: inferred;
|
|
112
116
|
subject =
|
|
113
117
|
intentRecipient(this.db, { session, subject, title, sources: refs }) ||
|
|
114
118
|
null;
|
|
@@ -207,6 +211,24 @@ export class Tasks {
|
|
|
207
211
|
{ sources: refs },
|
|
208
212
|
now,
|
|
209
213
|
);
|
|
214
|
+
if (type === "read" && awaitsTextInput(title)) {
|
|
215
|
+
this.db
|
|
216
|
+
.prepare("UPDATE mind_time_tasks SET checkpoint=? WHERE id=?")
|
|
217
|
+
.run(
|
|
218
|
+
JSON.stringify({
|
|
219
|
+
awaitedText: true,
|
|
220
|
+
expectedTextTitle: intent.topic,
|
|
221
|
+
awaitedAfter: now,
|
|
222
|
+
}),
|
|
223
|
+
id,
|
|
224
|
+
);
|
|
225
|
+
this.wait(
|
|
226
|
+
this.get(id),
|
|
227
|
+
"等待约定的正文或资料",
|
|
228
|
+
Number.MAX_SAFE_INTEGER,
|
|
229
|
+
now,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
210
232
|
return { id };
|
|
211
233
|
}
|
|
212
234
|
sync(now = this.time.now()) {
|
|
@@ -326,6 +348,22 @@ export class Tasks {
|
|
|
326
348
|
return ids;
|
|
327
349
|
}
|
|
328
350
|
ready(now = this.time.now()) {
|
|
351
|
+
for (const row of this.db
|
|
352
|
+
.prepare(
|
|
353
|
+
"SELECT * FROM mind_time_tasks WHERE state='waiting' AND activity='read' AND json_extract(checkpoint,'$.awaitedText')=1 AND json_extract(checkpoint,'$.mergedInto') IS NULL",
|
|
354
|
+
)
|
|
355
|
+
.all()) {
|
|
356
|
+
const task = decode(row);
|
|
357
|
+
if (
|
|
358
|
+
this.time.allowed(task, now) &&
|
|
359
|
+
nextReadingChunk(this.time.mind, task, now)
|
|
360
|
+
)
|
|
361
|
+
this.control(
|
|
362
|
+
task.id,
|
|
363
|
+
{ action: "resume", reason: "约定的正文已可阅读" },
|
|
364
|
+
now,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
329
367
|
return this.db
|
|
330
368
|
.prepare(
|
|
331
369
|
"SELECT * FROM mind_time_tasks WHERE state IN ('todo','scheduled','waiting','paused') AND ready_at<=? AND next_step<=? AND activity!='unknown' AND json_extract(checkpoint,'$.mergedInto') IS NULL ORDER BY priority DESC,created LIMIT 100",
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { evidence, hasCredential, parse, text } from "../util.js";
|
|
3
3
|
import { activityPresentation, gameText } from "./presentation.js";
|
|
4
|
+
import { interestTerms } from "../attention.js";
|
|
5
|
+
import { belongsToGame } from "./material-relevance.js";
|
|
6
|
+
import { gameTopic } from "./intent.js";
|
|
4
7
|
|
|
5
8
|
const decode = (row) =>
|
|
6
9
|
row
|
|
@@ -145,7 +148,7 @@ export class Works {
|
|
|
145
148
|
: row,
|
|
146
149
|
);
|
|
147
150
|
}
|
|
148
|
-
get(id, version) {
|
|
151
|
+
get(id, version, { before = Number.MAX_SAFE_INTEGER } = {}) {
|
|
149
152
|
const work = this.db
|
|
150
153
|
.prepare("SELECT * FROM mind_time_works WHERE id=?")
|
|
151
154
|
.get(id);
|
|
@@ -156,9 +159,9 @@ export class Works {
|
|
|
156
159
|
const saved = v
|
|
157
160
|
? this.db
|
|
158
161
|
.prepare(
|
|
159
|
-
"SELECT reason FROM mind_time_events WHERE kind='draft' AND json_extract(data,'$.workId')=? AND json_extract(data,'$.version')=? ORDER BY id DESC LIMIT 1",
|
|
162
|
+
"SELECT reason FROM mind_time_events WHERE kind='draft' AND json_extract(data,'$.workId')=? AND json_extract(data,'$.version')=? AND created<=? ORDER BY id DESC LIMIT 1",
|
|
160
163
|
)
|
|
161
|
-
.get(id, v.version)
|
|
164
|
+
.get(id, v.version, before)
|
|
162
165
|
: null;
|
|
163
166
|
return v
|
|
164
167
|
? {
|
|
@@ -170,7 +173,9 @@ export class Works {
|
|
|
170
173
|
? saved.reason === "保存完成稿"
|
|
171
174
|
? "complete"
|
|
172
175
|
: "draft"
|
|
173
|
-
: work.
|
|
176
|
+
: work.updated > before
|
|
177
|
+
? "draft"
|
|
178
|
+
: work.state,
|
|
174
179
|
sources: parse(v.sources, []),
|
|
175
180
|
provenance: parse(v.provenance, {}),
|
|
176
181
|
versions: this.db
|
|
@@ -396,9 +401,19 @@ export class Works {
|
|
|
396
401
|
}
|
|
397
402
|
fragments({ session, now = this.time.now(), cue = [] } = {}) {
|
|
398
403
|
const words = (Array.isArray(cue) ? cue : [cue])
|
|
399
|
-
.map((v) => (typeof v === "string" ? v :
|
|
404
|
+
.map((v) => (typeof v === "string" ? v : v?.text || ""))
|
|
400
405
|
.join(" ");
|
|
401
|
-
|
|
406
|
+
const terms = interestTerms([words]);
|
|
407
|
+
// Search the work catalogue, not just the two newest activities. Select
|
|
408
|
+
// the version that existed at the requested time, including in replays.
|
|
409
|
+
return this.db
|
|
410
|
+
.prepare(
|
|
411
|
+
`SELECT w.*,COALESCE(p.kind,(SELECT kind FROM mind_creations WHERE id=w.legacy_creation)) kind,p.title project_title,p.bible,v.version,v.title,v.created version_created,v.summary,v.sources,v.provenance,length(v.content) characters
|
|
412
|
+
FROM mind_time_works w LEFT JOIN mind_time_projects p ON p.id=w.project_id
|
|
413
|
+
JOIN mind_time_versions v ON v.work_id=w.id AND v.version=(SELECT max(h.version) FROM mind_time_versions h WHERE h.work_id=w.id AND h.created<=?)
|
|
414
|
+
WHERE w.created<=? ORDER BY v.created DESC`,
|
|
415
|
+
)
|
|
416
|
+
.all(now, now)
|
|
402
417
|
.filter(
|
|
403
418
|
(w) =>
|
|
404
419
|
w.created <= now &&
|
|
@@ -410,29 +425,103 @@ export class Works {
|
|
|
410
425
|
)
|
|
411
426
|
.map((w) => ({
|
|
412
427
|
...w,
|
|
413
|
-
score: [...
|
|
414
|
-
(term) =>
|
|
415
|
-
|
|
416
|
-
|
|
428
|
+
score: [...interestTerms([w.title, w.project_title, w.summary])]
|
|
429
|
+
.filter((term) => terms.has(term))
|
|
430
|
+
.reduce(
|
|
431
|
+
(total, term) => total + (/^[a-z][a-z0-9]{2,}$/.test(term) ? 8 : 1),
|
|
432
|
+
0,
|
|
433
|
+
),
|
|
417
434
|
}))
|
|
418
|
-
.sort(
|
|
419
|
-
|
|
435
|
+
.sort(
|
|
436
|
+
(a, b) => b.score - a.score || b.version_created - a.version_created,
|
|
437
|
+
)
|
|
438
|
+
.filter((w, index, rows) =>
|
|
439
|
+
rows[0]?.score >= 2 ? w.score >= 2 : index < 2,
|
|
440
|
+
)
|
|
441
|
+
.filter((w) => this.materialSupported(w))
|
|
442
|
+
.slice(0, 3)
|
|
420
443
|
.map((w) => ({
|
|
421
444
|
id: w.id,
|
|
422
445
|
title: w.title,
|
|
423
|
-
state: w.state,
|
|
446
|
+
state: this.get(w.id, w.version, { before: now }).state,
|
|
424
447
|
kind: w.kind,
|
|
425
448
|
version: w.version,
|
|
426
449
|
ordinal: w.ordinal,
|
|
427
450
|
summary: text(w.summary, 180),
|
|
428
|
-
fragment: text(this.get(w.id).content,
|
|
451
|
+
fragment: text(this.get(w.id, w.version).content, 300),
|
|
429
452
|
...(w.kind === "game"
|
|
430
453
|
? {
|
|
431
454
|
experienceMode: "reference",
|
|
432
|
-
provenance: this.get(w.id).provenance,
|
|
455
|
+
provenance: this.get(w.id, w.version).provenance,
|
|
433
456
|
}
|
|
434
457
|
: {}),
|
|
435
458
|
characters: w.characters,
|
|
436
459
|
}));
|
|
437
460
|
}
|
|
461
|
+
|
|
462
|
+
activityEvidence({ session, now = this.time.now(), cue = [] } = {}) {
|
|
463
|
+
const terms = interestTerms(
|
|
464
|
+
(Array.isArray(cue) ? cue : [cue]).map((v) =>
|
|
465
|
+
typeof v === "string"
|
|
466
|
+
? v
|
|
467
|
+
: v?.role === "assistant"
|
|
468
|
+
? ""
|
|
469
|
+
: v?.text || "",
|
|
470
|
+
),
|
|
471
|
+
);
|
|
472
|
+
if (!terms.size) return [];
|
|
473
|
+
// A private conversation can motivate her own reading. Its contents and
|
|
474
|
+
// promises stay private; absence from the public fragments must not erase
|
|
475
|
+
// the fact that the explicitly named reading activity took place.
|
|
476
|
+
return this.db
|
|
477
|
+
.prepare(
|
|
478
|
+
`SELECT w.id,w.title,w.discretion,w.session_id,p.kind,p.title project_title,p.bible,v.created,v.provenance,v.sources
|
|
479
|
+
FROM mind_time_works w JOIN mind_time_projects p ON p.id=w.project_id
|
|
480
|
+
JOIN mind_time_versions v ON v.work_id=w.id AND v.version=(SELECT max(h.version) FROM mind_time_versions h WHERE h.work_id=w.id AND h.created<=?)
|
|
481
|
+
WHERE w.created<=? AND p.kind='game' AND w.discretion!='secret' ORDER BY v.created DESC`,
|
|
482
|
+
)
|
|
483
|
+
.all(now, now)
|
|
484
|
+
.filter((w) => {
|
|
485
|
+
const shared = [...interestTerms([w.title, w.project_title])].filter(
|
|
486
|
+
(term) => terms.has(term),
|
|
487
|
+
);
|
|
488
|
+
return (
|
|
489
|
+
(shared.length >= 2 ||
|
|
490
|
+
shared.some((term) => /^[a-z][a-z0-9]{2,}$/.test(term))) &&
|
|
491
|
+
this.time.allowed({ ...w, sources: parse(w.sources, []) }, now) &&
|
|
492
|
+
this.materialSupported(w)
|
|
493
|
+
);
|
|
494
|
+
})
|
|
495
|
+
.slice(0, 3)
|
|
496
|
+
.map((w) => ({
|
|
497
|
+
title: w.title,
|
|
498
|
+
activity: "阅读游戏资料并写笔记",
|
|
499
|
+
experienceMode: "reference",
|
|
500
|
+
...(w.session_id !== session && w.discretion !== "open"
|
|
501
|
+
? { privateOrigin: true }
|
|
502
|
+
: {}),
|
|
503
|
+
instruction:
|
|
504
|
+
"仅用于核对自己确有这项阅读和笔记,不能据此否认从未发生;这里不提供私下正文、发起人、约定或对话。只能自然回应本轮对方已经提到的阅读本身,不披露私下内容。",
|
|
505
|
+
}));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
materialSupported(work) {
|
|
509
|
+
const project = work.kind ? null : this.project(work.project_id);
|
|
510
|
+
if ((work.kind || project?.kind) !== "game") return true;
|
|
511
|
+
const ids = parse(work.provenance, {}).sourceIds;
|
|
512
|
+
const topic =
|
|
513
|
+
parse(work.bible, {}).topic ||
|
|
514
|
+
project?.bible?.topic ||
|
|
515
|
+
gameTopic(work.project_title || project?.title);
|
|
516
|
+
// Missing old provenance is unknown, not evidence that reading never took
|
|
517
|
+
// place. Known references all belonging to another subject are different.
|
|
518
|
+
if (!Array.isArray(ids) || !ids.length || !topic) return true;
|
|
519
|
+
const source = this.db.prepare(
|
|
520
|
+
"SELECT title,url,content FROM mind_time_sources WHERE id=?",
|
|
521
|
+
);
|
|
522
|
+
return ids.some((id) => {
|
|
523
|
+
const row = source.get(id);
|
|
524
|
+
return row && belongsToGame(row, topic);
|
|
525
|
+
});
|
|
526
|
+
}
|
|
438
527
|
}
|
package/server/mind/view.js
CHANGED
|
@@ -6,7 +6,10 @@ import { describeFace } from "./faces.js";
|
|
|
6
6
|
import { SELF_KINDS } from "./self.js";
|
|
7
7
|
import { DAY, text } from "./util.js";
|
|
8
8
|
import { relationshipHistory } from "./relationship-context.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
fixedReplyHabit,
|
|
11
|
+
conversationalMemory,
|
|
12
|
+
} from "../core/conversation-grounding.js";
|
|
10
13
|
|
|
11
14
|
const FORGOTTEN = "(很久没想起了)";
|
|
12
15
|
|
|
@@ -172,9 +175,14 @@ export function innerView(
|
|
|
172
175
|
? {
|
|
173
176
|
here: describeFace({
|
|
174
177
|
...face,
|
|
175
|
-
notes: face.notes
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
notes: face.notes
|
|
179
|
+
.map((note) => ({
|
|
180
|
+
...note,
|
|
181
|
+
content: conversationalMemory(note.content),
|
|
182
|
+
}))
|
|
183
|
+
.filter(
|
|
184
|
+
(note) => note.content && spoken(`${note.content} ${note.why}`),
|
|
185
|
+
),
|
|
178
186
|
role: spoken(face.role),
|
|
179
187
|
tone: spoken(face.tone),
|
|
180
188
|
aspiration: spoken(face.aspiration),
|
|
@@ -240,7 +248,11 @@ export function innerView(
|
|
|
240
248
|
const thoughts = relevantContext(
|
|
241
249
|
mind.thoughts
|
|
242
250
|
.open({ now, limit: 400, session })
|
|
243
|
-
.filter(
|
|
251
|
+
.filter(
|
|
252
|
+
(t) =>
|
|
253
|
+
!fixedReplyHabit(t.content) &&
|
|
254
|
+
mind.meetings.sayable(t.content, session),
|
|
255
|
+
),
|
|
244
256
|
{ cues, limit: 3, requireOverlap: cue.length > 0 },
|
|
245
257
|
);
|
|
246
258
|
const heard = feedback(mind, session, now);
|
|
@@ -257,6 +269,7 @@ export function innerView(
|
|
|
257
269
|
});
|
|
258
270
|
const wills = mind.meetings.traces({ session, before: now, limit: 2 });
|
|
259
271
|
const will = wills
|
|
272
|
+
.filter((row) => !fixedReplyHabit(row.text))
|
|
260
273
|
.map((row) =>
|
|
261
274
|
row.living ? row.text : `${text(row.content, 28)}:${row.text}`,
|
|
262
275
|
)
|
|
@@ -272,8 +285,8 @@ export function innerView(
|
|
|
272
285
|
affect.cause && mind.meetings.sayable(affect.cause, session)
|
|
273
286
|
? text(affect.cause, 40)
|
|
274
287
|
: "";
|
|
275
|
-
const withLines = withWhom.filter(
|
|
276
|
-
mind.meetings.sayable(line, session),
|
|
288
|
+
const withLines = withWhom.filter(
|
|
289
|
+
(line) => !fixedReplyHabit(line) && mind.meetings.sayable(line, session),
|
|
277
290
|
);
|
|
278
291
|
const expectingLines = expecting.filter((line) =>
|
|
279
292
|
mind.meetings.sayable(line, session),
|